Merge remote-tracking branch 'origin/master' into post-4.13
# Conflicts: # src/playsim/p_3dfloors.cpp # src/playsim/p_mobj.cpp # src/playsim/p_spec.cpp # src/playsim/p_spec.h # src/version.h
This commit is contained in:
commit
51fddf08c3
43 changed files with 859 additions and 428 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "renderstyle.h"
|
||||
#include "dobject.h"
|
||||
#include "refcounted.h"
|
||||
#include "printf.h"
|
||||
|
||||
struct DrawParms;
|
||||
struct FColormap;
|
||||
|
|
@ -279,6 +280,7 @@ public:
|
|||
class FCanvas : public DObject
|
||||
{
|
||||
DECLARE_CLASS(FCanvas, DObject)
|
||||
void OnDestroy() override { I_Error("Calling Destroy on a canvas object is not allowed."); }
|
||||
public:
|
||||
F2DDrawer Drawer;
|
||||
FCanvasTexture* Tex = nullptr;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -268,7 +276,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.;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -282,7 +306,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.);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,25 +331,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.
|
||||
|
|
@ -333,7 +357,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;
|
||||
|
|
@ -348,6 +372,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
|
||||
|
|
@ -423,6 +518,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);
|
||||
|
|
@ -438,20 +534,34 @@ void PerformWipe(FTexture* startimg, FTexture* endimg, int wipe_type, bool stops
|
|||
|
||||
do
|
||||
{
|
||||
do
|
||||
if (wiper->Interpolatable() && !cl_capfps)
|
||||
{
|
||||
I_WaitVBL(2);
|
||||
nowtime = I_msTime();
|
||||
diff = (nowtime - wipestart) * 40 / 1000; // Using 35 here feels too slow.
|
||||
} while (diff < 1);
|
||||
wipestart = nowtime;
|
||||
twod->Begin(screen->GetWidth(), screen->GetHeight());
|
||||
done = wiper->Run(1);
|
||||
if (overlaydrawer) overlaydrawer();
|
||||
twod->End();
|
||||
screen->Update();
|
||||
twod->OnFrameDone();
|
||||
|
||||
diff_frac = (nowtime - wipestart) * 40. / 1000.; // Using 35 here feels too slow.
|
||||
wipestart = nowtime;
|
||||
twod->Begin(screen->GetWidth(), screen->GetHeight());
|
||||
done = wiper->RunInterpolated(diff_frac);
|
||||
if (overlaydrawer) overlaydrawer();
|
||||
twod->End();
|
||||
screen->Update();
|
||||
twod->OnFrameDone();
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
I_WaitVBL(2);
|
||||
nowtime = I_msTime();
|
||||
diff = (nowtime - wipestart) * 40 / 1000; // Using 35 here feels too slow.
|
||||
} while (diff < 1);
|
||||
wipestart = nowtime;
|
||||
twod->Begin(screen->GetWidth(), screen->GetHeight());
|
||||
done = wiper->Run(1);
|
||||
if (overlaydrawer) overlaydrawer();
|
||||
twod->End();
|
||||
screen->Update();
|
||||
twod->OnFrameDone();
|
||||
}
|
||||
} while (!done);
|
||||
delete wiper;
|
||||
I_FreezeTime(false);
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ CUSTOM_CVAR(Int, snd_samplerate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
|||
CVAR(Int, snd_buffersize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, snd_hrtf, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
CVAR(Float, snd_footstepvolume, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
|
||||
|
||||
#if !defined(NO_OPENAL)
|
||||
#define DEF_BACKEND "openal"
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
|
|
|
|||
|
|
@ -879,10 +879,18 @@ void FFunctionBuildList::Build()
|
|||
{
|
||||
if (!item.Code->CheckReturn())
|
||||
{
|
||||
auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
|
||||
newcmpd->Add(item.Code);
|
||||
newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
|
||||
item.Code = newcmpd->Resolve(ctx);
|
||||
if (ctx.ReturnProto == nullptr || !ctx.ReturnProto->ReturnTypes.Size())
|
||||
{
|
||||
auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
|
||||
newcmpd->Add(item.Code);
|
||||
newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
|
||||
item.Code = newcmpd->Resolve(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Code->ScriptPosition.Message(MSG_ERROR, "Missing return statement in %s", item.PrintableName.GetChars());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
item.Proto = ctx.ReturnProto;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include "c_cvars.h"
|
||||
#include "c_bind.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "m_misc.h"
|
||||
|
||||
#include "menu.h"
|
||||
#include "vm.h"
|
||||
|
|
@ -1032,6 +1033,17 @@ DEFINE_ACTION_FUNCTION(_CVar, FindCVar)
|
|||
ACTION_RETURN_POINTER(FindCVar(name.GetChars(), nullptr));
|
||||
}
|
||||
|
||||
static int SaveConfig()
|
||||
{
|
||||
return M_SaveDefaults(nullptr);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_CVar, SaveConfig, SaveConfig)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
ACTION_RETURN_INT(M_SaveDefaults(nullptr));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@
|
|||
static FRandom pr_dmspawn ("DMSpawn");
|
||||
static FRandom pr_pspawn ("PlayerSpawn");
|
||||
|
||||
extern int startpos, laststartpos;
|
||||
|
||||
bool WriteZip(const char* filename, const FileSys::FCompressedBuffer* content, size_t contentcount);
|
||||
bool G_CheckDemoStatus (void);
|
||||
void G_ReadDemoTiccmd (ticcmd_t *cmd, int player);
|
||||
|
|
@ -2146,7 +2148,9 @@ void G_DoLoadGame ()
|
|||
|
||||
arc("ticrate", time[0])
|
||||
("leveltime", time[1])
|
||||
("globalfreeze", globalfreeze);
|
||||
("globalfreeze", globalfreeze)
|
||||
("startpos", startpos)
|
||||
("laststartpos", laststartpos);
|
||||
// dearchive all the modifications
|
||||
level.time = Scale(time[1], TICRATE, time[0]);
|
||||
|
||||
|
|
@ -2434,6 +2438,10 @@ void G_DoSaveGame (bool okForQuicksave, bool forceQuicksave, FString filename, c
|
|||
savegameglobals("leveltime", level.time);
|
||||
}
|
||||
|
||||
savegameglobals("globalfreeze", globalfreeze)
|
||||
("startpos", startpos)
|
||||
("laststartpos", laststartpos);
|
||||
|
||||
STAT_Serialize(savegameglobals);
|
||||
FRandom::StaticWriteRNGState(savegameglobals);
|
||||
P_WriteACSDefereds(savegameglobals);
|
||||
|
|
|
|||
|
|
@ -111,6 +111,8 @@ EXTERN_CVAR (Int, disableautosave)
|
|||
EXTERN_CVAR (String, playerclass)
|
||||
|
||||
extern uint8_t globalfreeze, globalchangefreeze;
|
||||
int startpos = 0; // [RH] Support for multiple starts per level
|
||||
int laststartpos = 0;
|
||||
|
||||
#define SNAP_ID MAKE_ID('s','n','A','p')
|
||||
#define DSNP_ID MAKE_ID('d','s','N','p')
|
||||
|
|
@ -652,7 +654,9 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
|
|||
gamestate = GS_LEVEL;
|
||||
}
|
||||
|
||||
G_DoLoadLevel (mapname, 0, false, !savegamerestore);
|
||||
if (!savegamerestore)
|
||||
startpos = laststartpos = 0;
|
||||
G_DoLoadLevel (mapname, startpos, false, !savegamerestore);
|
||||
|
||||
if (!savegamerestore && (gameinfo.gametype == GAME_Strife || (SBarInfoScript[SCRIPT_CUSTOM] != nullptr && SBarInfoScript[SCRIPT_CUSTOM]->GetGameType() == GAME_Strife)))
|
||||
{
|
||||
|
|
@ -669,7 +673,6 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
|
|||
// G_DoCompleted
|
||||
//
|
||||
static FString nextlevel;
|
||||
static int startpos; // [RH] Support for multiple starts per level
|
||||
extern int NoWipe; // [RH] Don't wipe when travelling in hubs
|
||||
static int changeflags;
|
||||
static bool unloading;
|
||||
|
|
@ -1100,7 +1103,6 @@ void G_DoCompleted (void)
|
|||
if (gamestate == GS_TITLELEVEL)
|
||||
{
|
||||
G_DoLoadLevel (nextlevel, startpos, false, false);
|
||||
startpos = 0;
|
||||
viewactive = true;
|
||||
return;
|
||||
}
|
||||
|
|
@ -1373,7 +1375,6 @@ void G_DoLoadLevel(const FString &nextmapname, int position, bool autosave, bool
|
|||
void FLevelLocals::DoLoadLevel(const FString &nextmapname, int position, bool autosave, bool newGame)
|
||||
{
|
||||
MapName = nextmapname;
|
||||
static int lastposition = 0;
|
||||
int i;
|
||||
|
||||
if (NextSkill >= 0)
|
||||
|
|
@ -1385,9 +1386,9 @@ void FLevelLocals::DoLoadLevel(const FString &nextmapname, int position, bool au
|
|||
}
|
||||
|
||||
if (position == -1)
|
||||
position = lastposition;
|
||||
position = laststartpos;
|
||||
else
|
||||
lastposition = position;
|
||||
laststartpos = position;
|
||||
|
||||
Init();
|
||||
StatusBar->DetachAllMessages ();
|
||||
|
|
@ -1575,7 +1576,6 @@ void G_DoWorldDone (void)
|
|||
}
|
||||
primaryLevel->StartTravel ();
|
||||
G_DoLoadLevel (nextlevel, startpos, true, false);
|
||||
startpos = 0;
|
||||
gameaction = ga_nothing;
|
||||
viewactive = true;
|
||||
}
|
||||
|
|
@ -2400,6 +2400,24 @@ void FLevelLocals::ApplyCompatibility2()
|
|||
i_compatflags2 = GetCompatibility2(compatflags2) | ii_compatflags2;
|
||||
}
|
||||
|
||||
AActor* FLevelLocals::SelectActorFromTID(int tid, size_t index, AActor* defactor)
|
||||
{
|
||||
if (tid == 0)
|
||||
return defactor;
|
||||
|
||||
AActor* actor = nullptr;
|
||||
size_t cur = 0u;
|
||||
auto it = GetActorIterator(tid);
|
||||
while ((actor = it.Next()) != nullptr)
|
||||
{
|
||||
if (cur == index)
|
||||
return actor;
|
||||
++cur;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
// IsPointInMap
|
||||
//
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ struct FLevelLocals
|
|||
int GetCompatibility2(int mask);
|
||||
void ApplyCompatibility();
|
||||
void ApplyCompatibility2();
|
||||
AActor* SelectActorFromTID(int tid, size_t index, AActor* defactor);
|
||||
|
||||
void Init();
|
||||
|
||||
|
|
@ -166,8 +167,8 @@ private:
|
|||
|
||||
void SerializePlayers(FSerializer &arc, bool skipload);
|
||||
void CopyPlayer(player_t *dst, player_t *src, const char *name);
|
||||
void ReadOnePlayer(FSerializer &arc, bool skipload);
|
||||
void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload);
|
||||
void ReadOnePlayer(FSerializer &arc, bool fromHub);
|
||||
void ReadMultiplePlayers(FSerializer &arc, int numPlayers, bool fromHub);
|
||||
void SerializeSounds(FSerializer &arc);
|
||||
void PlayerSpawnPickClass (int playernum);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,13 +77,14 @@ enum ETerrainKeywords
|
|||
TR_DAMAGETIMEMASK,
|
||||
TR_FOOTCLIP,
|
||||
TR_STEPVOLUME,
|
||||
TR_WALKINGSTEPTIME,
|
||||
TR_RUNNINGSTEPTIME,
|
||||
TR_WALKSTEPTICS,
|
||||
TR_RUNSTEPTICS,
|
||||
TR_LEFTSTEPSOUNDS,
|
||||
TR_RIGHTSTEPSOUNDS,
|
||||
TR_LIQUID,
|
||||
TR_FRICTION,
|
||||
TR_ALLOWPROTECTION
|
||||
TR_ALLOWPROTECTION,
|
||||
TR_STEPSOUNDS
|
||||
};
|
||||
|
||||
enum EGenericType
|
||||
|
|
@ -95,7 +96,6 @@ enum EGenericType
|
|||
GEN_Splash,
|
||||
GEN_Float,
|
||||
GEN_Double,
|
||||
GEN_Time,
|
||||
GEN_Bool,
|
||||
GEN_Int,
|
||||
GEN_Custom,
|
||||
|
|
@ -179,14 +179,17 @@ static const char *TerrainKeywords[] =
|
|||
"damagetimemask",
|
||||
"footclip",
|
||||
"stepvolume",
|
||||
"walkingsteptime",
|
||||
"runningsteptime",
|
||||
"walksteptics",
|
||||
"runsteptics",
|
||||
"leftstepsounds",
|
||||
"rightstepsounds",
|
||||
"liquid",
|
||||
"friction",
|
||||
"allowprotection",
|
||||
"damageonland",
|
||||
"stepsounds",
|
||||
"stepdistance",
|
||||
"stepdistanceminvel",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -215,14 +218,17 @@ static FGenericParse TerrainParser[] =
|
|||
{ GEN_Int, {myoffsetof(FTerrainDef, DamageTimeMask)} },
|
||||
{ GEN_Double, {myoffsetof(FTerrainDef, FootClip)} },
|
||||
{ GEN_Float, {myoffsetof(FTerrainDef, StepVolume)} },
|
||||
{ GEN_Time, {myoffsetof(FTerrainDef, WalkStepTics)} },
|
||||
{ GEN_Time, {myoffsetof(FTerrainDef, RunStepTics)} },
|
||||
{ GEN_Int, {myoffsetof(FTerrainDef, WalkStepTics)} },
|
||||
{ GEN_Int, {myoffsetof(FTerrainDef, RunStepTics)} },
|
||||
{ GEN_Sound, {myoffsetof(FTerrainDef, LeftStepSound)} },
|
||||
{ GEN_Sound, {myoffsetof(FTerrainDef, RightStepSound)} },
|
||||
{ GEN_Bool, {myoffsetof(FTerrainDef, IsLiquid)} },
|
||||
{ GEN_Custom, {(size_t)ParseFriction} },
|
||||
{ GEN_Bool, {myoffsetof(FTerrainDef, AllowProtection)} },
|
||||
{ GEN_Bool, {myoffsetof(FTerrainDef, DamageOnLand)} },
|
||||
{ GEN_Sound, {myoffsetof(FTerrainDef, StepSound)} },
|
||||
{ GEN_Double, {myoffsetof(FTerrainDef, StepDistance)} },
|
||||
{ GEN_Double, {myoffsetof(FTerrainDef, StepDistanceMinVel)} },
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -597,11 +603,6 @@ static void GenericParse (FScanner &sc, FGenericParse *parser, const char **keyw
|
|||
SET_FIELD(double, sc.Float);
|
||||
break;
|
||||
|
||||
case GEN_Time:
|
||||
sc.MustGetFloat ();
|
||||
SET_FIELD (int, (int)(sc.Float * TICRATE));
|
||||
break;
|
||||
|
||||
case GEN_Bool:
|
||||
SET_FIELD (bool, true);
|
||||
break;
|
||||
|
|
@ -747,3 +748,6 @@ DEFINE_FIELD(FTerrainDef, AllowProtection)
|
|||
DEFINE_FIELD(FTerrainDef, DamageOnLand)
|
||||
DEFINE_FIELD(FTerrainDef, Friction)
|
||||
DEFINE_FIELD(FTerrainDef, MoveFactor)
|
||||
DEFINE_FIELD(FTerrainDef, StepSound)
|
||||
DEFINE_FIELD(FTerrainDef, StepDistance)
|
||||
DEFINE_FIELD(FTerrainDef, StepDistanceMinVel)
|
||||
|
|
@ -118,6 +118,9 @@ struct FTerrainDef
|
|||
bool DamageOnLand;
|
||||
double Friction;
|
||||
double MoveFactor;
|
||||
FSoundID StepSound;
|
||||
double StepDistance;
|
||||
double StepDistanceMinVel;
|
||||
};
|
||||
|
||||
extern TArray<FSplashDef> Splashes;
|
||||
|
|
|
|||
|
|
@ -504,6 +504,8 @@ enum
|
|||
SECMF_OVERLAPPING = 512, // floor and ceiling overlap and require special renderer action.
|
||||
SECMF_NOSKYWALLS = 1024, // Do not draw "sky walls"
|
||||
SECMF_LIFT = 2048, // For MBF monster AI
|
||||
SECMF_HURTMONSTERS = 4096, // Monsters in this sector are hurt like players.
|
||||
SECMF_HARMINAIR = 8192, // Actors in this sector are also hurt mid-air.
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
@ -1027,6 +1029,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);
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1979,6 +1979,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;
|
||||
|
|
|
|||
|
|
@ -810,6 +810,8 @@ xx(damageinterval)
|
|||
xx(leakiness)
|
||||
xx(damageterraineffect)
|
||||
xx(damagehazard)
|
||||
xx(hurtmonsters)
|
||||
xx(harminair)
|
||||
xx(floorterrain)
|
||||
xx(ceilingterrain)
|
||||
xx(floor_reflect)
|
||||
|
|
|
|||
215
src/p_saveg.cpp
215
src/p_saveg.cpp
|
|
@ -627,8 +627,8 @@ void FLevelLocals::SerializePlayers(FSerializer &arc, bool skipload)
|
|||
{
|
||||
if (arc.BeginObject(nullptr))
|
||||
{
|
||||
const char *n = Players[i]->userinfo.GetName();
|
||||
arc.StringPtr("playername", n);
|
||||
FString name = Players[i]->userinfo.GetName();
|
||||
arc("playername", name);
|
||||
Players[i]->Serialize(arc);
|
||||
arc.EndObject();
|
||||
}
|
||||
|
|
@ -651,7 +651,7 @@ void FLevelLocals::SerializePlayers(FSerializer &arc, bool skipload)
|
|||
}
|
||||
else
|
||||
{
|
||||
ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload);
|
||||
ReadMultiplePlayers(arc, numPlayers, skipload);
|
||||
}
|
||||
arc.EndArray();
|
||||
|
||||
|
|
@ -680,53 +680,41 @@ void FLevelLocals::SerializePlayers(FSerializer &arc, bool skipload)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void FLevelLocals::ReadOnePlayer(FSerializer &arc, bool skipload)
|
||||
void FLevelLocals::ReadOnePlayer(FSerializer &arc, bool fromHub)
|
||||
{
|
||||
int i;
|
||||
const char *name = NULL;
|
||||
bool didIt = false;
|
||||
if (!arc.BeginObject(nullptr))
|
||||
return;
|
||||
|
||||
if (arc.BeginObject(nullptr))
|
||||
FString name = {};
|
||||
arc("playername", name);
|
||||
player_t temp = {};
|
||||
temp.Serialize(arc);
|
||||
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
arc.StringPtr("playername", name);
|
||||
if (!PlayerInGame(i))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
if (!fromHub)
|
||||
{
|
||||
if (playeringame[i])
|
||||
{
|
||||
if (!didIt)
|
||||
{
|
||||
didIt = true;
|
||||
player_t playerTemp;
|
||||
playerTemp.Serialize(arc);
|
||||
if (!skipload)
|
||||
{
|
||||
// This temp player has undefined pitch limits, so set them to something
|
||||
// that should leave the pitch stored in the savegame intact when
|
||||
// rendering. The real pitch limits will be set by P_SerializePlayers()
|
||||
// via a net command, but that won't be processed in time for a screen
|
||||
// wipe, so we need something here.
|
||||
playerTemp.MaxPitch = playerTemp.MinPitch = playerTemp.mo->Angles.Pitch;
|
||||
CopyPlayer(Players[i], &playerTemp, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need the player actor, so that G_FinishTravel can destroy it later.
|
||||
Players[i]->mo = playerTemp.mo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Players[i]->mo != NULL)
|
||||
{
|
||||
Players[i]->mo->Destroy();
|
||||
Players[i]->mo = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This temp player has undefined pitch limits, so set them to something
|
||||
// that should leave the pitch stored in the savegame intact when
|
||||
// rendering. The real pitch limits will be set by P_SerializePlayers()
|
||||
// via a net command, but that won't be processed in time for a screen
|
||||
// wipe, so we need something here.
|
||||
temp.MaxPitch = temp.MinPitch = temp.mo->Angles.Pitch;
|
||||
CopyPlayer(Players[i], &temp, name.GetChars());
|
||||
}
|
||||
arc.EndObject();
|
||||
else
|
||||
{
|
||||
// we need the player actor, so that G_FinishTravel can destroy it later.
|
||||
Players[i]->mo = temp.mo;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
arc.EndObject();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -735,109 +723,96 @@ void FLevelLocals::ReadOnePlayer(FSerializer &arc, bool skipload)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void FLevelLocals::ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload)
|
||||
struct NetworkPlayerInfo
|
||||
{
|
||||
// For two or more players, read each player into a temporary array.
|
||||
int i, j;
|
||||
const char **nametemp = new const char *[numPlayers];
|
||||
player_t *playertemp = new player_t[numPlayers];
|
||||
uint8_t *tempPlayerUsed = new uint8_t[numPlayers];
|
||||
uint8_t playerUsed[MAXPLAYERS];
|
||||
FString Name = {};
|
||||
player_t Info = {};
|
||||
bool bUsed = false;
|
||||
};
|
||||
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
void FLevelLocals::ReadMultiplePlayers(FSerializer &arc, int numPlayers, bool fromHub)
|
||||
{
|
||||
TArray<NetworkPlayerInfo> tempPlayers = {};
|
||||
tempPlayers.Reserve(numPlayers);
|
||||
TArray<bool> assignedPlayers = {};
|
||||
assignedPlayers.Reserve(MAXPLAYERS);
|
||||
|
||||
// Read all the save game players into a temporary array
|
||||
for (auto& p : tempPlayers)
|
||||
{
|
||||
nametemp[i] = NULL;
|
||||
if (arc.BeginObject(nullptr))
|
||||
{
|
||||
arc.StringPtr("playername", nametemp[i]);
|
||||
playertemp[i].Serialize(arc);
|
||||
arc("playername", p.Name);
|
||||
p.Info.Serialize(arc);
|
||||
arc.EndObject();
|
||||
}
|
||||
tempPlayerUsed[i] = 0;
|
||||
}
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
playerUsed[i] = playeringame[i] ? 0 : 2;
|
||||
}
|
||||
|
||||
if (!skipload)
|
||||
// Now try to match players from the savegame with players present
|
||||
// based on their names. If two players in the savegame have the
|
||||
// same name, then they are assigned to players in the current game
|
||||
// on a first-come, first-served basis.
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
// Now try to match players from the savegame with players present
|
||||
// based on their names. If two players in the savegame have the
|
||||
// same name, then they are assigned to players in the current game
|
||||
// on a first-come, first-served basis.
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
{
|
||||
for (j = 0; j < MAXPLAYERS; ++j)
|
||||
{
|
||||
if (playerUsed[j] == 0 && stricmp(players[j].userinfo.GetName(), nametemp[i]) == 0)
|
||||
{ // Found a match, so copy our temp player to the real player
|
||||
Printf("Found player %d (%s) at %d\n", i, nametemp[i], j);
|
||||
CopyPlayer(Players[j], &playertemp[i], nametemp[i]);
|
||||
playerUsed[j] = 1;
|
||||
tempPlayerUsed[i] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!PlayerInGame(i))
|
||||
continue;
|
||||
|
||||
// Any players that didn't have matching names are assigned to existing
|
||||
// players on a first-come, first-served basis.
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
for (auto& p : tempPlayers)
|
||||
{
|
||||
if (tempPlayerUsed[i] == 0)
|
||||
if (!p.bUsed && !p.Name.Compare(Players[i]->userinfo.GetName()))
|
||||
{
|
||||
for (j = 0; j < MAXPLAYERS; ++j)
|
||||
// Found a match, so copy our temp player to the real player
|
||||
if (!fromHub)
|
||||
{
|
||||
if (playerUsed[j] == 0)
|
||||
{
|
||||
Printf("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.GetName());
|
||||
CopyPlayer(&players[j], &playertemp[i], nametemp[i]);
|
||||
playerUsed[j] = 1;
|
||||
tempPlayerUsed[i] = 1;
|
||||
break;
|
||||
}
|
||||
Printf("Found %s's (%d) data\n", Players[i]->userinfo.GetName(), i);
|
||||
CopyPlayer(Players[i], &p.Info, p.Name.GetChars());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure any extra players don't have actors spawned yet. Happens if the players
|
||||
// present now got the same slots as they had in the save, but there are not as many
|
||||
// as there were in the save.
|
||||
for (j = 0; j < MAXPLAYERS; ++j)
|
||||
{
|
||||
if (playerUsed[j] == 0)
|
||||
{
|
||||
if (players[j].mo != NULL)
|
||||
else
|
||||
{
|
||||
players[j].mo->Destroy();
|
||||
players[j].mo = NULL;
|
||||
Players[i]->mo = p.Info.mo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any temp players that were not used. Happens if there are fewer players
|
||||
// than there were in the save, and they got shuffled.
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
{
|
||||
if (tempPlayerUsed[i] == 0)
|
||||
{
|
||||
playertemp[i].mo->Destroy();
|
||||
playertemp[i].mo = NULL;
|
||||
p.bUsed = true;
|
||||
assignedPlayers[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Any players that didn't have matching names are assigned to existing
|
||||
// players on a first-come, first-served basis.
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
for (i = 0; i < numPlayers; ++i)
|
||||
if (!PlayerInGame(i) || assignedPlayers[i])
|
||||
continue;
|
||||
|
||||
for (auto& p : tempPlayers)
|
||||
{
|
||||
players[i].mo = playertemp[i].mo;
|
||||
if (!p.bUsed)
|
||||
{
|
||||
if (!fromHub)
|
||||
{
|
||||
Printf("Assigned %s (%d) to %s's data\n", Players[i]->userinfo.GetName(), i, p.Name.GetChars());
|
||||
CopyPlayer(Players[i], &p.Info, p.Name.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
Players[i]->mo = p.Info.mo;
|
||||
}
|
||||
|
||||
p.bUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] tempPlayerUsed;
|
||||
delete[] playertemp;
|
||||
delete[] nametemp;
|
||||
// Remove any temp players that were not used. Happens if there are now
|
||||
// less players in the game than there were in the save
|
||||
for (auto& p : tempPlayers)
|
||||
{
|
||||
if (!p.bUsed)
|
||||
p.Info.mo->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
|||
|
|
@ -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 ---
|
||||
|
|
|
|||
|
|
@ -198,39 +198,40 @@ void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flags, int
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// P_PlayerOnSpecial3DFloor
|
||||
// Checks to see if a player is standing on or is inside a 3D floor (water)
|
||||
// P_ActorOnSpecial3DFloor
|
||||
// Checks to see if an actor is standing on or is inside a 3D floor (water)
|
||||
// and applies any specials..
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void P_PlayerOnSpecial3DFloor(player_t* player)
|
||||
void P_ActorOnSpecial3DFloor(AActor* victim)
|
||||
{
|
||||
for(auto rover : player->mo->Sector->e->XFloor.ffloors)
|
||||
for(auto rover : victim->Sector->e->XFloor.ffloors)
|
||||
{
|
||||
if (!(rover->flags & FF_EXISTS)) continue;
|
||||
if (rover->flags & FF_FIX) continue;
|
||||
|
||||
if (!checkForSpecialSector(victim, rover->model)) continue;
|
||||
|
||||
// Check the 3D floor's type...
|
||||
if(rover->flags & FF_SOLID)
|
||||
{
|
||||
// Player must be on top of the floor to be affected...
|
||||
if(player->mo->Z() != rover->top.plane->ZatPoint(player->mo)) continue;
|
||||
if (victim->Z() != rover->top.plane->ZatPoint(victim)) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Water and DEATH FOG!!! heh
|
||||
if ((rover->flags & FF_NODAMAGE) ||
|
||||
player->mo->Z() > rover->top.plane->ZatPoint(player->mo) ||
|
||||
player->mo->Top() < rover->bottom.plane->ZatPoint(player->mo))
|
||||
victim->Z() > rover->top.plane->ZatPoint(victim) ||
|
||||
victim->Top() < rover->bottom.plane->ZatPoint(victim))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply sector specials
|
||||
P_PlayerInSpecialSector(player, rover->model);
|
||||
P_ActorInSpecialSector(victim, rover->model,rover);
|
||||
|
||||
// Apply flat specials (using the ceiling!)
|
||||
P_PlayerOnSpecialFlat(player, rover->model->GetTerrain(rover->top.isceiling));
|
||||
P_ActorOnSpecialFlat(victim, rover->model->GetTerrain(rover->top.isceiling));
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,8 +113,7 @@ struct lightlist_t
|
|||
|
||||
|
||||
|
||||
class player_t;
|
||||
void P_PlayerOnSpecial3DFloor(player_t* player);
|
||||
void P_ActorOnSpecial3DFloor(AActor* victim);
|
||||
|
||||
bool P_CheckFor3DFloorHit(AActor * mo, double z, bool trigger);
|
||||
bool P_CheckFor3DCeilingHit(AActor * mo, double z, bool trigger);
|
||||
|
|
|
|||
|
|
@ -4806,6 +4806,8 @@ enum EACSFunctions
|
|||
ACSF_GetSectorHealth,
|
||||
ACSF_GetLineHealth,
|
||||
ACSF_SetSubtitleNumber,
|
||||
ACSF_GetNetID,
|
||||
ACSF_SetActivatorByNetID,
|
||||
|
||||
// Eternity's
|
||||
ACSF_GetLineX = 300,
|
||||
|
|
@ -5380,6 +5382,13 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args, int &
|
|||
actor = Level->SingleActorFromTID(args[0], activator);
|
||||
return actor != NULL? DoubleToACS(actor->Vel.Z) : 0;
|
||||
|
||||
case ACSF_GetNetID:
|
||||
MIN_ARG_COUNT(2);
|
||||
actor = Level->SelectActorFromTID(args[0], args[1], activator);
|
||||
if (argCount > 2)
|
||||
actor = COPY_AAPTREX(Level, actor, args[2]);
|
||||
return actor != nullptr ? actor->GetNetworkID() : NetworkEntityManager::WorldNetID;
|
||||
|
||||
case ACSF_SetPointer:
|
||||
MIN_ARG_COUNT(2);
|
||||
if (activator)
|
||||
|
|
@ -5431,6 +5440,18 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args, int &
|
|||
}
|
||||
return 0;
|
||||
|
||||
case ACSF_SetActivatorByNetID:
|
||||
MIN_ARG_COUNT(1);
|
||||
actor = dyn_cast<AActor>(NetworkEntityManager::GetNetworkEntity(args[0]));
|
||||
if (argCount > 1)
|
||||
actor = COPY_AAPTREX(Level, actor, args[1]);
|
||||
if (actor != nullptr)
|
||||
{
|
||||
activator = actor;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case ACSF_GetActorViewHeight:
|
||||
MIN_ARG_COUNT(1);
|
||||
actor = Level->SingleActorFromTID(args[0], activator);
|
||||
|
|
|
|||
|
|
@ -106,50 +106,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 +305,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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -464,6 +464,12 @@ static int P_IsUnderDamage(AActor* actor)
|
|||
dir |= cl->getDirection();
|
||||
}
|
||||
// Q: consider crushing 3D floors too?
|
||||
// [inkoalawetrust] Check for sectors that can harm the actor.
|
||||
if (!(actor->flags9 & MF9_NOSECTORDAMAGE) && seclist->m_sector->damageamount > 0)
|
||||
{
|
||||
if (seclist->m_sector->MoreFlags & SECMF_HARMINAIR || actor->isAtZ(seclist->m_sector->LowestFloorAt(actor)) || actor->waterlevel)
|
||||
return (actor->player || (actor->player == nullptr && seclist->m_sector->MoreFlags & SECMF_HURTMONSTERS)) ? -1 : 0;
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
|
@ -1303,6 +1309,161 @@ int P_IsVisible(AActor *lookee, AActor *other, INTBOOL allaround, FLookExParams
|
|||
return P_CheckSight(lookee, other, SF_SEEPASTSHOOTABLELINES);
|
||||
}
|
||||
|
||||
bool isTargetablePlayer(AActor *actor, player_t *player, INTBOOL allaround, void* lookparams)
|
||||
{
|
||||
FLookExParams* params = (FLookExParams*)lookparams;
|
||||
|
||||
if (!(player->mo->flags & MF_SHOOTABLE))
|
||||
return false; // not shootable (observer or dead)
|
||||
|
||||
if (actor->IsFriend(player->mo))
|
||||
return false; // same +MF_FRIENDLY, ignore
|
||||
|
||||
if (player->cheats & CF_NOTARGET)
|
||||
return false; // no target
|
||||
|
||||
if (player->health <= 0)
|
||||
return false; // dead
|
||||
|
||||
if (!P_IsVisible(actor, player->mo, allaround, params))
|
||||
return false; // out of sight
|
||||
|
||||
// [RC] Well, let's let special monsters with this flag active be able to see
|
||||
// the player then, eh?
|
||||
if (!(actor->flags6 & MF6_SEEINVISIBLE))
|
||||
{
|
||||
if ((player->mo->flags & MF_SHADOW && !(actor->Level->i_compatflags & COMPATF_INVISIBILITY)) ||
|
||||
player->mo->flags3 & MF3_GHOST)
|
||||
{
|
||||
if (player->mo->Distance2D(actor) > 128 && player->mo->Vel.XY().LengthSquared() < 5 * 5)
|
||||
{ // Player is sneaking - can't detect
|
||||
return false;
|
||||
}
|
||||
if (pr_lookforplayers() < 225)
|
||||
{ // Player isn't sneaking, but still didn't detect
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidEnemyInBlock(AActor* lookee, AActor* other, void* lookparams)
|
||||
{
|
||||
FLookExParams* params = (FLookExParams*)lookparams;
|
||||
|
||||
if (!(other->flags & MF_SHOOTABLE))
|
||||
return false; // not shootable (observer or dead)
|
||||
|
||||
if (other == lookee)
|
||||
return false; // is self
|
||||
|
||||
if (other->health <= 0)
|
||||
return false; // dead
|
||||
|
||||
if (other->flags2 & MF2_DORMANT)
|
||||
return false; // don't target dormant things
|
||||
|
||||
if (!(other->flags3 & MF3_ISMONSTER))
|
||||
return false; // don't target it if it isn't a monster (could be a barrel)
|
||||
|
||||
if (other->flags7 & MF7_NEVERTARGET)
|
||||
return false;
|
||||
|
||||
bool keepChecking = false;
|
||||
if (lookee->flags & MF_FRIENDLY)
|
||||
{
|
||||
if (other->flags & MF_FRIENDLY)
|
||||
{
|
||||
if (!lookee->IsFriend(other))
|
||||
{
|
||||
// This is somebody else's friend, so go after it
|
||||
keepChecking = true;
|
||||
}
|
||||
else if (other->target != NULL && !(other->target->flags & MF_FRIENDLY))
|
||||
{
|
||||
other = other->target;
|
||||
if (!(other->flags & MF_SHOOTABLE) ||
|
||||
other->health <= 0 ||
|
||||
(other->flags2 & MF2_DORMANT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
keepChecking = true;
|
||||
}
|
||||
}
|
||||
else if (lookee->flags8 & MF8_SEEFRIENDLYMONSTERS && other->flags & MF_FRIENDLY)
|
||||
{
|
||||
keepChecking = true;
|
||||
}
|
||||
|
||||
// [MBF] If the monster is already engaged in a one-on-one attack
|
||||
// with a healthy friend, don't attack around 60% the time.
|
||||
|
||||
// [GrafZahl] This prevents friendlies from attacking all the same
|
||||
// target.
|
||||
|
||||
if (keepChecking)
|
||||
{
|
||||
AActor* targ = other->target;
|
||||
if (targ && targ->target == other && pr_skiptarget() > 100 && lookee->IsFriend(targ) &&
|
||||
targ->health * 2 >= targ->SpawnHealth())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// [KS] Hey, shouldn't there be a check for MF3_NOSIGHTCHECK here?
|
||||
|
||||
if (!keepChecking || !P_IsVisible(lookee, other, true, params))
|
||||
return false; // out of sight
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// LookForEnemiesEx
|
||||
//
|
||||
// [inkoalawetrust] Return a script array of all valid enemies of the caller
|
||||
// in range. For ZScript.
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, LookForEnemiesEx)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_OUTPOINTER(targets,TArray<AActor*>);
|
||||
PARAM_FLOAT(range);
|
||||
PARAM_BOOL(noPlayers);
|
||||
PARAM_BOOL(allaround);
|
||||
PARAM_POINTER(params, FLookExParams);
|
||||
|
||||
if (targets == nullptr)
|
||||
ThrowAbortException(X_WRITE_NIL,"No targets array passed");
|
||||
|
||||
if (range == -1)
|
||||
range = self->friendlyseeblocks * FBlockmap::MAPBLOCKUNITS;
|
||||
|
||||
FPortalGroupArray check(FPortalGroupArray::PGA_Full3d);
|
||||
FMultiBlockThingsIterator it(check, self, range, false);
|
||||
FMultiBlockThingsIterator::CheckResult cres;
|
||||
|
||||
while (it.Next(&cres))
|
||||
{
|
||||
if (cres.thing->player == nullptr && ValidEnemyInBlock(cres.thing, self, params) ||
|
||||
!noPlayers && cres.thing->player && isTargetablePlayer(self, cres.thing->player, allaround, params))
|
||||
targets->Push(cres.thing);
|
||||
}
|
||||
|
||||
ACTION_RETURN_INT(targets->Size());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// FUNC P_LookForMonsters
|
||||
|
|
@ -1546,80 +1707,10 @@ AActor *LookForEnemiesInBlock (AActor *lookee, int index, void *extparam)
|
|||
|
||||
for (block = lookee->Level->blockmap.blocklinks[index]; block != NULL; block = block->NextActor)
|
||||
{
|
||||
link = block->Me;
|
||||
|
||||
if (!(link->flags & MF_SHOOTABLE))
|
||||
continue; // not shootable (observer or dead)
|
||||
|
||||
if (link == lookee)
|
||||
if (!ValidEnemyInBlock(lookee, block->Me, params))
|
||||
continue;
|
||||
|
||||
if (link->health <= 0)
|
||||
continue; // dead
|
||||
|
||||
if (link->flags2 & MF2_DORMANT)
|
||||
continue; // don't target dormant things
|
||||
|
||||
if (!(link->flags3 & MF3_ISMONSTER))
|
||||
continue; // don't target it if it isn't a monster (could be a barrel)
|
||||
|
||||
if (link->flags7 & MF7_NEVERTARGET)
|
||||
continue;
|
||||
|
||||
other = NULL;
|
||||
if (lookee->flags & MF_FRIENDLY)
|
||||
{
|
||||
if (link->flags & MF_FRIENDLY)
|
||||
{
|
||||
if (!lookee->IsFriend(link))
|
||||
{
|
||||
// This is somebody else's friend, so go after it
|
||||
other = link;
|
||||
}
|
||||
else if (link->target != NULL && !(link->target->flags & MF_FRIENDLY))
|
||||
{
|
||||
other = link->target;
|
||||
if (!(other->flags & MF_SHOOTABLE) ||
|
||||
other->health <= 0 ||
|
||||
(other->flags2 & MF2_DORMANT))
|
||||
{
|
||||
other = NULL;;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
other = link;
|
||||
}
|
||||
}
|
||||
else if (lookee->flags8 & MF8_SEEFRIENDLYMONSTERS && link->flags & MF_FRIENDLY)
|
||||
{
|
||||
other = link;
|
||||
}
|
||||
|
||||
// [MBF] If the monster is already engaged in a one-on-one attack
|
||||
// with a healthy friend, don't attack around 60% the time.
|
||||
|
||||
// [GrafZahl] This prevents friendlies from attacking all the same
|
||||
// target.
|
||||
|
||||
if (other)
|
||||
{
|
||||
AActor *targ = other->target;
|
||||
if (targ && targ->target == other && pr_skiptarget() > 100 && lookee->IsFriend (targ) &&
|
||||
targ->health*2 >= targ->SpawnHealth())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// [KS] Hey, shouldn't there be a check for MF3_NOSIGHTCHECK here?
|
||||
|
||||
if (other == NULL || !P_IsVisible (lookee, other, true, params))
|
||||
continue; // out of sight
|
||||
|
||||
|
||||
return other;
|
||||
return block->Me;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1819,38 +1910,8 @@ int P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params)
|
|||
|
||||
player = actor->Level->Players[pnum];
|
||||
|
||||
if (!(player->mo->flags & MF_SHOOTABLE))
|
||||
continue; // not shootable (observer or dead)
|
||||
|
||||
if (actor->IsFriend(player->mo))
|
||||
continue; // same +MF_FRIENDLY, ignore
|
||||
|
||||
if (player->cheats & CF_NOTARGET)
|
||||
continue; // no target
|
||||
|
||||
if (player->health <= 0)
|
||||
continue; // dead
|
||||
|
||||
if (!P_IsVisible (actor, player->mo, allaround, params))
|
||||
continue; // out of sight
|
||||
|
||||
// [RC] Well, let's let special monsters with this flag active be able to see
|
||||
// the player then, eh?
|
||||
if(!(actor->flags6 & MF6_SEEINVISIBLE))
|
||||
{
|
||||
if ((player->mo->flags & MF_SHADOW && !(actor->Level->i_compatflags & COMPATF_INVISIBILITY)) ||
|
||||
player->mo->flags3 & MF3_GHOST)
|
||||
{
|
||||
if (player->mo->Distance2D (actor) > 128 && player->mo->Vel.XY().LengthSquared() < 5*5)
|
||||
{ // Player is sneaking - can't detect
|
||||
continue;
|
||||
}
|
||||
if (pr_lookforplayers() < 225)
|
||||
{ // Player isn't sneaking, but still didn't detect
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isTargetablePlayer(actor, player, allaround, params))
|
||||
continue;
|
||||
|
||||
// [RH] Need to be sure the reactiontime is 0 if the monster is
|
||||
// leaving its goal to go after a player.
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile ver
|
|||
void P_TraceBleed(int damage, FTranslatedLineTarget *t, AActor *puff); // hitscan version
|
||||
void P_TraceBleed (int damage, AActor *target); // random direction version
|
||||
bool P_HitFloor (AActor *thing);
|
||||
bool P_HitWater (AActor *thing, sector_t *sec, const DVector3 &pos, bool checkabove = false, bool alert = true, bool force = false);
|
||||
bool P_HitWater (AActor *thing, sector_t *sec, const DVector3 &pos, bool checkabove = false, bool alert = true, bool force = false, int flags = 0);
|
||||
|
||||
|
||||
struct FRailParams
|
||||
|
|
|
|||
|
|
@ -4435,6 +4435,14 @@ void AActor::Tick ()
|
|||
// must have been removed
|
||||
if (ObjectFlags & OF_EuthanizeMe) return;
|
||||
}
|
||||
//[inkoalawetrust] Genericized level damage handling that makes sector, 3D floor, and TERRAIN flat damage affect monsters and other NPCs too.
|
||||
P_ActorOnSpecial3DFloor(this); //3D floors must be checked separately to see if their control sector allows non-player damage
|
||||
if (checkForSpecialSector(this,Sector))
|
||||
{
|
||||
P_ActorInSpecialSector(this,Sector);
|
||||
if (!isAbove(Sector->floorplane.ZatPoint(this)) || waterlevel) // Actor must be touching the floor for TERRAIN flats.
|
||||
P_ActorOnSpecialFlat(this, P_GetThingFloorType(this));
|
||||
}
|
||||
|
||||
if (tics != -1)
|
||||
{
|
||||
|
|
@ -6623,7 +6631,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;
|
||||
|
|
@ -6711,13 +6725,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))
|
||||
|
|
@ -6782,7 +6796,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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@
|
|||
#include "c_console.h"
|
||||
#include "p_spec_thinkers.h"
|
||||
|
||||
static FRandom pr_playerinspecialsector ("PlayerInSpecialSector");
|
||||
static FRandom pr_actorinspecialsector ("ActorInSpecialSector");
|
||||
|
||||
EXTERN_CVAR(Bool, cl_predict_specials)
|
||||
EXTERN_CVAR(Bool, forcewater)
|
||||
|
|
@ -419,23 +419,26 @@ bool P_PredictLine(line_t *line, AActor *mo, int side, int activationType)
|
|||
}
|
||||
|
||||
//
|
||||
// P_PlayerInSpecialSector
|
||||
// P_ActorInSpecialSector
|
||||
// Called every tic frame
|
||||
// that the player origin is in a special sector
|
||||
// that the actor origin is in a special sector
|
||||
//
|
||||
void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
||||
void P_ActorInSpecialSector (AActor *victim, sector_t * sector, F3DFloor* Ffloor)
|
||||
{
|
||||
if (sector == NULL)
|
||||
{
|
||||
// Falling, not all the way down yet?
|
||||
sector = player->mo->Sector;
|
||||
if (!player->mo->isAtZ(sector->LowestFloorAt(player->mo))
|
||||
&& !player->mo->waterlevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
sector = victim->Sector;
|
||||
|
||||
// Falling, not all the way down yet?
|
||||
bool evilAir = (sector->MoreFlags & SECMF_HARMINAIR);
|
||||
bool SolidFfloor = Ffloor != nullptr && (Ffloor->flags & FF_SOLID);
|
||||
if ((!evilAir && !(Ffloor != nullptr && !SolidFfloor)) && !victim->waterlevel)
|
||||
{
|
||||
// [inkoalawetrust] Check for 3D floors differently, because non-FF_INVERTSECTOR ffloors have their floor plane as the 3D floor BOTTOM.
|
||||
double theZ = Ffloor == nullptr ? sector->LowestFloorAt(victim) : Ffloor->top.plane->ZatPoint(victim);
|
||||
if (!victim->isAtZ(theZ))
|
||||
return;
|
||||
}
|
||||
|
||||
// Has hit ground.
|
||||
|
||||
auto Level = sector->Level;
|
||||
|
|
@ -445,7 +448,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
|||
if (sector->damageinterval <= 0)
|
||||
sector->damageinterval = 32; // repair invalid damageinterval values
|
||||
|
||||
if (sector->Flags & (SECF_EXIT1 | SECF_EXIT2))
|
||||
if (victim->player && sector->Flags & (SECF_EXIT1 | SECF_EXIT2))
|
||||
{
|
||||
for (int i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
|
|
@ -464,7 +467,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
|||
// different damage types yet, so that's not happening for now.
|
||||
// [MK] account for subclasses that may have "Full" protection (i.e.: prevent leaky damage)
|
||||
int ironfeet = 0;
|
||||
for (auto i = player->mo->Inventory; i != NULL; i = i->Inventory)
|
||||
for (auto i = victim->Inventory; i != NULL; i = i->Inventory)
|
||||
{
|
||||
if (i->IsKindOf(NAME_PowerIronFeet))
|
||||
{
|
||||
|
|
@ -476,28 +479,28 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
|||
}
|
||||
}
|
||||
|
||||
if (sector->Flags & SECF_ENDGODMODE) player->cheats &= ~CF_GODMODE;
|
||||
if ((ironfeet == 0 || (ironfeet < 2 && pr_playerinspecialsector() < sector->leakydamage)))
|
||||
if (victim->player && sector->Flags & SECF_ENDGODMODE) victim->player->cheats &= ~CF_GODMODE;
|
||||
if ((ironfeet == 0 || (ironfeet < 2 && pr_actorinspecialsector() < sector->leakydamage)))
|
||||
{
|
||||
if (sector->Flags & SECF_HAZARD)
|
||||
if (victim->player && sector->Flags & SECF_HAZARD)
|
||||
{
|
||||
player->hazardcount += sector->damageamount;
|
||||
player->hazardtype = sector->damagetype;
|
||||
player->hazardinterval = sector->damageinterval;
|
||||
victim->player->hazardcount += sector->damageamount;
|
||||
victim->player->hazardtype = sector->damagetype;
|
||||
victim->player->hazardinterval = sector->damageinterval;
|
||||
}
|
||||
else if (Level->time % sector->damageinterval == 0)
|
||||
{
|
||||
if (!(player->cheats & (CF_GODMODE | CF_GODMODE2)))
|
||||
if (!(victim->player && victim->player->cheats & (CF_GODMODE | CF_GODMODE2)))
|
||||
{
|
||||
P_DamageMobj(player->mo, NULL, NULL, sector->damageamount, sector->damagetype);
|
||||
P_DamageMobj(victim, NULL, NULL, sector->damageamount, sector->damagetype);
|
||||
}
|
||||
if ((sector->Flags & SECF_ENDLEVEL) && player->health <= 10 && (!deathmatch || !(dmflags & DF_NO_EXIT)))
|
||||
if (victim->player && (sector->Flags & SECF_ENDLEVEL) && victim->player->health <= 10 && (!deathmatch || !(dmflags & DF_NO_EXIT)))
|
||||
{
|
||||
Level->ExitLevel(0, false);
|
||||
}
|
||||
if (sector->Flags & SECF_DMGTERRAINFX)
|
||||
{
|
||||
P_HitWater(player->mo, player->mo->Sector, player->mo->Pos(), false, true, true);
|
||||
P_HitWater(victim, victim->Sector, victim->Pos(), false, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -506,14 +509,14 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
|
|||
{
|
||||
if (Level->time % sector->damageinterval == 0)
|
||||
{
|
||||
P_GiveBody(player->mo, -sector->damageamount, 100);
|
||||
P_GiveBody(victim, -sector->damageamount, 100);
|
||||
}
|
||||
}
|
||||
|
||||
if (sector->isSecret())
|
||||
if (victim->player && sector->isSecret())
|
||||
{
|
||||
sector->ClearSecret();
|
||||
P_GiveSecret(Level, player->mo, true, true, sector->Index());
|
||||
P_GiveSecret(Level, victim, true, true, sector->Index());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -652,13 +655,13 @@ DEFINE_ACTION_FUNCTION(FLevelLocals, GiveSecret)
|
|||
|
||||
//============================================================================
|
||||
//
|
||||
// P_PlayerOnSpecialFlat
|
||||
// P_ActorOnSpecialFlat
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void P_PlayerOnSpecialFlat (player_t *player, int floorType)
|
||||
void P_ActorOnSpecialFlat (AActor *victim, int floorType)
|
||||
{
|
||||
auto Level = player->mo->Level;
|
||||
auto Level = victim->Level;
|
||||
|
||||
if (Terrains[floorType].DamageAmount &&
|
||||
!(Level->time % (Terrains[floorType].DamageTimeMask+1)))
|
||||
|
|
@ -668,7 +671,7 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType)
|
|||
if (Terrains[floorType].AllowProtection)
|
||||
{
|
||||
auto pitype = PClass::FindActor(NAME_PowerIronFeet);
|
||||
for (ironfeet = player->mo->Inventory; ironfeet != NULL; ironfeet = ironfeet->Inventory)
|
||||
for (ironfeet = victim->Inventory; ironfeet != NULL; ironfeet = ironfeet->Inventory)
|
||||
{
|
||||
if (ironfeet->IsKindOf (pitype))
|
||||
break;
|
||||
|
|
@ -678,20 +681,18 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType)
|
|||
int damage = 0;
|
||||
if (ironfeet == NULL)
|
||||
{
|
||||
damage = P_DamageMobj (player->mo, NULL, NULL, Terrains[floorType].DamageAmount,
|
||||
damage = P_DamageMobj (victim, NULL, NULL, Terrains[floorType].DamageAmount,
|
||||
Terrains[floorType].DamageMOD);
|
||||
}
|
||||
if (damage > 0 && Terrains[floorType].Splash != -1)
|
||||
{
|
||||
S_Sound (player->mo, CHAN_AUTO, 0,
|
||||
S_Sound (victim, CHAN_AUTO, 0,
|
||||
Splashes[Terrains[floorType].Splash].NormalSplashSound, 1,
|
||||
ATTN_IDLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// P_UpdateSpecials
|
||||
// Animate planes, scroll walls, etc.
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "dsectoreffect.h"
|
||||
#include "doomdata.h"
|
||||
#include "r_state.h"
|
||||
#include "d_player.h"
|
||||
|
||||
class FScanner;
|
||||
struct level_info_t;
|
||||
|
|
@ -90,13 +91,22 @@ bool P_ActivateLine (line_t *ld, AActor *mo, int side, int activationType, DVect
|
|||
bool P_TestActivateLine (line_t *ld, AActor *mo, int side, int activationType, DVector3 *optpos = NULL);
|
||||
bool P_PredictLine (line_t *ld, AActor *mo, int side, int activationType);
|
||||
|
||||
void P_PlayerInSpecialSector (player_t *player, sector_t * sector=NULL);
|
||||
void P_PlayerOnSpecialFlat (player_t *player, int floorType);
|
||||
void P_ActorInSpecialSector (AActor *victim, sector_t * sector = NULL, F3DFloor* Ffloor = NULL);
|
||||
void P_ActorOnSpecialFlat (AActor *victim, int floorType);
|
||||
void P_SectorDamage(FLevelLocals *Level, int tag, int amount, FName type, PClassActor *protectClass, int flags);
|
||||
void P_SetSectorFriction (FLevelLocals *level, int tag, int amount, bool alterFlag);
|
||||
double FrictionToMoveFactor(double friction);
|
||||
void P_GiveSecret(FLevelLocals *Level, AActor *actor, bool printmessage, bool playsound, int sectornum);
|
||||
|
||||
inline bool checkForSpecialSector(AActor* mo, sector_t* sec)
|
||||
{
|
||||
bool afsdnope = !!(mo->flags9 & MF9_NOSECTORDAMAGE);
|
||||
bool afsdforce = !!(mo->flags9 & MF9_FORCESECTORDAMAGE);
|
||||
bool sfhurtmonsters = !!(sec->MoreFlags & SECMF_HURTMONSTERS);
|
||||
bool isplayer = (mo->player != nullptr) && (mo == mo->player->mo);
|
||||
return ((!afsdnope || afsdforce) && (isplayer || sfhurtmonsters || afsdforce));
|
||||
}
|
||||
|
||||
//
|
||||
// getNextSector()
|
||||
// Return sector_t * of sector next to current.
|
||||
|
|
|
|||
|
|
@ -1204,15 +1204,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)
|
||||
|
|
|
|||
|
|
@ -286,12 +286,12 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip)
|
|||
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(seg->backsector, in_area, true);
|
||||
if (hw_CheckClip(seg->sidedef, currentsector, backsector)) clipperr.SafeAddClipRange(startAngleR, endAngleR);
|
||||
}
|
||||
|
|
@ -1011,7 +1011,7 @@ void HWDrawInfo::RenderBSP(void *node, bool drawpsprites)
|
|||
// 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 != NULL)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,9 +49,6 @@
|
|||
#include "v_text.h"
|
||||
#include "m_argv.h"
|
||||
#include "v_video.h"
|
||||
#ifndef _MSC_VER
|
||||
#include "i_system.h" // for strlwr()
|
||||
#endif // !_MSC_VER
|
||||
|
||||
void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns);
|
||||
EXTERN_CVAR(Bool, strictdecorate);
|
||||
|
|
@ -945,15 +942,12 @@ static void ParseActorProperty(FScanner &sc, Baggage &bag)
|
|||
"Spawn", "See", "Melee", "Missile", "Pain", "Death", "XDeath", "Burn",
|
||||
"Ice", "Raise", "Crash", "Crush", "Wound", "Disintegrate", "Heal", NULL };
|
||||
|
||||
strlwr (sc.String);
|
||||
|
||||
FString propname = sc.String;
|
||||
|
||||
if (sc.CheckString ("."))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
propname += '.';
|
||||
strlwr (sc.String);
|
||||
propname += sc.String;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -45,9 +45,6 @@
|
|||
#include "thingdef.h"
|
||||
#include "codegen.h"
|
||||
#include "backend/codegen_doom.h"
|
||||
#ifndef _MSC_VER
|
||||
#include "i_system.h" // for strlwr()
|
||||
#endif // !_MSC_VER
|
||||
|
||||
//==========================================================================
|
||||
//***
|
||||
|
|
@ -562,11 +559,8 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg
|
|||
|
||||
FxExpression* ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag)
|
||||
{
|
||||
// Make the action name lowercase
|
||||
strlwr (sc.String);
|
||||
|
||||
FxExpression *call = DoActionSpecials(sc, state, bag);
|
||||
if (call != NULL)
|
||||
if (call != nullptr)
|
||||
{
|
||||
return call;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -351,7 +351,9 @@ static FFlagDef ActorFlagDefs[]=
|
|||
DEFINE_FLAG(MF9, SHADOWBLOCK, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, SHADOWAIMVERT, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, DECOUPLEDANIMATIONS, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, NOSECTORDAMAGE, AActor, flags9),
|
||||
DEFINE_PROTECTED_FLAG(MF9, ISPUFF, AActor, flags9), //[AA] was spawned by SpawnPuff
|
||||
DEFINE_FLAG(MF9, FORCESECTORDAMAGE, AActor, flags9),
|
||||
|
||||
// Effect flags
|
||||
DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),
|
||||
|
|
|
|||
|
|
@ -869,6 +869,34 @@ DEFINE_ACTION_FUNCTION_NATIVE(_Sector, SetXOffset, SetXOffset)
|
|||
ACTION_RETURN_INT(self->GetLightLevel());
|
||||
}
|
||||
|
||||
static void SetPlaneReflectivity(sector_t* self, int pos, double val)
|
||||
{
|
||||
if (pos < 0 || pos > 1) ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "pos must be either 0 or 1");
|
||||
self->SetPlaneReflectivity(pos, val);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Sector, SetPlaneReflectivity, SetPlaneReflectivity)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
|
||||
PARAM_INT(pos);
|
||||
PARAM_FLOAT(val)
|
||||
SetPlaneReflectivity(self, pos, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static double GetPlaneReflectivity(sector_t* self, int pos)
|
||||
{
|
||||
if (pos < 0 || pos > 1) ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "pos must be either 0 or 1");
|
||||
return self->GetPlaneReflectivity(pos);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Sector, GetPlaneReflectivity, GetPlaneReflectivity)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
|
||||
PARAM_INT(pos);
|
||||
ACTION_RETURN_FLOAT(GetPlaneReflectivity(self, pos));
|
||||
}
|
||||
|
||||
static int PortalBlocksView(sector_t *self, int pos)
|
||||
{
|
||||
return self->PortalBlocksView(pos);
|
||||
|
|
@ -2266,7 +2294,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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,20 +41,20 @@ const char *GetVersionString();
|
|||
|
||||
/** Lots of different version numbers **/
|
||||
|
||||
#define VERSIONSTR "4.13.0"
|
||||
#define VERSIONSTR "4.14pre"
|
||||
|
||||
// The version as seen in the Windows resource
|
||||
#define RC_FILEVERSION 4,13,0,0
|
||||
#define RC_PRODUCTVERSION 4,13,0,0
|
||||
#define RC_FILEVERSION 4,13,9999,0
|
||||
#define RC_PRODUCTVERSION 4,13,9999,0
|
||||
#define RC_PRODUCTVERSION2 VERSIONSTR
|
||||
// These are for content versioning.
|
||||
#define VER_MAJOR 4
|
||||
#define VER_MINOR 13
|
||||
#define VER_MINOR 14
|
||||
#define VER_REVISION 0
|
||||
|
||||
// This should always refer to the GZDoom version a derived port is based on and not reflect the derived port's version number!
|
||||
#define ENG_MAJOR 4
|
||||
#define ENG_MINOR 13
|
||||
#define ENG_MINOR 14
|
||||
#define ENG_REVISION 0
|
||||
|
||||
// Version identifier for network games.
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ DoomEdNums
|
|||
9081 = SkyPicker
|
||||
9082 = SectorSilencer
|
||||
9083 = SkyCamCompat
|
||||
9084 = OrthographicCamera
|
||||
9200 = Decal
|
||||
9300 = "$PolyAnchor"
|
||||
9301 = "$PolySpawn"
|
||||
|
|
|
|||
|
|
@ -773,7 +773,7 @@ class Actor : Thinker native
|
|||
native Actor SpawnPuff(class<Actor> pufftype, vector3 pos, double hitdir, double particledir, int updown, int flags = 0, Actor victim = null);
|
||||
native void SpawnBlood (Vector3 pos1, double dir, int damage);
|
||||
native void BloodSplatter (Vector3 pos, double hitangle, bool axe = false);
|
||||
native bool HitWater (sector sec, Vector3 pos, bool checkabove = false, bool alert = true, bool force = false);
|
||||
native bool HitWater (sector sec, Vector3 pos, bool checkabove = false, bool alert = true, bool force = false, int flags = 0);
|
||||
native void PlaySpawnSound(Actor missile);
|
||||
native clearscope bool CountsAsKill() const;
|
||||
|
||||
|
|
@ -851,6 +851,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();
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ class PlayerPawn : Actor
|
|||
flagdef CanSuperMorph: PlayerFlags, 1;
|
||||
flagdef CrouchableMorph: PlayerFlags, 2;
|
||||
flagdef WeaponLevel2Ended: PlayerFlags, 3;
|
||||
//PF_VOODOO_ZOMBIE
|
||||
flagdef MakeFootsteps: PlayerFlags, 5; //[inkoalawetrust] Use footstep system virtual.
|
||||
|
||||
enum EPrivatePlayerFlags
|
||||
{
|
||||
|
|
@ -1690,6 +1692,7 @@ class PlayerPawn : Actor
|
|||
CheckPitch();
|
||||
HandleMovement();
|
||||
CalcHeight ();
|
||||
if (bMakeFootsteps) MakeFootsteps();
|
||||
|
||||
if (!(player.cheats & CF_PREDICTING))
|
||||
{
|
||||
|
|
@ -1718,6 +1721,106 @@ class PlayerPawn : Actor
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Handle player footstep sounds.
|
||||
// Default footstep handling.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
int footstepCounter;
|
||||
double footstepLength;
|
||||
bool footstepFoot;
|
||||
|
||||
void DoFootstep(TerrainDef Ground)
|
||||
{
|
||||
Sound Step = Ground.StepSound;
|
||||
|
||||
//Generic foot-agnostic sound takes precedence.
|
||||
if(!Step)
|
||||
{
|
||||
//Apparently most people walk with their right foot first, so assume that here.
|
||||
if (!footstepFoot)
|
||||
{
|
||||
Step = Ground.LeftStepSound;
|
||||
}
|
||||
else
|
||||
{
|
||||
Step = Ground.RightStepSound;
|
||||
}
|
||||
|
||||
footstepFoot = !footstepFoot;
|
||||
}
|
||||
|
||||
if(Step)
|
||||
{
|
||||
A_StartSound(Step, flags: CHANF_OVERLAP, volume: Ground.StepVolume * snd_footstepvolume);
|
||||
}
|
||||
|
||||
//Steps make splashes regardless.
|
||||
bool Heavy = (Mass >= 200) ? 0 : THW_SMALL; //Big player makes big splash.
|
||||
HitWater(CurSector, (Pos.XY, CurSector.FloorPlane.ZatPoint(Pos.XY)), true, false, flags: Heavy | THW_NOVEL);
|
||||
}
|
||||
|
||||
virtual void MakeFootsteps()
|
||||
{
|
||||
if(pos.z > floorz) return;
|
||||
|
||||
let Ground = GetFloorTerrain();
|
||||
|
||||
if(Ground && (player.cmd.forwardMove != 0 || player.cmd.sideMove != 0))
|
||||
{
|
||||
int Delay = (player.cmd.buttons & BT_RUN) ? Ground.RunStepTics : Ground.WalkStepTics;
|
||||
|
||||
if((player.cmd.buttons ^ player.oldbuttons) & BT_RUN)
|
||||
{ // zero out counters when starting/stopping a run
|
||||
footstepCounter = 0;
|
||||
footstepLength = Ground.StepDistance;
|
||||
}
|
||||
|
||||
if(Ground.StepDistance > 0)
|
||||
{ // distance-based terrain
|
||||
footstepCounter = 0;
|
||||
|
||||
double moveVel = vel.xy.length();
|
||||
|
||||
if(moveVel > Ground.StepDistanceMinVel)
|
||||
{
|
||||
footstepLength += moveVel;
|
||||
|
||||
while(footstepLength > Ground.StepDistance)
|
||||
{
|
||||
footstepLength -= Ground.StepDistance;
|
||||
DoFootstep(Ground);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
footstepLength = Ground.StepDistance;
|
||||
}
|
||||
|
||||
}
|
||||
else if(Delay > 0)
|
||||
{ // delay-based terrain
|
||||
footstepLength = 0;
|
||||
|
||||
if(footstepCounter % Delay == 0)
|
||||
{
|
||||
DoFootstep(Ground);
|
||||
}
|
||||
|
||||
footstepCounter = (footstepCounter + 1) % Delay;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
footstepCounter = 0;
|
||||
footstepLength = Ground.StepDistance;
|
||||
footstepFoot = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// PROC P_BringUpWeapon
|
||||
|
|
|
|||
|
|
@ -212,3 +212,35 @@ class SpectatorCamera : Actor
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Class OrthographicCamera : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
+NOBLOCKMAP
|
||||
+NOINTERACTION
|
||||
CameraHeight 0;
|
||||
RenderStyle "None";
|
||||
}
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
UpdateViewPos();
|
||||
}
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
if (current != args[0])
|
||||
UpdateViewPos();
|
||||
|
||||
Super.Tick();
|
||||
}
|
||||
|
||||
protected int current;
|
||||
protected void UpdateViewPos()
|
||||
{
|
||||
current = args[0];
|
||||
SetViewPos((-abs(max(1.0, double(current))), 0, 0), VPSF_ORTHOGRAPHIC|VPSF_ALLOWOUTOFBOUNDS);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1466,6 +1466,12 @@ enum ECompatFlags
|
|||
COMPATF2_VOODOO_ZOMBIES = 1 << 15, // allow playerinfo, playerpawn, and voodoo health to all be different, and allow monster targetting of 'dead' players that have positive health
|
||||
};
|
||||
|
||||
enum HitWaterFlags
|
||||
{
|
||||
THW_SMALL = 1 << 0,
|
||||
THW_NOVEL = 1 << 1,
|
||||
};
|
||||
|
||||
const M_E = 2.7182818284590452354; // e
|
||||
const M_LOG2E = 1.4426950408889634074; // log_2 e
|
||||
const M_LOG10E = 0.43429448190325182765; // log_10 e
|
||||
|
|
|
|||
|
|
@ -613,6 +613,9 @@ struct TerrainDef native
|
|||
native bool DamageOnLand;
|
||||
native double Friction;
|
||||
native double MoveFactor;
|
||||
native Sound StepSound;
|
||||
native double StepDistance;
|
||||
native double StepDistanceMinVel;
|
||||
};
|
||||
|
||||
enum EPickStart
|
||||
|
|
|
|||
|
|
@ -689,6 +689,7 @@ struct CVar native
|
|||
};
|
||||
|
||||
native static CVar FindCVar(Name name);
|
||||
native static bool SaveConfig();
|
||||
bool GetBool() { return GetInt(); }
|
||||
native int GetInt();
|
||||
native double GetFloat();
|
||||
|
|
|
|||
|
|
@ -435,6 +435,11 @@ struct Sector native play
|
|||
SECMF_UNDERWATERMASK = 32+64,
|
||||
SECMF_DRAWN = 128, // sector has been drawn at least once
|
||||
SECMF_HIDDEN = 256, // Do not draw on textured automap
|
||||
SECMF_OVERLAPPING = 512, // floor and ceiling overlap and require special renderer action.
|
||||
SECMF_NOSKYWALLS = 1024, // Do not draw "sky walls"
|
||||
SECMF_LIFT = 2048, // For MBF monster AI
|
||||
SECMF_HURTMONSTERS = 4096, // Monsters in this sector are hurt like players.
|
||||
SECMF_HARMINAIR = 8192, // Actors in this sector are also hurt mid-air.
|
||||
}
|
||||
native uint16 MoreFlags;
|
||||
|
||||
|
|
@ -452,6 +457,9 @@ struct Sector native play
|
|||
SECF_ENDLEVEL = 512, // ends level when health goes below 10
|
||||
SECF_HAZARD = 1024, // Change to Strife's delayed damage handling.
|
||||
SECF_NOATTACK = 2048, // monsters cannot start attacks in this sector.
|
||||
SECF_EXIT1 = 4096,
|
||||
SECF_EXIT2 = 8192,
|
||||
SECF_KILLMONSTERS = 16384,// Monsters in this sector are instantly killed.
|
||||
|
||||
SECF_WASSECRET = 1 << 30, // a secret that was discovered
|
||||
SECF_SECRET = 1 << 31, // a secret sector
|
||||
|
|
@ -545,6 +553,8 @@ struct Sector native play
|
|||
native void ChangeLightLevel(int newval);
|
||||
native void SetLightLevel(int newval);
|
||||
native clearscope int GetLightLevel() const;
|
||||
native void SetPlaneReflectivity(int pos, double val);
|
||||
native clearscope double GetPlaneReflectivity(int pos);
|
||||
native void AdjustFloorClip();
|
||||
native clearscope bool IsLinked(Sector other, bool ceiling) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -709,15 +709,15 @@ Order // Order in the IWAD selection box
|
|||
"DOOM 2: L'Enfer sur Terre"
|
||||
"DOOM 2: BFG Edition"
|
||||
"DOOM 2: XBox Edition"
|
||||
"DOOM 2: KEX Edition"
|
||||
"DOOM 2: Unity Edition"
|
||||
"DOOM 2: KEX Edition"
|
||||
"The Ultimate DOOM"
|
||||
"DOOM Registered"
|
||||
"DOOM Shareware"
|
||||
"DOOM: BFG Edition"
|
||||
"DOOM: XBox Edition"
|
||||
"DOOM: KEX Edition"
|
||||
"DOOM: Unity Edition"
|
||||
"DOOM: KEX Edition"
|
||||
"Final Doom: Plutonia Experiment"
|
||||
"Final Doom: Plutonia Experiment: Unity Edition"
|
||||
"Final Doom: Plutonia Experiment: KEX Edition"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue