Merge remote-tracking branch 'gzdoom/master' into qzdoom

# Conflicts:
#	src/r_things.cpp
This commit is contained in:
Magnus Norddahl 2017-01-18 04:09:16 +01:00
commit 53a79ca215
49 changed files with 1336 additions and 1275 deletions

View file

@ -1330,6 +1330,12 @@ void C_HideConsole ()
}
}
DEFINE_ACTION_FUNCTION(_Console, HideConsole)
{
C_HideConsole();
return 0;
}
static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
{
int data1 = ev->data1;

View file

@ -219,6 +219,7 @@ DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionStyle)
DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionAlpha)
DEFINE_FIELD_X(DehInfo, DehInfo, NoAutofreeze)
DEFINE_FIELD_X(DehInfo, DehInfo, BFGCells)
DEFINE_FIELD_X(DehInfo, DehInfo, BlueAC)
// Doom identified pickup items by their sprites. ZDoom prefers to use their
// class type to identify them instead. To support the traditional Doom

View file

@ -456,9 +456,7 @@ size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld, bool s
auto def = GetDefaultByType(p);
if (def != nullptr)
{
def->Class = p;
def->DObject::PointerSubstitution(old, notOld);
def->Class = nullptr; // reset pointer. Defaults should not have a valid class pointer.
}
}
}

View file

@ -208,7 +208,6 @@ enum EObjectFlags
OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls
OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list
OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk)
OF_SuperCall = 1 << 12, // A super call from the VM is about to be performed
};
template<class T> class TObjPtr;
@ -453,6 +452,8 @@ public:
void *ScriptVar(FName field, PType *type);
protected:
public:
DObject ();
DObject (PClass *inClass);

View file

@ -1782,6 +1782,18 @@ PClassPointer::PClassPointer(PClass *restrict)
else mDescriptiveName = "ClassPointer";
}
//==========================================================================
//
// PClassPointer - isCompatible
//
//==========================================================================
bool PClassPointer::isCompatible(PType *type)
{
auto other = dyn_cast<PClassPointer>(type);
return (other != nullptr && other->ClassRestriction->IsDescendantOf(ClassRestriction));
}
//==========================================================================
//
// PClassPointer :: IsMatch
@ -3333,6 +3345,20 @@ void PClass::InitializeDefaults()
{
assert(Defaults == NULL);
Defaults = (BYTE *)M_Malloc(Size);
// run the constructor on the defaults to set the vtbl pointer which is needed to run class-aware functions on them.
// bSerialOverride prevents linking into the thinker chains.
auto s = DThinker::bSerialOverride;
DThinker::bSerialOverride = true;
ConstructNative(Defaults);
DThinker::bSerialOverride = s;
// We must unlink the defaults from the class list because it's just a static block of data to the engine.
DObject *optr = (DObject*)Defaults;
GC::Root = optr->ObjNext;
optr->ObjNext = nullptr;
optr->SetClass(this);
if (ParentClass->Defaults != NULL)
{
memcpy(Defaults, ParentClass->Defaults, ParentClass->Size);

View file

@ -599,6 +599,7 @@ public:
// this is only here to block PPointer's implementation
void SetPointer(void *base, unsigned offset, TArray<size_t> *special = NULL) const override {}
bool isCompatible(PType *type);
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
@ -639,6 +640,19 @@ protected:
PProperty();
};
class PPropFlag : public PSymbol
{
DECLARE_CLASS(PPropFlag, PSymbol);
public:
PPropFlag(FName name, PField *offset, int bitval);
PField *Offset;
int bitval;
protected:
PPropFlag();
};
// Compound types -----------------------------------------------------------
class PEnum : public PNamedType

View file

@ -1263,10 +1263,11 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags)
// Strip all current powers, unless moving in a hub and the power is okay to keep.
item = p->mo->Inventory;
auto ptype = PClass::FindActor(NAME_Powerup);
while (item != NULL)
{
next = item->Inventory;
if (item->IsKindOf (RUNTIME_CLASS(APowerup)))
if (item->IsKindOf (ptype))
{
if (deathmatch || ((mode != FINISH_SameHub || !(item->ItemFlags & IF_HUBPOWER))
&& !(item->ItemFlags & IF_PERSISTENTPOWER))) // Keep persistent powers in non-deathmatch games

View file

@ -48,100 +48,13 @@ IMPLEMENT_CLASS(APowerup, false, false)
// Powerup-Giver -------------------------------------------------------------
IMPLEMENT_CLASS(APowerupGiver, false, true)
IMPLEMENT_POINTERS_START(APowerupGiver)
IMPLEMENT_POINTER(PowerupType)
IMPLEMENT_POINTERS_END
DEFINE_FIELD(APowerupGiver, PowerupType)
DEFINE_FIELD(APowerupGiver, EffectTics)
DEFINE_FIELD(APowerupGiver, BlendColor)
DEFINE_FIELD(APowerupGiver, Mode)
DEFINE_FIELD(APowerupGiver, Strength)
//===========================================================================
//
// APowerupGiver :: Use
//
//===========================================================================
bool APowerupGiver::Use (bool pickup)
{
if (PowerupType == NULL) return true; // item is useless
if (Owner == nullptr) return true;
APowerup *power = static_cast<APowerup *> (Spawn (PowerupType));
if (EffectTics != 0)
{
power->EffectTics = EffectTics;
}
if (BlendColor != 0)
{
if (BlendColor != MakeSpecialColormap(65535)) power->BlendColor = BlendColor;
else power->BlendColor = 0;
}
if (Mode != NAME_None)
{
power->Mode = Mode;
}
if (Strength != 0)
{
power->Strength = Strength;
}
power->ItemFlags |= ItemFlags & (IF_ALWAYSPICKUP|IF_ADDITIVETIME|IF_NOTELEPORTFREEZE);
if (power->CallTryPickup (Owner))
{
return true;
}
power->GoAwayAndDie ();
return false;
}
//===========================================================================
//
// APowerupGiver :: Serialize
//
//===========================================================================
void APowerupGiver::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
auto def = (APowerupGiver*)GetDefault();
arc("poweruptype", PowerupType, def->PowerupType)
("effecttics", EffectTics, def->EffectTics)
("blendcolor", BlendColor, def->BlendColor)
("mode", Mode, def->Mode)
("strength", Strength, def->Strength);
}
// Powerup -------------------------------------------------------------------
DEFINE_FIELD(APowerup, EffectTics)
DEFINE_FIELD(APowerup, BlendColor)
DEFINE_FIELD(APowerup, Mode)
DEFINE_FIELD(APowerup, Strength)
//===========================================================================
//
// APowerup :: Tick
//
//===========================================================================
void APowerup::Tick ()
{
// Powerups cannot exist outside an inventory
if (Owner == NULL)
{
Destroy ();
}
if (EffectTics > 0 && --EffectTics == 0)
{
Destroy ();
}
}
DEFINE_FIELD(APowerup, Colormap)
//===========================================================================
//
@ -156,438 +69,6 @@ void APowerup::Serialize(FSerializer &arc)
arc("effecttics", EffectTics, def->EffectTics)
("blendcolor", BlendColor, def->BlendColor)
("mode", Mode, def->Mode)
("strength", Strength, def->Strength);
("strength", Strength, def->Strength)
("colormap", Colormap, def->Colormap);
}
//===========================================================================
//
// APowerup :: GetBlend
//
//===========================================================================
PalEntry APowerup::GetBlend ()
{
if (isBlinking())
return 0;
if (IsSpecialColormap(BlendColor)) return 0;
return BlendColor;
}
//===========================================================================
//
// APowerup :: InitEffect
//
//===========================================================================
void APowerup::InitEffect ()
{
}
DEFINE_ACTION_FUNCTION(APowerup, InitEffect)
{
PARAM_SELF_PROLOGUE(APowerup);
self->InitEffect();
return 0;
}
void APowerup::CallInitEffect()
{
IFVIRTUAL(APowerup, InitEffect)
{
VMValue params[1] = { (DObject*)this };
VMFrameStack stack;
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else InitEffect();
}
//===========================================================================
//
// APowerup :: isBlinking (todo: make this virtual so that child classes can configure their blinking)
//
//===========================================================================
bool APowerup::isBlinking() const
{
return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !(ItemFlags & IF_NOSCREENBLINK));
}
DEFINE_ACTION_FUNCTION(APowerup, isBlinking)
{
PARAM_SELF_PROLOGUE(APowerup);
ACTION_RETURN_BOOL(self->isBlinking());
}
//===========================================================================
//
// APowerup :: DoEffect
//
//===========================================================================
void APowerup::DoEffect ()
{
if (Owner == NULL || Owner->player == NULL)
{
return;
}
if (EffectTics > 0)
{
int Colormap = GetSpecialColormap(BlendColor);
if (Colormap != NOFIXEDCOLORMAP)
{
if (!isBlinking())
{
Owner->player->fixedcolormap = Colormap;
}
else if (Owner->player->fixedcolormap == Colormap)
{
// only unset if the fixed colormap comes from this item
Owner->player->fixedcolormap = NOFIXEDCOLORMAP;
}
}
}
}
//===========================================================================
//
// APowerup :: EndEffect
//
//===========================================================================
void APowerup::EndEffect ()
{
int colormap = GetSpecialColormap(BlendColor);
if (colormap != NOFIXEDCOLORMAP && Owner && Owner->player && Owner->player->fixedcolormap == colormap)
{ // only unset if the fixed colormap comes from this item
Owner->player->fixedcolormap = NOFIXEDCOLORMAP;
}
}
DEFINE_ACTION_FUNCTION(APowerup, EndEffect)
{
PARAM_SELF_PROLOGUE(APowerup);
self->EndEffect();
return 0;
}
void APowerup::CallEndEffect()
{
IFVIRTUAL(APowerup, EndEffect)
{
VMValue params[1] = { (DObject*)this };
VMFrameStack stack;
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else EndEffect();
}
//===========================================================================
//
// APowerup :: Destroy
//
//===========================================================================
void APowerup::OnDestroy ()
{
CallEndEffect ();
Super::OnDestroy();
}
//===========================================================================
//
// APowerup :: DrawPowerup
//
//===========================================================================
bool APowerup::DrawPowerup (int x, int y)
{
if (!Icon.isValid())
{
return false;
}
if (!isBlinking())
{
FTexture *pic = TexMan(Icon);
screen->DrawTexture (pic, x, y,
DTA_HUDRules, HUD_Normal,
// DTA_TopOffset, pic->GetHeight()/2,
// DTA_LeftOffset, pic->GetWidth()/2,
TAG_DONE);
}
return true;
}
//===========================================================================
//
// APowerup :: HandlePickup
//
//===========================================================================
bool APowerup::HandlePickup (AInventory *item)
{
if (item->GetClass() == GetClass())
{
APowerup *power = static_cast<APowerup*>(item);
if (power->EffectTics == 0)
{
power->ItemFlags |= IF_PICKUPGOOD;
return true;
}
// Color gets transferred if the new item has an effect.
// Increase the effect's duration.
if (power->ItemFlags & IF_ADDITIVETIME)
{
EffectTics += power->EffectTics;
BlendColor = power->BlendColor;
}
// If it's not blinking yet, you can't replenish the power unless the
// powerup is required to be picked up.
else if (EffectTics > BLINKTHRESHOLD && !(power->ItemFlags & IF_ALWAYSPICKUP))
{
return true;
}
// Reset the effect duration.
else if (power->EffectTics > EffectTics)
{
EffectTics = power->EffectTics;
BlendColor = power->BlendColor;
}
power->ItemFlags |= IF_PICKUPGOOD;
return true;
}
return false;
}
//===========================================================================
//
// APowerup :: CreateCopy
//
//===========================================================================
AInventory *APowerup::CreateCopy (AActor *other)
{
// Get the effective effect time.
EffectTics = abs (EffectTics);
// Abuse the Owner field to tell the
// InitEffect method who started it;
// this should be cleared afterwards,
// as this powerup instance is not
// properly attached to anything yet.
Owner = other;
// Actually activate the powerup.
CallInitEffect ();
// Clear the Owner field, unless it was
// changed by the activation, for example,
// if this instance is a morph powerup;
// the flag tells the caller that the
// ownership has changed so that they
// can properly handle the situation.
if (!(ItemFlags & IF_CREATECOPYMOVED))
{
Owner = NULL;
}
// All done.
return this;
}
//===========================================================================
//
// APowerup :: CreateTossable
//
// Powerups are never droppable, even without IF_UNDROPPABLE set.
//
//===========================================================================
AInventory *APowerup::CreateTossable ()
{
return NULL;
}
//===========================================================================
//
// APowerup :: OwnerDied
//
// Powerups don't last beyond death.
//
//===========================================================================
void APowerup::OwnerDied ()
{
Destroy ();
}
// Invulnerability Powerup ---------------------------------------------------
IMPLEMENT_CLASS(APowerInvulnerable, false, false)
//===========================================================================
//
// APowerInvulnerable :: InitEffect
//
//===========================================================================
void APowerInvulnerable::InitEffect ()
{
Super::InitEffect();
Owner->effects &= ~FX_RESPAWNINVUL;
Owner->flags2 |= MF2_INVULNERABLE;
if (Mode == NAME_None && Owner->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
{
Mode = static_cast<PClassPlayerPawn *>(Owner->GetClass())->InvulMode;
}
if (Mode == NAME_Reflective)
{
Owner->flags2 |= MF2_REFLECTIVE;
}
}
//===========================================================================
//
// APowerInvulnerable :: DoEffect
//
//===========================================================================
void APowerInvulnerable::DoEffect ()
{
Super::DoEffect ();
if (Owner == NULL)
{
return;
}
if (Mode == NAME_Ghost)
{
if (!(Owner->flags & MF_SHADOW))
{
// Don't mess with the translucency settings if an
// invisibility powerup is active.
Owner->RenderStyle = STYLE_Translucent;
if (!(level.time & 7) && Owner->Alpha > 0 && Owner->Alpha < 1)
{
if (Owner->Alpha == HX_SHADOW)
{
Owner->Alpha = HX_ALTSHADOW;
}
else
{
Owner->Alpha = 0;
Owner->flags2 |= MF2_NONSHOOTABLE;
}
}
if (!(level.time & 31))
{
if (Owner->Alpha == 0)
{
Owner->flags2 &= ~MF2_NONSHOOTABLE;
Owner->Alpha = HX_ALTSHADOW;
}
else
{
Owner->Alpha = HX_SHADOW;
}
}
}
else
{
Owner->flags2 &= ~MF2_NONSHOOTABLE;
}
}
}
//===========================================================================
//
// APowerInvulnerable :: EndEffect
//
//===========================================================================
void APowerInvulnerable::EndEffect ()
{
Super::EndEffect();
if (Owner == NULL)
{
return;
}
Owner->flags2 &= ~MF2_INVULNERABLE;
Owner->effects &= ~FX_RESPAWNINVUL;
if (Mode == NAME_Ghost)
{
Owner->flags2 &= ~MF2_NONSHOOTABLE;
if (!(Owner->flags & MF_SHADOW))
{
// Don't mess with the translucency settings if an
// invisibility powerup is active.
Owner->RenderStyle = STYLE_Normal;
Owner->Alpha = 1.;
}
}
else if (Mode == NAME_Reflective)
{
Owner->flags2 &= ~MF2_REFLECTIVE;
}
if (Owner->player != NULL)
{
Owner->player->fixedcolormap = NOFIXEDCOLORMAP;
}
}
// Speed Powerup -------------------------------------------------------------
IMPLEMENT_CLASS(APowerSpeed, false, false)
DEFINE_FIELD(APowerSpeed, SpeedFlags)
//===========================================================================
//
// APowerSpeed :: Serialize
//
//===========================================================================
void APowerSpeed::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("speedflags", SpeedFlags);
}
// Morph powerup ------------------------------------------------------
IMPLEMENT_CLASS(APowerMorph, false, true)
IMPLEMENT_POINTERS_START(APowerMorph)
IMPLEMENT_POINTER(PlayerClass)
IMPLEMENT_POINTER(MorphFlash)
IMPLEMENT_POINTER(UnMorphFlash)
IMPLEMENT_POINTERS_END
DEFINE_FIELD(APowerMorph, PlayerClass)
DEFINE_FIELD(APowerMorph, MorphFlash)
DEFINE_FIELD(APowerMorph, UnMorphFlash)
DEFINE_FIELD(APowerMorph, MorphStyle)
DEFINE_FIELD(APowerMorph, MorphedPlayer)
//===========================================================================
//
// APowerMorph :: Serialize
//
//===========================================================================
void APowerMorph::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("playerclass", PlayerClass)
("morphstyle", MorphStyle)
("morphflash", MorphFlash)
("unmorphflash", UnMorphFlash)
("morphedplayer", MorphedPlayer);
}

View file

@ -11,85 +11,16 @@ class APowerup : public AInventory
{
DECLARE_CLASS (APowerup, AInventory)
public:
virtual void Tick () override;
virtual void OnDestroy() override;
virtual bool HandlePickup (AInventory *item) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual AInventory *CreateTossable () override;
virtual void Serialize(FSerializer &arc) override;
virtual void OwnerDied () override;
virtual PalEntry GetBlend () override;
virtual bool DrawPowerup (int x, int y) override;
int EffectTics;
PalEntry BlendColor;
FNameNoInit Mode;
double Strength;
int Colormap;
public:
virtual void InitEffect ();
virtual void DoEffect () override;
virtual void EndEffect ();
bool isBlinking() const;
protected:
void CallInitEffect();
void CallEndEffect();
friend void EndAllPowerupEffects(AInventory *item);
friend void InitAllPowerupEffects(AInventory *item);
};
// An artifact is an item that gives the player a powerup when activated.
class APowerupGiver : public AInventory
{
DECLARE_CLASS (APowerupGiver, AInventory)
HAS_OBJECT_POINTERS
public:
virtual bool Use (bool pickup) override;
virtual void Serialize(FSerializer &arc) override;
PClassActor *PowerupType;
int EffectTics; // Non-0 to override the powerup's default tics
PalEntry BlendColor; // Non-0 to override the powerup's default blend
FNameNoInit Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility
double Strength; // Meaning depends on powerup - currently used only by Invisibility
};
class APowerInvulnerable : public APowerup
{
DECLARE_CLASS (APowerInvulnerable, APowerup)
protected:
virtual void InitEffect () override;
virtual void DoEffect () override;
virtual void EndEffect () override;
};
class APowerSpeed : public APowerup
{
DECLARE_CLASS (APowerSpeed, APowerup)
protected:
virtual void Serialize(FSerializer &arc) override;
public:
int SpeedFlags;
};
#define PSF_NOTRAIL 1
class APowerMorph : public APowerup
{
DECLARE_CLASS( APowerMorph, APowerup )
HAS_OBJECT_POINTERS
public:
virtual void Serialize(FSerializer &arc) override;
// Variables
PClassPlayerPawn *PlayerClass;
PClassActor *MorphFlash, *UnMorphFlash;
int MorphStyle;
player_t *MorphedPlayer;
};
#endif //__A_ARTIFACTS_H__

View file

@ -368,18 +368,6 @@ void AInventory::CallDoEffect()
}
//===========================================================================
//
// AInventory :: OwnerDied
//
// Items receive this message when their owners die.
//
//===========================================================================
void AInventory::OwnerDied ()
{
}
//===========================================================================
//
// AInventory :: HandlePickup
@ -1348,7 +1336,7 @@ bool AInventory::TryPickup (AActor *&toucher)
}
// The item is placed in the inventory just long enough to be used.
toucher->AddInventory (this);
bool usegood = Use (true);
bool usegood = CallUse (true);
toucher->RemoveInventory (this);
if (usegood)

View file

@ -138,8 +138,6 @@ public:
// virtual on the script side only.
double GetSpeedFactor();
bool GetNoTeleportFreeze();
// Stuff for later when more features are exported.
virtual void OwnerDied();
bool GoAway();

View file

@ -1288,6 +1288,19 @@ bool FWeaponSlots::LocateWeapon (PClassWeapon *type, int *const slot, int *const
return false;
}
DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon)
{
PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots);
PARAM_CLASS(weap, AWeapon);
int slot = 0, index = 0;
bool retv = self->LocateWeapon(weap, &slot, &index);
if (numret >= 1) ret[0].SetInt(retv);
if (numret >= 2) ret[1].SetInt(slot);
if (numret >= 3) ret[2].SetInt(index);
return MIN(numret, 3);
}
//===========================================================================
//
// FindMostRecentWeapon

View file

@ -17,6 +17,7 @@
#include "a_armor.h"
#include "r_data/sprites.h"
#include "g_levellocals.h"
#include "virtual.h"
static FRandom pr_morphmonst ("MorphMonster");
@ -593,11 +594,17 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor
void EndAllPowerupEffects(AInventory *item)
{
auto ptype = PClass::FindActor(NAME_Powerup);
while (item != NULL)
{
if (item->IsKindOf(RUNTIME_CLASS(APowerup)))
if (item->IsKindOf(ptype))
{
static_cast<APowerup *>(item)->CallEndEffect();
IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect)
{
VMValue params[1] = { item };
VMFrameStack stack;
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
}
item = item->Inventory;
}
@ -613,11 +620,17 @@ void EndAllPowerupEffects(AInventory *item)
void InitAllPowerupEffects(AInventory *item)
{
auto ptype = PClass::FindActor(NAME_Powerup);
while (item != NULL)
{
if (item->IsKindOf(RUNTIME_CLASS(APowerup)))
if (item->IsKindOf(ptype))
{
static_cast<APowerup *>(item)->CallInitEffect();
IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect)
{
VMValue params[1] = { item };
VMFrameStack stack;
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
}
item = item->Inventory;
}

View file

@ -1158,10 +1158,10 @@ class CommandDrawNumber : public CommandDrawString
if(!parenthesized || !sc.CheckToken(TK_StringConst))
sc.MustGetToken(TK_Identifier);
inventoryItem = PClass::FindActor(sc.String);
if(inventoryItem == NULL || !RUNTIME_CLASS(APowerupGiver)->IsAncestorOf(inventoryItem))
if(inventoryItem == NULL || !PClass::FindActor(NAME_PowerupGiver)->IsAncestorOf(inventoryItem))
{
sc.ScriptMessage("'%s' is not a type of PowerupGiver.", sc.String);
inventoryItem = RUNTIME_CLASS(APowerupGiver);
inventoryItem = PClass::FindActor(NAME_PowerupGiver);
}
if(parenthesized) sc.MustGetToken(')');
@ -1433,11 +1433,14 @@ class CommandDrawNumber : public CommandDrawString
break;
case POWERUPTIME:
{
//Get the PowerupType and check to see if the player has any in inventory.
PClassActor* powerupType = ((APowerupGiver*) GetDefaultByType(inventoryItem))->PowerupType;
APowerup* powerup = (APowerup*) statusBar->CPlayer->mo->FindInventory(powerupType);
if(powerup != NULL)
num = powerup->EffectTics / TICRATE + 1;
// num = statusBar.CPlayer.mo.GetEffectTicsForItem(inventoryItem) / TICRATE + 1;
static VMFunction *func = nullptr;
if (func == nullptr) func = static_cast<PFunction*>(RUNTIME_CLASS(APlayerPawn)->Symbols.FindSymbol("GetEffectTicsForItem", false))->Variants[0].Implementation;
VMValue params[] = { statusBar->CPlayer->mo, inventoryItem };
int retv;
VMReturn ret(&retv);
GlobalVMStack.Call(func, params, 2, &ret, 1);
num = retv / TICRATE + 1;
break;
}
case INVENTORY:
@ -2655,10 +2658,10 @@ class CommandDrawBar : public SBarInfoCommand
if(!parenthesized || !sc.CheckToken(TK_StringConst))
sc.MustGetToken(TK_Identifier);
data.inventoryItem = PClass::FindActor(sc.String);
if(data.inventoryItem == NULL || !RUNTIME_CLASS(APowerupGiver)->IsAncestorOf(data.inventoryItem))
if(data.inventoryItem == NULL || !PClass::FindActor(NAME_PowerupGiver)->IsAncestorOf(data.inventoryItem))
{
sc.ScriptMessage("'%s' is not a type of PowerupGiver.", sc.String);
data.inventoryItem = RUNTIME_CLASS(APowerupGiver);
data.inventoryItem = PClass::FindActor(NAME_PowerupGiver);
}
if(parenthesized) sc.MustGetToken(')');
@ -2822,18 +2825,16 @@ class CommandDrawBar : public SBarInfoCommand
break;
case POWERUPTIME:
{
//Get the PowerupType and check to see if the player has any in inventory.
APowerupGiver *powerupGiver = (APowerupGiver*) GetDefaultByType(data.inventoryItem);
PClassActor *powerupType = powerupGiver->PowerupType;
APowerup *powerup = (APowerup*) statusBar->CPlayer->mo->FindInventory(powerupType);
if(powerup != NULL && powerupType != NULL && powerupGiver != NULL)
{
value = powerup->EffectTics + 1;
if(powerupGiver->EffectTics == 0) //if 0 we need to get the default from the powerup
max = ((APowerup*) GetDefaultByType(powerupType))->EffectTics + 1;
else
max = powerupGiver->EffectTics + 1;
}
static VMFunction *func = nullptr;
if (func == nullptr) func = static_cast<PFunction*>(RUNTIME_CLASS(APlayerPawn)->Symbols.FindSymbol("GetEffectTicsForItem", false))->Variants[0].Implementation;
VMValue params[] = { statusBar->CPlayer->mo, data.inventoryItem };
VMReturn ret[2];
int ival;
ret[0].IntAt(&ival);
ret[1].IntAt(&max);
GlobalVMStack.Call(func, params, 2, ret, 2);
value = ival + 1;
max++;
break;
}
case SAVEPERCENT:

View file

@ -45,6 +45,10 @@
gameinfo_t gameinfo;
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, backpacktype)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, Armor2Percent)
const char *GameNames[17] =
{
NULL, "Doom", "Heretic", NULL, "Hexen", NULL, NULL, NULL, "Strife", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Chex"

View file

@ -148,7 +148,7 @@ struct gameinfo_t
FString translator;
DWORD defaultbloodcolor;
DWORD defaultbloodparticlecolor;
FString backpacktype;
FName backpacktype;
FString statusbar;
FString intermissionMusic;
int intermissionOrder;

View file

@ -50,6 +50,7 @@
#include "a_armor.h"
#include "a_ammo.h"
#include "g_levellocals.h"
#include "virtual.h"
// [RH] Actually handle the cheat. The cheat code in st_stuff.c now just
// writes some bytes to the network data stream, and the network code
@ -324,16 +325,10 @@ void cht_DoCheat (player_t *player, int cheat)
}
else
{
player->mo->Revive();
player->playerstate = PST_LIVE;
player->health = player->mo->health = player->mo->GetDefault()->health;
player->viewheight = ((APlayerPawn *)player->mo->GetDefault())->ViewHeight;
player->mo->flags = player->mo->GetDefault()->flags;
player->mo->flags2 = player->mo->GetDefault()->flags2;
player->mo->flags3 = player->mo->GetDefault()->flags3;
player->mo->flags4 = player->mo->GetDefault()->flags4;
player->mo->flags5 = player->mo->GetDefault()->flags5;
player->mo->flags6 = player->mo->GetDefault()->flags6;
player->mo->flags7 = player->mo->GetDefault()->flags7;
player->mo->renderflags &= ~RF_INVISIBLE;
player->mo->Height = player->mo->GetDefault()->Height;
player->mo->radius = player->mo->GetDefault()->radius;
@ -344,7 +339,6 @@ void cht_DoCheat (player_t *player, int cheat)
{
player->mo->Translation = TRANSLATION(TRANSLATION_Players, BYTE(player-players));
}
player->mo->DamageType = NAME_None;
if (player->ReadyWeapon != nullptr)
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetUpState());
@ -588,434 +582,24 @@ const char *cht_Morph (player_t *player, PClassPlayerPawn *morphclass, bool quic
void cht_Give (player_t *player, const char *name, int amount)
{
enum { ALL_NO, ALL_YES, ALL_YESYES } giveall;
int i;
PClassActor *type;
if (player->mo == nullptr) return;
if (player != &players[consoleplayer])
Printf ("%s is a cheater: give %s\n", player->userinfo.GetName(), name);
if (player->mo == NULL || player->health <= 0)
IFVIRTUALPTR(player->mo, APlayerPawn, CheatGive)
{
return;
VMValue params[3] = { player->mo, FString(name), amount };
GlobalVMStack.Call(func, params, 3, nullptr, 0);
}
giveall = ALL_NO;
if (stricmp (name, "all") == 0)
{
giveall = ALL_YES;
}
else if (stricmp (name, "everything") == 0)
{
giveall = ALL_YESYES;
}
if (stricmp (name, "health") == 0)
{
if (amount > 0)
{
player->mo->health += amount;
player->health = player->mo->health;
}
else
{
player->health = player->mo->health = player->mo->GetMaxHealth();
}
}
if (giveall || stricmp (name, "backpack") == 0)
{
// Select the correct type of backpack based on the game
type = PClass::FindActor(gameinfo.backpacktype);
if (type != NULL)
{
player->mo->GiveInventory(static_cast<PClassInventory *>(type), 1, true);
}
if (!giveall)
return;
}
if (giveall || stricmp (name, "ammo") == 0)
{
// Find every unique type of ammo. Give it to the player if
// he doesn't have it already, and set each to its maximum.
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
PClassActor *type = PClassActor::AllActorClasses[i];
if (type->ParentClass == RUNTIME_CLASS(AAmmo))
{
PClassInventory *atype = static_cast<PClassInventory *>(type);
AInventory *ammo = player->mo->FindInventory(atype);
if (ammo == NULL)
{
ammo = static_cast<AInventory *>(Spawn (atype));
ammo->AttachToOwner (player->mo);
ammo->Amount = ammo->MaxAmount;
}
else if (ammo->Amount < ammo->MaxAmount)
{
ammo->Amount = ammo->MaxAmount;
}
}
}
if (!giveall)
return;
}
if (giveall || stricmp (name, "armor") == 0)
{
if (gameinfo.gametype != GAME_Hexen)
{
ABasicArmorPickup *armor = Spawn<ABasicArmorPickup> ();
armor->SaveAmount = 100*deh.BlueAC;
armor->SavePercent = gameinfo.Armor2Percent > 0? gameinfo.Armor2Percent : 0.5;
if (!armor->CallTryPickup (player->mo))
{
armor->Destroy ();
}
}
else
{
for (i = 0; i < 4; ++i)
{
AHexenArmor *armor = Spawn<AHexenArmor> ();
armor->health = i;
armor->Amount = 0;
if (!armor->CallTryPickup (player->mo))
{
armor->Destroy ();
}
}
}
if (!giveall)
return;
}
if (giveall || stricmp (name, "keys") == 0)
{
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
if (PClassActor::AllActorClasses[i]->IsDescendantOf (RUNTIME_CLASS(AKey)))
{
AKey *key = (AKey *)GetDefaultByType (PClassActor::AllActorClasses[i]);
if (key->KeyNumber != 0)
{
key = static_cast<AKey *>(Spawn(static_cast<PClassActor *>(PClassActor::AllActorClasses[i])));
if (!key->CallTryPickup (player->mo))
{
key->Destroy ();
}
}
}
}
if (!giveall)
return;
}
if (giveall || stricmp (name, "weapons") == 0)
{
AWeapon *savedpending = player->PendingWeapon;
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
// Don't give replaced weapons unless the replacement was done by Dehacked.
if (type != RUNTIME_CLASS(AWeapon) &&
type->IsDescendantOf (RUNTIME_CLASS(AWeapon)) &&
(static_cast<PClassActor *>(type)->GetReplacement() == type ||
static_cast<PClassActor *>(type)->GetReplacement()->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup))))
{
// Give the weapon only if it belongs to the current game or
if (player->weapons.LocateWeapon(static_cast<PClassWeapon*>(type), NULL, NULL))
{
AWeapon *def = (AWeapon*)GetDefaultByType (type);
if (giveall == ALL_YESYES || !(def->WeaponFlags & WIF_CHEATNOTWEAPON))
{
player->mo->GiveInventory(static_cast<PClassInventory *>(type), 1, true);
}
}
}
}
player->PendingWeapon = savedpending;
if (!giveall)
return;
}
if (giveall || stricmp (name, "artifacts") == 0)
{
auto pitype = PClass::FindActor(NAME_PuzzleItem);
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf (RUNTIME_CLASS(AInventory)))
{
AInventory *def = (AInventory*)GetDefaultByType (type);
if (def->Icon.isValid() && def->MaxAmount > 1 &&
!type->IsDescendantOf (pitype) &&
!type->IsDescendantOf (RUNTIME_CLASS(APowerup)) &&
!type->IsDescendantOf (RUNTIME_CLASS(AArmor)))
{
// Do not give replaced items unless using "give everything"
if (giveall == ALL_YESYES || type->GetReplacement() == type)
{
player->mo->GiveInventory(static_cast<PClassInventory *>(type), amount <= 0 ? def->MaxAmount : amount, true);
}
}
}
}
if (!giveall)
return;
}
if (giveall || stricmp (name, "puzzlepieces") == 0)
{
auto pitype = PClass::FindActor(NAME_PuzzleItem);
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf (pitype))
{
AInventory *def = (AInventory*)GetDefaultByType (type);
if (def->Icon.isValid())
{
// Do not give replaced items unless using "give everything"
if (giveall == ALL_YESYES || type->GetReplacement() == type)
{
player->mo->GiveInventory(static_cast<PClassInventory *>(type), amount <= 0 ? def->MaxAmount : amount, true);
}
}
}
}
if (!giveall)
return;
}
if (giveall)
return;
type = PClass::FindActor(name);
if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS(AInventory)))
{
if (player == &players[consoleplayer])
Printf ("Unknown item \"%s\"\n", name);
}
else
{
player->mo->GiveInventory(static_cast<PClassInventory *>(type), amount, true);
}
return;
}
void cht_Take (player_t *player, const char *name, int amount)
{
bool takeall;
PClassActor *type;
if (player->mo == nullptr) return;
if (player->mo == NULL || player->health <= 0)
IFVIRTUALPTR(player->mo, APlayerPawn, CheatTake)
{
return;
VMValue params[3] = { player->mo, FString(name), amount };
GlobalVMStack.Call(func, params, 3, nullptr, 0);
}
takeall = (stricmp (name, "all") == 0);
if (!takeall && stricmp (name, "health") == 0)
{
if (player->mo->health - amount <= 0
|| player->health - amount <= 0
|| amount == 0)
{
cht_Suicide (player);
if (player == &players[consoleplayer])
C_HideConsole ();
return;
}
if (amount > 0)
{
if (player->mo)
{
player->mo->health -= amount;
player->health = player->mo->health;
}
else
{
player->health -= amount;
}
}
if (!takeall)
return;
}
if (takeall || stricmp (name, "backpack") == 0)
{
// Take away all types of backpacks the player might own.
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
PClass *type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf(PClass::FindClass(NAME_BackpackItem)))
{
AInventory *pack = player->mo->FindInventory(static_cast<PClassActor *>(type));
if (pack)
pack->Destroy();
}
}
if (!takeall)
return;
}
if (takeall || stricmp (name, "ammo") == 0)
{
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
PClass *type = PClassActor::AllActorClasses[i];
if (type->ParentClass == RUNTIME_CLASS (AAmmo))
{
AInventory *ammo = player->mo->FindInventory(static_cast<PClassActor *>(type));
if (ammo)
ammo->DepleteOrDestroy();
}
}
if (!takeall)
return;
}
if (takeall || stricmp (name, "armor") == 0)
{
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf (RUNTIME_CLASS (AArmor)))
{
AInventory *armor = player->mo->FindInventory(static_cast<PClassActor *>(type));
if (armor)
armor->DepleteOrDestroy();
}
}
if (!takeall)
return;
}
if (takeall || stricmp (name, "keys") == 0)
{
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf (RUNTIME_CLASS (AKey)))
{
AActor *key = player->mo->FindInventory(static_cast<PClassActor *>(type));
if (key)
key->Destroy ();
}
}
if (!takeall)
return;
}
if (takeall || stricmp (name, "weapons") == 0)
{
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type != RUNTIME_CLASS(AWeapon) &&
type->IsDescendantOf (RUNTIME_CLASS (AWeapon)))
{
AActor *weapon = player->mo->FindInventory(static_cast<PClassActor *>(type));
if (weapon)
weapon->Destroy ();
player->ReadyWeapon = nullptr;
player->PendingWeapon = WP_NOCHANGE;
}
}
if (!takeall)
return;
}
if (takeall || stricmp (name, "artifacts") == 0)
{
auto pitype = PClass::FindActor(NAME_PuzzleItem);
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf (RUNTIME_CLASS (AInventory)))
{
if (!type->IsDescendantOf (pitype) &&
!type->IsDescendantOf (RUNTIME_CLASS (APowerup)) &&
!type->IsDescendantOf (RUNTIME_CLASS (AArmor)) &&
!type->IsDescendantOf (RUNTIME_CLASS (AWeapon)) &&
!type->IsDescendantOf (RUNTIME_CLASS (AKey)))
{
AActor *artifact = player->mo->FindInventory(static_cast<PClassActor *>(type));
if (artifact)
artifact->Destroy ();
}
}
}
if (!takeall)
return;
}
if (takeall || stricmp (name, "puzzlepieces") == 0)
{
auto pitype = PClass::FindActor(NAME_PuzzleItem);
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf (pitype))
{
AActor *puzzlepiece = player->mo->FindInventory(static_cast<PClassActor *>(type));
if (puzzlepiece)
puzzlepiece->Destroy ();
}
}
if (!takeall)
return;
}
if (takeall)
return;
type = PClass::FindActor (name);
if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS (AInventory)))
{
if (player == &players[consoleplayer])
Printf ("Unknown item \"%s\"\n", name);
}
else
{
player->mo->TakeInventory(type, amount ? amount : 1);
}
return;
}
class DSuicider : public DThinker
@ -1070,6 +654,12 @@ void cht_Suicide (player_t *plyr)
}
}
DEFINE_ACTION_FUNCTION(APlayerPawn, CheatSuicide)
{
PARAM_SELF_PROLOGUE(APlayerPawn);
cht_Suicide(self->player);
return 0;
}
CCMD (mdk)
{

View file

@ -706,6 +706,17 @@ xx(WBobSpeed)
xx(PlayerClass)
xx(Wi_NoAutostartMap)
xx(MorphStyle)
xx(MorphFlash)
xx(UnMorphFlash)
xx(Powerup)
xx(EffectTics)
xx(PowerupGiver)
xx(BlendColor)
xx(Strength)
xx(Mode)
xx(PowerupType)
// Decorate compatibility functions
xx(BuiltinTypeCheck)
xx(BuiltinRandom)

View file

@ -87,6 +87,7 @@
#include "a_ammo.h"
#include "r_data/colormaps.h"
#include "g_levellocals.h"
#include "stats.h"
extern FILE *Logfile;
@ -2974,8 +2975,12 @@ void DACSThinker::Serialize(FSerializer &arc)
}
}
cycle_t ACSTime;
void DACSThinker::Tick ()
{
ACSTime.Reset();
ACSTime.Clock();
DLevelScript *script = Scripts;
while (script)
@ -2993,6 +2998,7 @@ void DACSThinker::Tick ()
ACS_StringBuilderStack.Clear();
I_Error("Error: %d garbage entries on ACS string builder stack.", size);
}
ACSTime.Unclock();
}
void DACSThinker::StopScriptsFor (AActor *actor)
@ -5713,7 +5719,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
if (argCount >= 2)
{
PClassActor *powerupclass = PClass::FindActor(FBehavior::StaticLookupString(args[1]));
if (powerupclass == NULL || !RUNTIME_CLASS(APowerup)->IsAncestorOf(powerupclass))
if (powerupclass == NULL || !powerupclass->IsDescendantOf(PClass::FindActor(NAME_Powerup)))
{
Printf("'%s' is not a type of Powerup.\n", FBehavior::StaticLookupString(args[1]));
return 0;
@ -10269,3 +10275,8 @@ CCMD(acsprofile)
ShowProfileData(ScriptProfiles, limit, sorter, false);
ShowProfileData(FuncProfiles, limit, sorter, true);
}
ADD_STAT(ACS)
{
return FStringf("ACS time: %f ms", ACSTime.TimeMS());
}

View file

@ -1224,22 +1224,6 @@ DEFINE_ACTION_FUNCTION(AActor, CheckInventory)
}
//==========================================================================
//
// State jump function
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, CheckArmorType)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_NAME (type);
PARAM_INT_DEF(amount);
ABasicArmor *armor = (ABasicArmor *)self->FindInventory(NAME_BasicArmor);
ACTION_RETURN_BOOL(armor && armor->ArmorType == type && armor->Amount >= amount);
}
//==========================================================================
//
// Parameterized version of A_Explode

View file

@ -357,15 +357,19 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags)
static int dieticks[MAXPLAYERS]; // [ZzZombo] not used? Except if for peeking in debugger...
int pnum = int(this->player-players);
dieticks[pnum] = gametic;
fprintf (debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic,
this->player->cheats&CF_PREDICTING?"predicting":"real");
fprintf(debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic,
this->player->cheats&CF_PREDICTING ? "predicting" : "real");
}
// [RH] Notify this actor's items.
for (AInventory *item = Inventory; item != NULL; )
{
AInventory *next = item->Inventory;
item->OwnerDied();
IFVIRTUALPTR(item, AInventory, OwnerDied)
{
VMValue params[1] = { item };
GlobalVMStack.Call(func, params, 1, nullptr, 0);
}
item = next;
}

View file

@ -822,11 +822,13 @@ bool AActor::GiveInventory(PClassInventory *type, int amount, bool givecheat)
return result;
}
DEFINE_ACTION_FUNCTION(AActor, Inventory)
DEFINE_ACTION_FUNCTION(AActor, GiveInventory)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT_NOT_NULL(item, AInventory);
ACTION_RETURN_BOOL(self->UseInventory(item));
PARAM_CLASS(type, AInventory);
PARAM_INT(amount);
PARAM_BOOL_DEF(givecheat);
ACTION_RETURN_BOOL(self->GiveInventory(type, amount, givecheat));
}
@ -918,6 +920,16 @@ bool AActor::TakeInventory(PClassActor *itemclass, int amount, bool fromdecorate
return result;
}
DEFINE_ACTION_FUNCTION(AActor, TakeInventory)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT_NOT_NULL(item, AInventory);
PARAM_INT(amount);
PARAM_BOOL_DEF(fromdecorate);
PARAM_BOOL_DEF(notakeinfinite);
self->RemoveInventory(item);
return 0;
}
//============================================================================
//
@ -5369,15 +5381,13 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
oldactor->DestroyAllInventory();
}
// [BC] Handle temporary invulnerability when respawned
if ((state == PST_REBORN || state == PST_ENTER) &&
(dmflags2 & DF2_YES_RESPAWN_INVUL) &&
(multiplayer || alwaysapplydmflags))
if (state == PST_REBORN || state == PST_ENTER)
{
APowerup *invul = static_cast<APowerup*>(p->mo->GiveInventoryType (RUNTIME_CLASS(APowerInvulnerable)));
invul->EffectTics = 3*TICRATE;
invul->BlendColor = 0; // don't mess with the view
invul->ItemFlags |= IF_UNDROPPABLE; // Don't drop this
p->mo->effects |= FX_RESPAWNINVUL; // [RH] special effect
IFVIRTUALPTR(p->mo, APlayerPawn, OnRespawn)
{
VMValue param = p->mo;
GlobalVMStack.Call(func, &param, 1, nullptr, 0);
}
}
if (StatusBar != NULL && (playernum == consoleplayer || StatusBar->GetPlayer() == playernum))

View file

@ -638,6 +638,14 @@ void player_t::SendPitchLimits() const
}
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetUserName)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_STRING(self->userinfo.GetName());
}
//===========================================================================
//
// APlayerPawn
@ -1158,7 +1166,7 @@ void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer)
}
else if ((dmflags & DF_COOP_LOSE_POWERUPS) &&
defitem == NULL &&
item->IsKindOf(RUNTIME_CLASS(APowerupGiver)))
item->IsKindOf(PClass::FindActor(NAME_PowerupGiver)))
{
item->Destroy();
}

View file

@ -72,16 +72,6 @@ inline uint32 MakeSpecialColormap(int index)
return index | SPECIALCOLORMAP_MASK;
}
inline bool IsSpecialColormap(uint32 map)
{
return (map & 0xFFFF0000) == SPECIALCOLORMAP_MASK;
}
inline int GetSpecialColormap(int blend)
{
return IsSpecialColormap(blend) ? blend & 0xFFFF : NOFIXEDCOLORMAP;
}
int AddSpecialColormap(float r1, float g1, float b1, float r2, float g2, float b2);

View file

@ -4362,9 +4362,8 @@ ExpEmit FxDotCross::Emit(VMFunctionBuilder *build)
FxTypeCheck::FxTypeCheck(FxExpression *l, FxExpression *r)
: FxExpression(EFX_TypeCheck, l->ScriptPosition)
{
left = new FxTypeCast(l, NewPointer(RUNTIME_CLASS(DObject)), false);
right = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), r);
EmitTail = false;
left = l;
right = r;
ValueType = TypeBool;
}
@ -4389,9 +4388,27 @@ FxTypeCheck::~FxTypeCheck()
FxExpression *FxTypeCheck::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
// This must resolve the cast separately so that it can set the proper type for class descriptors.
RESOLVE(left, ctx);
RESOLVE(right, ctx);
ABORT(right && left);
if (left->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)))
{
left = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), left);
ClassCheck = true;
}
else
{
left = new FxTypeCast(left, NewPointer(RUNTIME_CLASS(DObject)), false);
ClassCheck = false;
}
right = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), right);
RESOLVE(left, ctx);
RESOLVE(right, ctx);
ABORT(right && left);
return this;
}
@ -4408,7 +4425,8 @@ ExpEmit FxTypeCheck::EmitCommon(VMFunctionBuilder *build)
castee.Free(build);
casttype.Free(build);
ExpEmit ares(build, REGT_POINTER);
build->Emit(casttype.Konst ? OP_DYNCAST_K : OP_DYNCAST_R, ares.RegNum, castee.RegNum, casttype.RegNum);
if (!ClassCheck) build->Emit(casttype.Konst ? OP_DYNCAST_K : OP_DYNCAST_R, ares.RegNum, castee.RegNum, casttype.RegNum);
else build->Emit(casttype.Konst ? OP_DYNCASTC_K : OP_DYNCASTC_R, ares.RegNum, castee.RegNum, casttype.RegNum);
return ares;
}
@ -9938,37 +9956,76 @@ ExpEmit FxJumpStatement::Emit(VMFunctionBuilder *build)
//==========================================================================
FxReturnStatement::FxReturnStatement(FxExpression *value, const FScriptPosition &pos)
: FxExpression(EFX_ReturnStatement, pos), Value(value)
: FxExpression(EFX_ReturnStatement, pos)
{
if (value != nullptr) Args.Push(value);
ValueType = TypeVoid;
}
FxReturnStatement::FxReturnStatement(FArgumentList &values, const FScriptPosition &pos)
: FxExpression(EFX_ReturnStatement, pos)
{
Args = std::move(values);
ValueType = TypeVoid;
}
FxReturnStatement::~FxReturnStatement()
{
SAFE_DELETE(Value);
}
FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
{
bool fail = false;
CHECKRESOLVED();
SAFE_RESOLVE_OPT(Value, ctx);
for (auto &Value : Args)
{
SAFE_RESOLVE_OPT(Value, ctx);
fail |= (Value == nullptr);
}
if (fail)
{
delete this;
return nullptr;
}
PPrototype *retproto;
if (Value == nullptr)
if (Args.Size() == 0)
{
TArray<PType *> none(0);
retproto = NewPrototype(none, none);
}
else
else if (Args.Size() == 1)
{
// If we already know the real return type we need at least try to cast the value to its proper type (unless in an anonymous function.)
if (ctx.ReturnProto != nullptr && ctx.ReturnProto->ReturnTypes.Size() > 0 && ctx.Function->SymbolName != NAME_None)
{
Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[0], false, false);
Value = Value->Resolve(ctx);
ABORT(Value);
Args[0] = new FxTypeCast(Args[0], ctx.ReturnProto->ReturnTypes[0], false, false);
Args[0] = Args[0]->Resolve(ctx);
ABORT(Args[0]);
}
retproto = Value->ReturnProto();
retproto = Args[0]->ReturnProto();
}
else if (ctx.ReturnProto != nullptr && ctx.ReturnProto->ReturnTypes.Size() == Args.Size())
{
for (unsigned i = 0; i < Args.Size(); i++)
{
auto &Value = Args[0];
Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[i], false, false);
Value = Value->Resolve(ctx);
if (Value == nullptr) fail = true;
}
if (fail)
{
delete this;
return nullptr;
}
return this; // no point calling CheckReturn here.
}
else
{
ScriptPosition.Message(MSG_ERROR, "Incorrect number of return values. Got %u, but expected %u", Args.Size(), ctx.ReturnProto->ReturnTypes.Size());
delete this;
return nullptr;
}
ctx.CheckReturn(retproto, ScriptPosition);
@ -9978,8 +10035,20 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
{
TArray<ExpEmit> outs;
ExpEmit out(0, REGT_NIL);
// If there's structs to destroy here we need to emit all returns before destroying them.
if (build->ConstructedStructs.Size())
{
for (auto ret : Args)
{
ExpEmit r = ret->Emit(build);
outs.Push(r);
}
}
// call the destructors for all structs requiring one.
// go in reverse order of construction
for (int i = build->ConstructedStructs.Size() - 1; i >= 0; i--)
@ -9995,19 +10064,19 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
// If we return nothing, use a regular RET opcode.
// Otherwise just return the value we're given.
if (Value == nullptr)
if (Args.Size() == 0)
{
build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
}
else
else if (Args.Size() == 1)
{
out = Value->Emit(build);
out = outs.Size() > 0? outs[0] : Args[0]->Emit(build);
// Check if it is a function call that simplified itself
// into a tail call in which case we don't emit anything.
if (!out.Final)
{
if (Value->ValueType == TypeVoid)
if (Args[0]->ValueType == TypeVoid)
{ // Nothing is returned.
build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
}
@ -10017,6 +10086,14 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
}
}
}
else
{
for (unsigned i = 0; i < Args.Size(); i++)
{
out = outs.Size() > 0 ? outs[i] : Args[i]->Emit(build);
build->Emit(OP_RET, i < Args.Size() - 1 ? i : i+RET_FINAL, EncodeRegType(out), out.RegNum);
}
}
out.Final = true;
return out;
@ -10024,9 +10101,9 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
VMFunction *FxReturnStatement::GetDirectFunction()
{
if (Value != nullptr)
if (Args.Size() == 1)
{
return Value->GetDirectFunction();
return Args[0]->GetDirectFunction();
}
return nullptr;
}

View file

@ -1086,7 +1086,7 @@ class FxTypeCheck : public FxExpression
public:
FxExpression *left;
FxExpression *right;
bool EmitTail;
bool ClassCheck;
FxTypeCheck(FxExpression*, FxExpression*);
~FxTypeCheck();
@ -1911,10 +1911,11 @@ public:
class FxReturnStatement : public FxExpression
{
FxExpression *Value;
FArgumentList Args;
public:
FxReturnStatement(FxExpression *value, const FScriptPosition &pos);
FxReturnStatement(FArgumentList &args, const FScriptPosition &pos);
~FxReturnStatement();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);

View file

@ -913,13 +913,14 @@ static void ParseActorProperty(FScanner &sc, Baggage &bag)
if (prop != NULL)
{
if (bag.Info->IsDescendantOf(*prop->cls))
auto pcls = PClass::FindActor(prop->clsname);
if (bag.Info->IsDescendantOf(pcls))
{
ParsePropertyParams(sc, prop, (AActor *)bag.Info->Defaults, bag);
}
else
{
sc.ScriptMessage("'%s' requires an actor of type '%s'\n", propname.GetChars(), (*prop->cls)->TypeName.GetChars());
sc.ScriptMessage("'%s' requires an actor of type '%s'\n", propname.GetChars(), pcls->TypeName.GetChars());
FScriptPosition::ErrorCounter++;
}
}

View file

@ -187,6 +187,7 @@ enum
DEPF_HEXENBOUNCE,
DEPF_DOOMBOUNCE,
DEPF_INTERHUBSTRIP,
DEPF_NOTRAIL,
};
// Types of old style decorations
@ -229,7 +230,7 @@ struct FPropertyInfo
{
const char *name;
const char *params;
const PClass * const *cls;
const char *clsname;
PropHandler Handler;
int category;
};
@ -241,17 +242,24 @@ int MatchString (const char *in, const char **strings);
#define DEFINE_PROPERTY_BASE(name, paramlist, clas, cat) \
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \
static FPropertyInfo Prop_##name##_##paramlist##_##clas = \
{ #name, #paramlist, &RUNTIME_CLASS_CASTLESS(A##clas), (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \
{ #name, #paramlist, #clas, (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \
MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params)
#define DEFINE_PREFIXED_PROPERTY_BASE(prefix, name, paramlist, clas, cat) \
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \
static FPropertyInfo Prop_##name##_##paramlist##_##clas = \
{ #prefix"."#name, #paramlist, &RUNTIME_CLASS_CASTLESS(A##clas), (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \
{ #prefix"."#name, #paramlist, #clas, (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \
MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params)
#define DEFINE_PREFIXED_SCRIPTED_PROPERTY_BASE(prefix, name, paramlist, clas, cat) \
static void Handler_##name##_##paramlist##_##clas(AActor *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \
static FPropertyInfo Prop_##name##_##paramlist##_##clas = \
{ #prefix"."#name, #paramlist, #clas, (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \
MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \
static void Handler_##name##_##paramlist##_##clas(AActor *defaults, PClassActor *info, Baggage &bag, FPropParam *params)
#define DEFINE_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_PROPERTY)
#define DEFINE_INFO_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_INFO)
@ -259,6 +267,9 @@ int MatchString (const char *in, const char **strings);
#define DEFINE_CLASS_PROPERTY(name, paramlist, clas) DEFINE_PREFIXED_PROPERTY_BASE(clas, name, paramlist, clas, CAT_PROPERTY)
#define DEFINE_CLASS_PROPERTY_PREFIX(prefix, name, paramlist, clas) DEFINE_PREFIXED_PROPERTY_BASE(prefix, name, paramlist, clas, CAT_PROPERTY)
#define DEFINE_SCRIPTED_PROPERTY(name, paramlist, clas) DEFINE_PREFIXED_SCRIPTED_PROPERTY_BASE(clas, name, paramlist, clas, CAT_PROPERTY)
#define DEFINE_SCRIPTED_PROPERTY_PREFIX(prefix, name, paramlist, clas) DEFINE_PREFIXED_SCRIPTED_PROPERTY_BASE(prefix, name, paramlist, clas, CAT_PROPERTY)
#define PROP_PARM_COUNT (params[0].i)
#define PROP_STRING_PARM(var, no) \

View file

@ -100,6 +100,7 @@ static FFlagDef InternalActorFlagDefs[]=
DEFINE_FLAG(MF6, INTRYMOVE, AActor, flags6),
DEFINE_FLAG(MF7, HANDLENODELAY, AActor, flags7),
DEFINE_FLAG(MF7, FLYCHEAT, AActor, flags7),
DEFINE_FLAG(FX, RESPAWNINVUL, AActor, effects),
};
@ -461,7 +462,7 @@ static FFlagDef PlayerPawnFlagDefs[] =
static FFlagDef PowerSpeedFlagDefs[] =
{
// PowerSpeed flags
DEFINE_FLAG(PSF, NOTRAIL, APowerSpeed, SpeedFlags),
DEFINE_DEPRECATED_FLAG(NOTRAIL),
};
static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int NumDefs; int Use; } FlagLists[] =
@ -472,7 +473,6 @@ static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int
{ &RUNTIME_CLASS_CASTLESS(AInventory), InventoryFlagDefs, countof(InventoryFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(AWeapon), WeaponFlagDefs, countof(WeaponFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(APlayerPawn), PlayerPawnFlagDefs, countof(PlayerPawnFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(APowerSpeed), PowerSpeedFlagDefs, countof(PowerSpeedFlagDefs), 1 },
};
#define NUM_FLAG_LISTS (countof(FlagLists))
@ -547,6 +547,12 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bo
}
}
}
// Handle that lone PowerSpeed flag - this should be more generalized but it's just this one flag and unlikely to become more so an explicit check will do.
if ((!stricmp(part1, "NOTRAIL") && !strict) || (!stricmp(part1, "POWERSPEED") && !stricmp(part2, "NOTRAIL")))
{
return &PowerSpeedFlagDefs[0];
}
return NULL;
}
@ -782,6 +788,11 @@ void InitThingdef()
PField *dehf = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh);
GlobalSymbols.AddSymbol(dehf);
// set up a variable for the global gameinfo data
PStruct *gistruct = NewNativeStruct("GameInfoStruct", nullptr);
PField *gi = new PField("gameinfo", gistruct, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gameinfo);
GlobalSymbols.AddSymbol(gi);
// set up a variable for the global players array.
PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr);
pstruct->Size = sizeof(player_t);
@ -790,6 +801,9 @@ void InitThingdef()
PField *playerf = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players);
GlobalSymbols.AddSymbol(playerf);
pstruct->AddNativeField("weapons", NewNativeStruct("WeaponSlots", nullptr), myoffsetof(player_t, weapons), VARF_Native);
parray = NewArray(TypeBool, MAXPLAYERS);
playerf = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame);
GlobalSymbols.AddSymbol(playerf);
@ -925,7 +939,7 @@ DEFINE_ACTION_FUNCTION(FString, Replace)
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_STRING(s1);
PARAM_STRING(s2);
self->Substitute(*s1, *s2);
self->Substitute(s1, s2);
return 0;
}

View file

@ -354,6 +354,23 @@ void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int in
break;
case DEPF_INTERHUBSTRIP: // Old system was 0 or 1, so if the flag is cleared, assume 1.
static_cast<AInventory*>(defaults)->InterHubAmount = set ? 0 : 1;
break;
case DEPF_NOTRAIL:
{
FString propname = "@property@powerspeed.notrail";
FName name(propname, true);
if (name != NAME_None)
{
auto propp = dyn_cast<PProperty>(info->Symbols.FindSymbol(name, true));
if (propp != nullptr)
{
*((char*)defaults + propp->Variables[0]->Offset) = set ? 1 : 0;
}
}
break;
}
default:
break; // silence GCC
}
@ -439,12 +456,45 @@ int MatchString (const char *in, const char **strings)
return -1;
}
//==========================================================================
//
// Get access to scripted fields.
// Fortunately there's only a handful that cannot be done with a
// scripted property definition, most notably the powerup and morph stuff.
//
//==========================================================================
static bool PointerCheck(PType *symtype, PType *checktype)
{
auto symptype = dyn_cast<PClassPointer>(symtype);
auto checkptype = dyn_cast<PClassPointer>(checktype);
return symptype != nullptr && checkptype != nullptr && symptype->ClassRestriction->IsDescendantOf(checkptype->ClassRestriction);
}
static void *ScriptVar(DObject *obj, PClass *cls, FName field, PType *type)
{
auto sym = dyn_cast<PField>(cls->Symbols.FindSymbol(field, true));
if (sym && (sym->Type == type || PointerCheck(sym->Type, type)))
{
return (((char*)obj) + sym->Offset);
}
I_Error("Variable %s of type %s not found in %s\n", field.GetChars(), type->DescriptiveName(), cls->TypeName.GetChars());
return nullptr;
}
template<class T>
T &TypedScriptVar(DObject *obj, PClass *cls, FName field, PType *type)
{
return *(T*)ScriptVar(obj, cls, field, type);
}
//==========================================================================
//
// Info Property handlers
//
//==========================================================================
//==========================================================================
//
//==========================================================================
@ -2235,14 +2285,11 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory)
int alpha;
PalEntry *pBlendColor;
bool isgiver = info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver));
if (info->IsDescendantOf(RUNTIME_CLASS(APowerup)))
if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || isgiver)
{
pBlendColor = &((APowerup*)defaults)->BlendColor;
}
else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver)))
{
pBlendColor = &((APowerupGiver*)defaults)->BlendColor;
pBlendColor = &TypedScriptVar<PalEntry>(defaults, info, NAME_BlendColor, TypeColor);
}
else
{
@ -2264,7 +2311,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory)
*pBlendColor = MakeSpecialColormap(v);
return;
}
else if (!stricmp(name, "none") && info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver)))
else if (!stricmp(name, "none") && isgiver)
{
*pBlendColor = MakeSpecialColormap(65535);
return;
@ -2290,13 +2337,9 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, colormap, FFFfff, Inventory)
{
PalEntry * pBlendColor;
if (info->IsDescendantOf(RUNTIME_CLASS(APowerup)))
if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver)))
{
pBlendColor = &((APowerup*)defaults)->BlendColor;
}
else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver)))
{
pBlendColor = &((APowerupGiver*)defaults)->BlendColor;
pBlendColor = &TypedScriptVar<PalEntry>(defaults, info, NAME_BlendColor, TypeColor);
}
else
{
@ -2334,13 +2377,9 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, duration, I, Inventory)
{
int *pEffectTics;
if (info->IsDescendantOf(RUNTIME_CLASS(APowerup)))
if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver)))
{
pEffectTics = &((APowerup*)defaults)->EffectTics;
}
else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver)))
{
pEffectTics = &((APowerupGiver*)defaults)->EffectTics;
pEffectTics = &TypedScriptVar<int>(defaults, info, NAME_EffectTics, TypeSInt32);
}
else
{
@ -2359,13 +2398,9 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, strength, F, Inventory)
{
double *pStrength;
if (info->IsDescendantOf(RUNTIME_CLASS(APowerup)))
if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver)))
{
pStrength = &((APowerup*)defaults)->Strength;
}
else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver)))
{
pStrength = &((APowerupGiver*)defaults)->Strength;
pStrength = &TypedScriptVar<double>(defaults, info, NAME_Strength, TypeFloat64);
}
else
{
@ -2383,13 +2418,10 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, mode, S, Inventory)
{
PROP_STRING_PARM(str, 0);
FName *pMode;
if (info->IsDescendantOf(RUNTIME_CLASS(APowerup)))
if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver)))
{
pMode = &((APowerup*)defaults)->Mode;
}
else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver)))
{
pMode = &((APowerupGiver*)defaults)->Mode;
pMode = &TypedScriptVar<FName>(defaults, info, NAME_Mode, TypeName);
}
else
{
@ -2402,7 +2434,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, mode, S, Inventory)
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY_PREFIX(powerup, type, S, PowerupGiver)
DEFINE_SCRIPTED_PROPERTY_PREFIX(powerup, type, S, PowerupGiver)
{
PROP_STRING_PARM(str, 0);
@ -2422,8 +2454,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, type, S, PowerupGiver)
I_Error("Unknown powerup type %s", str);
}
}
defaults->PowerupType = cls;
TypedScriptVar<PClassActor*>(defaults, info, NAME_PowerupType, NewClassPointer(RUNTIME_CLASS(AActor))) = cls;
}
//==========================================================================
@ -3009,37 +3040,37 @@ DEFINE_CLASS_PROPERTY(unmorphflash, S, MorphProjectile)
//==========================================================================
// (non-fatal with non-existent types only in DECORATE)
//==========================================================================
DEFINE_CLASS_PROPERTY(playerclass, S, PowerMorph)
DEFINE_SCRIPTED_PROPERTY(playerclass, S, PowerMorph)
{
PROP_STRING_PARM(str, 0);
defaults->PlayerClass = FindClassTentativePlayerPawn(str, bag.fromDecorate);
TypedScriptVar<PClassActor*>(defaults, bag.Info, NAME_PlayerClass, NewClassPointer(RUNTIME_CLASS(APlayerPawn))) = FindClassTentativePlayerPawn(str, bag.fromDecorate);
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(morphstyle, M, PowerMorph)
DEFINE_SCRIPTED_PROPERTY(morphstyle, M, PowerMorph)
{
PROP_INT_PARM(i, 0);
defaults->MorphStyle = i;
TypedScriptVar<int>(defaults, bag.Info, NAME_MorphStyle, TypeSInt32) = i;
}
//==========================================================================
// (non-fatal with non-existent types only in DECORATE)
//==========================================================================
DEFINE_CLASS_PROPERTY(morphflash, S, PowerMorph)
DEFINE_SCRIPTED_PROPERTY(morphflash, S, PowerMorph)
{
PROP_STRING_PARM(str, 0);
defaults->MorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate);
TypedScriptVar<PClassActor*>(defaults, bag.Info, NAME_MorphFlash, NewClassPointer(RUNTIME_CLASS(AActor))) = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate);
}
//==========================================================================
// (non-fatal with non-existent types only in DECORATE)
//==========================================================================
DEFINE_CLASS_PROPERTY(unmorphflash, S, PowerMorph)
DEFINE_SCRIPTED_PROPERTY(unmorphflash, S, PowerMorph)
{
PROP_STRING_PARM(str, 0);
defaults->UnMorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate);
TypedScriptVar<PClassActor*>(defaults, bag.Info, NAME_UnMorphFlash, NewClassPointer(RUNTIME_CLASS(AActor))) = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate);
}

View file

@ -393,6 +393,11 @@ struct VMReturn
TagOfs = 0;
RegType = REGT_POINTER;
}
VMReturn() { }
VMReturn(int *loc) { IntAt(loc); }
VMReturn(double *loc) { FloatAt(loc); }
VMReturn(FString *loc) { StringAt(loc); }
VMReturn(void **loc) { PointerAt(loc); }
};
struct VMRegisters;

View file

@ -401,7 +401,7 @@ begin:
OP(MOVEA):
{
ASSERTA(a); ASSERTA(B);
int b = B;
b = B;
reg.a[a] = reg.a[b];
reg.atag[a] = reg.atag[b];
NEXTOP;
@ -409,7 +409,7 @@ begin:
OP(MOVEV2):
{
ASSERTF(a); ASSERTF(B);
int b = B;
b = B;
reg.f[a] = reg.f[b];
reg.f[a + 1] = reg.f[b + 1];
NEXTOP;
@ -417,7 +417,7 @@ begin:
OP(MOVEV3):
{
ASSERTF(a); ASSERTF(B);
int b = B;
b = B;
reg.f[a] = reg.f[b];
reg.f[a + 1] = reg.f[b + 1];
reg.f[a + 2] = reg.f[b + 2];
@ -435,6 +435,18 @@ begin:
reg.a[a] = (reg.a[b] && ((DObject*)(reg.a[b]))->IsKindOf((PClass*)(konsta[C].o))) ? reg.a[b] : nullptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(DYNCASTC_R) :
ASSERTA(a); ASSERTA(B); ASSERTA(C);
b = B;
reg.a[a] = (reg.a[b] && ((PClass*)(reg.a[b]))->IsDescendantOf((PClass*)(reg.a[C]))) ? reg.a[b] : nullptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(DYNCASTC_K) :
ASSERTA(a); ASSERTA(B); ASSERTKA(C);
b = B;
reg.a[a] = (reg.a[b] && ((PClass*)(reg.a[b]))->IsDescendantOf((PClass*)(konsta[C].o))) ? reg.a[b] : nullptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(CAST):
if (C == CAST_I2F)
{
@ -1746,9 +1758,21 @@ static void DoCast(const VMRegisters &reg, const VMFrame *f, int a, int b, int c
break;
case CAST_P2S:
{
ASSERTS(a); ASSERTA(b);
reg.s[a].Format("%s<%p>", reg.atag[b] == ATAG_OBJECT ? (reg.a[b] == nullptr? "Object" : ((DObject*)reg.a[b])->GetClass()->TypeName.GetChars() ) : "Pointer", reg.a[b]);
break;
if (reg.a[b] == nullptr) reg.s[a] = "null";
else if (reg.atag[b] == ATAG_OBJECT)
{
auto op = static_cast<DObject*>(reg.a[b]);
if (op->IsKindOf(RUNTIME_CLASS(PClass))) reg.s[a].Format("Class<%s>", static_cast<PClass*>(op)->TypeName.GetChars());
else reg.s[a].Format("Object<%p>", ((DObject*)reg.a[b])->GetClass()->TypeName.GetChars());
}
else
{
reg.s[a].Format("%s<%p>", "Pointer", reg.a[b]);
}
break;
}
case CAST_S2I:
ASSERTD(a); ASSERTS(b);

View file

@ -36,6 +36,7 @@
#include "dobject.h"
#include "v_text.h"
#include "stats.h"
#include "templates.h"
cycle_t VMCycles[10];
int VMCalls[10];
@ -605,11 +606,16 @@ ADD_STAT(VM)
{
double added = 0;
int addedc = 0;
for (auto d : VMCycles) added += d.TimeMS();
double peak = 0;
for (auto d : VMCycles)
{
added += d.TimeMS();
peak = MAX<double>(peak, d.TimeMS());
}
for (auto d : VMCalls) addedc += d;
memmove(&VMCycles[1], &VMCycles[0], 9 * sizeof(cycle_t));
memmove(&VMCalls[1], &VMCalls[0], 9 * sizeof(int));
VMCycles[0].Reset();
VMCalls[0] = 0;
return FStringf("VM time in last 10 tics: %f ms, %d calls", added, addedc);
return FStringf("VM time in last 10 tics: %f ms, %d calls, peak = %f ms", added, addedc, peak);
}

View file

@ -86,6 +86,8 @@ xx(CAST, cast, CAST, NOP, 0, 0), // xA = xB, conversion specified by C
xx(CASTB, castb, CAST, NOP, 0, 0), // xA = !!xB, type specified by C
xx(DYNCAST_R, dyncast, RPRPRP, NOP, 0, 0), // aA = dyn_cast<aC>(aB);
xx(DYNCAST_K, dyncast, RPRPKP, NOP, 0, 0), // aA = dyn_cast<aKC>(aB);
xx(DYNCASTC_R, dyncastc, RPRPRP, NOP, 0, 0), // aA = dyn_cast<aC>(aB); for class types
xx(DYNCASTC_K, dyncastc, RPRPKP, NOP, 0, 0), // aA = dyn_cast<aKC>(aB);
// Control flow.
xx(TEST, test, RII16, NOP, 0, 0), // if (dA != BC) then pc++

View file

@ -2022,13 +2022,14 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro
if (property != nullptr && property->category != CAT_INFO)
{
if (cls->IsDescendantOf(*property->cls))
auto pcls = PClass::FindActor(property->clsname);
if (cls->IsDescendantOf(pcls))
{
DispatchProperty(property, prop, (AActor *)bag.Info->Defaults, bag);
}
else
{
Error(prop, "'%s' requires an actor of type '%s'\n", propname.GetChars(), (*property->cls)->TypeName.GetChars());
Error(prop, "'%s' requires an actor of type '%s'\n", propname.GetChars(), pcls->TypeName.GetChars());
}
}
else
@ -3313,16 +3314,9 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
{
return new FxReturnStatement(nullptr, *ast);
}
else if (args.Size() == 1)
{
auto arg = args[0];
args[0] = nullptr;
return new FxReturnStatement(arg, *ast);
}
else
{
Error(ast, "Return with multiple values not implemented yet.");
return new FxReturnStatement(nullptr, *ast);
return new FxReturnStatement(args, *ast);
}
}

View file

@ -615,8 +615,8 @@ SoundDecoder *SoundRenderer::CreateDecoder(FileReader *reader)
SoundDecoder *decoder = NULL;
int pos = reader->Tell();
#ifdef HAVE_MPG123
decoder = new MPG123Decoder;
#ifdef HAVE_SNDFILE
decoder = new SndFileDecoder;
if (decoder->open(reader))
return decoder;
reader->Seek(pos, SEEK_SET);
@ -624,8 +624,8 @@ SoundDecoder *SoundRenderer::CreateDecoder(FileReader *reader)
delete decoder;
decoder = NULL;
#endif
#ifdef HAVE_SNDFILE
decoder = new SndFileDecoder;
#ifdef HAVE_MPG123
decoder = new MPG123Decoder;
if (decoder->open(reader))
return decoder;
reader->Seek(pos, SEEK_SET);

View file

@ -14,25 +14,22 @@ static bool inited = false;
off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence)
{
MPG123Decoder *self = reinterpret_cast<MPG123Decoder*>(handle);
FileReader *reader = self->Reader;
FileReader *reader = reinterpret_cast<MPG123Decoder*>(handle)->Reader;
if(whence == SEEK_SET)
offset += self->StartOffset;
else if(whence == SEEK_CUR)
if(whence == SEEK_CUR)
{
if(offset < 0 && reader->Tell()+offset < self->StartOffset)
if(offset < 0 && reader->Tell()+offset < 0)
return -1;
}
else if(whence == SEEK_END)
{
if(offset < 0 && reader->GetLength()+offset < self->StartOffset)
if(offset < 0 && reader->GetLength()+offset < 0)
return -1;
}
if(reader->Seek(offset, whence) != 0)
return -1;
return reader->Tell() - self->StartOffset;
return reader->Tell();
}
ssize_t MPG123Decoder::file_read(void *handle, void *buffer, size_t bytes)
@ -71,45 +68,7 @@ bool MPG123Decoder::open(FileReader *reader)
}
Reader = reader;
StartOffset = 0;
char data[10];
if(file_read(this, data, 10) != 10)
return false;
int start_offset = 0;
// Check for ID3 tags and skip them
if(memcmp(data, "ID3", 3) == 0 &&
(BYTE)data[3] <= 4 && (BYTE)data[4] != 0xff &&
(data[5]&0x0f) == 0 && (data[6]&0x80) == 0 &&
(data[7]&0x80) == 0 && (data[8]&0x80) == 0 &&
(data[9]&0x80) == 0)
{
// ID3v2
start_offset = (data[6]<<21) | (data[7]<<14) |
(data[8]<< 7) | (data[9] );
start_offset += ((data[5]&0x10) ? 20 : 10);
}
StartOffset = start_offset;
if(file_lseek(this, 0, SEEK_SET) != 0)
return false;
// Check for a frame header
bool frame_ok = false;
if(file_read(this, data, 3) == 3)
{
if((BYTE)data[0] == 0xff &&
((data[1]&0xfe) == 0xfa/*MPEG-1*/ || (data[1]&0xfe) == 0xf2/*MPEG-2*/))
{
int brate_idx = (data[2]>>4) & 0x0f;
int srate_idx = (data[2]>>2) & 0x03;
if(brate_idx != 0 && brate_idx != 15 && srate_idx != 3)
frame_ok = (file_lseek(this, 0, SEEK_SET) == 0);
}
}
if(frame_ok)
{
MPG123 = mpg123_new(NULL, NULL);
if(mpg123_replace_reader_handle(MPG123, file_read, file_lseek, NULL) == MPG123_OK &&

View file

@ -30,7 +30,6 @@ private:
bool Done;
FileReader *Reader;
int StartOffset;
static off_t file_lseek(void *handle, off_t offset, int whence);
static ssize_t file_read(void *handle, void *buffer, size_t bytes);

View file

@ -60,6 +60,7 @@ bool SndFileDecoder::open(FileReader *reader)
SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell };
Reader = reader;
SndInfo.format = 0;
SndFile = sf_open_virtual(&sfio, SFM_READ, &SndInfo, this);
if (SndFile)
{

View file

@ -15,6 +15,7 @@
#include "zscript/inventory/powerups.txt"
#include "zscript/shared/player.txt"
#include "zscript/shared/player_cheat.txt"
#include "zscript/shared/morph.txt"
#include "zscript/shared/botstuff.txt"
#include "zscript/shared/sharedmisc.txt"

View file

@ -487,6 +487,8 @@ class Actor : Thinker native
native void AddInventory(Inventory inv);
native void RemoveInventory(Inventory inv);
native void ClearInventory();
native bool GiveInventory(class<Inventory> type, int amount, bool givecheat = false);
native bool TakeInventory(class<Inventory> itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false);
native Inventory FindInventory(class<Inventory> itemtype, bool subclass = false);
native Inventory GiveInventoryType(class<Inventory> itemtype);
native Inventory DropInventory (Inventory item);

View file

@ -106,11 +106,15 @@ extend class Actor
//==========================================================================
//
//
// rather pointless these days to do it this way.
//
//==========================================================================
native bool CheckArmorType(name Type, int amount = 1);
bool CheckArmorType(name Type, int amount = 1)
{
let myarmor = BasicArmor(FindInventory("BasicArmor"));
return myarmor != null && myarmor.ArmorType == type && myarmor.Amount >= amount;
}
action state A_JumpIfArmorType(name Type, statelabel label, int amount = 1)
{

View file

@ -32,11 +32,23 @@ struct TexMan
native static TextureID CheckForTexture(String name, int usetype, int flags = TryAny);
}
struct Screen
struct Screen native
{
native static void DrawHUDTexture(TextureID tex, double x, double y);
}
struct Console native
{
native static void HideConsole();
}
struct GameInfoStruct native
{
// will be extended as needed.
native Name backpacktype;
native double Armor2Percent;
}
class Object native
{
native bool bDestroyed;
@ -200,6 +212,7 @@ struct DehInfo native
native double ExplosionAlpha;
native int NoAutofreeze;
native int BFGCells;
native int BlueAC;
}
struct State native

View file

@ -64,6 +64,7 @@ class Inventory : Actor native
virtual bool GetNoTeleportFreeze() { return false; }
virtual void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive) {}
virtual void AlterWeaponSprite(VisStyle vis, in out int changed) {}
virtual void OwnerDied() {}
native bool GoAway();
native void GoAwayAndDie();

View file

@ -1,11 +1,11 @@
class PowerupGiver : Inventory native
class PowerupGiver : Inventory
{
native Class<Actor> PowerupType;
native int EffectTics; // Non-0 to override the powerup's default tics
native color BlendColor; // Non-0 to override the powerup's default blend
native Name Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility
native double Strength; // Meaning depends on powerup - currently used only by Invisibility
Class<Actor> PowerupType;
int EffectTics; // Non-0 to override the powerup's default tics
color BlendColor; // Non-0 to override the powerup's default blend
Name Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility
double Strength; // Meaning depends on powerup - currently used only by Invisibility
Default
{
@ -14,6 +14,48 @@ class PowerupGiver : Inventory native
+INVENTORY.FANCYPICKUPSOUND
Inventory.PickupSound "misc/p_pkup";
}
//===========================================================================
//
// APowerupGiver :: Use
//
//===========================================================================
override bool Use (bool pickup)
{
if (PowerupType == NULL) return true; // item is useless
if (Owner == null) return true;
let power = Powerup(Spawn (PowerupType));
if (EffectTics != 0)
{
power.EffectTics = EffectTics;
}
if (BlendColor != 0)
{
if (BlendColor != Powerup.SPECIALCOLORMAP_MASK | 65535) power.BlendColor = BlendColor;
else power.BlendColor = 0;
}
if (Mode != 'None')
{
power.Mode = Mode;
}
if (Strength != 0)
{
power.Strength = Strength;
}
power.bAlwaysPickup |= bAlwaysPickup;
power.bAdditiveTime |= bAdditiveTime;
power.bNoTeleportFreeze |= bNoTeleportFreeze;
if (power.CallTryPickup (Owner))
{
return true;
}
power.GoAwayAndDie ();
return false;
}
}
class Powerup : Inventory native
@ -22,13 +64,245 @@ class Powerup : Inventory native
native color BlendColor;
native Name Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility
native double Strength; // Meaning depends on powerup - currently used only by Invisibility
native int Colormap;
const SPECIALCOLORMAP_MASK = 0x00b60000;
// Note, that while this is an inventory flag, it only has meaning on an active powerup.
override bool GetNoTeleportFreeze() { return bNoTeleportFreeze; }
override bool GetNoTeleportFreeze()
{
return bNoTeleportFreeze;
}
//===========================================================================
//
// APowerup :: Tick
//
//===========================================================================
override void Tick ()
{
// Powerups cannot exist outside an inventory
if (Owner == NULL)
{
Destroy ();
}
if (EffectTics > 0 && --EffectTics == 0)
{
Destroy ();
}
}
//===========================================================================
//
// APowerup :: HandlePickup
//
//===========================================================================
override bool HandlePickup (Inventory item)
{
if (item.GetClass() == GetClass())
{
let power = Powerup(item);
if (power.EffectTics == 0)
{
power.bPickupGood = true;
return true;
}
// Color gets transferred if the new item has an effect.
// Increase the effect's duration.
if (power.bAdditiveTime)
{
EffectTics += power.EffectTics;
BlendColor = power.BlendColor;
}
// If it's not blinking yet, you can't replenish the power unless the
// powerup is required to be picked up.
else if (EffectTics > BLINKTHRESHOLD && !power.bAlwaysPickup)
{
return true;
}
// Reset the effect duration.
else if (power.EffectTics > EffectTics)
{
EffectTics = power.EffectTics;
BlendColor = power.BlendColor;
}
power.bPickupGood = true;
return true;
}
return false;
}
//===========================================================================
//
// APowerup :: CreateCopy
//
//===========================================================================
override Inventory CreateCopy (Actor other)
{
// Get the effective effect time.
EffectTics = abs (EffectTics);
// Abuse the Owner field to tell the
// InitEffect method who started it;
// this should be cleared afterwards,
// as this powerup instance is not
// properly attached to anything yet.
Owner = other;
// Actually activate the powerup.
InitEffect ();
// Clear the Owner field, unless it was
// changed by the activation, for example,
// if this instance is a morph powerup;
// the flag tells the caller that the
// ownership has changed so that they
// can properly handle the situation.
if (!bCreateCopyMoved)
{
Owner = NULL;
}
// All done.
return self;
}
//===========================================================================
//
// APowerup :: CreateTossable
//
// Powerups are never droppable, even without IF_UNDROPPABLE set.
//
//===========================================================================
override Inventory CreateTossable ()
{
return NULL;
}
//===========================================================================
//
// APowerup :: InitEffect
//
//===========================================================================
virtual void InitEffect()
{
// initialize this only once instead of recalculating repeatedly.
Colormap = ((BlendColor & 0xFFFF0000) == SPECIALCOLORMAP_MASK)? BlendColor & 0xffff : PlayerInfo.NOFIXEDCOLORMAP;
}
//===========================================================================
//
// APowerup :: DoEffect
//
//===========================================================================
override void DoEffect ()
{
if (Owner == NULL || Owner.player == NULL)
{
return;
}
if (EffectTics > 0)
{
if (Colormap != PlayerInfo.NOFIXEDCOLORMAP)
{
if (!isBlinking())
{
Owner.player.fixedcolormap = Colormap;
}
else if (Owner.player.fixedcolormap == Colormap)
{
// only unset if the fixed colormap comes from this item
Owner.player.fixedcolormap = PlayerInfo.NOFIXEDCOLORMAP;
}
}
}
}
//===========================================================================
//
// APowerup :: EndEffect
//
//===========================================================================
virtual void EndEffect ()
{
if (colormap != PlayerInfo.NOFIXEDCOLORMAP && Owner && Owner.player && Owner.player.fixedcolormap == colormap)
{ // only unset if the fixed colormap comes from this item
Owner.player.fixedcolormap = PlayerInfo.NOFIXEDCOLORMAP;
}
}
//===========================================================================
//
// APowerup :: Destroy
//
//===========================================================================
override void OnDestroy ()
{
EndEffect ();
Super.OnDestroy();
}
//===========================================================================
//
// APowerup :: GetBlend
//
//===========================================================================
override color GetBlend ()
{
if (Colormap != Player.NOFIXEDCOLORMAP) return 0;
if (isBlinking()) return 0;
return BlendColor;
}
//===========================================================================
//
// APowerup :: DrawPowerup
//
//===========================================================================
override bool DrawPowerup (int x, int y)
{
if (!Icon.isValid())
{
return false;
}
if (!isBlinking())
{
screen.DrawHUDTexture(Icon, x, y);
}
return true;
}
//===========================================================================
//
// APowerup :: isBlinking
//
//===========================================================================
virtual bool isBlinking()
{
return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !bNoScreenBlink);
}
//===========================================================================
//
// APowerup :: OwnerDied
//
// Powerups don't last beyond death.
//
//===========================================================================
override void OwnerDied ()
{
Destroy ();
}
native virtual void InitEffect();
native virtual void EndEffect();
native bool isBlinking();
}
@ -38,14 +312,134 @@ class Powerup : Inventory native
//
//===========================================================================
class PowerInvulnerable : Powerup native
class PowerInvulnerable : Powerup
{
Default
{
Powerup.Duration -30;
inventory.icon "SPSHLD0";
}
//===========================================================================
//
// APowerInvulnerable :: InitEffect
//
//===========================================================================
override void InitEffect ()
{
Super.InitEffect();
Owner.bRespawnInvul = false;
Owner.bInvulnerable = true;
if (Mode == 'None' && Owner is "PlayerPawn")
{
Mode = PlayerPawn(Owner).InvulMode;
}
if (Mode == 'Reflective')
{
Owner.bReflective = true;
}
}
//===========================================================================
//
// APowerInvulnerable :: DoEffect
//
//===========================================================================
override void DoEffect ()
{
Super.DoEffect ();
if (Owner == NULL)
{
return;
}
if (Mode == 'Ghost')
{
if (!Owner.bShadow)
{
// Don't mess with the translucency settings if an
// invisibility powerup is active.
let alpha = Owner.Alpha;
if (!(level.time & 7) && alpha > 0 && alpha < 1)
{
if (alpha == HX_SHADOW)
{
alpha = HX_ALTSHADOW;
}
else
{
alpha = 0;
Owner.bNonShootable = true;
}
}
if (!(level.time & 31))
{
if (alpha == 0)
{
Owner.bNonShootable = false;
alpha = HX_ALTSHADOW;
}
else
{
alpha = HX_SHADOW;
}
}
Owner.A_SetRenderStyle(alpha, STYLE_Translucent);
}
else
{
Owner.bNonShootable = false;
}
}
}
//===========================================================================
//
// APowerInvulnerable :: EndEffect
//
//===========================================================================
override void EndEffect ()
{
Super.EndEffect();
if (Owner == NULL)
{
return;
}
Owner.bRespawnInvul = false;
Owner.bInvulnerable = false;
if (Mode == 'Ghost')
{
Owner.bNonShootable = false;
if (!bShadow)
{
// Don't mess with the translucency settings if an
// invisibility powerup is active.
Owner.A_SetRenderStyle(1, STYLE_Normal);
}
}
else if (Mode == 'Reflective')
{
Owner.bReflective = false;
}
if (Owner.player != NULL)
{
Owner.player.fixedcolormap = PlayerInfo.NOFIXEDCOLORMAP;
}
}
//===========================================================================
//
// APowerInvulnerable :: AlterWeaponSprite
//
//===========================================================================
override void AlterWeaponSprite (VisStyle vis, in out int changed)
{
if (Owner != NULL)
@ -761,13 +1155,12 @@ class PowerWeaponLevel2 : Powerup
//
//===========================================================================
class PowerSpeed : Powerup native
class PowerSpeed : Powerup
{
native int SpeedFlags;
int NoTrail;
Property NoTrail: NoTrail;
const PSF_NOTRAIL = 1;
Default
{
Powerup.Duration -45;
@ -797,7 +1190,7 @@ class PowerSpeed : Powerup native
if (Owner.player.cheats & CF_PREDICTING)
return;
if (SpeedFlags & PSF_NOTRAIL)
if (NoTrail)
return;
if (level.time & 1)
@ -808,7 +1201,7 @@ class PowerSpeed : Powerup native
for (Inventory item = Inv; item != NULL; item = item.Inv)
{
let sitem = PowerSpeed(item);
if (sitem != null && !(sitem.SpeedFlags & PSF_NOTRAIL))
if (sitem != null && !NoTrail)
{
return;
}
@ -1552,13 +1945,13 @@ class PowerInfiniteAmmo : Powerup
//
//===========================================================================
class PowerMorph : Powerup native
class PowerMorph : Powerup
{
native Class<PlayerPawn> PlayerClass;
native Class<Actor> MorphFlash, UnMorphFlash;
native int MorphStyle;
native PlayerInfo MorphedPlayer;
Class<PlayerPawn> PlayerClass;
Class<Actor> MorphFlash, UnMorphFlash;
int MorphStyle;
PlayerInfo MorphedPlayer;
Default
{
Powerup.Duration -40;

View file

@ -125,3 +125,7 @@ class WeaponPiece : Inventory native
}
}
struct WeaponSlots native
{
native bool, int, int LocateWeapon(class<Weapon> weap);
}

View file

@ -102,6 +102,36 @@ class PlayerPawn : Actor native
virtual void MorphPlayerThink()
{
}
virtual void OnRespawn()
{
if (sv_respawnprotect && (multiplayer || alwaysapplydmflags))
{
let invul = Powerup(Spawn("PowerInvulnerable"));
invul.EffectTics = 3 * TICRATE;
invul.BlendColor = 0; // don't mess with the view
invul.bUndroppable = true; // Don't drop this
bRespawnInvul = true; // [RH] special effect
}
}
// This is for SBARINFO.
int, int GetEffectTicsForItem(class<Inventory> item)
{
let pg = (class<PowerupGiver>)(item);
if (pg != null)
{
let powerupType = (class<Powerup>)(GetDefaultByType(pg).PowerupType);
let powerup = Powerup(FindInventory(powerupType));
if(powerup != null)
{
let maxtics = GetDefaultByType(powerupType).EffectTics;
if (maxtics == 0) maxtics = powerup.default.EffectTics;
return powerup.EffectTics, maxtics;
}
}
return 0, 0;
}
native int GetMaxHealth();
native bool ResetAirSupply (bool playgasp = false);
@ -260,11 +290,12 @@ struct PlayerInfo native // this is what internally is known as player_t
native Actor ConversationPC;
native double ConversationNPCAngle;
native bool ConversationFaceTalker;
//native WeaponSlots weapons; <- defined internally
/* these are not doable yet
ticcmd_t cmd;
usercmd_t original_cmd;
userinfo_t userinfo; // [RH] who is this?
FWeaponSlots weapons;
userinfo_t userinfo;
*/
@ -278,5 +309,6 @@ FWeaponSlots weapons;
native PSprite FindPSprite(int id);
native void SetLogNumber (int text);
native void SetLogText (String text);
native String GetUserName();
}

View file

@ -0,0 +1,402 @@
/*
** player_cheat.txt
**
**---------------------------------------------------------------------------
** Copyright 1999-2016 Randy Heit
** Copyright 2006-2017 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
extend class PlayerPawn
{
enum EAll
{
ALL_NO,
ALL_YES,
ALL_YESYES
}
native void CheatSuicide();
virtual void CheatGive (String name, int amount)
{
int i;
Class<Inventory> type;
let player = self.player;
if (PlayerNumber() != consoleplayer)
A_Log(format ("%s is a cheater: give %s\n", player.GetUserName(), name));
if (player.mo == NULL || player.health <= 0)
{
return;
}
int giveall = ALL_NO;
if (name ~== "all")
{
giveall = ALL_YES;
}
else if (name ~== "everything")
{
giveall = ALL_YESYES;
}
if (name ~== "health")
{
if (amount > 0)
{
health += amount;
player.health = health;
}
else
{
player.health = health = GetMaxHealth();
}
}
if (giveall || name ~== "backpack")
{
// Select the correct type of backpack based on the game
type = (class<Inventory>)(gameinfo.backpacktype);
if (type != NULL)
{
GiveInventory(type, 1, true);
}
if (!giveall)
return;
}
if (giveall || name ~== "ammo")
{
// Find every unique type of ammo. Give it to the player if
// he doesn't have it already, and set each to its maximum.
for (i = 0; i < AllActorClasses.Size(); ++i)
{
type = (class<Inventory>)(AllActorClasses[i]);
if (type != null && type.GetParentClass() == "Ammo")
{
let ammoitem = FindInventory(type);
if (ammoitem == NULL)
{
ammoitem = Inventory(Spawn (type));
ammoitem.AttachToOwner (self);
ammoitem.Amount = ammoitem.MaxAmount;
}
else if (ammoitem.Amount < ammoitem.MaxAmount)
{
ammoitem.Amount = ammoitem.MaxAmount;
}
}
}
if (!giveall)
return;
}
if (giveall || name ~== "armor")
{
if (GameType() != GAME_Hexen)
{
let armoritem = BasicArmorPickup(Spawn("BasicArmorPickup"));
armoritem.SaveAmount = 100*deh.BlueAC;
armoritem.SavePercent = gameinfo.Armor2Percent > 0? gameinfo.Armor2Percent : 0.5;
if (!armoritem.CallTryPickup (self))
{
armoritem.Destroy ();
}
}
else
{
for (i = 0; i < 4; ++i)
{
let armoritem = Inventory(Spawn("HexenArmor"));
armoritem.health = i;
armoritem.Amount = 0;
if (!armoritem.CallTryPickup (self))
{
armoritem.Destroy ();
}
}
}
if (!giveall)
return;
}
if (giveall || name ~== "keys")
{
for (int i = 0; i < AllActorClasses.Size(); ++i)
{
if (AllActorClasses[i] is "Key")
{
readonly<Key> keyitem = GetDefaultByType ((class<Key>)(AllActorClasses[i]));
if (keyitem.KeyNumber != 0)
{
let item = Inventory(Spawn(AllActorClasses[i]));
if (!item.CallTryPickup (self))
{
item.Destroy ();
}
}
}
}
if (!giveall)
return;
}
if (giveall || name ~== "weapons")
{
let savedpending = player.PendingWeapon;
for (i = 0; i < AllActorClasses.Size(); ++i)
{
let type = (class<Weapon>)(AllActorClasses[i]);
if (type != null && type != "Weapon")
{
// Don't give replaced weapons unless the replacement was done by Dehacked.
let rep = GetReplacement(type);
if (rep == type || rep is "DehackedPickup")
{
// Give the weapon only if it is set in a weapon slot.
if (player.weapons.LocateWeapon(type))
{
readonly<Weapon> def = GetDefaultByType (type);
if (giveall == ALL_YESYES || !def.bCheatNotWeapon)
{
GiveInventory(type, 1, true);
}
}
}
}
}
player.PendingWeapon = savedpending;
if (!giveall)
return;
}
if (giveall || name ~== "artifacts")
{
for (i = 0; i < AllActorClasses.Size(); ++i)
{
type = (class<Inventory>)(AllActorClasses[i]);
if (type!= null)
{
let def = GetDefaultByType (type);
if (def.Icon.isValid() && def.MaxAmount > 1 &&
!(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor"))
{
// Do not give replaced items unless using "give everything"
if (giveall == ALL_YESYES || GetReplacement(type) == type)
{
GiveInventory(type, amount <= 0 ? def.MaxAmount : amount, true);
}
}
}
}
if (!giveall)
return;
}
if (giveall || name ~== "puzzlepieces")
{
for (i = 0; i < AllActorClasses.Size(); ++i)
{
let type = (class<PuzzleItem>)(AllActorClasses[i]);
if (type != null)
{
let def = GetDefaultByType (type);
if (def.Icon.isValid())
{
// Do not give replaced items unless using "give everything"
if (giveall == ALL_YESYES || GetReplacement(type) == type)
{
GiveInventory(type, amount <= 0 ? def.MaxAmount : amount, true);
}
}
}
}
if (!giveall)
return;
}
if (giveall)
return;
type = name;
if (type == NULL)
{
if (PlayerNumber() == consoleplayer)
A_Log(format("Unknown item \"%s\"\n", name));
}
else
{
GiveInventory(type, amount, true);
}
return;
}
void CheatTakeType(class<Inventory> deletetype)
{
for (int i = 0; i < AllActorClasses.Size(); ++i)
{
let type = (class<Inventory>)(AllActorClasses[i]);
if (type != null && type is deletetype)
{
let pack = FindInventory(type);
if (pack) pack.Destroy();
}
}
}
virtual void CheatTake (String name, int amount)
{
bool takeall;
Class<Inventory> type;
let player = self.player;
if (player.mo == NULL || player.health <= 0)
{
return;
}
takeall = name ~== "all";
if (!takeall && name ~== "health")
{
if (player.mo.health - amount <= 0
|| player.health - amount <= 0
|| amount == 0)
{
CheatSuicide ();
if (PlayerNumber() == consoleplayer)
Console.HideConsole ();
return;
}
if (amount > 0)
{
if (player.mo)
{
player.mo.health -= amount;
player.health = player.mo.health;
}
else
{
player.health -= amount;
}
}
if (!takeall)
return;
}
if (takeall || name ~== "backpack")
{
CheatTakeType("BackpackItem");
if (!takeall)
return;
}
if (takeall || name ~== "ammo")
{
CheatTakeType("Ammo");
if (!takeall)
return;
}
if (takeall || name ~== "armor")
{
CheatTakeType("Armor");
if (!takeall)
return;
}
if (takeall || name ~== "keys")
{
CheatTakeType("Key");
if (!takeall)
return;
}
if (takeall || name ~== "weapons")
{
CheatTakeType("Weapon");
CheatTakeType("WeaponHolder");
player.ReadyWeapon = null;
player.PendingWeapon = WP_NOCHANGE;
if (!takeall)
return;
}
if (takeall || name ~== "artifacts")
{
for (int i = 0; i < AllActorClasses.Size(); ++i)
{
type = (class<Inventory>)(AllActorClasses[i]);
if (type!= null && !(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor"))
{
let pack = FindInventory(type);
if (pack) pack.Destroy();
}
}
if (!takeall)
return;
}
if (takeall || name ~== "puzzlepieces")
{
CheatTakeType("PuzzleItem");
if (!takeall)
return;
}
if (takeall)
return;
type = name;
if (type == NULL)
{
if (PlayerNumber() == consoleplayer)
A_Log(format("Unknown item \"%s\"\n", name));
}
else
{
TakeInventory(type, max(amount, 1));
}
return;
}
}

View file

@ -166,7 +166,7 @@ class AlienSpectre1 : SpectralMonster
player.GiveInventoryType ("UpgradeAccuracy");
}
Sigil sigl = Sigil(player.FindInventory("Sigil"));
if (sigl != null /*&& sigl.NumPieces == 5*/)
if (sigl != null && sigl.health == 5)
{ // You wield the power of the complete Sigil.
log = 85;
}