Merge commit 'e73969d' into gzd4-12-merge
|
|
@ -203,21 +203,21 @@ Note: All <bool> fields default to false unless mentioned otherwise.
|
|||
nogradient_top = <bool>; // disables color gradient on upper tier. (Hardware rendering only.)
|
||||
flipgradient_top = <bool>; // flips gradient colors on upper tier. (Hardware rendering only.)
|
||||
clampgradient_top = <bool>; // clamps gradient on upper tier to actual bounds (default is the entire front sector height, hardware rendering only.)
|
||||
useowncolors_top = <bool>; // Set to 1 to use the colors set in the sidedef. Default is using the colors from the owning sector.
|
||||
useowncolors_top = <bool>; // Set to 1 to use the colors set in the sidedef. Default is true if uppercolor or lowercolor are set, otherwise it's false
|
||||
uppercolor_top = <int>; // Material color of the top of the upper tier.
|
||||
lowercolor_top = <int>; // Material color of the bottom of the upper tier. (Hardware rendering only.)
|
||||
|
||||
nogradient_mid = <bool>; // disables color gradient on middle tier. (Hardware rendering only.)
|
||||
flipgradient_mid = <bool>; // flips gradient colors on middle tier. (Hardware rendering only.)
|
||||
clampgradient_mid = <bool>; // clamps gradient on middle tier to actual bounds (default is the entire front sector height, hardware rendering only.)
|
||||
useowncolors_mid = <bool>; // Set to 1 to use the colors set in the sidedef. Default is using the colors from the owning sector.
|
||||
useowncolors_mid = <bool>; // Set to 1 to use the colors set in the sidedef. Default is true if uppercolor or lowercolor are set, otherwise it's false
|
||||
uppercolor_mid = <int>; // Material color of the top of the middle tier.
|
||||
lowercolor_mid = <int>; // Material color of the bottom of the middle tier. (Hardware rendering only.)
|
||||
|
||||
nogradient_bottom = <bool>; // disables color gradient on lower tier. (Hardware rendering only.)
|
||||
flipgradient_bottom = <bool>; // flips gradient colors on lower tier. (Hardware rendering only.)
|
||||
clampgradient_bottom = <bool>;// clamps gradient on lower tier to actual bounds (default is the entire front sector height, hardware rendering only.)
|
||||
useowncolors_bottom = <bool>; // Set to 1 to use the colors set in the sidedef. Default is using the colors from the owning sector.
|
||||
useowncolors_bottom = <bool>; // Set to 1 to use the colors set in the sidedef. Default is true if uppercolor or lowercolor are set, otherwise it's false
|
||||
uppercolor_bottom = <int>; // Material color of the top of the lower tier.
|
||||
lowercolor_bottom = <int>; // Material color of the bottom of the lower tier. (Hardware rendering only.)
|
||||
|
||||
|
|
@ -307,6 +307,8 @@ Note: All <bool> fields default to false unless mentioned otherwise.
|
|||
leakiness = <int>; // Probability of leaking through radiation suit (0 = never, 256 = always), default = 0.
|
||||
damageterraineffect = <bool>; // Will spawn a terrain splash when damage is inflicted. Default = false.
|
||||
damagehazard = <bool>; // Changes damage model to Strife's delayed damage for the given sector. Default = false.
|
||||
hurtmonsters = <bool>; // Non-players like monsters and decorations are hurt by this sector in the same manner as player. Doesn't work with damagehazard.
|
||||
harminair = <bool>; // Actors in this sector are harmed by the damage effects of the floor even if they aren't touching it.
|
||||
floorterrain = <string>; // Sets the terrain for the sector's floor. Default = 'use the flat texture's terrain definition.'
|
||||
ceilingterrain = <string>; // Sets the terrain for the sector's ceiling. Default = 'use the flat texture's terrain definition.'
|
||||
floor_reflect = <float>; // reflectiveness of floor (OpenGL only, not functional on sloped sectors)
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ static const char *TelefragSounds[] =
|
|||
#endif
|
||||
|
||||
static int LastAnnounceTime;
|
||||
static FRandom pr_bbannounce ("BBAnnounce");
|
||||
static FCRandom pr_bbannounce ("BBAnnounce");
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@
|
|||
#include "s_soundinternal.h"
|
||||
#include "i_time.h"
|
||||
|
||||
EXTERN_CVAR(Bool, cl_capfps)
|
||||
|
||||
class FBurnTexture : public FTexture
|
||||
{
|
||||
TArray<uint32_t> WorkBuffer;
|
||||
|
|
@ -163,6 +165,8 @@ protected:
|
|||
public:
|
||||
virtual ~Wiper();
|
||||
virtual bool Run(int ticks) = 0;
|
||||
virtual bool RunInterpolated(double ticks) { return true; };
|
||||
virtual bool Interpolatable() { return false; }
|
||||
virtual void SetTextures(FGameTexture* startscreen, FGameTexture* endscreen)
|
||||
{
|
||||
startScreen = startscreen;
|
||||
|
|
@ -177,9 +181,11 @@ class Wiper_Crossfade : public Wiper
|
|||
{
|
||||
public:
|
||||
bool Run(int ticks) override;
|
||||
bool RunInterpolated(double ticks) override;
|
||||
bool Interpolatable() override { return true; }
|
||||
|
||||
private:
|
||||
int Clock = 0;
|
||||
float Clock = 0;
|
||||
};
|
||||
|
||||
class Wiper_Melt : public Wiper
|
||||
|
|
@ -187,10 +193,12 @@ class Wiper_Melt : public Wiper
|
|||
public:
|
||||
Wiper_Melt();
|
||||
bool Run(int ticks) override;
|
||||
bool RunInterpolated(double ticks) override;
|
||||
bool Interpolatable() override { return true; }
|
||||
|
||||
private:
|
||||
enum { WIDTH = 320, HEIGHT = 200 };
|
||||
int y[WIDTH];
|
||||
double y[WIDTH];
|
||||
};
|
||||
|
||||
class Wiper_Burn : public Wiper
|
||||
|
|
@ -286,7 +294,23 @@ bool Wiper_Crossfade::Run(int ticks)
|
|||
Clock += ticks;
|
||||
DrawTexture(twod, startScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
|
||||
DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, DTA_Alpha, clamp(Clock / 32.f, 0.f, 1.f), TAG_DONE);
|
||||
return Clock >= 32;
|
||||
return Clock >= 32.;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenGLFrameBuffer :: Wiper_Crossfade :: Run
|
||||
//
|
||||
// Fades the old screen into the new one over 32 ticks.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool Wiper_Crossfade::RunInterpolated(double ticks)
|
||||
{
|
||||
Clock += ticks;
|
||||
DrawTexture(twod, startScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
|
||||
DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, DTA_Alpha, clamp(Clock / 32.f, 0.f, 1.f), TAG_DONE);
|
||||
return Clock >= 32.;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -300,7 +324,7 @@ Wiper_Melt::Wiper_Melt()
|
|||
y[0] = -(M_Random() & 15);
|
||||
for (int i = 1; i < WIDTH; ++i)
|
||||
{
|
||||
y[i] = clamp(y[i-1] + (M_Random() % 3) - 1, -15, 0);
|
||||
y[i] = clamp(y[i-1] + (double)(M_Random() % 3) - 1., -15., 0.);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,25 +349,25 @@ bool Wiper_Melt::Run(int ticks)
|
|||
{
|
||||
if (y[i] < HEIGHT)
|
||||
{
|
||||
if (y[i] < 0)
|
||||
y[i]++;
|
||||
else if (y[i] < 16)
|
||||
y[i] += y[i] + 1;
|
||||
if (y[i] < 0.)
|
||||
y[i] = y[i] + 1.;
|
||||
else if (y[i] < 16.)
|
||||
y[i] += y[i] + 1.;
|
||||
else
|
||||
y[i] = min<int>(y[i] + 8, HEIGHT);
|
||||
y[i] = min<double>(y[i] + 8., HEIGHT);
|
||||
done = false;
|
||||
}
|
||||
if (ticks == 0)
|
||||
{
|
||||
struct {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
double y;
|
||||
} dpt;
|
||||
struct {
|
||||
int32_t left;
|
||||
int32_t top;
|
||||
double top;
|
||||
int32_t right;
|
||||
int32_t bottom;
|
||||
double bottom;
|
||||
} rect;
|
||||
|
||||
// Only draw for the final tick.
|
||||
|
|
@ -351,7 +375,7 @@ bool Wiper_Melt::Run(int ticks)
|
|||
int w = startScreen->GetTexelWidth();
|
||||
int h = startScreen->GetTexelHeight();
|
||||
dpt.x = i * w / WIDTH;
|
||||
dpt.y = max(0, y[i] * h / HEIGHT);
|
||||
dpt.y = max(0., y[i] * (double)h / (double)HEIGHT);
|
||||
rect.left = dpt.x;
|
||||
rect.top = 0;
|
||||
rect.right = (i + 1) * w / WIDTH;
|
||||
|
|
@ -366,6 +390,77 @@ bool Wiper_Melt::Run(int ticks)
|
|||
return done;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Wiper_Melt :: RunInterpolated
|
||||
//
|
||||
// Melts the old screen into the new one over 32 ticks (interpolated).
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool Wiper_Melt::RunInterpolated(double ticks)
|
||||
{
|
||||
bool done = false;
|
||||
DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
|
||||
|
||||
// Copy the old screen in vertical strips on top of the new one.
|
||||
while (ticks > 0.)
|
||||
{
|
||||
done = true;
|
||||
for (int i = 0; i < WIDTH; i++)
|
||||
{
|
||||
if (y[i] < (double)HEIGHT)
|
||||
{
|
||||
if (ticks > 0. && ticks < 1.)
|
||||
{
|
||||
if (y[i] < 0)
|
||||
y[i] += ticks;
|
||||
else if (y[i] < 16)
|
||||
y[i] += (y[i] + 1) * ticks;
|
||||
else
|
||||
y[i] = min<double>(y[i] + (8 * ticks), (double)HEIGHT);
|
||||
}
|
||||
else if (y[i] < 0.)
|
||||
y[i] = y[i] + 1.;
|
||||
else if (y[i] < 16.)
|
||||
y[i] += y[i] + 1.;
|
||||
else
|
||||
y[i] = min<double>(y[i] + 8., HEIGHT);
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
ticks -= 1.;
|
||||
}
|
||||
for (int i = 0; i < WIDTH; i++)
|
||||
{
|
||||
struct {
|
||||
int32_t x;
|
||||
double y;
|
||||
} dpt;
|
||||
struct {
|
||||
int32_t left;
|
||||
double top;
|
||||
int32_t right;
|
||||
double bottom;
|
||||
} rect;
|
||||
|
||||
// Only draw for the final tick.
|
||||
int w = startScreen->GetTexelWidth();
|
||||
double h = startScreen->GetTexelHeight();
|
||||
dpt.x = i * w / WIDTH;
|
||||
dpt.y = max(0., y[i] * (double)h / (double)HEIGHT);
|
||||
rect.left = dpt.x;
|
||||
rect.top = 0;
|
||||
rect.right = (i + 1) * w / WIDTH;
|
||||
rect.bottom = h - dpt.y;
|
||||
if (rect.bottom > rect.top)
|
||||
{
|
||||
DrawTexture(twod, startScreen, 0, dpt.y, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_ClipLeft, rect.left, DTA_ClipRight, rect.right, DTA_Masked, false, TAG_DONE);
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenGLFrameBuffer :: Wiper_Burn Constructor
|
||||
|
|
@ -504,6 +599,7 @@ void PerformWipe(FTexture* startimg, FTexture* endimg, int wipe_type, bool stops
|
|||
{
|
||||
// wipe update
|
||||
uint64_t wipestart, nowtime, diff;
|
||||
double diff_frac;
|
||||
bool done;
|
||||
|
||||
GSnd->SetSfxPaused(true, 1);
|
||||
|
|
@ -519,20 +615,34 @@ void PerformWipe(FTexture* startimg, FTexture* endimg, int wipe_type, bool stops
|
|||
|
||||
do
|
||||
{
|
||||
do
|
||||
if (wiper->Interpolatable() && !cl_capfps)
|
||||
{
|
||||
I_WaitVBL(2);
|
||||
nowtime = I_msTime();
|
||||
diff = (nowtime - wipestart) * 40 / 1000; // Using 35 here feels too slow.
|
||||
} while (diff < 1);
|
||||
wipestart = nowtime;
|
||||
twod->Begin(screen->GetWidth(), screen->GetHeight());
|
||||
done = wiper->Run(1);
|
||||
if (overlaydrawer) overlaydrawer();
|
||||
twod->End();
|
||||
screen->Update();
|
||||
twod->OnFrameDone();
|
||||
|
||||
diff_frac = (nowtime - wipestart) * 40. / 1000.; // Using 35 here feels too slow.
|
||||
wipestart = nowtime;
|
||||
twod->Begin(screen->GetWidth(), screen->GetHeight());
|
||||
done = wiper->RunInterpolated(diff_frac);
|
||||
if (overlaydrawer) overlaydrawer();
|
||||
twod->End();
|
||||
screen->Update();
|
||||
twod->OnFrameDone();
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
I_WaitVBL(2);
|
||||
nowtime = I_msTime();
|
||||
diff = (nowtime - wipestart) * 40 / 1000; // Using 35 here feels too slow.
|
||||
} while (diff < 1);
|
||||
wipestart = nowtime;
|
||||
twod->Begin(screen->GetWidth(), screen->GetHeight());
|
||||
done = wiper->Run(1);
|
||||
if (overlaydrawer) overlaydrawer();
|
||||
twod->End();
|
||||
screen->Update();
|
||||
twod->OnFrameDone();
|
||||
}
|
||||
} while (!done);
|
||||
delete wiper;
|
||||
I_FreezeTime(false);
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ CUSTOM_CVAR(Int, snd_samplerate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
|||
CVAR(Int, snd_buffersize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, snd_hrtf, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
CVAR(Float, snd_footstepvolume, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
|
||||
|
||||
#if !defined(NO_OPENAL)
|
||||
#define DEF_BACKEND "openal"
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ enum
|
|||
{
|
||||
DEFAULT_PITCH = 128,
|
||||
};
|
||||
static FRandom pr_soundpitch ("SoundPitch");
|
||||
static FCRandom pr_soundpitch ("SoundPitch");
|
||||
SoundEngine* soundEngine;
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
|||
|
|
@ -132,7 +132,8 @@ enum
|
|||
PRE_CONACK, // Sent from host to guest to acknowledge PRE_CONNECT receipt
|
||||
PRE_ALLFULL, // Sent from host to an unwanted guest
|
||||
PRE_ALLHEREACK, // Sent from guest to host to acknowledge PRE_ALLHEREACK receipt
|
||||
PRE_GO // Sent from host to guest to continue game startup
|
||||
PRE_GO, // Sent from host to guest to continue game startup
|
||||
PRE_IN_PROGRESS, // Sent from host to guest if the game has already started
|
||||
};
|
||||
|
||||
// Set PreGamePacket.fake to this so that the game rejects any pregame packets
|
||||
|
|
@ -269,6 +270,8 @@ void PacketSend (void)
|
|||
// I_Error ("SendPacket error: %s",strerror(errno));
|
||||
}
|
||||
|
||||
void PreSend(const void* buffer, int bufferlen, const sockaddr_in* to);
|
||||
void SendConAck(int num_connected, int num_needed);
|
||||
|
||||
//
|
||||
// PacketGet
|
||||
|
|
@ -303,7 +306,7 @@ void PacketGet (void)
|
|||
GetPlayerName(node).GetChars());
|
||||
}
|
||||
|
||||
doomcom.data[0] = 0x80; // NCMD_EXIT
|
||||
doomcom.data[0] = NCMD_EXIT;
|
||||
c = 1;
|
||||
}
|
||||
else if (err != WSAEWOULDBLOCK)
|
||||
|
|
@ -341,10 +344,11 @@ void PacketGet (void)
|
|||
}
|
||||
else if (c > 0)
|
||||
{ //The packet is not from any in-game node, so we might as well discard it.
|
||||
// Don't show the message for disconnect notifications.
|
||||
if (c != 2 || TransmitBuffer[0] != PRE_FAKE || TransmitBuffer[1] != PRE_DISCONNECT)
|
||||
if (TransmitBuffer[0] == PRE_FAKE)
|
||||
{
|
||||
DPrintf(DMSG_WARNING, "Dropped packet: Unknown host (%s:%d)\n", inet_ntoa(fromaddress.sin_addr), fromaddress.sin_port);
|
||||
// If it's someone waiting in the lobby, let them know the game already started
|
||||
uint8_t msg[] = { PRE_FAKE, PRE_IN_PROGRESS };
|
||||
PreSend(msg, 2, &fromaddress);
|
||||
}
|
||||
doomcom.remotenode = -1;
|
||||
return;
|
||||
|
|
@ -369,7 +373,22 @@ sockaddr_in *PreGet (void *buffer, int bufferlen, bool noabort)
|
|||
int err = WSAGetLastError();
|
||||
if (err == WSAEWOULDBLOCK || (noabort && err == WSAECONNRESET))
|
||||
return NULL; // no packet
|
||||
I_Error ("PreGet: %s", neterror ());
|
||||
|
||||
if (doomcom.consoleplayer == 0)
|
||||
{
|
||||
int node = FindNode(&fromaddress);
|
||||
I_NetMessage("Got unexpected disconnect.");
|
||||
doomcom.numnodes--;
|
||||
for (; node < doomcom.numnodes; ++node)
|
||||
sendaddress[node] = sendaddress[node + 1];
|
||||
|
||||
// Let remaining guests know that somebody left.
|
||||
SendConAck(doomcom.numnodes, doomcom.numplayers);
|
||||
}
|
||||
else
|
||||
{
|
||||
I_NetError("The host disbanded the game unexpectedly");
|
||||
}
|
||||
}
|
||||
return &fromaddress;
|
||||
}
|
||||
|
|
@ -499,7 +518,7 @@ void SendAbort (void)
|
|||
}
|
||||
}
|
||||
|
||||
static void SendConAck (int num_connected, int num_needed)
|
||||
void SendConAck (int num_connected, int num_needed)
|
||||
{
|
||||
PreGamePacket packet;
|
||||
|
||||
|
|
@ -708,7 +727,7 @@ bool HostGame (int i)
|
|||
|
||||
doomcom.numnodes = 1;
|
||||
|
||||
I_NetInit ("Waiting for players", numplayers);
|
||||
I_NetInit ("Hosting game", numplayers);
|
||||
|
||||
// Wait for numplayers-1 different connections
|
||||
if (!I_NetLoop (Host_CheckForConnects, (void *)(intptr_t)numplayers))
|
||||
|
|
@ -783,13 +802,15 @@ bool Guest_ContactHost (void *userdata)
|
|||
}
|
||||
else if (packet.Message == PRE_DISCONNECT)
|
||||
{
|
||||
doomcom.numnodes = 0;
|
||||
I_FatalError ("The host cancelled the game.");
|
||||
I_NetError("The host cancelled the game.");
|
||||
}
|
||||
else if (packet.Message == PRE_ALLFULL)
|
||||
{
|
||||
doomcom.numnodes = 0;
|
||||
I_FatalError ("The game is full.");
|
||||
I_NetError("The game is full.");
|
||||
}
|
||||
else if (packet.Message == PRE_IN_PROGRESS)
|
||||
{
|
||||
I_NetError("The game was already started.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -850,7 +871,7 @@ bool Guest_WaitForOthers (void *userdata)
|
|||
return true;
|
||||
|
||||
case PRE_DISCONNECT:
|
||||
I_FatalError ("The host cancelled the game.");
|
||||
I_NetError("The host cancelled the game.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -875,6 +896,7 @@ bool JoinGame (int i)
|
|||
BuildAddress (&sendaddress[1], Args->GetArg(i+1));
|
||||
sendplayer[1] = 0;
|
||||
doomcom.numnodes = 2;
|
||||
doomcom.consoleplayer = -1;
|
||||
|
||||
|
||||
// Let host know we are here
|
||||
|
|
@ -1046,6 +1068,13 @@ void I_NetMessage(const char* text, ...)
|
|||
#endif
|
||||
}
|
||||
|
||||
void I_NetError(const char* error)
|
||||
{
|
||||
doomcom.numnodes = 0;
|
||||
StartWindow->NetClose();
|
||||
I_FatalError(error);
|
||||
}
|
||||
|
||||
// todo: later these must be dispatched by the main menu, not the start screen.
|
||||
void I_NetProgress(int val)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
int I_InitNetwork (void);
|
||||
void I_NetCmd (void);
|
||||
void I_NetMessage(const char*, ...);
|
||||
void I_NetError(const char* error);
|
||||
void I_NetProgress(int val);
|
||||
void I_NetInit(const char* msg, int num);
|
||||
bool I_NetLoop(bool (*timer_callback)(void*), void* userdata);
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ FRandom pr_exrandom("EX_Random");
|
|||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
FRandom M_Random;
|
||||
FCRandom M_Random;
|
||||
|
||||
// Global seed. This is modified predictably to initialize every RNG.
|
||||
uint32_t rngseed;
|
||||
|
|
@ -126,8 +126,8 @@ CCMD(rngseed)
|
|||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
FRandom *FRandom::RNGList;
|
||||
static TDeletingArray<FRandom *> NewRNGs;
|
||||
FRandom *FRandom::RNGList, *FRandom::CRNGList;
|
||||
static TDeletingArray<FRandom *> NewRNGs, NewCRNGs;
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
|
|
@ -139,14 +139,22 @@ static TDeletingArray<FRandom *> NewRNGs;
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FRandom::FRandom ()
|
||||
: NameCRC (0)
|
||||
FRandom::FRandom (bool client)
|
||||
: NameCRC (0), bClient(client)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
Name = NULL;
|
||||
#endif
|
||||
Next = RNGList;
|
||||
RNGList = this;
|
||||
if (bClient)
|
||||
{
|
||||
Next = CRNGList;
|
||||
CRNGList = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Next = RNGList;
|
||||
RNGList = this;
|
||||
}
|
||||
Init(0);
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +166,7 @@ FRandom::FRandom ()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FRandom::FRandom (const char *name)
|
||||
FRandom::FRandom (const char *name, bool client) : bClient(client)
|
||||
{
|
||||
NameCRC = CalcCRC32 ((const uint8_t *)name, (unsigned int)strlen (name));
|
||||
#ifndef NDEBUG
|
||||
|
|
@ -170,7 +178,7 @@ FRandom::FRandom (const char *name)
|
|||
#endif
|
||||
|
||||
// Insert the RNG in the list, sorted by CRC
|
||||
FRandom **prev = &RNGList, *probe = RNGList;
|
||||
FRandom **prev = (bClient ? &CRNGList : &RNGList), * probe = (bClient ? CRNGList : RNGList);
|
||||
|
||||
while (probe != NULL && probe->NameCRC < NameCRC)
|
||||
{
|
||||
|
|
@ -205,8 +213,8 @@ FRandom::~FRandom ()
|
|||
|
||||
FRandom *last = NULL;
|
||||
|
||||
prev = &RNGList;
|
||||
rng = RNGList;
|
||||
prev = bClient ? &CRNGList : &RNGList;
|
||||
rng = bClient ? CRNGList : RNGList;
|
||||
|
||||
while (rng != NULL && rng != this)
|
||||
{
|
||||
|
|
@ -237,6 +245,11 @@ void FRandom::StaticClearRandom ()
|
|||
{
|
||||
rng->Init(rngseed);
|
||||
}
|
||||
|
||||
for (FRandom* rng = FRandom::CRNGList; rng != NULL; rng = rng->Next)
|
||||
{
|
||||
rng->Init(rngseed);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -345,15 +358,15 @@ void FRandom::StaticReadRNGState(FSerializer &arc)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FRandom *FRandom::StaticFindRNG (const char *name)
|
||||
FRandom *FRandom::StaticFindRNG (const char *name, bool client)
|
||||
{
|
||||
uint32_t NameCRC = CalcCRC32 ((const uint8_t *)name, (unsigned int)strlen (name));
|
||||
|
||||
// Use the default RNG if this one happens to have a CRC of 0.
|
||||
if (NameCRC == 0) return &pr_exrandom;
|
||||
if (NameCRC == 0) return client ? &M_Random : &pr_exrandom;
|
||||
|
||||
// Find the RNG in the list, sorted by CRC
|
||||
FRandom **prev = &RNGList, *probe = RNGList;
|
||||
FRandom **prev = (client ? &CRNGList : &RNGList), *probe = (client ? CRNGList : RNGList);
|
||||
|
||||
while (probe != NULL && probe->NameCRC < NameCRC)
|
||||
{
|
||||
|
|
@ -364,14 +377,32 @@ FRandom *FRandom::StaticFindRNG (const char *name)
|
|||
if (probe == NULL || probe->NameCRC != NameCRC)
|
||||
{
|
||||
// A matching RNG doesn't exist yet so create it.
|
||||
probe = new FRandom(name);
|
||||
probe = new FRandom(name, client);
|
||||
|
||||
// Store the new RNG for destruction when ZDoom quits.
|
||||
NewRNGs.Push(probe);
|
||||
if (client)
|
||||
NewCRNGs.Push(probe);
|
||||
else
|
||||
NewRNGs.Push(probe);
|
||||
}
|
||||
return probe;
|
||||
}
|
||||
|
||||
void FRandom::SaveRNGState(TArray<FRandom>& backups)
|
||||
{
|
||||
for (auto cur = RNGList; cur != nullptr; cur = cur->Next)
|
||||
backups.Push(*cur);
|
||||
}
|
||||
|
||||
void FRandom::RestoreRNGState(TArray<FRandom>& backups)
|
||||
{
|
||||
unsigned int i = 0u;
|
||||
for (auto cur = RNGList; cur != nullptr; cur = cur->Next)
|
||||
*cur = backups[i++];
|
||||
|
||||
backups.Clear();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FRandom :: StaticPrintSeeds
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@ class FSerializer;
|
|||
class FRandom : public SFMTObj
|
||||
{
|
||||
public:
|
||||
FRandom ();
|
||||
FRandom (const char *name);
|
||||
~FRandom ();
|
||||
FRandom() : FRandom(false) {}
|
||||
FRandom(const char* name) : FRandom(name, false) {}
|
||||
~FRandom();
|
||||
|
||||
int Seed() const
|
||||
{
|
||||
|
|
@ -170,20 +170,34 @@ public:
|
|||
static void StaticClearRandom ();
|
||||
static void StaticReadRNGState (FSerializer &arc);
|
||||
static void StaticWriteRNGState (FSerializer &file);
|
||||
static FRandom *StaticFindRNG(const char *name);
|
||||
static FRandom *StaticFindRNG(const char *name, bool client);
|
||||
static void SaveRNGState(TArray<FRandom>& backups);
|
||||
static void RestoreRNGState(TArray<FRandom>& backups);
|
||||
|
||||
#ifndef NDEBUG
|
||||
static void StaticPrintSeeds ();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
FRandom(bool client);
|
||||
FRandom(const char* name, bool client);
|
||||
|
||||
private:
|
||||
#ifndef NDEBUG
|
||||
const char *Name;
|
||||
#endif
|
||||
FRandom *Next;
|
||||
uint32_t NameCRC;
|
||||
bool bClient;
|
||||
|
||||
static FRandom *RNGList;
|
||||
static FRandom *RNGList, *CRNGList;
|
||||
};
|
||||
|
||||
class FCRandom : public FRandom
|
||||
{
|
||||
public:
|
||||
FCRandom() : FRandom(true) {}
|
||||
FCRandom(const char* name) : FRandom(name, true) {}
|
||||
};
|
||||
|
||||
extern uint32_t rngseed; // The starting seed (not part of state)
|
||||
|
|
@ -193,6 +207,6 @@ extern bool use_staticrng;
|
|||
|
||||
|
||||
// M_Random can be used for numbers that do not affect gameplay
|
||||
extern FRandom M_Random;
|
||||
extern FCRandom M_Random;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -41,6 +41,12 @@ xx(Random2)
|
|||
xx(RandomPick)
|
||||
xx(FRandomPick)
|
||||
xx(SetRandomSeed)
|
||||
xx(CRandom)
|
||||
xx(CFRandom)
|
||||
xx(CRandom2)
|
||||
xx(CRandomPick)
|
||||
xx(CFRandomPick)
|
||||
xx(CSetRandomSeed)
|
||||
xx(BuiltinRandomSeed)
|
||||
xx(BuiltinNew)
|
||||
xx(GetClass)
|
||||
|
|
|
|||
|
|
@ -252,7 +252,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelOverride &
|
|||
FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimModelOverride &mo, struct AnimModelOverride *def);
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnim &ao, ModelAnim *def);
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnimFrame &ao, ModelAnimFrame *def);
|
||||
FSerializer& Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval);
|
||||
FSerializer &Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval);
|
||||
FSerializer &Serialize(FSerializer& arc, const char* key, struct FStandaloneAnimation& value, struct FStandaloneAnimation* defval);
|
||||
|
||||
void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p);
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ public:
|
|||
virtual void NetInit(const char *message, int num_players) {}
|
||||
virtual void NetProgress(int count) {}
|
||||
virtual void NetDone() {}
|
||||
virtual void NetClose() {}
|
||||
virtual bool NetLoop(bool (*timer_callback)(void *), void *userdata) { return false; }
|
||||
virtual void AppendStatusLine(const char* status) {}
|
||||
virtual void LoadingStatus(const char* message, int colors) {}
|
||||
|
|
@ -74,6 +75,7 @@ public:
|
|||
void NetProgress(int count);
|
||||
void NetMessage(const char* format, ...); // cover for printf
|
||||
void NetDone();
|
||||
void NetClose();
|
||||
bool NetLoop(bool (*timer_callback)(void*), void* userdata);
|
||||
protected:
|
||||
int NetMaxPos, NetCurPos;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
#include "symbols.h"
|
||||
#include "types.h"
|
||||
|
||||
#include "p_visualthinker.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
|
@ -421,7 +423,7 @@ PClass *PClass::FindClass (FName zaname)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
DObject *PClass::CreateNew()
|
||||
DObject *PClass::CreateNew(int *statnum)
|
||||
{
|
||||
uint8_t *mem = (uint8_t *)M_Malloc (Size);
|
||||
assert (mem != nullptr);
|
||||
|
|
@ -444,6 +446,12 @@ DObject *PClass::CreateNew()
|
|||
|
||||
((DObject *)mem)->SetClass (const_cast<PClass *>(this));
|
||||
InitializeSpecials(mem, Defaults, &PClass::SpecialInits);
|
||||
|
||||
if(statnum && ((DObject *)mem)->IsKindOf(RUNTIME_CLASS(DVisualThinker)))
|
||||
{
|
||||
*statnum = STAT_VISUALTHINKER;
|
||||
}
|
||||
|
||||
return (DObject *)mem;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ public:
|
|||
PClass();
|
||||
~PClass();
|
||||
void InsertIntoHash(bool native);
|
||||
DObject *CreateNew();
|
||||
DObject *CreateNew(int *statnum = nullptr);
|
||||
PClass *CreateDerivedClass(FName name, unsigned int size, bool *newlycreated = nullptr, int fileno = 0);
|
||||
|
||||
void InitializeActorInfo();
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ public:
|
|||
void NetInit(const char* message, int playerCount);
|
||||
void NetProgress(int count);
|
||||
void NetDone();
|
||||
void NetClose();
|
||||
|
||||
private:
|
||||
NSWindow* m_window;
|
||||
|
|
|
|||
|
|
@ -531,3 +531,8 @@ void FConsoleWindow::NetDone()
|
|||
m_netAbortButton = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void FConsoleWindow::NetClose()
|
||||
{
|
||||
// TODO: Implement this
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,11 @@ void FBasicStartupScreen::NetDone()
|
|||
FConsoleWindow::GetInstance().NetDone();
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetClose()
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetClose();
|
||||
}
|
||||
|
||||
bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const userData)
|
||||
{
|
||||
while (true)
|
||||
|
|
|
|||
|
|
@ -54,17 +54,6 @@ bool I_WriteIniFailed (const char* filename);
|
|||
class FGameTexture;
|
||||
bool I_SetCursor(FGameTexture *);
|
||||
|
||||
static inline char *strlwr(char *str)
|
||||
{
|
||||
char *ptr = str;
|
||||
while(*ptr)
|
||||
{
|
||||
*ptr = tolower(*ptr);
|
||||
++ptr;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
inline int I_GetNumaNodeCount() { return 1; }
|
||||
inline int I_GetNumaNodeThreadCount(int numaNode) { return std::max<int>(std::thread::hardware_concurrency(), 1); }
|
||||
inline void I_SetThreadNumaNode(std::thread &thread, int numaNode) { }
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class FTTYStartupScreen : public FStartupScreen
|
|||
void NetInit(const char *message, int num_players);
|
||||
void NetProgress(int count);
|
||||
void NetDone();
|
||||
void NetClose();
|
||||
bool NetLoop(bool (*timer_callback)(void *), void *userdata);
|
||||
protected:
|
||||
bool DidNetInit;
|
||||
|
|
@ -237,6 +238,11 @@ void FTTYStartupScreen::NetProgress(int count)
|
|||
}
|
||||
}
|
||||
|
||||
void FTTYStartupScreen::NetClose()
|
||||
{
|
||||
// TODO: Implement this
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: NetLoop
|
||||
|
|
|
|||
|
|
@ -201,3 +201,8 @@ bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata
|
|||
{
|
||||
return NetStartWindow::RunMessageLoop(timer_callback, userdata);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetClose()
|
||||
{
|
||||
NetStartWindow::CloseNetStartPane();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8268,8 +8268,12 @@ static bool CheckFunctionCompatiblity(FScriptPosition &ScriptPosition, PFunction
|
|||
FxFunctionCall::FxFunctionCall(FName methodname, FName rngname, FArgumentList &&args, const FScriptPosition &pos)
|
||||
: FxExpression(EFX_FunctionCall, pos)
|
||||
{
|
||||
const bool isClient = methodname == NAME_CRandom || methodname == NAME_CFRandom
|
||||
|| methodname == NAME_CRandomPick || methodname == NAME_CFRandomPick
|
||||
|| methodname == NAME_CRandom2 || methodname == NAME_CSetRandomSeed;
|
||||
|
||||
MethodName = methodname;
|
||||
RNG = &pr_exrandom;
|
||||
RNG = isClient ? &M_Random : &pr_exrandom;
|
||||
ArgList = std::move(args);
|
||||
if (rngname != NAME_None)
|
||||
{
|
||||
|
|
@ -8281,7 +8285,16 @@ FxFunctionCall::FxFunctionCall(FName methodname, FName rngname, FArgumentList &&
|
|||
case NAME_FRandomPick:
|
||||
case NAME_Random2:
|
||||
case NAME_SetRandomSeed:
|
||||
RNG = FRandom::StaticFindRNG(rngname.GetChars());
|
||||
RNG = FRandom::StaticFindRNG(rngname.GetChars(), false);
|
||||
break;
|
||||
|
||||
case NAME_CRandom:
|
||||
case NAME_CFRandom:
|
||||
case NAME_CRandomPick:
|
||||
case NAME_CFRandomPick:
|
||||
case NAME_CRandom2:
|
||||
case NAME_CSetRandomSeed:
|
||||
RNG = FRandom::StaticFindRNG(rngname.GetChars(), true);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -8547,13 +8560,22 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
break;
|
||||
|
||||
case NAME_CSetRandomSeed:
|
||||
if (CheckArgSize(NAME_CRandom, ArgList, 1, 1, ScriptPosition))
|
||||
{
|
||||
func = new FxRandomSeed(RNG, ArgList[0], ScriptPosition, ctx.FromDecorate);
|
||||
ArgList[0] = nullptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME_Random:
|
||||
case NAME_CRandom:
|
||||
// allow calling Random without arguments to default to (0, 255)
|
||||
if (ArgList.Size() == 0)
|
||||
{
|
||||
func = new FxRandom(RNG, new FxConstant(0, ScriptPosition), new FxConstant(255, ScriptPosition), ScriptPosition, ctx.FromDecorate);
|
||||
}
|
||||
else if (CheckArgSize(NAME_Random, ArgList, 2, 2, ScriptPosition))
|
||||
else if (CheckArgSize(MethodName, ArgList, 2, 2, ScriptPosition))
|
||||
{
|
||||
func = new FxRandom(RNG, ArgList[0], ArgList[1], ScriptPosition, ctx.FromDecorate);
|
||||
ArgList[0] = ArgList[1] = nullptr;
|
||||
|
|
@ -8561,7 +8583,8 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
break;
|
||||
|
||||
case NAME_FRandom:
|
||||
if (CheckArgSize(NAME_FRandom, ArgList, 2, 2, ScriptPosition))
|
||||
case NAME_CFRandom:
|
||||
if (CheckArgSize(MethodName, ArgList, 2, 2, ScriptPosition))
|
||||
{
|
||||
func = new FxFRandom(RNG, ArgList[0], ArgList[1], ScriptPosition);
|
||||
ArgList[0] = ArgList[1] = nullptr;
|
||||
|
|
@ -8570,14 +8593,17 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
|
||||
case NAME_RandomPick:
|
||||
case NAME_FRandomPick:
|
||||
case NAME_CRandomPick:
|
||||
case NAME_CFRandomPick:
|
||||
if (CheckArgSize(MethodName, ArgList, 1, -1, ScriptPosition))
|
||||
{
|
||||
func = new FxRandomPick(RNG, ArgList, MethodName == NAME_FRandomPick, ScriptPosition, ctx.FromDecorate);
|
||||
func = new FxRandomPick(RNG, ArgList, MethodName == NAME_FRandomPick || MethodName == NAME_CFRandomPick, ScriptPosition, ctx.FromDecorate);
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME_Random2:
|
||||
if (CheckArgSize(NAME_Random2, ArgList, 0, 1, ScriptPosition))
|
||||
case NAME_CRandom2:
|
||||
if (CheckArgSize(MethodName, ArgList, 0, 1, ScriptPosition))
|
||||
{
|
||||
func = new FxRandom2(RNG, ArgList.Size() == 0? nullptr : ArgList[0], ScriptPosition, ctx.FromDecorate);
|
||||
if (ArgList.Size() > 0) ArgList[0] = nullptr;
|
||||
|
|
|
|||
|
|
@ -879,10 +879,18 @@ void FFunctionBuildList::Build()
|
|||
{
|
||||
if (!item.Code->CheckReturn())
|
||||
{
|
||||
auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
|
||||
newcmpd->Add(item.Code);
|
||||
newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
|
||||
item.Code = newcmpd->Resolve(ctx);
|
||||
if (ctx.ReturnProto == nullptr || !ctx.ReturnProto->ReturnTypes.Size())
|
||||
{
|
||||
auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
|
||||
newcmpd->Add(item.Code);
|
||||
newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
|
||||
item.Code = newcmpd->Resolve(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Code->ScriptPosition.Message(MSG_ERROR, "Missing return statement in %s", item.PrintableName.GetChars());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
item.Proto = ctx.ReturnProto;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include "c_cvars.h"
|
||||
#include "c_bind.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "m_misc.h"
|
||||
|
||||
#include "menu.h"
|
||||
#include "vm.h"
|
||||
|
|
@ -1032,6 +1033,17 @@ DEFINE_ACTION_FUNCTION(_CVar, FindCVar)
|
|||
ACTION_RETURN_POINTER(FindCVar(name.GetChars(), nullptr));
|
||||
}
|
||||
|
||||
static int SaveConfig()
|
||||
{
|
||||
return M_SaveDefaults(nullptr);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_CVar, SaveConfig, SaveConfig)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
ACTION_RETURN_INT(M_SaveDefaults(nullptr));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ void NetStartWindow::HideNetStartPane()
|
|||
Instance = nullptr;
|
||||
}
|
||||
|
||||
void NetStartWindow::CloseNetStartPane()
|
||||
{
|
||||
NetStartWindow::NetClose();
|
||||
}
|
||||
|
||||
void NetStartWindow::SetNetStartProgress(int pos)
|
||||
{
|
||||
if (Instance)
|
||||
|
|
@ -64,6 +69,12 @@ bool NetStartWindow::RunMessageLoop(bool (*newtimer_callback)(void*), void* newu
|
|||
return Instance->exitreason;
|
||||
}
|
||||
|
||||
void NetStartWindow::NetClose()
|
||||
{
|
||||
if (Instance != nullptr)
|
||||
Instance->OnClose();
|
||||
}
|
||||
|
||||
NetStartWindow::NetStartWindow() : Widget(nullptr, WidgetType::Window)
|
||||
{
|
||||
SetWindowBackground(Colorf::fromRgba8(51, 51, 51));
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ class NetStartWindow : public Widget
|
|||
public:
|
||||
static void ShowNetStartPane(const char* message, int maxpos);
|
||||
static void HideNetStartPane();
|
||||
static void CloseNetStartPane();
|
||||
static void SetNetStartProgress(int pos);
|
||||
static bool RunMessageLoop(bool (*timer_callback)(void*), void* userdata);
|
||||
static void NetClose();
|
||||
|
||||
private:
|
||||
NetStartWindow();
|
||||
|
|
|
|||
|
|
@ -701,6 +701,36 @@ void EventManager::WorldThingDied(AActor* actor, AActor* inflictor)
|
|||
handler->WorldThingDied(actor, inflictor);
|
||||
}
|
||||
|
||||
bool EventManager::WorldHitscanPreFired(AActor* actor, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, double sz, double offsetforward, double offsetside)
|
||||
{
|
||||
// don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever.
|
||||
if (actor->ObjectFlags & OF_EuthanizeMe)
|
||||
return false;
|
||||
|
||||
bool ret = false;
|
||||
if (ShouldCallStatic(true)) ret = staticEventManager.WorldHitscanPreFired(actor, angle, distance, pitch, damage, damageType, pufftype, flags, sz, offsetforward, offsetside);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
for (DStaticEventHandler* handler = FirstEventHandler; handler && ret == false; handler = handler->next)
|
||||
ret = handler->WorldHitscanPreFired(actor, angle, distance, pitch, damage, damageType, pufftype, flags, sz, offsetforward, offsetside);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EventManager::WorldHitscanFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags)
|
||||
{
|
||||
// don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever.
|
||||
if (actor->ObjectFlags & OF_EuthanizeMe)
|
||||
return;
|
||||
|
||||
if (ShouldCallStatic(true)) staticEventManager.WorldHitscanFired(actor, AttackPos, DamagePosition, Inflictor, flags);
|
||||
|
||||
for (DStaticEventHandler* handler = FirstEventHandler; handler; handler = handler->next)
|
||||
handler->WorldHitscanFired(actor, AttackPos, DamagePosition, Inflictor, flags);
|
||||
}
|
||||
|
||||
void EventManager::WorldThingGround(AActor* actor, FState* st)
|
||||
{
|
||||
// don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever.
|
||||
|
|
@ -1026,6 +1056,14 @@ DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamagePosition);
|
|||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageIsRadius);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, NewDamage);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, CrushedState);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackPos);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackAngle);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackPitch);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackDistance);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackOffsetForward);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackOffsetSide);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackZ);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackPuffType);
|
||||
|
||||
DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, PlayerNumber);
|
||||
DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, IsReturn);
|
||||
|
|
@ -1729,6 +1767,51 @@ void DStaticEventHandler::WorldThingDied(AActor* actor, AActor* inflictor)
|
|||
}
|
||||
}
|
||||
|
||||
bool DStaticEventHandler::WorldHitscanPreFired(AActor* actor, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, double sz, double offsetforward, double offsetside)
|
||||
{
|
||||
IFVIRTUAL(DStaticEventHandler, WorldHitscanPreFired)
|
||||
{
|
||||
// don't create excessive DObjects if not going to be processed anyway
|
||||
if (isEmpty(func)) return false;
|
||||
FWorldEvent e = owner->SetupWorldEvent();
|
||||
e.Thing = actor;
|
||||
e.AttackAngle = angle;
|
||||
e.AttackPitch = pitch;
|
||||
e.AttackDistance = distance;
|
||||
e.Damage = damage;
|
||||
e.DamageType = damageType;
|
||||
e.AttackPuffType = pufftype;
|
||||
e.AttackOffsetForward = offsetforward;
|
||||
e.AttackOffsetSide = offsetside;
|
||||
e.AttackZ = sz;
|
||||
e.DamageFlags = flags;
|
||||
int processed;
|
||||
VMReturn results[1] = { &processed };
|
||||
VMValue params[2] = { (DStaticEventHandler*)this, &e };
|
||||
VMCall(func, params, 2, results, 1);
|
||||
return !!processed;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DStaticEventHandler::WorldHitscanFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags)
|
||||
{
|
||||
IFVIRTUAL(DStaticEventHandler, WorldHitscanFired)
|
||||
{
|
||||
// don't create excessive DObjects if not going to be processed anyway
|
||||
if (isEmpty(func)) return;
|
||||
FWorldEvent e = owner->SetupWorldEvent();
|
||||
e.Thing = actor;
|
||||
e.AttackPos = AttackPos;
|
||||
e.DamagePosition = DamagePosition;
|
||||
e.Inflictor = Inflictor;
|
||||
e.DamageFlags = flags;
|
||||
VMValue params[2] = { (DStaticEventHandler*)this, &e };
|
||||
VMCall(func, params, 2, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void DStaticEventHandler::WorldThingGround(AActor* actor, FState* st)
|
||||
{
|
||||
IFVIRTUAL(DStaticEventHandler, WorldThingGround)
|
||||
|
|
@ -1743,7 +1826,6 @@ void DStaticEventHandler::WorldThingGround(AActor* actor, FState* st)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void DStaticEventHandler::WorldThingRevived(AActor* actor)
|
||||
{
|
||||
IFVIRTUAL(DStaticEventHandler, WorldThingRevived)
|
||||
|
|
|
|||
14
src/events.h
Executable file → Normal file
|
|
@ -311,6 +311,8 @@ public:
|
|||
void WorldThingRevived(AActor* actor);
|
||||
void WorldThingDamaged(AActor* actor, AActor* inflictor, AActor* source, int damage, FName mod, int flags, DAngle angle);
|
||||
void WorldThingDestroyed(AActor* actor);
|
||||
bool WorldHitscanPreFired(AActor* actor, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, double sz, double offsetforward, double offsetside);
|
||||
void WorldHitscanFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags);
|
||||
void WorldLinePreActivated(line_t* line, AActor* actor, int activationType, bool* shouldactivate);
|
||||
void WorldLineActivated(line_t* line, AActor* actor, int activationType);
|
||||
int WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius);
|
||||
|
|
@ -393,6 +395,14 @@ struct FWorldEvent
|
|||
bool DamageIsRadius; // radius damage yes/no
|
||||
int NewDamage = 0; // sector/line damaged. allows modifying damage
|
||||
FState* CrushedState = nullptr; // custom crush state set in thingground
|
||||
DVector3 AttackPos; //hitscan point of origin
|
||||
DAngle AttackAngle;
|
||||
DAngle AttackPitch;
|
||||
double AttackDistance = 0;
|
||||
double AttackOffsetForward = 0;
|
||||
double AttackOffsetSide = 0;
|
||||
double AttackZ = 0;
|
||||
PClassActor* AttackPuffType = nullptr;
|
||||
};
|
||||
|
||||
struct FPlayerEvent
|
||||
|
|
@ -467,6 +477,10 @@ struct EventManager
|
|||
void WorldThingSpawned(AActor* actor);
|
||||
// called after AActor::Die of each actor.
|
||||
void WorldThingDied(AActor* actor, AActor* inflictor);
|
||||
// called when a hitscan attack is fired (can be overridden to block it)
|
||||
bool WorldHitscanPreFired(AActor* actor, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, double sz, double offsetforward, double offsetside);
|
||||
// called when a hitscan attack has been fired
|
||||
void WorldHitscanFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags);
|
||||
// called inside AActor::Grind just before the corpse is destroyed
|
||||
void WorldThingGround(AActor* actor, FState* st);
|
||||
// called after AActor::Revive.
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@
|
|||
static FRandom pr_dmspawn ("DMSpawn");
|
||||
static FRandom pr_pspawn ("PlayerSpawn");
|
||||
|
||||
extern int startpos, laststartpos;
|
||||
|
||||
bool WriteZip(const char* filename, const FileSys::FCompressedBuffer* content, size_t contentcount);
|
||||
bool G_CheckDemoStatus (void);
|
||||
void G_ReadDemoTiccmd (ticcmd_t *cmd, int player);
|
||||
|
|
@ -2146,7 +2148,9 @@ void G_DoLoadGame ()
|
|||
|
||||
arc("ticrate", time[0])
|
||||
("leveltime", time[1])
|
||||
("globalfreeze", globalfreeze);
|
||||
("globalfreeze", globalfreeze)
|
||||
("startpos", startpos)
|
||||
("laststartpos", laststartpos);
|
||||
// dearchive all the modifications
|
||||
level.time = Scale(time[1], TICRATE, time[0]);
|
||||
|
||||
|
|
@ -2437,6 +2441,10 @@ void G_DoSaveGame (bool okForQuicksave, bool forceQuicksave, FString filename, c
|
|||
savegameglobals("leveltime", level.time);
|
||||
}
|
||||
|
||||
savegameglobals("globalfreeze", globalfreeze)
|
||||
("startpos", startpos)
|
||||
("laststartpos", laststartpos);
|
||||
|
||||
STAT_Serialize(savegameglobals);
|
||||
FRandom::StaticWriteRNGState(savegameglobals);
|
||||
P_WriteACSDefereds(savegameglobals);
|
||||
|
|
|
|||
|
|
@ -111,6 +111,8 @@ EXTERN_CVAR (Int, disableautosave)
|
|||
EXTERN_CVAR (String, playerclass)
|
||||
|
||||
extern uint8_t globalfreeze, globalchangefreeze;
|
||||
int startpos = 0; // [RH] Support for multiple starts per level
|
||||
int laststartpos = 0;
|
||||
|
||||
#define SNAP_ID MAKE_ID('s','n','A','p')
|
||||
#define DSNP_ID MAKE_ID('d','s','N','p')
|
||||
|
|
@ -652,7 +654,9 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
|
|||
gamestate = GS_LEVEL;
|
||||
}
|
||||
|
||||
G_DoLoadLevel (mapname, 0, false, !savegamerestore);
|
||||
if (!savegamerestore)
|
||||
startpos = laststartpos = 0;
|
||||
G_DoLoadLevel (mapname, startpos, false, !savegamerestore);
|
||||
|
||||
if (!savegamerestore && (gameinfo.gametype == GAME_Strife || (SBarInfoScript[SCRIPT_CUSTOM] != nullptr && SBarInfoScript[SCRIPT_CUSTOM]->GetGameType() == GAME_Strife)))
|
||||
{
|
||||
|
|
@ -669,7 +673,6 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
|
|||
// G_DoCompleted
|
||||
//
|
||||
static FString nextlevel;
|
||||
static int startpos; // [RH] Support for multiple starts per level
|
||||
extern int NoWipe; // [RH] Don't wipe when travelling in hubs
|
||||
static int changeflags;
|
||||
static bool unloading;
|
||||
|
|
@ -1100,7 +1103,6 @@ void G_DoCompleted (void)
|
|||
if (gamestate == GS_TITLELEVEL)
|
||||
{
|
||||
G_DoLoadLevel (nextlevel, startpos, false, false);
|
||||
startpos = 0;
|
||||
viewactive = true;
|
||||
return;
|
||||
}
|
||||
|
|
@ -1373,7 +1375,6 @@ void G_DoLoadLevel(const FString &nextmapname, int position, bool autosave, bool
|
|||
void FLevelLocals::DoLoadLevel(const FString &nextmapname, int position, bool autosave, bool newGame)
|
||||
{
|
||||
MapName = nextmapname;
|
||||
static int lastposition = 0;
|
||||
int i;
|
||||
|
||||
if (NextSkill >= 0)
|
||||
|
|
@ -1385,9 +1386,9 @@ void FLevelLocals::DoLoadLevel(const FString &nextmapname, int position, bool au
|
|||
}
|
||||
|
||||
if (position == -1)
|
||||
position = lastposition;
|
||||
position = laststartpos;
|
||||
else
|
||||
lastposition = position;
|
||||
laststartpos = position;
|
||||
|
||||
Init();
|
||||
StatusBar->DetachAllMessages ();
|
||||
|
|
@ -1579,7 +1580,6 @@ void G_DoWorldDone (void)
|
|||
}
|
||||
primaryLevel->StartTravel ();
|
||||
G_DoLoadLevel (nextlevel, startpos, true, false);
|
||||
startpos = 0;
|
||||
gameaction = ga_nothing;
|
||||
viewactive = true;
|
||||
}
|
||||
|
|
@ -2407,6 +2407,24 @@ void FLevelLocals::ApplyCompatibility2()
|
|||
i_compatflags2 = GetCompatibility2(compatflags2) | ii_compatflags2;
|
||||
}
|
||||
|
||||
AActor* FLevelLocals::SelectActorFromTID(int tid, size_t index, AActor* defactor)
|
||||
{
|
||||
if (tid == 0)
|
||||
return defactor;
|
||||
|
||||
AActor* actor = nullptr;
|
||||
size_t cur = 0u;
|
||||
auto it = GetActorIterator(tid);
|
||||
while ((actor = it.Next()) != nullptr)
|
||||
{
|
||||
if (cur == index)
|
||||
return actor;
|
||||
++cur;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
// IsPointInMap
|
||||
//
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ struct FLevelLocals
|
|||
int GetCompatibility2(int mask);
|
||||
void ApplyCompatibility();
|
||||
void ApplyCompatibility2();
|
||||
AActor* SelectActorFromTID(int tid, size_t index, AActor* defactor);
|
||||
|
||||
void Init();
|
||||
|
||||
|
|
@ -166,8 +167,8 @@ private:
|
|||
|
||||
void SerializePlayers(FSerializer &arc, bool skipload);
|
||||
void CopyPlayer(player_t *dst, player_t *src, const char *name);
|
||||
void ReadOnePlayer(FSerializer &arc, bool skipload);
|
||||
void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload);
|
||||
void ReadOnePlayer(FSerializer &arc, bool fromHub);
|
||||
void ReadMultiplePlayers(FSerializer &arc, int numPlayers, bool fromHub);
|
||||
void SerializeSounds(FSerializer &arc);
|
||||
void PlayerSpawnPickClass (int playernum);
|
||||
|
||||
|
|
@ -198,7 +199,7 @@ public:
|
|||
void ClearDynamic3DFloorData();
|
||||
void WorldDone(void);
|
||||
void AirControlChanged();
|
||||
AActor *SelectTeleDest(int tid, int tag, bool norandom);
|
||||
AActor *SelectTeleDest(int tid, int tag, bool norandom, bool isPlayer);
|
||||
bool AlignFlat(int linenum, int side, int fc);
|
||||
void ReplaceTextures(const char *fromname, const char *toname, int flags);
|
||||
|
||||
|
|
@ -424,11 +425,9 @@ public:
|
|||
|
||||
DThinker *CreateThinker(PClass *cls, int statnum = STAT_DEFAULT)
|
||||
{
|
||||
DThinker *thinker = static_cast<DThinker*>(cls->CreateNew());
|
||||
DThinker *thinker = static_cast<DThinker*>(cls->CreateNew(&statnum));
|
||||
assert(thinker->IsKindOf(RUNTIME_CLASS(DThinker)));
|
||||
thinker->ObjectFlags |= OF_JustSpawned;
|
||||
if (thinker->IsKindOf(RUNTIME_CLASS(DVisualThinker))) // [MC] This absolutely must happen for this class!
|
||||
statnum = STAT_VISUALTHINKER;
|
||||
Thinkers.Link(thinker, statnum);
|
||||
thinker->Level = this;
|
||||
return thinker;
|
||||
|
|
|
|||
|
|
@ -3225,9 +3225,9 @@ class CommandDrawGem : public SBarInfoCommand
|
|||
int goalValue;
|
||||
private:
|
||||
int chainWiggle;
|
||||
static FRandom pr_chainwiggle;
|
||||
static FCRandom pr_chainwiggle;
|
||||
};
|
||||
FRandom CommandDrawGem::pr_chainwiggle; //use the same method of chain wiggling as heretic.
|
||||
FCRandom CommandDrawGem::pr_chainwiggle; //use the same method of chain wiggling as heretic.
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ static TArray<uint8_t> DecalTranslations;
|
|||
// Sometimes two machines in a game will disagree on the state of
|
||||
// decals. I do not know why.
|
||||
|
||||
static FRandom pr_decalchoice ("DecalChoice");
|
||||
static FRandom pr_decal ("Decal");
|
||||
static FCRandom pr_decalchoice ("DecalChoice");
|
||||
static FCRandom pr_decal ("Decal");
|
||||
|
||||
class FDecalGroup : public FDecalBase
|
||||
{
|
||||
|
|
|
|||
|
|
@ -77,13 +77,14 @@ enum ETerrainKeywords
|
|||
TR_DAMAGETIMEMASK,
|
||||
TR_FOOTCLIP,
|
||||
TR_STEPVOLUME,
|
||||
TR_WALKINGSTEPTIME,
|
||||
TR_RUNNINGSTEPTIME,
|
||||
TR_WALKSTEPTICS,
|
||||
TR_RUNSTEPTICS,
|
||||
TR_LEFTSTEPSOUNDS,
|
||||
TR_RIGHTSTEPSOUNDS,
|
||||
TR_LIQUID,
|
||||
TR_FRICTION,
|
||||
TR_ALLOWPROTECTION
|
||||
TR_ALLOWPROTECTION,
|
||||
TR_STEPSOUNDS
|
||||
};
|
||||
|
||||
enum EGenericType
|
||||
|
|
@ -95,7 +96,6 @@ enum EGenericType
|
|||
GEN_Splash,
|
||||
GEN_Float,
|
||||
GEN_Double,
|
||||
GEN_Time,
|
||||
GEN_Bool,
|
||||
GEN_Int,
|
||||
GEN_Custom,
|
||||
|
|
@ -179,14 +179,17 @@ static const char *TerrainKeywords[] =
|
|||
"damagetimemask",
|
||||
"footclip",
|
||||
"stepvolume",
|
||||
"walkingsteptime",
|
||||
"runningsteptime",
|
||||
"walksteptics",
|
||||
"runsteptics",
|
||||
"leftstepsounds",
|
||||
"rightstepsounds",
|
||||
"liquid",
|
||||
"friction",
|
||||
"allowprotection",
|
||||
"damageonland",
|
||||
"stepsounds",
|
||||
"stepdistance",
|
||||
"stepdistanceminvel",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -215,14 +218,17 @@ static FGenericParse TerrainParser[] =
|
|||
{ GEN_Int, {myoffsetof(FTerrainDef, DamageTimeMask)} },
|
||||
{ GEN_Double, {myoffsetof(FTerrainDef, FootClip)} },
|
||||
{ GEN_Float, {myoffsetof(FTerrainDef, StepVolume)} },
|
||||
{ GEN_Time, {myoffsetof(FTerrainDef, WalkStepTics)} },
|
||||
{ GEN_Time, {myoffsetof(FTerrainDef, RunStepTics)} },
|
||||
{ GEN_Int, {myoffsetof(FTerrainDef, WalkStepTics)} },
|
||||
{ GEN_Int, {myoffsetof(FTerrainDef, RunStepTics)} },
|
||||
{ GEN_Sound, {myoffsetof(FTerrainDef, LeftStepSound)} },
|
||||
{ GEN_Sound, {myoffsetof(FTerrainDef, RightStepSound)} },
|
||||
{ GEN_Bool, {myoffsetof(FTerrainDef, IsLiquid)} },
|
||||
{ GEN_Custom, {(size_t)ParseFriction} },
|
||||
{ GEN_Bool, {myoffsetof(FTerrainDef, AllowProtection)} },
|
||||
{ GEN_Bool, {myoffsetof(FTerrainDef, DamageOnLand)} },
|
||||
{ GEN_Sound, {myoffsetof(FTerrainDef, StepSound)} },
|
||||
{ GEN_Double, {myoffsetof(FTerrainDef, StepDistance)} },
|
||||
{ GEN_Double, {myoffsetof(FTerrainDef, StepDistanceMinVel)} },
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -597,11 +603,6 @@ static void GenericParse (FScanner &sc, FGenericParse *parser, const char **keyw
|
|||
SET_FIELD(double, sc.Float);
|
||||
break;
|
||||
|
||||
case GEN_Time:
|
||||
sc.MustGetFloat ();
|
||||
SET_FIELD (int, (int)(sc.Float * TICRATE));
|
||||
break;
|
||||
|
||||
case GEN_Bool:
|
||||
SET_FIELD (bool, true);
|
||||
break;
|
||||
|
|
@ -747,3 +748,6 @@ DEFINE_FIELD(FTerrainDef, AllowProtection)
|
|||
DEFINE_FIELD(FTerrainDef, DamageOnLand)
|
||||
DEFINE_FIELD(FTerrainDef, Friction)
|
||||
DEFINE_FIELD(FTerrainDef, MoveFactor)
|
||||
DEFINE_FIELD(FTerrainDef, StepSound)
|
||||
DEFINE_FIELD(FTerrainDef, StepDistance)
|
||||
DEFINE_FIELD(FTerrainDef, StepDistanceMinVel)
|
||||
|
|
@ -118,6 +118,9 @@ struct FTerrainDef
|
|||
bool DamageOnLand;
|
||||
double Friction;
|
||||
double MoveFactor;
|
||||
FSoundID StepSound;
|
||||
double StepDistance;
|
||||
double StepDistanceMinVel;
|
||||
};
|
||||
|
||||
extern TArray<FSplashDef> Splashes;
|
||||
|
|
|
|||
|
|
@ -504,6 +504,8 @@ enum
|
|||
SECMF_OVERLAPPING = 512, // floor and ceiling overlap and require special renderer action.
|
||||
SECMF_NOSKYWALLS = 1024, // Do not draw "sky walls"
|
||||
SECMF_LIFT = 2048, // For MBF monster AI
|
||||
SECMF_HURTMONSTERS = 4096, // Monsters in this sector are hurt like players.
|
||||
SECMF_HARMINAIR = 8192, // Actors in this sector are also hurt mid-air.
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
@ -1062,6 +1064,16 @@ public:
|
|||
return pos == floor ? floorplane : ceilingplane;
|
||||
}
|
||||
|
||||
void SetPlaneReflectivity(int pos, double val)
|
||||
{
|
||||
reflect[pos] = val;
|
||||
}
|
||||
|
||||
double GetPlaneReflectivity(int pos)
|
||||
{
|
||||
return reflect[pos];
|
||||
}
|
||||
|
||||
bool isSecret() const
|
||||
{
|
||||
return !!(Flags & SECF_SECRET);
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ FTextureAnimator TexAnim;
|
|||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
static FRandom pr_animatepictures ("AnimatePics");
|
||||
static FCRandom pr_animatepictures ("AnimatePics");
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -148,8 +148,22 @@ static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape, int *id24_l
|
|||
}
|
||||
else if (!pname.CompareNoCase("label"))
|
||||
{
|
||||
scanner.MustGetToken(TK_StringConst);
|
||||
mape->label = scanner.String;
|
||||
if (scanner.CheckToken(TK_Identifier))
|
||||
{
|
||||
if (!stricmp(scanner.String, "clear"))
|
||||
{
|
||||
mape->label = "*";
|
||||
}
|
||||
else
|
||||
{
|
||||
scanner.ScriptError("Either 'clear' or string constant expected");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scanner.MustGetToken(TK_StringConst);
|
||||
mape->label = scanner.String;
|
||||
}
|
||||
}
|
||||
else if (!pname.CompareNoCase("next"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,19 +56,16 @@
|
|||
|
||||
class DLevelPostProcessor : public DObject
|
||||
{
|
||||
DECLARE_ABSTRACT_CLASS(DLevelPostProcessor, DObject)
|
||||
DECLARE_CLASS(DLevelPostProcessor, DObject)
|
||||
public:
|
||||
MapLoader *loader;
|
||||
FLevelLocals *Level;
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(DLevelPostProcessor, true, false);
|
||||
IMPLEMENT_CLASS(DLevelPostProcessor, false, false);
|
||||
|
||||
void MapLoader::PostProcessLevel(FName checksum)
|
||||
{
|
||||
auto lc = Create<DLevelPostProcessor>();
|
||||
lc->loader = this;
|
||||
lc->Level = Level;
|
||||
for(auto cls : PClass::AllClasses)
|
||||
{
|
||||
if (cls->IsDescendantOf(RUNTIME_CLASS(DLevelPostProcessor)))
|
||||
|
|
@ -87,8 +84,14 @@ void MapLoader::PostProcessLevel(FName checksum)
|
|||
continue;
|
||||
}
|
||||
|
||||
auto lc = static_cast<DLevelPostProcessor*>(cls->CreateNew());
|
||||
lc->loader = this;
|
||||
lc->Level = Level;
|
||||
|
||||
VMValue param[] = { lc, checksum.GetIndex(), &Level->MapName };
|
||||
VMCall(func->Variants[0].Implementation, param, 3, nullptr, 0);
|
||||
|
||||
lc->Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2050,6 +2050,14 @@ public:
|
|||
Flag(sec->Flags, SECF_HAZARD, key);
|
||||
break;
|
||||
|
||||
case NAME_hurtmonsters:
|
||||
Flag(sec->MoreFlags, SECMF_HURTMONSTERS, key);
|
||||
break;
|
||||
|
||||
case NAME_harminair:
|
||||
Flag(sec->MoreFlags, SECMF_HARMINAIR, key);
|
||||
break;
|
||||
|
||||
case NAME_floorterrain:
|
||||
sec->terrainnum[sector_t::floor] = P_FindTerrain(CheckString(key));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -812,6 +812,8 @@ xx(damageinterval)
|
|||
xx(leakiness)
|
||||
xx(damageterraineffect)
|
||||
xx(damagehazard)
|
||||
xx(hurtmonsters)
|
||||
xx(harminair)
|
||||
xx(floorterrain)
|
||||
xx(ceilingterrain)
|
||||
xx(floor_reflect)
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
#include "doommenu.h"
|
||||
#include "g_game.h"
|
||||
|
||||
static FRandom pr_randomspeech("RandomSpeech");
|
||||
static FCRandom pr_randomspeech("RandomSpeech");
|
||||
|
||||
static int ConversationMenuY;
|
||||
|
||||
|
|
|
|||
215
src/p_saveg.cpp
|
|
@ -627,8 +627,8 @@ void FLevelLocals::SerializePlayers(FSerializer &arc, bool skipload)
|
|||
{
|
||||
if (arc.BeginObject(nullptr))
|
||||
{
|
||||
const char *n = Players[i]->userinfo.GetName();
|
||||
arc.StringPtr("playername", n);
|
||||
FString name = Players[i]->userinfo.GetName();
|
||||
arc("playername", name);
|
||||
Players[i]->Serialize(arc);
|
||||
arc.EndObject();
|
||||
}
|
||||
|
|
@ -651,7 +651,7 @@ void FLevelLocals::SerializePlayers(FSerializer &arc, bool skipload)
|
|||
}
|
||||
else
|
||||
{
|
||||
ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload);
|
||||
ReadMultiplePlayers(arc, numPlayers, skipload);
|
||||
}
|
||||
arc.EndArray();
|
||||
|
||||
|
|
@ -680,53 +680,41 @@ void FLevelLocals::SerializePlayers(FSerializer &arc, bool skipload)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void FLevelLocals::ReadOnePlayer(FSerializer &arc, bool skipload)
|
||||
void FLevelLocals::ReadOnePlayer(FSerializer &arc, bool fromHub)
|
||||
{
|
||||
int i;
|
||||
const char *name = NULL;
|
||||
bool didIt = false;
|
||||
if (!arc.BeginObject(nullptr))
|
||||
return;
|
||||
|
||||
if (arc.BeginObject(nullptr))
|
||||
FString name = {};
|
||||
arc("playername", name);
|
||||
player_t temp = {};
|
||||
temp.Serialize(arc);
|
||||
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
arc.StringPtr("playername", name);
|
||||
if (!PlayerInGame(i))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
if (!fromHub)
|
||||
{
|
||||
if (playeringame[i])
|
||||
{
|
||||
if (!didIt)
|
||||
{
|
||||
didIt = true;
|
||||
player_t playerTemp;
|
||||
playerTemp.Serialize(arc);
|
||||
if (!skipload)
|
||||
{
|
||||
// This temp player has undefined pitch limits, so set them to something
|
||||
// that should leave the pitch stored in the savegame intact when
|
||||
// rendering. The real pitch limits will be set by P_SerializePlayers()
|
||||
// via a net command, but that won't be processed in time for a screen
|
||||
// wipe, so we need something here.
|
||||
playerTemp.MaxPitch = playerTemp.MinPitch = playerTemp.mo->Angles.Pitch;
|
||||
CopyPlayer(Players[i], &playerTemp, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need the player actor, so that G_FinishTravel can destroy it later.
|
||||
Players[i]->mo = playerTemp.mo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Players[i]->mo != NULL)
|
||||
{
|
||||
Players[i]->mo->Destroy();
|
||||
Players[i]->mo = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This temp player has undefined pitch limits, so set them to something
|
||||
// that should leave the pitch stored in the savegame intact when
|
||||
// rendering. The real pitch limits will be set by P_SerializePlayers()
|
||||
// via a net command, but that won't be processed in time for a screen
|
||||
// wipe, so we need something here.
|
||||
temp.MaxPitch = temp.MinPitch = temp.mo->Angles.Pitch;
|
||||
CopyPlayer(Players[i], &temp, name.GetChars());
|
||||
}
|
||||
arc.EndObject();
|
||||
else
|
||||
{
|
||||
// we need the player actor, so that G_FinishTravel can destroy it later.
|
||||
Players[i]->mo = temp.mo;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
arc.EndObject();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -735,109 +723,96 @@ void FLevelLocals::ReadOnePlayer(FSerializer &arc, bool skipload)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void FLevelLocals::ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload)
|
||||
struct NetworkPlayerInfo
|
||||
{
|
||||
// For two or more players, read each player into a temporary array.
|
||||
int i, j;
|
||||
const char **nametemp = new const char *[numPlayers];
|
||||
player_t *playertemp = new player_t[numPlayers];
|
||||
uint8_t *tempPlayerUsed = new uint8_t[numPlayers];
|
||||
uint8_t playerUsed[MAXPLAYERS];
|
||||
FString Name = {};
|
||||
player_t Info = {};
|
||||
bool bUsed = false;
|
||||
};
|
||||
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
void FLevelLocals::ReadMultiplePlayers(FSerializer &arc, int numPlayers, bool fromHub)
|
||||
{
|
||||
TArray<NetworkPlayerInfo> tempPlayers = {};
|
||||
tempPlayers.Reserve(numPlayers);
|
||||
TArray<bool> assignedPlayers = {};
|
||||
assignedPlayers.Reserve(MAXPLAYERS);
|
||||
|
||||
// Read all the save game players into a temporary array
|
||||
for (auto& p : tempPlayers)
|
||||
{
|
||||
nametemp[i] = NULL;
|
||||
if (arc.BeginObject(nullptr))
|
||||
{
|
||||
arc.StringPtr("playername", nametemp[i]);
|
||||
playertemp[i].Serialize(arc);
|
||||
arc("playername", p.Name);
|
||||
p.Info.Serialize(arc);
|
||||
arc.EndObject();
|
||||
}
|
||||
tempPlayerUsed[i] = 0;
|
||||
}
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
playerUsed[i] = playeringame[i] ? 0 : 2;
|
||||
}
|
||||
|
||||
if (!skipload)
|
||||
// Now try to match players from the savegame with players present
|
||||
// based on their names. If two players in the savegame have the
|
||||
// same name, then they are assigned to players in the current game
|
||||
// on a first-come, first-served basis.
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
// Now try to match players from the savegame with players present
|
||||
// based on their names. If two players in the savegame have the
|
||||
// same name, then they are assigned to players in the current game
|
||||
// on a first-come, first-served basis.
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
{
|
||||
for (j = 0; j < MAXPLAYERS; ++j)
|
||||
{
|
||||
if (playerUsed[j] == 0 && stricmp(players[j].userinfo.GetName(), nametemp[i]) == 0)
|
||||
{ // Found a match, so copy our temp player to the real player
|
||||
Printf("Found player %d (%s) at %d\n", i, nametemp[i], j);
|
||||
CopyPlayer(Players[j], &playertemp[i], nametemp[i]);
|
||||
playerUsed[j] = 1;
|
||||
tempPlayerUsed[i] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!PlayerInGame(i))
|
||||
continue;
|
||||
|
||||
// Any players that didn't have matching names are assigned to existing
|
||||
// players on a first-come, first-served basis.
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
for (auto& p : tempPlayers)
|
||||
{
|
||||
if (tempPlayerUsed[i] == 0)
|
||||
if (!p.bUsed && !p.Name.Compare(Players[i]->userinfo.GetName()))
|
||||
{
|
||||
for (j = 0; j < MAXPLAYERS; ++j)
|
||||
// Found a match, so copy our temp player to the real player
|
||||
if (!fromHub)
|
||||
{
|
||||
if (playerUsed[j] == 0)
|
||||
{
|
||||
Printf("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.GetName());
|
||||
CopyPlayer(&players[j], &playertemp[i], nametemp[i]);
|
||||
playerUsed[j] = 1;
|
||||
tempPlayerUsed[i] = 1;
|
||||
break;
|
||||
}
|
||||
Printf("Found %s's (%d) data\n", Players[i]->userinfo.GetName(), i);
|
||||
CopyPlayer(Players[i], &p.Info, p.Name.GetChars());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure any extra players don't have actors spawned yet. Happens if the players
|
||||
// present now got the same slots as they had in the save, but there are not as many
|
||||
// as there were in the save.
|
||||
for (j = 0; j < MAXPLAYERS; ++j)
|
||||
{
|
||||
if (playerUsed[j] == 0)
|
||||
{
|
||||
if (players[j].mo != NULL)
|
||||
else
|
||||
{
|
||||
players[j].mo->Destroy();
|
||||
players[j].mo = NULL;
|
||||
Players[i]->mo = p.Info.mo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any temp players that were not used. Happens if there are fewer players
|
||||
// than there were in the save, and they got shuffled.
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
{
|
||||
if (tempPlayerUsed[i] == 0)
|
||||
{
|
||||
playertemp[i].mo->Destroy();
|
||||
playertemp[i].mo = NULL;
|
||||
p.bUsed = true;
|
||||
assignedPlayers[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Any players that didn't have matching names are assigned to existing
|
||||
// players on a first-come, first-served basis.
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
if (!PlayerInGame(i) || assignedPlayers[i])
|
||||
continue;
|
||||
|
||||
for (auto& p : tempPlayers)
|
||||
{
|
||||
players[i].mo = playertemp[i].mo;
|
||||
if (!p.bUsed)
|
||||
{
|
||||
if (!fromHub)
|
||||
{
|
||||
Printf("Assigned %s (%d) to %s's data\n", Players[i]->userinfo.GetName(), i, p.Name.GetChars());
|
||||
CopyPlayer(Players[i], &p.Info, p.Name.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
Players[i]->mo = p.Info.mo;
|
||||
}
|
||||
|
||||
p.bUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] tempPlayerUsed;
|
||||
delete[] playertemp;
|
||||
delete[] nametemp;
|
||||
// Remove any temp players that were not used. Happens if there are now
|
||||
// less players in the game than there were in the save
|
||||
for (auto& p : tempPlayers)
|
||||
{
|
||||
if (!p.bUsed)
|
||||
p.Info.mo->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
static FMemArena DynLightArena(sizeof(FDynamicLight) * 200);
|
||||
static TArray<FDynamicLight*> FreeList;
|
||||
static FRandom randLight;
|
||||
static FCRandom randLight;
|
||||
|
||||
extern TArray<FLightDefaults *> StateLights;
|
||||
|
||||
|
|
@ -405,10 +405,14 @@ void FDynamicLight::UpdateLocation()
|
|||
Pos = target->Vec3Offset(m_off.X * c + m_off.Y * s, m_off.X * s - m_off.Y * c, m_off.Z + target->GetBobOffset());
|
||||
Sector = target->subsector->sector; // Get the render sector. target->Sector is the sector according to play logic.
|
||||
|
||||
// Some z-coordinate fudging to prevent the light from getting too close to the floor or ceiling planes. With proper attenuation this would render them invisible.
|
||||
// A distance of 5 is needed so that the light's effect doesn't become too small.
|
||||
if (Z() < target->floorz + 5.) Pos.Z = target->floorz + 5.;
|
||||
else if (Z() > target->ceilingz - 5.) Pos.Z = target->ceilingz - 5.;
|
||||
if (!(target->flags5 & MF5_NOINTERACTION))
|
||||
{
|
||||
// Some z-coordinate fudging to prevent the light from getting too close to the floor or ceiling planes. With proper attenuation this would render them invisible.
|
||||
// A distance of 5 is needed so that the light's effect doesn't become too small.
|
||||
// [SP] don't do this if +NOINTERACTION is set, since the object can fly right through floors and ceilings with that flag
|
||||
if (Z() < target->floorz + 5.) Pos.Z = target->floorz + 5.;
|
||||
else if (Z() > target->ceilingz - 5.) Pos.Z = target->ceilingz - 5.;
|
||||
}
|
||||
|
||||
// The radius being used here is always the maximum possible with the
|
||||
// current settings. This avoids constant relinking of flickering lights
|
||||
|
|
|
|||
|
|
@ -443,7 +443,9 @@ enum ActorFlag9
|
|||
MF9_SHADOWBLOCK = 0x00000004, // [inkoalawetrust] Actors in the line of fire with this flag trigger the MF_SHADOW aiming penalty.
|
||||
MF9_SHADOWAIMVERT = 0x00000008, // [inkoalawetrust] Monster aim is also offset vertically when aiming at shadow actors.
|
||||
MF9_DECOUPLEDANIMATIONS = 0x00000010, // [RL0] Decouple model animations from states
|
||||
MF9_NOSECTORDAMAGE = 0x00000020, // [inkoalawetrust] Actor ignores any sector-based damage (i.e damaging floors, NOT crushers)
|
||||
MF9_ISPUFF = 0x00000040, // [AA] Set on actors by P_SpawnPuff
|
||||
MF9_FORCESECTORDAMAGE = 0x00000080, // [inkoalawetrust] Actor ALWAYS takes hurt floor damage if there's any. Even if the floor doesn't have SECMF_HURTMONSTERS.
|
||||
};
|
||||
|
||||
// --- mobj.renderflags ---
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
#include "g_cvars.h"
|
||||
#include "d_main.h"
|
||||
|
||||
#include "p_visualthinker.h"
|
||||
|
||||
static int ThinkCount;
|
||||
static cycle_t ThinkCycles;
|
||||
extern cycle_t BotSupportCycles;
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@
|
|||
// State.
|
||||
#include "serializer.h"
|
||||
|
||||
static FRandom pr_flicker ("Flicker");
|
||||
static FRandom pr_lightflash ("LightFlash");
|
||||
static FRandom pr_strobeflash ("StrobeFlash");
|
||||
static FRandom pr_fireflicker ("FireFlicker");
|
||||
static FCRandom pr_flicker ("Flicker");
|
||||
static FCRandom pr_lightflash ("LightFlash");
|
||||
static FCRandom pr_strobeflash ("StrobeFlash");
|
||||
static FCRandom pr_fireflicker ("FireFlicker");
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
#include "actorinlines.h"
|
||||
#include <p_maputl.h>
|
||||
|
||||
static FRandom pr_quake ("Quake");
|
||||
static FCRandom pr_quake ("Quake");
|
||||
|
||||
IMPLEMENT_CLASS(DEarthquake, false, true)
|
||||
|
||||
|
|
|
|||
|
|
@ -198,39 +198,40 @@ void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flags, int
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// P_PlayerOnSpecial3DFloor
|
||||
// Checks to see if a player is standing on or is inside a 3D floor (water)
|
||||
// P_ActorOnSpecial3DFloor
|
||||
// Checks to see if an actor is standing on or is inside a 3D floor (water)
|
||||
// and applies any specials..
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void P_PlayerOnSpecial3DFloor(player_t* player)
|
||||
void P_ActorOnSpecial3DFloor(AActor* victim)
|
||||
{
|
||||
for(auto rover : player->mo->Sector->e->XFloor.ffloors)
|
||||
for(auto rover : victim->Sector->e->XFloor.ffloors)
|
||||
{
|
||||
if (!(rover->flags & FF_EXISTS)) continue;
|
||||
if (rover->flags & FF_FIX) continue;
|
||||
|
||||
if (!checkForSpecialSector(victim, rover->model)) continue;
|
||||
|
||||
// Check the 3D floor's type...
|
||||
if(rover->flags & FF_SOLID)
|
||||
{
|
||||
// Player must be on top of the floor to be affected...
|
||||
if(player->mo->Z() != rover->top.plane->ZatPoint(player->mo)) continue;
|
||||
if (victim->Z() != rover->top.plane->ZatPoint(victim)) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Water and DEATH FOG!!! heh
|
||||
if ((rover->flags & FF_NODAMAGE) ||
|
||||
player->mo->Z() > rover->top.plane->ZatPoint(player->mo) ||
|
||||
player->mo->Top() < rover->bottom.plane->ZatPoint(player->mo))
|
||||
victim->Z() > rover->top.plane->ZatPoint(victim) ||
|
||||
victim->Top() < rover->bottom.plane->ZatPoint(victim))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply sector specials
|
||||
P_PlayerInSpecialSector(player, rover->model);
|
||||
P_ActorInSpecialSector(victim, rover->model,rover);
|
||||
|
||||
// Apply flat specials (using the ceiling!)
|
||||
P_PlayerOnSpecialFlat(player, rover->model->GetTerrain(rover->top.isceiling));
|
||||
P_ActorOnSpecialFlat(victim, rover->model->GetTerrain(rover->top.isceiling));
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,8 +113,7 @@ struct lightlist_t
|
|||
|
||||
|
||||
|
||||
class player_t;
|
||||
void P_PlayerOnSpecial3DFloor(player_t* player);
|
||||
void P_ActorOnSpecial3DFloor(AActor* victim);
|
||||
|
||||
bool P_CheckFor3DFloorHit(AActor * mo, double z, bool trigger);
|
||||
bool P_CheckFor3DCeilingHit(AActor * mo, double z, bool trigger);
|
||||
|
|
|
|||
|
|
@ -4806,6 +4806,8 @@ enum EACSFunctions
|
|||
ACSF_GetSectorHealth,
|
||||
ACSF_GetLineHealth,
|
||||
ACSF_SetSubtitleNumber,
|
||||
ACSF_GetNetID,
|
||||
ACSF_SetActivatorByNetID,
|
||||
|
||||
// Eternity's
|
||||
ACSF_GetLineX = 300,
|
||||
|
|
@ -5380,6 +5382,13 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args, int &
|
|||
actor = Level->SingleActorFromTID(args[0], activator);
|
||||
return actor != NULL? DoubleToACS(actor->Vel.Z) : 0;
|
||||
|
||||
case ACSF_GetNetID:
|
||||
MIN_ARG_COUNT(2);
|
||||
actor = Level->SelectActorFromTID(args[0], args[1], activator);
|
||||
if (argCount > 2)
|
||||
actor = COPY_AAPTREX(Level, actor, args[2]);
|
||||
return actor != nullptr ? actor->GetNetworkID() : NetworkEntityManager::WorldNetID;
|
||||
|
||||
case ACSF_SetPointer:
|
||||
MIN_ARG_COUNT(2);
|
||||
if (activator)
|
||||
|
|
@ -5431,6 +5440,18 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args, int &
|
|||
}
|
||||
return 0;
|
||||
|
||||
case ACSF_SetActivatorByNetID:
|
||||
MIN_ARG_COUNT(1);
|
||||
actor = dyn_cast<AActor>(NetworkEntityManager::GetNetworkEntity(args[0]));
|
||||
if (argCount > 1)
|
||||
actor = COPY_AAPTREX(Level, actor, args[1]);
|
||||
if (actor != nullptr)
|
||||
{
|
||||
activator = actor;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case ACSF_GetActorViewHeight:
|
||||
MIN_ARG_COUNT(1);
|
||||
actor = Level->SingleActorFromTID(args[0], activator);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
#include "actorinlines.h"
|
||||
#include "g_game.h"
|
||||
#include "serializer_doom.h"
|
||||
#include "p_visualthinker.h"
|
||||
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
|
||||
|
|
@ -64,7 +65,7 @@ CVAR (Int, r_rail_trailsparsity, 1, CVAR_ARCHIVE);
|
|||
CVAR (Bool, r_particles, true, 0);
|
||||
EXTERN_CVAR(Int, r_maxparticles);
|
||||
|
||||
FRandom pr_railtrail("RailTrail");
|
||||
FCRandom pr_railtrail("RailTrail");
|
||||
|
||||
#define FADEFROMTTL(a) (1.f/(a))
|
||||
|
||||
|
|
@ -106,50 +107,45 @@ static const struct ColorList {
|
|||
{NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
inline particle_t *NewParticle (FLevelLocals *Level, bool replace = false)
|
||||
static void FreeParticle(FLevelLocals* Level, particle_t* particle)
|
||||
{
|
||||
particle_t *result = nullptr;
|
||||
// [MC] Thanks to RaveYard and randi for helping me with this addition.
|
||||
// Array's filled up
|
||||
if (Level->InactiveParticles == NO_PARTICLE)
|
||||
auto prev = particle->tprev == NO_PARTICLE? nullptr : &Level->Particles[particle->tprev];
|
||||
int pindex = (int)(particle - Level->Particles.Data());
|
||||
auto tnext = particle->tnext;
|
||||
assert(!prev || (prev->tnext == pindex));
|
||||
if (prev)
|
||||
prev->tnext = tnext;
|
||||
else
|
||||
Level->ActiveParticles = tnext;
|
||||
|
||||
if (tnext != NO_PARTICLE)
|
||||
{
|
||||
if (replace)
|
||||
{
|
||||
result = &Level->Particles[Level->OldestParticle];
|
||||
particle_t* next = &Level->Particles[tnext];
|
||||
assert(next->tprev == pindex);
|
||||
next->tprev = particle->tprev;
|
||||
}
|
||||
if (Level->OldestParticle == pindex)
|
||||
{
|
||||
assert(tnext == NO_PARTICLE);
|
||||
Level->OldestParticle = particle->tprev;
|
||||
}
|
||||
memset(particle, 0, sizeof(particle_t));
|
||||
particle->tnext = Level->InactiveParticles;
|
||||
Level->InactiveParticles = pindex;
|
||||
}
|
||||
|
||||
// There should be NO_PARTICLE for the oldest's tnext
|
||||
if (result->tprev != NO_PARTICLE)
|
||||
{
|
||||
// tnext: youngest to oldest
|
||||
// tprev: oldest to youngest
|
||||
|
||||
// 2nd oldest -> oldest
|
||||
particle_t *nbottom = &Level->Particles[result->tprev];
|
||||
nbottom->tnext = NO_PARTICLE;
|
||||
|
||||
// now oldest becomes youngest
|
||||
Level->OldestParticle = result->tprev;
|
||||
result->tnext = Level->ActiveParticles;
|
||||
result->tprev = NO_PARTICLE;
|
||||
Level->ActiveParticles = uint32_t(result - Level->Particles.Data());
|
||||
|
||||
// youngest -> 2nd youngest
|
||||
particle_t* ntop = &Level->Particles[result->tnext];
|
||||
ntop->tprev = Level->ActiveParticles;
|
||||
}
|
||||
// [MC] Future proof this by resetting everything when replacing a particle.
|
||||
auto tnext = result->tnext;
|
||||
auto tprev = result->tprev;
|
||||
*result = {};
|
||||
result->tnext = tnext;
|
||||
result->tprev = tprev;
|
||||
}
|
||||
return result;
|
||||
static particle_t *NewParticle (FLevelLocals *Level, bool replace = false)
|
||||
{
|
||||
// Array's filled up
|
||||
if (Level->InactiveParticles == NO_PARTICLE && Level->OldestParticle != NO_PARTICLE)
|
||||
{
|
||||
if (!replace) return nullptr;
|
||||
FreeParticle(Level, &Level->Particles[Level->OldestParticle]);
|
||||
}
|
||||
|
||||
// Array isn't full.
|
||||
uint32_t current = Level->ActiveParticles;
|
||||
result = &Level->Particles[Level->InactiveParticles];
|
||||
auto result = &Level->Particles[Level->InactiveParticles];
|
||||
Level->InactiveParticles = result->tnext;
|
||||
result->tnext = current;
|
||||
result->tprev = NO_PARTICLE;
|
||||
|
|
@ -310,19 +306,7 @@ void P_ThinkParticles (FLevelLocals *Level)
|
|||
particle->size += particle->sizestep;
|
||||
if (particle->alpha <= 0 || --particle->ttl <= 0 || (particle->size <= 0))
|
||||
{ // The particle has expired, so free it
|
||||
*particle = {};
|
||||
if (prev)
|
||||
prev->tnext = i;
|
||||
else
|
||||
Level->ActiveParticles = i;
|
||||
|
||||
if (i != NO_PARTICLE)
|
||||
{
|
||||
particle_t *next = &Level->Particles[i];
|
||||
next->tprev = particle->tprev;
|
||||
}
|
||||
particle->tnext = Level->InactiveParticles;
|
||||
Level->InactiveParticles = (int)(particle - Level->Particles.Data());
|
||||
FreeParticle(Level, particle);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -381,9 +365,9 @@ void P_SpawnParticle(FLevelLocals *Level, const DVector3 &pos, const DVector3 &v
|
|||
particle->sizestep = sizestep;
|
||||
particle->texture = texture;
|
||||
particle->style = style;
|
||||
particle->Roll = startroll;
|
||||
particle->RollVel = rollvel;
|
||||
particle->RollAcc = rollacc;
|
||||
particle->Roll = (float)startroll;
|
||||
particle->RollVel = (float)rollvel;
|
||||
particle->RollAcc = (float)rollacc;
|
||||
particle->flags = flags;
|
||||
if(flags & SPF_LOCAL_ANIM)
|
||||
{
|
||||
|
|
@ -1022,7 +1006,6 @@ void DVisualThinker::Construct()
|
|||
PT.subsector = nullptr;
|
||||
cursector = nullptr;
|
||||
PT.color = 0xffffff;
|
||||
spr = new HWSprite();
|
||||
AnimatedTexture.SetNull();
|
||||
}
|
||||
|
||||
|
|
@ -1034,11 +1017,6 @@ DVisualThinker::DVisualThinker()
|
|||
void DVisualThinker::OnDestroy()
|
||||
{
|
||||
PT.alpha = 0.0; // stops all rendering.
|
||||
if(spr)
|
||||
{
|
||||
delete spr;
|
||||
spr = nullptr;
|
||||
}
|
||||
Super::OnDestroy();
|
||||
}
|
||||
|
||||
|
|
@ -1067,6 +1045,33 @@ static DVisualThinker* SpawnVisualThinker(FLevelLocals* Level, PClass* type)
|
|||
return DVisualThinker::NewVisualThinker(Level, type);
|
||||
}
|
||||
|
||||
void DVisualThinker::UpdateSector(subsector_t * newSubsector)
|
||||
{
|
||||
assert(newSubsector);
|
||||
if(PT.subsector != newSubsector)
|
||||
{
|
||||
PT.subsector = newSubsector;
|
||||
cursector = newSubsector->sector;
|
||||
}
|
||||
}
|
||||
|
||||
void DVisualThinker::UpdateSector()
|
||||
{
|
||||
UpdateSector(Level->PointInRenderSubsector(PT.Pos));
|
||||
}
|
||||
|
||||
static void UpdateSector(DVisualThinker * self)
|
||||
{
|
||||
self->UpdateSector();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DVisualThinker, UpdateSector, UpdateSector)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(DVisualThinker);
|
||||
self->UpdateSector();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(FLevelLocals, SpawnVisualThinker, SpawnVisualThinker)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals);
|
||||
|
|
@ -1085,6 +1090,19 @@ void DVisualThinker::UpdateSpriteInfo()
|
|||
}
|
||||
}
|
||||
|
||||
static void UpdateSpriteInfo(DVisualThinker * self)
|
||||
{
|
||||
self->UpdateSpriteInfo();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DVisualThinker, UpdateSpriteInfo, UpdateSpriteInfo)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(DVisualThinker);
|
||||
self->UpdateSpriteInfo();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// This runs just like Actor's, make sure to call Super.Tick() in ZScript.
|
||||
void DVisualThinker::Tick()
|
||||
{
|
||||
|
|
@ -1114,27 +1132,27 @@ void DVisualThinker::Tick()
|
|||
PT.Pos.Y = newxy.Y;
|
||||
PT.Pos.Z += PT.Vel.Z;
|
||||
|
||||
PT.subsector = Level->PointInRenderSubsector(PT.Pos);
|
||||
cursector = PT.subsector->sector;
|
||||
subsector_t * ss = Level->PointInRenderSubsector(PT.Pos);
|
||||
|
||||
// Handle crossing a sector portal.
|
||||
if (!cursector->PortalBlocksMovement(sector_t::ceiling))
|
||||
if (!ss->sector->PortalBlocksMovement(sector_t::ceiling))
|
||||
{
|
||||
if (PT.Pos.Z > cursector->GetPortalPlaneZ(sector_t::ceiling))
|
||||
if (PT.Pos.Z > ss->sector->GetPortalPlaneZ(sector_t::ceiling))
|
||||
{
|
||||
PT.Pos += cursector->GetPortalDisplacement(sector_t::ceiling);
|
||||
PT.subsector = Level->PointInRenderSubsector(PT.Pos);
|
||||
cursector = PT.subsector->sector;
|
||||
PT.Pos += ss->sector->GetPortalDisplacement(sector_t::ceiling);
|
||||
ss = Level->PointInRenderSubsector(PT.Pos);
|
||||
}
|
||||
}
|
||||
else if (!cursector->PortalBlocksMovement(sector_t::floor))
|
||||
else if (!ss->sector->PortalBlocksMovement(sector_t::floor))
|
||||
{
|
||||
if (PT.Pos.Z < cursector->GetPortalPlaneZ(sector_t::floor))
|
||||
if (PT.Pos.Z < ss->sector->GetPortalPlaneZ(sector_t::floor))
|
||||
{
|
||||
PT.Pos += cursector->GetPortalDisplacement(sector_t::floor);
|
||||
PT.subsector = Level->PointInRenderSubsector(PT.Pos);
|
||||
cursector = PT.subsector->sector;
|
||||
PT.Pos += ss->sector->GetPortalDisplacement(sector_t::floor);
|
||||
ss = Level->PointInRenderSubsector(PT.Pos);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateSector(ss);
|
||||
UpdateSpriteInfo();
|
||||
}
|
||||
|
||||
|
|
@ -1142,7 +1160,7 @@ int DVisualThinker::GetLightLevel(sector_t* rendersector) const
|
|||
{
|
||||
int lightlevel = rendersector->GetSpriteLight();
|
||||
|
||||
if (bAddLightLevel)
|
||||
if (flags & VTF_AddLightLevel)
|
||||
{
|
||||
lightlevel += LightLevel;
|
||||
}
|
||||
|
|
@ -1155,7 +1173,7 @@ int DVisualThinker::GetLightLevel(sector_t* rendersector) const
|
|||
|
||||
FVector3 DVisualThinker::InterpolatedPosition(double ticFrac) const
|
||||
{
|
||||
if (bDontInterpolate) return FVector3(PT.Pos);
|
||||
if (flags & VTF_DontInterpolate) return FVector3(PT.Pos);
|
||||
|
||||
DVector3 proc = Prev + (ticFrac * (PT.Pos - Prev));
|
||||
return FVector3(proc);
|
||||
|
|
@ -1164,7 +1182,7 @@ FVector3 DVisualThinker::InterpolatedPosition(double ticFrac) const
|
|||
|
||||
float DVisualThinker::InterpolatedRoll(double ticFrac) const
|
||||
{
|
||||
if (bDontInterpolate) return PT.Roll;
|
||||
if (flags & VTF_DontInterpolate) return PT.Roll;
|
||||
|
||||
return float(PrevRoll + (PT.Roll - PrevRoll) * ticFrac);
|
||||
}
|
||||
|
|
@ -1244,17 +1262,29 @@ int DVisualThinker::GetRenderStyle()
|
|||
float DVisualThinker::GetOffset(bool y) const // Needed for the renderer.
|
||||
{
|
||||
if (y)
|
||||
return (float)(bFlipOffsetY ? Offset.Y : -Offset.Y);
|
||||
return (float)((flags & VTF_FlipOffsetY) ? Offset.Y : -Offset.Y);
|
||||
else
|
||||
return (float)(bFlipOffsetX ? Offset.X : -Offset.X);
|
||||
return (float)((flags & VTF_FlipOffsetX) ? Offset.X : -Offset.X);
|
||||
}
|
||||
|
||||
|
||||
FSerializer& Serialize(FSerializer& arc, const char* key, FStandaloneAnimation& value, FStandaloneAnimation* defval)
|
||||
{
|
||||
arc.BeginObject(key);
|
||||
arc("SwitchTic", value.SwitchTic);
|
||||
arc("AnimIndex", value.AnimIndex);
|
||||
arc("CurFrame", value.CurFrame);
|
||||
arc("Ok", value.ok);
|
||||
arc("AnimType", value.AnimType);
|
||||
arc.EndObject();
|
||||
return arc;
|
||||
}
|
||||
|
||||
void DVisualThinker::Serialize(FSerializer& arc)
|
||||
{
|
||||
Super::Serialize(arc);
|
||||
|
||||
arc
|
||||
("pos", PT.Pos)
|
||||
arc ("pos", PT.Pos)
|
||||
("vel", PT.Vel)
|
||||
("prev", Prev)
|
||||
("scale", Scale)
|
||||
|
|
@ -1267,15 +1297,15 @@ void DVisualThinker::Serialize(FSerializer& arc)
|
|||
("translation", Translation)
|
||||
("cursector", cursector)
|
||||
("scolor", PT.color)
|
||||
("flipx", bXFlip)
|
||||
("flipy", bYFlip)
|
||||
("dontinterpolate", bDontInterpolate)
|
||||
("addlightlevel", bAddLightLevel)
|
||||
("flipoffsetx", bFlipOffsetX)
|
||||
("flipoffsetY", bFlipOffsetY)
|
||||
("lightlevel", LightLevel)
|
||||
("flags", PT.flags);
|
||||
|
||||
("animData", PT.animData)
|
||||
("flags", PT.flags)
|
||||
("visualThinkerFlags", flags);
|
||||
|
||||
if(arc.isReading())
|
||||
{
|
||||
UpdateSector();
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_CLASS(DVisualThinker, false, false);
|
||||
|
|
@ -1286,6 +1316,7 @@ DEFINE_FIELD_NAMED(DVisualThinker, PT.Roll, Roll);
|
|||
DEFINE_FIELD_NAMED(DVisualThinker, PT.alpha, Alpha);
|
||||
DEFINE_FIELD_NAMED(DVisualThinker, PT.texture, Texture);
|
||||
DEFINE_FIELD_NAMED(DVisualThinker, PT.flags, Flags);
|
||||
DEFINE_FIELD_NAMED(DVisualThinker, flags, VisualThinkerFlags);
|
||||
|
||||
DEFINE_FIELD(DVisualThinker, Prev);
|
||||
DEFINE_FIELD(DVisualThinker, Scale);
|
||||
|
|
@ -1294,9 +1325,3 @@ DEFINE_FIELD(DVisualThinker, PrevRoll);
|
|||
DEFINE_FIELD(DVisualThinker, Translation);
|
||||
DEFINE_FIELD(DVisualThinker, LightLevel);
|
||||
DEFINE_FIELD(DVisualThinker, cursector);
|
||||
DEFINE_FIELD(DVisualThinker, bXFlip);
|
||||
DEFINE_FIELD(DVisualThinker, bYFlip);
|
||||
DEFINE_FIELD(DVisualThinker, bDontInterpolate);
|
||||
DEFINE_FIELD(DVisualThinker, bAddLightLevel);
|
||||
DEFINE_FIELD(DVisualThinker, bFlipOffsetX);
|
||||
DEFINE_FIELD(DVisualThinker, bFlipOffsetY);
|
||||
|
|
|
|||
|
|
@ -147,56 +147,4 @@ struct SPortalHit
|
|||
void P_DrawRailTrail(AActor *source, TArray<SPortalHit> &portalhits, int color1, int color2, double maxdiff = 0, int flags = 0, PClassActor *spawnclass = NULL, DAngle angle = nullAngle, int duration = TICRATE, double sparsity = 1.0, double drift = 1.0, int SpiralOffset = 270, DAngle pitch = nullAngle);
|
||||
void P_DrawSplash (FLevelLocals *Level, int count, const DVector3 &pos, DAngle angle, int kind);
|
||||
void P_DrawSplash2 (FLevelLocals *Level, int count, const DVector3 &pos, DAngle angle, int updown, int kind);
|
||||
void P_DisconnectEffect (AActor *actor);
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VisualThinkers
|
||||
// by Major Cooke
|
||||
// Credit to phantombeta, RicardoLuis0 & RaveYard for aid
|
||||
//
|
||||
//===========================================================================
|
||||
class HWSprite;
|
||||
struct FTranslationID;
|
||||
class DVisualThinker : public DThinker
|
||||
{
|
||||
DECLARE_CLASS(DVisualThinker, DThinker);
|
||||
public:
|
||||
DVector3 Prev;
|
||||
DVector2 Scale,
|
||||
Offset;
|
||||
float PrevRoll;
|
||||
int16_t LightLevel;
|
||||
FTranslationID Translation;
|
||||
FTextureID AnimatedTexture;
|
||||
sector_t *cursector;
|
||||
|
||||
bool bFlipOffsetX,
|
||||
bFlipOffsetY,
|
||||
bXFlip,
|
||||
bYFlip, // flip the sprite on the x/y axis.
|
||||
bDontInterpolate, // disable all interpolation
|
||||
bAddLightLevel; // adds sector light level to 'LightLevel'
|
||||
|
||||
// internal only variables
|
||||
particle_t PT;
|
||||
HWSprite *spr; //in an effort to cache the result.
|
||||
|
||||
DVisualThinker();
|
||||
void Construct();
|
||||
void OnDestroy() override;
|
||||
|
||||
static DVisualThinker* NewVisualThinker(FLevelLocals* Level, PClass* type);
|
||||
void SetTranslation(FName trname);
|
||||
int GetRenderStyle();
|
||||
bool isFrozen();
|
||||
int GetLightLevel(sector_t *rendersector) const;
|
||||
FVector3 InterpolatedPosition(double ticFrac) const;
|
||||
float InterpolatedRoll(double ticFrac) const;
|
||||
|
||||
void Tick() override;
|
||||
void UpdateSpriteInfo();
|
||||
void Serialize(FSerializer& arc) override;
|
||||
|
||||
float GetOffset(bool y) const;
|
||||
};
|
||||
void P_DisconnectEffect (AActor *actor);
|
||||
|
|
@ -464,6 +464,12 @@ static int P_IsUnderDamage(AActor* actor)
|
|||
dir |= cl->getDirection();
|
||||
}
|
||||
// Q: consider crushing 3D floors too?
|
||||
// [inkoalawetrust] Check for sectors that can harm the actor.
|
||||
if (!(actor->flags9 & MF9_NOSECTORDAMAGE) && seclist->m_sector->damageamount > 0)
|
||||
{
|
||||
if (seclist->m_sector->MoreFlags & SECMF_HARMINAIR || actor->isAtZ(seclist->m_sector->LowestFloorAt(actor)) || actor->waterlevel)
|
||||
return (actor->player || (actor->player == nullptr && seclist->m_sector->MoreFlags & SECMF_HURTMONSTERS)) ? -1 : 0;
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
|
@ -1303,6 +1309,161 @@ int P_IsVisible(AActor *lookee, AActor *other, INTBOOL allaround, FLookExParams
|
|||
return P_CheckSight(lookee, other, SF_SEEPASTSHOOTABLELINES);
|
||||
}
|
||||
|
||||
bool isTargetablePlayer(AActor *actor, player_t *player, INTBOOL allaround, void* lookparams)
|
||||
{
|
||||
FLookExParams* params = (FLookExParams*)lookparams;
|
||||
|
||||
if (!(player->mo->flags & MF_SHOOTABLE))
|
||||
return false; // not shootable (observer or dead)
|
||||
|
||||
if (actor->IsFriend(player->mo))
|
||||
return false; // same +MF_FRIENDLY, ignore
|
||||
|
||||
if (player->cheats & CF_NOTARGET)
|
||||
return false; // no target
|
||||
|
||||
if (player->health <= 0)
|
||||
return false; // dead
|
||||
|
||||
if (!P_IsVisible(actor, player->mo, allaround, params))
|
||||
return false; // out of sight
|
||||
|
||||
// [RC] Well, let's let special monsters with this flag active be able to see
|
||||
// the player then, eh?
|
||||
if (!(actor->flags6 & MF6_SEEINVISIBLE))
|
||||
{
|
||||
if ((player->mo->flags & MF_SHADOW && !(actor->Level->i_compatflags & COMPATF_INVISIBILITY)) ||
|
||||
player->mo->flags3 & MF3_GHOST)
|
||||
{
|
||||
if (player->mo->Distance2D(actor) > 128 && player->mo->Vel.XY().LengthSquared() < 5 * 5)
|
||||
{ // Player is sneaking - can't detect
|
||||
return false;
|
||||
}
|
||||
if (pr_lookforplayers() < 225)
|
||||
{ // Player isn't sneaking, but still didn't detect
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidEnemyInBlock(AActor* lookee, AActor* other, void* lookparams)
|
||||
{
|
||||
FLookExParams* params = (FLookExParams*)lookparams;
|
||||
|
||||
if (!(other->flags & MF_SHOOTABLE))
|
||||
return false; // not shootable (observer or dead)
|
||||
|
||||
if (other == lookee)
|
||||
return false; // is self
|
||||
|
||||
if (other->health <= 0)
|
||||
return false; // dead
|
||||
|
||||
if (other->flags2 & MF2_DORMANT)
|
||||
return false; // don't target dormant things
|
||||
|
||||
if (!(other->flags3 & MF3_ISMONSTER))
|
||||
return false; // don't target it if it isn't a monster (could be a barrel)
|
||||
|
||||
if (other->flags7 & MF7_NEVERTARGET)
|
||||
return false;
|
||||
|
||||
bool keepChecking = false;
|
||||
if (lookee->flags & MF_FRIENDLY)
|
||||
{
|
||||
if (other->flags & MF_FRIENDLY)
|
||||
{
|
||||
if (!lookee->IsFriend(other))
|
||||
{
|
||||
// This is somebody else's friend, so go after it
|
||||
keepChecking = true;
|
||||
}
|
||||
else if (other->target != NULL && !(other->target->flags & MF_FRIENDLY))
|
||||
{
|
||||
other = other->target;
|
||||
if (!(other->flags & MF_SHOOTABLE) ||
|
||||
other->health <= 0 ||
|
||||
(other->flags2 & MF2_DORMANT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
keepChecking = true;
|
||||
}
|
||||
}
|
||||
else if (lookee->flags8 & MF8_SEEFRIENDLYMONSTERS && other->flags & MF_FRIENDLY)
|
||||
{
|
||||
keepChecking = true;
|
||||
}
|
||||
|
||||
// [MBF] If the monster is already engaged in a one-on-one attack
|
||||
// with a healthy friend, don't attack around 60% the time.
|
||||
|
||||
// [GrafZahl] This prevents friendlies from attacking all the same
|
||||
// target.
|
||||
|
||||
if (keepChecking)
|
||||
{
|
||||
AActor* targ = other->target;
|
||||
if (targ && targ->target == other && pr_skiptarget() > 100 && lookee->IsFriend(targ) &&
|
||||
targ->health * 2 >= targ->SpawnHealth())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// [KS] Hey, shouldn't there be a check for MF3_NOSIGHTCHECK here?
|
||||
|
||||
if (!keepChecking || !P_IsVisible(lookee, other, true, params))
|
||||
return false; // out of sight
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// LookForEnemiesEx
|
||||
//
|
||||
// [inkoalawetrust] Return a script array of all valid enemies of the caller
|
||||
// in range. For ZScript.
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, LookForEnemiesEx)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_OUTPOINTER(targets,TArray<AActor*>);
|
||||
PARAM_FLOAT(range);
|
||||
PARAM_BOOL(noPlayers);
|
||||
PARAM_BOOL(allaround);
|
||||
PARAM_POINTER(params, FLookExParams);
|
||||
|
||||
if (targets == nullptr)
|
||||
ThrowAbortException(X_WRITE_NIL,"No targets array passed");
|
||||
|
||||
if (range == -1)
|
||||
range = self->friendlyseeblocks * FBlockmap::MAPBLOCKUNITS;
|
||||
|
||||
FPortalGroupArray check(FPortalGroupArray::PGA_Full3d);
|
||||
FMultiBlockThingsIterator it(check, self, range, false);
|
||||
FMultiBlockThingsIterator::CheckResult cres;
|
||||
|
||||
while (it.Next(&cres))
|
||||
{
|
||||
if (cres.thing->player == nullptr && ValidEnemyInBlock(cres.thing, self, params) ||
|
||||
!noPlayers && cres.thing->player && isTargetablePlayer(self, cres.thing->player, allaround, params))
|
||||
targets->Push(cres.thing);
|
||||
}
|
||||
|
||||
ACTION_RETURN_INT(targets->Size());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// FUNC P_LookForMonsters
|
||||
|
|
@ -1546,80 +1707,10 @@ AActor *LookForEnemiesInBlock (AActor *lookee, int index, void *extparam)
|
|||
|
||||
for (block = lookee->Level->blockmap.blocklinks[index]; block != NULL; block = block->NextActor)
|
||||
{
|
||||
link = block->Me;
|
||||
|
||||
if (!(link->flags & MF_SHOOTABLE))
|
||||
continue; // not shootable (observer or dead)
|
||||
|
||||
if (link == lookee)
|
||||
if (!ValidEnemyInBlock(lookee, block->Me, params))
|
||||
continue;
|
||||
|
||||
if (link->health <= 0)
|
||||
continue; // dead
|
||||
|
||||
if (link->flags2 & MF2_DORMANT)
|
||||
continue; // don't target dormant things
|
||||
|
||||
if (!(link->flags3 & MF3_ISMONSTER))
|
||||
continue; // don't target it if it isn't a monster (could be a barrel)
|
||||
|
||||
if (link->flags7 & MF7_NEVERTARGET)
|
||||
continue;
|
||||
|
||||
other = NULL;
|
||||
if (lookee->flags & MF_FRIENDLY)
|
||||
{
|
||||
if (link->flags & MF_FRIENDLY)
|
||||
{
|
||||
if (!lookee->IsFriend(link))
|
||||
{
|
||||
// This is somebody else's friend, so go after it
|
||||
other = link;
|
||||
}
|
||||
else if (link->target != NULL && !(link->target->flags & MF_FRIENDLY))
|
||||
{
|
||||
other = link->target;
|
||||
if (!(other->flags & MF_SHOOTABLE) ||
|
||||
other->health <= 0 ||
|
||||
(other->flags2 & MF2_DORMANT))
|
||||
{
|
||||
other = NULL;;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
other = link;
|
||||
}
|
||||
}
|
||||
else if (lookee->flags8 & MF8_SEEFRIENDLYMONSTERS && link->flags & MF_FRIENDLY)
|
||||
{
|
||||
other = link;
|
||||
}
|
||||
|
||||
// [MBF] If the monster is already engaged in a one-on-one attack
|
||||
// with a healthy friend, don't attack around 60% the time.
|
||||
|
||||
// [GrafZahl] This prevents friendlies from attacking all the same
|
||||
// target.
|
||||
|
||||
if (other)
|
||||
{
|
||||
AActor *targ = other->target;
|
||||
if (targ && targ->target == other && pr_skiptarget() > 100 && lookee->IsFriend (targ) &&
|
||||
targ->health*2 >= targ->SpawnHealth())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// [KS] Hey, shouldn't there be a check for MF3_NOSIGHTCHECK here?
|
||||
|
||||
if (other == NULL || !P_IsVisible (lookee, other, true, params))
|
||||
continue; // out of sight
|
||||
|
||||
|
||||
return other;
|
||||
return block->Me;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1819,38 +1910,8 @@ int P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params)
|
|||
|
||||
player = actor->Level->Players[pnum];
|
||||
|
||||
if (!(player->mo->flags & MF_SHOOTABLE))
|
||||
continue; // not shootable (observer or dead)
|
||||
|
||||
if (actor->IsFriend(player->mo))
|
||||
continue; // same +MF_FRIENDLY, ignore
|
||||
|
||||
if (player->cheats & CF_NOTARGET)
|
||||
continue; // no target
|
||||
|
||||
if (player->health <= 0)
|
||||
continue; // dead
|
||||
|
||||
if (!P_IsVisible (actor, player->mo, allaround, params))
|
||||
continue; // out of sight
|
||||
|
||||
// [RC] Well, let's let special monsters with this flag active be able to see
|
||||
// the player then, eh?
|
||||
if(!(actor->flags6 & MF6_SEEINVISIBLE))
|
||||
{
|
||||
if ((player->mo->flags & MF_SHADOW && !(actor->Level->i_compatflags & COMPATF_INVISIBILITY)) ||
|
||||
player->mo->flags3 & MF3_GHOST)
|
||||
{
|
||||
if (player->mo->Distance2D (actor) > 128 && player->mo->Vel.XY().LengthSquared() < 5*5)
|
||||
{ // Player is sneaking - can't detect
|
||||
continue;
|
||||
}
|
||||
if (pr_lookforplayers() < 225)
|
||||
{ // Player isn't sneaking, but still didn't detect
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isTargetablePlayer(actor, player, allaround, params))
|
||||
continue;
|
||||
|
||||
// [RH] Need to be sure the reactiontime is 0 if the monster is
|
||||
// leaving its goal to go after a player.
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile ver
|
|||
void P_TraceBleed(int damage, FTranslatedLineTarget *t, AActor *puff); // hitscan version
|
||||
void P_TraceBleed (int damage, AActor *target); // random direction version
|
||||
bool P_HitFloor (AActor *thing);
|
||||
bool P_HitWater (AActor *thing, sector_t *sec, const DVector3 &pos, bool checkabove = false, bool alert = true, bool force = false);
|
||||
bool P_HitWater (AActor *thing, sector_t *sec, const DVector3 &pos, bool checkabove = false, bool alert = true, bool force = false, int flags = 0);
|
||||
|
||||
|
||||
struct FRailParams
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@
|
|||
#include "r_utility.h"
|
||||
#include "p_blockmap.h"
|
||||
#include "p_3dmidtex.h"
|
||||
#include "events.h"
|
||||
#include "vm.h"
|
||||
#include "d_main.h"
|
||||
|
||||
|
|
@ -121,13 +122,20 @@ TArray<spechit_t> portalhit;
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
bool P_ShouldPassThroughPlayer(AActor *self, AActor *other)
|
||||
static int P_ShouldPassThroughPlayer(AActor *self, AActor *other)
|
||||
{
|
||||
return (dmflags3 & DF3_NO_PLAYER_CLIP) &&
|
||||
other->player && other->player->mo == other &&
|
||||
self->IsFriend(other);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, ShouldPassThroughPlayer, P_ShouldPassThroughPlayer)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_OBJECT_NOT_NULL(other, AActor);
|
||||
ACTION_RETURN_BOOL(P_ShouldPassThroughPlayer(self, other));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CanCollideWith
|
||||
|
|
@ -2100,10 +2108,13 @@ int P_TestMobjZ(AActor *actor, bool quick, AActor **pOnmobj)
|
|||
while (it.Next(&cres))
|
||||
{
|
||||
AActor *thing = cres.thing;
|
||||
if (!quick && onmobj != NULL && thing->Top() < onmobj->Top())
|
||||
{ // something higher is in the way
|
||||
continue;
|
||||
}
|
||||
|
||||
double blockdist = thing->radius + actor->radius;
|
||||
if (fabs(thing->X() - cres.Position.X) >= blockdist || fabs(thing->Y() - cres.Position.Y) >= blockdist)
|
||||
{
|
||||
if (thing == actor)
|
||||
{ // Don't clip against self.
|
||||
continue;
|
||||
}
|
||||
if (thing->flags2 & MF2_THRUACTORS)
|
||||
|
|
@ -2114,19 +2125,24 @@ int P_TestMobjZ(AActor *actor, bool quick, AActor **pOnmobj)
|
|||
{
|
||||
continue;
|
||||
}
|
||||
if ((actor->flags6 & MF6_THRUSPECIES) && (thing->GetSpecies() == actor->GetSpecies()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!(thing->flags & MF_SOLID))
|
||||
{ // Can't hit thing
|
||||
continue;
|
||||
}
|
||||
if (thing->flags & (MF_SPECIAL | MF_NOCLIP))
|
||||
if ((thing->flags & (MF_SPECIAL | MF_NOCLIP)) || (thing->flags6 & MF6_TOUCHY))
|
||||
{ // [RH] Specials and noclippers don't block moves
|
||||
continue;
|
||||
}
|
||||
if (thing->flags & (MF_CORPSE))
|
||||
const double blockdist = thing->radius + actor->radius;
|
||||
if (fabs(thing->X() - cres.Position.X) >= blockdist || fabs(thing->Y() - cres.Position.Y) >= blockdist)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((actor->flags6 & MF6_THRUSPECIES) && thing->GetSpecies() == actor->GetSpecies())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (thing->flags & MF_CORPSE)
|
||||
{ // Corpses need a few more checks
|
||||
if (!(actor->flags & MF_ICECORPSE))
|
||||
continue;
|
||||
|
|
@ -2135,33 +2151,78 @@ int P_TestMobjZ(AActor *actor, bool quick, AActor **pOnmobj)
|
|||
{ // [RH] Only bridges block pickup items
|
||||
continue;
|
||||
}
|
||||
if (thing == actor)
|
||||
{ // Don't clip against self
|
||||
continue;
|
||||
}
|
||||
if ((actor->flags & MF_MISSILE) && (thing == actor->target))
|
||||
{ // Don't clip against whoever shot the missile.
|
||||
if (actor->player != nullptr && P_ShouldPassThroughPlayer(actor, thing))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (actor->Z() > thing->Top())
|
||||
{ // over thing
|
||||
continue;
|
||||
}
|
||||
else if (actor->Top() <= thing->Z())
|
||||
if (actor->Top() <= thing->Z())
|
||||
{ // under thing
|
||||
continue;
|
||||
}
|
||||
else if (!quick && onmobj != NULL && thing->Top() < onmobj->Top())
|
||||
{ // something higher is in the way
|
||||
continue;
|
||||
}
|
||||
else if (!P_CanCollideWith(actor, thing))
|
||||
if (!P_CanCollideWith(actor, thing))
|
||||
{ // If they cannot collide, they cannot block each other.
|
||||
continue;
|
||||
}
|
||||
if (actor->player && P_ShouldPassThroughPlayer(actor, thing))
|
||||
if ((actor->flags & MF_MISSILE) || ((actor->BounceFlags & BOUNCE_MBF) && !(actor->flags & MF_SOLID)))
|
||||
{
|
||||
continue;
|
||||
if (thing->flags2 & MF2_NONSHOOTABLE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((thing->flags3 & MF3_GHOST) && (actor->flags2 & MF2_THRUGHOST))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((thing->flags4 & MF4_SPECTRAL) && !(actor->flags4 & MF4_SPECTRAL))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (actor->target != nullptr)
|
||||
{
|
||||
if ((actor->flags6 & MF6_MTHRUSPECIES) && actor->target->GetSpecies() == thing->GetSpecies())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (actor->target == thing)
|
||||
{
|
||||
if (!(actor->flags8 & MF8_HITOWNER))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (actor->target->player != nullptr && P_ShouldPassThroughPlayer(actor->target, thing))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((thing->flags7 & MF7_THRUREFLECT) && (thing->flags2 & MF2_REFLECTIVE) && (actor->flags & MF_MISSILE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double clipheight = thing->Height;
|
||||
if (thing->projectilepassheight > 0)
|
||||
{
|
||||
clipheight = thing->projectilepassheight;
|
||||
}
|
||||
else if (thing->projectilepassheight < 0 && (thing->Level->i_compatflags & COMPATF_MISSILECLIP))
|
||||
{
|
||||
clipheight = -thing->projectilepassheight;
|
||||
}
|
||||
if (actor->Z() > thing->Z() + clipheight)
|
||||
{ // Over thing
|
||||
continue;
|
||||
}
|
||||
if ((actor->flags2 & MF2_RIP) && !(thing->flags5 & MF5_DONTRIP)
|
||||
&& (!(actor->flags6 & MF6_NOBOSSRIP) || !(thing->flags2 & MF2_BOSS))
|
||||
&& CheckRipLevel(thing, actor))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
onmobj = thing;
|
||||
|
|
@ -4620,6 +4681,12 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
|
|||
DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, FTranslatedLineTarget*victim, int *actualdamage,
|
||||
double sz, double offsetforward, double offsetside)
|
||||
{
|
||||
if (t1->Level->localEventManager->WorldHitscanPreFired(t1, angle, distance, pitch, damage, damageType, pufftype, flags, sz, offsetforward, offsetside))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool nointeract = !!(flags & LAF_NOINTERACT);
|
||||
DVector3 direction;
|
||||
double shootz;
|
||||
|
|
@ -4634,6 +4701,7 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
|
|||
if (flags & LAF_NORANDOMPUFFZ)
|
||||
puffFlags |= PF_NORANDOMZ;
|
||||
|
||||
|
||||
if (victim != NULL)
|
||||
{
|
||||
memset(victim, 0, sizeof(*victim));
|
||||
|
|
@ -4724,6 +4792,7 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
|
|||
// LAF_ABSOFFSET: Ignore the angle.
|
||||
|
||||
DVector3 tempos;
|
||||
DVector3 puffpos;
|
||||
|
||||
if (flags & LAF_ABSPOSITION)
|
||||
{
|
||||
|
|
@ -4759,7 +4828,8 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
|
|||
|
||||
if (nointeract || (puffDefaults && puffDefaults->flags3 & MF3_ALWAYSPUFF))
|
||||
{ // Spawn the puff anyway
|
||||
puff = P_SpawnPuff(t1, pufftype, trace.HitPos, trace.SrcAngleFromTarget, trace.SrcAngleFromTarget, 2, puffFlags);
|
||||
puffpos = trace.HitPos;
|
||||
puff = P_SpawnPuff(t1, pufftype, puffpos, trace.SrcAngleFromTarget, trace.SrcAngleFromTarget, 2, puffFlags);
|
||||
|
||||
if (nointeract)
|
||||
{
|
||||
|
|
@ -4789,7 +4859,8 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
|
|||
if (nointeract || trace.HitType != TRACE_HitWall || ((trace.Line->special != Line_Horizon) || spawnSky))
|
||||
{
|
||||
DVector2 pos = t1->Level->GetPortalOffsetPosition(trace.HitPos.X, trace.HitPos.Y, -trace.HitVector.X * 4, -trace.HitVector.Y * 4);
|
||||
puff = P_SpawnPuff(t1, pufftype, DVector3(pos, trace.HitPos.Z - trace.HitVector.Z * 4), trace.SrcAngleFromTarget,
|
||||
puffpos = DVector3(pos, trace.HitPos.Z - trace.HitVector.Z * 4);
|
||||
puff = P_SpawnPuff(t1, pufftype, puffpos, trace.SrcAngleFromTarget,
|
||||
trace.SrcAngleFromTarget - DAngle::fromDeg(90), 0, puffFlags);
|
||||
puff->radius = 1/65536.;
|
||||
|
||||
|
|
@ -4836,6 +4907,7 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
|
|||
{
|
||||
// Hit a thing, so it could be either a puff or blood
|
||||
DVector3 bleedpos = trace.HitPos;
|
||||
puffpos = bleedpos;
|
||||
// position a bit closer for puffs/blood if using compatibility mode.
|
||||
if (trace.Actor->Level->i_compatflags & COMPATF_HITSCAN)
|
||||
{
|
||||
|
|
@ -4928,6 +5000,9 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
|
|||
SpawnDeepSplash(t1, trace, puff);
|
||||
}
|
||||
}
|
||||
|
||||
t1->Level->localEventManager->WorldHitscanFired(t1, tempos, puffpos, puff, flags);
|
||||
|
||||
if (killPuff && puff != NULL)
|
||||
{
|
||||
puff->Destroy();
|
||||
|
|
@ -5378,16 +5453,30 @@ static ETraceStatus ProcessRailHit(FTraceResults &res, void *userdata)
|
|||
//==========================================================================
|
||||
void P_RailAttack(FRailParams *p)
|
||||
{
|
||||
DVector3 start;
|
||||
FTraceResults trace;
|
||||
AActor *source = p->source;
|
||||
|
||||
PClassActor *puffclass = p->puff;
|
||||
if (puffclass == NULL)
|
||||
{
|
||||
puffclass = PClass::FindActor(NAME_BulletPuff);
|
||||
}
|
||||
assert(puffclass != NULL); // Because we set it to a default above
|
||||
AActor *puffDefaults = GetDefaultByType(puffclass->GetReplacement(source->Level)); //Contains all the flags such as FOILINVUL, etc.
|
||||
FName damagetype = (puffDefaults == NULL || puffDefaults->DamageType == NAME_None) ? FName(NAME_Railgun) : puffDefaults->DamageType;
|
||||
|
||||
int flags;
|
||||
|
||||
// disabled because not complete yet.
|
||||
flags = (puffDefaults->flags6 & MF6_NOTRIGGER) ? TRACE_ReportPortals : TRACE_PCross | TRACE_Impact | TRACE_ReportPortals;
|
||||
|
||||
if (source->Level->localEventManager->WorldHitscanPreFired(source, source->Angles.Yaw + p->angleoffset, p->distance, source->Angles.Pitch + p->pitchoffset, p->damage, damagetype, puffclass, flags, p->offset_z, 0, p->offset_xy))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DVector3 start;
|
||||
FTraceResults trace;
|
||||
|
||||
AActor *source = p->source;
|
||||
DAngle pitch = source->Angles.Pitch + p->pitchoffset;
|
||||
DAngle angle = source->Angles.Yaw + p->angleoffset;
|
||||
|
||||
|
|
@ -5416,13 +5505,6 @@ void P_RailAttack(FRailParams *p)
|
|||
start.Y = xy.Y;
|
||||
start.Z = shootz;
|
||||
|
||||
int flags;
|
||||
|
||||
assert(puffclass != NULL); // Because we set it to a default above
|
||||
AActor *puffDefaults = GetDefaultByType(puffclass->GetReplacement(source->Level)); //Contains all the flags such as FOILINVUL, etc.
|
||||
|
||||
// disabled because not complete yet.
|
||||
flags = (puffDefaults->flags6 & MF6_NOTRIGGER) ? TRACE_ReportPortals : TRACE_PCross | TRACE_Impact | TRACE_ReportPortals;
|
||||
rail_data.StopAtInvul = (puffDefaults->flags3 & MF3_FOILINVUL) ? false : true;
|
||||
rail_data.MThruSpecies = ((puffDefaults->flags6 & MF6_MTHRUSPECIES)) ? true : false;
|
||||
|
||||
|
|
@ -5459,8 +5541,7 @@ void P_RailAttack(FRailParams *p)
|
|||
|
||||
// Hurt anything the trace hit
|
||||
unsigned int i;
|
||||
FName damagetype = (puffDefaults == NULL || puffDefaults->DamageType == NAME_None) ? FName(NAME_Railgun) : puffDefaults->DamageType;
|
||||
|
||||
|
||||
for (i = 0; i < rail_data.RailHits.Size(); i++)
|
||||
{
|
||||
bool spawnpuff;
|
||||
|
|
@ -5548,6 +5629,9 @@ void P_RailAttack(FRailParams *p)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
source->Level->localEventManager->WorldHitscanFired(source, start, trace.HitPos, thepuff, flags);
|
||||
|
||||
if (thepuff != NULL)
|
||||
{
|
||||
if (trace.Crossed3DWater || trace.CrossedWater)
|
||||
|
|
|
|||
|
|
@ -4435,6 +4435,14 @@ void AActor::Tick ()
|
|||
// must have been removed
|
||||
if (ObjectFlags & OF_EuthanizeMe) return;
|
||||
}
|
||||
//[inkoalawetrust] Genericized level damage handling that makes sector, 3D floor, and TERRAIN flat damage affect monsters and other NPCs too.
|
||||
P_ActorOnSpecial3DFloor(this); //3D floors must be checked separately to see if their control sector allows non-player damage
|
||||
if (checkForSpecialSector(this,Sector))
|
||||
{
|
||||
P_ActorInSpecialSector(this,Sector);
|
||||
if (!isAbove(Sector->floorplane.ZatPoint(this)) || waterlevel) // Actor must be touching the floor for TERRAIN flats.
|
||||
P_ActorOnSpecialFlat(this, P_GetThingFloorType(this));
|
||||
}
|
||||
|
||||
if (tics != -1)
|
||||
{
|
||||
|
|
@ -6652,7 +6660,13 @@ int P_GetThingFloorType (AActor *thing)
|
|||
// Returns true if hit liquid and splashed, false if not.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool P_HitWater (AActor * thing, sector_t * sec, const DVector3 &pos, bool checkabove, bool alert, bool force)
|
||||
enum HitWaterFlags
|
||||
{
|
||||
THW_SMALL = 1 << 0,
|
||||
THW_NOVEL = 1 << 1,
|
||||
};
|
||||
|
||||
bool P_HitWater (AActor * thing, sector_t * sec, const DVector3 &pos, bool checkabove, bool alert, bool force, int flags)
|
||||
{
|
||||
if (thing->player && (thing->player->cheats & CF_PREDICTING))
|
||||
return false;
|
||||
|
|
@ -6740,13 +6754,13 @@ foundone:
|
|||
|
||||
// Don't splash for living things with small vertical velocities.
|
||||
// There are levels where the constant splashing from the monsters gets extremely annoying
|
||||
if (((thing->flags3&MF3_ISMONSTER || thing->player) && thing->Vel.Z >= -6) && !force)
|
||||
if (!(flags & THW_NOVEL) && ((thing->flags3 & MF3_ISMONSTER || thing->player) && thing->Vel.Z >= -6) && !force)
|
||||
return Terrains[terrainnum].IsLiquid;
|
||||
|
||||
splash = &Splashes[splashnum];
|
||||
|
||||
// Small splash for small masses
|
||||
if (thing->Mass < 10)
|
||||
if (flags & THW_SMALL || thing->Mass < 10)
|
||||
smallsplash = true;
|
||||
|
||||
if (!(thing->flags3 & MF3_DONTSPLASH))
|
||||
|
|
@ -6811,7 +6825,8 @@ DEFINE_ACTION_FUNCTION(AActor, HitWater)
|
|||
PARAM_BOOL(checkabove);
|
||||
PARAM_BOOL(alert);
|
||||
PARAM_BOOL(force);
|
||||
ACTION_RETURN_BOOL(P_HitWater(self, sec, DVector3(x, y, z), checkabove, alert, force));
|
||||
PARAM_INT(flags);
|
||||
ACTION_RETURN_BOOL(P_HitWater(self, sec, DVector3(x, y, z), checkabove, alert, force, flags));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -7820,6 +7835,19 @@ void AActor::Revive()
|
|||
target = nullptr;
|
||||
lastenemy = nullptr;
|
||||
|
||||
// Make sure to clear poison damage.
|
||||
PoisonDamageReceived = 0;
|
||||
PoisonDamageTypeReceived = NAME_None;
|
||||
PoisonDurationReceived = 0;
|
||||
PoisonPeriodReceived = 0;
|
||||
Poisoner = nullptr;
|
||||
if (player != nullptr)
|
||||
{
|
||||
player->poisoncount = 0;
|
||||
player->poisoner = nullptr;
|
||||
player->poisontype = player->poisonpaintype = NAME_None;
|
||||
}
|
||||
|
||||
// [RH] If it's a monster, it gets to count as another kill
|
||||
if (CountsAsKill())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@
|
|||
#include "c_console.h"
|
||||
#include "p_spec_thinkers.h"
|
||||
|
||||
static FRandom pr_playerinspecialsector ("PlayerInSpecialSector");
|
||||
static FRandom pr_actorinspecialsector ("ActorInSpecialSector");
|
||||
|
||||
EXTERN_CVAR(Bool, cl_predict_specials)
|
||||
EXTERN_CVAR(Bool, forcewater)
|
||||
|
|
@ -419,23 +419,26 @@ bool P_PredictLine(line_t *line, AActor *mo, int side, int activationType)
|
|||
}
|
||||
|
||||
//
|
||||
// P_PlayerInSpecialSector
|
||||
// P_ActorInSpecialSector
|
||||
// Called every tic frame
|
||||
// that the player origin is in a special sector
|
||||
// that the actor origin is in a special sector
|
||||
//
|
||||
void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
||||
void P_ActorInSpecialSector (AActor *victim, sector_t * sector, F3DFloor* Ffloor)
|
||||
{
|
||||
if (sector == NULL)
|
||||
{
|
||||
// Falling, not all the way down yet?
|
||||
sector = player->mo->Sector;
|
||||
if (!player->mo->isAtZ(sector->LowestFloorAt(player->mo))
|
||||
&& !player->mo->waterlevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
sector = victim->Sector;
|
||||
|
||||
// Falling, not all the way down yet?
|
||||
bool evilAir = (sector->MoreFlags & SECMF_HARMINAIR);
|
||||
bool SolidFfloor = Ffloor != nullptr && (Ffloor->flags & FF_SOLID);
|
||||
if ((!evilAir && !(Ffloor != nullptr && !SolidFfloor)) && !victim->waterlevel)
|
||||
{
|
||||
// [inkoalawetrust] Check for 3D floors differently, because non-FF_INVERTSECTOR ffloors have their floor plane as the 3D floor BOTTOM.
|
||||
double theZ = Ffloor == nullptr ? sector->LowestFloorAt(victim) : Ffloor->top.plane->ZatPoint(victim);
|
||||
if (!victim->isAtZ(theZ))
|
||||
return;
|
||||
}
|
||||
|
||||
// Has hit ground.
|
||||
|
||||
auto Level = sector->Level;
|
||||
|
|
@ -445,7 +448,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
|||
if (sector->damageinterval <= 0)
|
||||
sector->damageinterval = 32; // repair invalid damageinterval values
|
||||
|
||||
if (sector->Flags & (SECF_EXIT1 | SECF_EXIT2))
|
||||
if (victim->player && sector->Flags & (SECF_EXIT1 | SECF_EXIT2))
|
||||
{
|
||||
for (int i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
|
|
@ -464,7 +467,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
|||
// different damage types yet, so that's not happening for now.
|
||||
// [MK] account for subclasses that may have "Full" protection (i.e.: prevent leaky damage)
|
||||
int ironfeet = 0;
|
||||
for (auto i = player->mo->Inventory; i != NULL; i = i->Inventory)
|
||||
for (auto i = victim->Inventory; i != NULL; i = i->Inventory)
|
||||
{
|
||||
if (i->IsKindOf(NAME_PowerIronFeet))
|
||||
{
|
||||
|
|
@ -476,28 +479,28 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
|||
}
|
||||
}
|
||||
|
||||
if (sector->Flags & SECF_ENDGODMODE) player->cheats &= ~CF_GODMODE;
|
||||
if ((ironfeet == 0 || (ironfeet < 2 && pr_playerinspecialsector() < sector->leakydamage)))
|
||||
if (victim->player && sector->Flags & SECF_ENDGODMODE) victim->player->cheats &= ~CF_GODMODE;
|
||||
if ((ironfeet == 0 || (ironfeet < 2 && pr_actorinspecialsector() < sector->leakydamage)))
|
||||
{
|
||||
if (sector->Flags & SECF_HAZARD)
|
||||
if (victim->player && sector->Flags & SECF_HAZARD)
|
||||
{
|
||||
player->hazardcount += sector->damageamount;
|
||||
player->hazardtype = sector->damagetype;
|
||||
player->hazardinterval = sector->damageinterval;
|
||||
victim->player->hazardcount += sector->damageamount;
|
||||
victim->player->hazardtype = sector->damagetype;
|
||||
victim->player->hazardinterval = sector->damageinterval;
|
||||
}
|
||||
else if (Level->time % sector->damageinterval == 0)
|
||||
{
|
||||
if (!(player->cheats & (CF_GODMODE | CF_GODMODE2)))
|
||||
if (!(victim->player && victim->player->cheats & (CF_GODMODE | CF_GODMODE2)))
|
||||
{
|
||||
P_DamageMobj(player->mo, NULL, NULL, sector->damageamount, sector->damagetype);
|
||||
P_DamageMobj(victim, NULL, NULL, sector->damageamount, sector->damagetype);
|
||||
}
|
||||
if ((sector->Flags & SECF_ENDLEVEL) && player->health <= 10 && (!deathmatch || !(dmflags & DF_NO_EXIT)))
|
||||
if (victim->player && (sector->Flags & SECF_ENDLEVEL) && victim->player->health <= 10 && (!deathmatch || !(dmflags & DF_NO_EXIT)))
|
||||
{
|
||||
Level->ExitLevel(0, false);
|
||||
}
|
||||
if (sector->Flags & SECF_DMGTERRAINFX)
|
||||
{
|
||||
P_HitWater(player->mo, player->mo->Sector, player->mo->Pos(), false, true, true);
|
||||
P_HitWater(victim, victim->Sector, victim->Pos(), false, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -506,14 +509,14 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
|||
{
|
||||
if (Level->time % sector->damageinterval == 0)
|
||||
{
|
||||
P_GiveBody(player->mo, -sector->damageamount, 100);
|
||||
P_GiveBody(victim, -sector->damageamount, 100);
|
||||
}
|
||||
}
|
||||
|
||||
if (sector->isSecret())
|
||||
if (victim->player && sector->isSecret())
|
||||
{
|
||||
sector->ClearSecret();
|
||||
P_GiveSecret(Level, player->mo, true, true, sector->Index());
|
||||
P_GiveSecret(Level, victim, true, true, sector->Index());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -652,13 +655,13 @@ DEFINE_ACTION_FUNCTION(FLevelLocals, GiveSecret)
|
|||
|
||||
//============================================================================
|
||||
//
|
||||
// P_PlayerOnSpecialFlat
|
||||
// P_ActorOnSpecialFlat
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void P_PlayerOnSpecialFlat (player_t *player, int floorType)
|
||||
void P_ActorOnSpecialFlat (AActor *victim, int floorType)
|
||||
{
|
||||
auto Level = player->mo->Level;
|
||||
auto Level = victim->Level;
|
||||
|
||||
if (Terrains[floorType].DamageAmount &&
|
||||
!(Level->time % (Terrains[floorType].DamageTimeMask+1)))
|
||||
|
|
@ -668,7 +671,7 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType)
|
|||
if (Terrains[floorType].AllowProtection)
|
||||
{
|
||||
auto pitype = PClass::FindActor(NAME_PowerIronFeet);
|
||||
for (ironfeet = player->mo->Inventory; ironfeet != NULL; ironfeet = ironfeet->Inventory)
|
||||
for (ironfeet = victim->Inventory; ironfeet != NULL; ironfeet = ironfeet->Inventory)
|
||||
{
|
||||
if (ironfeet->IsKindOf (pitype))
|
||||
break;
|
||||
|
|
@ -678,20 +681,18 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType)
|
|||
int damage = 0;
|
||||
if (ironfeet == NULL)
|
||||
{
|
||||
damage = P_DamageMobj (player->mo, NULL, NULL, Terrains[floorType].DamageAmount,
|
||||
damage = P_DamageMobj (victim, NULL, NULL, Terrains[floorType].DamageAmount,
|
||||
Terrains[floorType].DamageMOD);
|
||||
}
|
||||
if (damage > 0 && Terrains[floorType].Splash != -1)
|
||||
{
|
||||
S_Sound (player->mo, CHAN_AUTO, 0,
|
||||
S_Sound (victim, CHAN_AUTO, 0,
|
||||
Splashes[Terrains[floorType].Splash].NormalSplashSound, 1,
|
||||
ATTN_IDLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// P_UpdateSpecials
|
||||
// Animate planes, scroll walls, etc.
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "dsectoreffect.h"
|
||||
#include "doomdata.h"
|
||||
#include "r_state.h"
|
||||
#include "d_player.h"
|
||||
|
||||
class FScanner;
|
||||
struct level_info_t;
|
||||
|
|
@ -90,13 +91,22 @@ bool P_ActivateLine (line_t *ld, AActor *mo, int side, int activationType, DVect
|
|||
bool P_TestActivateLine (line_t *ld, AActor *mo, int side, int activationType, DVector3 *optpos = NULL);
|
||||
bool P_PredictLine (line_t *ld, AActor *mo, int side, int activationType);
|
||||
|
||||
void P_PlayerInSpecialSector (player_t *player, sector_t * sector=NULL);
|
||||
void P_PlayerOnSpecialFlat (player_t *player, int floorType);
|
||||
void P_ActorInSpecialSector (AActor *victim, sector_t * sector = NULL, F3DFloor* Ffloor = NULL);
|
||||
void P_ActorOnSpecialFlat (AActor *victim, int floorType);
|
||||
void P_SectorDamage(FLevelLocals *Level, int tag, int amount, FName type, PClassActor *protectClass, int flags);
|
||||
void P_SetSectorFriction (FLevelLocals *level, int tag, int amount, bool alterFlag);
|
||||
double FrictionToMoveFactor(double friction);
|
||||
void P_GiveSecret(FLevelLocals *Level, AActor *actor, bool printmessage, bool playsound, int sectornum);
|
||||
|
||||
inline bool checkForSpecialSector(AActor* mo, sector_t* sec)
|
||||
{
|
||||
bool afsdnope = !!(mo->flags9 & MF9_NOSECTORDAMAGE);
|
||||
bool afsdforce = !!(mo->flags9 & MF9_FORCESECTORDAMAGE);
|
||||
bool sfhurtmonsters = !!(sec->MoreFlags & SECMF_HURTMONSTERS);
|
||||
bool isplayer = (mo->player != nullptr) && (mo == mo->player->mo);
|
||||
return ((!afsdnope || afsdforce) && (isplayer || sfhurtmonsters || afsdforce));
|
||||
}
|
||||
|
||||
//
|
||||
// getNextSector()
|
||||
// Return sector_t * of sector next to current.
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
#include "actorinlines.h"
|
||||
#include "animations.h"
|
||||
|
||||
static FRandom pr_switchanim ("AnimSwitch");
|
||||
static FCRandom pr_switchanim ("AnimSwitch");
|
||||
|
||||
class DActiveButton : public DThinker
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#define FUDGEFACTOR 10
|
||||
|
||||
static FRandom pr_teleport ("Teleport");
|
||||
static FRandom pr_playerteleport("PlayerTeleport");
|
||||
|
||||
CVAR (Bool, telezoom, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||
|
||||
|
|
@ -271,7 +272,7 @@ DEFINE_ACTION_FUNCTION(AActor, Teleport)
|
|||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
AActor *FLevelLocals::SelectTeleDest (int tid, int tag, bool norandom)
|
||||
AActor *FLevelLocals::SelectTeleDest (int tid, int tag, bool norandom, bool isPlayer)
|
||||
{
|
||||
AActor *searcher;
|
||||
|
||||
|
|
@ -323,7 +324,8 @@ AActor *FLevelLocals::SelectTeleDest (int tid, int tag, bool norandom)
|
|||
{
|
||||
if (count != 1 && !norandom)
|
||||
{
|
||||
count = 1 + (pr_teleport() % count);
|
||||
// Players get their own RNG seed to reduce likelihood of breaking prediction.
|
||||
count = 1 + ((isPlayer ? pr_playerteleport() : pr_teleport()) % count);
|
||||
}
|
||||
searcher = NULL;
|
||||
while (count > 0)
|
||||
|
|
@ -394,7 +396,7 @@ bool FLevelLocals::EV_Teleport (int tid, int tag, line_t *line, int side, AActor
|
|||
{ // Don't teleport if hit back of line, so you can get out of teleporter.
|
||||
return 0;
|
||||
}
|
||||
searcher = SelectTeleDest(tid, tag, predicting);
|
||||
searcher = SelectTeleDest(tid, tag, false, thing->player != nullptr && thing->player->mo == thing);
|
||||
if (searcher == NULL)
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -144,6 +144,8 @@ static DVector3 LastPredictedPosition;
|
|||
static int LastPredictedPortalGroup;
|
||||
static int LastPredictedTic;
|
||||
|
||||
static TArray<FRandom> PredictionRNG;
|
||||
|
||||
static player_t PredictionPlayerBackup;
|
||||
static AActor *PredictionActor;
|
||||
static TArray<uint8_t> PredictionActorBackupArray;
|
||||
|
|
@ -1204,15 +1206,6 @@ DEFINE_ACTION_FUNCTION(APlayerPawn, CheckMusicChange)
|
|||
|
||||
void P_CheckEnvironment(player_t *player)
|
||||
{
|
||||
P_PlayerOnSpecial3DFloor(player);
|
||||
P_PlayerInSpecialSector(player);
|
||||
|
||||
if (!player->mo->isAbove(player->mo->Sector->floorplane.ZatPoint(player->mo)) ||
|
||||
player->mo->waterlevel)
|
||||
{
|
||||
// Player must be touching the floor
|
||||
P_PlayerOnSpecialFlat(player, P_GetThingFloorType(player->mo));
|
||||
}
|
||||
if (player->mo->Vel.Z <= -player->mo->FloatVar(NAME_FallingScreamMinSpeed) &&
|
||||
player->mo->Vel.Z >= -player->mo->FloatVar(NAME_FallingScreamMaxSpeed) && player->mo->alternative == nullptr &&
|
||||
player->mo->waterlevel == 0)
|
||||
|
|
@ -1470,6 +1463,8 @@ void P_PredictPlayer (player_t *player)
|
|||
return;
|
||||
}
|
||||
|
||||
FRandom::SaveRNGState(PredictionRNG);
|
||||
|
||||
// Save original values for restoration later
|
||||
PredictionPlayerBackup.CopyFrom(*player, false);
|
||||
|
||||
|
|
@ -1609,6 +1604,8 @@ void P_UnPredictPlayer ()
|
|||
// Q: Can this happen? If yes, can we continue?
|
||||
}
|
||||
|
||||
FRandom::RestoreRNGState(PredictionRNG);
|
||||
|
||||
AActor *savedcamera = player->camera;
|
||||
|
||||
auto &actInvSel = act->PointerVar<AActor*>(NAME_InvSel);
|
||||
|
|
|
|||
62
src/playsim/p_visualthinker.h
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include "palettecontainer.h"
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VisualThinkers
|
||||
// by Major Cooke
|
||||
// Credit to phantombeta, RicardoLuis0 & RaveYard for aid
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
enum EVisualThinkerFlags
|
||||
{
|
||||
VTF_FlipOffsetX = 1 << 0,
|
||||
VTF_FlipOffsetY = 1 << 1,
|
||||
VTF_FlipX = 1 << 2,
|
||||
VTF_FlipY = 1 << 3, // flip the sprite on the x/y axis.
|
||||
VTF_DontInterpolate = 1 << 4, // disable all interpolation
|
||||
VTF_AddLightLevel = 1 << 5, // adds sector light level to 'LightLevel'
|
||||
};
|
||||
|
||||
class DVisualThinker : public DThinker
|
||||
{
|
||||
DECLARE_CLASS(DVisualThinker, DThinker);
|
||||
void UpdateSector(subsector_t * newSubsector);
|
||||
public:
|
||||
DVector3 Prev;
|
||||
DVector2 Scale,
|
||||
Offset;
|
||||
float PrevRoll;
|
||||
int16_t LightLevel;
|
||||
FTranslationID Translation;
|
||||
FTextureID AnimatedTexture;
|
||||
sector_t *cursector;
|
||||
|
||||
int flags;
|
||||
|
||||
// internal only variables
|
||||
particle_t PT;
|
||||
|
||||
DVisualThinker();
|
||||
void Construct();
|
||||
void OnDestroy() override;
|
||||
|
||||
static DVisualThinker* NewVisualThinker(FLevelLocals* Level, PClass* type);
|
||||
void SetTranslation(FName trname);
|
||||
int GetRenderStyle();
|
||||
bool isFrozen();
|
||||
int GetLightLevel(sector_t *rendersector) const;
|
||||
FVector3 InterpolatedPosition(double ticFrac) const;
|
||||
float InterpolatedRoll(double ticFrac) const;
|
||||
|
||||
void Tick() override;
|
||||
void UpdateSpriteInfo();
|
||||
void UpdateSector();
|
||||
void Serialize(FSerializer& arc) override;
|
||||
|
||||
float GetOffset(bool y) const;
|
||||
};
|
||||
|
|
@ -45,6 +45,8 @@
|
|||
#include "hw_walldispatcher.h"
|
||||
#include "hw_flatdispatcher.h"
|
||||
|
||||
#include "p_visualthinker.h"
|
||||
|
||||
#ifdef ARCH_IA32
|
||||
#include <immintrin.h>
|
||||
#endif // ARCH_IA32
|
||||
|
|
@ -289,12 +291,12 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip, FRenderState& state)
|
|||
angle_t startAngleR = clipperr.PointToPseudoAngle(seg->v2->fX(), seg->v2->fY());
|
||||
angle_t endAngleR = clipperr.PointToPseudoAngle(seg->v1->fX(), seg->v1->fY());
|
||||
|
||||
if(r_radarclipper && !(Level->flags3 & LEVEL3_NOFOGOFWAR) && (startAngleR - endAngleR >= ANGLE_180))
|
||||
if(Viewpoint.IsAllowedOoB() && r_radarclipper && !(Level->flags3 & LEVEL3_NOFOGOFWAR) && (startAngleR - endAngleR >= ANGLE_180))
|
||||
{
|
||||
if (!seg->backsector) clipperr.SafeAddClipRange(startAngleR, endAngleR);
|
||||
else if((seg->sidedef != nullptr) && !uint8_t(seg->sidedef->Flags & WALLF_POLYOBJ) && (currentsector->sectornum != seg->backsector->sectornum))
|
||||
{
|
||||
if (in_area == area_default) in_area = hw_CheckViewArea(seg->v2, seg->v1, seg->frontsector, seg->backsector);
|
||||
if (in_area == area_default) in_area = hw_CheckViewArea(seg->v1, seg->v2, seg->frontsector, seg->backsector);
|
||||
backsector = hw_FakeFlat(drawctx, seg->backsector, in_area, true);
|
||||
if (hw_CheckClip(seg->sidedef, currentsector, backsector)) clipperr.SafeAddClipRange(startAngleR, endAngleR);
|
||||
}
|
||||
|
|
@ -670,10 +672,9 @@ void HWDrawInfo::RenderParticles(subsector_t *sub, sector_t *front, FRenderState
|
|||
int clipres = mClipPortal->ClipPoint(sp->PT.Pos.XY());
|
||||
if (clipres == PClip_InFront) continue;
|
||||
}
|
||||
|
||||
assert(sp->spr);
|
||||
|
||||
sp->spr->ProcessParticle(this, state, &sp->PT, front, sp);
|
||||
HWSprite sprite;
|
||||
sprite.ProcessParticle(this, state, &sp->PT, front, sp);
|
||||
}
|
||||
for (int i = Level->ParticlesInSubsec[sub->Index()]; i != NO_PARTICLE; i = Level->Particles[i].snext)
|
||||
{
|
||||
|
|
@ -1015,7 +1016,7 @@ void HWDrawInfo::RenderBSP(void *node, bool drawpsprites, FRenderState& state)
|
|||
// Give the DrawInfo the viewpoint in fixed point because that's what the nodes are.
|
||||
viewx = FLOAT2FIXED(Viewpoint.Pos.X);
|
||||
viewy = FLOAT2FIXED(Viewpoint.Pos.Y);
|
||||
if (r_radarclipper && !(Level->flags3 & LEVEL3_NOFOGOFWAR) && Viewpoint.IsAllowedOoB() && (Viewpoint.camera->ViewPos->Flags & VPSF_ABSOLUTEOFFSET))
|
||||
if (r_radarclipper && !(Level->flags3 & LEVEL3_NOFOGOFWAR) && Viewpoint.IsAllowedOoB())
|
||||
{
|
||||
if (Viewpoint.camera->tracer != nullptr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@
|
|||
#include "hw_drawcontext.h"
|
||||
#include "quaternion.h"
|
||||
|
||||
#include "p_visualthinker.h"
|
||||
|
||||
extern TArray<spritedef_t> sprites;
|
||||
extern TArray<spriteframe_t> SpriteFrames;
|
||||
extern uint32_t r_renderercaps;
|
||||
|
|
@ -1650,7 +1652,7 @@ void HWSprite::AdjustVisualThinker(HWDrawInfo* di, DVisualThinker* spr, sector_t
|
|||
? TexAnim.UpdateStandaloneAnimation(spr->PT.animData, di->Level->maptime + timefrac)
|
||||
: spr->PT.texture, !custom_anim);
|
||||
|
||||
if (spr->bDontInterpolate)
|
||||
if (spr->flags & VTF_DontInterpolate)
|
||||
timefrac = 0.;
|
||||
|
||||
FVector3 interp = spr->InterpolatedPosition(timefrac);
|
||||
|
|
@ -1680,13 +1682,12 @@ void HWSprite::AdjustVisualThinker(HWDrawInfo* di, DVisualThinker* spr, sector_t
|
|||
double mult = 1.0 / sqrt(ps); // shrink slightly
|
||||
r.Scale(mult * ps, mult);
|
||||
}
|
||||
|
||||
if (spr->bXFlip)
|
||||
if (spr->flags & VTF_FlipX)
|
||||
{
|
||||
std::swap(ul,ur);
|
||||
r.left = -r.width - r.left; // mirror the sprite's x-offset
|
||||
}
|
||||
if (spr->bYFlip) std::swap(vt,vb);
|
||||
if (spr->flags & VTF_FlipY) std::swap(vt,vb);
|
||||
|
||||
float viewvecX = vp.ViewVector.X;
|
||||
float viewvecY = vp.ViewVector.Y;
|
||||
|
|
|
|||
|
|
@ -94,8 +94,8 @@ struct InterpolationViewer
|
|||
|
||||
// PRIVATE DATA DECLARATIONS -----------------------------------------------
|
||||
static TArray<InterpolationViewer> PastViewers;
|
||||
static FRandom pr_torchflicker ("TorchFlicker");
|
||||
static FRandom pr_hom;
|
||||
static FCRandom pr_torchflicker ("TorchFlicker");
|
||||
static FCRandom pr_hom;
|
||||
bool NoInterpolateView; // GL needs access to this.
|
||||
static TArray<DVector3a> InterpolationPath;
|
||||
|
||||
|
|
@ -464,7 +464,8 @@ bool P_NoInterpolation(player_t const *player, AActor const *actor)
|
|||
&& player->mo->reactiontime == 0
|
||||
&& !NoInterpolateView
|
||||
&& !paused
|
||||
&& !LocalKeyboardTurner;
|
||||
&& !LocalKeyboardTurner
|
||||
&& !player->mo->isFrozen();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@
|
|||
|
||||
extern FRandom pr_exrandom;
|
||||
|
||||
static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cls);
|
||||
static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor *cls);
|
||||
static FxExpression *ParseRandom2(FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cls, bool client);
|
||||
static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor *cls, bool client);
|
||||
static FxExpression *ParseRandom2(FScanner &sc, PClassActor *cls, bool client);
|
||||
static FxExpression *ParseAbs(FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseAtan2(FScanner &sc, FName identifier, PClassActor *cls);
|
||||
static FxExpression *ParseMinMax(FScanner &sc, FName identifier, PClassActor *cls);
|
||||
|
|
@ -491,12 +491,17 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls)
|
|||
{
|
||||
case NAME_Random:
|
||||
case NAME_FRandom:
|
||||
return ParseRandom(sc, identifier, cls);
|
||||
case NAME_CRandom:
|
||||
case NAME_CFRandom:
|
||||
return ParseRandom(sc, identifier, cls, identifier == NAME_CRandom || identifier == NAME_CFRandom);
|
||||
case NAME_RandomPick:
|
||||
case NAME_FRandomPick:
|
||||
return ParseRandomPick(sc, identifier, cls);
|
||||
case NAME_CRandomPick:
|
||||
case NAME_CFRandomPick:
|
||||
return ParseRandomPick(sc, identifier, cls, identifier == NAME_CRandomPick || identifier == NAME_CFRandomPick);
|
||||
case NAME_Random2:
|
||||
return ParseRandom2(sc, cls);
|
||||
case NAME_CRandom2:
|
||||
return ParseRandom2(sc, cls, identifier == NAME_CRandom2);
|
||||
default:
|
||||
if (cls != nullptr)
|
||||
{
|
||||
|
|
@ -559,26 +564,26 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static FRandom *ParseRNG(FScanner &sc)
|
||||
static FRandom *ParseRNG(FScanner &sc, bool client)
|
||||
{
|
||||
FRandom *rng;
|
||||
|
||||
if (sc.CheckToken('['))
|
||||
{
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
rng = FRandom::StaticFindRNG(sc.String);
|
||||
rng = FRandom::StaticFindRNG(sc.String, client);
|
||||
sc.MustGetToken(']');
|
||||
}
|
||||
else
|
||||
{
|
||||
rng = &pr_exrandom;
|
||||
rng = client ? &M_Random : &pr_exrandom;
|
||||
}
|
||||
return rng;
|
||||
}
|
||||
|
||||
static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cls)
|
||||
static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cls, bool client)
|
||||
{
|
||||
FRandom *rng = ParseRNG(sc);
|
||||
FRandom *rng = ParseRNG(sc, client);
|
||||
|
||||
sc.MustGetToken('(');
|
||||
FxExpression *min = ParseExpressionM (sc, cls);
|
||||
|
|
@ -586,7 +591,7 @@ static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cl
|
|||
FxExpression *max = ParseExpressionM (sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
|
||||
if (identifier == NAME_Random)
|
||||
if (identifier == NAME_Random || identifier == NAME_CRandom)
|
||||
{
|
||||
return new FxRandom(rng, min, max, sc, true);
|
||||
}
|
||||
|
|
@ -596,7 +601,7 @@ static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cl
|
|||
}
|
||||
}
|
||||
|
||||
static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor *cls)
|
||||
static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor *cls, bool client)
|
||||
{
|
||||
bool floaty = identifier == NAME_FRandomPick;
|
||||
FRandom *rng;
|
||||
|
|
@ -604,7 +609,7 @@ static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor
|
|||
list.Clear();
|
||||
int index = 0;
|
||||
|
||||
rng = ParseRNG(sc);
|
||||
rng = ParseRNG(sc, client);
|
||||
sc.MustGetToken('(');
|
||||
|
||||
for (;;)
|
||||
|
|
@ -618,9 +623,9 @@ static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor
|
|||
return new FxRandomPick(rng, list, floaty, sc, true);
|
||||
}
|
||||
|
||||
static FxExpression *ParseRandom2(FScanner &sc, PClassActor *cls)
|
||||
static FxExpression *ParseRandom2(FScanner &sc, PClassActor *cls, bool client)
|
||||
{
|
||||
FRandom *rng = ParseRNG(sc);
|
||||
FRandom *rng = ParseRNG(sc, client);
|
||||
FxExpression *mask = NULL;
|
||||
|
||||
sc.MustGetToken('(');
|
||||
|
|
|
|||
|
|
@ -49,9 +49,6 @@
|
|||
#include "v_text.h"
|
||||
#include "m_argv.h"
|
||||
#include "v_video.h"
|
||||
#ifndef _MSC_VER
|
||||
#include "i_system.h" // for strlwr()
|
||||
#endif // !_MSC_VER
|
||||
|
||||
void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns);
|
||||
EXTERN_CVAR(Bool, strictdecorate);
|
||||
|
|
@ -945,15 +942,12 @@ static void ParseActorProperty(FScanner &sc, Baggage &bag)
|
|||
"Spawn", "See", "Melee", "Missile", "Pain", "Death", "XDeath", "Burn",
|
||||
"Ice", "Raise", "Crash", "Crush", "Wound", "Disintegrate", "Heal", NULL };
|
||||
|
||||
strlwr (sc.String);
|
||||
|
||||
FString propname = sc.String;
|
||||
|
||||
if (sc.CheckString ("."))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
propname += '.';
|
||||
strlwr (sc.String);
|
||||
propname += sc.String;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -45,9 +45,6 @@
|
|||
#include "thingdef.h"
|
||||
#include "codegen.h"
|
||||
#include "backend/codegen_doom.h"
|
||||
#ifndef _MSC_VER
|
||||
#include "i_system.h" // for strlwr()
|
||||
#endif // !_MSC_VER
|
||||
|
||||
//==========================================================================
|
||||
//***
|
||||
|
|
@ -562,11 +559,8 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg
|
|||
|
||||
FxExpression* ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag)
|
||||
{
|
||||
// Make the action name lowercase
|
||||
strlwr (sc.String);
|
||||
|
||||
FxExpression *call = DoActionSpecials(sc, state, bag);
|
||||
if (call != NULL)
|
||||
if (call != nullptr)
|
||||
{
|
||||
return call;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -351,7 +351,9 @@ static FFlagDef ActorFlagDefs[]=
|
|||
DEFINE_FLAG(MF9, SHADOWBLOCK, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, SHADOWAIMVERT, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, DECOUPLEDANIMATIONS, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, NOSECTORDAMAGE, AActor, flags9),
|
||||
DEFINE_PROTECTED_FLAG(MF9, ISPUFF, AActor, flags9), //[AA] was spawned by SpawnPuff
|
||||
DEFINE_FLAG(MF9, FORCESECTORDAMAGE, AActor, flags9),
|
||||
|
||||
// Effect flags
|
||||
DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),
|
||||
|
|
|
|||
|
|
@ -869,6 +869,34 @@ DEFINE_ACTION_FUNCTION_NATIVE(_Sector, SetXOffset, SetXOffset)
|
|||
ACTION_RETURN_INT(self->GetLightLevel());
|
||||
}
|
||||
|
||||
static void SetPlaneReflectivity(sector_t* self, int pos, double val)
|
||||
{
|
||||
if (pos < 0 || pos > 1) ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "pos must be either 0 or 1");
|
||||
self->SetPlaneReflectivity(pos, val);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Sector, SetPlaneReflectivity, SetPlaneReflectivity)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
|
||||
PARAM_INT(pos);
|
||||
PARAM_FLOAT(val)
|
||||
SetPlaneReflectivity(self, pos, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static double GetPlaneReflectivity(sector_t* self, int pos)
|
||||
{
|
||||
if (pos < 0 || pos > 1) ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "pos must be either 0 or 1");
|
||||
return self->GetPlaneReflectivity(pos);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Sector, GetPlaneReflectivity, GetPlaneReflectivity)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
|
||||
PARAM_INT(pos);
|
||||
ACTION_RETURN_FLOAT(GetPlaneReflectivity(self, pos));
|
||||
}
|
||||
|
||||
static int PortalBlocksView(sector_t *self, int pos)
|
||||
{
|
||||
return self->PortalBlocksView(pos);
|
||||
|
|
@ -2253,7 +2281,8 @@ void FormatMapName(FLevelLocals *self, int cr, FString *result)
|
|||
// If a label is specified, use it uncontitionally here.
|
||||
if (self->info->MapLabel.IsNotEmpty())
|
||||
{
|
||||
*result << self->info->MapLabel << ": ";
|
||||
if (self->info->MapLabel.Compare("*"))
|
||||
*result << self->info->MapLabel << ": ";
|
||||
}
|
||||
else if (am_showmaplabel == 1 || (am_showmaplabel == 2 && !ishub))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -161,17 +161,14 @@ bool ZCCDoomCompiler::CompileProperties(PClass *type, TArray<ZCC_Property *> &Pr
|
|||
|
||||
bool ZCCDoomCompiler::CompileFlagDefs(PClass *type, TArray<ZCC_FlagDef *> &Properties, FName prefix)
|
||||
{
|
||||
if (!type->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
||||
{
|
||||
Error(Properties[0], "Flags can only be defined for actors");
|
||||
return false;
|
||||
}
|
||||
//[RL0] allow property-less flagdefs for non-actors
|
||||
bool isActor = type->IsDescendantOf(RUNTIME_CLASS(AActor));
|
||||
for (auto p : Properties)
|
||||
{
|
||||
PField *field;
|
||||
FName referenced = FName(p->RefName);
|
||||
|
||||
if (FName(p->NodeName) == FName("prefix") && fileSystem.GetFileContainer(Lump) == 0)
|
||||
if (isActor && FName(p->NodeName) == FName("prefix") && fileSystem.GetFileContainer(Lump) == 0)
|
||||
{
|
||||
// only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use.
|
||||
prefix = referenced;
|
||||
|
|
@ -191,24 +188,28 @@ bool ZCCDoomCompiler::CompileFlagDefs(PClass *type, TArray<ZCC_FlagDef *> &Prope
|
|||
}
|
||||
}
|
||||
else field = nullptr;
|
||||
|
||||
|
||||
FString qualifiedname;
|
||||
// Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen.
|
||||
// All these will be removed from the symbol table after the compiler finishes to free up the allocated space.
|
||||
|
||||
FName name = FName(p->NodeName);
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (i == 0) qualifiedname.Format("@flagdef@%s", name.GetChars());
|
||||
else
|
||||
{
|
||||
if (prefix == NAME_None) continue;
|
||||
qualifiedname.Format("@flagdef@%s.%s", prefix.GetChars(), name.GetChars());
|
||||
}
|
||||
|
||||
if (!type->VMType->Symbols.AddSymbol(Create<PPropFlag>(qualifiedname, field, p->BitValue, i == 0 && prefix != NAME_None)))
|
||||
if(isActor)
|
||||
{
|
||||
FString qualifiedname;
|
||||
// Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen.
|
||||
// All these will be removed from the symbol table after the compiler finishes to free up the allocated space.
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Error(p, "Unable to add flag definition %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars());
|
||||
if (i == 0) qualifiedname.Format("@flagdef@%s", name.GetChars());
|
||||
else
|
||||
{
|
||||
if (prefix == NAME_None) continue;
|
||||
qualifiedname.Format("@flagdef@%s.%s", prefix.GetChars(), name.GetChars());
|
||||
}
|
||||
|
||||
if (!type->VMType->Symbols.AddSymbol(Create<PPropFlag>(qualifiedname, field, p->BitValue, i == 0 && prefix != NAME_None)))
|
||||
{
|
||||
Error(p, "Unable to add flag definition %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1163,7 +1163,7 @@ TArray<uint8_t> DoomSoundEngine::ReadSound(int lumpnum)
|
|||
// This is overridden to use a synchronized RNG.
|
||||
//
|
||||
//==========================================================================
|
||||
static FRandom pr_randsound("RandSound");
|
||||
static FCRandom pr_randsound("RandSound");
|
||||
|
||||
FSoundID DoomSoundEngine::PickReplacement(FSoundID refid)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ static const hexenseq_t HexenSequences[] = {
|
|||
|
||||
static int SeqTrans[MAX_SNDSEQS*3];
|
||||
|
||||
static FRandom pr_sndseq ("SndSeq");
|
||||
static FCRandom pr_sndseq ("SndSeq");
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ const char *GetVersionString();
|
|||
|
||||
// These are for zscript versioning.
|
||||
#define ZSCRIPT_VER_MAJOR 4
|
||||
#define ZSCRIPT_VER_MINOR 13
|
||||
#define ZSCRIPT_VER_MINOR 14
|
||||
#define ZSCRIPT_VER_REVISION 0
|
||||
|
||||
// This should always refer to the VkDoom version a derived port is based on and not reflect the derived port's version number!
|
||||
|
|
|
|||
BIN
wadsrc/static/graphics/AMMNUM0.png
Normal file
|
After Width: | Height: | Size: 89 B |
BIN
wadsrc/static/graphics/AMMNUM1.png
Normal file
|
After Width: | Height: | Size: 88 B |
BIN
wadsrc/static/graphics/AMMNUM2.png
Normal file
|
After Width: | Height: | Size: 86 B |
BIN
wadsrc/static/graphics/AMMNUM3.png
Normal file
|
After Width: | Height: | Size: 84 B |
BIN
wadsrc/static/graphics/AMMNUM4.png
Normal file
|
After Width: | Height: | Size: 89 B |
BIN
wadsrc/static/graphics/AMMNUM5.png
Normal file
|
After Width: | Height: | Size: 87 B |
BIN
wadsrc/static/graphics/AMMNUM6.png
Normal file
|
After Width: | Height: | Size: 89 B |
BIN
wadsrc/static/graphics/AMMNUM7.png
Normal file
|
After Width: | Height: | Size: 87 B |
BIN
wadsrc/static/graphics/AMMNUM8.png
Normal file
|
After Width: | Height: | Size: 86 B |
BIN
wadsrc/static/graphics/AMMNUM9.png
Normal file
|
After Width: | Height: | Size: 89 B |
|
|
@ -78,6 +78,7 @@ DoomEdNums
|
|||
9081 = SkyPicker
|
||||
9082 = SectorSilencer
|
||||
9083 = SkyCamCompat
|
||||
9084 = OrthographicCamera
|
||||
9200 = Decal
|
||||
9300 = "$PolyAnchor"
|
||||
9301 = "$PolySpawn"
|
||||
|
|
|
|||
|
|
@ -773,7 +773,7 @@ class Actor : Thinker native
|
|||
native Actor SpawnPuff(class<Actor> pufftype, vector3 pos, double hitdir, double particledir, int updown, int flags = 0, Actor victim = null);
|
||||
native void SpawnBlood (Vector3 pos1, double dir, int damage);
|
||||
native void BloodSplatter (Vector3 pos, double hitangle, bool axe = false);
|
||||
native bool HitWater (sector sec, Vector3 pos, bool checkabove = false, bool alert = true, bool force = false);
|
||||
native bool HitWater (sector sec, Vector3 pos, bool checkabove = false, bool alert = true, bool force = false, int flags = 0);
|
||||
native void PlaySpawnSound(Actor missile);
|
||||
native clearscope bool CountsAsKill() const;
|
||||
|
||||
|
|
@ -844,6 +844,7 @@ class Actor : Thinker native
|
|||
native void Thrust(double speed = 1e37, double angle = 1e37);
|
||||
native clearscope bool isFriend(Actor other) const;
|
||||
native clearscope bool isHostile(Actor other) const;
|
||||
native clearscope bool ShouldPassThroughPlayer(Actor other) const;
|
||||
native void AdjustFloorClip();
|
||||
native clearscope DropItem GetDropItems() const;
|
||||
native void CopyFriendliness (Actor other, bool changeTarget, bool resetHealth = true);
|
||||
|
|
@ -851,6 +852,7 @@ class Actor : Thinker native
|
|||
native bool LookForTid(bool allaround, LookExParams params = null);
|
||||
native bool LookForEnemies(bool allaround, LookExParams params = null);
|
||||
native bool LookForPlayers(bool allaround, LookExParams params = null);
|
||||
native int LookForEnemiesEx(out Array<Actor> targets, double range = -1, bool noPlayers = true, bool allaround = false, LookExParams params = null);
|
||||
native bool TeleportMove(Vector3 pos, bool telefrag, bool modifyactor = true);
|
||||
native clearscope double DistanceBySpeed(Actor other, double speed) const;
|
||||
native name GetSpecies();
|
||||
|
|
|
|||
|
|
@ -139,6 +139,11 @@ extend class Actor
|
|||
{ // Must be monster, player, missile, touchy or vulnerable
|
||||
continue;
|
||||
}
|
||||
if (player && ShouldPassThroughPlayer(mo))
|
||||
{
|
||||
// Don't blast friendly players if collision is disabled.
|
||||
continue;
|
||||
}
|
||||
if (Distance2D(mo) > radius)
|
||||
{ // Out of range
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -468,11 +468,12 @@ class HexenArmor : Armor
|
|||
|
||||
override Inventory CreateCopy (Actor other)
|
||||
{
|
||||
// Like BasicArmor, HexenArmor is used in the inventory but not the map.
|
||||
// health is the slot this armor occupies.
|
||||
// Unlike BasicArmor, Hexen's armor pieces directly inherit from this class.
|
||||
// Health is the slot this armor occupies.
|
||||
// Amount is the quantity to give (0 = normal max).
|
||||
let copy = HexenArmor(Spawn(GetHexenArmorClass()));
|
||||
copy.AddArmorToSlot (health, Amount);
|
||||
let copy = HexenArmor(Spawn(GetClass()));
|
||||
copy.Health = Health;
|
||||
copy.Amount = Amount;
|
||||
GoAwayAndDie ();
|
||||
return copy;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,13 @@ class WeaponPiece : Inventory
|
|||
|
||||
property number: PieceValue;
|
||||
property weapon: WeaponClass;
|
||||
|
||||
// Account for weapon replacers, but make sure it's still a Weapon
|
||||
clearscope class<Weapon> GetWeaponClass() const
|
||||
{
|
||||
class<Weapon> type = WeaponClass ? (class<Weapon>)(GetReplacement(WeaponClass)) : null;
|
||||
return type ? type : WeaponClass;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
@ -74,7 +81,11 @@ class WeaponPiece : Inventory
|
|||
return false;
|
||||
}
|
||||
|
||||
let Defaults = GetDefaultByType(WeaponClass);
|
||||
class<Weapon> type = GetWeaponClass();
|
||||
if (!type)
|
||||
return false;
|
||||
|
||||
let Defaults = GetDefaultByType(type);
|
||||
|
||||
bool gaveSome = !!(toucher.GiveAmmo (Defaults.AmmoType1, Defaults.AmmoGive1) +
|
||||
toucher.GiveAmmo (Defaults.AmmoType2, Defaults.AmmoGive2));
|
||||
|
|
@ -94,11 +105,15 @@ class WeaponPiece : Inventory
|
|||
|
||||
override bool TryPickup (in out Actor toucher)
|
||||
{
|
||||
class<Weapon> type = GetWeaponClass();
|
||||
if (!type)
|
||||
return false;
|
||||
|
||||
Inventory item;
|
||||
WeaponHolder hold = NULL;
|
||||
bool shouldStay = ShouldStay ();
|
||||
int gaveAmmo;
|
||||
let Defaults = GetDefaultByType(WeaponClass);
|
||||
let Defaults = GetDefaultByType(type);
|
||||
|
||||
FullWeapon = NULL;
|
||||
for(item=toucher.Inv; item; item=item.Inv)
|
||||
|
|
@ -106,6 +121,7 @@ class WeaponPiece : Inventory
|
|||
hold = WeaponHolder(item);
|
||||
if (hold != null)
|
||||
{
|
||||
// Intentionally check against the unreplaced class
|
||||
if (hold.PieceWeapon == WeaponClass)
|
||||
{
|
||||
break;
|
||||
|
|
@ -153,9 +169,9 @@ class WeaponPiece : Inventory
|
|||
// Check if weapon assembled
|
||||
if (hold.PieceMask == (1 << Defaults.health) - 1)
|
||||
{
|
||||
if (!toucher.FindInventory (WeaponClass))
|
||||
if (!toucher.FindInventory (type))
|
||||
{
|
||||
FullWeapon= Weapon(Spawn(WeaponClass));
|
||||
FullWeapon= Weapon(Spawn(type));
|
||||
|
||||
// The weapon itself should not give more ammo to the player.
|
||||
FullWeapon.AmmoGive1 = 0;
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ class PlayerPawn : Actor
|
|||
flagdef CanSuperMorph: PlayerFlags, 1;
|
||||
flagdef CrouchableMorph: PlayerFlags, 2;
|
||||
flagdef WeaponLevel2Ended: PlayerFlags, 3;
|
||||
//PF_VOODOO_ZOMBIE
|
||||
flagdef MakeFootsteps: PlayerFlags, 5; //[inkoalawetrust] Use footstep system virtual.
|
||||
|
||||
enum EPrivatePlayerFlags
|
||||
{
|
||||
|
|
@ -1690,6 +1692,7 @@ class PlayerPawn : Actor
|
|||
CheckPitch();
|
||||
HandleMovement();
|
||||
CalcHeight ();
|
||||
if (bMakeFootsteps) MakeFootsteps();
|
||||
|
||||
if (!(player.cheats & CF_PREDICTING))
|
||||
{
|
||||
|
|
@ -1718,6 +1721,106 @@ class PlayerPawn : Actor
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Handle player footstep sounds.
|
||||
// Default footstep handling.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
int footstepCounter;
|
||||
double footstepLength;
|
||||
bool footstepFoot;
|
||||
|
||||
void DoFootstep(TerrainDef Ground)
|
||||
{
|
||||
Sound Step = Ground.StepSound;
|
||||
|
||||
//Generic foot-agnostic sound takes precedence.
|
||||
if(!Step)
|
||||
{
|
||||
//Apparently most people walk with their right foot first, so assume that here.
|
||||
if (!footstepFoot)
|
||||
{
|
||||
Step = Ground.LeftStepSound;
|
||||
}
|
||||
else
|
||||
{
|
||||
Step = Ground.RightStepSound;
|
||||
}
|
||||
|
||||
footstepFoot = !footstepFoot;
|
||||
}
|
||||
|
||||
if(Step)
|
||||
{
|
||||
A_StartSound(Step, flags: CHANF_OVERLAP, volume: Ground.StepVolume * snd_footstepvolume);
|
||||
}
|
||||
|
||||
//Steps make splashes regardless.
|
||||
bool Heavy = (Mass >= 200) ? 0 : THW_SMALL; //Big player makes big splash.
|
||||
HitWater(CurSector, (Pos.XY, CurSector.FloorPlane.ZatPoint(Pos.XY)), true, false, flags: Heavy | THW_NOVEL);
|
||||
}
|
||||
|
||||
virtual void MakeFootsteps()
|
||||
{
|
||||
if(pos.z > floorz) return;
|
||||
|
||||
let Ground = GetFloorTerrain();
|
||||
|
||||
if(Ground && (player.cmd.forwardMove != 0 || player.cmd.sideMove != 0))
|
||||
{
|
||||
int Delay = (player.cmd.buttons & BT_RUN) ? Ground.RunStepTics : Ground.WalkStepTics;
|
||||
|
||||
if((player.cmd.buttons ^ player.oldbuttons) & BT_RUN)
|
||||
{ // zero out counters when starting/stopping a run
|
||||
footstepCounter = 0;
|
||||
footstepLength = Ground.StepDistance;
|
||||
}
|
||||
|
||||
if(Ground.StepDistance > 0)
|
||||
{ // distance-based terrain
|
||||
footstepCounter = 0;
|
||||
|
||||
double moveVel = vel.xy.length();
|
||||
|
||||
if(moveVel > Ground.StepDistanceMinVel)
|
||||
{
|
||||
footstepLength += moveVel;
|
||||
|
||||
while(footstepLength > Ground.StepDistance)
|
||||
{
|
||||
footstepLength -= Ground.StepDistance;
|
||||
DoFootstep(Ground);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
footstepLength = Ground.StepDistance;
|
||||
}
|
||||
|
||||
}
|
||||
else if(Delay > 0)
|
||||
{ // delay-based terrain
|
||||
footstepLength = 0;
|
||||
|
||||
if(footstepCounter % Delay == 0)
|
||||
{
|
||||
DoFootstep(Ground);
|
||||
}
|
||||
|
||||
footstepCounter = (footstepCounter + 1) % Delay;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
footstepCounter = 0;
|
||||
footstepLength = Ground.StepDistance;
|
||||
footstepFoot = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// PROC P_BringUpWeapon
|
||||
|
|
|
|||
|
|
@ -212,3 +212,35 @@ class SpectatorCamera : Actor
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Class OrthographicCamera : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
+NOBLOCKMAP
|
||||
+NOINTERACTION
|
||||
CameraHeight 0;
|
||||
RenderStyle "None";
|
||||
}
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
UpdateViewPos();
|
||||
}
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
if (current != args[0])
|
||||
UpdateViewPos();
|
||||
|
||||
Super.Tick();
|
||||
}
|
||||
|
||||
protected int current;
|
||||
protected void UpdateViewPos()
|
||||
{
|
||||
current = args[0];
|
||||
SetViewPos((-abs(max(1.0, double(current))), 0, 0), VPSF_ORTHOGRAPHIC|VPSF_ALLOWOUTOFBOUNDS);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1410,6 +1410,8 @@ enum ELevelFlags
|
|||
LEVEL3_AVOIDMELEE = 0x00020000, // global flag needed for proper MBF support.
|
||||
LEVEL3_NOJUMPDOWN = 0x00040000, // only for MBF21. Inverse of MBF's dog_jumping flag.
|
||||
LEVEL3_LIGHTCREATED = 0x00080000, // a light had been created in the last frame
|
||||
LEVEL3_NOFOGOFWAR = 0x00100000, // disables effect of r_radarclipper CVAR on this map
|
||||
LEVEL3_SECRET = 0x00200000, // level is a secret level
|
||||
|
||||
VKDLEVELFLAG_NOUSERSAVE = 0x00000001,
|
||||
VKDLEVELFLAG_NOAUTOMAP = 0x00000002,
|
||||
|
|
@ -1471,6 +1473,12 @@ enum ECompatFlags
|
|||
COMPATF2_VOODOO_ZOMBIES = 1 << 15, // allow playerinfo, playerpawn, and voodoo health to all be different, and allow monster targetting of 'dead' players that have positive health
|
||||
};
|
||||
|
||||
enum HitWaterFlags
|
||||
{
|
||||
THW_SMALL = 1 << 0,
|
||||
THW_NOVEL = 1 << 1,
|
||||
};
|
||||
|
||||
const M_E = 2.7182818284590452354; // e
|
||||
const M_LOG2E = 1.4426950408889634074; // log_2 e
|
||||
const M_LOG10E = 0.43429448190325182765; // log_10 e
|
||||
|
|
@ -1513,3 +1521,13 @@ enum EModelFlags
|
|||
MDL_CORRECTPIXELSTRETCH = 1<<13, // ensure model does not distort with pixel stretch when pitch/roll is applied
|
||||
MDL_FORCECULLBACKFACES = 1<<14,
|
||||
};
|
||||
|
||||
enum EVisualThinkerFlags
|
||||
{
|
||||
VTF_FlipOffsetX = 1 << 0,
|
||||
VTF_FlipOffsetY = 1 << 1,
|
||||
VTF_FlipX = 1 << 2,
|
||||
VTF_FlipY = 1 << 3, // flip the sprite on the x/y axis.
|
||||
VTF_DontInterpolate = 1 << 4, // disable all interpolation
|
||||
VTF_AddLightLevel = 1 << 5, // adds sector light level to 'LightLevel'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -621,6 +621,9 @@ struct TerrainDef native
|
|||
native bool DamageOnLand;
|
||||
native double Friction;
|
||||
native double MoveFactor;
|
||||
native Sound StepSound;
|
||||
native double StepDistance;
|
||||
native double StepDistanceMinVel;
|
||||
};
|
||||
|
||||
enum EPickStart
|
||||
|
|
|
|||
|
|
@ -15,27 +15,29 @@ enum ESoundFlags
|
|||
// modifier flags
|
||||
CHAN_LISTENERZ = 8,
|
||||
CHAN_MAYBE_LOCAL = 16,
|
||||
CHAN_UI = 32,
|
||||
CHAN_NOPAUSE = 64,
|
||||
CHAN_UI = 32, // Do not record sound in savegames.
|
||||
CHAN_NOPAUSE = 64, // Do not pause this sound in menus.
|
||||
CHAN_LOOP = 256,
|
||||
CHAN_PICKUP = (CHAN_ITEM|CHAN_MAYBE_LOCAL), // Do not use this with A_StartSound! It would not do what is expected.
|
||||
CHAN_NOSTOP = 4096,
|
||||
CHAN_OVERLAP = 8192,
|
||||
CHAN_NOSTOP = 4096, // only for A_PlaySound. Does not start if channel is playing something.
|
||||
CHAN_OVERLAP = 8192, // [MK] Does not stop any sounds in the channel and instead plays over them.
|
||||
|
||||
// Same as above, with an F appended to allow better distinction of channel and channel flags.
|
||||
CHANF_DEFAULT = 0, // just to make the code look better and avoid literal 0's.
|
||||
CHANF_DEFAULT = 0, // just to make the code look better and avoid literal 0's.
|
||||
CHANF_LISTENERZ = 8,
|
||||
CHANF_MAYBE_LOCAL = 16,
|
||||
CHANF_UI = 32,
|
||||
CHANF_NOPAUSE = 64,
|
||||
CHANF_UI = 32, // Do not record sound in savegames.
|
||||
CHANF_NOPAUSE = 64, // Do not pause this sound in menus.
|
||||
CHANF_LOOP = 256,
|
||||
CHANF_NOSTOP = 4096,
|
||||
CHANF_OVERLAP = 8192,
|
||||
CHANF_LOCAL = 16384,
|
||||
CHANF_NOSTOP = 4096, // only for A_PlaySound. Does not start if channel is playing something.
|
||||
CHANF_OVERLAP = 8192, // [MK] Does not stop any sounds in the channel and instead plays over them.
|
||||
CHANF_LOCAL = 16384, // only plays locally for the calling actor
|
||||
CHANF_TRANSIENT = 32768, // Do not record in savegames - used for sounds that get restarted outside the sound system (e.g. ambients in SW and Blood)
|
||||
CHANF_FORCE = 65536, // Start, even if sound is paused.
|
||||
CHANF_SINGULAR = 0x20000, // Only start if no sound of this name is already playing.
|
||||
|
||||
|
||||
CHANF_LOOPING = CHANF_LOOP | CHANF_NOSTOP, // convenience value for replicating the old 'looping' boolean.
|
||||
|
||||
};
|
||||
|
||||
// sound attenuation values
|
||||
|
|
@ -689,6 +691,7 @@ struct CVar native
|
|||
};
|
||||
|
||||
native static CVar FindCVar(Name name);
|
||||
native static bool SaveConfig();
|
||||
bool GetBool() { return GetInt(); }
|
||||
native int GetInt();
|
||||
native double GetFloat();
|
||||
|
|
@ -785,7 +788,9 @@ class Object native
|
|||
//
|
||||
// Intrinsic random number generation functions. Note that the square
|
||||
// bracket syntax for specifying an RNG ID is only available for these
|
||||
// functions.
|
||||
// functions. If the function is prefixed with a C, this is a client-side RNG
|
||||
// call that isn't backed up while predicting and has a unique name space from
|
||||
// regular RNG calls. This should be used for things like HUD elements.
|
||||
// clearscope void SetRandomSeed[Name rngId = 'None'](int seed); // Set the seed for the given RNG.
|
||||
// clearscope int Random[Name rngId = 'None'](int min, int max); // Use the given RNG to generate a random integer number in the range (min, max) inclusive.
|
||||
// clearscope int Random2[Name rngId = 'None'](int mask); // Use the given RNG to generate a random integer number, and do a "union" (bitwise AND, AKA &) operation with the bits in the mask integer.
|
||||
|
|
|
|||
|
|
@ -97,6 +97,14 @@ struct WorldEvent native play version("2.4")
|
|||
native readonly bool DamageIsRadius;
|
||||
native int NewDamage;
|
||||
native readonly State CrushedState;
|
||||
native readonly double AttackAngle;
|
||||
native readonly double AttackPitch;
|
||||
native readonly double AttackDistance;
|
||||
native readonly vector3 AttackPos;
|
||||
native readonly double AttackOffsetForward;
|
||||
native readonly double AttackOffsetSide;
|
||||
native readonly double AttackZ;
|
||||
native readonly class<Actor> AttackPuffType;
|
||||
}
|
||||
|
||||
struct PlayerEvent native play version("2.4")
|
||||
|
|
@ -155,6 +163,8 @@ class StaticEventHandler : Object native play version("2.4")
|
|||
virtual void WorldThingRevived(WorldEvent e) {}
|
||||
virtual void WorldThingDamaged(WorldEvent e) {}
|
||||
virtual void WorldThingDestroyed(WorldEvent e) {}
|
||||
virtual bool WorldHitscanPreFired(WorldEvent e) { return false; }
|
||||
virtual void WorldHitscanFired(WorldEvent e) {}
|
||||
virtual void WorldLinePreActivated(WorldEvent e) {}
|
||||
virtual void WorldLineActivated(WorldEvent e) {}
|
||||
virtual void WorldSectorDamaged(WorldEvent e) {}
|
||||
|
|
|
|||
|
|
@ -435,6 +435,11 @@ struct Sector native play
|
|||
SECMF_UNDERWATERMASK = 32+64,
|
||||
SECMF_DRAWN = 128, // sector has been drawn at least once
|
||||
SECMF_HIDDEN = 256, // Do not draw on textured automap
|
||||
SECMF_OVERLAPPING = 512, // floor and ceiling overlap and require special renderer action.
|
||||
SECMF_NOSKYWALLS = 1024, // Do not draw "sky walls"
|
||||
SECMF_LIFT = 2048, // For MBF monster AI
|
||||
SECMF_HURTMONSTERS = 4096, // Monsters in this sector are hurt like players.
|
||||
SECMF_HARMINAIR = 8192, // Actors in this sector are also hurt mid-air.
|
||||
}
|
||||
native uint16 MoreFlags;
|
||||
|
||||
|
|
@ -452,6 +457,9 @@ struct Sector native play
|
|||
SECF_ENDLEVEL = 512, // ends level when health goes below 10
|
||||
SECF_HAZARD = 1024, // Change to Strife's delayed damage handling.
|
||||
SECF_NOATTACK = 2048, // monsters cannot start attacks in this sector.
|
||||
SECF_EXIT1 = 4096,
|
||||
SECF_EXIT2 = 8192,
|
||||
SECF_KILLMONSTERS = 16384,// Monsters in this sector are instantly killed.
|
||||
|
||||
SECF_WASSECRET = 1 << 30, // a secret that was discovered
|
||||
SECF_SECRET = 1 << 31, // a secret sector
|
||||
|
|
@ -545,6 +553,8 @@ struct Sector native play
|
|||
native void ChangeLightLevel(int newval);
|
||||
native void SetLightLevel(int newval);
|
||||
native clearscope int GetLightLevel() const;
|
||||
native void SetPlaneReflectivity(int pos, double val);
|
||||
native clearscope double GetPlaneReflectivity(int pos);
|
||||
native void AdjustFloorClip();
|
||||
native clearscope bool IsLinked(Sector other, bool ceiling) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -216,11 +216,11 @@ class ConversationMenu : Menu
|
|||
let goodbyestr = mCurNode.Goodbye;
|
||||
if (goodbyestr.Length() == 0)
|
||||
{
|
||||
goodbyestr = String.Format("$TXT_RANDOMGOODBYE_%d", Random[RandomSpeech](1, NUM_RANDOM_GOODBYES));
|
||||
goodbyestr = String.Format("$TXT_RANDOMGOODBYE_%d", CRandom[RandomSpeech](1, NUM_RANDOM_GOODBYES));
|
||||
}
|
||||
else if (goodbyestr.Left(7) == "RANDOM_")
|
||||
{
|
||||
goodbyestr = String.Format("$TXT_%s_%02d", goodbyestr, Random[RandomSpeech](1, NUM_RANDOM_LINES));
|
||||
goodbyestr = String.Format("$TXT_%s_%02d", goodbyestr, CRandom[RandomSpeech](1, NUM_RANDOM_LINES));
|
||||
}
|
||||
goodbyestr = Stringtable.Localize(goodbyestr);
|
||||
if (goodbyestr.Length() == 0 || goodbyestr.Left(1) == "$") goodbyestr = "Bye.";
|
||||
|
|
@ -254,7 +254,7 @@ class ConversationMenu : Menu
|
|||
String toSay = mCurNode.Dialogue;
|
||||
if (toSay.Left(7) == "RANDOM_")
|
||||
{
|
||||
let dlgtext = String.Format("$TXT_%s_%02d", toSay, random[RandomSpeech](1, NUM_RANDOM_LINES));
|
||||
let dlgtext = String.Format("$TXT_%s_%02d", toSay, crandom[RandomSpeech](1, NUM_RANDOM_LINES));
|
||||
toSay = Stringtable.Localize(dlgtext);
|
||||
if (toSay.Left(1) == "$") toSay = Stringtable.Localize("$TXT_GOAWAY");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class HereticStatusBar : BaseStatusBar
|
|||
// wiggle the chain if it moves
|
||||
if (Level.time & 1)
|
||||
{
|
||||
wiggle = (mHealthInterpolator.GetValue() != CPlayer.health) && Random[ChainWiggle](0, 1);
|
||||
wiggle = (mHealthInterpolator.GetValue() != CPlayer.health) && CRandom[ChainWiggle](0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,18 @@ Class VisualThinker : Thinker native
|
|||
Alpha;
|
||||
native TextureID Texture;
|
||||
native TranslationID Translation;
|
||||
native uint16 Flags;
|
||||
native int16 LightLevel;
|
||||
native bool bFlipOffsetX,
|
||||
bFlipOffsetY,
|
||||
bXFlip,
|
||||
bYFlip,
|
||||
bDontInterpolate,
|
||||
bAddLightLevel;
|
||||
|
||||
native uint16 Flags;
|
||||
native int VisualThinkerFlags;
|
||||
|
||||
FlagDef FlipOffsetX : VisualThinkerFlags, 0;
|
||||
FlagDef FlipOffsetY : VisualThinkerFlags, 1;
|
||||
FlagDef XFlip : VisualThinkerFlags, 2;
|
||||
FlagDef YFlip : VisualThinkerFlags, 3;
|
||||
FlagDef DontInterpolate : VisualThinkerFlags, 4;
|
||||
FlagDef AddLightLevel : VisualThinkerFlags, 5;
|
||||
|
||||
native Color scolor;
|
||||
|
||||
native Sector CurSector; // can be null!
|
||||
|
|
@ -26,8 +30,11 @@ Class VisualThinker : Thinker native
|
|||
native void SetRenderStyle(int mode); // see ERenderStyle
|
||||
native bool IsFrozen();
|
||||
|
||||
native protected void UpdateSector(); // needs to be called if the thinker is set to a non-ticking statnum and the position is modified (or if Tick is overriden and doesn't call Super.Tick())
|
||||
native protected void UpdateSpriteInfo(); // needs to be called every time the texture is updated if the thinker uses SPF_LOCAL_ANIM and is set to a non-ticking statnum (or if Tick is overriden and doesn't call Super.Tick())
|
||||
|
||||
static VisualThinker Spawn(Class<VisualThinker> type, TextureID tex, Vector3 pos, Vector3 vel, double alpha = 1.0, int flags = 0,
|
||||
double roll = 0.0, Vector2 scale = (1,1), Vector2 offset = (0,0), int style = STYLE_Normal, TranslationID trans = 0)
|
||||
double roll = 0.0, Vector2 scale = (1,1), Vector2 offset = (0,0), int style = STYLE_Normal, TranslationID trans = 0, int VisualThinkerFlags = 0)
|
||||
{
|
||||
if (!Level) return null;
|
||||
|
||||
|
|
@ -44,6 +51,9 @@ Class VisualThinker : Thinker native
|
|||
p.SetRenderStyle(style);
|
||||
p.Translation = trans;
|
||||
p.Flags = flags;
|
||||
p.VisualThinkerFlags = VisualThinkerFlags;
|
||||
p.UpdateSector();
|
||||
p.UpdateSpriteInfo();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -709,15 +709,15 @@ Order // Order in the IWAD selection box
|
|||
"DOOM 2: L'Enfer sur Terre"
|
||||
"DOOM 2: BFG Edition"
|
||||
"DOOM 2: XBox Edition"
|
||||
"DOOM 2: KEX Edition"
|
||||
"DOOM 2: Unity Edition"
|
||||
"DOOM 2: KEX Edition"
|
||||
"The Ultimate DOOM"
|
||||
"DOOM Registered"
|
||||
"DOOM Shareware"
|
||||
"DOOM: BFG Edition"
|
||||
"DOOM: XBox Edition"
|
||||
"DOOM: KEX Edition"
|
||||
"DOOM: Unity Edition"
|
||||
"DOOM: KEX Edition"
|
||||
"Final Doom: Plutonia Experiment"
|
||||
"Final Doom: Plutonia Experiment: Unity Edition"
|
||||
"Final Doom: Plutonia Experiment: KEX Edition"
|
||||
|
|
|
|||