This commit is contained in:
nashmuhandes 2017-03-02 22:15:28 +08:00
commit d298cc7414
80 changed files with 1264 additions and 873 deletions

View file

@ -263,8 +263,8 @@ Note: All <bool> fields default to false unless mentioned otherwise.
gravity = <float>; // Set per-actor gravity. Positive values are multiplied with the class's property,
// negative values are used as their absolute. Default = 1.0.
health = <int>; // Set per-actor health as an absolute value. Default = actor default.
healthfactor = <float>; // Set per-actor health as a factor to the original. Default = 1.
health = <float>; // Set per-actor health. Positive values are multiplied with the class's property,
// negative values are used as their absolute. Default = 1.
renderstyle = <string>; // Set per-actor render style, overriding the class default. Possible values can be "normal",
// "none", "add" or "additive", "subtract" or "subtractive", "stencil", "translucentstencil",

View file

@ -260,5 +260,7 @@ DEFINE_SPECIAL(Stairs_BuildUpDoomSync, 271, 4, 4, 4)
DEFINE_SPECIAL(Stairs_BuildDownDoomSync, 272, 4, 4, 4)
DEFINE_SPECIAL(Stairs_BuildUpDoomCrush, 273, 5, 5, 5)
DEFINE_SPECIAL(Door_AnimatedClose, 274, 4, 4, 4)
DEFINE_SPECIAL(Floor_Stop, 275, 1, 1, 1)
DEFINE_SPECIAL(Ceiling_Stop, 276, 1, 1, 1)
#undef DEFINE_SPECIAL

View file

@ -808,42 +808,12 @@ public:
return (flags & MF_COUNTKILL) && !(flags & MF_FRIENDLY);
}
PalEntry GetBloodColor() const
{
return GetClass()->BloodColor;
}
// These also set CF_INTERPVIEW for players.
void SetPitch(DAngle p, bool interpolate, bool forceclamp = false);
void SetAngle(DAngle ang, bool interpolate);
void SetRoll(DAngle roll, bool interpolate);
PClassActor *GetBloodType(int type = 0) const
{
PClassActor *bloodcls;
if (type == 0)
{
bloodcls = PClass::FindActor(GetClass()->BloodType);
}
else if (type == 1)
{
bloodcls = PClass::FindActor(GetClass()->BloodType2);
}
else if (type == 2)
{
bloodcls = PClass::FindActor(GetClass()->BloodType3);
}
else
{
return NULL;
}
if (bloodcls != NULL)
{
bloodcls = bloodcls->GetReplacement();
}
return bloodcls;
}
PClassActor *GetBloodType(int type = 0) const;
double Distance2DSquared(AActor *other, bool absolute = false)
{
@ -1009,29 +979,42 @@ public:
// NOTE: The first member variable *must* be snext.
AActor *snext, **sprev; // links in sector (if needed)
DVector3 __Pos; // double underscores so that it won't get used by accident. Access to this should be exclusively through the designated access functions.
DVector3 OldRenderPos;
DAngle SpriteAngle;
DAngle SpriteRotation;
DAngle VisibleStartAngle;
DAngle VisibleStartPitch;
DAngle VisibleEndAngle;
DAngle VisibleEndPitch;
DRotator Angles;
DVector3 Vel;
double Speed;
double FloatSpeed;
DVector2 Scale; // Scaling values; 1 is normal size
double Alpha; // Since P_CheckSight makes an alpha check this can't be a float. It has to be a double.
int sprite; // used to find patch_t and flip value
uint8_t frame; // sprite frame to draw
uint8_t effects; // [RH] see p_effect.h
uint8_t fountaincolor; // Split out of 'effect' to have easier access.
DVector2 Scale; // Scaling values; 1 is normal size
FRenderStyle RenderStyle; // Style to draw this actor with
ActorRenderFlags renderflags; // Different rendering flags
FTextureID picnum; // Draw this instead of sprite if valid
double Alpha; // Since P_CheckSight makes an alpha check this can't be a float. It has to be a double.
DWORD fillcolor; // Color to draw when STYLE_Shaded
DWORD Translation;
ActorRenderFlags renderflags; // Different rendering flags
ActorFlags flags;
ActorFlags2 flags2; // Heretic flags
ActorFlags3 flags3; // [RH] Hexen/Heretic actor-dependant behavior made flaggable
ActorFlags4 flags4; // [RH] Even more flags!
ActorFlags5 flags5; // OMG! We need another one.
ActorFlags6 flags6; // Shit! Where did all the flags go?
ActorFlags7 flags7; // WHO WANTS TO BET ON 8!?
double Floorclip; // value to use for floor clipping
double radius, Height; // for movement checking
DAngle VisibleStartAngle;
DAngle VisibleStartPitch;
DAngle VisibleEndAngle;
DAngle VisibleEndPitch;
DVector3 OldRenderPos;
DVector3 Vel;
double Speed;
double FloatSpeed;
// interaction info
FBlockNode *BlockNode; // links in blocks (if needed)
@ -1045,24 +1028,22 @@ public:
int floorterrain;
struct sector_t *ceilingsector;
FTextureID ceilingpic; // contacted sec ceilingpic
double radius, Height; // for movement checking
double renderradius;
double projectilepassheight; // height for clipping projectile movement against this actor
double CameraHeight; // Height of camera when used as such
double RadiusDamageFactor; // Radius damage factor
double SelfDamageFactor;
double StealthAlpha; // Minmum alpha for MF_STEALTH.
int WoundHealth; // Health needed to enter wound state
SDWORD tics; // state tic counter
FState *state;
//VMFunction *Damage; // For missiles and monster railgun
int DamageVal;
VMFunction *DamageFunc;
int projectileKickback;
ActorFlags flags;
ActorFlags2 flags2; // Heretic flags
ActorFlags3 flags3; // [RH] Hexen/Heretic actor-dependant behavior made flaggable
ActorFlags4 flags4; // [RH] Even more flags!
ActorFlags5 flags5; // OMG! We need another one.
ActorFlags6 flags6; // Shit! Where did all the flags go?
ActorFlags7 flags7; // WHO WANTS TO BET ON 8!?
// [BB] If 0, everybody can see the actor, if > 0, only members of team (VisibleToTeam-1) can see it.
DWORD VisibleToTeam;
@ -1100,7 +1081,6 @@ public:
TObjPtr<AActor> alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals.
TObjPtr<AActor> tracer; // Thing being chased/attacked for tracers
TObjPtr<AActor> master; // Thing which spawned this one (prevents mutual attacks)
double Floorclip; // value to use for floor clipping
int tid; // thing identifier
int special; // special
@ -1163,7 +1143,8 @@ public:
BYTE smokecounter;
BYTE FloatBobPhase;
BYTE FriendPlayer; // [RH] Player # + 1 this friendly monster works for (so 0 is no player, 1 is player 0, etc)
DWORD Translation;
PalEntry BloodColor;
DWORD BloodTranslation;
// [RH] Stuff that used to be part of an Actor Info
FSoundIDNoInit SeeSound;

View file

@ -358,7 +358,7 @@ void DBot::WhatToGet (AActor *item)
}
else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere)
return;
else if (item->IsKindOf (PClass::FindActor(NAME_Health)) && player->mo->health >= player->mo->GetMaxHealth() + player->mo->stamina)
else if (item->IsKindOf (PClass::FindActor(NAME_Health)) && player->mo->health >= player->mo->GetMaxHealth(true))
return;
if ((dest == NULL ||

View file

@ -732,34 +732,6 @@ CCMD (dir)
chdir (curdir);
}
CCMD (fov)
{
player_t *player = who ? who->player : &players[consoleplayer];
if (argv.argc() != 2)
{
Printf ("fov is %g\n", player->DesiredFOV);
return;
}
else if (dmflags & DF_NO_FOV)
{
if (consoleplayer == Net_Arbitrator)
{
Net_WriteByte (DEM_FOV);
}
else
{
Printf ("A setting controller has disabled FOV changes.\n");
return;
}
}
else
{
Net_WriteByte (DEM_MYFOV);
}
Net_WriteByte (clamp (atoi (argv[1]), 5, 179));
}
//==========================================================================
//
// CCMD warp

View file

@ -206,6 +206,9 @@ struct FCommandBuffer
unsigned CursorPos;
unsigned StartPos; // First character to display
FString YankBuffer; // Deleted text buffer
bool AppendToYankBuffer; // Append consecutive deletes to buffer
FCommandBuffer()
{
CursorPos = StartPos = 0;
@ -278,6 +281,30 @@ struct FCommandBuffer
StartPos = MAX(0, n);
}
unsigned WordBoundaryRight()
{
unsigned index = CursorPos;
while (index < Text.Len() && Text[index] == ' ') {
index++;
}
while (index < Text.Len() && Text[index] != ' ') {
index++;
}
return index;
}
unsigned WordBoundaryLeft()
{
int index = CursorPos - 1;
while (index > -1 && Text[index] == ' ') {
index--;
}
while (index > -1 && Text[index] != ' ') {
index--;
}
return (unsigned)index + 1;
}
void CursorStart()
{
CursorPos = 0;
@ -309,6 +336,18 @@ struct FCommandBuffer
}
}
void CursorWordLeft()
{
CursorPos = WordBoundaryLeft();
MakeStartPosGood();
}
void CursorWordRight()
{
CursorPos = WordBoundaryRight();
MakeStartPosGood();
}
void DeleteLeft()
{
if (CursorPos > 0)
@ -328,6 +367,50 @@ struct FCommandBuffer
}
}
void DeleteWordLeft()
{
if (CursorPos > 0)
{
unsigned index = WordBoundaryLeft();
if (AppendToYankBuffer) {
YankBuffer = FString(&Text[index], CursorPos - index) + YankBuffer;
} else {
YankBuffer = FString(&Text[index], CursorPos - index);
}
Text.Remove(index, CursorPos - index);
CursorPos = index;
MakeStartPosGood();
}
}
void DeleteLineLeft()
{
if (CursorPos > 0)
{
if (AppendToYankBuffer) {
YankBuffer = FString(&Text[0], CursorPos) + YankBuffer;
} else {
YankBuffer = FString(&Text[0], CursorPos);
}
Text.Remove(0, CursorPos);
CursorStart();
}
}
void DeleteLineRight()
{
if (CursorPos < Text.Len())
{
if (AppendToYankBuffer) {
YankBuffer += FString(&Text[CursorPos], Text.Len() - CursorPos);
} else {
YankBuffer = FString(&Text[CursorPos], Text.Len() - CursorPos);
}
Text.Truncate(CursorPos);
CursorEnd();
}
}
void AddChar(int character)
{
///FIXME: Not Unicode-aware
@ -1345,6 +1428,7 @@ DEFINE_ACTION_FUNCTION(_Console, Printf)
static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
{
int data1 = ev->data1;
bool keepappending = false;
switch (ev->subtype)
{
@ -1352,8 +1436,22 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
return false;
case EV_GUI_Char:
if (ev->data2)
{
// Bash-style shortcuts
if (data1 == 'b')
{
buffer.CursorWordLeft();
break;
}
else if (data1 == 'f')
{
buffer.CursorWordRight();
break;
}
}
// Add keypress to command line
buffer.AddChar(ev->data1);
buffer.AddChar(data1);
HistPos = NULL;
TabbedLast = false;
TabbedList = false;
@ -1654,6 +1752,56 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
break;
}
break;
// Bash-style shortcuts
case 'A':
if (ev->data3 & GKM_CTRL)
{
buffer.CursorStart();
}
break;
case 'E':
if (ev->data3 & GKM_CTRL)
{
buffer.CursorEnd();
}
break;
case 'W':
if (ev->data3 & GKM_CTRL)
{
buffer.DeleteWordLeft();
keepappending = true;
TabbedLast = false;
TabbedList = false;
}
break;
case 'U':
if (ev->data3 & GKM_CTRL)
{
buffer.DeleteLineLeft();
keepappending = true;
TabbedLast = false;
TabbedList = false;
}
break;
case 'K':
if (ev->data3 & GKM_CTRL)
{
buffer.DeleteLineRight();
keepappending = true;
TabbedLast = false;
TabbedList = false;
}
break;
case 'Y':
if (ev->data3 & GKM_CTRL)
{
buffer.AddString(buffer.YankBuffer);
TabbedLast = false;
TabbedList = false;
HistPos = NULL;
}
break;
}
break;
@ -1664,6 +1812,9 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
break;
#endif
}
buffer.AppendToYankBuffer = keepappending;
// Ensure that the cursor is always visible while typing
CursorTicker = C_BLINKRATE;
cursoron = 1;

View file

@ -86,6 +86,7 @@ enum
CP_SETTHINGZ,
CP_SETTAG,
CP_SETTHINGFLAGS,
CP_SETVERTEX,
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -345,7 +346,19 @@ void ParseCompatibility()
sc.MustGetNumber();
CompatParams.Push(sc.Number);
}
else
else if (sc.Compare("setvertex"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETVERTEX);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetFloat();
CompatParams.Push(int(sc.Float * 256)); // do not use full fixed here so that it can eventually handle larger levels
sc.MustGetFloat();
CompatParams.Push(int(sc.Float * 256)); // do not use full fixed here so that it can eventually handle larger levels
flags.CompatFlags[SLOT_BCOMPAT] |= BCOMPATF_REBUILDNODES;
}
else
{
sc.UnGet();
break;
@ -600,6 +613,16 @@ void SetCompatibilityParams()
i += 3;
break;
}
case CP_SETVERTEX:
{
if ((unsigned)CompatParams[i + 1] < level.vertexes.Size())
{
level.vertexes[CompatParams[i + 1]].p.X = CompatParams[i + 2] / 256.;
level.vertexes[CompatParams[i + 1]].p.Y = CompatParams[i + 3] / 256.;
}
i += 4;
break;
}
}
}
}

View file

@ -2693,6 +2693,7 @@ void D_DoomMain (void)
// These calls from inside V_Init2 are still necessary
C_NewModeAdjust();
M_InitVideoModesMenu();
Renderer->RemapVoxels();
D_StartTitle (); // start up intro loop
setmodeneeded = false; // This may be set to true here, but isn't needed for a restart
}

View file

@ -2381,6 +2381,11 @@ void Net_DoCommand (int type, BYTE **stream, int player)
SprayDecal(players[player].mo, s);
break;
case DEM_MDK:
s = ReadString(stream);
cht_DoMDK(&players[player], s);
break;
case DEM_PAUSE:
if (gamestate == GS_LEVEL)
{
@ -2667,13 +2672,12 @@ void Net_DoCommand (int type, BYTE **stream, int player)
case DEM_NETEVENT:
{
const char *ename = ReadString(stream);
s = ReadString(stream);
int argn = ReadByte(stream);
int arg[3] = { 0, 0, 0 };
for (int i = 0; i < 3; i++)
arg[i] = ReadLong(stream);
E_Console(player, ename, arg[0], arg[1], arg[2]);
delete[] ename;
E_Console(player, s, arg[0], arg[1], arg[2]);
}
break;
@ -2724,7 +2728,7 @@ void Net_SkipCommand (int type, BYTE **stream)
break;
case DEM_NETEVENT:
skip = strlen((char *)(*stream)) + 13;
skip = strlen((char *)(*stream)) + 14;
break;
case DEM_SUMMON2:
@ -2748,6 +2752,7 @@ void Net_SkipCommand (int type, BYTE **stream)
case DEM_SPRAY:
case DEM_MORPHEX:
case DEM_KILLCLASSCHEAT:
case DEM_MDK:
skip = strlen ((char *)(*stream)) + 1;
break;

View file

@ -94,7 +94,7 @@ public:
virtual bool UpdateWaterLevel (bool splash) override;
bool ResetAirSupply (bool playgasp = true);
int GetMaxHealth() const;
int GetMaxHealth(bool withupgrades = false) const;
void TweakSpeeds (double &forwardmove, double &sidemove);
void MorphPlayerThink ();
void ActivateMorphWeapon ();
@ -126,6 +126,8 @@ public:
int crouchsprite;
int MaxHealth;
int BonusHealth;
int MugShotMaxHealth;
int RunHealth;
int PlayerFlags;
@ -160,8 +162,6 @@ public:
FNameNoInit Face; // Doom status bar face (when used)
FNameNoInit Portrait;
FNameNoInit Slot[10];
FNameNoInit InvulMode;
FNameNoInit HealingRadiusType;
double HexenArmor[5];
BYTE ColorRangeStart; // Skin color range
BYTE ColorRangeEnd;
@ -530,6 +530,9 @@ public:
DPSprite *GetPSprite(PSPLayers layer);
bool GetPainFlash(FName type, PalEntry *color) const;
// [Nash] set player FOV
void SetFOV(float fov);
};
// Bookkeeping on players - state.

View file

@ -159,7 +159,8 @@ enum EDemoCommand
DEM_SETSLOTPNUM, // 67 Byte: player number, the rest is the same as DEM_SETSLOT
DEM_REMOVE, // 68
DEM_FINISHGAME, // 69
DEM_NETEVENT // 70 String: Event name, Byte: Arg count; each arg is a 4-byte int
DEM_NETEVENT, // 70 String: Event name, Byte: Arg count; each arg is a 4-byte int
DEM_MDK // 71 String: Damage type
};
// The following are implemented by cht_DoCheat in m_cheat.cpp

View file

@ -623,12 +623,20 @@ DEFINE_ACTION_FUNCTION(DObject, MSTime)
void *DObject::ScriptVar(FName field, PType *type)
{
auto sym = dyn_cast<PField>(GetClass()->Symbols.FindSymbol(field, true));
auto cls = GetClass();
auto sym = dyn_cast<PField>(cls->Symbols.FindSymbol(field, true));
if (sym && (sym->Type == type || type == nullptr))
{
return (((char*)this) + sym->Offset);
if (!(sym->Flags & VARF_Meta))
{
return (((char*)this) + sym->Offset);
}
else
{
return (cls->Meta + sym->Offset);
}
}
// This is only for internal use so I_Error is fine.
I_Error("Variable %s not found in %s\n", field.GetChars(), GetClass()->TypeName.GetChars());
I_Error("Variable %s not found in %s\n", field.GetChars(), cls->TypeName.GetChars());
return nullptr;
}

View file

@ -41,6 +41,7 @@
class PClass;
class PType;
class FSerializer;
class FSoundID;
class DObject;
/*
@ -483,9 +484,11 @@ public:
// Add other types as needed.
bool &BoolVar(FName field);
int &IntVar(FName field);
FSoundID &SoundVar(FName field);
PalEntry &ColorVar(FName field);
FName &NameVar(FName field);
double &FloatVar(FName field);
FString &StringVar(FName field);
template<class T> T*& PointerVar(FName field);
// If you need to replace one object with another and want to

View file

@ -722,10 +722,6 @@ PBool::PBool()
{
mDescriptiveName = "Bool";
MemberOnly = false;
// Override the default max set by PInt's constructor
PSymbolConstNumeric *maxsym = static_cast<PSymbolConstNumeric *>(Symbols.FindSymbol(NAME_Max, false));
assert(maxsym != nullptr && maxsym->IsKindOf(RUNTIME_CLASS(PSymbolConstNumeric)));
maxsym->Value = 1;
}
/* PFloat *****************************************************************/
@ -2311,7 +2307,7 @@ void PStruct::WriteFields(FSerializer &ar, const void *addr, const TArray<PField
{
const PField *field = fields[i];
// Skip fields without or with native serialization
if (!(field->Flags & VARF_Transient))
if (!(field->Flags & (VARF_Transient|VARF_Meta)))
{
field->Type->WriteValue(ar, field->SymbolName.GetChars(), (const BYTE *)addr + field->Offset);
}
@ -2344,6 +2340,11 @@ bool PStruct::ReadFields(FSerializer &ar, void *addr) const
DPrintf(DMSG_ERROR, "Symbol %s in %s is not a field\n",
label, TypeName.GetChars());
}
else if ((static_cast<const PField *>(sym)->Flags & (VARF_Transient | VARF_Meta)))
{
DPrintf(DMSG_ERROR, "Symbol %s in %s is not a serializable field\n",
label, TypeName.GetChars());
}
else
{
readsomething |= static_cast<const PField *>(sym)->Type->ReadValue(ar, nullptr,
@ -2642,7 +2643,7 @@ static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void *
// Don't write this part if it has no non-transient variables
for (unsigned i = 0; i < type->Fields.Size(); ++i)
{
if (!(type->Fields[i]->Flags & VARF_Transient))
if (!(type->Fields[i]->Flags & (VARF_Transient|VARF_Meta)))
{
// Tag this section with the class it came from in case
// a more-derived class has variables that shadow a less-
@ -2892,6 +2893,7 @@ PClass::PClass()
bExported = false;
bDecorateClass = false;
ConstructNative = nullptr;
Meta = nullptr;
mDescriptiveName = "Class";
PClass::AllClasses.Push(this);
@ -2910,6 +2912,11 @@ PClass::~PClass()
M_Free(Defaults);
Defaults = nullptr;
}
if (Meta != nullptr)
{
M_Free(Meta);
Meta = nullptr;
}
}
//==========================================================================
@ -3047,7 +3054,7 @@ PClass *PClass::FindClass (FName zaname)
//
//==========================================================================
DObject *PClass::CreateNew() const
DObject *PClass::CreateNew()
{
BYTE *mem = (BYTE *)M_Malloc (Size);
assert (mem != nullptr);
@ -3064,7 +3071,7 @@ DObject *PClass::CreateNew() const
}
ConstructNative (mem);
((DObject *)mem)->SetClass (const_cast<PClass *>(this));
InitializeSpecials(mem, Defaults);
InitializeSpecials(mem, Defaults, &PClass::SpecialInits);
return (DObject *)mem;
}
@ -3076,17 +3083,16 @@ DObject *PClass::CreateNew() const
//
//==========================================================================
void PClass::InitializeSpecials(void *addr, void *defaults) const
void PClass::InitializeSpecials(void *addr, void *defaults, TArray<FTypeAndOffset> PClass::*Inits)
{
// Once we reach a native class, we can stop going up the family tree,
// since native classes handle initialization natively.
if (!bRuntimeClass)
if ((!bRuntimeClass && Inits == &PClass::SpecialInits) || ParentClass == nullptr)
{
return;
}
assert(ParentClass != nullptr);
ParentClass->InitializeSpecials(addr, defaults);
for (auto tao : SpecialInits)
ParentClass->InitializeSpecials(addr, defaults, Inits);
for (auto tao : (this->*Inits))
{
tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second);
}
@ -3101,7 +3107,7 @@ void PClass::InitializeSpecials(void *addr, void *defaults) const
//
//==========================================================================
void PClass::DestroySpecials(void *addr) const
void PClass::DestroySpecials(void *addr)
{
// Once we reach a native class, we can stop going up the family tree,
// since native classes handle deinitialization natively.
@ -3160,7 +3166,6 @@ void PClass::InitializeDefaults()
optr->ObjNext = nullptr;
optr->SetClass(this);
// Copy the defaults from the parent but leave the DObject part alone because it contains important data.
if (ParentClass->Defaults != nullptr)
{
@ -3174,21 +3179,53 @@ void PClass::InitializeDefaults()
{
memset(Defaults + sizeof(DObject), 0, Size - sizeof(DObject));
}
assert(MetaSize >= ParentClass->MetaSize);
if (MetaSize != 0)
{
Meta = (BYTE*)M_Malloc(MetaSize);
// Copy the defaults from the parent but leave the DObject part alone because it contains important data.
if (ParentClass->Meta != nullptr)
{
memcpy(Meta, ParentClass->Meta, ParentClass->MetaSize);
if (MetaSize > ParentClass->MetaSize)
{
memset(Meta + ParentClass->MetaSize, 0, MetaSize - ParentClass->MetaSize);
}
}
else
{
memset(Meta, 0, MetaSize);
}
if (MetaSize > 0) memcpy(Meta, ParentClass->Meta, ParentClass->MetaSize);
else memset(Meta, 0, MetaSize);
}
}
if (bRuntimeClass)
{
// Copy parent values from the parent defaults.
assert(ParentClass != nullptr);
if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults);
if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults, &PClass::SpecialInits);
if (Meta != nullptr) ParentClass->InitializeSpecials(Meta, ParentClass->Meta, &PClass::MetaInits);
for (const PField *field : Fields)
{
if (!(field->Flags & VARF_Native))
if (!(field->Flags & VARF_Native) && !(field->Flags & VARF_Meta))
{
field->Type->SetDefaultValue(Defaults, unsigned(field->Offset), &SpecialInits);
}
}
}
if (Meta != nullptr) ParentClass->InitializeSpecials(Meta, ParentClass->Meta, &PClass::MetaInits);
for (const PField *field : Fields)
{
if (!(field->Flags & VARF_Native) && (field->Flags & VARF_Meta))
{
field->Type->SetDefaultValue(Meta, unsigned(field->Offset), &MetaInits);
}
}
}
//==========================================================================
@ -3248,6 +3285,7 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
type->bRuntimeClass = true;
Derive(type, name);
type->Size = size;
type->MetaSize = MetaSize;
if (size != TentativeClass)
{
type->InitializeDefaults();
@ -3264,6 +3302,39 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
return type;
}
//==========================================================================
//
// PStruct :: AddField
//
// Appends a new metadata field to the end of a struct. Returns either the new field
// or nullptr if a symbol by that name already exists.
//
//==========================================================================
PField *PClass::AddMetaField(FName name, PType *type, DWORD flags)
{
PField *field = new PField(name, type, flags);
// The new field is added to the end of this struct, alignment permitting.
field->Offset = (MetaSize + (type->Align - 1)) & ~(type->Align - 1);
// Enlarge this struct to enclose the new field.
MetaSize = unsigned(field->Offset + type->Size);
// This struct's alignment is the same as the largest alignment of any of
// its fields.
Align = MAX(Align, type->Align);
if (Symbols.AddSymbol(field) == nullptr)
{ // name is already in use
field->Destroy();
return nullptr;
}
Fields.Push(field);
return field;
}
//==========================================================================
//
// PClass :: AddField
@ -3272,18 +3343,36 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
PField *PClass::AddField(FName name, PType *type, DWORD flags)
{
unsigned oldsize = Size;
PField *field = Super::AddField(name, type, flags);
// Only initialize the defaults if they have already been created.
// For ZScript this is not the case, it will first define all fields before
// setting up any defaults for any class.
if (field != nullptr && !(flags & VARF_Native) && Defaults != nullptr)
if (!(flags & VARF_Meta))
{
Defaults = (BYTE *)M_Realloc(Defaults, Size);
memset(Defaults + oldsize, 0, Size - oldsize);
unsigned oldsize = Size;
PField *field = Super::AddField(name, type, flags);
// Only initialize the defaults if they have already been created.
// For ZScript this is not the case, it will first define all fields before
// setting up any defaults for any class.
if (field != nullptr && !(flags & VARF_Native) && Defaults != nullptr)
{
Defaults = (BYTE *)M_Realloc(Defaults, Size);
memset(Defaults + oldsize, 0, Size - oldsize);
}
return field;
}
else
{
unsigned oldsize = MetaSize;
PField *field = AddMetaField(name, type, flags);
// Only initialize the defaults if they have already been created.
// For ZScript this is not the case, it will first define all fields before
// setting up any defaults for any class.
if (field != nullptr && !(flags & VARF_Native) && Meta != nullptr)
{
Meta = (BYTE *)M_Realloc(Meta, MetaSize);
memset(Meta + oldsize, 0, MetaSize - oldsize);
}
return field;
}
return field;
}
//==========================================================================

View file

@ -557,12 +557,13 @@ enum
class PClass : public PNativeStruct
{
DECLARE_CLASS(PClass, PNativeStruct);
protected:
// We unravel _WITH_META here just as we did for PType.
TArray<FTypeAndOffset> SpecialInits;
protected:
TArray<FTypeAndOffset> MetaInits;
void Derive(PClass *newclass, FName name);
void InitializeSpecials(void *addr, void *defaults) const;
void InitializeSpecials(void *addr, void *defaults, TArray<FTypeAndOffset> PClass::*Inits);
void SetSuper();
PField *AddMetaField(FName name, PType *type, DWORD flags);
public:
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
void WriteAllFields(FSerializer &ar, const void *addr) const;
@ -577,11 +578,14 @@ public:
static void StaticBootstrap();
// Per-class information -------------------------------------
TArray<FTypeAndOffset> SpecialInits;
PClass *ParentClass; // the class this class derives from
const size_t *Pointers; // object pointers defined by this class *only*
const size_t *FlatPointers; // object pointers defined by this class and all its superclasses; not initialized by default
const size_t *ArrayPointers; // dynamic arrays containing object pointers.
BYTE *Defaults;
BYTE *Meta; // Per-class static script data
unsigned MetaSize;
bool bRuntimeClass; // class was defined at run-time, not compile-time
bool bExported; // This type has been declared in a script
bool bDecorateClass; // may be subject to some idiosyncracies due to DECORATE backwards compatibility
@ -593,13 +597,14 @@ public:
PClass();
~PClass();
void InsertIntoHash();
DObject *CreateNew() const;
DObject *CreateNew();
PClass *CreateDerivedClass(FName name, unsigned int size);
PField *AddField(FName name, PType *type, DWORD flags=0) override;
void InitializeActorInfo();
void BuildFlatPointers();
void BuildArrayPointers();
void DestroySpecials(void *addr) const;
void InitMeta();
void DestroySpecials(void *addr);
const PClass *NativeClass() const;
// Returns true if this type is an ancestor of (or same as) the passed type.
@ -723,6 +728,11 @@ inline int &DObject::IntVar(FName field)
return *(int*)ScriptVar(field, TypeSInt32);
}
inline FSoundID &DObject::SoundVar(FName field)
{
return *(FSoundID*)ScriptVar(field, TypeSound);
}
inline PalEntry &DObject::ColorVar(FName field)
{
return *(PalEntry*)ScriptVar(field, TypeColor);
@ -738,6 +748,11 @@ inline double &DObject::FloatVar(FName field)
return *(double*)ScriptVar(field, TypeFloat64);
}
inline FString &DObject::StringVar(FName field)
{
return *(FString*)ScriptVar(field, TypeString);
}
template<class T>
inline T *&DObject::PointerVar(FName field)
{

View file

@ -360,7 +360,7 @@ struct FMapThing
double Alpha;
DWORD fillcolor;
DVector2 Scale;
int health;
double Health;
int score;
short pitch;
short roll;

View file

@ -50,8 +50,6 @@ DEFINE_FIELD(AInventory, DropTime)
DEFINE_FIELD(AInventory, SpawnPointClass)
DEFINE_FIELD(AInventory, PickupFlash)
DEFINE_FIELD(AInventory, PickupSound)
DEFINE_FIELD(AInventory, GiveQuest)
DEFINE_FIELD(PClassActor, PickupMsg)
//===========================================================================
//
@ -115,8 +113,7 @@ void AInventory::Serialize(FSerializer &arc)
("icon", Icon, def->Icon)
("pickupsound", PickupSound, def->PickupSound)
("spawnpointclass", SpawnPointClass, def->SpawnPointClass)
("droptime", DropTime, def->DropTime)
("givequest", GiveQuest, def->GiveQuest);
("droptime", DropTime, def->DropTime);
}
//===========================================================================

View file

@ -17,7 +17,7 @@ struct visstyle_t;
// A pickup is anything the player can pickup (i.e. weapons, ammo, powerups, etc)
enum
enum ItemFlag
{
IF_ACTIVATABLE = 1<<0, // can be activated
IF_ACTIVATED = 1<<1, // is currently activated
@ -46,8 +46,14 @@ enum
IF_TRANSFER = 1<<24, // All inventory items that the inventory item contains is also transfered to the pickuper
IF_NOTELEPORTFREEZE = 1<<25, // does not 'freeze' the player right after teleporting.
IF_NOSCREENBLINK = 1<<26, // Does not blink the screen overlay when expiring.
IF_ISHEALTH = 1<<27, // for the DM flag so that it can recognize items that are not obviously health givers.
IF_ISARMOR = 1<<28, // for the DM flag so that it can recognize items that are not obviously armor givers.
};
typedef TFlags<ItemFlag> InvFlags;
//typedef TFlags<ItemFlag2> ItemFlags2;
DEFINE_TFLAGS_OPERATORS(InvFlags)
//DEFINE_TFLAGS_OPERATORS(ItemFlags2)
class AInventory : public AActor
{
@ -87,10 +93,9 @@ public:
FTextureID Icon; // Icon to show on status bar or HUD
int DropTime; // Countdown after dropping
PClassActor *SpawnPointClass; // For respawning like Heretic's mace
int GiveQuest; // Optionally give one of the quest items.
FTextureID AltHUDIcon;
DWORD ItemFlags;
InvFlags ItemFlags;
PClassActor *PickupFlash; // actor to spawn as pickup flash
FSoundIDNoInit PickupSound;

View file

@ -1624,6 +1624,7 @@ void G_UnSnapshotLevel (bool hubLoad)
}
}
}
arc.Close();
}
// No reason to keep the snapshot around once the level's been entered.
level.info->Snapshot.Clean();

View file

@ -2744,7 +2744,7 @@ class CommandDrawBar : public SBarInfoCommand
max = 0;
}
else //default to the class's health
max = statusBar->CPlayer->mo->GetMaxHealth() + statusBar->CPlayer->mo->stamina;
max = statusBar->CPlayer->mo->GetMaxHealth(true);
break;
case ARMOR:
value = statusBar->armor != NULL ? statusBar->armor->Amount : 0;
@ -3251,7 +3251,7 @@ class CommandDrawGem : public SBarInfoCommand
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
{
goalValue = armor ? (statusBar->armor ? statusBar->armor->Amount : 0) : statusBar->CPlayer->mo->health;
int max = armor ? 100 : statusBar->CPlayer->mo->GetMaxHealth() + statusBar->CPlayer->mo->stamina;
int max = armor ? 100 : statusBar->CPlayer->mo->GetMaxHealth(true);
if(max != 0 && goalValue > 0)
{
goalValue = (goalValue*100)/max;

View file

@ -56,6 +56,7 @@ DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mBackButton)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mStatscreenMapNameFont)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mStatscreenEnteringFont)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mStatscreenFinishedFont)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, gibfactor)
const char *GameNames[17] =

View file

@ -204,7 +204,8 @@ void ADynamicLight::Activate(AActor *activator)
float pulseTime = specialf1 / TICRATE;
m_lastUpdate = level.maptime;
m_cycler.SetParams(float(args[LIGHT_SECONDARY_INTENSITY]), float(args[LIGHT_INTENSITY]), pulseTime);
if (!swapped) m_cycler.SetParams(float(args[LIGHT_SECONDARY_INTENSITY]), float(args[LIGHT_INTENSITY]), pulseTime);
else m_cycler.SetParams(float(args[LIGHT_INTENSITY]), float(args[LIGHT_SECONDARY_INTENSITY]), pulseTime);
m_cycler.ShouldCycle(true);
m_cycler.SetCycleType(CYCLE_Sin);
m_currentRadius = m_cycler.GetVal();

View file

@ -135,6 +135,16 @@ public:
void SetDontLightSelf(bool add) { m_dontlightself = add; }
void SetAttenuate(bool on) { m_attenuate = on; }
void SetHalo(bool halo) { m_halo = halo; }
void OrderIntensities()
{
if (m_Args[LIGHT_INTENSITY] > m_Args[LIGHT_SECONDARY_INTENSITY])
{
std::swap(m_Args[LIGHT_INTENSITY], m_Args[LIGHT_SECONDARY_INTENSITY]);
m_swapped = true;
}
}
protected:
FName m_Name;
int m_Args[5];
@ -143,6 +153,7 @@ protected:
ELightType m_type;
int8_t m_attenuate;
bool m_subtractive, m_additive, m_halo, m_dontlightself;
bool m_swapped = false;
};
TArray<FLightDefaults *> LightDefaults;
@ -178,8 +189,8 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const
light->SetOffset(m_Pos);
light->halo = m_halo;
for (int a = 0; a < 3; a++) light->args[a] = clamp<int>((int)(m_Args[a]), 0, 255);
light->args[LIGHT_INTENSITY] = int(m_Args[LIGHT_INTENSITY]);
light->args[LIGHT_SECONDARY_INTENSITY] = int(m_Args[LIGHT_SECONDARY_INTENSITY]);
light->args[LIGHT_INTENSITY] = m_Args[LIGHT_INTENSITY];
light->args[LIGHT_SECONDARY_INTENSITY] = m_Args[LIGHT_SECONDARY_INTENSITY];
light->flags4 &= ~(MF4_ADDITIVE | MF4_SUBTRACTIVE | MF4_DONTLIGHTSELF);
if (m_subtractive) light->flags4 |= MF4_SUBTRACTIVE;
if (m_additive) light->flags4 |= MF4_ADDITIVE;
@ -190,11 +201,13 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const
float pulseTime = float(m_Param / TICRATE);
light->m_lastUpdate = level.maptime;
light->m_cycler.SetParams(float(light->args[LIGHT_SECONDARY_INTENSITY]), float(light->args[LIGHT_INTENSITY]), pulseTime, oldtype == PulseLight);
if (m_swapped) light->m_cycler.SetParams(float(light->args[LIGHT_SECONDARY_INTENSITY]), float(light->args[LIGHT_INTENSITY]), pulseTime, oldtype == PulseLight);
else light->m_cycler.SetParams(float(light->args[LIGHT_INTENSITY]), float(light->args[LIGHT_SECONDARY_INTENSITY]), pulseTime, oldtype == PulseLight);
light->m_cycler.ShouldCycle(true);
light->m_cycler.SetCycleType(CYCLE_Sin);
light->m_currentRadius = light->m_cycler.GetVal();
if (light->m_currentRadius <= 0) light->m_currentRadius = 1;
light->swapped = m_swapped;
}
switch (m_attenuate)
@ -474,13 +487,7 @@ void gl_ParsePulseLight(FScanner &sc)
sc.ScriptError("Unknown tag: %s\n", sc.String);
}
}
if (defaults->GetArg(LIGHT_INTENSITY) > defaults->GetArg(LIGHT_SECONDARY_INTENSITY))
{
auto i = defaults->GetArg(LIGHT_INTENSITY);
auto j = defaults->GetArg(LIGHT_SECONDARY_INTENSITY);
defaults->SetArg(LIGHT_INTENSITY, j);
defaults->SetArg(LIGHT_SECONDARY_INTENSITY, i);
}
defaults->OrderIntensities();
gl_AddLightDefaults(defaults);
}
@ -564,6 +571,7 @@ void gl_ParseFlickerLight(FScanner &sc)
sc.ScriptError("Unknown tag: %s\n", sc.String);
}
}
defaults->OrderIntensities();
gl_AddLightDefaults(defaults);
}
else

View file

@ -141,6 +141,7 @@ public:
bool halo;
BYTE color2[3];
bool visibletoplayer;
bool swapped;
int bufferindex;

View file

@ -665,7 +665,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal)
bool isPicnumOverride = thing->picnum.isValid();
// Don't waste time projecting sprites that are definitely not visible.
if ((thing->sprite == 0 && !isPicnumOverride) || !thing->IsVisibleToPlayer() || !thing->IsInsideVisibleAngles())
if ((thing->sprite == 0 && !isPicnumOverride) || !thing->IsVisibleToPlayer() || ((thing->renderflags & RF_MASKROTATION) && !thing->IsInsideVisibleAngles()))
{
return;
}

View file

@ -251,20 +251,7 @@ PClassActor::PClassActor()
DamageFactors = NULL;
PainChances = NULL;
DeathHeight = -1;
BurnHeight = -1;
GibHealth = INT_MIN;
WoundHealth = 6;
FastSpeed = -1.;
RDFactor = 1.;
CameraHeight = INT_MIN;
DropItems = NULL;
DontHurtShooter = false;
ExplosionRadius = -1;
MeleeDamage = 0;
// Record this in the master list.
AllActorClasses.Push(this);
}
@ -308,32 +295,10 @@ void PClassActor::DeriveData(PClass *newclass)
PClassActor *newa = static_cast<PClassActor *>(newclass);
newa->DefaultStateUsage = DefaultStateUsage;
newa->Obituary = Obituary;
newa->HitObituary = HitObituary;
newa->DeathHeight = DeathHeight;
newa->BurnHeight = BurnHeight;
newa->BloodColor = BloodColor;
newa->GibHealth = GibHealth;
newa->WoundHealth = WoundHealth;
newa->FastSpeed = FastSpeed;
newa->RDFactor = RDFactor;
newa->CameraHeight = CameraHeight;
newa->HowlSound = HowlSound;
newa->BloodType = BloodType;
newa->BloodType2 = BloodType2;
newa->BloodType3 = BloodType3;
newa->distancecheck = distancecheck;
newa->DropItems = DropItems;
newa->DontHurtShooter = DontHurtShooter;
newa->ExplosionRadius = ExplosionRadius;
newa->ExplosionDamage = ExplosionDamage;
newa->MeleeDamage = MeleeDamage;
newa->MeleeSound = MeleeSound;
newa->MissileName = MissileName;
newa->MissileHeight = MissileHeight;
newa->VisibleToPlayerClass = VisibleToPlayerClass;
if (DamageFactors != NULL)
@ -350,7 +315,6 @@ void PClassActor::DeriveData(PClass *newclass)
}
// Inventory stuff
newa->PickupMsg = PickupMsg;
newa->ForbiddenToPlayerClass = ForbiddenToPlayerClass;
newa->RestrictedToPlayerClass = RestrictedToPlayerClass;
@ -866,7 +830,7 @@ void FMapInfoParser::ParseDamageDefinition()
dtd.DefaultFactor = sc.Float;
if (dtd.DefaultFactor == 0) dtd.ReplaceFactor = true;
}
if (sc.Compare("OBITUARY"))
else if (sc.Compare("OBITUARY"))
{
sc.MustGetStringName("=");
sc.MustGetString();

View file

@ -290,36 +290,11 @@ public:
TArray<PClassActor *> VisibleToPlayerClass;
FString Obituary; // Player was killed by this actor
FString HitObituary; // Player was killed by this actor in melee
double DeathHeight; // Height on normal death
double BurnHeight; // Height on burning death
PalEntry BloodColor; // Colorized blood
int GibHealth; // Negative health below which this monster dies an extreme death
int WoundHealth; // Health needed to enter wound state
double FastSpeed; // speed in fast mode
double RDFactor; // Radius damage factor
double CameraHeight; // Height of camera when used as such
FSoundID HowlSound; // Sound being played when electrocuted or poisoned
FName BloodType; // Blood replacement type
FName BloodType2; // Bloopsplatter replacement type
FName BloodType3; // AxeBlood replacement type
FDropItem *DropItems;
FString SourceLumpName;
FIntCVar *distancecheck;
// Old Decorate compatibility stuff
bool DontHurtShooter;
int ExplosionRadius;
int ExplosionDamage;
int MeleeDamage;
FSoundID MeleeSound;
FName MissileName;
double MissileHeight;
// These are only valid for inventory items.
FString PickupMsg;
TArray<PClassActor *> RestrictedToPlayerClass;
TArray<PClassActor *> ForbiddenToPlayerClass;

View file

@ -58,15 +58,15 @@ enum EScrollDir
};
// actions that don't create objects
#define WIPER_ID ((const PClass*)intptr_t(-1))
#define TITLE_ID ((const PClass*)intptr_t(-2))
#define WIPER_ID ((PClass*)intptr_t(-1))
#define TITLE_ID ((PClass*)intptr_t(-2))
//==========================================================================
struct FIntermissionAction
{
int mSize;
const PClass *mClass;
PClass *mClass;
FString mMusic;
int mMusicOrder;
int mCdTrack;

View file

@ -56,6 +56,22 @@
// writes some bytes to the network data stream, and the network code
// later calls us.
void cht_DoMDK(player_t *player, const char *mod)
{
if (player->mo == NULL)
{
Printf("What do you want to kill outside of a game?\n");
}
else if (!deathmatch)
{
// Don't allow this in deathmatch even with cheats enabled, because it's
// a very very cheap kill.
P_LineAttack(player->mo, player->mo->Angles.Yaw, PLAYERMISSILERANGE,
P_AimLineAttack(player->mo, player->mo->Angles.Yaw, PLAYERMISSILERANGE), TELEFRAG_DAMAGE,
mod, NAME_BulletPuff);
}
}
void cht_DoCheat (player_t *player, int cheat)
{
static const char * const BeholdPowers[9] =
@ -672,6 +688,7 @@ CCMD (mdk)
if (CheckCheatmode ())
return;
Net_WriteByte (DEM_GENERICCHEAT);
Net_WriteByte (CHT_MDK);
const char *name = argv.argc() > 1 ? argv[1] : "";
Net_WriteByte (DEM_MDK);
Net_WriteString(name);
}

View file

@ -32,6 +32,7 @@
class player_t;
class PClassActor;
void cht_DoMDK(player_t *player, const char *mod);
void cht_DoCheat (player_t *player, int cheat);
void cht_Give (player_t *player, const char *item, int amount=1);
void cht_Take (player_t *player, const char *item, int amount=1);

View file

@ -472,7 +472,7 @@ void M_SetMenu(FName menu, int param)
}
else
{
const PClass *menuclass = PClass::FindClass(menu);
PClass *menuclass = PClass::FindClass(menu);
if (menuclass != nullptr)
{
if (menuclass->IsDescendantOf("GenericMenu"))

View file

@ -867,6 +867,25 @@ static void ParseOptionMenu(FScanner &sc)
}
//=============================================================================
//
//
//
//=============================================================================
static void ParseAddOptionMenu(FScanner &sc)
{
sc.MustGetString();
DMenuDescriptor **pOld = MenuDescriptors.CheckKey(sc.String);
if (pOld == nullptr || *pOld == nullptr || !(*pOld)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor)))
{
sc.ScriptError("%s is not an option menu that can be extended", sc.String);
}
ParseOptionMenuBody(sc, (DOptionMenuDescriptor*)(*pOld));
}
//=============================================================================
//
//
@ -931,6 +950,10 @@ void M_ParseMenuDefs()
{
ParseOptionMenu(sc);
}
else if (sc.Compare("ADDOPTIONMENU"))
{
ParseAddOptionMenu(sc);
}
else if (sc.Compare("DEFAULTOPTIONMENU"))
{
ParseOptionMenuBody(sc, DefaultOptionMenuSettings);

View file

@ -46,7 +46,6 @@ xx(Shadow)
xx(Subtract)
xx(Subtractive)
xx(FillColor)
xx(HealthFactor)
// Healingradius types
xx(Mana)
@ -392,12 +391,19 @@ xx(Radius)
xx(ReactionTime)
xx(MeleeRange)
xx(Speed)
xx(FastSpeed)
xx(HowlSound)
xx(Clamp)
xx(VisibleStartAngle)
xx(VisibleStartPitch)
xx(VisibleEndAngle)
xx(VisibleEndPitch)
xx(Format)
xx(PickupMsg)
xx(Respawnable)
xx(ExplosionDamage)
xx(ExplosionRadius)
xx(DontHurtShooter)
// Various actor names which are used internally
xx(MapSpot)

View file

@ -1330,7 +1330,7 @@ static int CheckInventory (AActor *activator, const char *type, bool max)
if (max)
{
if (activator->IsKindOf (RUNTIME_CLASS (APlayerPawn)))
return static_cast<APlayerPawn *>(activator)->MaxHealth;
return static_cast<APlayerPawn *>(activator)->GetMaxHealth();
else
return activator->SpawnHealth();
}
@ -3939,7 +3939,7 @@ int DLevelScript::GetActorProperty (int tid, int property)
case APROP_Dormant: return !!(actor->flags2 & MF2_DORMANT);
case APROP_SpawnHealth: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn)))
{
return static_cast<APlayerPawn *>(actor)->MaxHealth;
return static_cast<APlayerPawn *>(actor)->GetMaxHealth();
}
else
{
@ -4232,7 +4232,7 @@ enum
SOUND_Howl,
};
static FSoundID GetActorSound(const AActor *actor, int soundtype)
static FSoundID GetActorSound(AActor *actor, int soundtype)
{
switch (soundtype)
{
@ -4245,7 +4245,7 @@ static FSoundID GetActorSound(const AActor *actor, int soundtype)
case SOUND_Bounce: return actor->BounceSound;
case SOUND_WallBounce: return actor->WallBounceSound;
case SOUND_CrushPain: return actor->CrushPainSound;
case SOUND_Howl: return actor->GetClass()->HowlSound;
case SOUND_Howl: return actor->SoundVar(NAME_HowlSound);
default: return 0;
}
}
@ -4367,6 +4367,10 @@ enum EACSFunctions
ACSF_SetTranslation,
ACSF_GetActorFloorTexture,
ACSF_GetActorFloorTerrain,
ACSF_StrArg,
ACSF_Floor,
ACSF_Round,
ACSF_Ceil,
// OpenGL stuff
@ -6087,7 +6091,17 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break;
}
case ACSF_StrArg:
return -FName(FBehavior::StaticLookupString(args[0]));
case ACSF_Floor:
return args[0] & ~0xffff;
case ACSF_Ceil:
return (args[0] & ~0xffff) + 0x10000;
case ACSF_Round:
return (args[0] + 32768) & ~0xffff;
default:
break;

View file

@ -86,7 +86,6 @@ AActor *SingleActorFromTID(int tid, AActor *defactor);
static FRandom pr_camissile ("CustomActorfire");
static FRandom pr_camelee ("CustomMelee");
static FRandom pr_cabullet ("CustomBullet");
static FRandom pr_cajump ("CustomJump");
static FRandom pr_cwbullet ("CustomWpBullet");
@ -439,22 +438,6 @@ DEFINE_ACTION_FUNCTION(AActor, GetSpawnHealth)
return 0;
}
//==========================================================================
//
// GetGibHealth
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, GetGibHealth)
{
if (numret > 0)
{
PARAM_SELF_PROLOGUE(AActor);
ret->SetInt(self->GetGibHealth());
return 1;
}
return 0;
}
//==========================================================================
//
// GetSpriteAngle
@ -924,86 +907,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_CopyFriendliness)
return 0;
}
//==========================================================================
//
// Customizable attack functions which use actor parameters.
//
//==========================================================================
static void DoAttack (AActor *self, bool domelee, bool domissile,
int MeleeDamage, FSoundID MeleeSound, PClassActor *MissileType,double MissileHeight)
{
if (self->target == NULL) return;
A_FaceTarget (self);
if (domelee && MeleeDamage>0 && self->CheckMeleeRange ())
{
int damage = pr_camelee.HitDice(MeleeDamage);
if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
}
else if (domissile && MissileType != NULL)
{
// This seemingly senseless code is needed for proper aiming.
double add = MissileHeight + self->GetBobOffset() - 32;
self->AddZ(add);
AActor *missile = P_SpawnMissileXYZ (self->PosPlusZ(32.), self, self->target, MissileType, false);
self->AddZ(-add);
if (missile)
{
// automatic handling of seeker missiles
if (missile->flags2&MF2_SEEKERMISSILE)
{
missile->tracer=self->target;
}
P_CheckMissileSpawn(missile, self->radius);
}
}
}
DEFINE_ACTION_FUNCTION(AActor, A_MeleeAttack)
{
PARAM_SELF_PROLOGUE(AActor);
int MeleeDamage = self->GetClass()->MeleeDamage;
FSoundID MeleeSound = self->GetClass()->MeleeSound;
DoAttack(self, true, false, MeleeDamage, MeleeSound, NULL, 0);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_MissileAttack)
{
PARAM_SELF_PROLOGUE(AActor);
PClassActor *MissileType = PClass::FindActor(self->GetClass()->MissileName);
DoAttack(self, false, true, 0, 0, MissileType, self->GetClass()->MissileHeight);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_ComboAttack)
{
PARAM_SELF_PROLOGUE(AActor);
int MeleeDamage = self->GetClass()->MeleeDamage;
FSoundID MeleeSound = self->GetClass()->MeleeSound;
PClassActor *MissileType = PClass::FindActor(self->GetClass()->MissileName);
DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, self->GetClass()->MissileHeight);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_BasicAttack)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT (melee_damage);
PARAM_SOUND (melee_sound);
PARAM_CLASS (missile_type, AActor);
PARAM_FLOAT (missile_height);
if (missile_type != NULL)
{
DoAttack(self, true, true, melee_damage, melee_sound, missile_type, missile_height);
}
return 0;
}
//==========================================================================
//
// Custom sound functions.
@ -1261,9 +1164,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_Explode)
if (damage < 0) // get parameters from metadata
{
damage = self->GetClass()->ExplosionDamage;
distance = self->GetClass()->ExplosionRadius;
flags = !self->GetClass()->DontHurtShooter;
damage = self->IntVar(NAME_ExplosionDamage);
distance = self->IntVar(NAME_ExplosionRadius);
flags = !self->BoolVar(NAME_DontHurtShooter);
alert = false;
}
if (distance <= 0) distance = damage;
@ -2677,8 +2580,7 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags)
else if (flags & SIXF_USEBLOODCOLOR)
{
// [XA] Use the spawning actor's BloodColor to translate the newly-spawned object.
PalEntry bloodcolor = self->GetBloodColor();
mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
mo->Translation = self->BloodTranslation;
}
}
if (flags & SIXF_TRANSFERPOINTERS)

View file

@ -583,3 +583,18 @@ bool EV_CeilingCrushStop (int tag, bool remove)
return rtn;
}
bool EV_StopCeiling(int tag)
{
FSectorTagIterator it(tag);
while (int sec = it.Next())
{
if (level.sectors[sec].ceilingdata)
{
SN_StopSequence(&level.sectors[sec], CHAN_CEILING);
level.sectors[sec].ceilingdata->Destroy();
level.sectors[sec].ceilingdata = nullptr;
}
}
return true;
}

View file

@ -3218,13 +3218,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_ActiveSound)
//---------------------------------------------------------------------------
void ModifyDropAmount(AInventory *inv, int dropamount)
{
int flagmask = IF_IGNORESKILL;
auto flagmask = IF_IGNORESKILL;
double dropammofactor = G_SkillProperty(SKILLP_DropAmmoFactor);
// Default drop amount is half of regular amount * regular ammo multiplication
if (dropammofactor == -1)
{
dropammofactor = 0.5;
flagmask = 0;
flagmask = ItemFlag(0);
}
if (dropamount > 0)

View file

@ -562,6 +562,21 @@ bool EV_FloorCrushStop (int tag)
return true;
}
// same as above but stops any floor mover that was active on the given sector.
bool EV_StopFloor(int tag)
{
FSectorTagIterator it(tag);
while (int sec = it.Next())
{
if (level.sectors[sec].floordata)
{
SN_StopSequence(&level.sectors[sec], CHAN_FLOOR);
level.sectors[sec].floordata->Destroy();
level.sectors[sec].floordata = nullptr;
}
}
return true;
}
//==========================================================================
//
// BUILD A STAIRCASE!

View file

@ -63,7 +63,6 @@
#include "g_levellocals.h"
#include "events.h"
static FRandom pr_obituary ("Obituary");
static FRandom pr_botrespawn ("BotRespawn");
static FRandom pr_killmobj ("ActorDie");
FRandom pr_damagemobj ("ActorTakeDamage");
@ -187,14 +186,11 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf
const char *message;
const char *messagename;
char gendermessage[1024];
int gender;
// No obituaries for non-players, voodoo dolls or when not wanted
if (self->player == NULL || self->player->mo != self || !show_obituaries)
return;
gender = self->player->userinfo.GetGender();
// Treat voodoo dolls as unknown deaths
if (inflictor && inflictor->player && inflictor->player->mo != inflictor)
MeansOfDeath = NAME_None;
@ -218,93 +214,48 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf
}
FString obit = DamageTypeDefinition::GetObituary(mod);
if (obit.IsNotEmpty()) messagename = obit;
if (attacker == nullptr) messagename = obit;
else
{
switch (mod)
{
case NAME_Suicide: messagename = "OB_SUICIDE"; break;
case NAME_Falling: messagename = "OB_FALLING"; break;
case NAME_Crush: messagename = "OB_CRUSH"; break;
case NAME_Exit: messagename = "OB_EXIT"; break;
case NAME_Drowning: messagename = "OB_WATER"; break;
case NAME_Slime: messagename = "OB_SLIME"; break;
case NAME_Fire: if (attacker == NULL) messagename = "OB_LAVA"; break;
case NAME_Suicide: message = "$OB_SUICIDE"; break;
case NAME_Falling: message = "$OB_FALLING"; break;
case NAME_Crush: message = "$OB_CRUSH"; break;
case NAME_Exit: message = "$OB_EXIT"; break;
case NAME_Drowning: message = "$OB_WATER"; break;
case NAME_Slime: message = "$OB_SLIME"; break;
case NAME_Fire: messagename = "$OB_LAVA"; break;
}
}
// Check for being killed by a voodoo doll.
if (inflictor && inflictor->player && inflictor->player->mo != inflictor)
{
messagename = "OB_VOODOO";
messagename = "$OB_VOODOO";
}
if (messagename != NULL)
message = GStrings(messagename);
if (attacker != NULL && message == NULL)
{
if (attacker == self)
{
message = GStrings("OB_KILLEDSELF");
message = "$OB_KILLEDSELF";
}
else if (attacker->player == NULL)
else
{
if (mod == NAME_Telefrag)
IFVIRTUALPTR(attacker, AActor, GetObituary)
{
message = GStrings("OB_MONTELEFRAG");
}
else if (mod == NAME_Melee && attacker->GetClass()->HitObituary.IsNotEmpty())
{
message = attacker->GetClass()->HitObituary;
}
else if (attacker->GetClass()->Obituary.IsNotEmpty())
{
message = attacker->GetClass()->Obituary;
VMValue params[] = { attacker, self, inflictor, mod.GetIndex(), !!(dmgflags & DMG_PLAYERATTACK) };
FString ret;
VMReturn rett(&ret);
GlobalVMStack.Call(func, params, countof(params), &rett, 1);
if (ret.IsNotEmpty()) message = ret;
}
}
}
if (message == NULL && attacker != NULL && attacker->player != NULL)
{
if (self->player != attacker->player && self->IsTeammate(attacker))
{
self = attacker;
gender = self->player->userinfo.GetGender();
mysnprintf (gendermessage, countof(gendermessage), "OB_FRIENDLY%c", '1' + (pr_obituary() & 3));
message = GStrings(gendermessage);
}
else
{
if (mod == NAME_Telefrag) message = GStrings("OB_MPTELEFRAG");
if (message == NULL)
{
if (inflictor != NULL && inflictor->GetClass()->Obituary.IsNotEmpty())
{
message = inflictor->GetClass()->Obituary;
}
if (message == NULL && (dmgflags & DMG_PLAYERATTACK) && attacker->player->ReadyWeapon != NULL)
{
message = attacker->player->ReadyWeapon->GetClass()->Obituary;
}
if (message == NULL)
{
switch (mod)
{
case NAME_BFGSplash: messagename = "OB_MPBFG_SPLASH"; break;
case NAME_Railgun: messagename = "OB_RAILGUN"; break;
}
if (messagename != NULL)
message = GStrings(messagename);
}
if (message == NULL)
{
message = attacker->GetClass()->Obituary;
}
}
}
}
else attacker = self; // for the message creation
if (message == nullptr) message = messagename; // fallback to defaults if possible.
if (attacker == nullptr) attacker = self; // world
if (attacker->player == nullptr) attacker = self; // for the message creation
if (message != NULL && message[0] == '$')
{
@ -320,7 +271,7 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf
if (message == NULL || strlen(message) <= 0)
return;
SexMessage (message, gendermessage, gender,
SexMessage (message, gendermessage, self->player->userinfo.GetGender(),
self->player->userinfo.GetName(), attacker->player->userinfo.GetName());
Printf (PRINT_MEDIUM, "%s\n", gendermessage);
}
@ -412,23 +363,11 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags)
}
flags6 |= MF6_KILLED;
// [RH] Allow the death height to be overridden using metadata.
double metaheight = -1;
if (DamageType == NAME_Fire)
IFVIRTUAL(AActor, GetDeathHeight)
{
metaheight = GetClass()->BurnHeight;
}
if (metaheight < 0)
{
metaheight = GetClass()->DeathHeight;
}
if (metaheight < 0)
{
Height *= 0.25;
}
else
{
Height = MAX<double> (metaheight, 0);
VMValue params[] = { (DObject*)this };
VMReturn ret(&Height);
GlobalVMStack.Call(func, params, 1, &ret, 1);
}
// [RH] If the thing has a special, execute and remove it
@ -1021,6 +960,11 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
}
return 0;
}
if (target == source && damage < TELEFRAG_DAMAGE)
{
damage = int(damage * target->SelfDamageFactor);
}
// [MC] Changed it to check rawdamage here for consistency, even though that doesn't actually do anything
// different here. At any rate, invulnerable is being checked before type factoring, which is then being
// checked by player cheats/invul/buddha followed by monster buddha. This is inconsistent. Don't let the
@ -1532,7 +1476,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
woundstate = target->FindState(NAME_Wound, mod);
if (woundstate != NULL)
{
int woundhealth = target->GetClass()->WoundHealth;
int woundhealth = target->WoundHealth;
if (target->health <= woundhealth)
{

View file

@ -563,6 +563,13 @@ FUNC(LS_Generic_Floor)
}
FUNC(LS_Floor_Stop)
// Floor_Stop (tag)
{
return EV_StopFloor(arg0);
}
FUNC(LS_Stairs_BuildDown)
// Stair_BuildDown (tag, speed, height, delay, reset)
{
@ -860,6 +867,13 @@ FUNC(LS_Ceiling_LowerByTexture)
return EV_DoCeiling (DCeiling::ceilLowerByTexture, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4));
}
FUNC(LS_Ceiling_Stop)
// Ceiling_Stop (tag)
{
return EV_StopCeiling(arg0);
}
FUNC(LS_Generic_Ceiling)
// Generic_Ceiling (tag, speed, height, target, change/model/direct/crush)
{
@ -3614,6 +3628,8 @@ static lnSpecFunc LineSpecials[] =
/* 272 */ LS_Stairs_BuildDownDoomSync,
/* 273 */ LS_Stairs_BuildUpDoomCrush,
/* 274 */ LS_Door_AnimatedClose,
/* 275 */ LS_Floor_Stop,
/* 276 */ LS_Ceiling_Stop,
};

View file

@ -2282,9 +2282,9 @@ bool P_TryMove(AActor *thing, const DVector2 &pos,
}
#endif
}
if (!(thing->flags & MF_TELEPORT) && !(thing->flags3 & MF3_FLOORHUGGER))
if (!(thing->flags & MF_TELEPORT) && (!(thing->flags3 & MF3_FLOORHUGGER) || thing->flags5 & MF5_NODROPOFF))
{
if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > thing->Z())
if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > thing->Z() && !(thing->flags3 & MF3_FLOORHUGGER))
{ // [RH] Don't let normal missiles climb steps
goto pushline;
}
@ -2773,13 +2773,13 @@ bool P_CheckMove(AActor *thing, const DVector2 &pos, int flags)
if (thing->Top() > tm.ceilingz)
return false;
}
if (!(thing->flags & MF_TELEPORT) && !(thing->flags3 & MF3_FLOORHUGGER))
if (!(thing->flags & MF_TELEPORT) && (!(thing->flags3 & MF3_FLOORHUGGER) || thing->flags5 & MF5_NODROPOFF))
{
if (tm.floorz - newz > thing->MaxStepHeight)
{ // too big a step up
return false;
}
else if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > newz)
else if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > newz && !(thing->flags3 & MF3_FLOORHUGGER))
{ // [RH] Don't let normal missiles climb steps
return false;
}
@ -4804,7 +4804,7 @@ void P_TraceBleed(int damage, const DVector3 &pos, AActor *actor, DAngle angle,
{
if (bleedtrace.HitType == TRACE_HitWall)
{
PalEntry bloodcolor = actor->GetBloodColor();
PalEntry bloodcolor = actor->BloodColor;
if (bloodcolor != 0)
{
bloodcolor.r >>= 1; // the full color is too bright for blood decals
@ -5681,7 +5681,7 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom
{
points = points * splashfactor;
}
points *= thing->GetClass()->RDFactor;
points *= thing->RadiusDamageFactor;
double check = int(points) * bombdamage;
// points and bombdamage should be the same sign (the double cast of 'points' is needed to prevent overflows and incorrect values slipping through.)
@ -5760,7 +5760,7 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom
dist = clamp<double>(dist - fulldamagedistance, 0, dist);
int damage = Scale(bombdamage, bombdistance - int(dist), bombdistance);
double factor = splashfactor * thing->GetClass()->RDFactor;
double factor = splashfactor * thing->RadiusDamageFactor;
damage = int(damage * factor);
if (damage > 0 || (bombspot->flags7 & MF7_FORCEZERORADIUSDMG))
{
@ -5995,7 +5995,6 @@ void P_DoCrunch(AActor *thing, FChangePosition *cpos)
{
if (!(thing->flags&MF_NOBLOOD))
{
PalEntry bloodcolor = thing->GetBloodColor();
PClassActor *bloodcls = thing->GetBloodType();
P_TraceBleed (newdam > 0 ? newdam : cpos->crushchange, thing);
@ -6007,9 +6006,9 @@ void P_DoCrunch(AActor *thing, FChangePosition *cpos)
mo->Vel.X = pr_crunch.Random2() / 16.;
mo->Vel.Y = pr_crunch.Random2() / 16.;
if (bloodcolor != 0 && !(mo->flags2 & MF2_DONTTRANSLATE))
if (thing->BloodTranslation != 0 && !(mo->flags2 & MF2_DONTTRANSLATE))
{
mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
mo->Translation = thing->BloodTranslation;
}
if (!(cl_bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE;
@ -6018,7 +6017,7 @@ void P_DoCrunch(AActor *thing, FChangePosition *cpos)
DAngle an = (M_Random() - 128) * (360./256);
if (cl_bloodtype >= 1)
{
P_DrawSplash2(32, thing->PosPlusZ(thing->Height/2), an, 2, bloodcolor);
P_DrawSplash2(32, thing->PosPlusZ(thing->Height/2), an, 2, thing->BloodColor);
}
}
if (thing->CrushPainSound != 0 && !S_GetSoundPlayingInfo(thing, thing->CrushPainSound))

View file

@ -312,28 +312,13 @@ DEFINE_FIELD(AActor, ConversationRoot)
DEFINE_FIELD(AActor, Conversation)
DEFINE_FIELD(AActor, DecalGenerator)
DEFINE_FIELD(AActor, fountaincolor)
DEFINE_FIELD(PClassActor, Obituary)
DEFINE_FIELD(PClassActor, HitObituary)
DEFINE_FIELD(PClassActor, DeathHeight)
DEFINE_FIELD(PClassActor, BurnHeight)
DEFINE_FIELD(PClassActor, BloodColor)
DEFINE_FIELD(PClassActor, GibHealth)
DEFINE_FIELD(PClassActor, WoundHealth)
DEFINE_FIELD(PClassActor, FastSpeed)
DEFINE_FIELD(PClassActor, RDFactor)
DEFINE_FIELD(PClassActor, CameraHeight)
DEFINE_FIELD(PClassActor, HowlSound)
DEFINE_FIELD(PClassActor, BloodType)
DEFINE_FIELD(PClassActor, BloodType2)
DEFINE_FIELD(PClassActor, BloodType3)
DEFINE_FIELD(PClassActor, DontHurtShooter)
DEFINE_FIELD(PClassActor, ExplosionRadius)
DEFINE_FIELD(PClassActor, ExplosionDamage)
DEFINE_FIELD(PClassActor, MeleeDamage)
DEFINE_FIELD(PClassActor, MeleeSound)
DEFINE_FIELD(PClassActor, MissileName)
DEFINE_FIELD(PClassActor, MissileHeight)
DEFINE_FIELD(AActor, CameraHeight)
DEFINE_FIELD(AActor, RadiusDamageFactor)
DEFINE_FIELD(AActor, SelfDamageFactor)
DEFINE_FIELD(AActor, StealthAlpha)
DEFINE_FIELD(AActor, WoundHealth)
DEFINE_FIELD(AActor, BloodColor)
DEFINE_FIELD(AActor, BloodTranslation)
//==========================================================================
//
@ -425,6 +410,8 @@ void AActor::Serialize(FSerializer &arc)
A("inventoryid", InventoryID)
A("floatbobphase", FloatBobPhase)
A("translation", Translation)
A("bloodcolor", BloodColor)
A("bloodtranslation", BloodTranslation)
A("seesound", SeeSound)
A("attacksound", AttackSound)
A("paimsound", PainSound)
@ -494,11 +481,17 @@ void AActor::Serialize(FSerializer &arc)
A("spriteangle", SpriteAngle)
A("spriterotation", SpriteRotation)
("alternative", alternative)
A("cameraheight", CameraHeight)
A("tag", Tag)
A("visiblestartangle",VisibleStartAngle)
A("visibleendangle",VisibleEndAngle)
A("visiblestartpitch",VisibleStartPitch)
A("visibleendpitch",VisibleEndPitch);
A("visibleendpitch",VisibleEndPitch)
A("woundhealth", WoundHealth)
A("rdfactor", RadiusDamageFactor)
A("selfdamagefactor", SelfDamageFactor)
A("stealthalpha", StealthAlpha);
}
#undef A
@ -1340,7 +1333,7 @@ bool P_GiveBody(AActor *actor, int num, int max)
// calls while supporting health pickups.
if (max <= 0)
{
max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->mo->stamina;
max = static_cast<APlayerPawn*>(actor)->GetMaxHealth(true);
// [MH] First step in predictable generic morph effects
if (player->morphTics)
{
@ -1348,7 +1341,7 @@ bool P_GiveBody(AActor *actor, int num, int max)
{
if (!(player->MorphStyle & MORPH_ADDSTAMINA))
{
max -= player->mo->stamina;
max -= player->mo->stamina + player->mo->BonusHealth;
}
}
else // old health behaviour
@ -1356,7 +1349,7 @@ bool P_GiveBody(AActor *actor, int num, int max)
max = MAXMORPHHEALTH;
if (player->MorphStyle & MORPH_ADDSTAMINA)
{
max += player->mo->stamina;
max += player->mo->stamina + player->mo->BonusHealth;
}
}
}
@ -1675,8 +1668,7 @@ bool AActor::Grind(bool items)
if (isgeneric) // Not a custom crush state, so colorize it appropriately.
{
S_Sound (this, CHAN_BODY, "misc/fallingsplat", 1, ATTN_IDLE);
PalEntry bloodcolor = GetBloodColor();
if (bloodcolor!=0) Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
Translation = BloodTranslation;
}
return false;
}
@ -1717,10 +1709,7 @@ bool AActor::Grind(bool items)
gib->Alpha = Alpha;
gib->Height = 0;
gib->radius = 0;
PalEntry bloodcolor = GetBloodColor();
if (bloodcolor != 0)
gib->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
gib->Translation = BloodTranslation;
}
S_Sound (this, CHAN_BODY, "misc/fallingsplat", 1, ATTN_IDLE);
}
@ -2959,7 +2948,7 @@ void P_ZMovement (AActor *mo, double oldfloorz)
mo->Vel.Z = 0;
return;
}
else if (mo->flags3 & MF3_FLOORHUGGER)
else if ((mo->flags3 & MF3_FLOORHUGGER) && !(mo->flags5 & MF5_NODROPOFF))
{ // Floor huggers can go up steps
return;
}
@ -3520,7 +3509,7 @@ int AActor::GetMissileDamage (int mask, int add)
void AActor::Howl ()
{
FSoundID howl = GetClass()->HowlSound;
FSoundID howl = IntVar(NAME_HowlSound);
if (!S_IsActorPlayingSomething(this, CHAN_BODY, howl))
{
S_Sound (this, CHAN_BODY, howl, 1, ATTN_NORM);
@ -3822,6 +3811,19 @@ void AActor::SetRoll(DAngle r, bool interpolate)
}
}
PClassActor *AActor::GetBloodType(int type) const
{
IFVIRTUAL(AActor, GetBloodType)
{
VMValue params[] = { (DObject*)this, type };
PClassActor *res;
VMReturn ret((void**)&res);
GlobalVMStack.Call(func, params, countof(params), &ret, 1);
return res;
}
return nullptr;
}
DVector3 AActor::GetPortalTransition(double byoffset, sector_t **pSec)
{
@ -4100,9 +4102,9 @@ void AActor::Tick ()
else if (visdir < 0)
{
Alpha -= 1.5/TICRATE;
if (Alpha < 0)
if (Alpha < StealthAlpha)
{
Alpha = 0;
Alpha = StealthAlpha;
visdir = 0;
}
}
@ -4823,8 +4825,11 @@ AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t a
actor->renderflags = (actor->renderflags & ~RF_FULLBRIGHT) | ActorRenderFlags::FromInt (st->GetFullbright());
actor->touching_sectorlist = nullptr; // NULL head of sector list // phares 3/13/98
actor->touching_rendersectors = nullptr;
if (G_SkillProperty(SKILLP_FastMonsters) && actor->GetClass()->FastSpeed >= 0)
actor->Speed = actor->GetClass()->FastSpeed;
if (G_SkillProperty(SKILLP_FastMonsters))
{
double f = actor->FloatVar(NAME_FastSpeed);
if (f >= 0) actor->Speed = f;
}
// set subsector and/or block links
actor->LinkToWorld (nullptr, SpawningMapThing);
@ -5304,6 +5309,7 @@ DEFINE_ACTION_FUNCTION(AActor, AdjustFloorClip)
//
EXTERN_CVAR (Bool, chasedemo)
EXTERN_CVAR(Bool, sv_singleplayerrespawn)
EXTERN_CVAR(Float, fov)
extern bool demonew;
@ -5441,7 +5447,7 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
mobj->sprite = Skins[p->userinfo.GetSkin()].sprite;
}
p->DesiredFOV = p->FOV = 90.f;
p->DesiredFOV = p->FOV = fov;
p->camera = p->mo;
p->playerstate = PST_LIVE;
p->refire = 0;
@ -5814,27 +5820,23 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
// [RH] Other things that shouldn't be spawned depending on dmflags
if (deathmatch || alwaysapplydmflags)
{
// Fixme: This needs to be done differently, it's quite broken.
if (dmflags & DF_NO_HEALTH)
if (i->IsDescendantOf(RUNTIME_CLASS(AInventory)))
{
if (i->IsDescendantOf (PClass::FindActor(NAME_Health)))
return NULL;
if (i->TypeName == NAME_Berserk)
return NULL;
if (i->TypeName == NAME_Megasphere)
return NULL;
}
if (dmflags & DF_NO_ITEMS)
{
// if (i->IsDescendantOf (RUNTIME_CLASS(AArtifact)))
// return;
}
if (dmflags & DF_NO_ARMOR)
{
if (i->IsDescendantOf (PClass::FindActor(NAME_Armor)))
return NULL;
if (i->TypeName == NAME_Megasphere)
return NULL;
auto it = static_cast<AInventory*>(GetDefaultByType(i));
if (dmflags & DF_NO_HEALTH)
{
if (it->ItemFlags & IF_ISHEALTH) return nullptr;
}
if (dmflags & DF_NO_ITEMS)
{
// if (i->IsDescendantOf (RUNTIME_CLASS(AArtifact)))
// return;
}
if (dmflags & DF_NO_ARMOR)
{
if (it->ItemFlags & IF_ISARMOR) return nullptr;
}
}
}
@ -5929,13 +5931,13 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
mobj->LevelSpawned ();
}
if (mthing->health > 0)
mobj->health *= mthing->health;
if (mthing->Health > 0)
mobj->health = int(mobj->health * mthing->Health);
else
mobj->health = -mthing->health;
if (mthing->health == 0)
mobj->health = -int(mthing->Health);
if (mthing->Health == 0)
mobj->CallDie(NULL, NULL);
else if (mthing->health != 1)
else if (mthing->Health != 1)
mobj->StartHealth = mobj->health;
return mobj;
@ -6008,7 +6010,7 @@ AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, const DVector3 &pos1
if (cl_pufftype && updown != 3 && (puff->flags4 & MF4_ALLOWPARTICLES))
{
P_DrawSplash2 (32, pos, particledir, updown, 1);
puff->renderflags |= RF_INVISIBLE;
if (cl_pufftype == 1) puff->renderflags |= RF_INVISIBLE;
}
if ((flags & PF_HITTHING) && puff->SeeSound)
@ -6048,7 +6050,6 @@ DEFINE_ACTION_FUNCTION(AActor, SpawnPuff)
void P_SpawnBlood (const DVector3 &pos1, DAngle dir, int damage, AActor *originator)
{
AActor *th;
PalEntry bloodcolor = originator->GetBloodColor();
PClassActor *bloodcls = originator->GetBloodType();
DVector3 pos = pos1;
pos.Z += pr_spawnblood.Random2() / 64.;
@ -6073,9 +6074,9 @@ void P_SpawnBlood (const DVector3 &pos1, DAngle dir, int damage, AActor *origina
th->tics = 1;
}
// colorize the blood
if (bloodcolor != 0 && !(th->flags2 & MF2_DONTTRANSLATE))
if (!(th->flags2 & MF2_DONTTRANSLATE))
{
th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
th->Translation = originator->BloodTranslation;
}
// Moved out of the blood actor so that replacing blood is easier
@ -6132,7 +6133,7 @@ void P_SpawnBlood (const DVector3 &pos1, DAngle dir, int damage, AActor *origina
}
if (bloodtype >= 1)
P_DrawSplash2 (40, pos, dir, 2, bloodcolor);
P_DrawSplash2 (40, pos, dir, 2, originator->BloodColor);
}
DEFINE_ACTION_FUNCTION(AActor, SpawnBlood)
@ -6156,7 +6157,6 @@ DEFINE_ACTION_FUNCTION(AActor, SpawnBlood)
void P_BloodSplatter (const DVector3 &pos, AActor *originator, DAngle hitangle)
{
PalEntry bloodcolor = originator->GetBloodColor();
PClassActor *bloodcls = originator->GetBloodType(1);
int bloodtype = cl_bloodtype;
@ -6175,16 +6175,16 @@ void P_BloodSplatter (const DVector3 &pos, AActor *originator, DAngle hitangle)
mo->Vel.Z = 3;
// colorize the blood!
if (bloodcolor!=0 && !(mo->flags2 & MF2_DONTTRANSLATE))
if (!(mo->flags2 & MF2_DONTTRANSLATE))
{
mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
mo->Translation = originator->BloodTranslation;
}
if (!(bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE;
}
if (bloodtype >= 1)
{
P_DrawSplash2 (40, pos, hitangle-180., 2, bloodcolor);
P_DrawSplash2 (40, pos, hitangle-180., 2, originator->BloodColor);
}
}
@ -6196,7 +6196,6 @@ void P_BloodSplatter (const DVector3 &pos, AActor *originator, DAngle hitangle)
void P_BloodSplatter2 (const DVector3 &pos, AActor *originator, DAngle hitangle)
{
PalEntry bloodcolor = originator->GetBloodColor();
PClassActor *bloodcls = originator->GetBloodType(2);
int bloodtype = cl_bloodtype;
@ -6217,16 +6216,16 @@ void P_BloodSplatter2 (const DVector3 &pos, AActor *originator, DAngle hitangle)
mo->target = originator;
// colorize the blood!
if (bloodcolor != 0 && !(mo->flags2 & MF2_DONTTRANSLATE))
if (!(mo->flags2 & MF2_DONTTRANSLATE))
{
mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
mo->Translation = originator->BloodTranslation;
}
if (!(bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE;
}
if (bloodtype >= 1)
{
P_DrawSplash2(40, pos + add, hitangle - 180., 2, bloodcolor);
P_DrawSplash2(40, pos + add, hitangle - 180., 2, originator->BloodColor);
}
}
@ -6252,7 +6251,6 @@ DEFINE_ACTION_FUNCTION(AActor, BloodSplatter)
void P_RipperBlood (AActor *mo, AActor *bleeder)
{
PalEntry bloodcolor = bleeder->GetBloodColor();
PClassActor *bloodcls = bleeder->GetBloodType();
double xo = pr_ripperblood.Random2() / 16.;
@ -6278,16 +6276,16 @@ void P_RipperBlood (AActor *mo, AActor *bleeder)
th->tics += pr_ripperblood () & 3;
// colorize the blood!
if (bloodcolor!=0 && !(th->flags2 & MF2_DONTTRANSLATE))
if (!(th->flags2 & MF2_DONTTRANSLATE))
{
th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
th->Translation = bleeder->BloodTranslation;
}
if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE;
}
if (bloodtype >= 1)
{
P_DrawSplash2(28, pos, bleeder->AngleTo(mo) + 180., 0, bloodcolor);
P_DrawSplash2(28, pos, bleeder->AngleTo(mo) + 180., 0, bleeder->BloodColor);
}
}
@ -6696,10 +6694,14 @@ static double GetDefaultSpeed(PClassActor *type)
{
if (type == NULL)
return 0;
else if (G_SkillProperty(SKILLP_FastMonsters) && type->FastSpeed >= 0)
return type->FastSpeed;
else
return GetDefaultByType(type)->Speed;
auto def = GetDefaultByType(type);
if (G_SkillProperty(SKILLP_FastMonsters))
{
double f = def->FloatVar(NAME_FastSpeed);
if (f >= 0) return f;
}
return def->Speed;
}
DEFINE_ACTION_FUNCTION(AActor, GetDefaultSpeed)
@ -7465,9 +7467,10 @@ void AActor::Crash()
{
FState *crashstate = NULL;
int gibh = GetGibHealth();
if (DamageType != NAME_None)
{
if (health < GetGibHealth())
if (health < gibh)
{ // Extreme death
FName labels[] = { NAME_Crash, NAME_Extreme, DamageType };
crashstate = FindState (3, labels, true);
@ -7479,7 +7482,7 @@ void AActor::Crash()
}
if (crashstate == NULL)
{
if (health < GetGibHealth())
if (health < gibh)
{ // Extreme death
crashstate = FindState(NAME_Crash, NAME_Extreme);
}
@ -7585,21 +7588,20 @@ void AActor::Revive()
int AActor::GetGibHealth() const
{
int gibhealth = GetClass()->GibHealth;
if (gibhealth != INT_MIN)
IFVIRTUAL(AActor, GetGibHealth)
{
return -abs(gibhealth);
}
else
{
return -int(SpawnHealth() * gameinfo.gibfactor);
VMValue params[] = { (DObject*)this };
int h;
VMReturn ret(&h);
GlobalVMStack.Call(func, params, 1, &ret, 1);
return h;
}
return -SpawnHealth();
}
double AActor::GetCameraHeight() const
{
return GetClass()->CameraHeight == INT_MIN ? Height / 2 : GetClass()->CameraHeight;
return CameraHeight == INT_MIN ? Height / 2 : CameraHeight;
}
DEFINE_ACTION_FUNCTION(AActor, GetCameraHeight)
@ -8278,9 +8280,9 @@ void PrintMiscActorInfo(AActor *query)
query->args[0], query->args[1], query->args[2], query->args[3],
query->args[4], query->special1, query->special2);
Printf("\nTID: %d", query->tid);
Printf("\nCoord= x: %f, y: %f, z:%f, floor:%f, ceiling:%f.",
Printf("\nCoord= x: %f, y: %f, z:%f, floor:%f, ceiling:%f, height= %f",
query->X(), query->Y(), query->Z(),
query->floorz, query->ceilingz);
query->floorz, query->ceilingz, query->Height);
Printf("\nSpeed= %f, velocity= x:%f, y:%f, z:%f, combined:%f.\n",
query->Speed, query->Vel.X, query->Vel.Y, query->Vel.Z, query->Vel.Length());
Printf("Scale: x:%f, y:%f\n", query->Scale.X, query->Scale.Y);

View file

@ -536,6 +536,7 @@ void P_FireWeapon (player_t *player, FState *state)
return;
}
player->WeaponState &= ~WF_WEAPONBOBBING;
player->mo->PlayAttacking ();
weapon->bAltFire = false;
if (state == nullptr)
@ -572,6 +573,7 @@ void P_FireWeaponAlt (player_t *player, FState *state)
return;
}
player->WeaponState &= ~WF_WEAPONBOBBING;
player->mo->PlayAttacking ();
weapon->bAltFire = true;

View file

@ -1742,7 +1742,7 @@ void P_LoadThings (MapData * map)
mti[i].ClassFilter = 0xffff; // Doom map format doesn't have class flags so spawn for all player classes
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].health = 1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
mti[i].pos.X = LittleShort(mt->x);
@ -1838,7 +1838,7 @@ void P_LoadThings2 (MapData * map)
mti[i].Gravity = 1;
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].health = 1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
}
delete[] mtp;

View file

@ -452,6 +452,7 @@ bool P_CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int t
bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, int tag, double speed, double speed2, double height, int crush, int silent, int change, DCeiling::ECrushMode hexencrush = DCeiling::ECrushMode::crushDoom);
bool EV_CeilingCrushStop (int tag, bool remove);
bool EV_StopCeiling(int tag);
void P_ActivateInStasisCeiling (int tag);
@ -565,6 +566,7 @@ bool EV_DoFloor(DFloor::EFloor floortype, line_t *line, int tag,
double speed, double height, int crush, int change, bool hexencrush, bool hereticlower = false);
bool EV_FloorCrushStop (int tag);
bool EV_StopFloor(int tag);
bool EV_DoDonut (int tag, line_t *line, double pillarspeed, double slimespeed);
class DElevator : public DMover

View file

@ -515,11 +515,10 @@ public:
FString arg0str, arg1str;
memset(th, 0, sizeof(*th));
double healthfactor = 1;
th->Gravity = 1;
th->RenderStyle = STYLE_Count;
th->Alpha = -1;
th->health = 1;
th->Health = 1;
th->FloatbobPhase = -1;
sc.MustGetToken('{');
while (!sc.CheckToken('}'))
@ -739,52 +738,38 @@ public:
break;
case NAME_Alpha:
CHECK_N(Zd | Zdt)
th->Alpha = CheckFloat(key);
break;
case NAME_FillColor:
CHECK_N(Zd | Zdt)
th->fillcolor = CheckInt(key);
break;
case NAME_Health:
CHECK_N(Zd | Zdt)
th->health = CheckInt(key);
break;
case NAME_HealthFactor:
CHECK_N(Zd | Zdt)
healthfactor = CheckFloat(key);
th->Health = CheckFloat(key);
break;
case NAME_Score:
CHECK_N(Zd | Zdt)
th->score = CheckInt(key);
break;
case NAME_Pitch:
CHECK_N(Zd | Zdt)
th->pitch = (short)CheckInt(key);
break;
case NAME_Roll:
CHECK_N(Zd | Zdt)
th->roll = (short)CheckInt(key);
break;
case NAME_ScaleX:
CHECK_N(Zd | Zdt)
th->Scale.X = CheckFloat(key);
break;
case NAME_ScaleY:
CHECK_N(Zd | Zdt)
th->Scale.Y = CheckFloat(key);
break;
case NAME_Scale:
CHECK_N(Zd | Zdt)
th->Scale.X = th->Scale.Y = CheckFloat(key);
break;
@ -808,7 +793,6 @@ public:
{
th->args[1] = -FName(arg1str);
}
th->health = int(th->health * healthfactor);
// Thing specials are only valid in namespaces with Hexen-type specials
// and in ZDoomTranslated - which will use the translator on them.
if (namespc == NAME_ZDoomTranslated)

View file

@ -89,6 +89,13 @@ CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00f, CVAR_ARCHIVE | CVAR_GLOBALCO
ColorSetList ColorSets;
PainFlashList PainFlashes;
// [Nash] FOV cvar setting
CUSTOM_CVAR(Float, fov, 90.f, CVAR_ARCHIVE | CVAR_USERINFO | CVAR_NOINITCALL)
{
player_t *p = &players[consoleplayer];
p->SetFOV(fov);
}
struct PredictPos
{
int gametic;
@ -551,6 +558,40 @@ int player_t::GetSpawnClass()
return static_cast<APlayerPawn*>(GetDefaultByType(type))->SpawnMask;
}
// [Nash] Set FOV
void player_t::SetFOV(float fov)
{
player_t *p = &players[consoleplayer];
if (p != nullptr && p->mo != nullptr)
{
if (dmflags & DF_NO_FOV)
{
if (consoleplayer == Net_Arbitrator)
{
Net_WriteByte(DEM_MYFOV);
}
else
{
Printf("A setting controller has disabled FOV changes.\n");
return;
}
}
else
{
Net_WriteByte(DEM_MYFOV);
}
Net_WriteByte((BYTE)clamp<float>(fov, 5.f, 179.f));
}
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, SetFOV)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
PARAM_FLOAT(fov);
self->SetFOV((float)fov);
return 0;
}
//===========================================================================
//
// EnumColorsets
@ -741,6 +782,7 @@ void APlayerPawn::Serialize(FSerializer &arc)
arc("jumpz", JumpZ, def->JumpZ)
("maxhealth", MaxHealth, def->MaxHealth)
("bonushealth", BonusHealth, def->BonusHealth)
("runhealth", RunHealth, def->RunHealth)
("spawnmask", SpawnMask, def->SpawnMask)
("forwardmove1", ForwardMove1, def->ForwardMove1)
@ -1310,15 +1352,18 @@ const char *APlayerPawn::GetSoundClass() const
//
//===========================================================================
int APlayerPawn::GetMaxHealth() const
int APlayerPawn::GetMaxHealth(bool withupgrades) const
{
return MaxHealth > 0? MaxHealth : ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth);
int ret = MaxHealth > 0? MaxHealth : ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth);
if (withupgrades) ret += stamina + BonusHealth;
return ret;
}
DEFINE_ACTION_FUNCTION(APlayerPawn, GetMaxHealth)
{
PARAM_SELF_PROLOGUE(APlayerPawn);
ACTION_RETURN_INT(self->GetMaxHealth());
PARAM_BOOL_DEF(withupgrades);
ACTION_RETURN_INT(self->GetMaxHealth(withupgrades));
}
//===========================================================================
@ -2829,7 +2874,7 @@ void P_PlayerThink (player_t *player)
// Apply degeneration.
if (dmflags2 & DF2_YES_DEGENERATION)
{
int maxhealth = player->mo->GetMaxHealth() + player->mo->stamina;
int maxhealth = player->mo->GetMaxHealth(true);
if ((level.time % TICRATE) == 0 && player->health > maxhealth)
{
if (player->health - 5 < maxhealth)
@ -3302,6 +3347,7 @@ bool P_IsPlayerTotallyFrozen(const player_t *player)
DEFINE_FIELD(APlayerPawn, crouchsprite)
DEFINE_FIELD(APlayerPawn, MaxHealth)
DEFINE_FIELD(APlayerPawn, BonusHealth)
DEFINE_FIELD(APlayerPawn, MugShotMaxHealth)
DEFINE_FIELD(APlayerPawn, RunHealth)
DEFINE_FIELD(APlayerPawn, PlayerFlags)
@ -3326,19 +3372,14 @@ DEFINE_FIELD(APlayerPawn, FlechetteType)
DEFINE_FIELD(APlayerPawn, DamageFade)
DEFINE_FIELD(APlayerPawn, ViewBob)
DEFINE_FIELD(APlayerPawn, FullHeight)
DEFINE_FIELD(APlayerPawn, HealingRadiusType)
DEFINE_FIELD(APlayerPawn, SoundClass)
DEFINE_FIELD(APlayerPawn, Face)
DEFINE_FIELD(APlayerPawn, Portrait)
DEFINE_FIELD(APlayerPawn, Slot)
DEFINE_FIELD(APlayerPawn, InvulMode)
DEFINE_FIELD(APlayerPawn, HexenArmor)
DEFINE_FIELD(APlayerPawn, ColorRangeStart)
DEFINE_FIELD(APlayerPawn, ColorRangeEnd)
DEFINE_FIELD(PClassActor, DisplayName)
DEFINE_FIELD_X(PlayerInfo, player_t, mo)
DEFINE_FIELD_X(PlayerInfo, player_t, playerstate)
DEFINE_FIELD_X(PlayerInfo, player_t, original_oldbuttons)

View file

@ -395,9 +395,10 @@ void MessagePump (const SDL_Event &sev)
{
event.type = EV_GUI_Event;
event.subtype = sev.type == SDL_KEYDOWN ? EV_GUI_KeyDown : EV_GUI_KeyUp;
event.data3 = ((sev.key.keysym.mod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
((sev.key.keysym.mod & KMOD_CTRL) ? GKM_CTRL : 0) |
((sev.key.keysym.mod & KMOD_ALT) ? GKM_ALT : 0);
SDL_Keymod kmod = SDL_GetModState();
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
((kmod & KMOD_ALT) ? GKM_ALT : 0);
if (event.subtype == EV_GUI_KeyDown)
{
@ -458,6 +459,7 @@ void MessagePump (const SDL_Event &sev)
event.type = EV_GUI_Event;
event.subtype = EV_GUI_Char;
event.data1 = sev.text.text[0];
event.data2 = !!(SDL_GetModState() & KMOD_ALT);
D_PostEvent (&event);
}
break;

View file

@ -218,8 +218,11 @@ bool FZipFile::Open(bool quiet)
char *dirptr = (char*)directory;
FZipLump *lump_p = Lumps;
// Check if all files have the same prefix so that this can be stripped out.
FString name0;
bool foundspeciallump = false;
// Check if all files have the same prefix so that this can be stripped out.
// This will only be done if there is either a MAPINFO, ZMAPINFO or GAMEINFO lump in the subdirectory, denoting a ZDoom mod.
if (NumLumps > 1) for (DWORD i = 0; i < NumLumps; i++)
{
FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr;
@ -251,6 +254,7 @@ bool FZipFile::Open(bool quiet)
!name.Compare("voxels/") ||
!name.Compare("colormaps/") ||
!name.Compare("acs/") ||
!name.Compare("maps/") ||
!name.Compare("voices/") ||
!name.Compare("patches/") ||
!name.Compare("graphics/") ||
@ -266,6 +270,23 @@ bool FZipFile::Open(bool quiet)
name0 = "";
break;
}
else if (!foundspeciallump)
{
// at least one of the more common definition lumps must be present.
if (name.IndexOf(name0 + "mapinfo") == 0) foundspeciallump = true;
else if (name.IndexOf(name0 + "zmapinfo") == 0) foundspeciallump = true;
else if (name.IndexOf(name0 + "gameinfo") == 0) foundspeciallump = true;
else if (name.IndexOf(name0 + "sndinfo") == 0) foundspeciallump = true;
else if (name.IndexOf(name0 + "sbarinfo") == 0) foundspeciallump = true;
else if (name.IndexOf(name0 + "menudef") == 0) foundspeciallump = true;
else if (name.IndexOf(name0 + "gldefs") == 0) foundspeciallump = true;
else if (name.IndexOf(name0 + "animdefs") == 0) foundspeciallump = true;
else if (name.IndexOf(name0 + "decorate.") == 0) foundspeciallump = true; // DECORATE is a common subdirectory name, so the check needs to be a bit different.
else if (name.Compare(name0 + "decorate") == 0) foundspeciallump = true;
else if (name.IndexOf(name0 + "zscript.") == 0) foundspeciallump = true; // same here.
else if (name.Compare(name0 + "zscript") == 0) foundspeciallump = true;
else if (name.Compare(name0 + "maps/") == 0) foundspeciallump = true;
}
}
}

View file

@ -6088,19 +6088,85 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx)
if (Object->ExprType == EFX_Identifier)
{
auto id = static_cast<FxIdentifier *>(Object)->Identifier;
// If the left side is a class name for a static member function call it needs to be resolved manually
// because the resulting value type would cause problems in nearly every other place where identifiers are being used.
ccls = FindStructType(static_cast<FxIdentifier *>(Object)->Identifier, ctx);
if (ccls != nullptr) static_cast<FxIdentifier *>(Object)->noglobal = true;
ccls = FindStructType(id, ctx);
if (ccls != nullptr)
{
static_cast<FxIdentifier *>(Object)->noglobal = true;
}
else
{
PType *type;
// Another special case to deal with here is constants assigned to non-struct types. The code below cannot deal with them so it needs to be done here explicitly.
// Thanks to the messed up search logic of the type system, which doesn't allow any search by type name for the basic types at all,
// we have to do this manually, though and check for all types that may have values attached explicitly.
// (What's the point of attached fields to types if you cannot even search for the types...???)
switch (id)
{
default:
type = nullptr;
break;
case NAME_Byte:
case NAME_uint8:
type = TypeUInt8;
break;
case NAME_sByte:
case NAME_int8:
type = TypeSInt8;
break;
case NAME_uShort:
case NAME_uint16:
type = TypeUInt16;
break;
case NAME_Short:
case NAME_int16:
type = TypeSInt16;
break;
case NAME_Int:
type = TypeSInt32;
break;
case NAME_uInt:
type = TypeUInt32;
break;
case NAME_Float:
type = TypeFloat32;
break;
case NAME_Double:
type = TypeFloat64;
break;
}
if (type != nullptr)
{
auto sym = type->Symbols.FindSymbol(Identifier, true);
if (sym != nullptr)
{
// non-struct symbols must be constant numbers and can only be defined internally.
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolConstNumeric)));
auto sn = static_cast<PSymbolConstNumeric*>(sym);
VMValue vmv;
if (sn->ValueType->IsKindOf(RUNTIME_CLASS(PInt))) vmv = sn->Value;
else vmv = sn->Float;
auto x = new FxConstant(sn->ValueType, vmv, ScriptPosition);
delete this;
return x->Resolve(ctx);
}
}
}
}
SAFE_RESOLVE(Object, ctx);
if (Identifier == FName("allmap"))
{
int a = 2;
}
// check for class or struct constants if the left side is a type name.
if (Object->ValueType == TypeError)
{
@ -6371,7 +6437,7 @@ ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build)
ExpEmit ob = obj->Emit(build);
ob.Free(build);
ExpEmit meta(build, REGT_POINTER);
build->Emit(OP_META, meta.RegNum, ob.RegNum);
build->Emit(OP_CLSS, meta.RegNum, ob.RegNum);
build->Emit(OP_LOS, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults)));
return meta;
@ -8832,7 +8898,7 @@ ExpEmit FxGetClass::Emit(VMFunctionBuilder *build)
ExpEmit op = Self->Emit(build);
op.Free(build);
ExpEmit to(build, REGT_POINTER);
build->Emit(OP_META, to.RegNum, op.RegNum);
build->Emit(OP_CLSS, to.RegNum, op.RegNum);
return to;
}
@ -8873,7 +8939,7 @@ ExpEmit FxGetParentClass::Emit(VMFunctionBuilder *build)
if (Self->IsObject())
{
ExpEmit to(build, REGT_POINTER);
build->Emit(OP_META, to.RegNum, op.RegNum);
build->Emit(OP_CLSS, to.RegNum, op.RegNum);
op = to;
op.Free(build);
}

View file

@ -846,6 +846,7 @@ void FFunctionBuildList::Build()
{
int errorcount = 0;
int codesize = 0;
int datasize = 0;
FILE *dump = nullptr;
if (Args->CheckParm("-dumpdisasm")) dump = fopen("disasm.txt", "w");
@ -927,6 +928,8 @@ void FFunctionBuildList::Build()
{
DumpFunction(dump, sfunc, item.PrintableName.GetChars(), (int)item.PrintableName.Len());
codesize += sfunc->CodeSize;
datasize += sfunc->LineInfoCount * sizeof(FStatementInfo) + sfunc->ExtraSpace + sfunc->NumKonstD * sizeof(int) +
sfunc->NumKonstA * sizeof(void*) + sfunc->NumKonstF * sizeof(double) + sfunc->NumKonstS * sizeof(FString);
}
sfunc->Unsafe = ctx.Unsafe;
}
@ -944,10 +947,11 @@ void FFunctionBuildList::Build()
}
if (dump != nullptr)
{
fprintf(dump, "\n*************************************************************************\n%i code bytes\n", codesize * 4);
fprintf(dump, "\n*************************************************************************\n%i code bytes\n%i data bytes", codesize * 4, datasize);
fclose(dump);
}
FScriptPosition::StrictErrors = false;
mItems.Clear();
mItems.ShrinkToFit();
FxAlloc.FreeAllBlocks();
}

View file

@ -223,7 +223,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns)
{
extra.DeathHeight = ((AActor*)(type->Defaults))->Height;
}
type->DeathHeight = extra.DeathHeight;
((AActor*)(type->Defaults))->FloatVar("DeathHeight") = extra.DeathHeight;
}
bag.statedef.SetStateLabel("Death", &type->OwnedStates[extra.DeathStart]);
}
@ -262,7 +262,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns)
}
if (extra.BurnHeight == 0) extra.BurnHeight = ((AActor*)(type->Defaults))->Height;
type->BurnHeight = extra.BurnHeight;
((AActor*)(type->Defaults))->FloatVar("BurnHeight") = extra.BurnHeight;
bag.statedef.SetStateLabel("Burn", &type->OwnedStates[extra.FireDeathStart]);
}
@ -445,18 +445,18 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
else if (def == DEF_Projectile && sc.Compare ("ExplosionRadius"))
{
sc.MustGetNumber ();
bag.Info->ExplosionRadius = sc.Number;
defaults->IntVar(NAME_ExplosionRadius) = sc.Number;
extra.bExplosive = true;
}
else if (def == DEF_Projectile && sc.Compare ("ExplosionDamage"))
{
sc.MustGetNumber ();
bag.Info->ExplosionDamage = sc.Number;
defaults->IntVar(NAME_ExplosionDamage) = sc.Number;
extra.bExplosive = true;
}
else if (def == DEF_Projectile && sc.Compare ("DoNotHurtShooter"))
{
bag.Info->DontHurtShooter = true;
defaults->BoolVar(NAME_DontHurtShooter) = true;
}
else if (def == DEF_Projectile && sc.Compare ("Damage"))
{
@ -541,11 +541,11 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
else if (def == DEF_Pickup && sc.Compare ("PickupMessage"))
{
sc.MustGetString ();
bag.Info->PickupMsg = sc.String;
inv->StringVar(NAME_PickupMsg) = sc.String;
}
else if (def == DEF_Pickup && sc.Compare ("Respawns"))
{
inv->BoolVar("Respawnable") = true;
inv->BoolVar(NAME_Respawnable) = true;
}
else if (def == DEF_BreakableDecoration && sc.Compare ("SolidOnDeath"))
{

View file

@ -827,7 +827,7 @@ static void DispatchScriptProperty(FScanner &sc, PProperty *prop, AActor *defaul
if (i > 0) sc.MustGetStringName(",");
if (f->Flags & VARF_Meta)
{
addr = ((char*)bag.Info) + f->Offset;
addr = ((char*)bag.Info->Meta) + f->Offset;
}
else
{
@ -867,7 +867,7 @@ static void DispatchScriptProperty(FScanner &sc, PProperty *prop, AActor *defaul
else if (f->Type->IsKindOf(RUNTIME_CLASS(PString)))
{
sc.MustGetString();
*(FString*)addr = sc.String;
*(FString*)addr = strbin1(sc.String);
}
else if (f->Type->IsKindOf(RUNTIME_CLASS(PClassPointer)))
{

View file

@ -430,6 +430,8 @@ static FFlagDef InventoryFlagDefs[] =
DEFINE_FLAG(IF, TRANSFER, AInventory, ItemFlags),
DEFINE_FLAG(IF, NOTELEPORTFREEZE, AInventory, ItemFlags),
DEFINE_FLAG(IF, NOSCREENBLINK, AInventory, ItemFlags),
DEFINE_FLAG(IF, ISARMOR, AInventory, ItemFlags),
DEFINE_FLAG(IF, ISHEALTH, AInventory, ItemFlags),
DEFINE_DUMMY_FLAG(FORCERESPAWNINSURVIVAL, false),

View file

@ -592,24 +592,13 @@ DEFINE_PROPERTY(health, I, Actor)
defaults->health=id;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(gibhealth, I, Actor)
{
PROP_INT_PARM(id, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->GibHealth = id;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(woundhealth, I, Actor)
{
PROP_INT_PARM(id, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->WoundHealth = id;
defaults->WoundHealth = id;
}
//==========================================================================
@ -888,16 +877,6 @@ DEFINE_PROPERTY(activesound, S, Actor)
defaults->ActiveSound = str;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(howlsound, S, Actor)
{
PROP_STRING_PARM(str, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->HowlSound = str;
}
//==========================================================================
//
//==========================================================================
@ -981,75 +960,6 @@ DEFINE_PROPERTY(alpha, F, Actor)
defaults->Alpha = id;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(obituary, S, Actor)
{
PROP_STRING_PARM(str, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->Obituary = str;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(hitobituary, S, Actor)
{
PROP_STRING_PARM(str, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->HitObituary = str;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(donthurtshooter, 0, Actor)
{
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->DontHurtShooter = true;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(explosionradius, I, Actor)
{
PROP_INT_PARM(id, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->ExplosionRadius = id;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(explosiondamage, I, Actor)
{
PROP_INT_PARM(id, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->ExplosionDamage = id;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(deathheight, F, Actor)
{
PROP_DOUBLE_PARM(h, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->DeathHeight = MAX(0., h);
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(burnheight, F, Actor)
{
PROP_DOUBLE_PARM(h, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->BurnHeight = MAX(0., h);
}
//==========================================================================
//
//==========================================================================
@ -1068,16 +978,6 @@ DEFINE_PROPERTY(meleethreshold, F, Actor)
defaults->meleethreshold = id;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(meleedamage, I, Actor)
{
PROP_INT_PARM(id, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->MeleeDamage = id;
}
//==========================================================================
//
//==========================================================================
@ -1087,36 +987,6 @@ DEFINE_PROPERTY(meleerange, F, Actor)
defaults->meleerange = id;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(meleesound, S, Actor)
{
PROP_STRING_PARM(str, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->MeleeSound = str;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(missiletype, S, Actor)
{
PROP_STRING_PARM(str, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->MissileName = str;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(missileheight, F, Actor)
{
PROP_DOUBLE_PARM(id, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->MissileHeight = id;
}
//==========================================================================
//
//==========================================================================
@ -1183,13 +1053,11 @@ DEFINE_PROPERTY(bloodcolor, C, Actor)
{
PROP_COLOR_PARM(color, 0);
PalEntry pe = color;
pe.a = CreateBloodTranslation(pe);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->BloodColor = pe;
defaults->BloodColor = color;
defaults->BloodColor.a = 255; // a should not be 0.
defaults->BloodTranslation = TRANSLATION(TRANSLATION_Blood, CreateBloodTranslation(color));
}
//==========================================================================
//
//==========================================================================
@ -1199,26 +1067,23 @@ DEFINE_PROPERTY(bloodtype, Sss, Actor)
PROP_STRING_PARM(str1, 1)
PROP_STRING_PARM(str2, 2)
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
PClassActor *ainfo = static_cast<PClassActor *>(info);
FName blood = str;
// normal blood
ainfo->BloodType = blood;
defaults->NameVar("BloodType") = blood;
if (PROP_PARM_COUNT > 1)
{
blood = str1;
}
// blood splatter
ainfo->BloodType2 = blood;
defaults->NameVar("BloodType2") = blood;
if (PROP_PARM_COUNT > 2)
{
blood = str2;
}
// axe blood
ainfo->BloodType3 = blood;
defaults->NameVar("BloodType3") = blood;
}
//==========================================================================
@ -1413,21 +1278,28 @@ DEFINE_PROPERTY(poisondamagetype, S, Actor)
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(fastspeed, F, Actor)
DEFINE_PROPERTY(radiusdamagefactor, F, Actor)
{
PROP_DOUBLE_PARM(i, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->FastSpeed = i;
defaults->RadiusDamageFactor = i;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(radiusdamagefactor, F, Actor)
DEFINE_PROPERTY(selfdamagefactor, F, Actor)
{
PROP_DOUBLE_PARM(i, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->RDFactor = i;
defaults->SelfDamageFactor = i;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(stealthalpha, F, Actor)
{
PROP_DOUBLE_PARM(i, 0);
defaults->StealthAlpha = i;
}
//==========================================================================
@ -1436,8 +1308,7 @@ DEFINE_PROPERTY(radiusdamagefactor, F, Actor)
DEFINE_PROPERTY(cameraheight, F, Actor)
{
PROP_DOUBLE_PARM(i, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->CameraHeight = i;
defaults->CameraHeight = i;
}
//==========================================================================
@ -1811,16 +1682,6 @@ DEFINE_CLASS_PROPERTY(pickupflash, S, Inventory)
defaults->PickupFlash = FindClassTentative(str, RUNTIME_CLASS(AActor));
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(pickupmessage, T, Inventory)
{
PROP_STRING_PARM(str, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->PickupMsg = str;
}
//==========================================================================
//
//==========================================================================
@ -1855,15 +1716,6 @@ DEFINE_CLASS_PROPERTY(usesound, S, Inventory)
defaults->UseSound = str;
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(givequest, I, Inventory)
{
PROP_INT_PARM(i, 0);
defaults->GiveQuest = i;
}
//==========================================================================
//
//==========================================================================
@ -2698,24 +2550,6 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, startitem, S_i, PlayerPawn)
bag.DropItemList = di;
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY_PREFIX(player, invulnerabilitymode, S, PlayerPawn)
{
PROP_STRING_PARM(str, 0);
defaults->InvulMode = str;
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY_PREFIX(player, healradiustype, S, PlayerPawn)
{
PROP_STRING_PARM(str, 0);
defaults->HealingRadiusType = str;
}
//==========================================================================
//
//==========================================================================

View file

@ -109,12 +109,18 @@ begin:
reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts.
NEXTOP;
OP(META):
OP(CLSS):
ASSERTA(a); ASSERTO(B);
reg.a[a] = ((DObject*)reg.a[B])->GetClass(); // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer...
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(META):
ASSERTA(a); ASSERTO(B);
reg.a[a] = ((DObject*)reg.a[B])->GetClass()->Meta; // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer...
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LB):
ASSERTD(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);

View file

@ -23,7 +23,8 @@ xx(LKF_R, lk, RFRII8, NOP, 0, 0), // load float constant indexed
xx(LKS_R, lk, RSRII8, NOP, 0, 0), // load string constant indexed
xx(LKP_R, lk, RPRII8, NOP, 0, 0), // load pointer constant indexed
xx(LFP, lf, LFP, NOP, 0, 0), // load frame pointer
xx(META, meta, RPRP, NOP, 0, 0), // load a class's meta class address
xx(META, meta, RPRP, NOP, 0, 0), // load a class's meta data address
xx(CLSS, clss, RPRP, NOP, 0, 0), // load a class's descriptor address
// Load from memory. rA = *(rB + rkC)
xx(LB, lb, RIRPKI, LB_R, 4, REGT_INT), // load byte

View file

@ -1007,6 +1007,11 @@ void ZCCCompiler::CompileAllFields()
type->Size = Classes[i]->Type()->ParentClass->Size;
}
}
if (Classes[i]->Type()->ParentClass)
type->MetaSize = Classes[i]->Type()->ParentClass->MetaSize;
else
type->MetaSize = 0;
if (CompileFields(type, Classes[i]->Fields, nullptr, &Classes[i]->TreeNodes, false, !!HasNativeChildren.CheckKey(type)))
{
// Remove from the list if all fields got compiled.
@ -1070,11 +1075,6 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (field->Flags & ZCC_Meta)
{
varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly
if (!(field->Flags & ZCC_Native))
{
// Non-native meta data is not implemented yet and requires some groundwork in the class copy code.
Error(field, "Metadata member %s must be native", FName(field->Names->Name).GetChars());
}
}
if (field->Type->ArraySize != nullptr)
@ -1095,22 +1095,28 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (varflags & VARF_Native)
{
auto querytype = (varflags & VARF_Meta) ? type->GetClass() : type;
fd = FindField(querytype, FName(name->Name).GetChars());
if (fd == nullptr)
if (varflags & VARF_Meta)
{
Error(field, "The member variable '%s.%s' has not been exported from the executable.", type->TypeName.GetChars(), FName(name->Name).GetChars());
Error(field, "Native meta variable %s not allowed", FName(name->Name).GetChars());
}
else if (thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0)
{
Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration. (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size);
}
// Q: Should we check alignment, too? A mismatch may be an indicator for bad assumptions.
else
{
// for bit fields the type must point to the source variable.
if (fd->BitValue != 0) thisfieldtype = fd->FieldSize == 1 ? TypeUInt8 : fd->FieldSize == 2 ? TypeUInt16 : TypeUInt32;
type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue);
fd = FindField(type, FName(name->Name).GetChars());
if (fd == nullptr)
{
Error(field, "The member variable '%s.%s' has not been exported from the executable.", type->TypeName.GetChars(), FName(name->Name).GetChars());
}
else if (thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0)
{
Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration. (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size);
}
// Q: Should we check alignment, too? A mismatch may be an indicator for bad assumptions.
else
{
// for bit fields the type must point to the source variable.
if (fd->BitValue != 0) thisfieldtype = fd->FieldSize == 1 ? TypeUInt8 : fd->FieldSize == 2 ? TypeUInt16 : TypeUInt32;
type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue);
}
}
}
else if (hasnativechildren)
@ -1188,8 +1194,10 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray<ZCC_Property *> &Proper
FString qualifiedname;
// Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen.
// All these will be removed from the symbol table after the compiler finishes to free up the allocated space.
if (prefix == NAME_None) qualifiedname.Format("@property@%s", FName(p->NodeName).GetChars());
else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), FName(p->NodeName).GetChars());
FName name = FName(p->NodeName);
if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars());
else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars());
fields.ShrinkToFit();
if (!type->Symbols.AddSymbol(new PProperty(qualifiedname, fields)))
{
@ -1692,7 +1700,7 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop
if (f->Flags & VARF_Meta)
{
addr = ((char*)bag.Info) + f->Offset;
addr = ((char*)bag.Info->Meta) + f->Offset;
}
else
{

View file

@ -281,6 +281,7 @@ static void ParseSingleFile(const char *filename, int lump, void *parser, ZCCPar
tokentype = ZCC_FLOATCONST;
break;
case TK_None: // 'NONE' is a token for SBARINFO but not here.
case TK_Identifier:
value.Int = FName(sc.String);
tokentype = ZCC_IDENTIFIER;

View file

@ -486,6 +486,8 @@ bool FSerializer::OpenReader(FCompressedBuffer *input)
void FSerializer::Close()
{
if (w == nullptr && r == nullptr) return; // double close? This should skip the I_Error at the bottom.
if (w != nullptr)
{
delete w;
@ -590,7 +592,7 @@ bool FSerializer::BeginObject(const char *name)
}
else
{
Printf(TEXTCOLOR_RED "Object expected for '%s'", name);
Printf(TEXTCOLOR_RED "Object expected for '%s'\n", name);
mErrors++;
return false;
}
@ -656,7 +658,7 @@ bool FSerializer::BeginArray(const char *name)
}
else
{
Printf(TEXTCOLOR_RED "Array expected for '%s'", name);
Printf(TEXTCOLOR_RED "Array expected for '%s'\n", name);
mErrors++;
return false;
}
@ -748,7 +750,7 @@ FSerializer &FSerializer::Args(const char *key, int *args, int *defargs, int spe
else
{
assert(false && "Integer expected");
Printf(TEXTCOLOR_RED "Integer expected for '%s[%d]'", key, i);
Printf(TEXTCOLOR_RED "Integer expected for '%s[%d]'\n", key, i);
mErrors++;
}
}
@ -756,7 +758,7 @@ FSerializer &FSerializer::Args(const char *key, int *args, int *defargs, int spe
else
{
assert(false && "array expected");
Printf(TEXTCOLOR_RED "array expected for '%s'", key);
Printf(TEXTCOLOR_RED "array expected for '%s'\n", key);
mErrors++;
}
}
@ -800,7 +802,7 @@ FSerializer &FSerializer::ScriptNum(const char *key, int &num)
else
{
assert(false && "Integer expected");
Printf(TEXTCOLOR_RED "Integer expected for '%s'", key);
Printf(TEXTCOLOR_RED "Integer expected for '%s'\n", key);
mErrors++;
}
}
@ -1005,7 +1007,7 @@ void FSerializer::ReadObjects(bool hubtravel)
PClass *cls = PClass::FindClass(clsname);
if (cls == nullptr)
{
Printf("Unknown object class '%s' in savegame", clsname.GetChars());
Printf(TEXTCOLOR_RED "Unknown object class '%s' in savegame\n", clsname.GetChars());
founderrors = true;
r->mDObjects[i] = RUNTIME_CLASS(AActor)->CreateNew(); // make sure we got at least a valid pointer for the duration of the loading process.
r->mDObjects[i]->Destroy(); // but we do not want to keep this around, so destroy it right away.
@ -1041,7 +1043,7 @@ void FSerializer::ReadObjects(bool hubtravel)
catch (CRecoverableError &err)
{
// In case something in here throws an error, let's continue and deal with it later.
Printf(TEXTCOLOR_RED "'%s'\n while restoring %s", err.GetMessage(), obj ? obj->GetClass()->TypeName.GetChars() : "invalid object");
Printf(TEXTCOLOR_RED "'%s'\n while restoring %s\n", err.GetMessage(), obj ? obj->GetClass()->TypeName.GetChars() : "invalid object");
mErrors++;
}
}
@ -1055,7 +1057,7 @@ void FSerializer::ReadObjects(bool hubtravel)
assert(!founderrors);
if (founderrors)
{
Printf(TEXTCOLOR_RED "Failed to restore all objects in savegame");
Printf(TEXTCOLOR_RED "Failed to restore all objects in savegame\n");
mErrors++;
}
}
@ -1064,7 +1066,7 @@ void FSerializer::ReadObjects(bool hubtravel)
// nuke all objects we created here.
for (auto obj : r->mDObjects)
{
obj->Destroy();
if (!(obj->ObjectFlags & OF_EuthanizeMe)) obj->Destroy();
}
r->mDObjects.Clear();
@ -1182,7 +1184,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *def
}
else
{
Printf(TEXTCOLOR_RED "boolean type expected for '%s'", key);
Printf(TEXTCOLOR_RED "boolean type expected for '%s'\n", key);
arc.mErrors++;
}
}
@ -1218,7 +1220,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value, int64_
}
else
{
Printf(TEXTCOLOR_RED "integer type expected for '%s'", key);
Printf(TEXTCOLOR_RED "integer type expected for '%s'\n", key);
arc.mErrors++;
}
}
@ -1254,7 +1256,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value, uint6
}
else
{
Printf(TEXTCOLOR_RED "integer type expected for '%s'", key);
Printf(TEXTCOLOR_RED "integer type expected for '%s'\n", key);
arc.mErrors++;
}
}
@ -1291,7 +1293,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, int32_t &value, int32_
}
else
{
Printf(TEXTCOLOR_RED "integer type expected for '%s'", key);
Printf(TEXTCOLOR_RED "integer type expected for '%s'\n", key);
arc.mErrors++;
}
}
@ -1327,7 +1329,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value, uint3
}
else
{
Printf(TEXTCOLOR_RED "integer type expected for '%s'", key);
Printf(TEXTCOLOR_RED "integer type expected for '%s'\n", key);
arc.mErrors++;
}
}
@ -1405,7 +1407,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double
}
else
{
Printf(TEXTCOLOR_RED "float type expected for '%s'", key);
Printf(TEXTCOLOR_RED "float type expected for '%s'\n", key);
arc.mErrors++;
}
}
@ -1548,7 +1550,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe
}
else
{
Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'", key);
Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'\n", key);
value.SetNull();
arc.mErrors++;
}
@ -1564,7 +1566,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe
else
{
assert(false && "not a texture");
Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'", key);
Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'\n", key);
value.SetNull();
arc.mErrors++;
}
@ -1649,7 +1651,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje
else
{
assert(false && "invalid object reference");
Printf(TEXTCOLOR_RED "Invalid object reference for '%s'", key);
Printf(TEXTCOLOR_RED "Invalid object reference for '%s'\n", key);
value = nullptr;
arc.mErrors++;
if (retcode) *retcode = false;
@ -1698,7 +1700,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *d
}
else
{
Printf(TEXTCOLOR_RED "String expected for '%s'", key);
Printf(TEXTCOLOR_RED "String expected for '%s'\n", key);
arc.mErrors++;
value = NAME_None;
}
@ -1746,7 +1748,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicCol
}
}
assert(false && "not a colormap");
Printf(TEXTCOLOR_RED "object does not represent a colormap for '%s'", key);
Printf(TEXTCOLOR_RED "object does not represent a colormap for '%s'\n", key);
cm = &NormalLight;
}
}
@ -1787,7 +1789,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI
}
else
{
Printf(TEXTCOLOR_RED "string type expected for '%s'", key);
Printf(TEXTCOLOR_RED "string type expected for '%s'\n", key);
sid = 0;
arc.mErrors++;
}
@ -1836,7 +1838,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor
}
else
{
Printf(TEXTCOLOR_RED "string type expected for '%s'", key);
Printf(TEXTCOLOR_RED "string type expected for '%s'\n", key);
clst = nullptr;
arc.mErrors++;
}
@ -1884,7 +1886,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&cl
}
else
{
Printf(TEXTCOLOR_RED "string type expected for '%s'", key);
Printf(TEXTCOLOR_RED "string type expected for '%s'\n", key);
clst = nullptr;
arc.mErrors++;
}
@ -1960,20 +1962,20 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState
{
// this can actually happen by changing the DECORATE so treat it as a warning, not an error.
state = nullptr;
Printf(TEXTCOLOR_ORANGE "Invalid state '%s+%d' for '%s'", cls.GetString(), ndx.GetInt(), key);
Printf(TEXTCOLOR_ORANGE "Invalid state '%s+%d' for '%s'\n", cls.GetString(), ndx.GetInt(), key);
}
}
else
{
assert(false && "not a state");
Printf(TEXTCOLOR_RED "data does not represent a state for '%s'", key);
Printf(TEXTCOLOR_RED "data does not represent a state for '%s'\n", key);
arc.mErrors++;
}
}
else if (!retcode)
{
assert(false && "not an array");
Printf(TEXTCOLOR_RED "array type expected for '%s'", key);
Printf(TEXTCOLOR_RED "array type expected for '%s'\n", key);
arc.mErrors++;
}
}
@ -2028,7 +2030,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial
}
else
{
Printf(TEXTCOLOR_RED "integer expected for '%s'", key);
Printf(TEXTCOLOR_RED "integer expected for '%s'\n", key);
arc.mErrors++;
node = nullptr;
}
@ -2077,7 +2079,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&p
}
else
{
Printf(TEXTCOLOR_RED "string expected for '%s'", key);
Printf(TEXTCOLOR_RED "string expected for '%s'\n", key);
pstr = nullptr;
arc.mErrors++;
}
@ -2119,7 +2121,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FString &pstr, FString
}
else
{
Printf(TEXTCOLOR_RED "string expected for '%s'", key);
Printf(TEXTCOLOR_RED "string expected for '%s'\n", key);
pstr = "";
arc.mErrors++;
}
@ -2168,7 +2170,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr
}
else
{
Printf(TEXTCOLOR_RED "string expected for '%s'", key);
Printf(TEXTCOLOR_RED "string expected for '%s'\n", key);
pstr = nullptr;
arc.mErrors++;
}

View file

@ -64,6 +64,7 @@ public:
~FSerializer()
{
mErrors = 0; // The destructor may not throw an exception so silence the error checker.
Close();
}
bool OpenWriter(bool pretty = true);

View file

@ -57,7 +57,7 @@ const char *GetVersionString();
// Version identifier for network games.
// Bump it every time you do a release unless you're certain you
// didn't change anything that will affect sync.
#define NETGAMEVERSION 232
#define NETGAMEVERSION 233
// Version stored in the ini's [LastRun] section.
// Bump it if you made some configuration change that you want to

View file

@ -393,7 +393,6 @@ void FString::Remove(size_t index, size_t remlen)
}
else
{
remlen = Len() - remlen < remlen ? Len() - remlen : remlen;
if (Data()->RefCount == 1)
{ // Can do this in place
memmove(Chars + index, Chars + index + remlen, Len() - index - remlen);

View file

@ -497,6 +497,12 @@ ABC4EB5A1535ECCD0061AD14F3547908 // Plutonia Experiment, map26
setsectorspecial 156 0
}
B68EB7CFB4CC481796E2919B9C16DFBD // Moc11.wad e1m6
{
setvertex 1650 -3072 2671
setvertex 1642 -2944 2671
}
712BB4CFBD0753178CA0C6814BE4C288 // map12 BTSX_E1 - patch some rendering glitches that are problematic to detect
{
setsectortag 545 32000

View file

@ -106,7 +106,6 @@ CMPTMNU_RENDERINGBEHAVIOR = "Rendering Behaviour";
CMPTMNU_SOUNDBEHAVIOR = "Sound Behaviour";
CMPTMNU_SECTORSOUNDS = "Sector sounds use centre as source";
OPTVAL_MAPDEFINEDCOLORSONLY = "Map defined colours only";
OPTVAL_NODOORS = "All except doors";
C_GRAY = "\ccgrey";
C_DARKGRAY = "\cudark grey";

View file

@ -2286,6 +2286,7 @@ OPTVAL_FRONT = "Front";
OPTVAL_ANIMATED = "Animated";
OPTVAL_ROTATED = "Rotated";
OPTVAL_MAPDEFINEDCOLORSONLY = "Map defined colors only";
OPTVAL_NODOORS = "All except doors";
OPTVAL_DOUBLE = "Double";
OPTVAL_TRIPLE = "Triple";
OPTVAL_QUADRUPLE = "Quadruple";

View file

@ -638,6 +638,7 @@ OptionValue PuffTypes
{
0.0, "$OPTVAL_SPRITES"
1.0, "$OPTVAL_PARTICLES"
2.0, "$OPTVAL_SPRITESPARTICLES"
}
OptionValue Wipes
@ -1013,7 +1014,7 @@ OptionValue MapBackTypes
OptionValue MapTriggers
{
0, "$OPTVAL_OFF"
1, "$OPTVAL_NO_DOORS"
1, "$OPTVAL_NODOORS"
2, "$OPTVAL_ON"
}

View file

@ -189,30 +189,49 @@ class Actor : Thinker native
native State MissileState;
native voidptr /*DecalBase*/ DecalGenerator;
native uint8 fountaincolor;
native meta String Obituary; // Player was killed by this actor
native meta String HitObituary; // Player was killed by this actor in melee
native meta double DeathHeight; // Height on normal death
native meta double BurnHeight; // Height on burning death
native meta color BloodColor; // Colorized blood
native meta int GibHealth; // Negative health below which this monster dies an extreme death
native meta int WoundHealth; // Health needed to enter wound state
native meta double FastSpeed; // speed in fast mode
native meta double RDFactor; // Radius damage factor
native meta double CameraHeight; // Height of camera when used as such
native meta Sound HowlSound; // Sound being played when electrocuted or poisoned
native meta Name BloodType; // Blood replacement type
native meta Name BloodType2; // Bloopsplatter replacement type
native meta Name BloodType3; // AxeBlood replacement type
native meta bool DontHurtShooter;
native meta int ExplosionRadius;
native meta int ExplosionDamage;
native meta int MeleeDamage;
native meta Sound MeleeSound;
native meta Name MissileName;
native meta double MissileHeight;
native double CameraHeight; // Height of camera when used as such
native double RadiusDamageFactor; // Radius damage factor
native double SelfDamageFactor;
native double StealthAlpha;
native int WoundHealth; // Health needed to enter wound state
native readonly color BloodColor;
native readonly int BloodTranslation;
meta String Obituary; // Player was killed by this actor
meta String HitObituary; // Player was killed by this actor in melee
meta double DeathHeight; // Height on normal death
meta double BurnHeight; // Height on burning death
meta int GibHealth; // Negative health below which this monster dies an extreme death
meta Sound HowlSound; // Sound being played when electrocuted or poisoned
meta Name BloodType; // Blood replacement type
meta Name BloodType2; // Bloopsplatter replacement type
meta Name BloodType3; // AxeBlood replacement type
meta bool DontHurtShooter;
meta int ExplosionRadius;
meta int ExplosionDamage;
meta int MeleeDamage;
meta Sound MeleeSound;
meta double MissileHeight;
meta Name MissileName;
meta double FastSpeed; // speed in fast mode
Property prefix: none;
Property Obituary: Obituary;
Property HitObituary: HitObituary;
Property MeleeDamage: MeleeDamage;
Property MeleeSound: MeleeSound;
Property MissileHeight: MissileHeight;
Property MissileType: MissileName;
Property DontHurtShooter: DontHurtShooter;
Property ExplosionRadius: ExplosionRadius;
Property ExplosionDamage: ExplosionDamage;
//Property BloodType: BloodType, BloodType2, BloodType3;
Property FastSpeed: FastSpeed;
Property HowlSound: HowlSound;
Property GibHealth: GibHealth;
Property DeathHeight: DeathHeight;
Property BurnHeight: BurnHeight;
// need some definition work first
//FRenderStyle RenderStyle;
//int ConversationRoot; // THe root of the current dialogue
@ -239,7 +258,7 @@ class Actor : Thinker native
Health DEFAULT_HEALTH;
Reactiontime 8;
Radius 20;
RenderRadius 0;
RenderRadius 0;
Height 16;
Mass 100;
RenderStyle 'Normal';
@ -271,6 +290,7 @@ class Actor : Thinker native
DefThreshold 100;
BloodType "Blood", "BloodSplatter", "AxeBlood";
ExplosionDamage 128;
ExplosionRadius -1; // i.e. use ExplosionDamage value
MissileHeight 32;
SpriteAngle 0;
SpriteRotation 0;
@ -278,6 +298,15 @@ class Actor : Thinker native
VisibleAngles 0, 0;
VisiblePitch 0, 0;
DefaultStateUsage SUF_ACTOR|SUF_OVERLAY;
CameraHeight int.min;
FastSpeed -1;
RadiusDamageFactor 1;
SelfDamageFactor 1;
StealthAlpha 0;
WoundHealth 6;
GibHealth int.min;
DeathHeight -1;
BurnHeight -1;
}
// Functions
@ -310,7 +339,7 @@ class Actor : Thinker native
virtual native void Touch(Actor toucher);
virtual native void MarkPrecacheSounds();
// Called by PIT_CheckThing to check if two actos actually can collide.
// Called by PIT_CheckThing to check if two actors actually can collide.
virtual bool CanCollideWith(Actor other, bool passive)
{
return true;
@ -334,6 +363,81 @@ class Actor : Thinker native
{
return false;
}
virtual class<Actor> GetBloodType(int type)
{
Class<Actor> bloodcls;
if (type == 0)
{
bloodcls = BloodType;
}
else if (type == 1)
{
bloodcls = BloodType2;
}
else if (type == 2)
{
bloodcls = BloodType3;
}
else
{
return NULL;
}
if (bloodcls != NULL)
{
bloodcls = GetReplacement(bloodcls);
}
return bloodcls;
}
virtual int GetGibHealth()
{
if (GibHealth != int.min)
{
return -abs(GibHealth);
}
else
{
return -int(GetSpawnHealth() * gameinfo.gibfactor);
}
}
virtual double GetDeathHeight()
{
// [RH] Allow the death height to be overridden using metadata.
double metaheight = -1;
if (DamageType == 'Fire')
{
metaheight = BurnHeight;
}
if (metaheight < 0)
{
metaheight = DeathHeight;
}
if (metaheight < 0)
{
return Height * 0.25;
}
else
{
return MAX(metaheight, 0);
}
}
virtual String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack)
{
if (mod == 'Telefrag')
{
return "$OB_MONTELEFRAG";
}
else if (mod == 'Melee' && HitObituary.Length() > 0)
{
return HitObituary;
}
return Obituary;
}
native static class<Actor> GetReplacement(class<Actor> cls);
native static class<Actor> GetReplacee(class<Actor> cls);
@ -510,7 +614,6 @@ class Actor : Thinker native
native double GetAngle(int flags, int ptr = AAPTR_TARGET);
native double GetZAt(double px = 0, double py = 0, double angle = 0, int flags = 0, int pick_pointer = AAPTR_DEFAULT);
native int GetSpawnHealth();
native int GetGibHealth();
native double GetCrouchFactor(int ptr = AAPTR_PLAYER1);
native double GetCVar(string cvar);
native int GetPlayerInput(int inputnum, int ptr = AAPTR_DEFAULT);
@ -724,6 +827,68 @@ class Actor : Thinker native
// Meh, MBF redundant functions. Only for DeHackEd support.
native bool A_LineEffect(int boomspecial = 0, int tag = 0);
// End of MBF redundant functions.
//==========================================================================
//
// old customizable attack functions which use actor parameters.
//
//==========================================================================
private void DoAttack (bool domelee, bool domissile, int MeleeDamage, Sound MeleeSound, Class<Actor> MissileType,double MissileHeight)
{
if (target == NULL) return;
A_FaceTarget ();
if (domelee && MeleeDamage>0 && CheckMeleeRange ())
{
int damage = random[CustomMelee](1, 8) * MeleeDamage;
if (MeleeSound) A_PlaySound (MeleeSound, CHAN_WEAPON);
int newdam = target.DamageMobj (self, self, damage, 'Melee');
target.TraceBleed (newdam > 0 ? newdam : damage, self);
}
else if (domissile && MissileType != NULL)
{
// This seemingly senseless code is needed for proper aiming.
double add = MissileHeight + GetBobOffset() - 32;
AddZ(add);
Actor missile = SpawnMissileXYZ (Pos + (0, 0, 32), target, MissileType, false);
AddZ(-add);
if (missile)
{
// automatic handling of seeker missiles
if (missile.bSeekerMissile)
{
missile.tracer = target;
}
missile.CheckMissileSpawn(radius);
}
}
}
deprecated void A_MeleeAttack()
{
DoAttack(true, false, MeleeDamage, MeleeSound, NULL, 0);
}
deprecated void A_MissileAttack()
{
Class<Actor> MissileType = MissileName;
DoAttack(false, true, 0, 0, MissileType, MissileHeight);
}
deprecated void A_ComboAttack()
{
Class<Actor> MissileType = MissileName;
DoAttack(true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight);
}
void A_BasicAttack(int melee_damage, sound melee_sound, class<actor> missile_type, double missile_height)
{
DoAttack(true, true, melee_damage, melee_sound, missile_type, missile_height);
}
native void A_MonsterRail();
native void A_Pain();
@ -753,9 +918,6 @@ class Actor : Thinker native
native void A_Wander(int flags = 0);
native void A_Look2();
deprecated native void A_MissileAttack();
deprecated native void A_MeleeAttack();
deprecated native void A_ComboAttack();
deprecated native void A_BulletAttack();
native void A_WolfAttack(int flags = 0, sound whattoplay = "weapons/pistol", double snipe = 1.0, int maxdamage = 64, int blocksize = 128, int pointblank = 2, int longrange = 4, double runspeed = 160.0, class<Actor> pufftype = "BulletPuff");
native void A_PlaySound(sound whattoplay = "weapons/pistol", int slot = CHAN_BODY, double volume = 1.0, bool looping = false, double attenuation = ATTN_NORM, bool local = false);
@ -793,7 +955,6 @@ class Actor : Thinker native
native void A_RaiseMaster(int flags = 0);
native void A_RaiseChildren(int flags = 0);
native void A_RaiseSiblings(int flags = 0);
deprecated native void A_BasicAttack(int meleedamage, sound meleesound, class<actor> missiletype, double missileheight);
action native bool, Actor A_ThrowGrenade(class<Actor> itemtype, double zheight = 0, double xyvel = 0, double zvel = 0, bool useammo = true);
native void A_Weave(int xspeed, int yspeed, double xdist, double ydist);
native bool A_Morph(class<Actor> type, int duration = 0, int flags = 0, class<Actor> enter_flash = null, class<Actor> exit_flash = null);

View file

@ -310,6 +310,7 @@ struct GameInfoStruct native
native GIFont mStatscreenMapNameFont;
native GIFont mStatscreenEnteringFont;
native GIFont mStatscreenFinishedFont;
native double gibfactor;
}
class Object native

View file

@ -1005,6 +1005,13 @@ enum ERaise
RF_NOCHECKPOSITION = 2
}
enum eFogParm
{
FOGP_DENSITY = 0,
FOGP_OUTSIDEDENSITY = 1,
FOGP_SKYFOG = 2,
}
enum ETeleport
{
TELF_DESTFOG = 1,

View file

@ -72,6 +72,8 @@ class Megasphere : CustomInventory
{
+COUNTITEM
+INVENTORY.ALWAYSPICKUP
+INVENTORY.ISHEALTH
+INVENTORY.ISARMOR
Inventory.PickupMessage "$GOTMSPHERE";
Inventory.PickupSound "misc/p_pkup";
}
@ -183,6 +185,7 @@ class Berserk : CustomInventory
{
+COUNTITEM
+INVENTORY.ALWAYSPICKUP
+INVENTORY.ISHEALTH
Inventory.PickupMessage "$GOTBERSERK";
Inventory.PickupSound "misc/p_pkup";
}

View file

@ -37,7 +37,7 @@ class Ammo : Inventory
{
int BackpackAmount;
int BackpackMaxAmount;
/*meta*/ int DropAmount;
meta int DropAmount;
property BackpackAmount: BackpackAmount;
property BackpackMaxAmount: BackpackMaxAmount;

View file

@ -38,6 +38,7 @@ class Armor : Inventory
Default
{
Inventory.PickupSound "misc/armor_pkup";
+INVENTORY.ISARMOR
}
}

View file

@ -36,13 +36,14 @@
class Health : Inventory
{
transient int PrevHealth;
/*meta*/ int LowHealth;
/*meta*/ String LowHealthMessage;
meta int LowHealth;
meta String LowHealthMessage;
property LowMessage: LowHealth, LowHealthMessage;
Default
{
+INVENTORY.ISHEALTH
Inventory.Amount 1;
Inventory.MaxAmount 0;
Inventory.PickupSound "misc/health_pkup";
@ -85,8 +86,36 @@ class Health : Inventory
}
return false;
}
}
class MaxHealth : Health
{
//===========================================================================
//
// TryPickup
//
//===========================================================================
override bool TryPickup (in out Actor other)
{
bool success = false;
int savedAmount = MaxAmount;
let player = PlayerPawn(other);
MaxAmount = Health;
if (player)
{
if (player.BonusHealth < savedAmount)
{
player.BonusHealth = min(player.BonusHealth + Amount, savedAmount);
success = true;
}
MaxAmount += player.BonusHealth;
}
success |= Super.TryPickup(other);
MaxAmount = savedAmount;
if (success) GoAwayAndDie();
return success;
}
}
class HealthPickup : Inventory
@ -99,6 +128,7 @@ class HealthPickup : Inventory
{
Inventory.DefMaxAmount;
+INVENTORY.INVBAR
+INVENTORY.ISHEALTH
}
//===========================================================================

View file

@ -87,8 +87,8 @@ class MapRevealer : Inventory
class PuzzleItem : Inventory
{
/*meta*/ int PuzzleItemNumber;
/*meta*/ String PuzzFailMessage;
meta int PuzzleItemNumber;
meta String PuzzFailMessage;
property Number: PuzzleItemNumber;
property FailMessage: PuzzFailMessage;

View file

@ -23,8 +23,11 @@ class Inventory : Actor native
native bool bPickupGood;
native bool bCreateCopyMoved;
native bool bInitEffectFailed;
native meta String PickupMsg;
native /*meta*/ int GiveQuest;
meta String PickupMsg;
meta int GiveQuest;
Property PickupMessage: PickupMsg;
Property GiveQuest: GiveQuest;
Default
{

View file

@ -33,7 +33,7 @@ class Weapon : StateProvider native
native float FOVScale;
native int Crosshair; // 0 to use player's crosshair
native bool GivenAsMorphWeapon;
native bool bAltFire; // Set when self weapon's alternate fire is used.
native bool bAltFire; // Set when this weapon's alternate fire is used.
native readonly bool bDehAmmo;
Default
@ -89,6 +89,12 @@ class Weapon : StateProvider native
return s;
}
override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack)
{
// Weapons may never return HitObituary by default. Override this if it is needed.
return Obituary;
}
action void A_GunFlash(statelabel flashlabel = null, int flags = 0)
{
let player = player;

View file

@ -3,23 +3,20 @@ class PlayerPawn : Actor native
native int crouchsprite;
native int MaxHealth;
native int BonusHealth;
native int MugShotMaxHealth;
native int RunHealth;
native int PlayerFlags;
native Inventory InvFirst; // first inventory item displayed on inventory bar
native Inventory InvSel; // selected inventory item
native meta String DisplayName; // Display name (used in menus, etc.)
native Name SoundClass; // Sound class
native Name Face; // Doom status bar face (when used)
native Name Portrait;
native Name Slot[10];
native double HexenArmor[5];
native uint8 ColorRangeStart; // Skin color range
native uint8 ColorRangeEnd;
native /*meta*/ Name SoundClass; // Sound class
native /*meta*/ Name Face; // Doom status bar face (when used)
native /*meta*/ Name Portrait;
native /*meta*/ Name Slot[10];
native /*meta*/ Name InvulMode;
native /*meta*/ Name HealingRadiusType;
native /*meta*/ double HexenArmor[5];
native /*meta*/ uint8 ColorRangeStart; // Skin color range
native /*meta*/ uint8 ColorRangeEnd;
// [GRB] Player class properties
native double JumpZ;
native double GruntSpeed;
@ -37,6 +34,13 @@ class PlayerPawn : Actor native
native color DamageFade; // [CW] Fades for when you are being damaged.
native double ViewBob; // [SP] ViewBob Multiplier
native double FullHeight;
meta Name HealingRadiusType;
meta Name InvulMode;
Property prefix: Player;
Property HealRadiusType: HealingradiusType;
Property InvulnerabilityMode: InvulMode;
Default
{
@ -115,6 +119,36 @@ class PlayerPawn : Actor native
}
}
override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack)
{
if (victim.player != player && victim.IsTeammate(self))
{
victim = self;
return String.Format("$OB_FRIENDLY%c", random[Obituary](49, 53));
}
else
{
if (mod == 'Telefrag') return "$OB_MPTELEFRAG";
String message;
if (inflictor != NULL)
{
message = inflictor.GetObituary(victim, inflictor, mod, playerattack);
}
if (message.Length() == 0 && playerattack && player.ReadyWeapon != NULL)
{
message = player.ReadyWeapon.GetObituary(victim, inflictor, mod, playerattack);
}
if (message.Length() == 0)
{
if (mod == 'BFGSplash') return "$OB_MPBFG_SPLASH";
if (mod == 'Railgun') return "$OB_RAILGUN";
message = Obituary;
}
return message;
}
}
// This is for SBARINFO.
int, int GetEffectTicsForItem(class<Inventory> item)
{
@ -133,7 +167,7 @@ class PlayerPawn : Actor native
return -1, -1;
}
native int GetMaxHealth();
native int GetMaxHealth(bool withupgrades = false);
native bool ResetAirSupply (bool playgasp = false);
native void CheckWeaponSwitch(class<Inventory> item);
native static String GetPrintableDisplayName(Class<Actor> cls);
@ -320,6 +354,7 @@ usercmd_t original_cmd;
native int GetGender();
native int GetTeam();
native float GetAutoaim();
native void SetFOV(float fov);
}
struct PlayerClass native

View file

@ -76,7 +76,7 @@ extend class PlayerPawn
}
else
{
player.health = health = GetMaxHealth();
player.health = health = GetMaxHealth(true);
}
}

View file

@ -95,7 +95,7 @@ class Programmer : Actor
A_PlaySound("programmer/clank", CHAN_WEAPON);
int damage = ((random[Programmer]() % 10) + 1) * 6;
int newdam = DamageMobj (self, self, damage, 'Melee');
int newdam = target.DamageMobj (self, self, damage, 'Melee');
target.TraceBleed (newdam > 0 ? newdam : damage, self);
}