mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-12-11 21:00:53 +00:00
Merge branch 'master' of https://github.com/coelckers/gzdoom
# Conflicts: # src/sound/fmodsound.cpp
This commit is contained in:
commit
8e87f5debe
22 changed files with 301 additions and 128 deletions
|
@ -2817,8 +2817,8 @@ void AM_drawThings ()
|
||||||
|
|
||||||
if (texture == NULL) goto drawTriangle; // fall back to standard display if no sprite can be found.
|
if (texture == NULL) goto drawTriangle; // fall back to standard display if no sprite can be found.
|
||||||
|
|
||||||
const double spriteXScale = (t->Scale.X * 10 * scale_mtof);
|
const double spriteXScale = (t->Scale.X * (10. / 16.) * scale_mtof);
|
||||||
const double spriteYScale = (t->Scale.Y * 10 * scale_mtof);
|
const double spriteYScale = (t->Scale.Y * (10. / 16.) * scale_mtof);
|
||||||
|
|
||||||
DrawMarker (texture, p.x, p.y, 0, !!(frame->Flip & (1 << rotation)),
|
DrawMarker (texture, p.x, p.y, 0, !!(frame->Flip & (1 << rotation)),
|
||||||
spriteXScale, spriteYScale, t->Translation, 1., 0, LegacyRenderStyles[STYLE_Normal]);
|
spriteXScale, spriteYScale, t->Translation, 1., 0, LegacyRenderStyles[STYLE_Normal]);
|
||||||
|
@ -2918,8 +2918,8 @@ static void DrawMarker (FTexture *tex, double x, double y, int yadjust,
|
||||||
AM_rotatePoint (&x, &y);
|
AM_rotatePoint (&x, &y);
|
||||||
}
|
}
|
||||||
screen->DrawTexture (tex, CXMTOF(x) + f_x, CYMTOF(y) + yadjust + f_y,
|
screen->DrawTexture (tex, CXMTOF(x) + f_x, CYMTOF(y) + yadjust + f_y,
|
||||||
DTA_DestWidthF, tex->GetScaledWidthDouble() * CleanXfac * xscale / 16,
|
DTA_DestWidthF, tex->GetScaledWidthDouble() * CleanXfac * xscale,
|
||||||
DTA_DestHeightF, tex->GetScaledHeightDouble() * CleanYfac * yscale / 16,
|
DTA_DestHeightF, tex->GetScaledHeightDouble() * CleanYfac * yscale,
|
||||||
DTA_ClipTop, f_y,
|
DTA_ClipTop, f_y,
|
||||||
DTA_ClipBottom, f_y + f_h,
|
DTA_ClipBottom, f_y + f_h,
|
||||||
DTA_ClipLeft, f_x,
|
DTA_ClipLeft, f_x,
|
||||||
|
@ -3012,7 +3012,7 @@ void AM_drawAuthorMarkers ()
|
||||||
marked->subsector->flags & SSECF_DRAWN :
|
marked->subsector->flags & SSECF_DRAWN :
|
||||||
marked->Sector->MoreFlags & SECF_DRAWN)))
|
marked->Sector->MoreFlags & SECF_DRAWN)))
|
||||||
{
|
{
|
||||||
DrawMarker (tex, marked->X(), marked->Y(), 0, flip, mark->Scale.X*16, mark->Scale.Y*16, mark->Translation,
|
DrawMarker (tex, marked->X(), marked->Y(), 0, flip, mark->Scale.X, mark->Scale.Y, mark->Translation,
|
||||||
mark->Alpha, mark->fillcolor, mark->RenderStyle);
|
mark->Alpha, mark->fillcolor, mark->RenderStyle);
|
||||||
}
|
}
|
||||||
marked = mark->args[0] != 0 ? it.Next() : NULL;
|
marked = mark->args[0] != 0 ? it.Next() : NULL;
|
||||||
|
|
|
@ -555,8 +555,15 @@ void SetCompatibilityParams()
|
||||||
if ((unsigned)CompatParams[i + 1] < (unsigned)numsectors)
|
if ((unsigned)CompatParams[i + 1] < (unsigned)numsectors)
|
||||||
{
|
{
|
||||||
// this assumes that the sector does not have any tags yet!
|
// this assumes that the sector does not have any tags yet!
|
||||||
|
if (CompatParams[i + 2] == 0)
|
||||||
|
{
|
||||||
|
tagManager.RemoveSectorTags(CompatParams[i + 1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
tagManager.AddSectorTag(CompatParams[i + 1], CompatParams[i + 2]);
|
tagManager.AddSectorTag(CompatParams[i + 1], CompatParams[i + 2]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
i += 3;
|
i += 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -914,7 +914,6 @@ void D_Display ()
|
||||||
} while (diff < 1);
|
} while (diff < 1);
|
||||||
wipestart = nowtime;
|
wipestart = nowtime;
|
||||||
done = screen->WipeDo (1);
|
done = screen->WipeDo (1);
|
||||||
S_UpdateMusic(); // OpenAL needs this to keep the music running, thanks to a complete lack of a sane streaming implementation using callbacks. :(
|
|
||||||
C_DrawConsole (hw2d); // console and
|
C_DrawConsole (hw2d); // console and
|
||||||
M_Drawer (); // menu are drawn even on top of wipes
|
M_Drawer (); // menu are drawn even on top of wipes
|
||||||
screen->Update (); // page flip or blit buffer
|
screen->Update (); // page flip or blit buffer
|
||||||
|
@ -1013,7 +1012,6 @@ void D_DoomLoop ()
|
||||||
// Update display, next frame, with current state.
|
// Update display, next frame, with current state.
|
||||||
I_StartTic ();
|
I_StartTic ();
|
||||||
D_Display ();
|
D_Display ();
|
||||||
S_UpdateMusic(); // OpenAL needs this to keep the music running, thanks to a complete lack of a sane streaming implementation using callbacks. :(
|
|
||||||
if (wantToRestart)
|
if (wantToRestart)
|
||||||
{
|
{
|
||||||
wantToRestart = false;
|
wantToRestart = false;
|
||||||
|
|
|
@ -223,7 +223,7 @@ static void ParseLock(FScanner &sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
ignorekey = true;
|
ignorekey = true;
|
||||||
if (keynum > 0 && keynum < 255)
|
if (keynum > 0 && keynum <= 255)
|
||||||
{
|
{
|
||||||
lock = new Lock;
|
lock = new Lock;
|
||||||
if (locks[keynum])
|
if (locks[keynum])
|
||||||
|
|
|
@ -963,7 +963,7 @@ void gl_RenderModel(GLSprite * spr)
|
||||||
|
|
||||||
// Added MDL_INHERITACTORPITCH and MDL_INHERITACTORROLL flags processing.
|
// Added MDL_INHERITACTORPITCH and MDL_INHERITACTORROLL flags processing.
|
||||||
// If both flags MDL_INHERITACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the momentum vector pitch.
|
// If both flags MDL_INHERITACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the momentum vector pitch.
|
||||||
if(smf->flags & MDL_INHERITACTORPITCH) pitch += spr->actor->Angles.Pitch.Degrees;
|
if(smf->flags & MDL_INHERITACTORPITCH) pitch -= spr->actor->Angles.Pitch.Degrees;
|
||||||
if(smf->flags & MDL_INHERITACTORROLL) roll += spr->actor->Angles.Roll.Degrees;
|
if(smf->flags & MDL_INHERITACTORROLL) roll += spr->actor->Angles.Roll.Degrees;
|
||||||
|
|
||||||
gl_RenderState.mModelMatrix.loadIdentity();
|
gl_RenderState.mModelMatrix.loadIdentity();
|
||||||
|
@ -974,7 +974,7 @@ void gl_RenderModel(GLSprite * spr)
|
||||||
// Applying model transformations:
|
// Applying model transformations:
|
||||||
// 1) Applying actor angle, pitch and roll to the model
|
// 1) Applying actor angle, pitch and roll to the model
|
||||||
gl_RenderState.mModelMatrix.rotate(-angle, 0, 1, 0);
|
gl_RenderState.mModelMatrix.rotate(-angle, 0, 1, 0);
|
||||||
gl_RenderState.mModelMatrix.rotate(-pitch, 0, 0, 1);
|
gl_RenderState.mModelMatrix.rotate(pitch, 0, 0, 1);
|
||||||
gl_RenderState.mModelMatrix.rotate(-roll, 1, 0, 0);
|
gl_RenderState.mModelMatrix.rotate(-roll, 1, 0, 0);
|
||||||
|
|
||||||
// 2) Applying Doomsday like rotation of the weapon pickup models
|
// 2) Applying Doomsday like rotation of the weapon pickup models
|
||||||
|
|
|
@ -120,6 +120,8 @@ void GLFlat::SetupSubsectorLights(int pass, subsector_t * sub, int *dli)
|
||||||
{
|
{
|
||||||
Plane p;
|
Plane p;
|
||||||
|
|
||||||
|
if (renderstyle == STYLE_Add) return; // no lights on additively blended surfaces.
|
||||||
|
|
||||||
if (dli != NULL && *dli != -1)
|
if (dli != NULL && *dli != -1)
|
||||||
{
|
{
|
||||||
gl_RenderState.ApplyLightIndex(GLRenderer->mLights->GetIndex(*dli));
|
gl_RenderState.ApplyLightIndex(GLRenderer->mLights->GetIndex(*dli));
|
||||||
|
@ -453,6 +455,7 @@ void GLFlat::Draw(int pass, bool trans) // trans only has meaning for GLPASS_LIG
|
||||||
gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false);
|
gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false);
|
||||||
gl_SetPlaneTextureRotation(&plane, gltexture);
|
gl_SetPlaneTextureRotation(&plane, gltexture);
|
||||||
DrawSubsectors(pass, false, false);
|
DrawSubsectors(pass, false, false);
|
||||||
|
gl_RenderState.EnableTextureMatrix(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,8 @@ FDynLightData lightdata;
|
||||||
|
|
||||||
void GLWall::SetupLights()
|
void GLWall::SetupLights()
|
||||||
{
|
{
|
||||||
|
if (RenderStyle == STYLE_Add) return; // no lights on additively blended surfaces.
|
||||||
|
|
||||||
// check for wall types which cannot have dynamic lights on them (portal types never get here so they don't need to be checked.)
|
// check for wall types which cannot have dynamic lights on them (portal types never get here so they don't need to be checked.)
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
|
|
@ -458,6 +458,7 @@ void M_SetMenu(FName menu, int param)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Printf("Attempting to open menu of unknown type '%s'\n", menu.GetChars());
|
Printf("Attempting to open menu of unknown type '%s'\n", menu.GetChars());
|
||||||
|
M_ClearMenus();
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
|
|
@ -1333,9 +1333,13 @@ static int CheckInventory (AActor *activator, const char *type, bool max)
|
||||||
if (max)
|
if (max)
|
||||||
{
|
{
|
||||||
if (item)
|
if (item)
|
||||||
|
{
|
||||||
return item->MaxAmount;
|
return item->MaxAmount;
|
||||||
else
|
}
|
||||||
return ((AInventory *)GetDefaultByType (info))->MaxAmount;
|
else if (info != nullptr && info->IsDescendantOf(RUNTIME_CLASS(AInventory)))
|
||||||
|
{
|
||||||
|
return ((AInventory *)GetDefaultByType(info))->MaxAmount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return item ? item->Amount : 0;
|
return item ? item->Amount : 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -783,6 +783,63 @@ DEFINE_ACTION_FUNCTION(AInventory, A_CheckReload)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// PROC A_WeaponOffset
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
enum WOFFlags
|
||||||
|
{
|
||||||
|
WOF_KEEPX = 1,
|
||||||
|
WOF_KEEPY = 1 << 1,
|
||||||
|
WOF_ADD = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_ACTION_FUNCTION(AInventory, A_WeaponOffset)
|
||||||
|
{
|
||||||
|
PARAM_ACTION_PROLOGUE;
|
||||||
|
PARAM_FLOAT_OPT(wx) { wx = 0.; }
|
||||||
|
PARAM_FLOAT_OPT(wy) { wy = 32.; }
|
||||||
|
PARAM_INT_OPT(flags) { flags = 0; }
|
||||||
|
|
||||||
|
if ((flags & WOF_KEEPX) && (flags & WOF_KEEPY))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
player_t *player = self->player;
|
||||||
|
pspdef_t *psp;
|
||||||
|
|
||||||
|
if (player && (player->playerstate != PST_DEAD))
|
||||||
|
{
|
||||||
|
psp = &player->psprites[ps_weapon];
|
||||||
|
if (!(flags & WOF_KEEPX))
|
||||||
|
{
|
||||||
|
if (flags & WOF_ADD)
|
||||||
|
{
|
||||||
|
psp->sx += wx;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
psp->sx = wx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(flags & WOF_KEEPY))
|
||||||
|
{
|
||||||
|
if (flags & WOF_ADD)
|
||||||
|
{
|
||||||
|
psp->sy += wy;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
psp->sy = wy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// PROC A_Lower
|
// PROC A_Lower
|
||||||
|
|
|
@ -2661,17 +2661,6 @@ void S_StopMusic (bool force)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void S_UpdateMusic()
|
|
||||||
{
|
|
||||||
GSnd->UpdateMusic();
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// CCMD playsound
|
// CCMD playsound
|
||||||
|
|
|
@ -333,7 +333,6 @@ int S_GetMusic (char **name);
|
||||||
|
|
||||||
// Stops the music for sure.
|
// Stops the music for sure.
|
||||||
void S_StopMusic (bool force);
|
void S_StopMusic (bool force);
|
||||||
void S_UpdateMusic();
|
|
||||||
|
|
||||||
// Stop and resume music, during game PAUSE.
|
// Stop and resume music, during game PAUSE.
|
||||||
void S_PauseSound (bool notmusic, bool notsfx);
|
void S_PauseSound (bool notmusic, bool notsfx);
|
||||||
|
|
|
@ -23,19 +23,6 @@ inline int CheckException(DWORD code)
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#ifdef __try
|
|
||||||
#undef __try
|
|
||||||
#endif
|
|
||||||
#define __try
|
|
||||||
|
|
||||||
#ifdef __except
|
|
||||||
#undef __except
|
|
||||||
#endif
|
|
||||||
#define __except(a) if (0)
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -148,7 +148,6 @@ public:
|
||||||
|
|
||||||
virtual void UpdateListener (SoundListener *) = 0;
|
virtual void UpdateListener (SoundListener *) = 0;
|
||||||
virtual void UpdateSounds () = 0;
|
virtual void UpdateSounds () = 0;
|
||||||
virtual void UpdateMusic() {}
|
|
||||||
|
|
||||||
virtual bool IsValid () = 0;
|
virtual bool IsValid () = 0;
|
||||||
virtual void PrintStatus () = 0;
|
virtual void PrintStatus () = 0;
|
||||||
|
|
|
@ -56,17 +56,18 @@ bool MPG123Decoder::open(FileReader *reader)
|
||||||
{
|
{
|
||||||
if(!inited)
|
if(!inited)
|
||||||
{
|
{
|
||||||
__try
|
#ifdef _MSC_VER
|
||||||
{
|
__try {
|
||||||
|
#endif
|
||||||
if(mpg123_init() != MPG123_OK)
|
if(mpg123_init() != MPG123_OK)
|
||||||
return false;
|
return false;
|
||||||
inited = true;
|
inited = true;
|
||||||
}
|
#ifdef _MSC_VER
|
||||||
__except (CheckException(GetExceptionCode()))
|
} __except (CheckException(GetExceptionCode())) {
|
||||||
{
|
|
||||||
// this means that the delay loaded decoder DLL was not found.
|
// this means that the delay loaded decoder DLL was not found.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Reader = reader;
|
Reader = reader;
|
||||||
|
|
|
@ -75,8 +75,12 @@ CUSTOM_CVAR (Int, snd_mididevice, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
|
|
||||||
if ((self >= (signed)nummididevices) || (self < -6))
|
if ((self >= (signed)nummididevices) || (self < -6))
|
||||||
{
|
{
|
||||||
Printf ("ID out of range. Using default device.\n");
|
// Don't do repeated message spam if there is no valid device.
|
||||||
|
if (self != 0)
|
||||||
|
{
|
||||||
|
Printf("ID out of range. Using default device.\n");
|
||||||
self = 0;
|
self = 0;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mididevice = MAX<UINT>(0, self);
|
mididevice = MAX<UINT>(0, self);
|
||||||
|
|
|
@ -221,7 +221,7 @@ class OpenALSoundStream : public SoundStream
|
||||||
ALuint Buffers[BufferCount];
|
ALuint Buffers[BufferCount];
|
||||||
ALuint Source;
|
ALuint Source;
|
||||||
|
|
||||||
bool Playing;
|
std::atomic<bool> Playing;
|
||||||
bool Looping;
|
bool Looping;
|
||||||
ALfloat Volume;
|
ALfloat Volume;
|
||||||
|
|
||||||
|
@ -287,12 +287,14 @@ public:
|
||||||
OpenALSoundStream(OpenALSoundRenderer *renderer)
|
OpenALSoundStream(OpenALSoundRenderer *renderer)
|
||||||
: Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL)
|
: Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL)
|
||||||
{
|
{
|
||||||
Renderer->Streams.Push(this);
|
|
||||||
memset(Buffers, 0, sizeof(Buffers));
|
memset(Buffers, 0, sizeof(Buffers));
|
||||||
|
Renderer->AddStream(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~OpenALSoundStream()
|
virtual ~OpenALSoundStream()
|
||||||
{
|
{
|
||||||
|
Renderer->RemoveStream(this);
|
||||||
|
|
||||||
if(Source)
|
if(Source)
|
||||||
{
|
{
|
||||||
alSourceRewind(Source);
|
alSourceRewind(Source);
|
||||||
|
@ -309,9 +311,6 @@ public:
|
||||||
}
|
}
|
||||||
getALError();
|
getALError();
|
||||||
|
|
||||||
Renderer->Streams.Delete(Renderer->Streams.Find(this));
|
|
||||||
Renderer = NULL;
|
|
||||||
|
|
||||||
delete Decoder;
|
delete Decoder;
|
||||||
delete Reader;
|
delete Reader;
|
||||||
}
|
}
|
||||||
|
@ -321,7 +320,7 @@ public:
|
||||||
{
|
{
|
||||||
SetVolume(vol);
|
SetVolume(vol);
|
||||||
|
|
||||||
if(Playing)
|
if(Playing.load())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* Clear the buffer queue, then fill and queue each buffer */
|
/* Clear the buffer queue, then fill and queue each buffer */
|
||||||
|
@ -342,21 +341,24 @@ public:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
alSourcePlay(Source);
|
alSourcePlay(Source);
|
||||||
Playing = (getALError()==AL_NO_ERROR);
|
if(getALError() != AL_NO_ERROR)
|
||||||
|
return false;
|
||||||
|
|
||||||
return Playing;
|
Playing.store(true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Stop()
|
virtual void Stop()
|
||||||
{
|
{
|
||||||
if(!Playing)
|
if(!Playing.load())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(Renderer->StreamLock);
|
||||||
alSourceStop(Source);
|
alSourceStop(Source);
|
||||||
alSourcei(Source, AL_BUFFER, 0);
|
alSourcei(Source, AL_BUFFER, 0);
|
||||||
getALError();
|
getALError();
|
||||||
|
|
||||||
Playing = false;
|
Playing.store(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void SetVolume(float vol)
|
virtual void SetVolume(float vol)
|
||||||
|
@ -382,21 +384,25 @@ public:
|
||||||
|
|
||||||
virtual bool SetPosition(unsigned int ms_pos)
|
virtual bool SetPosition(unsigned int ms_pos)
|
||||||
{
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(Renderer->StreamLock);
|
||||||
if(!Decoder->seek(ms_pos))
|
if(!Decoder->seek(ms_pos))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!Playing)
|
if(!Playing.load())
|
||||||
return true;
|
return true;
|
||||||
// Stop the source so that all buffers become processed, then call
|
// Stop the source so that all buffers become processed, which will
|
||||||
// IsEnded() to refill and restart the source queue with the new
|
// allow the next update to restart the source queue with the new
|
||||||
// position.
|
// position.
|
||||||
alSourceStop(Source);
|
alSourceStop(Source);
|
||||||
getALError();
|
getALError();
|
||||||
return !IsEnded();
|
lock.unlock();
|
||||||
|
Renderer->StreamWake.notify_all();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual unsigned int GetPosition()
|
virtual unsigned int GetPosition()
|
||||||
{
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(Renderer->StreamLock);
|
||||||
ALint offset, queued, state;
|
ALint offset, queued, state;
|
||||||
alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset);
|
alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset);
|
||||||
alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued);
|
alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued);
|
||||||
|
@ -405,6 +411,8 @@ public:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
size_t pos = Decoder->getSampleOffset();
|
size_t pos = Decoder->getSampleOffset();
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
if(state != AL_STOPPED)
|
if(state != AL_STOPPED)
|
||||||
{
|
{
|
||||||
size_t rem = queued*(Data.Size()/FrameSize) - offset;
|
size_t rem = queued*(Data.Size()/FrameSize) - offset;
|
||||||
|
@ -416,54 +424,10 @@ public:
|
||||||
|
|
||||||
virtual bool IsEnded()
|
virtual bool IsEnded()
|
||||||
{
|
{
|
||||||
if(!Playing)
|
return !Playing.load();
|
||||||
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,
|
virtual FString GetStats()
|
||||||
// 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;
|
FString stats;
|
||||||
size_t pos, len;
|
size_t pos, len;
|
||||||
|
@ -474,6 +438,7 @@ public:
|
||||||
ALint state;
|
ALint state;
|
||||||
ALenum err;
|
ALenum err;
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(Renderer->StreamLock);
|
||||||
alGetSourcef(Source, AL_GAIN, &volume);
|
alGetSourcef(Source, AL_GAIN, &volume);
|
||||||
alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset);
|
alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset);
|
||||||
alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed);
|
alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed);
|
||||||
|
@ -481,16 +446,19 @@ public:
|
||||||
alGetSourcei(Source, AL_SOURCE_STATE, &state);
|
alGetSourcei(Source, AL_SOURCE_STATE, &state);
|
||||||
if((err=alGetError()) != AL_NO_ERROR)
|
if((err=alGetError()) != AL_NO_ERROR)
|
||||||
{
|
{
|
||||||
|
lock.unlock();
|
||||||
stats = "Error getting stats: ";
|
stats = "Error getting stats: ";
|
||||||
stats += alGetString(err);
|
stats += alGetString(err);
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos = Decoder->getSampleOffset();
|
||||||
|
len = Decoder->getSampleLength();
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" :
|
stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" :
|
||||||
(state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state";
|
(state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state";
|
||||||
|
|
||||||
pos = Decoder->getSampleOffset();
|
|
||||||
len = Decoder->getSampleLength();
|
|
||||||
if(state == AL_STOPPED)
|
if(state == AL_STOPPED)
|
||||||
offset = BufferCount * (Data.Size()/FrameSize);
|
offset = BufferCount * (Data.Size()/FrameSize);
|
||||||
else
|
else
|
||||||
|
@ -516,6 +484,57 @@ public:
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Process()
|
||||||
|
{
|
||||||
|
if(!Playing.load())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ALint state, processed;
|
||||||
|
alGetSourcei(Source, AL_SOURCE_STATE, &state);
|
||||||
|
alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed);
|
||||||
|
if(getALError() != AL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Playing.store(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
bool ok = (getALError()==AL_NO_ERROR);
|
||||||
|
if(ok && state != AL_PLAYING && state != AL_PAUSED)
|
||||||
|
{
|
||||||
|
ALint queued = 0;
|
||||||
|
alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued);
|
||||||
|
|
||||||
|
ok = (getALError() == AL_NO_ERROR) && (queued > 0);
|
||||||
|
if(ok)
|
||||||
|
{
|
||||||
|
alSourcePlay(Source);
|
||||||
|
ok = (getALError()==AL_NO_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Playing.store(ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata)
|
bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata)
|
||||||
{
|
{
|
||||||
if(!SetupSource())
|
if(!SetupSource())
|
||||||
|
@ -707,7 +726,7 @@ static void LoadALCFunc(ALCdevice *device, const char *name, T *x)
|
||||||
#define LOAD_FUNC(x) (LoadALFunc(#x, &x))
|
#define LOAD_FUNC(x) (LoadALFunc(#x, &x))
|
||||||
#define LOAD_DEV_FUNC(d, x) (LoadALCFunc(d, #x, &x))
|
#define LOAD_DEV_FUNC(d, x) (LoadALCFunc(d, #x, &x))
|
||||||
OpenALSoundRenderer::OpenALSoundRenderer()
|
OpenALSoundRenderer::OpenALSoundRenderer()
|
||||||
: Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0)
|
: QuitThread(false), Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0)
|
||||||
{
|
{
|
||||||
EnvFilters[0] = EnvFilters[1] = 0;
|
EnvFilters[0] = EnvFilters[1] = 0;
|
||||||
|
|
||||||
|
@ -940,6 +959,15 @@ OpenALSoundRenderer::~OpenALSoundRenderer()
|
||||||
if(!Device)
|
if(!Device)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(StreamThread.joinable())
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(StreamLock);
|
||||||
|
QuitThread.store(true);
|
||||||
|
lock.unlock();
|
||||||
|
StreamWake.notify_all();
|
||||||
|
StreamThread.join();
|
||||||
|
}
|
||||||
|
|
||||||
while(Streams.Size() > 0)
|
while(Streams.Size() > 0)
|
||||||
delete Streams[0];
|
delete Streams[0];
|
||||||
|
|
||||||
|
@ -974,6 +1002,43 @@ OpenALSoundRenderer::~OpenALSoundRenderer()
|
||||||
Device = NULL;
|
Device = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenALSoundRenderer::BackgroundProc()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(StreamLock);
|
||||||
|
while(!QuitThread.load())
|
||||||
|
{
|
||||||
|
if(Streams.Size() == 0)
|
||||||
|
{
|
||||||
|
// If there's nothing to play, wait indefinitely.
|
||||||
|
StreamWake.wait(lock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Else, process all active streams and sleep for 100ms
|
||||||
|
for(size_t i = 0;i < Streams.Size();i++)
|
||||||
|
Streams[i]->Process();
|
||||||
|
StreamWake.wait_for(lock, std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenALSoundRenderer::AddStream(OpenALSoundStream *stream)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(StreamLock);
|
||||||
|
Streams.Push(stream);
|
||||||
|
lock.unlock();
|
||||||
|
// There's a stream to play, make sure the background thread is aware
|
||||||
|
StreamWake.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenALSoundRenderer::RemoveStream(OpenALSoundStream *stream)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(StreamLock);
|
||||||
|
unsigned int idx = Streams.Find(stream);
|
||||||
|
if(idx < Streams.Size())
|
||||||
|
Streams.Delete(idx);
|
||||||
|
}
|
||||||
|
|
||||||
void OpenALSoundRenderer::SetSfxVolume(float volume)
|
void OpenALSoundRenderer::SetSfxVolume(float volume)
|
||||||
{
|
{
|
||||||
SfxVolume = volume;
|
SfxVolume = volume;
|
||||||
|
@ -1252,6 +1317,8 @@ void OpenALSoundRenderer::UnloadSound(SoundHandle sfx)
|
||||||
|
|
||||||
SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata)
|
SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata)
|
||||||
{
|
{
|
||||||
|
if(StreamThread.get_id() == std::thread::id())
|
||||||
|
StreamThread = std::thread(std::mem_fn(&OpenALSoundRenderer::BackgroundProc), this);
|
||||||
OpenALSoundStream *stream = new OpenALSoundStream(this);
|
OpenALSoundStream *stream = new OpenALSoundStream(this);
|
||||||
if (!stream->Init(callback, buffbytes, flags, samplerate, userdata))
|
if (!stream->Init(callback, buffbytes, flags, samplerate, userdata))
|
||||||
{
|
{
|
||||||
|
@ -1263,6 +1330,8 @@ SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int
|
||||||
|
|
||||||
SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags)
|
SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags)
|
||||||
{
|
{
|
||||||
|
if(StreamThread.get_id() == std::thread::id())
|
||||||
|
StreamThread = std::thread(std::mem_fn(&OpenALSoundRenderer::BackgroundProc), this);
|
||||||
OpenALSoundStream *stream = new OpenALSoundStream(this);
|
OpenALSoundStream *stream = new OpenALSoundStream(this);
|
||||||
if (!stream->Init(reader, !!(flags&SoundStream::Loop)))
|
if (!stream->Init(reader, !!(flags&SoundStream::Loop)))
|
||||||
{
|
{
|
||||||
|
@ -1460,8 +1529,18 @@ FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener
|
||||||
}
|
}
|
||||||
dir += listener->position;
|
dir += listener->position;
|
||||||
|
|
||||||
|
if(dist_sqr < (0.0004f*0.0004f))
|
||||||
|
{
|
||||||
|
// Head relative
|
||||||
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||||
|
alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
|
||||||
alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]);
|
alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FVector3 dir = pos;
|
FVector3 dir = pos;
|
||||||
|
@ -1478,12 +1557,21 @@ FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener
|
||||||
|
|
||||||
dir += listener->position;
|
dir += listener->position;
|
||||||
}
|
}
|
||||||
|
if(dist_sqr < (0.0004f*0.0004f))
|
||||||
|
{
|
||||||
|
// Head relative
|
||||||
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||||
|
alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
|
||||||
alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]);
|
alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]);
|
alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]);
|
||||||
alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f);
|
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);
|
alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE);
|
||||||
|
|
||||||
alSourcef(source, AL_MAX_GAIN, SfxVolume);
|
alSourcef(source, AL_MAX_GAIN, SfxVolume);
|
||||||
|
@ -1724,9 +1812,18 @@ void OpenALSoundRenderer::UpdateSoundParams3D(SoundListener *listener, FISoundCh
|
||||||
dir += listener->position;
|
dir += listener->position;
|
||||||
|
|
||||||
alDeferUpdatesSOFT();
|
alDeferUpdatesSOFT();
|
||||||
|
|
||||||
ALuint source = GET_PTRID(chan->SysChannel);
|
ALuint source = GET_PTRID(chan->SysChannel);
|
||||||
|
|
||||||
|
if(chan->DistanceSqr < (0.0004f*0.0004f))
|
||||||
|
{
|
||||||
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||||
|
alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
|
||||||
alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]);
|
alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]);
|
||||||
|
}
|
||||||
alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]);
|
alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]);
|
||||||
getALError();
|
getALError();
|
||||||
}
|
}
|
||||||
|
@ -1852,13 +1949,6 @@ void OpenALSoundRenderer::UpdateSounds()
|
||||||
PurgeStoppedSources();
|
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()
|
bool OpenALSoundRenderer::IsValid()
|
||||||
{
|
{
|
||||||
return Device != NULL;
|
return Device != NULL;
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
#ifndef OALSOUND_H
|
#ifndef OALSOUND_H
|
||||||
#define OALSOUND_H
|
#define OALSOUND_H
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
#include "i_sound.h"
|
#include "i_sound.h"
|
||||||
#include "s_sound.h"
|
#include "s_sound.h"
|
||||||
#include "menu/menu.h"
|
#include "menu/menu.h"
|
||||||
|
@ -114,7 +119,6 @@ public:
|
||||||
|
|
||||||
virtual void UpdateListener(SoundListener *);
|
virtual void UpdateListener(SoundListener *);
|
||||||
virtual void UpdateSounds();
|
virtual void UpdateSounds();
|
||||||
virtual void UpdateMusic();
|
|
||||||
|
|
||||||
virtual void MarkStartTime(FISoundChannel*);
|
virtual void MarkStartTime(FISoundChannel*);
|
||||||
virtual float GetAudibility(FISoundChannel*);
|
virtual float GetAudibility(FISoundChannel*);
|
||||||
|
@ -184,10 +188,19 @@ private:
|
||||||
void (ALC_APIENTRY*alcDevicePauseSOFT)(ALCdevice *device);
|
void (ALC_APIENTRY*alcDevicePauseSOFT)(ALCdevice *device);
|
||||||
void (ALC_APIENTRY*alcDeviceResumeSOFT)(ALCdevice *device);
|
void (ALC_APIENTRY*alcDeviceResumeSOFT)(ALCdevice *device);
|
||||||
|
|
||||||
|
void BackgroundProc();
|
||||||
|
void AddStream(OpenALSoundStream *stream);
|
||||||
|
void RemoveStream(OpenALSoundStream *stream);
|
||||||
|
|
||||||
void LoadReverb(const ReverbContainer *env);
|
void LoadReverb(const ReverbContainer *env);
|
||||||
void PurgeStoppedSources();
|
void PurgeStoppedSources();
|
||||||
static FSoundChan *FindLowestChannel();
|
static FSoundChan *FindLowestChannel();
|
||||||
|
|
||||||
|
std::thread StreamThread;
|
||||||
|
std::mutex StreamLock;
|
||||||
|
std::condition_variable StreamWake;
|
||||||
|
std::atomic<bool> QuitThread;
|
||||||
|
|
||||||
ALCdevice *Device;
|
ALCdevice *Device;
|
||||||
ALCcontext *Context;
|
ALCcontext *Context;
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,9 @@ SndFileDecoder::~SndFileDecoder()
|
||||||
|
|
||||||
bool SndFileDecoder::open(FileReader *reader)
|
bool SndFileDecoder::open(FileReader *reader)
|
||||||
{
|
{
|
||||||
__try
|
#ifdef _MSC_VER
|
||||||
{
|
__try {
|
||||||
|
#endif
|
||||||
SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell };
|
SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell };
|
||||||
|
|
||||||
Reader = reader;
|
Reader = reader;
|
||||||
|
@ -68,11 +69,11 @@ bool SndFileDecoder::open(FileReader *reader)
|
||||||
sf_close(SndFile);
|
sf_close(SndFile);
|
||||||
SndFile = 0;
|
SndFile = 0;
|
||||||
}
|
}
|
||||||
}
|
#ifdef _MSC_VER
|
||||||
__except (CheckException(GetExceptionCode()))
|
} __except (CheckException(GetExceptionCode())) {
|
||||||
{
|
|
||||||
// this means that the delay loaded decoder DLL was not found.
|
// this means that the delay loaded decoder DLL was not found.
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -561,3 +561,11 @@ enum
|
||||||
GZF_NOPORTALS = 1 << 4, // Don't pass through any portals.
|
GZF_NOPORTALS = 1 << 4, // Don't pass through any portals.
|
||||||
GZF_NO3DFLOOR = 1 << 5, // Pass all 3D floors.
|
GZF_NO3DFLOOR = 1 << 5, // Pass all 3D floors.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Flags for A_WeaponOffset
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
WOF_KEEPX = 1,
|
||||||
|
WOF_KEEPY = 1 << 1,
|
||||||
|
WOF_ADD = 1 << 2,
|
||||||
|
};
|
|
@ -48,6 +48,7 @@ ACTOR Inventory native
|
||||||
action native A_RestoreSpecialDoomThing();
|
action native A_RestoreSpecialDoomThing();
|
||||||
action native A_RestoreSpecialThing1();
|
action native A_RestoreSpecialThing1();
|
||||||
action native A_RestoreSpecialThing2();
|
action native A_RestoreSpecialThing2();
|
||||||
|
action native A_WeaponOffset(float wx = 0, float wy = 32, int flags = 0);
|
||||||
|
|
||||||
States
|
States
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,6 +51,15 @@ A80E7EE40E0D0C76A6FBD242BE29FE27 // map15
|
||||||
resetplayerspeed
|
resetplayerspeed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5C594C67CF7721005DE71429F9811370 // Eternal Doom map03
|
||||||
|
{
|
||||||
|
// fix broken staircase. The compatibility option is not sufficient
|
||||||
|
// to reliably handle this so clear the tags from the unwanted sectors.
|
||||||
|
setsectortag 212 0
|
||||||
|
setsectortag 213 0
|
||||||
|
setsectortag 214 0
|
||||||
|
}
|
||||||
|
|
||||||
6DA6FCBA8089161BDEC6A1D3F6C8D60F // Eternal Doom MAP25
|
6DA6FCBA8089161BDEC6A1D3F6C8D60F // Eternal Doom MAP25
|
||||||
{
|
{
|
||||||
stairs
|
stairs
|
||||||
|
|
Loading…
Reference in a new issue