Merge pull request #99 from madame-rachelle/gzd4-14-merge

Gzd4 14 merge
This commit is contained in:
Magnus Norddahl 2024-12-18 23:27:24 +01:00 committed by GitHub
commit 6f1f33e90d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
115 changed files with 1967 additions and 956 deletions

View file

@ -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)

View file

@ -169,7 +169,7 @@ static const char *TelefragSounds[] =
#endif
static int LastAnnounceTime;
static FRandom pr_bbannounce ("BBAnnounce");
static FCRandom pr_bbannounce ("BBAnnounce");
// CODE --------------------------------------------------------------------

View file

@ -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);

View file

@ -61,7 +61,7 @@ enum
{
DEFAULT_PITCH = 128,
};
static FRandom pr_soundpitch ("SoundPitch");
static FCRandom pr_soundpitch ("SoundPitch");
SoundEngine* soundEngine;
//==========================================================================

View file

@ -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)
{

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -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) {}

View file

@ -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;
}
}

View file

@ -66,6 +66,7 @@ public:
void NetInit(const char* message, int playerCount);
void NetProgress(int count);
void NetDone();
void NetClose();
private:
NSWindow* m_window;

View file

@ -531,3 +531,8 @@ void FConsoleWindow::NetDone()
m_netAbortButton = nil;
}
}
void FConsoleWindow::NetClose()
{
// TODO: Implement this
}

View file

@ -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)

View file

@ -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) { }

View file

@ -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

View file

@ -201,3 +201,8 @@ bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata
{
return NetStartWindow::RunMessageLoop(timer_callback, userdata);
}
void FBasicStartupScreen::NetClose()
{
NetStartWindow::CloseNetStartPane();
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}
//==========================================================================

View file

@ -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));

View file

@ -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();

View file

@ -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++;
}

View file

@ -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");
}

View file

@ -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
View 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.

View file

@ -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);

View file

@ -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
//

View file

@ -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;

View file

@ -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;

View file

@ -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.
////////////////////////////////////////////////////////////////////////////////

View file

@ -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
{

View file

@ -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)

View file

@ -118,6 +118,9 @@ struct FTerrainDef
bool DamageOnLand;
double Friction;
double MoveFactor;
FSoundID StepSound;
double StepDistance;
double StepDistanceMinVel;
};
extern TArray<FSplashDef> Splashes;

View file

@ -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);

View file

@ -56,7 +56,7 @@ FTextureAnimator TexAnim;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static FRandom pr_animatepictures ("AnimatePics");
static FCRandom pr_animatepictures ("AnimatePics");
// CODE --------------------------------------------------------------------

View file

@ -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"))
{

View file

@ -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));
}

View file

@ -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();
}
}
}

View file

@ -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;

View file

@ -812,6 +812,8 @@ xx(damageinterval)
xx(leakiness)
xx(damageterraineffect)
xx(damagehazard)
xx(hurtmonsters)
xx(harminair)
xx(floorterrain)
xx(ceilingterrain)
xx(floor_reflect)

View file

@ -62,7 +62,7 @@
#include "doommenu.h"
#include "g_game.h"
static FRandom pr_randomspeech("RandomSpeech");
static FCRandom pr_randomspeech("RandomSpeech");
static int ConversationMenuY;

View file

@ -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();
}
}
//==========================================================================

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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");
//-----------------------------------------------------------------------------

View file

@ -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)

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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.

View file

@ -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)
{

View file

@ -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

View file

@ -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)

View file

@ -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())
{

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -49,7 +49,7 @@
#include "actorinlines.h"
#include "animations.h"
static FRandom pr_switchanim ("AnimSwitch");
static FCRandom pr_switchanim ("AnimSwitch");
class DActiveButton : public DThinker
{

View file

@ -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;

View file

@ -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);

View file

@ -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 ())
{

View 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;
};

View file

@ -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)
{

View file

@ -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;

View file

@ -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();
}
//==========================================================================

View file

@ -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('(');

View file

@ -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

View file

@ -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;
}

View file

@ -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--)

View file

@ -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),

View file

@ -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))
{

View file

@ -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)

View file

@ -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());
}
}
}

View file

@ -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);

View file

@ -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)
{

View file

@ -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 --------------------------------------------------------------------

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

View file

@ -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,

View file

@ -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.

View file

@ -78,6 +78,7 @@ DoomEdNums
9081 = SkyPicker
9082 = SectorSilencer
9083 = SkyCamCompat
9084 = OrthographicCamera
9200 = Decal
9300 = "$PolyAnchor"
9301 = "$PolySpawn"

View file

@ -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);

Some files were not shown because too many files have changed in this diff Show more