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:
Rachael Alexanderson 2024-11-05 08:37:29 -05:00
commit 51fddf08c3
No known key found for this signature in database
GPG key ID: 26A8ACCE97115EE0
43 changed files with 859 additions and 428 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

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

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

View file

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

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

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

View file

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

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

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

View file

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

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

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

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

View file

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

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

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

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

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

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

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

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

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

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

View file

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

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

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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