Merge branch 'meta'

This commit is contained in:
Christoph Oelckers 2017-02-28 14:47:00 +01:00
commit 314a642527
36 changed files with 633 additions and 685 deletions

View file

@ -817,32 +817,7 @@ public:
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)
{
@ -1048,7 +1023,13 @@ public:
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

View file

@ -159,8 +159,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;

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 *****************************************************************/
@ -2892,6 +2888,7 @@ PClass::PClass()
bExported = false;
bDecorateClass = false;
ConstructNative = nullptr;
Meta = nullptr;
mDescriptiveName = "Class";
PClass::AllClasses.Push(this);
@ -2910,6 +2907,11 @@ PClass::~PClass()
M_Free(Defaults);
Defaults = nullptr;
}
if (Meta != nullptr)
{
M_Free(Meta);
Meta = nullptr;
}
}
//==========================================================================
@ -3047,7 +3049,7 @@ PClass *PClass::FindClass (FName zaname)
//
//==========================================================================
DObject *PClass::CreateNew() const
DObject *PClass::CreateNew()
{
BYTE *mem = (BYTE *)M_Malloc (Size);
assert (mem != nullptr);
@ -3064,7 +3066,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 +3078,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 +3102,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 +3161,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 +3174,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 +3280,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 +3297,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 +3338,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;
}
//==========================================================================
@ -3313,6 +3397,7 @@ PClass *PClass::FindClassTentative(FName name)
PClass *type = static_cast<PClass *>(GetClass()->CreateNew());
DPrintf(DMSG_SPAMMY, "Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars());
assert(MetaSize == 0);
Derive(type, name);
type->Size = TentativeClass;
TypeTable.AddType(type, RUNTIME_CLASS(PClass), 0, name, bucket);

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

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

@ -87,7 +87,6 @@ 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;

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

@ -251,22 +251,7 @@ PClassActor::PClassActor()
DamageFactors = NULL;
PainChances = NULL;
DeathHeight = -1;
BurnHeight = -1;
GibHealth = INT_MIN;
WoundHealth = 6;
FastSpeed = -1.;
RDFactor = 1.;
SelfDamageFactor = 1.;
StealthAlpha = 0.;
CameraHeight = INT_MIN;
DropItems = NULL;
DontHurtShooter = false;
ExplosionRadius = -1;
MeleeDamage = 0;
// Record this in the master list.
AllActorClasses.Push(this);
}
@ -310,34 +295,11 @@ 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->SelfDamageFactor = SelfDamageFactor;
newa->StealthAlpha = StealthAlpha;
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)
@ -354,7 +316,6 @@ void PClassActor::DeriveData(PClass *newclass)
}
// Inventory stuff
newa->PickupMsg = PickupMsg;
newa->ForbiddenToPlayerClass = ForbiddenToPlayerClass;
newa->RestrictedToPlayerClass = RestrictedToPlayerClass;

View file

@ -290,38 +290,13 @@ 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 SelfDamageFactor;
double CameraHeight; // Height of camera when used as such
double StealthAlpha; // Minmum alpha for MF_STEALTH.
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

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

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

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

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;

View file

@ -62,7 +62,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");
@ -186,14 +185,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;
@ -217,93 +213,47 @@ 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->player == nullptr) attacker = self; // for the message creation
if (message != NULL && message[0] == '$')
{
@ -319,7 +269,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);
}
@ -411,23 +361,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
@ -1022,7 +960,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
}
if (target == source && damage < TELEFRAG_DAMAGE)
{
damage = int(damage * target->GetClass()->SelfDamageFactor);
damage = int(damage * target->SelfDamageFactor);
}
// [MC] Changed it to check rawdamage here for consistency, even though that doesn't actually do anything
@ -1536,7 +1474,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

@ -5680,7 +5680,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.)
@ -5759,7 +5759,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))
{

View file

@ -311,30 +311,13 @@ DEFINE_FIELD(AActor, ConversationRoot)
DEFINE_FIELD(AActor, Conversation)
DEFINE_FIELD(AActor, DecalGenerator)
DEFINE_FIELD(AActor, fountaincolor)
DEFINE_FIELD(AActor, CameraHeight)
DEFINE_FIELD(AActor, RadiusDamageFactor)
DEFINE_FIELD(AActor, SelfDamageFactor)
DEFINE_FIELD(AActor, StealthAlpha)
DEFINE_FIELD(AActor, WoundHealth)
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, SelfDamageFactor)
DEFINE_FIELD(PClassActor, StealthAlpha)
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(PClassActor, BloodColor)
//==========================================================================
//
@ -495,11 +478,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
@ -3521,7 +3510,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);
@ -3823,6 +3812,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)
{
@ -4101,9 +4103,9 @@ void AActor::Tick ()
else if (visdir < 0)
{
Alpha -= 1.5/TICRATE;
if (Alpha < GetClass()->StealthAlpha)
if (Alpha < StealthAlpha)
{
Alpha = GetClass()->StealthAlpha;
Alpha = StealthAlpha;
visdir = 0;
}
}
@ -4824,8 +4826,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);
@ -6698,10 +6703,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)
@ -7467,9 +7476,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);
@ -7481,7 +7491,7 @@ void AActor::Crash()
}
if (crashstate == NULL)
{
if (health < GetGibHealth())
if (health < gibh)
{ // Extreme death
crashstate = FindState(NAME_Crash, NAME_Extreme);
}
@ -7587,21 +7597,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)
@ -8280,9 +8289,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

@ -3366,19 +3366,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

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

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

@ -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;
}
//==========================================================================
//
//==========================================================================
@ -1190,37 +1060,6 @@ DEFINE_PROPERTY(bloodcolor, C, Actor)
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(bloodtype, Sss, Actor)
{
PROP_STRING_PARM(str, 0)
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;
if (PROP_PARM_COUNT > 1)
{
blood = str1;
}
// blood splatter
ainfo->BloodType2 = blood;
if (PROP_PARM_COUNT > 2)
{
blood = str2;
}
// axe blood
ainfo->BloodType3 = blood;
}
//==========================================================================
//
//==========================================================================
@ -1410,24 +1249,13 @@ DEFINE_PROPERTY(poisondamagetype, S, Actor)
defaults->PoisonDamageType = poisondamagetype;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(fastspeed, F, Actor)
{
PROP_DOUBLE_PARM(i, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->FastSpeed = i;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(radiusdamagefactor, F, Actor)
{
PROP_DOUBLE_PARM(i, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->RDFactor = i;
defaults->RadiusDamageFactor = i;
}
//==========================================================================
@ -1436,8 +1264,7 @@ 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)->SelfDamageFactor = i;
defaults->SelfDamageFactor = i;
}
//==========================================================================
@ -1446,8 +1273,7 @@ DEFINE_PROPERTY(selfdamagefactor, F, Actor)
DEFINE_PROPERTY(stealthalpha, F, Actor)
{
PROP_DOUBLE_PARM(i, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassActor)));
static_cast<PClassActor *>(info)->StealthAlpha = i;
defaults->StealthAlpha = i;
}
//==========================================================================
@ -1456,8 +1282,7 @@ DEFINE_PROPERTY(stealthalpha, 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;
}
//==========================================================================
@ -1831,16 +1656,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;
}
//==========================================================================
//
//==========================================================================
@ -1875,15 +1690,6 @@ DEFINE_CLASS_PROPERTY(usesound, S, Inventory)
defaults->UseSound = str;
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(givequest, I, Inventory)
{
PROP_INT_PARM(i, 0);
defaults->GiveQuest = i;
}
//==========================================================================
//
//==========================================================================
@ -2718,24 +2524,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

@ -189,30 +189,48 @@ 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 color BloodColor; // won't be accessible for now because it needs refactoring to remove the 255-translations limit.
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 +257,7 @@ class Actor : Thinker native
Health DEFAULT_HEALTH;
Reactiontime 8;
Radius 20;
RenderRadius 0;
RenderRadius 0;
Height 16;
Mass 100;
RenderStyle 'Normal';
@ -271,6 +289,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 +297,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 +338,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 +362,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 +613,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 +826,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 +917,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 +954,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

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

@ -36,8 +36,8 @@
class Health : Inventory
{
transient int PrevHealth;
/*meta*/ int LowHealth;
/*meta*/ String LowHealthMessage;
meta int LowHealth;
meta String LowHealthMessage;
property LowMessage: LowHealth, LowHealthMessage;

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

@ -8,18 +8,14 @@ class PlayerPawn : Actor native
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 +33,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 +118,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)
{