Merge pull request #99 from madame-rachelle/gzd4-14-merge
Gzd4 14 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);
|
||||
|
|
|
|||
|
|
@ -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,7 @@ 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);
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ public:
|
|||
void SetMaxIwadNum(int x) { MaxIwadIndex = x; }
|
||||
|
||||
bool InitSingleFile(const char *filename, FileSystemMessageFunc Printf = nullptr);
|
||||
bool InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false, FILE* hashfile = nullptr);
|
||||
void AddFile (const char *filename, FileReader *wadinfo, LumpFilterInfo* filter, FileSystemMessageFunc Printf, FILE* hashfile);
|
||||
bool InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false);
|
||||
void AddFile (const char *filename, FileReader *wadinfo, LumpFilterInfo* filter, FileSystemMessageFunc Printf);
|
||||
int CheckIfResourceFileLoaded (const char *name) noexcept;
|
||||
void AddAdditionalFile(const char* filename, FileReader* wadinfo = NULL) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ bool FileSystem::InitSingleFile(const char* filename, FileSystemMessageFunc Prin
|
|||
return InitMultipleFiles(filenames, nullptr, Printf);
|
||||
}
|
||||
|
||||
bool FileSystem::InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter, FileSystemMessageFunc Printf, bool allowduplicates, FILE* hashfile)
|
||||
bool FileSystem::InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter, FileSystemMessageFunc Printf, bool allowduplicates)
|
||||
{
|
||||
int numfiles;
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ bool FileSystem::InitMultipleFiles (std::vector<std::string>& filenames, LumpFil
|
|||
|
||||
for(size_t i=0;i<filenames.size(); i++)
|
||||
{
|
||||
AddFile(filenames[i].c_str(), nullptr, filter, Printf, hashfile);
|
||||
AddFile(filenames[i].c_str(), nullptr, filter, Printf);
|
||||
|
||||
if (i == (unsigned)MaxIwadIndex) MoveLumpsInFolder("after_iwad/");
|
||||
std::string path = "filter/%s";
|
||||
|
|
@ -327,7 +327,7 @@ int FileSystem::AddFromBuffer(const char* name, char* data, int size, int id, in
|
|||
// [RH] Removed reload hack
|
||||
//==========================================================================
|
||||
|
||||
void FileSystem::AddFile (const char *filename, FileReader *filer, LumpFilterInfo* filter, FileSystemMessageFunc Printf, FILE* hashfile)
|
||||
void FileSystem::AddFile (const char *filename, FileReader *filer, LumpFilterInfo* filter, FileSystemMessageFunc Printf)
|
||||
{
|
||||
int startlump;
|
||||
bool isdir = false;
|
||||
|
|
@ -396,49 +396,10 @@ void FileSystem::AddFile (const char *filename, FileReader *filer, LumpFilterInf
|
|||
path += ':';
|
||||
path += resfile->getName(i);
|
||||
auto embedded = resfile->GetEntryReader(i, READER_CACHED);
|
||||
AddFile(path.c_str(), &embedded, filter, Printf, hashfile);
|
||||
AddFile(path.c_str(), &embedded, filter, Printf);
|
||||
}
|
||||
}
|
||||
|
||||
if (hashfile)
|
||||
{
|
||||
uint8_t cksum[16];
|
||||
char cksumout[33];
|
||||
memset(cksumout, 0, sizeof(cksumout));
|
||||
|
||||
if (filereader.isOpen())
|
||||
{
|
||||
filereader.Seek(0, FileReader::SeekSet);
|
||||
md5Hash(filereader, cksum);
|
||||
|
||||
for (size_t j = 0; j < sizeof(cksum); ++j)
|
||||
{
|
||||
snprintf(cksumout + (j * 2), 3, "%02X", cksum[j]);
|
||||
}
|
||||
|
||||
fprintf(hashfile, "file: %s, hash: %s, size: %td\n", filename, cksumout, filereader.GetLength());
|
||||
}
|
||||
|
||||
else
|
||||
fprintf(hashfile, "file: %s, Directory structure\n", filename);
|
||||
|
||||
for (int i = 0; i < resfile->EntryCount(); i++)
|
||||
{
|
||||
int flags = resfile->GetEntryFlags(i);
|
||||
if (!(flags & RESFF_EMBEDDED))
|
||||
{
|
||||
auto reader = resfile->GetEntryReader(i, READER_SHARED, 0);
|
||||
md5Hash(filereader, cksum);
|
||||
|
||||
for (size_t j = 0; j < sizeof(cksum); ++j)
|
||||
{
|
||||
snprintf(cksumout + (j * 2), 3, "%02X", cksum[j]);
|
||||
}
|
||||
|
||||
fprintf(hashfile, "file: %s, lump: %s, hash: %s, size: %zu\n", filename, resfile->getName(i), cksumout, (uint64_t)resfile->Length(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -8967,7 +8993,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (PFunction **Override; ctx.Version >= MakeVersion(4, 11, 0) && (Override = static_cast<PDynArray*>(Self->ValueType)->FnOverrides.CheckKey(MethodName)))
|
||||
if (PFunction **Override; (Override = static_cast<PDynArray*>(Self->ValueType)->FnOverrides.CheckKey(MethodName)))
|
||||
{
|
||||
afd_override = *Override;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "filesystem.h"
|
||||
|
||||
CVAR(Bool, strictdecorate, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
|
||||
CVAR(Bool, warningstoerrors, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
|
||||
|
||||
EXTERN_CVAR(Bool, vm_jit)
|
||||
EXTERN_CVAR(Bool, vm_jit_aot)
|
||||
|
|
@ -879,10 +880,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;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
// The maximum size of an IDAT chunk ZDoom will write. This is also the
|
||||
// size of the compression buffer it allocates on the stack.
|
||||
// size of the compression buffer it allocates on the heap.
|
||||
#define PNG_WRITE_SIZE 32768
|
||||
|
||||
// Set this to 1 to use a simple heuristic to select the filter to apply
|
||||
|
|
@ -926,8 +926,7 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
|
|||
temprow[i] = &temprow_storage[temprow_size * i];
|
||||
}
|
||||
|
||||
TArray<Byte> array(PNG_WRITE_SIZE, true);
|
||||
auto buffer = array.data();
|
||||
TArray<Byte> buffer(PNG_WRITE_SIZE, true);
|
||||
z_stream stream;
|
||||
int err;
|
||||
int y;
|
||||
|
|
@ -944,8 +943,8 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
|
|||
}
|
||||
|
||||
y = height;
|
||||
stream.next_out = buffer;
|
||||
stream.avail_out = sizeof(buffer);
|
||||
stream.next_out = buffer.data();
|
||||
stream.avail_out = buffer.size();
|
||||
|
||||
temprow[0][0] = 0;
|
||||
#if USE_FILTER_HEURISTIC
|
||||
|
|
@ -1007,12 +1006,12 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
|
|||
}
|
||||
while (stream.avail_out == 0)
|
||||
{
|
||||
if (!WriteIDAT (file, buffer, sizeof(buffer)))
|
||||
if (!WriteIDAT (file, buffer.data(), buffer.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
stream.next_out = buffer;
|
||||
stream.avail_out = sizeof(buffer);
|
||||
stream.next_out = buffer.data();
|
||||
stream.avail_out = buffer.size();
|
||||
if (stream.avail_in != 0)
|
||||
{
|
||||
err = deflate (&stream, (y == 0) ? Z_FINISH : 0);
|
||||
|
|
@ -1033,12 +1032,12 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
|
|||
}
|
||||
if (stream.avail_out == 0)
|
||||
{
|
||||
if (!WriteIDAT (file, buffer, sizeof(buffer)))
|
||||
if (!WriteIDAT (file, buffer.data(), buffer.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
stream.next_out = buffer;
|
||||
stream.avail_out = sizeof(buffer);
|
||||
stream.next_out = buffer.data();
|
||||
stream.avail_out = buffer.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1048,7 +1047,7 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
|
|||
{
|
||||
return false;
|
||||
}
|
||||
return WriteIDAT (file, buffer, sizeof(buffer)-stream.avail_out);
|
||||
return WriteIDAT (file, buffer.data(), buffer.size() - stream.avail_out);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -824,7 +824,7 @@ int FIWadManager::IdentifyVersion (std::vector<std::string>&wadfiles, const char
|
|||
|
||||
// [SP] Load non-free assets if available. This must be done before the IWAD.
|
||||
int iwadnum = 1;
|
||||
if (D_AddFile(wadfiles, optional_wad, true, -1, GameConfig))
|
||||
if (optional_wad && D_AddFile(wadfiles, optional_wad, true, -1, GameConfig))
|
||||
{
|
||||
iwadnum++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3071,39 +3071,6 @@ static void System_HudScaleChanged()
|
|||
|
||||
bool CheckSkipGameOptionBlock(const char* str);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FILE* D_GetHashFile()
|
||||
{
|
||||
FILE *hashfile = nullptr;
|
||||
|
||||
if (Args->CheckParm("-hashfiles"))
|
||||
{
|
||||
const char *filename = "fileinfo.txt";
|
||||
Printf("Hashing loaded content to: %s\n", filename);
|
||||
hashfile = fopen(filename, "w");
|
||||
if (hashfile)
|
||||
{
|
||||
Printf("Notice: File hashing is incredibly verbose. Expect loading files to take much longer than usual.\n");
|
||||
fprintf(hashfile, "%s version %s (%s)\n", GAMENAME, GetVersionString(), GetGitHash());
|
||||
#ifdef __VERSION__
|
||||
fprintf(hashfile, "Compiler version: %s\n", __VERSION__);
|
||||
#endif
|
||||
fprintf(hashfile, "Command line:");
|
||||
for (int i = 0; i < Args->NumArgs(); ++i)
|
||||
{
|
||||
fprintf(hashfile, " %s", Args->GetArg(i));
|
||||
}
|
||||
fprintf(hashfile, "\n");
|
||||
}
|
||||
}
|
||||
return hashfile;
|
||||
}
|
||||
|
||||
// checks if a file within a directory is allowed to be added to the file system.
|
||||
static bool FileNameCheck(const char* base, const char* path)
|
||||
{
|
||||
|
|
@ -3260,8 +3227,7 @@ static int D_InitGame(const FIWADInfo* iwad_info, std::vector<std::string>& allw
|
|||
);
|
||||
|
||||
bool allowduplicates = Args->CheckParm("-allowduplicates");
|
||||
auto hashfile = D_GetHashFile();
|
||||
if (!fileSystem.InitMultipleFiles(allwads, &lfi, FileSystemPrintf, allowduplicates, hashfile))
|
||||
if (!fileSystem.InitMultipleFiles(allwads, &lfi, FileSystemPrintf, allowduplicates))
|
||||
{
|
||||
I_FatalError("FileSystem: no files found");
|
||||
}
|
||||
|
|
|
|||
166
src/events.cpp
|
|
@ -701,6 +701,69 @@ 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;
|
||||
}
|
||||
|
||||
bool EventManager::WorldRailgunPreFired(FName damageType, PClassActor* pufftype, FRailParams* param)
|
||||
{
|
||||
auto actor = param->source;
|
||||
|
||||
// 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.WorldRailgunPreFired(damageType, pufftype, param);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
for (DStaticEventHandler* handler = FirstEventHandler; handler && ret == false; handler = handler->next)
|
||||
ret = handler->WorldRailgunPreFired(damageType, pufftype, param);
|
||||
}
|
||||
|
||||
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::WorldRailgunFired(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.WorldRailgunFired(actor, AttackPos, DamagePosition, Inflictor, flags);
|
||||
|
||||
for (DStaticEventHandler* handler = FirstEventHandler; handler; handler = handler->next)
|
||||
handler->WorldRailgunFired(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 +1089,16 @@ 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(WorldEvent, FWorldEvent, RailParams);
|
||||
DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackLineFlags);
|
||||
|
||||
DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, PlayerNumber);
|
||||
DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, IsReturn);
|
||||
|
|
@ -1729,6 +1802,98 @@ 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.AttackLineFlags = flags;
|
||||
int processed;
|
||||
VMReturn results[1] = { &processed };
|
||||
VMValue params[2] = { (DStaticEventHandler*)this, &e };
|
||||
VMCall(func, params, 2, results, 1);
|
||||
return !!processed;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DStaticEventHandler::WorldRailgunPreFired(FName damageType, PClassActor* pufftype, FRailParams* param)
|
||||
{
|
||||
IFVIRTUAL(DStaticEventHandler, WorldRailgunPreFired)
|
||||
{
|
||||
// don't create excessive DObjects if not going to be processed anyway
|
||||
if (isEmpty(func)) return false;
|
||||
FWorldEvent e = owner->SetupWorldEvent();
|
||||
e.Thing = param->source;
|
||||
e.AttackPuffType = pufftype;
|
||||
e.DamageType = damageType;
|
||||
e.Damage = param->damage;
|
||||
e.AttackOffsetForward = 0;
|
||||
e.AttackOffsetSide = param->offset_xy;
|
||||
e.AttackDistance = param->distance;
|
||||
e.AttackZ = param->offset_z;
|
||||
e.AttackAngle = e.Thing->Angles.Yaw + param->angleoffset;
|
||||
e.AttackPitch = e.Thing->Angles.Pitch + param->pitchoffset;
|
||||
e.RailParams = *param;
|
||||
e.RailParams.puff = pufftype;
|
||||
|
||||
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.AttackLineFlags = flags;
|
||||
VMValue params[2] = { (DStaticEventHandler*)this, &e };
|
||||
VMCall(func, params, 2, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void DStaticEventHandler::WorldRailgunFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags)
|
||||
{
|
||||
IFVIRTUAL(DStaticEventHandler, WorldRailgunFired)
|
||||
{
|
||||
// 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.AttackLineFlags = 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 +1908,6 @@ void DStaticEventHandler::WorldThingGround(AActor* actor, FState* st)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void DStaticEventHandler::WorldThingRevived(AActor* actor)
|
||||
{
|
||||
IFVIRTUAL(DStaticEventHandler, WorldThingRevived)
|
||||
|
|
|
|||
23
src/events.h
Executable file → Normal file
|
|
@ -4,6 +4,7 @@
|
|||
#include "dobject.h"
|
||||
#include "serializer.h"
|
||||
#include "d_event.h"
|
||||
#include "p_local.h"
|
||||
#include "sbar.h"
|
||||
#include "info.h"
|
||||
#include "vm.h"
|
||||
|
|
@ -311,6 +312,10 @@ 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);
|
||||
bool WorldRailgunPreFired(FName damageType, PClassActor* pufftype, FRailParams* param);
|
||||
void WorldHitscanFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags);
|
||||
void WorldRailgunFired(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 +398,16 @@ 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;
|
||||
FRailParams RailParams;
|
||||
int AttackLineFlags = 0;
|
||||
};
|
||||
|
||||
struct FPlayerEvent
|
||||
|
|
@ -467,6 +482,14 @@ 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 when a railgun attack has been fired
|
||||
void WorldRailgunFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags);
|
||||
// called when a railgun attack has been fired (can be overridden to block it)
|
||||
bool WorldRailgunPreFired(FName damageType, PClassActor* pufftype, FRailParams* param);
|
||||
// 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);
|
||||
|
|
@ -1436,6 +1438,7 @@ void FLevelLocals::PlayerReborn (int player)
|
|||
p->cheats |= chasecam;
|
||||
p->Bot = Bot; //Added by MC:
|
||||
p->settings_controller = settings_controller;
|
||||
p->LastSafePos = p->mo->Pos();
|
||||
|
||||
p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately
|
||||
p->original_oldbuttons = ~0;
|
||||
|
|
@ -2146,7 +2149,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 +2442,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
|
||||
//
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
#include "r_data/r_interpolate.h"
|
||||
#include "doom_aabbtree.h"
|
||||
#include "doom_levelmesh.h"
|
||||
#include "p_visualthinker.h"
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
|
|
@ -149,6 +150,7 @@ struct FLevelLocals
|
|||
int GetCompatibility2(int mask);
|
||||
void ApplyCompatibility();
|
||||
void ApplyCompatibility2();
|
||||
AActor* SelectActorFromTID(int tid, size_t index, AActor* defactor);
|
||||
|
||||
void Init();
|
||||
|
||||
|
|
@ -166,8 +168,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 +200,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);
|
||||
|
||||
|
|
@ -425,10 +427,13 @@ public:
|
|||
DThinker *CreateThinker(PClass *cls, int statnum = STAT_DEFAULT)
|
||||
{
|
||||
DThinker *thinker = static_cast<DThinker*>(cls->CreateNew());
|
||||
if (statnum && thinker->IsKindOf(RUNTIME_CLASS(DVisualThinker)))
|
||||
{
|
||||
statnum = STAT_VISUALTHINKER;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -381,6 +381,7 @@ enum //Key words
|
|||
SBARINFO_MUGSHOT,
|
||||
SBARINFO_CREATEPOPUP,
|
||||
SBARINFO_PROTRUSION,
|
||||
SBARINFO_APPENDSTATUSBAR,
|
||||
};
|
||||
|
||||
enum //Bar types
|
||||
|
|
@ -410,6 +411,7 @@ static const char *SBarInfoTopLevel[] =
|
|||
"mugshot",
|
||||
"createpopup",
|
||||
"protrusion",
|
||||
"appendstatusbar",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -488,7 +490,9 @@ void SBarInfo::ParseSBarInfo(int lump)
|
|||
continue;
|
||||
}
|
||||
int baselump = -2;
|
||||
switch(sc.MustMatchString(SBarInfoTopLevel))
|
||||
// Store the command, used for the switch statement and case SBARINFO_APPENDSTATUSBAR.
|
||||
const int command = sc.MustMatchString(SBarInfoTopLevel);
|
||||
switch(command)
|
||||
{
|
||||
case SBARINFO_BASE:
|
||||
baseSet = true;
|
||||
|
|
@ -629,6 +633,7 @@ void SBarInfo::ParseSBarInfo(int lump)
|
|||
sc.MustGetToken(';');
|
||||
break;
|
||||
case SBARINFO_STATUSBAR:
|
||||
case SBARINFO_APPENDSTATUSBAR:
|
||||
{
|
||||
if(!baseSet) //If the user didn't explicitly define a base, do so now.
|
||||
gameType = GAME_Any;
|
||||
|
|
@ -638,11 +643,19 @@ void SBarInfo::ParseSBarInfo(int lump)
|
|||
sc.MustGetToken(TK_Identifier);
|
||||
barNum = sc.MustMatchString(StatusBars);
|
||||
}
|
||||
if (this->huds[barNum] != NULL)
|
||||
// SBARINFO_APPENDSTATUSBAR shouldn't delete the old HUD if it exists.
|
||||
if(command != SBARINFO_APPENDSTATUSBAR)
|
||||
{
|
||||
delete this->huds[barNum];
|
||||
if (this->huds[barNum] != NULL)
|
||||
{
|
||||
delete this->huds[barNum];
|
||||
}
|
||||
this->huds[barNum] = new SBarInfoMainBlock(this);
|
||||
}
|
||||
else if(this->huds[barNum] == NULL)
|
||||
{
|
||||
sc.ScriptError("Status bar '%s' has not been created and cannot be appended to. Use 'StatusBar' instead.", StatusBars[barNum]);
|
||||
}
|
||||
this->huds[barNum] = new SBarInfoMainBlock(this);
|
||||
if(barNum == STBAR_AUTOMAP)
|
||||
{
|
||||
automapbar = true;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -806,6 +808,8 @@ public:
|
|||
void RemoveForceField();
|
||||
int Index() const { return sectornum; }
|
||||
|
||||
bool IsDangerous(const DVector3& pos, double height) const;
|
||||
|
||||
void AdjustFloorClip () const;
|
||||
void SetColor(PalEntry pe, int desat);
|
||||
void SetFade(PalEntry pe);
|
||||
|
|
@ -1062,6 +1066,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"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
|
||||
#include "gameconfigfile.h"
|
||||
#include "gstrings.h"
|
||||
#include "vm.h"
|
||||
|
||||
FGameConfigFile *GameConfig;
|
||||
|
||||
|
|
@ -691,3 +692,14 @@ CCMD(openscreenshots)
|
|||
I_OpenShellFolder(autoname.GetChars());
|
||||
}
|
||||
|
||||
static int SaveConfig()
|
||||
{
|
||||
return M_SaveDefaults(nullptr);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_CVar, SaveConfig, SaveConfig)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
ACTION_RETURN_INT(M_SaveDefaults(nullptr));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
|||
|
|
@ -849,7 +849,7 @@ void SprayDecal(AActor *shooter, const char *name, double distance, DVector3 off
|
|||
{
|
||||
if (trace.HitType == TRACE_HitWall)
|
||||
{
|
||||
DImpactDecal::StaticCreate(shooter->Level, name, trace.HitPos, trace.Line->sidedef[trace.Side], NULL, entry, bloodTrans);
|
||||
DImpactDecal::StaticCreate(shooter->Level, name, trace.HitPos, trace.Line->sidedef[trace.Side], trace.ffloor, entry, bloodTrans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ---
|
||||
|
|
@ -754,12 +756,6 @@ public:
|
|||
int Flags;
|
||||
|
||||
// Functions
|
||||
DViewPosition()
|
||||
{
|
||||
Offset = { 0,0,0 };
|
||||
Flags = 0;
|
||||
}
|
||||
|
||||
void Set(DVector3 &off, int f = -1)
|
||||
{
|
||||
ZeroSubnormalsF(off.X);
|
||||
|
|
@ -775,6 +771,8 @@ public:
|
|||
{
|
||||
return Offset.isZero();
|
||||
}
|
||||
|
||||
void Serialize(FSerializer& arc) override;
|
||||
};
|
||||
|
||||
const double MinVel = EQUAL_EPSILON;
|
||||
|
|
@ -871,7 +869,7 @@ public:
|
|||
void PlayPushSound();
|
||||
|
||||
// Called when an actor with MF_MISSILE and MF2_FLOORBOUNCE hits the floor
|
||||
bool FloorBounceMissile (secplane_t &plane);
|
||||
bool FloorBounceMissile (secplane_t &plane, bool is3DFloor);
|
||||
|
||||
// Called by RoughBlockCheck
|
||||
bool IsOkayToAttack (AActor *target);
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ void WriteUserInfo(FSerializer &arc, userinfo_t &info);
|
|||
class player_t
|
||||
{
|
||||
public:
|
||||
player_t() = default;
|
||||
player_t() { angleOffsetTargets.Zero(); }
|
||||
~player_t();
|
||||
player_t &operator= (const player_t &p) = delete;
|
||||
void CopyFrom(player_t &src, bool copyPSP);
|
||||
|
|
@ -322,8 +322,8 @@ public:
|
|||
AActor *mo = nullptr;
|
||||
uint8_t playerstate = 0;
|
||||
ticcmd_t cmd = {};
|
||||
usercmd_t original_cmd;
|
||||
uint32_t original_oldbuttons;
|
||||
usercmd_t original_cmd = {};
|
||||
uint32_t original_oldbuttons = 0;
|
||||
|
||||
userinfo_t userinfo; // [RH] who is this?
|
||||
|
||||
|
|
@ -340,7 +340,7 @@ public:
|
|||
// mo->velx and mo->vely represent true velocity experienced by player.
|
||||
// This only represents the thrust that the player applies himself.
|
||||
// This avoids anomalies with such things as Boom ice and conveyors.
|
||||
DVector2 Vel = { 0,0 };
|
||||
DVector2 Vel = { 0.0, 0.0 };
|
||||
|
||||
bool centering = false;
|
||||
uint8_t turnticks = 0;
|
||||
|
|
@ -419,7 +419,7 @@ public:
|
|||
FString SoundClass;
|
||||
FString LogText; // [RH] Log for Strife
|
||||
FString SubtitleText;
|
||||
int SubtitleCounter;
|
||||
int SubtitleCounter = 0;
|
||||
|
||||
DAngle MinPitch = nullAngle; // Viewpitch limits (negative is up, positive is down)
|
||||
DAngle MaxPitch = nullAngle;
|
||||
|
|
@ -435,6 +435,8 @@ public:
|
|||
DAngle ConversationNPCAngle = nullAngle;
|
||||
bool ConversationFaceTalker = false;
|
||||
|
||||
DVector3 LastSafePos = {}; // Mark the last known safe location the player was standing.
|
||||
|
||||
double GetDeltaViewHeight() const
|
||||
{
|
||||
return (mo->FloatVar(NAME_ViewHeight) + crouchviewdelta - viewheight) / 8;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -5130,6 +5130,7 @@ enum ESetAnimationFlags
|
|||
|
||||
void SetAnimationInternal(AActor * self, FName animName, double framerate, int startFrame, int loopFrame, int endFrame, int interpolateTics, int flags, double ticFrac)
|
||||
{
|
||||
|
||||
if(!self) ThrowAbortException(X_READ_NIL, "In function parameter self");
|
||||
|
||||
if(!(self->flags9 & MF9_DECOUPLEDANIMATIONS))
|
||||
|
|
@ -5176,43 +5177,46 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
|
|||
|
||||
if(!(flags & SAF_INSTANT))
|
||||
{
|
||||
if(self->modelData->curAnim.startTic > tic)
|
||||
{
|
||||
ModelAnimFrameInterp to;
|
||||
float inter;
|
||||
|
||||
calcFrames(self->modelData->curAnim, tic, to, inter);
|
||||
|
||||
const TArray<TRS>* animationData = nullptr;
|
||||
|
||||
int animationid = -1;
|
||||
|
||||
const FSpriteModelFrame * smf = &BaseSpriteModelFrames[self->GetClass()];
|
||||
|
||||
if (self->modelData->animationIDs.Size() > 0 && self->modelData->animationIDs[0] >= 0)
|
||||
if((self->modelData->curAnim.startTic - self->modelData->curAnim.switchOffset) != int(floor(tic)))
|
||||
{ // don't change interpolation data if animation switch happened in the same tic
|
||||
if(self->modelData->curAnim.startTic > tic)
|
||||
{
|
||||
animationid = self->modelData->animationIDs[0];
|
||||
ModelAnimFrameInterp to;
|
||||
float inter;
|
||||
|
||||
calcFrames(self->modelData->curAnim, tic, to, inter);
|
||||
|
||||
const TArray<TRS>* animationData = nullptr;
|
||||
|
||||
int animationid = -1;
|
||||
|
||||
const FSpriteModelFrame * smf = &BaseSpriteModelFrames[self->GetClass()];
|
||||
|
||||
if (self->modelData->animationIDs.Size() > 0 && self->modelData->animationIDs[0] >= 0)
|
||||
{
|
||||
animationid = self->modelData->animationIDs[0];
|
||||
}
|
||||
else if(smf->modelsAmount > 0)
|
||||
{
|
||||
animationid = smf->animationIDs[0];
|
||||
}
|
||||
|
||||
FModel* animation = mdl;
|
||||
|
||||
if (animationid >= 0)
|
||||
{
|
||||
animation = Models[animationid];
|
||||
animationData = animation->AttachAnimationData();
|
||||
}
|
||||
|
||||
self->modelData->prevAnim = animation->PrecalculateFrame(self->modelData->prevAnim, to, inter, animationData, self->boneComponentData, 0);
|
||||
}
|
||||
else if(smf->modelsAmount > 0)
|
||||
else
|
||||
{
|
||||
animationid = smf->animationIDs[0];
|
||||
self->modelData->prevAnim = ModelAnimFrameInterp{};
|
||||
|
||||
calcFrame(self->modelData->curAnim, tic, std::get<ModelAnimFrameInterp>(self->modelData->prevAnim));
|
||||
}
|
||||
|
||||
FModel* animation = mdl;
|
||||
|
||||
if (animationid >= 0)
|
||||
{
|
||||
animation = Models[animationid];
|
||||
animationData = animation->AttachAnimationData();
|
||||
}
|
||||
|
||||
self->modelData->prevAnim = animation->PrecalculateFrame(self->modelData->prevAnim, to, inter, animationData, self->boneComponentData, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->modelData->prevAnim = ModelAnimFrameInterp{};
|
||||
|
||||
calcFrame(self->modelData->curAnim, tic, std::get<ModelAnimFrameInterp>(self->modelData->prevAnim));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -5524,7 +5528,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_ChangeModel, ChangeModelNative)
|
|||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimation, SetAnimationNative)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(animName);
|
||||
PARAM_FLOAT(framerate);
|
||||
PARAM_INT(startFrame);
|
||||
|
|
@ -5540,7 +5544,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimation, SetAnimationNative)
|
|||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationUI, SetAnimationUINative)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(animName);
|
||||
PARAM_FLOAT(framerate);
|
||||
PARAM_INT(startFrame);
|
||||
|
|
@ -5556,7 +5560,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationUI, SetAnimationUINative)
|
|||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationFrameRate, SetAnimationFrameRateNative)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_FLOAT(framerate);
|
||||
|
||||
SetAnimationFrameRateInternal(self, framerate, 1);
|
||||
|
|
@ -5566,7 +5570,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationFrameRate, SetAnimationFrameRa
|
|||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationFrameRateUI, SetAnimationFrameRateUINative)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_FLOAT(framerate);
|
||||
|
||||
SetAnimationFrameRateInternal(self, framerate, I_GetTimeFrac());
|
||||
|
|
@ -5576,7 +5580,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationFrameRateUI, SetAnimationFrame
|
|||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetModelFlag, SetModelFlag)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(flag);
|
||||
|
||||
SetModelFlag(self, flag);
|
||||
|
|
@ -5586,7 +5590,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetModelFlag, SetModelFlag)
|
|||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, ClearModelFlag, ClearModelFlag)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(flag);
|
||||
|
||||
ClearModelFlag(self, flag);
|
||||
|
|
@ -5596,7 +5600,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, ClearModelFlag, ClearModelFlag)
|
|||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, ResetModelFlags, ResetModelFlags)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
ResetModelFlags(self);
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1158,7 +1158,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
// Special damage types
|
||||
if (inflictor)
|
||||
{
|
||||
if (inflictor->flags4 & MF4_SPECTRAL)
|
||||
if (inflictor->flags4 & MF4_SPECTRAL && !(inflictor->flags9 & MF9_ISPUFF))
|
||||
{
|
||||
if (player != NULL)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1335,46 +1343,44 @@ static bool CanAttackHurt(AActor *victim, AActor *shooter)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void P_DoMissileDamage(AActor* inflictor, AActor* target)
|
||||
void P_DoMissileDamage(AActor* self, AActor* victim)
|
||||
{
|
||||
const bool ripper = (self->flags2 & MF2_RIP);
|
||||
|
||||
// Do poisoning (if using new style poison)
|
||||
if (inflictor->PoisonDamage > 0 && inflictor->PoisonDuration != INT_MIN)
|
||||
{
|
||||
P_PoisonMobj(target, inflictor, inflictor->target, inflictor->PoisonDamage, inflictor->PoisonDuration, inflictor->PoisonPeriod, inflictor->PoisonDamageType);
|
||||
}
|
||||
if (self->PoisonDamage > 0 && self->PoisonDuration != INT_MIN)
|
||||
P_PoisonMobj(victim, self, self->target, self->PoisonDamage, self->PoisonDuration, self->PoisonPeriod, self->PoisonDamageType);
|
||||
|
||||
// Do damage
|
||||
int damage = inflictor->GetMissileDamage((inflictor->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1);
|
||||
if ((damage > 0) || (inflictor->flags6 & MF6_FORCEPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN))
|
||||
int damage = ripper ? self->GetMissileDamage(3, 2) : self->GetMissileDamage((self->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1);
|
||||
if (damage > 0 || (self->flags6 & MF6_FORCEPAIN) || (self->flags7 & MF7_CAUSEPAIN))
|
||||
{
|
||||
int newdam = P_DamageMobj(target, inflictor, inflictor->target, damage, inflictor->DamageType);
|
||||
if (damage > 0)
|
||||
if (ripper)
|
||||
S_Sound(self, CHAN_BODY, 0, self->SoundVar(NAME_RipSound), 1.0f, ATTN_IDLE);
|
||||
|
||||
int dealt = P_DamageMobj(victim, self, self->target, damage, self->DamageType);
|
||||
if (damage > 0 && !(self->flags3 & MF3_BLOODLESSIMPACT)
|
||||
&& !(victim->flags & MF_NOBLOOD)
|
||||
&& !(victim->flags2 & (MF2_INVULNERABLE | MF2_DORMANT | MF2_REFLECTIVE)))
|
||||
{
|
||||
if ((inflictor->flags5 & MF5_BLOODSPLATTER) &&
|
||||
!(target->flags & MF_NOBLOOD) &&
|
||||
!(target->flags2 & MF2_REFLECTIVE) &&
|
||||
!(target->flags2 & (MF2_INVULNERABLE | MF2_DORMANT)) &&
|
||||
!(inflictor->flags3 & MF3_BLOODLESSIMPACT) &&
|
||||
(pr_checkthing() < 192))
|
||||
{
|
||||
P_BloodSplatter(inflictor->Pos(), target, inflictor->AngleTo(target));
|
||||
}
|
||||
if (!(inflictor->flags3 & MF3_BLOODLESSIMPACT))
|
||||
{
|
||||
P_TraceBleed(newdam > 0 ? newdam : damage, target, inflictor);
|
||||
}
|
||||
if (ripper)
|
||||
P_RipperBlood(self, victim);
|
||||
else if ((self->flags5 & MF5_BLOODSPLATTER) && pr_checkthing() < 192)
|
||||
P_BloodSplatter(self->Pos(), victim, self->AngleTo(victim));
|
||||
|
||||
P_TraceBleed(dealt > 0 ? dealt : damage, victim, self);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
P_GiveBody(target, -damage);
|
||||
P_GiveBody(victim, -damage);
|
||||
}
|
||||
}
|
||||
DEFINE_ACTION_FUNCTION(AActor, DoMissileDamage)
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, DoMissileDamage, P_DoMissileDamage)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_OBJECT_NOT_NULL(target, AActor);
|
||||
P_DoMissileDamage(self, target);
|
||||
PARAM_OBJECT_NOT_NULL(victim, AActor);
|
||||
P_DoMissileDamage(self, victim);
|
||||
return 0;
|
||||
}
|
||||
//==========================================================================
|
||||
|
|
@ -1679,27 +1685,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch
|
|||
if (check == NULL || !*check)
|
||||
{
|
||||
tm.LastRipped[thing] = true;
|
||||
if (!(thing->flags & MF_NOBLOOD) &&
|
||||
!(thing->flags2 & MF2_REFLECTIVE) &&
|
||||
!(tm.thing->flags3 & MF3_BLOODLESSIMPACT) &&
|
||||
!(thing->flags2 & (MF2_INVULNERABLE | MF2_DORMANT)))
|
||||
{ // Ok to spawn blood
|
||||
P_RipperBlood(tm.thing, thing);
|
||||
}
|
||||
S_Sound(tm.thing, CHAN_BODY, 0, tm.thing->SoundVar(NAME_RipSound), 1, ATTN_IDLE);
|
||||
|
||||
// Do poisoning (if using new style poison)
|
||||
if (tm.thing->PoisonDamage > 0 && tm.thing->PoisonDuration != INT_MIN)
|
||||
{
|
||||
P_PoisonMobj(thing, tm.thing, tm.thing->target, tm.thing->PoisonDamage, tm.thing->PoisonDuration, tm.thing->PoisonPeriod, tm.thing->PoisonDamageType);
|
||||
}
|
||||
|
||||
damage = tm.thing->GetMissileDamage(3, 2);
|
||||
int newdam = P_DamageMobj(thing, tm.thing, tm.thing->target, damage, tm.thing->DamageType);
|
||||
if (!(tm.thing->flags3 & MF3_BLOODLESSIMPACT))
|
||||
{
|
||||
P_TraceBleed(newdam > 0 ? newdam : damage, thing, tm.thing);
|
||||
}
|
||||
P_DoMissileDamage(tm.thing, thing);
|
||||
if (thing->flags2 & MF2_PUSHABLE
|
||||
&& !(tm.thing->flags2 & MF2_CANNOTPUSH))
|
||||
{ // Push thing
|
||||
|
|
@ -2100,10 +2086,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 +2103,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 +2129,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;
|
||||
|
|
@ -2425,7 +2464,9 @@ bool P_TryMove(AActor *thing, const DVector2 &pos,
|
|||
// If it's a bouncer, let it bounce off its new floor, too.
|
||||
if (thing->BounceFlags & BOUNCE_Floors)
|
||||
{
|
||||
thing->FloorBounceMissile(tm.floorsector->floorplane);
|
||||
F3DFloor* ff = nullptr;
|
||||
NextLowestFloorAt(tm.sector, tm.pos.X, tm.pos.Y, tm.pos.Z, 0, thing->MaxStepHeight, nullptr, &ff);
|
||||
thing->FloorBounceMissile(ff != nullptr ? *ff->top.plane : tm.floorsector->floorplane, ff != nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -3511,7 +3552,9 @@ bool FSlide::BounceWall(AActor *mo)
|
|||
double movelen;
|
||||
line_t *line;
|
||||
|
||||
if (!(mo->BounceFlags & BOUNCE_Walls))
|
||||
// The plane bounce flags need to be checked here in case it hit a ramp while
|
||||
// moving along the xy axes.
|
||||
if (!(mo->BounceFlags & (BOUNCE_Walls | BOUNCE_Ceilings | BOUNCE_Floors)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3542,17 +3585,22 @@ bool FSlide::BounceWall(AActor *mo)
|
|||
{ // Could not find a wall, so bounce off the floor/ceiling instead.
|
||||
double floordist = mo->Z() - mo->floorz;
|
||||
double ceildist = mo->ceilingz - mo->Z();
|
||||
F3DFloor* ff = nullptr;
|
||||
if (floordist <= ceildist)
|
||||
{
|
||||
mo->FloorBounceMissile(mo->Sector->floorplane);
|
||||
return true;
|
||||
NextLowestFloorAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), 0, mo->MaxStepHeight, nullptr, &ff);
|
||||
return !mo->FloorBounceMissile(ff != nullptr ? *ff->top.plane : mo->floorsector->floorplane, ff != nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
mo->FloorBounceMissile(mo->Sector->ceilingplane);
|
||||
return true;
|
||||
NextHighestCeilingAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), mo->Top(), 0, nullptr, &ff);
|
||||
return !mo->FloorBounceMissile(ff != nullptr ? *ff->bottom.plane : mo->ceilingsector->ceilingplane, ff != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(mo->BounceFlags & BOUNCE_Walls))
|
||||
return false;
|
||||
|
||||
line = bestslideline;
|
||||
|
||||
if (mo->flags & MF_MISSILE)
|
||||
|
|
@ -3726,7 +3774,8 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop)
|
|||
else // Don't run through this for MBF-style bounces
|
||||
{
|
||||
// The reflected velocity keeps only about 70% of its original speed
|
||||
mo->Vel.Z = (mo->Vel.Z - 2. * dot) * mo->bouncefactor;
|
||||
mo->Vel.Z -= 2. * dot;
|
||||
mo->Vel *= mo->bouncefactor;
|
||||
}
|
||||
|
||||
mo->PlayBounceSound(true);
|
||||
|
|
@ -3737,8 +3786,11 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop)
|
|||
}
|
||||
else if (mo->BounceFlags & (BOUNCE_AutoOff | BOUNCE_AutoOffFloorOnly))
|
||||
{
|
||||
if (!(mo->flags & MF_NOGRAVITY) && (mo->Vel.Z < 3.))
|
||||
mo->BounceFlags &= ~BOUNCE_TypeMask;
|
||||
if (mo->Vel.Z >= 0 || (mo->BounceFlags & BOUNCE_AutoOff))
|
||||
{
|
||||
if (!(mo->flags & MF_NOGRAVITY) && (mo->Vel.Z < 3.))
|
||||
mo->BounceFlags &= ~BOUNCE_TypeMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mo->BounceFlags & BOUNCE_UseBounceState)
|
||||
|
|
@ -4620,6 +4672,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 +4692,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 +4783,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 +4819,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 +4850,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 +4898,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 +4991,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 +5444,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->WorldRailgunPreFired(damagetype, puffclass, p))
|
||||
{
|
||||
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 +5496,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 +5532,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 +5620,9 @@ void P_RailAttack(FRailParams *p)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
source->Level->localEventManager->WorldRailgunFired(source, start, trace.HitPos, thepuff, flags);
|
||||
|
||||
if (thepuff != NULL)
|
||||
{
|
||||
if (trace.Crossed3DWater || trace.CrossedWater)
|
||||
|
|
|
|||
|
|
@ -1545,6 +1545,13 @@ void DActorModelData::OnDestroy()
|
|||
animationIDs.Reset();
|
||||
}
|
||||
|
||||
void DViewPosition::Serialize(FSerializer& arc)
|
||||
{
|
||||
Super::Serialize(arc);
|
||||
arc("offset", Offset)
|
||||
("flags", Flags);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// PROC P_ExplodeMissile
|
||||
|
|
@ -1732,7 +1739,7 @@ void AActor::PlayBounceSound(bool onfloor)
|
|||
// Returns true if the missile was destroyed
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool AActor::FloorBounceMissile (secplane_t &plane)
|
||||
bool AActor::FloorBounceMissile (secplane_t &plane, bool is3DFloor)
|
||||
{
|
||||
if (flags & MF_MISSILE)
|
||||
{
|
||||
|
|
@ -1775,9 +1782,13 @@ bool AActor::FloorBounceMissile (secplane_t &plane)
|
|||
}
|
||||
}
|
||||
|
||||
DVector3 norm = plane.Normal();
|
||||
if (is3DFloor)
|
||||
norm = -norm;
|
||||
|
||||
bool onsky;
|
||||
|
||||
if (plane.fC() < 0)
|
||||
if (norm.Z < 0)
|
||||
{ // on ceiling
|
||||
if (!(BounceFlags & BOUNCE_Ceilings))
|
||||
return true;
|
||||
|
|
@ -1808,11 +1819,11 @@ bool AActor::FloorBounceMissile (secplane_t &plane)
|
|||
return true;
|
||||
}
|
||||
|
||||
double dot = (Vel | plane.Normal()) * 2;
|
||||
double dot = (Vel | norm) * 2;
|
||||
|
||||
if (BounceFlags & (BOUNCE_HereticType | BOUNCE_MBF))
|
||||
{
|
||||
Vel -= plane.Normal() * dot;
|
||||
Vel -= norm * dot;
|
||||
AngleFromVel();
|
||||
if (!(BounceFlags & BOUNCE_MBF)) // Heretic projectiles die, MBF projectiles don't.
|
||||
{
|
||||
|
|
@ -1826,7 +1837,7 @@ bool AActor::FloorBounceMissile (secplane_t &plane)
|
|||
else // Don't run through this for MBF-style bounces
|
||||
{
|
||||
// The reflected velocity keeps only about 70% of its original speed
|
||||
Vel = (Vel - plane.Normal() * dot) * bouncefactor;
|
||||
Vel = (Vel - norm * dot) * bouncefactor;
|
||||
AngleFromVel();
|
||||
}
|
||||
|
||||
|
|
@ -1835,7 +1846,7 @@ bool AActor::FloorBounceMissile (secplane_t &plane)
|
|||
// Set bounce state
|
||||
if (BounceFlags & BOUNCE_UseBounceState)
|
||||
{
|
||||
FName names[2] = { NAME_Bounce, plane.fC() < 0 ? NAME_Ceiling : NAME_Floor };
|
||||
FName names[2] = { NAME_Bounce, norm.Z < 0 ? NAME_Ceiling : NAME_Floor };
|
||||
FState *bouncestate = FindState(2, names);
|
||||
if (bouncestate != nullptr)
|
||||
{
|
||||
|
|
@ -1850,10 +1861,10 @@ bool AActor::FloorBounceMissile (secplane_t &plane)
|
|||
}
|
||||
else if (BounceFlags & (BOUNCE_AutoOff|BOUNCE_AutoOffFloorOnly))
|
||||
{
|
||||
if (plane.fC() > 0 || (BounceFlags & BOUNCE_AutoOff))
|
||||
if (norm.Z > 0 || (BounceFlags & BOUNCE_AutoOff))
|
||||
{
|
||||
// AutoOff only works when bouncing off a floor, not a ceiling (or in compatibility mode.)
|
||||
if (!(flags & MF_NOGRAVITY) && (Vel.Z < 3))
|
||||
if (!(flags & MF_NOGRAVITY) && (norm.Z < 0 || ((Vel | norm) < 3)))
|
||||
BounceFlags &= ~BOUNCE_TypeMask;
|
||||
}
|
||||
}
|
||||
|
|
@ -2646,7 +2657,9 @@ static void P_ZMovement (AActor *mo, double oldfloorz)
|
|||
mo->SetZ(mo->floorz);
|
||||
if (mo->BounceFlags & BOUNCE_Floors)
|
||||
{
|
||||
mo->FloorBounceMissile (mo->floorsector->floorplane);
|
||||
F3DFloor* ff = nullptr;
|
||||
NextLowestFloorAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), 0, mo->MaxStepHeight, nullptr, &ff);
|
||||
mo->FloorBounceMissile (ff != nullptr ? *ff->top.plane : mo->floorsector->floorplane, ff != nullptr);
|
||||
/* if (!CanJump(mo)) */ return;
|
||||
}
|
||||
else if (mo->flags3 & MF3_NOEXPLODEFLOOR)
|
||||
|
|
@ -2682,7 +2695,9 @@ static void P_ZMovement (AActor *mo, double oldfloorz)
|
|||
}
|
||||
else if (mo->BounceFlags & BOUNCE_MBF && mo->Vel.Z) // check for MBF-like bounce on non-missiles
|
||||
{
|
||||
mo->FloorBounceMissile(mo->floorsector->floorplane);
|
||||
F3DFloor* ff = nullptr;
|
||||
NextLowestFloorAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), 0, mo->MaxStepHeight, nullptr, &ff);
|
||||
mo->FloorBounceMissile(ff != nullptr ? *ff->top.plane : mo->floorsector->floorplane, ff != nullptr);
|
||||
}
|
||||
if (mo->flags3 & MF3_ISMONSTER) // Blasted mobj falling
|
||||
{
|
||||
|
|
@ -2753,7 +2768,9 @@ static void P_ZMovement (AActor *mo, double oldfloorz)
|
|||
mo->SetZ(mo->ceilingz - mo->Height);
|
||||
if (mo->BounceFlags & BOUNCE_Ceilings)
|
||||
{ // ceiling bounce
|
||||
mo->FloorBounceMissile (mo->ceilingsector->ceilingplane);
|
||||
F3DFloor* ff = nullptr;
|
||||
NextHighestCeilingAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), mo->Top(), 0, nullptr, &ff);
|
||||
mo->FloorBounceMissile(ff != nullptr ? *ff->bottom.plane : mo->ceilingsector->ceilingplane, ff != nullptr);
|
||||
/* if (!CanJump(mo)) */ return;
|
||||
}
|
||||
if (mo->flags & MF_SKULLFLY)
|
||||
|
|
@ -4362,14 +4379,13 @@ void AActor::Tick ()
|
|||
}
|
||||
if (Vel.Z != 0 && (BounceFlags & BOUNCE_Actors))
|
||||
{
|
||||
bool res = P_BounceActor(this, onmo, true);
|
||||
if (flags & MF_MISSILE)
|
||||
P_DoMissileDamage(this, onmo);
|
||||
|
||||
// If the bouncer is a missile and has hit the other actor it needs to be exploded here
|
||||
// to be in line with the case when an actor's side is hit.
|
||||
if (!res && (flags & MF_MISSILE))
|
||||
{
|
||||
P_DoMissileDamage(this, onmo);
|
||||
if (!P_BounceActor(this, onmo, true) && (flags & MF_MISSILE))
|
||||
P_ExplodeMissile(this, nullptr, onmo);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -4435,6 +4451,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)
|
||||
{
|
||||
|
|
@ -5586,16 +5610,13 @@ AActor *FLevelLocals::SpawnPlayer (FPlayerStart *mthing, int playernum, int flag
|
|||
|
||||
PlayerSpawnPickClass(playernum);
|
||||
|
||||
if (( dmflags2 & DF2_SAME_SPAWN_SPOT ) &&
|
||||
( p->playerstate == PST_REBORN ) &&
|
||||
( deathmatch == false ) &&
|
||||
( gameaction != ga_worlddone ) &&
|
||||
( p->mo != NULL ) &&
|
||||
( !(p->mo->Sector->Flags & SECF_NORESPAWN) ) &&
|
||||
( NULL != p->attacker ) && // don't respawn on damaging floors
|
||||
( p->mo->Sector->damageamount < TELEFRAG_DAMAGE )) // this really should be a bit smarter...
|
||||
if ((dmflags2 & DF2_SAME_SPAWN_SPOT) && !deathmatch
|
||||
&& p->mo != nullptr && p->playerstate == PST_REBORN
|
||||
&& gameaction != ga_worlddone
|
||||
&& !(p->mo->Sector->Flags & SECF_NORESPAWN)
|
||||
&& p->LastDamageType != NAME_Suicide)
|
||||
{
|
||||
spawn = p->mo->Pos();
|
||||
spawn = p->LastSafePos;
|
||||
SpawnAngle = p->mo->Angles.Yaw;
|
||||
}
|
||||
else
|
||||
|
|
@ -6652,7 +6673,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 +6767,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 +6838,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 +7848,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())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -129,6 +129,28 @@ DEFINE_ACTION_FUNCTION_NATIVE(_Sector, NextSpecialSector, P_NextSpecialSector)
|
|||
ACTION_RETURN_POINTER(P_NextSpecialSector(self, type, nogood));
|
||||
}
|
||||
|
||||
bool sector_t::IsDangerous(const DVector3& pos, double height) const
|
||||
{
|
||||
if (damageamount > 0)
|
||||
return true;
|
||||
|
||||
auto cl = dyn_cast<DCeiling>(ceilingdata.Get());
|
||||
if (cl != nullptr && cl->getCrush() > 0)
|
||||
return true;
|
||||
|
||||
for (auto rover : e->XFloor.ffloors)
|
||||
{
|
||||
if ((rover->flags & FF_EXISTS) && rover->model->damageamount > 0
|
||||
&& pos.Z <= rover->top.plane->ZatPoint(pos)
|
||||
&& pos.Z + height >= rover->bottom.plane->ZatPoint(pos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// P_FindLowestFloorSurrounding()
|
||||
// FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -496,6 +496,10 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec
|
|||
entersector = &DummySector[sectorsel];
|
||||
sectorsel ^= 1;
|
||||
|
||||
// We need to make sure that 3D floors clipping underneath the ground/above the ceiling don't
|
||||
// accidentally get ignored.
|
||||
bool setFloor = false, setCeiling = false;
|
||||
|
||||
for (auto rover : entersector->e->XFloor.ffloors)
|
||||
{
|
||||
int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH);
|
||||
|
|
@ -508,8 +512,8 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec
|
|||
// clip to the part of the sector we are in
|
||||
if (hit.Z > ff_top)
|
||||
{
|
||||
// 3D floor height is the same as the floor height. We need to test a second spot to see if it is above or below
|
||||
if (fabs(bf - ff_top) < EQUAL_EPSILON)
|
||||
// 3D floor height is the same as the floor height or underground. We need to test a second spot to see if it is above or below
|
||||
if ((ff_top < bf && !setFloor) || fabs(bf - ff_top) < EQUAL_EPSILON)
|
||||
{
|
||||
double cf = entersector->floorplane.ZatPoint(entersector->centerspot);
|
||||
double ffc = rover->top.plane->ZatPoint(entersector->centerspot);
|
||||
|
|
@ -522,6 +526,7 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec
|
|||
// above
|
||||
if (bf < ff_top)
|
||||
{
|
||||
setFloor = true;
|
||||
entersector->floorplane = *rover->top.plane;
|
||||
entersector->SetTexture(sector_t::floor, *rover->top.texture, false, false);
|
||||
entersector->ClearPortal(sector_t::floor);
|
||||
|
|
@ -530,8 +535,8 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec
|
|||
}
|
||||
else if (hit.Z < ff_bottom)
|
||||
{
|
||||
// 3D floor height is the same as the ceiling height. We need to test a second spot to see if it is above or below
|
||||
if (fabs(bc - ff_bottom) < EQUAL_EPSILON)
|
||||
// 3D floor height is the same as the ceiling height or above it. We need to test a second spot to see if it is above or below
|
||||
if ((ff_bottom > bc && !setCeiling) || fabs(bc - ff_bottom) < EQUAL_EPSILON)
|
||||
{
|
||||
double cc = entersector->ceilingplane.ZatPoint(entersector->centerspot);
|
||||
double fcc = rover->bottom.plane->ZatPoint(entersector->centerspot);
|
||||
|
|
@ -544,6 +549,7 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec
|
|||
//below
|
||||
if (bc > ff_bottom)
|
||||
{
|
||||
setCeiling = true;
|
||||
entersector->ceilingplane = *rover->bottom.plane;
|
||||
entersector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false, false);
|
||||
entersector->ClearPortal(sector_t::ceiling);
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ static FRandom pr_skullpop ("SkullPop");
|
|||
|
||||
// [SP] Allows respawn in single player
|
||||
CVAR(Bool, sv_singleplayerrespawn, false, CVAR_SERVERINFO | CVAR_CHEAT)
|
||||
CVAR(Float, snd_footstepvolume, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
// Variables for prediction
|
||||
CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
|
@ -144,6 +145,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;
|
||||
|
|
@ -358,6 +361,7 @@ void player_t::CopyFrom(player_t &p, bool copyPSP)
|
|||
MUSINFOactor = p.MUSINFOactor;
|
||||
MUSINFOtics = p.MUSINFOtics;
|
||||
SoundClass = p.SoundClass;
|
||||
LastSafePos = p.LastSafePos;
|
||||
angleOffsetTargets = p.angleOffsetTargets;
|
||||
if (copyPSP)
|
||||
{
|
||||
|
|
@ -1204,15 +1208,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)
|
||||
|
|
@ -1297,6 +1292,13 @@ void P_PlayerThink (player_t *player)
|
|||
player->SubtitleCounter--;
|
||||
}
|
||||
|
||||
if (player->playerstate == PST_LIVE
|
||||
&& player->mo->Z() <= player->mo->floorz
|
||||
&& !player->mo->Sector->IsDangerous(player->mo->Pos(), player->mo->Height))
|
||||
{
|
||||
player->LastSafePos = player->mo->Pos();
|
||||
}
|
||||
|
||||
// Bots do not think in freeze mode.
|
||||
if (player->mo->Level->isFrozen() && player->Bot != nullptr)
|
||||
{
|
||||
|
|
@ -1470,6 +1472,8 @@ void P_PredictPlayer (player_t *player)
|
|||
return;
|
||||
}
|
||||
|
||||
FRandom::SaveRNGState(PredictionRNG);
|
||||
|
||||
// Save original values for restoration later
|
||||
PredictionPlayerBackup.CopyFrom(*player, false);
|
||||
|
||||
|
|
@ -1609,6 +1613,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);
|
||||
|
|
@ -1781,7 +1787,9 @@ void player_t::Serialize(FSerializer &arc)
|
|||
("onground", onground)
|
||||
("musinfoactor", MUSINFOactor)
|
||||
("musinfotics", MUSINFOtics)
|
||||
("soundclass", SoundClass);
|
||||
("soundclass", SoundClass)
|
||||
("angleoffsettargets", angleOffsetTargets)
|
||||
("lastsafepos", LastSafePos);
|
||||
|
||||
if (arc.isWriting ())
|
||||
{
|
||||
|
|
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ static TMap<FState *, FScriptPosition> StateSourceLines;
|
|||
static FScriptPosition unknownstatesource("unknown file", 0);
|
||||
|
||||
EXTERN_CVAR(Bool, strictdecorate);
|
||||
EXTERN_CVAR(Bool, warningstoerrors);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
@ -464,8 +465,27 @@ void LoadActors()
|
|||
|
||||
if (FScriptPosition::ErrorCounter > 0)
|
||||
{
|
||||
I_Error("%d errors while parsing DECORATE scripts", FScriptPosition::ErrorCounter);
|
||||
if (FScriptPosition::WarnCounter > 0)
|
||||
{
|
||||
I_Error("%d errors, %d warnings while parsing scripts", FScriptPosition::ErrorCounter, FScriptPosition::WarnCounter);
|
||||
}
|
||||
else
|
||||
{
|
||||
I_Error("%d errors while parsing scripts", FScriptPosition::ErrorCounter);
|
||||
}
|
||||
}
|
||||
else if (FScriptPosition::WarnCounter > 0)
|
||||
{
|
||||
if(warningstoerrors)
|
||||
{
|
||||
I_Error("%d warnings while parsing scripts\n", FScriptPosition::WarnCounter);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf(TEXTCOLOR_ORANGE "%d warnings while parsing scripts\n", FScriptPosition::WarnCounter);
|
||||
}
|
||||
}
|
||||
|
||||
FScriptPosition::ResetErrorCounter();
|
||||
// AllActorClasses hasn'T been set up yet.
|
||||
for (int i = PClass::AllClasses.Size() - 1; i >= 0; i--)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1851,8 +1851,9 @@ DEFINE_ACTION_FUNCTION(AActor, BouncePlane)
|
|||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_POINTER(plane, secplane_t);
|
||||
PARAM_BOOL(is3DFloor);
|
||||
|
||||
ACTION_RETURN_BOOL(self->FloorBounceMissile(*plane));
|
||||
ACTION_RETURN_BOOL(self->FloorBounceMissile(*plane, is3DFloor));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, PlayBounceSound)
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* def
|
|||
FSerializer &Serialize(FSerializer &arc, const char *key, ticcmd_t &sid, ticcmd_t *def);
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, usercmd_t &cmd, usercmd_t *def);
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, FInterpolator &rs, FInterpolator *def);
|
||||
FSerializer& Serialize(FSerializer& arc, const char* key, struct FStandaloneAnimation& value, struct FStandaloneAnimation* defval);
|
||||
|
||||
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval);
|
||||
template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval);
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
@ -99,6 +99,7 @@ const char *GetVersionString();
|
|||
// This is so that derivates can use the same savegame versions without worrying about engine compatibility
|
||||
#define GAMESIG "VKDOOM"
|
||||
#define BASEWAD "vkdoom.pk3"
|
||||
// Set OPTIONALWAD to "" (null) to disable searching for it
|
||||
#define OPTIONALWAD "game_support.pk3"
|
||||
#define GZDOOM 1
|
||||
#define VR3D_ENABLED
|
||||
|
|
|
|||
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 |
|
|
@ -873,12 +873,12 @@ Appearance,DSPLYMNU_APPEARANCE,,,,Vzhled,Udseende,Spieldarstellung,,Aspekto,Apar
|
|||
Advanced Display Options,DSPLYMNU_ADVANCED,,,,Grafika (pokročilé),Avancerede visningsindstillinger,Erweiterte Anzeigeoptionen,,Altnivelaj ekran-agordoj,Opciones avanzadas de visualización,,Näytön lisäasetukset,Options d'affichage avancées,Napredne postavke zaslona,Speciális megjelenítési beállítások,Opzioni di visualizzazione avanzate,高度なディスプレイオプション,고급 디스플레이 옵션,Geavanceerde Weergave Opties,Avanserte visningsalternativer,Zaawansowane Opcje Wyświetlania,Opções de vídeo avançadas,,Opțiuni avansate de afișare,Расширенные настройки экрана,Напредне опције приказа,Avancerade visningsalternativ,Gelişmiş Görüntüleme Seçenekleri,Додаткові параметри відображення,
|
||||
,,IWAD/Game picker,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
Select which game file to run.,PICKER_SELECT,,,,"Vyberte, jaký herní soubor spustit.","Vælg, hvilket spil du vil spille",Bitte wähle ein Spiel aus.,,Elektu kiun ludodosieron ruli.,,,Valitse suoritettava pelitiedosto.,Sélectionner le jeu à jouer,Odaberi koju igru želiš pokrenuti.,"Válassza ki, hogy melyik játékfájlt futtassa.",Selezionare il file di gioco da eseguire.,実行するゲームファイルを選択します。,실행할 게임 파일을 선택합니다.,Selecteer welk spel je wilt spelen,Velg hvilket spill du vil spille,"Wybierz, który plik gry uruchomić.",Selecione qual arquivo de jogo rodar.,,Selectați ce fișier de joc să rulați.,Выбор файла игры для запуска.,Изаберите коју датотеку игре желите да покренете.,Välj vilket spel du vill spela,Hangi oyunu oynayacağınızı seçin,Виберіть файл гри для запуску.,
|
||||
Play Game,PICKER_PLAY,,,,Hrát hru,Start spil,Spielen,,Ludi ludon,,,Pelin pelaaminen,Démarrer le jeu,Zaigraj igru,Játék lejátszása,Esegui gioco,ゲームをプレイする,게임 플레이,Spel starten,Start spill,Graj,Jogar,,Joacă jocul,Играть,Играј игру,Starta spel,Oyunu Başlat,Запустити гру,
|
||||
Play Game,PICKER_PLAY,,,,Hrát hru,Start spil,Spielen,,Ludi ludon,,,Pelin pelaaminen,Démarrer le jeu,Zaigraj igru,Játék lejátszása,Esegui gioco,プレイ,게임 플레이,Spel starten,Start spill,Graj,Jogar,,Joacă jocul,Играть,Играј игру,Starta spel,Oyunu Başlat,Запустити гру,
|
||||
Exit,PICKER_EXIT,,,,Odejít,Afslut,Verlassen,,Eliri,,,Poistu,Quitter,Izlaz,Kilépés,Esci,終了,종료,Verlaten,Avslutt,Wyjdź,Sair,,Ieșire,Выход,Изађи,Avsluta,Çıkış,Вихід,
|
||||
General,PICKER_GENERAL,,,,Obecné,Generelt,Allgemein,,Ĝenerala,,,Yleistä,Général,Općenito,Általános,Generale,一般,일반,Algemeen,Generelt,Ogólne,Geral,,,Общее,Генерал,Allmänt,Genel,Загальні,
|
||||
Extra Graphics,PICKER_EXTRA,,,,Grafické doplňky,Ekstra grafik,Extragrafiken,,Ekstra grafiko,,,Extra Graphics,Graphiques supplémentaires,Dodatna grafika,Extra grafika,Grafica extra,追加グラフィックス,추가 그래픽,Extra afbeeldingen,Ekstra grafikk,Ekstra grafiki,Gráficos extras,,Grafică suplimentară,Доп. графика,Ектра Грапхицс,Extra grafik,Ekstra Grafikler,Додаткова графіка,
|
||||
Fullscreen,PICKER_FULLSCREEN,,,,Přes celou obrazovku,Fuld skærm,Vollbild,,Plena ekrano,,,Koko näyttö,Plein écran,Puni zaslon,Teljes képernyő,Schermo intero,フルスクリーン,전체 화면,Volledig scherm,Fullskjerm,Pełny ekran,Tela cheia,,Ecran complet,Полный экран,Цео екран,Fullskärm,Tam Ekran,Повноекранний режим,
|
||||
Disable autoload,PICKER_NOAUTOLOAD,,,,Zakázat autoload,Deaktiver autoload,Autoload deaktivieren,,Malvalidigi aŭtoŝargon,,,Poista automaattinen lataus käytöstä,Désactiver le chargement automatique,Onemogući automatsko učitavanje,Automatikus betöltés kikapcsolása,Disabilita il caricamento automatico,オートロードを無効にする,자동 로드 비활성화,Autoload uitschakelen,Deaktiver autolading,Wyłącz auto-ładowanie,Desativar autocarregam.,,Dezactivați încărcarea automată,Откл. автозагрузку,Онемогући аутоматско учитавање,Inaktivera autoload,Otomatik yükleme yok,Вимкнути автозавантаження,
|
||||
Disable autoload,PICKER_NOAUTOLOAD,,,,Zakázat autoload,Deaktiver autoload,Autoload deaktivieren,,Malvalidigi aŭtoŝargon,,,Poista automaattinen lataus käytöstä,Désactiver le chargement automatique,Onemogući automatsko učitavanje,Automatikus betöltés kikapcsolása,Disabilita il caricamento automatico,オートロード無効,자동 로드 비활성화,Autoload uitschakelen,Deaktiver autolading,Wyłącz auto-ładowanie,Desativar autocarregam.,,Dezactivați încărcarea automată,Откл. автозагрузку,Онемогући аутоматско учитавање,Inaktivera autoload,Otomatik yükleme yok,Вимкнути автозавантаження,
|
||||
Don't ask me again,PICKER_DONTASK,,,,Již se neptat,Spørg mig ikke igen,Nicht nochmal fragen,,Ne demandu min denove,,,Älä kysy uudestaan,Ne me demandez plus rien,Više me ne pitaj,Ne kérdezz újra,Non chiedermelo più,再度聞かない,다시 묻지 마세요,Vraag me niet opnieuw,Ikke spør meg igjen,Nie pytaj ponownie,Não perguntar de novo,,Nu mă mai întrebați din nou,Не спрашивать снова,Не питај ме поново,Fråga mig inte igen,Bir daha sorma.,Не запитуйте мене більше,
|
||||
Lights,PICKER_LIGHTS,,,,Světla,Lys,Lichtdefinitionen,,Lumoj,,,Valot,Lumières,Svjetla,Fények,Luci,ライト,조명,Verlichting,Lysdefinisjoner,Oświetlenie,Luzes,,Lumini,Освещение,Светла,Definitioner av ljus,Işık tanımları,Освітлення,
|
||||
Brightmaps,PICKER_BRIGHTMAPS,,,,,,,,Helomapoj,,,Brightmaps,Cartes lumineuses,Svijetle karte,Brightmaps,Mappe luminose,ブライトマップ,브라이트맵,Heldermaps,Lyskart,Mapowanie świateł,,,,Карты освещения,Бригхтмапс,Ljuskartor,Brightmaps,Яскраві карти,
|
||||
|
|
@ -891,4 +891,4 @@ Game,PICKER_TAB_PLAY,,,,Hra,Spil,Spiel,,Ludo,Juego,,Peli,Jeu,Igra,Játék,Gioco,
|
|||
Enable this controller,JOYMNU_JOYENABLE,Option to enable or disable individual controllers/joysticks when configuring it in the Controller Options menu,,,Povolit tento ovladač,Aktivér denne controller,Diesen Controller aktivieren,,Ŝalti ĉi tiun ludregilon,Habilitar este controlador,,Aktivoi tämä ohjain,Activer ce contrôleur,Omogući ovaj kontroler,Engedélyezze ezt a vezérlőt,Attiva questo controller,このコントローラーを有効にする,이 컨트롤러 활성화,Deze controller inschakelen,Aktiver denne kontrolleren,Włącz ten kontroler,Habilitar este controle,,Activați acest controler,Включить этот контроллер,Омогућите овај контролер,Aktivera denna styrenhet,Bu denetleyiciyi etkinleştirin,Увімкніть цей контролер,
|
||||
Open Main Menu,CNTRLMNU_OPEN_MAIN,,,,Otevřít hlavní menu,Åbn hovedmenuen,Hauptmenü öffnen,,Malfermi ĉefan menuon,Abrir el menú principal,,Avaa päävalikko,Ouvrir le menu principal,Otvori glavni izbornik,Főmenü megnyitása,Apri il menu principale,メインメニューを開く,메인 메뉴 열기,Hoofdmenu openen,Åpne hovedmenyen,Otwórz meny główne,Abrir menu principal,,Deschideți meniul principal,Открыть главное меню,Отворите главни мени,Öppna huvudmenyn,Ana Menüyü Aç,Відкрити головне меню,
|
||||
,,New content,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
Module replayer,MODMNU_REPLAYER,,,,Nastavení přehrávače modulů,Modulafspiller,Modul-Spieler,,Legilo de moduloj,Reproductor de módulos,,,Lecteur de module,,,Module replayer,,,Module replayer,Modulavspiller,,Reprodutor de módulos,,Redare a modulelor,Модуль-повторитель,,Modulåterspelare,,Module replayer,
|
||||
Module replayer,MODMNU_REPLAYER,,,,Nastavení přehrávače modulů,Modulafspiller,Modul-Spieler,,Legilo de moduloj,Reproductor de módulos,,,Lecteur de module,Ponavljač modula,,Module replayer,モジュール リプレイヤー,,Module replayer,Modulavspiller,,Reprodutor de módulos,,Redare a modulelor,Модуль-повторитель,,Modulåterspelare,,Module replayer,
|
||||
|
|
@ -1911,7 +1911,7 @@ armură.",Игрока %o обслюнявила бронированная дв
|
|||
%o was hit by %k's propulsor.,OB_MPP_SPLASH,,,,%o byl@[ao_cs] zasažen@[ao_cs] propulzorem hráčem %k.,%o blev ramt af %k's propulsor.,%o wurde von %ks Propeller getroffen,,%o estas trafita de la propulsilo de %k.,%o fue impactad@[ao_esp] por el propulsor de %k.,,%k osui %o parkaan työntimellään.,%o à été frappé@[e_fr] par le propulseur de %k.,,%o elérte %k elhárítóját.,%o è stato colpito dal propulsore di %k.,%o に %k のロケットゾーチャーがとどいた。,%o 은(는) %k 의 저치 추진기의 방사 능력을 우습게 봤습니다.,%o werd geraakt door %k's propulsor.,%o ble truffet av %k's propulsor.,%o został@[ao_pl] trafion@[adj_pl] pędnikiem %k.,%o foi atingid@[ao_ptb] pelo propulsor de %k.,,%o a fost învins de acceleratorul lui %k.,Игрок %o подстрелен из ускорителя зорча %k.,%o је ударен %k погоном.,%o blev träffad av %ks propulsor.,"%o, %k tarafından yenildi.",%k врази@[adj_1_ua] %o з прискореного зорчера.
|
||||
%o was phase zorched by %k.,OB_MPPHASEZORCH,,,,%o byl@[ao_cs] bzukr-vyfázován@[ao_cs] hráčem %k.,%o blev fasezorched af %k.,%o wurde von %k wegsynchronisiert,,%o estas fluktuantzorĉita de %k.,%o fue electrizad@[ao_esp] en fase por %k.,,%k vaiheiszorchasi %o paran.,%o à été phasé@[e_fr] par %k.,,%o fázis zorker áldozata lett %k által.,%o è stato phase-zorchato da %k.,%o は %k によってスベスベにされた。,%o 은(는) %k 의 전자 자쳐에 의해 쓰러졌습니다.,%o was fase zorched door %k.,%o ble fasebrent av %k.,%o został@[ao_pl] został sfazowan@[adj_pl] przez %k.,%o foi atingid@[ao_ptb] pelo zorcher fásico por %k.,,%o a fost înfrânt de zorcherul fazat al lui %k.,Игрок %o получил заряд из фазерного зорчера %k.,%o је фазно торчован %k.,%o blev faszorchad av %k.,"%o, %k tarafından yenildi.",%k фазорчну@[adj_1_ua] %o.
|
||||
%o fell prey to %k's LAZ device.,OB_MPLAZ_BOOM,,,,%o padl@[ao_cs] velkodosahovému bzukru hráče %k.,%o blev offer for %k's LAZ-enhed.,%o fiel %ks Flächenzorcher zum Opfer,,%o iĝis predo de la LAZ-aparato de %k.,%o cayó pres@[ao_esp] ante el dispositivo LAZ de %k.,,%o lankesi pelaajan %k SAZ-laitteen uhriksi.,%o est devenu@[e_fr] la proie du ZZL de %k.,,%o prédául lett ejtve %k LAZ készülékével.,%o è stato preda del dispositivo LAZ di %k.,%o は %k のLAZデバイスのえじきになった。,%k 이(가) LAZ 장치를 들기 전에 %o 은(는) 도망쳐야 했습니다.,%o viel ten prooi aan %k's LAZ apparaat.,%o ble offer for %k's LAZ-enhet.,%o stał@[ao_pl] się ofiarą urządzenia LAZ %k.,%o foi vítima do dispositivo LAZ de %k.,,%o a căzut pradă dispozitivului LAZ al lui %k.,Игрок %o склонился перед «ЗБР» игрока %k.,%o је постао жртва %k ЛАЗ уређаја.,%o föll offer för %ks LAZ-enhet.,"%o, %k tarafından lazzlandı.",%o па@[adj_1_ua] жертвою LAZ девайсу %k.
|
||||
%o was lazzed by %k.,OB_MPLAZ_SPLASH,,,,%o byl@[ao_cs] odbzukrován hráčem %k.,%o blev lazzed af %k.,%o wurde von %k weggebeamt.,,%o estas LAZ-ita de %k.,%o fue LAZeado por %k.,,%k sazzasi pelaajaan %o.,%o à été pris@[e_fr] par le ZZL de %k.,,%o el lett lazzázva %k által.,%o è stato lazzato da %k.,%o は %k にとかされた。,%o 은(는) %k 의 LAZ 장치 범위를 벗어날 수 없었습니다.,%o werd gelazed door %k.,%o ble lazzed av %k.,%o został@[ao_pl] zLAZowan@[adj_pl] przez %k.,%o foi LAZead@[ao_ptb] por %k.,,%o a fost stropit de dispozitivul LAZ al lui %k.,Игрок %o получил заряд из «ЗБР» игрока %k.,%o је ЛАЗ-овао %k.,%o blev lazzad av %k.,"%o, %k tarafından lazzlandı.",%k LAZну@[adj_1_ua] %o.
|
||||
%o was lazzed by %k.,OB_MPBFG_SPLASH,,chex,,%o byl@[ao_cs] odbzukrován hráčem %k.,%o blev lazzed af %k.,%o wurde von %k weggebeamt.,,%o estas LAZ-ita de %k.,%o fue LAZeado por %k.,,%k sazzasi pelaajaan %o.,%o à été pris@[e_fr] par le ZZL de %k.,,%o el lett lazzázva %k által.,%o è stato lazzato da %k.,%o は %k にとかされた。,%o 은(는) %k 의 LAZ 장치 범위를 벗어날 수 없었습니다.,%o werd gelazed door %k.,%o ble lazzed av %k.,%o został@[ao_pl] zLAZowan@[adj_pl] przez %k.,%o foi LAZead@[ao_ptb] por %k.,,%o a fost stropit de dispozitivul LAZ al lui %k.,Игрок %o получил заряд из «ЗБР» игрока %k.,%o је ЛАЗ-овао %k.,%o blev lazzad av %k.,"%o, %k tarafından lazzlandı.",%k LAZну@[adj_1_ua] %o.
|
||||
,,Miscellaneous,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
E1M1: Landing Zone,CHUSTR_E1M1,,,,E1M1: Přistávací zóna,E1M1: Landingszone,E1M1: Landezone,,E1M1: Surterejo,E1M1: Zona de Aterrizaje,,E1M1: Laskeutumisalue,E1M1: La piste d'atterissage,,E1M1: Leszállópálya,E1M1: Zona di Atterraggio,E1M1: ちゃくりく ちてん,E1M1: 착륙 지점,E1M1: Landingszone,E1M1: Landingssone,E1M1: Strefa Lądowania,E1M1: Zona de Pouso,E1M1: Zona de Aterragem,E1M1: Pista de Aterizare,E1M1: Зона приземления,Е1М1: Зона слетања,E1M1: Landningszon,E1M1: İniş Bölgesi,E1M1: Зона висадки
|
||||
E1M2: Storage Facility,CHUSTR_E1M2,,,,E1M2: Skladiště,E1M2: Opbevaringsanlæg,E1M2: Lagerhalle,,E1M2: Stokejo,E1M2: Instalación de Almacenamiento,,E1M2: Varastolaitos,E1M2: Le centre de stockage,,E1M2: Raktárépület,E1M2: Struttura di Immagazzinamento,E1M2: ほかん しせつ,E1M2: 저장 시설,E1M2: Opslagfaciliteit,E1M2: Lagringsanlegg,E1M2: Magazyn,E1M2: Depósito,E1M2: Armazém,E1M2: Depozitul,E1M2: Хранилище,Е1М2: Складиште,E1M2: Förvaringsanläggning,E1M2: Depolama Tesisi,E1M2: Сховище
|
||||
|
|
@ -3415,4 +3415,5 @@ Restart level on Death,MISCMNU_RESTARTONDEATH,Disables autoloading save on death
|
|||
Pistol Start,GMPLYMNU_PISTOLSTART,Resets inventory on every map,,,Začít jen s pistolí,Start med pistol,Pistolenstart,,Komenci kun pistolo,,,,Démarrage du pistolet,,,Avvio pistola,ピストルスタート,,Start met pistool,Start med pistol,Start z pistoletem,Iniciar com a pistola,,,Пистолет в начале,,Starta med pistol,Tabanca ile başlayın,
|
||||
Allow creation of zombie players,CMPTMNU_VOODOOZOMBIES,,,,Povolit zombie hráče,,Erlaube Zombieplayer,,Permesi kreadon de zombiaj ludantoj,,,,,,,,ゾンビプレイヤーの生成許可,,,,Pozwól na tworzenie graczy-zombie,Permitir criação de jogadores zumbi,,,Разрешить создание игроков-зомби,,,,
|
||||
ignore floor z when teleporting,CMPTMNU_FDTELEPORT,,,,Při teleportu ignorovat výšku podlahy,,Ignoriere Fußbodenhöhe bem Teleportieren,,Ignori altecon de planko dum teleportado,,,,,,,,テレポート時にfloor z値を無視する,,,,Ignoruj wysokość podłogi podczas teleportacji,Ignorar altura do chão ao teletransportar,,,игнорирование диагонали пола (Z) при телепортации,,,,
|
||||
Forced-Perspective,OPTVAL_ANAMORPHIC,"Hardware Sprite Clipping mode that gives the illusion of a sprite being in a different position than it really is, allowing it to be rendered under the floor despite clipping",,,,,Erzwungene Perspektive,,,,,,,,,,,,,,,,,,Перспективный (анаморфотный),,,,
|
||||
Forced-Perspective,OPTVAL_ANAMORPHIC,"Hardware Sprite Clipping mode that gives the illusion of a sprite being in a different position than it really is, allowing it to be rendered under the floor despite clipping",,,Vynucená perspektiva,,Erzwungene Perspektive,,Devigita perspektivo,,,,,,,,,,,,,Perspectiva forçada,,,Принудительно перспективный,,,,
|
||||
Footstep Volume,SNDMENU_FOOTSTEPVOLUME,,,,Hlasitost kroků,,Schrittlautstärke,,,,,,,,,,,,,,,Volume dos passos,,,Громкость шагов,,,,
|
||||
|
Can't render this file because it is too large.
|
|
|
@ -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();
|
||||
|
|
@ -864,7 +866,7 @@ class Actor : Thinker native
|
|||
native void PlayPushSound();
|
||||
native bool BounceActor(Actor blocking, bool onTop);
|
||||
native bool BounceWall(Line l = null);
|
||||
native bool BouncePlane(readonly<SecPlane> plane);
|
||||
native bool BouncePlane(readonly<SecPlane> plane, bool is3DFloor = false);
|
||||
native void PlayBounceSound(bool onFloor);
|
||||
native bool ReflectOffActor(Actor blocking);
|
||||
|
||||
|
|
|
|||