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

This commit is contained in:
Magnus Norddahl 2017-01-20 00:22:29 +01:00
commit 545ae678e8
27 changed files with 773 additions and 1008 deletions

View file

@ -26,6 +26,8 @@ IMPLEMENT_POINTERS_START(DBot)
IMPLEMENT_POINTER(last_mate)
IMPLEMENT_POINTERS_END
DEFINE_FIELD(DBot, dest)
DBot::DBot ()
: DThinker(STAT_BOT)
{

View file

@ -222,23 +222,17 @@ 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
// behavior, for every thing touched by dehacked that has the MF_PICKUP flag,
// a new subclass of ADehackedPickup will be created with properties copied
// a new subclass of DehackedPickup will be created with properties copied
// from the original actor's defaults. The original actor is then changed to
// spawn the new class.
IMPLEMENT_CLASS(ADehackedPickup, false, true)
IMPLEMENT_POINTERS_START(ADehackedPickup)
IMPLEMENT_POINTER(RealPickup)
IMPLEMENT_POINTERS_END
TArray<PClassActor *> TouchedActors;
char *UnchangedSpriteNames;
int NumUnchangedSprites;
bool changedStates;
// Sprite<->Class map for ADehackedPickup::DetermineType
// Sprite<->Class map for DehackedPickup::DetermineType
static struct DehSpriteMap
{
char Sprite[5];
@ -3009,12 +3003,12 @@ void FinishDehPatch ()
// Create a new class that will serve as the actual pickup
char typeNameBuilder[32];
//
auto dehtype = PClass::FindActor(NAME_DehackedPickup);
do
{
// Retry until we find a free name. This is unlikely to happen but not impossible.
mysnprintf(typeNameBuilder, countof(typeNameBuilder), "DehackedPickup%d", nameindex++);
subclass = static_cast<PClassActor *>(RUNTIME_CLASS(ADehackedPickup)->
CreateDerivedClass(typeNameBuilder, sizeof(ADehackedPickup)));
subclass = static_cast<PClassActor *>(dehtype->CreateDerivedClass(typeNameBuilder, dehtype->Size));
}
while (subclass == nullptr);
@ -3120,93 +3114,10 @@ void FinishDehPatch ()
WeaponNames.ShrinkToFit();
}
void ModifyDropAmount(AInventory *inv, int dropamount);
bool ADehackedPickup::TryPickup (AActor *&toucher)
DEFINE_ACTION_FUNCTION(ADehackedPickup, DetermineType)
{
PClassActor *type = DetermineType ();
if (type == NULL)
{
return false;
}
RealPickup = static_cast<AInventory *>(Spawn (type, Pos(), NO_REPLACE));
if (RealPickup != NULL)
{
// The internally spawned item should never count towards statistics.
RealPickup->ClearCounters();
if (!(flags & MF_DROPPED))
{
RealPickup->flags &= ~MF_DROPPED;
}
// If this item has been dropped by a monster the
// amount of ammo this gives must be adjusted.
if (droppedbymonster)
{
ModifyDropAmount(RealPickup, 0);
}
if (!RealPickup->CallTryPickup (toucher))
{
RealPickup->Destroy ();
RealPickup = NULL;
return false;
}
GoAwayAndDie ();
return true;
}
return false;
}
PARAM_SELF_PROLOGUE(AInventory);
FString ADehackedPickup::PickupMessage ()
{
if (RealPickup != nullptr)
return RealPickup->PickupMessage ();
else return "";
}
bool ADehackedPickup::ShouldStay ()
{
if (RealPickup != nullptr)
return RealPickup->CallShouldStay ();
else return true;
}
bool ADehackedPickup::ShouldRespawn ()
{
if (RealPickup != nullptr)
return RealPickup->ShouldRespawn ();
else return false;
}
void ADehackedPickup::PlayPickupSound (AActor *toucher)
{
if (RealPickup != nullptr)
RealPickup->CallPlayPickupSound (toucher);
}
void ADehackedPickup::DoPickupSpecial (AActor *toucher)
{
Super::DoPickupSpecial (toucher);
// If the real pickup hasn't joined the toucher's inventory, make sure it
// doesn't stick around.
if (RealPickup != nullptr && RealPickup->Owner != toucher)
{
RealPickup->Destroy ();
}
RealPickup = nullptr;
}
void ADehackedPickup::OnDestroy ()
{
if (RealPickup != nullptr)
{
RealPickup->Destroy ();
RealPickup = nullptr;
}
Super::OnDestroy();
}
PClassActor *ADehackedPickup::DetermineType ()
{
// Look at the actor's current sprite to determine what kind of
// item to pretend to me.
int min = 0;
@ -3215,10 +3126,10 @@ PClassActor *ADehackedPickup::DetermineType ()
while (min <= max)
{
int mid = (min + max) / 2;
int lex = memcmp (DehSpriteMappings[mid].Sprite, sprites[sprite].name, 4);
int lex = memcmp (DehSpriteMappings[mid].Sprite, sprites[self->sprite].name, 4);
if (lex == 0)
{
return PClass::FindActor(DehSpriteMappings[mid].ClassName);
ACTION_RETURN_OBJECT(PClass::FindActor(DehSpriteMappings[mid].ClassName));
}
else if (lex < 0)
{
@ -3229,11 +3140,6 @@ PClassActor *ADehackedPickup::DetermineType ()
max = mid - 1;
}
}
return NULL;
ACTION_RETURN_OBJECT(nullptr);
}
void ADehackedPickup::Serialize(FSerializer &arc)
{
Super::Serialize(arc);
arc("droppedbymonster", droppedbymonster);
}

View file

@ -34,29 +34,6 @@
#ifndef __D_DEHACK_H__
#define __D_DEHACK_H__
#include "a_pickups.h"
class ADehackedPickup : public AInventory
{
DECLARE_CLASS (ADehackedPickup, AInventory)
HAS_OBJECT_POINTERS
public:
void OnDestroy() override;
FString PickupMessage ();
bool ShouldRespawn ();
bool ShouldStay ();
bool TryPickup (AActor *&toucher);
void PlayPickupSound (AActor *toucher);
void DoPickupSpecial (AActor *toucher);
void Serialize(FSerializer &arc);
private:
PClassActor *DetermineType ();
AInventory *RealPickup;
public:
bool droppedbymonster;
};
int D_LoadDehLumps();
bool D_LoadDehLump(int lumpnum);
bool D_LoadDehFile(const char *filename);

View file

@ -478,6 +478,7 @@ public:
void Destroy();
// Add other types as needed.
bool &BoolVar(FName field);
int &IntVar(FName field);
PalEntry &ColorVar(FName field);
FName &NameVar(FName field);

View file

@ -1073,6 +1073,11 @@ enum ETypeVal : BYTE
VAL_Class,
};
inline bool &DObject::BoolVar(FName field)
{
return *(bool*)ScriptVar(field, TypeBool);
}
inline int &DObject::IntVar(FName field)
{
return *(int*)ScriptVar(field, TypeSInt32);

View file

@ -39,7 +39,7 @@ void PClassInventory::DeriveData(PClass *newclass)
Super::DeriveData(newclass);
PClassInventory *newc = static_cast<PClassInventory *>(newclass);
newc->PickupMessage = PickupMessage;
newc->PickupMsg = PickupMsg;
newc->GiveQuest = GiveQuest;
newc->AltHUDIcon = AltHUDIcon;
newc->ForbiddenToPlayerClass = ForbiddenToPlayerClass;
@ -79,19 +79,16 @@ void PClassInventory::Finalize(FStateDefinitions &statedef)
((AActor*)Defaults)->flags |= MF_SPECIAL;
}
int AInventory::StaticLastMessageTic;
FString AInventory::StaticLastMessage;
IMPLEMENT_CLASS(AInventory, false, true)
IMPLEMENT_POINTERS_START(AInventory)
IMPLEMENT_POINTER(Owner)
IMPLEMENT_POINTER(Owner)
IMPLEMENT_POINTERS_END
DEFINE_FIELD_BIT(AInventory, ItemFlags, bPickupGood, IF_PICKUPGOOD)
DEFINE_FIELD_BIT(AInventory, ItemFlags, bCreateCopyMoved, IF_CREATECOPYMOVED)
DEFINE_FIELD_BIT(AInventory, ItemFlags, bInitEffectFailed, IF_INITEFFECTFAILED)
DEFINE_FIELD(AInventory, Owner)
DEFINE_FIELD(AInventory, Owner)
DEFINE_FIELD(AInventory, Amount)
DEFINE_FIELD(AInventory, MaxAmount)
DEFINE_FIELD(AInventory, InterHubAmount)
@ -101,6 +98,8 @@ DEFINE_FIELD(AInventory, DropTime)
DEFINE_FIELD(AInventory, SpawnPointClass)
DEFINE_FIELD(AInventory, PickupFlash)
DEFINE_FIELD(AInventory, PickupSound)
DEFINE_FIELD(PClassInventory, PickupMsg)
DEFINE_FIELD(PClassInventory, GiveQuest)
//===========================================================================
//
@ -179,70 +178,6 @@ void AInventory::MarkPrecacheSounds() const
PickupSound.MarkUsed();
}
//===========================================================================
//
// AInventory :: SpecialDropAction
//
// Called by P_DropItem. Return true to prevent the standard drop tossing.
// A few Strife items that are meant to trigger actions rather than be
// picked up use this. Normal items shouldn't need it.
//
//===========================================================================
bool AInventory::SpecialDropAction (AActor *dropper)
{
return false;
}
DEFINE_ACTION_FUNCTION(AInventory, SpecialDropAction)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT_NOT_NULL(dropper, AActor);
ACTION_RETURN_BOOL(self->SpecialDropAction(dropper));
}
bool AInventory::CallSpecialDropAction(AActor *dropper)
{
IFVIRTUAL(AInventory, SpecialDropAction)
{
VMValue params[2] = { (DObject*)this, (DObject*)dropper };
VMReturn ret;
int retval;
ret.IntAt(&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
return !!retval;
}
return SpecialDropAction(dropper);
}
//===========================================================================
//
// AInventory :: ShouldRespawn
//
// Returns true if the item should hide itself and reappear later when picked
// up.
//
//===========================================================================
bool AInventory::ShouldRespawn ()
{
if ((ItemFlags & IF_BIGPOWERUP) && !(dmflags2 & DF2_RESPAWN_SUPER)) return false;
if (ItemFlags & IF_NEVERRESPAWN) return false;
return !!((dmflags & DF_ITEMS_RESPAWN) || (ItemFlags & IF_ALWAYSRESPAWN));
}
//===========================================================================
//
// AInventory :: BeginPlay
//
//===========================================================================
void AInventory::BeginPlay ()
{
Super::BeginPlay ();
flags |= MF_DROPPED; // [RH] Items are dropped by default
}
//===========================================================================
//
// AInventory :: Grind
@ -270,189 +205,6 @@ bool AInventory::Grind(bool items)
return Super::Grind(items);
}
//===========================================================================
//
// AInventory :: DoEffect
//
// Handles any effect an item might apply to its owner
// Normally only used by subclasses of Powerup
//
//===========================================================================
void AInventory::DoEffect()
{
IFVIRTUAL(AInventory, DoEffect)
{
VMValue params[1] = { (DObject*)this };
VMFrameStack stack;
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
}
//===========================================================================
//
// AInventory :: HandlePickup
//
// Returns true if the pickup was handled (or should not happen at all),
// false if not.
//
//===========================================================================
bool AInventory::HandlePickup (AInventory *item)
{
if (item->GetClass() == GetClass())
{
if (Amount < MaxAmount || (sv_unlimited_pickup && !item->CallShouldStay()))
{
if (Amount > 0 && Amount + item->Amount < 0)
{
Amount = 0x7fffffff;
}
else
{
Amount += item->Amount;
}
if (Amount > MaxAmount && !sv_unlimited_pickup)
{
Amount = MaxAmount;
}
item->ItemFlags |= IF_PICKUPGOOD;
}
return true;
}
return false;
}
DEFINE_ACTION_FUNCTION(AInventory, HandlePickup)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT_NOT_NULL(item, AInventory);
ACTION_RETURN_BOOL(self->HandlePickup(item));
}
//===========================================================================
//
// AInventory :: GoAway
//
// Returns true if you must create a copy of this item to give to the player
// or false if you can use this one instead.
//
//===========================================================================
bool AInventory::GoAway ()
{
// Dropped items never stick around
if (flags & MF_DROPPED)
{
return false;
}
if (!CallShouldStay ())
{
Hide ();
if (ShouldRespawn ())
{
return true;
}
return false;
}
return true;
}
DEFINE_ACTION_FUNCTION(AInventory, GoAway)
{
PARAM_SELF_PROLOGUE(AInventory);
ACTION_RETURN_BOOL(self->GoAway());
}
//===========================================================================
//
// AInventory :: GoAwayAndDie
//
// Like GoAway but used by items that don't insert themselves into the
// inventory. If they won't be respawning, then they can destroy themselves.
//
//===========================================================================
void AInventory::GoAwayAndDie ()
{
if (!GoAway ())
{
flags &= ~MF_SPECIAL;
SetState (FindState("HoldAndDestroy"));
}
}
DEFINE_ACTION_FUNCTION(AInventory, GoAwayAndDie)
{
PARAM_SELF_PROLOGUE(AInventory);
self->GoAwayAndDie();
return 0;
}
//===========================================================================
//
// AInventory :: CreateCopy
//
// Returns an actor suitable for placing in an inventory, either itself or
// a copy based on whether it needs to respawn or not. Returning NULL
// indicates the item should not be picked up.
//
//===========================================================================
AInventory *AInventory::CreateCopy (AActor *other)
{
AInventory *copy;
Amount = MIN(Amount, MaxAmount);
if (GoAway ())
{
copy = static_cast<AInventory *>(Spawn (GetClass()));
copy->Amount = Amount;
copy->MaxAmount = MaxAmount;
}
else
{
copy = this;
}
return copy;
}
DEFINE_ACTION_FUNCTION(AInventory, CreateCopy)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT(other, AActor);
ACTION_RETURN_OBJECT(self->CreateCopy(other));
}
//===========================================================================
//
// AInventory::CreateTossable
//
// Creates a copy of the item suitable for dropping. If this actor embodies
// only one item, then it is tossed out itself. Otherwise, the count drops
// by one and a new item with an amount of 1 is spawned.
//
//===========================================================================
AInventory *AInventory::CreateTossable()
{
IFVIRTUAL(AInventory, CreateTossable)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
AInventory *retval;
ret.PointerAt((void**)&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return retval;
}
else return CreateTossable();
}
//===========================================================================
//
// AInventory :: BecomeItem
@ -518,30 +270,6 @@ DEFINE_ACTION_FUNCTION(AInventory, BecomePickup)
return 0;
}
//===========================================================================
//
// AInventory :: ModifyDamage
//
// Allows inventory items to manipulate the amount of damage
// inflicted. Damage is the amount of damage that would be done without manipulation,
// and newdamage is the amount that should be done after the item has changed
// it.
// 'active' means it is called by the inflictor, 'passive' by the target.
// It may seem that this is redundant and AbsorbDamage is the same. However,
// AbsorbDamage is called only for players and also depends on other settings
// which are undesirable for a protection artifact.
//
//===========================================================================
void AInventory::ModifyDamage (int damage, FName damageType, int &newdamage, bool passive)
{
IFVIRTUAL(AInventory, ModifyDamage)
{
VMValue params[5] = { (DObject*)this, damage, int(damageType), &newdamage, passive };
GlobalVMStack.Call(func, params, 5, nullptr, 0, nullptr);
}
}
//===========================================================================
//
// AInventory :: GetSpeedFactor
@ -556,10 +284,9 @@ double AInventory::GetSpeedFactor()
{
IFVIRTUALPTR(self, AInventory, GetSpeedFactor)
{
VMValue params[2] = { (DObject*)self };
VMReturn ret;
VMValue params[1] = { (DObject*)self };
double retval;
ret.FloatAt(&retval);
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
factor *= retval;
}
@ -581,10 +308,9 @@ bool AInventory::GetNoTeleportFreeze ()
{
IFVIRTUALPTR(self, AInventory, GetNoTeleportFreeze)
{
VMValue params[2] = { (DObject*)self };
VMReturn ret;
VMValue params[1] = { (DObject*)self };
int retval;
ret.IntAt(&retval);
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
if (retval) return true;
}
@ -604,308 +330,39 @@ bool AInventory::CallUse(bool pickup)
IFVIRTUAL(AInventory, Use)
{
VMValue params[2] = { (DObject*)this, pickup };
VMReturn ret;
int retval;
ret.IntAt(&retval);
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
return !!retval;
}
}
//===========================================================================
//
// AInventory :: Hide
//
// Hides this actor until it's time to respawn again.
//
//===========================================================================
void AInventory::Hide ()
{
FState *HideSpecialState = NULL, *HideDoomishState = NULL;
flags = (flags & ~MF_SPECIAL) | MF_NOGRAVITY;
renderflags |= RF_INVISIBLE;
if (gameinfo.gametype & GAME_Raven)
{
HideSpecialState = FindState("HideSpecial");
if (HideSpecialState == NULL)
{
HideDoomishState = FindState("HideDoomish");
}
}
else
{
HideDoomishState = FindState("HideDoomish");
if (HideDoomishState == NULL)
{
HideSpecialState = FindState("HideSpecial");
}
}
assert(HideDoomishState != NULL || HideSpecialState != NULL);
if (HideSpecialState != NULL)
{
SetState (HideSpecialState);
tics = 1400;
if (PickupFlash != NULL) tics += 30;
}
else if (HideDoomishState != NULL)
{
SetState (HideDoomishState);
tics = 1050;
}
if (RespawnTics != 0)
{
tics = RespawnTics;
}
}
//===========================================================================
//
//
//===========================================================================
static void PrintPickupMessage (const char *str)
{
if (str != NULL)
{
if (str[0]=='$')
{
str=GStrings(str+1);
}
if (str[0] != 0) Printf (PRINT_LOW, "%s\n", str);
}
}
//===========================================================================
//
// AInventory :: Touch
//
// Handles collisions from another actor, possible adding itself to the
// collider's inventory.
//
//===========================================================================
void AInventory::Touch (AActor *toucher)
{
player_t *player = toucher->player;
// If a voodoo doll touches something, pretend the real player touched it instead.
if (player != NULL)
{
toucher = player->mo;
}
bool localview = toucher->CheckLocalView(consoleplayer);
if (!CallTryPickup (toucher, &toucher)) return;
// This is the only situation when a pickup flash should ever play.
if (PickupFlash != NULL && !CallShouldStay())
{
Spawn(PickupFlash, Pos(), ALLOW_REPLACE);
}
if (!(ItemFlags & IF_QUIET))
{
FString message = GetPickupMessage ();
if (message.IsNotEmpty() && localview
&& (StaticLastMessageTic != gametic || StaticLastMessage.Compare(message)))
{
StaticLastMessageTic = gametic;
StaticLastMessage = message;
PrintPickupMessage (message);
StatusBar->FlashCrosshair ();
}
// Special check so voodoo dolls picking up items cause the
// real player to make noise.
if (player != NULL)
{
CallPlayPickupSound (player->mo);
if (!(ItemFlags & IF_NOSCREENFLASH))
{
player->bonuscount = BONUSADD;
}
}
else
{
CallPlayPickupSound (toucher);
}
}
// [RH] Execute an attached special (if any)
DoPickupSpecial (toucher);
if (flags & MF_COUNTITEM)
{
if (player != NULL)
{
player->itemcount++;
}
level.found_items++;
}
if (flags5 & MF5_COUNTSECRET)
{
P_GiveSecret(player != NULL? (AActor*)player->mo : toucher, true, true, -1);
}
//Added by MC: Check if item taken was the roam destination of any bot
for (int i = 0; i < MAXPLAYERS; i++)
{
if (players[i].Bot != NULL && this == players[i].Bot->dest)
players[i].Bot->dest = NULL;
}
}
//===========================================================================
//
// AInventory :: DoPickupSpecial
//
// Executes this actor's special when it is picked up.
//
//===========================================================================
void AInventory::DoPickupSpecial (AActor *toucher)
{
if (special)
{
P_ExecuteSpecial(special, NULL, toucher, false,
args[0], args[1], args[2], args[3], args[4]);
special = 0;
}
}
//===========================================================================
//
// AInventory :: PickupMessage
//
// Returns the message to print when this actor is picked up.
//
//===========================================================================
FString AInventory::PickupMessage ()
{
return GetClass()->PickupMessage;
}
DEFINE_ACTION_FUNCTION(AInventory, PickupMessage)
{
PARAM_SELF_PROLOGUE(AInventory);
ACTION_RETURN_STRING(self->PickupMessage());
}
FString AInventory::GetPickupMessage()
{
IFVIRTUAL(AInventory, PickupMessage)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
FString retval;
ret.StringAt(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return retval;
}
else return PickupMessage();
}
//===========================================================================
//
// AInventory :: PlayPickupSound
//
//===========================================================================
void AInventory::PlayPickupSound (AActor *toucher)
{
float atten;
int chan;
if (ItemFlags & IF_NOATTENPICKUPSOUND)
{
atten = ATTN_NONE;
}
#if 0
else if ((ItemFlags & IF_FANCYPICKUPSOUND) &&
(toucher == NULL || toucher->CheckLocalView(consoeplayer)))
{
atten = ATTN_NONE;
}
#endif
else
{
atten = ATTN_NORM;
}
if (toucher != NULL && toucher->CheckLocalView(consoleplayer))
{
chan = CHAN_PICKUP|CHAN_NOPAUSE;
}
else
{
chan = CHAN_PICKUP;
}
S_Sound (toucher, chan, PickupSound, 1, atten);
}
DEFINE_ACTION_FUNCTION(AInventory, PlayPickupSound)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT(other, AActor);
self->PlayPickupSound(other);
return 0;
}
void AInventory::CallPlayPickupSound(AActor *other)
{
IFVIRTUAL(AInventory, PlayPickupSound)
{
VMValue params[2] = { (DObject*)this, (DObject*)other };
GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr);
}
else PlayPickupSound(other);
}
//===========================================================================
//
// AInventory :: ShouldStay
//
// Returns true if the item should not disappear, even temporarily.
//
//===========================================================================
bool AInventory::ShouldStay ()
{
return false;
}
DEFINE_ACTION_FUNCTION(AInventory, ShouldStay)
{
PARAM_SELF_PROLOGUE(AInventory);
ACTION_RETURN_BOOL(self->ShouldStay());
}
bool AInventory::CallShouldStay()
//===========================================================================
//
//
//===========================================================================
static int StaticLastMessageTic;
static FString StaticLastMessage;
DEFINE_ACTION_FUNCTION(AInventory, PrintPickupMessage)
{
IFVIRTUAL(AInventory, ShouldStay)
PARAM_PROLOGUE;
PARAM_BOOL(localview);
PARAM_STRING(str);
if (str.IsNotEmpty() && localview && (StaticLastMessageTic != gametic || StaticLastMessage.Compare(str)))
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
int retval;
ret.IntAt(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return !!retval;
}
else return ShouldStay();
}
StaticLastMessageTic = gametic;
StaticLastMessage = str;
const char *pstr = str.GetChars();
if (pstr[0] == '$') pstr = GStrings(pstr + 1);
if (pstr[0] != 0) Printf(PRINT_LOW, "%s\n", pstr);
StatusBar->FlashCrosshair();
}
return 0;
}
//===========================================================================
//
@ -953,29 +410,17 @@ void AInventory::DepleteOrDestroy ()
//
//===========================================================================
PalEntry AInventory::GetBlend ()
{
return 0;
}
DEFINE_ACTION_FUNCTION(AInventory, GetBlend)
{
PARAM_SELF_PROLOGUE(AInventory);
ACTION_RETURN_INT(self->GetBlend());
}
PalEntry AInventory::CallGetBlend()
{
IFVIRTUAL(AInventory, GetBlend)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
int retval;
ret.IntAt(&retval);
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return retval;
}
else return GetBlend();
else return 0;
}
//===========================================================================
@ -1068,129 +513,51 @@ DEFINE_ACTION_FUNCTION(AInventory, DoRespawn)
ACTION_RETURN_BOOL(self->DoRespawn());
}
//===========================================================================
//
// AInventory :: GiveQuest
//
//===========================================================================
void AInventory::GiveQuest (AActor *toucher)
{
int quest = GetClass()->GiveQuest;
if (quest > 0 && quest <= (int)countof(QuestItemClasses))
{
toucher->GiveInventoryType (QuestItemClasses[quest-1]);
}
}
//===========================================================================
//
// AInventory :: CallTryPickup
//
//===========================================================================
bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return)
bool AInventory::CallTryPickup(AActor *toucher, AActor **toucher_return)
{
TObjPtr<AInventory> Invstack = Inventory; // A pointer of the inventories item stack.
// unmorphed versions of a currently morphed actor cannot pick up anything.
if (toucher->flags & MF_UNMORPHED) return false;
bool res;
if (CanPickup(toucher))
{
IFVIRTUAL(AInventory, TryPickup)
{
VMValue params[2] = { (DObject*)this, (void*)&toucher };
VMReturn ret;
int retval;
ret.IntAt(&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
res = !!retval;
}
}
else if (!(ItemFlags & IF_RESTRICTABSOLUTELY))
{
// let an item decide for itself how it will handle this
IFVIRTUAL(AInventory, TryPickupRestricted)
{
VMValue params[2] = { (DObject*)this, (void*)&toucher };
VMReturn ret;
int retval;
ret.IntAt(&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
res = !!retval;
}
}
else
return false;
// Morph items can change the toucher so we need an option to return this info.
if (toucher_return != NULL) *toucher_return = toucher;
if (!res && (ItemFlags & IF_ALWAYSPICKUP) && !CallShouldStay())
{
res = true;
GoAwayAndDie();
}
if (res)
{
GiveQuest(toucher);
// Transfer all inventory accross that the old object had, if requested.
if ((ItemFlags & IF_TRANSFER))
{
while (Invstack)
{
AInventory* titem = Invstack;
Invstack = titem->Inventory;
if (titem->Owner == this)
{
if (!titem->CallTryPickup(toucher)) // The object no longer can exist
{
titem->Destroy();
}
}
}
}
}
return res;
static VMFunction *func = nullptr;
if (func == nullptr) func = PClass::FindFunction(NAME_Inventory, NAME_CallTryPickup);
VMValue params[2] = { (DObject*)this, toucher };
VMReturn ret[2];
int res;
AActor *tret;
ret[0].IntAt(&res);
ret[1].PointerAt((void**)&tret);
GlobalVMStack.Call(func, params, 2, ret, 2);
if (toucher_return) *toucher_return = tret;
return !!res;
}
DEFINE_ACTION_FUNCTION(AInventory, CallTryPickup)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT(toucher, AActor);
AActor *t_ret;
bool res = self->CallTryPickup(toucher, &t_ret);
if (numret > 0) ret[0].SetInt(res);
if (numret > 1) ret[1].SetPointer(t_ret, ATAG_OBJECT), numret = 2;
return numret;
}
//===========================================================================
//
// AInventory :: CanPickup
//
//===========================================================================
bool AInventory::CanPickup (AActor *toucher)
DEFINE_ACTION_FUNCTION(AInventory, CanPickup)
{
if (!toucher)
return false;
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT(toucher, AActor);
PClassInventory *ai = GetClass();
if (!toucher)
ACTION_RETURN_BOOL(false);
PClassInventory *ai = self->GetClass();
// Is the item restricted to certain player classes?
if (ai->RestrictedToPlayerClass.Size() != 0)
{
for (unsigned i = 0; i < ai->RestrictedToPlayerClass.Size(); ++i)
{
if (toucher->IsKindOf(ai->RestrictedToPlayerClass[i]))
return true;
ACTION_RETURN_BOOL(true);
}
return false;
ACTION_RETURN_BOOL(false);
}
// Or is it forbidden to certain other classes?
else
@ -1198,10 +565,10 @@ bool AInventory::CanPickup (AActor *toucher)
for (unsigned i = 0; i < ai->ForbiddenToPlayerClass.Size(); ++i)
{
if (toucher->IsKindOf(ai->ForbiddenToPlayerClass[i]))
return false;
ACTION_RETURN_BOOL(false);
}
}
return true;
ACTION_RETURN_BOOL(true);
}
//===========================================================================
@ -1247,22 +614,6 @@ CCMD (targetinv)
"the NOBLOCKMAP flag or have height/radius of 0.\n");
}
//===========================================================================
//
// AInventory :: AttachToOwner
//
//===========================================================================
void AInventory::CallAttachToOwner(AActor *other)
{
IFVIRTUAL(AInventory, AttachToOwner)
{
VMValue params[2] = { (DObject*)this, (DObject*)other };
GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr);
}
}
//===========================================================================
//===========================================================================

View file

@ -59,7 +59,7 @@ public:
virtual size_t PointerSubstitution(DObject *oldclass, DObject *newclass);
void Finalize(FStateDefinitions &statedef);
FString PickupMessage;
FString PickupMsg;
int GiveQuest; // Optionally give one of the quest items.
FTextureID AltHUDIcon;
TArray<PClassPlayerPawn *> RestrictedToPlayerClass;
@ -72,72 +72,29 @@ class AInventory : public AActor
HAS_OBJECT_POINTERS
public:
virtual void Touch (AActor *toucher) override;
virtual void Serialize(FSerializer &arc) override;
virtual void MarkPrecacheSounds() const override;
virtual void BeginPlay () override;
virtual void OnDestroy() override;
virtual void Tick() override;
virtual bool Grind(bool items) override;
// virtual methods that only get overridden by special internal classes, like DehackedPickup.
// There is no need to expose these to scripts.
void DepleteOrDestroy ();
virtual bool ShouldRespawn ();
virtual void DoPickupSpecial (AActor *toucher);
bool CallTryPickup(AActor *toucher, AActor **toucher_return = NULL); // Wrapper for script function.
// methods that can be overridden by scripts, plus their callers.
virtual bool SpecialDropAction (AActor *dropper);
bool CallSpecialDropAction(AActor *dropper);
void DepleteOrDestroy (); // virtual on the script side.
bool CallUse(bool pickup); // virtual on the script side.
PalEntry CallGetBlend(); // virtual on the script side.
double GetSpeedFactor(); // virtual on the script side.
bool GetNoTeleportFreeze(); // virtual on the script side.
bool CallTryPickup(AActor *toucher, AActor **toucher_return = NULL); // This wraps both virtual methods plus a few more checks.
virtual AInventory *CreateCopy(AActor *other);
AInventory *CreateTossable();
virtual FString PickupMessage();
FString GetPickupMessage();
virtual bool HandlePickup(AInventory *item);
bool CallUse(bool pickup);
virtual PalEntry GetBlend();
PalEntry CallGetBlend();
virtual bool ShouldStay();
bool CallShouldStay();
void DoEffect();
virtual void PlayPickupSound(AActor *toucher);
void CallPlayPickupSound(AActor *toucher);
void CallAttachToOwner(AActor *other);
// still need to be done.
void ModifyDamage(int damage, FName damageType, int &newdamage, bool passive);
// virtual on the script side only.
double GetSpeedFactor();
bool GetNoTeleportFreeze();
bool GoAway();
void GoAwayAndDie();
void Hide();
void BecomeItem ();
void BecomePickup ();
bool DoRespawn();
AInventory *PrevItem(); // Returns the item preceding this one in the list.
AInventory *PrevInv(); // Returns the previous item with IF_INVBAR set.
AInventory *NextInv(); // Returns the next item with IF_INVBAR set.
bool CallStateChain(AActor *actor, FState *state);
TObjPtr<AActor> Owner; // Who owns this item? NULL if it's still a pickup.
int Amount; // Amount of item this instance has
int MaxAmount; // Max amount of item this instance can have
@ -151,23 +108,13 @@ public:
PClassActor *PickupFlash; // actor to spawn as pickup flash
FSoundIDNoInit PickupSound;
protected:
bool CanPickup(AActor * toucher);
void GiveQuest(AActor * toucher);
private:
static int StaticLastMessageTic;
static FString StaticLastMessage;
};
class AStateProvider : public AInventory
{
DECLARE_CLASS (AStateProvider, AInventory)
public:
bool CallStateChain(AActor *actor, FState *state);
};
extern PClassActor *QuestItemClasses[31];
#endif //__A_PICKUPS_H__

View file

@ -56,8 +56,6 @@
#include "virtual.h"
#define BONUSADD 6
extern FFlagDef WeaponFlagDefs[];
IMPLEMENT_CLASS(AWeapon, false, true)

View file

@ -1097,8 +1097,8 @@ void G_WorldDone (void)
// Strife needs a special case here to choose between good and sad ending. Bad is handled elsewhere.
if (endsequence == NAME_Inter_Strife)
{
if (players[0].mo->FindInventory (QuestItemClasses[24]) ||
players[0].mo->FindInventory (QuestItemClasses[27]))
if (players[0].mo->FindInventory (NAME_QuestItem25) ||
players[0].mo->FindInventory (NAME_QuestItem28))
{
endsequence = NAME_Inter_Strife_Good;
}

View file

@ -77,7 +77,7 @@ void gl_ParseVavoomSkybox();
inline PClassActor * GetRealType(PClassActor * ti)
{
PClassActor *rep = ti->GetReplacement(false);
if (rep != ti && rep != NULL && rep->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup)))
if (rep != ti && rep != NULL && rep->IsDescendantOf(PClass::FindActor(NAME_DehackedPickup)))
{
return rep;
}

View file

@ -771,7 +771,7 @@ void FGLRenderer::SetFixedColormap (player_t *player)
auto litetype = PClass::FindActor(NAME_PowerLightAmp);
for(AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory)
{
PalEntry color = in->GetBlend ();
PalEntry color = in->CallGetBlend ();
// Need special handling for light amplifiers
if (in->IsKindOf(torchtype))

View file

@ -186,6 +186,7 @@ xx(HealthPickup)
xx(autousemode)
xx(Ammo)
xx(WeaponGiver)
xx(DehackedPickup)
xx(PowerTargeter)
xx(PowerInvulnerable)
xx(PowerStrength)
@ -197,6 +198,11 @@ xx(PowerFlight)
xx(PowerSpeed)
xx(PowerTorch)
xx(CustomInventory)
xx(Inventory)
xx(CallTryPickup)
xx(QuestItem25)
xx(QuestItem28)
xx(QuestItem29)
xx(AcolyteBlue)
xx(SpectralLightningV1)

View file

@ -108,7 +108,7 @@ static FRandom pr_bfgselfdamage("BFGSelfDamage");
//
//==========================================================================
bool AInventory::CallStateChain (AActor *actor, FState *state)
bool AStateProvider::CallStateChain (AActor *actor, FState *state)
{
INTBOOL result = false;
int counter = 0;
@ -223,9 +223,9 @@ bool AInventory::CallStateChain (AActor *actor, FState *state)
return !!result;
}
DEFINE_ACTION_FUNCTION(AInventory, CallStateChain)
DEFINE_ACTION_FUNCTION(ACustomInventory, CallStateChain)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_SELF_PROLOGUE(AStateProvider);
PARAM_OBJECT(affectee, AActor);
PARAM_STATE(state);
ACTION_RETURN_BOOL(self->CallStateChain(affectee, state));

View file

@ -52,6 +52,7 @@
#include "p_checkposition.h"
#include "math/cmath.h"
#include "g_levellocals.h"
#include "virtual.h"
#include "gi.h"
@ -3260,13 +3261,22 @@ void ModifyDropAmount(AInventory *inv, int dropamount)
static_cast<AWeapon *>(inv)->AmmoGive2 = int(static_cast<AWeapon *>(inv)->AmmoGive2 * dropammofactor);
inv->ItemFlags |= flagmask;
}
else if (inv->IsKindOf (RUNTIME_CLASS(ADehackedPickup)))
else if (inv->IsKindOf (PClass::FindClass(NAME_DehackedPickup)))
{
// For weapons and ammo modified by Dehacked we need to flag the item.
static_cast<ADehackedPickup *>(inv)->droppedbymonster = true;
inv->BoolVar("droppedbymonster") = true;
}
}
// todo: make this a scripted virtual function so it can better deal with some of the classes involved.
DEFINE_ACTION_FUNCTION(AInventory, ModifyDropAmount)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_INT(dropamount);
ModifyDropAmount(self, dropamount);
return 0;
}
//---------------------------------------------------------------------------
//
// PROC P_DropItem
@ -3312,11 +3322,19 @@ AInventory *P_DropItem (AActor *source, PClassActor *type, int dropamount, int c
AInventory *inv = static_cast<AInventory *>(mo);
ModifyDropAmount(inv, dropamount);
inv->ItemFlags |= IF_TOSSED;
if (inv->CallSpecialDropAction (source))
IFVIRTUALPTR(inv, AInventory, SpecialDropAction)
{
// The special action indicates that the item should not spawn
inv->Destroy();
return NULL;
VMValue params[2] = { inv, source };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
if (retval)
{
// The special action indicates that the item should not spawn
inv->Destroy();
return NULL;
}
}
return inv;
}

View file

@ -3230,7 +3230,7 @@ FUNC(LS_GlassBreak)
}
if (it != NULL)
{
it->GiveInventoryType (QuestItemClasses[28]);
it->GiveInventoryType (PClass::FindActor("QuestItem29"));
it->GiveInventoryType (PClass::FindActor("UpgradeAccuracy"));
it->GiveInventoryType (PClass::FindActor("UpgradeStamina"));
}

View file

@ -48,8 +48,6 @@ struct FLinePortal;
#define STEEPSLOPE (46342/65536.) // [RH] Minimum floorplane.c value for walking
#define BONUSADD 6
// Inspired by Maes
extern int bmapnegx;
extern int bmapnegy;

View file

@ -1038,12 +1038,14 @@ DEFINE_ACTION_FUNCTION(AActor, UseInventory)
AInventory *AActor::DropInventory (AInventory *item)
{
AInventory *drop = item->CreateTossable ();
if (drop == NULL)
AInventory *drop = nullptr;
IFVIRTUALPTR(item, AInventory, CreateTossable)
{
return NULL;
VMValue params[1] = { (DObject*)this };
VMReturn ret((void**)&drop);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
}
if (drop == nullptr) return NULL;
drop->SetOrigin(PosPlusZ(10.), false);
drop->Angles.Yaw = Angles.Yaw;
drop->VelFromAngle(5.);
@ -3945,7 +3947,11 @@ void AActor::Tick ()
// by the order in the inventory, not the order in the thinker table
while (item != NULL && item->Owner == this)
{
item->DoEffect();
IFVIRTUALPTR(item, AInventory, DoEffect)
{
VMValue params[1] = { item };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
item = item->Inventory;
}
@ -7592,10 +7598,13 @@ int AActor::GetModifiedDamage(FName damagetype, int damage, bool passive)
auto inv = Inventory;
while (inv != nullptr)
{
inv->ModifyDamage(damage, damagetype, damage, passive);
IFVIRTUALPTR(inv, AInventory, ModifyDamage)
{
VMValue params[5] = { (DObject*)inv, damage, int(damagetype), &damage, passive };
GlobalVMStack.Call(func, params, 5, nullptr, 0, nullptr);
}
inv = inv->Inventory;
}
return damage;
}

View file

@ -420,6 +420,10 @@ PPrototype *FxExpression::ReturnProto()
static int EncodeRegType(ExpEmit reg)
{
int regtype = reg.RegType;
if (reg.Fixed && reg.Target)
{
regtype |= REGT_ADDROF;
}
if (reg.Konst)
{
regtype |= REGT_KONST;
@ -10008,10 +10012,9 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
{
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;
Args[i] = new FxTypeCast(Args[i], ctx.ReturnProto->ReturnTypes[i], false, false);
Args[i] = Args[i]->Resolve(ctx);
if (Args[i] == nullptr) fail = true;
}
if (fail)
{

View file

@ -65,40 +65,6 @@ struct FExtraInfo
double DeathHeight, BurnHeight;
};
class AFakeInventory : public AInventory
{
DECLARE_CLASS (AFakeInventory, AInventory);
public:
bool Respawnable;
bool ShouldRespawn ()
{
return Respawnable && Super::ShouldRespawn();
}
bool TryPickup (AActor *&toucher)
{
INTBOOL success = P_ExecuteSpecial(special, NULL, toucher, false,
args[0], args[1], args[2], args[3], args[4]);
if (success)
{
GoAwayAndDie ();
return true;
}
return false;
}
void DoPickupSpecial (AActor *toucher)
{
// The special was already executed by TryPickup, so do nothing here
}
};
IMPLEMENT_CLASS(AFakeInventory, false, false)
DEFINE_FIELD(AFakeInventory, Respawnable)
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
@ -125,16 +91,6 @@ static const char *RenderStyles[] =
// CODE --------------------------------------------------------------------
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(respawns, 0, FakeInventory)
{
defaults->Respawnable = true;
}
//==========================================================================
//
// ParseOldDecoration
@ -154,7 +110,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
PClassActor *parent;
FName typeName;
parent = (def == DEF_Pickup) ? RUNTIME_CLASS(AFakeInventory) : RUNTIME_CLASS(AActor);
parent = (def == DEF_Pickup) ? PClass::FindActor("FakeInventory") : RUNTIME_CLASS(AActor);
sc.MustGetString();
typeName = FName(sc.String);
@ -360,7 +316,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray<FState> &StateArray, TArray<FScriptPosition> &SourceLines)
{
AFakeInventory *const inv = static_cast<AFakeInventory *>(defaults);
AInventory *const inv = static_cast<AInventory *>(defaults);
char sprite[5] = "TNT1";
sc.MustGetString ();
@ -584,11 +540,11 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
else if (def == DEF_Pickup && sc.Compare ("PickupMessage"))
{
sc.MustGetString ();
static_cast<PClassInventory *>(bag.Info)->PickupMessage = sc.String;
static_cast<PClassInventory *>(bag.Info)->PickupMsg = sc.String;
}
else if (def == DEF_Pickup && sc.Compare ("Respawns"))
{
inv->Respawnable = true;
inv->BoolVar("Respawnable") = true;
}
else if (def == DEF_BreakableDecoration && sc.Compare ("SolidOnDeath"))
{

View file

@ -842,7 +842,12 @@ static void DispatchScriptProperty(FScanner &sc, PProperty *prop, AActor *defaul
addr = ((char*)defaults) + f->Offset;
}
if (f->Type->IsKindOf(RUNTIME_CLASS(PInt)))
if (f->Type == TypeBool)
{
bool val = sc.CheckNumber() ? !!sc.Number : true;
static_cast<PBool*>(f->Type)->SetValue(addr, !!val);
}
else if (f->Type->IsKindOf(RUNTIME_CLASS(PInt)))
{
sc.MustGetNumber();
static_cast<PInt*>(f->Type)->SetValue(addr, sc.Number);

View file

@ -69,8 +69,6 @@
void InitThingdef();
// STATIC FUNCTION PROTOTYPES --------------------------------------------
PClassActor *QuestItemClasses[31];
static TMap<FState *, FScriptPosition> StateSourceLines;
static FScriptPosition unknownstatesource("unknown file", 0);
@ -448,12 +446,5 @@ void LoadActors()
// Now we may call the scripted OnDestroy method.
PClass::bVMOperational = true;
// Since these are defined in DECORATE now the table has to be initialized here.
for (int i = 0; i < 31; i++)
{
char fmt[20];
mysnprintf(fmt, countof(fmt), "QuestItem%d", i + 1);
QuestItemClasses[i] = PClass::FindActor(fmt);
}
StateSourceLines.Clear();
}

View file

@ -1802,7 +1802,7 @@ DEFINE_CLASS_PROPERTY(pickupmessage, T, Inventory)
{
PROP_STRING_PARM(str, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassInventory)));
static_cast<PClassInventory *>(info)->PickupMessage = str;
static_cast<PClassInventory *>(info)->PickupMsg = str;
}
//==========================================================================

View file

@ -1926,6 +1926,7 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag)
{
ZCC_ExprConstant one;
unsigned parmcount = 1;
ZCC_TreeNode *x = property->Values;
while (x->SiblingNext != property->Values)
@ -1933,7 +1934,16 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop
x = x->SiblingNext;
parmcount++;
}
if (parmcount != prop->Variables.Size())
if (parmcount == 0 && prop->Variables.Size() == 1 && prop->Variables[0]->Type == TypeBool)
{
// allow boolean properties to have the parameter omitted
one.Operation = PEX_ConstValue;
one.NodeType = AST_ExprConstant;
one.Type = TypeBool;
one.IntVal = 1;
property->Values = &one;
}
else if (parmcount != prop->Variables.Size())
{
Error(x, "Argument count mismatch: Got %u, expected %u", parmcount, prop->Variables.Size());
return;
@ -1954,6 +1964,10 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop
addr = ((char*)defaults) + f->Offset;
}
if (f->Type == TypeBool)
{
static_cast<PBool*>(f->Type)->SetValue(addr, !!GetInt(exp));
}
if (f->Type->IsKindOf(RUNTIME_CLASS(PInt)))
{
static_cast<PInt*>(f->Type)->SetValue(addr, GetInt(exp));

View file

@ -411,7 +411,9 @@ enum ESoundFlags
CHAN_LISTENERZ = 8,
CHAN_MAYBE_LOCAL = 16,
CHAN_UI = 32,
CHAN_NOPAUSE = 64
CHAN_NOPAUSE = 64,
CHAN_PICKUP = (CHAN_ITEM|CHAN_MAYBE_LOCAL)
};
// sound attenuation values

View file

@ -8,6 +8,7 @@ struct VisStyle
class Inventory : Actor native
{
const BLINKTHRESHOLD = (4*32);
const BONUSADD = 6;
native Actor Owner; // Who owns self item? NULL if it's still a pickup.
native int Amount; // Amount of item self instance has
@ -22,6 +23,8 @@ class Inventory : Actor native
native bool bPickupGood;
native bool bCreateCopyMoved;
native bool bInitEffectFailed;
native meta String PickupMsg;
native meta int GiveQuest;
Default
{
@ -33,24 +36,12 @@ class Inventory : Actor native
Inventory.PickupMessage "$TXT_DEFAULTPICKUPMSG";
}
virtual native color GetBlend ();
virtual native bool HandlePickup(Inventory item);
virtual native Inventory CreateCopy(Actor other);
virtual native bool SpecialDropAction (Actor dropper);
virtual native String PickupMessage();
virtual native bool ShouldStay();
virtual native void PlayPickupSound(Actor user);
native bool CanPickup(Actor toucher);
native bool DoRespawn();
native bool GoAway();
native void GoAwayAndDie();
native void BecomeItem();
native void BecomePickup();
// In this case the caller function is more than a simple wrapper around the virtual method and
// is what must be actually called to pick up an item.
native bool, Actor CallTryPickup(Actor toucher);
native bool CallStateChain (Actor actor, State state);
native void ModifyDropAmount(int dropamount);
native static void PrintPickupMessage (bool localview, String str);
States(Actor, Overlay, Weapon, Item)
{
@ -74,8 +65,18 @@ class Inventory : Actor native
Stop;
}
// These are regular functions for the item itself.
//===========================================================================
//
// AInventory :: BeginPlay
//
//===========================================================================
override void BeginPlay ()
{
Super.BeginPlay ();
bDropped = true; // [RH] Items are dropped by default
}
//---------------------------------------------------------------------------
//
// PROC A_RestoreSpecialThing1
@ -132,6 +133,69 @@ class Inventory : Actor native
}
//===========================================================================
//
// AInventory :: CreateCopy
//
// Returns an actor suitable for placing in an inventory, either itself or
// a copy based on whether it needs to respawn or not. Returning NULL
// indicates the item should not be picked up.
//
//===========================================================================
virtual Inventory CreateCopy (Actor other)
{
Inventory copy;
Amount = MIN(Amount, MaxAmount);
if (GoAway ())
{
copy = Inventory(Spawn (GetClass()));
copy.Amount = Amount;
copy.MaxAmount = MaxAmount;
}
else
{
copy = self;
}
return copy;
}
//===========================================================================
//
// AInventory :: HandlePickup
//
// Returns true if the pickup was handled (or should not happen at all),
// false if not.
//
//===========================================================================
virtual bool HandlePickup (Inventory item)
{
if (item.GetClass() == GetClass())
{
if (Amount < MaxAmount || (sv_unlimited_pickup && !item.ShouldStay()))
{
if (Amount > 0 && Amount + item.Amount < 0)
{
Amount = 0x7fffffff;
}
else
{
Amount += item.Amount;
}
if (Amount > MaxAmount && !sv_unlimited_pickup)
{
Amount = MaxAmount;
}
item.bPickupGood = true;
}
return true;
}
return false;
}
//===========================================================================
//
// AInventory :: CallHandlePickup
@ -236,6 +300,99 @@ class Inventory : Actor native
return true;
}
//===========================================================================
//
// AInventory :: GiveQuest
//
//===========================================================================
void GiveQuestItem (Actor toucher)
{
if (GiveQuest > 0)
{
String qname = "QuestItem" .. GiveQuest;
class<Inventory> type = qname;
if (type != null)
{
toucher.GiveInventoryType (type);
}
}
}
//===========================================================================
//
// AInventory :: CallTryPickup
//
// In this case the caller function is more than a simple wrapper around the virtual method and
// is what must be actually called to pick up an item.
//
//===========================================================================
bool, Actor CallTryPickup(Actor toucher)
{
let saved_toucher = toucher;
let Invstack = Inv; // A pointer of the inventories item stack.
// unmorphed versions of a currently morphed actor cannot pick up anything.
if (bUnmorphed) return false, null;
bool res;
if (CanPickup(toucher))
{
res = TryPickup(toucher);
}
else if (!bRestrictAbsolutely)
{
// let an item decide for itself how it will handle this
res = TryPickupRestricted(toucher);
}
else
return false, null;
if (!res && (bAlwaysPickup) && !ShouldStay())
{
res = true;
GoAwayAndDie();
}
if (res)
{
GiveQuestItem(toucher);
// Transfer all inventory across that the old object had, if requested.
if (bTransfer)
{
while (Invstack)
{
let titem = Invstack;
Invstack = titem.Inv;
if (titem.Owner == self)
{
if (!titem.CallTryPickup(toucher)) // The object no longer can exist
{
titem.Destroy();
}
}
}
}
}
return res, toucher;
}
//===========================================================================
//
// AInventory :: ShouldStay
//
// Returns true if the item should not disappear, even temporarily.
//
//===========================================================================
virtual bool ShouldStay ()
{
return false;
}
//===========================================================================
//
// AInventory :: TryPickupRestricted
@ -313,6 +470,97 @@ class Inventory : Actor native
return copy;
}
//===========================================================================
//
// AInventory :: PickupMessage
//
// Returns the message to print when this actor is picked up.
//
//===========================================================================
virtual String PickupMessage ()
{
return PickupMsg;
}
//===========================================================================
//
// AInventory :: Touch
//
// Handles collisions from another actor, possible adding itself to the
// collider's inventory.
//
//===========================================================================
override void Touch (Actor toucher)
{
let player = toucher.player;
// If a voodoo doll touches something, pretend the real player touched it instead.
if (player != NULL)
{
toucher = player.mo;
}
bool localview = toucher.CheckLocalView(consoleplayer);
bool res;
[res, toucher] = CallTryPickup(toucher);
if (!res) return;
// This is the only situation when a pickup flash should ever play.
if (PickupFlash != NULL && !ShouldStay())
{
Spawn(PickupFlash, Pos, ALLOW_REPLACE);
}
if (!bQuiet)
{
PrintPickupMessage(localview, PickupMessage ());
// Special check so voodoo dolls picking up items cause the
// real player to make noise.
if (player != NULL)
{
PlayPickupSound (player.mo);
if (!bNoScreenFlash)
{
player.bonuscount = BONUSADD;
}
}
else
{
PlayPickupSound (toucher);
}
}
// [RH] Execute an attached special (if any)
DoPickupSpecial (toucher);
if (bCountItem)
{
if (player != NULL)
{
player.itemcount++;
}
level.found_items++;
}
if (bCountSecret)
{
Actor ac = player != NULL? Actor(player.mo) : toucher;
ac.GiveSecret(true, true);
}
//Added by MC: Check if item taken was the roam destination of any bot
for (int i = 0; i < MAXPLAYERS; i++)
{
if (players[i].Bot != NULL && self == players[i].Bot.dest)
players[i].Bot.dest = NULL;
}
}
//===========================================================================
//
// AInventory :: DepleteOrDestroy
@ -361,13 +609,199 @@ class Inventory : Actor native
virtual void DoEffect() {}
//===========================================================================
//
// AInventory :: Hide
//
// Hides this actor until it's time to respawn again.
//
//===========================================================================
virtual void Hide ()
{
State HideSpecialState = NULL, HideDoomishState = NULL;
bSpecial = false;
bNoGravity = true;
bInvisible = true;
if (gameinfo.gametype & GAME_Raven)
{
HideSpecialState = FindState("HideSpecial");
if (HideSpecialState == NULL)
{
HideDoomishState = FindState("HideDoomish");
}
}
else
{
HideDoomishState = FindState("HideDoomish");
if (HideDoomishState == NULL)
{
HideSpecialState = FindState("HideSpecial");
}
}
if (HideSpecialState != NULL)
{
SetState (HideSpecialState);
tics = 1400;
if (PickupFlash != NULL) tics += 30;
}
else if (HideDoomishState != NULL)
{
SetState (HideDoomishState);
tics = 1050;
}
if (RespawnTics != 0)
{
tics = RespawnTics;
}
}
//===========================================================================
//
// AInventory :: ShouldRespawn
//
// Returns true if the item should hide itself and reappear later when picked
// up.
//
//===========================================================================
virtual bool ShouldRespawn ()
{
if (bBigPowerup && !sv_respawnsuper) return false;
if (bNeverRespawn) return false;
return sv_itemrespawn || bAlwaysRespawn;
}
//===========================================================================
//
// AInventory :: GoAway
//
// Returns true if you must create a copy of this item to give to the player
// or false if you can use this one instead.
//
//===========================================================================
protected bool GoAway ()
{
// Dropped items never stick around
if (bDropped)
{
return false;
}
if (!ShouldStay ())
{
Hide ();
if (ShouldRespawn ())
{
return true;
}
return false;
}
return true;
}
//===========================================================================
//
// AInventory :: GoAwayAndDie
//
// Like GoAway but used by items that don't insert themselves into the
// inventory. If they won't be respawning, then they can destroy themselves.
//
//===========================================================================
protected void GoAwayAndDie ()
{
if (!GoAway ())
{
bSpecial = false;
SetStateLabel("HoldAndDestroy");
}
}
//===========================================================================
//
// AInventory :: ModifyDamage
//
// Allows inventory items to manipulate the amount of damage
// inflicted. Damage is the amount of damage that would be done without manipulation,
// and newdamage is the amount that should be done after the item has changed
// it.
// 'active' means it is called by the inflictor, 'passive' by the target.
// It may seem that this is redundant and AbsorbDamage is the same. However,
// AbsorbDamage is called only for players and also depends on other settings
// which are undesirable for a protection artifact.
//
//===========================================================================
virtual void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive) {}
virtual bool Use (bool pickup) { return false; }
virtual double GetSpeedFactor() { return 1; }
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() {}
virtual Color GetBlend () { return 0; }
//===========================================================================
//
// AInventory :: DoPickupSpecial
//
// Executes this actor's special when it is picked up.
//
//===========================================================================
virtual void DoPickupSpecial (Actor toucher)
{
if (special)
{
toucher.A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]);
special = 0;
}
}
//===========================================================================
//
// AInventory :: PlayPickupSound
//
//===========================================================================
virtual void PlayPickupSound (Actor toucher)
{
double atten;
int chan;
if (bNoAttenPickupSound)
{
atten = ATTN_NONE;
}
/*
else if ((ItemFlags & IF_FANCYPICKUPSOUND) &&
(toucher == NULL || toucher->CheckLocalView(consoeplayer)))
{
atten = ATTN_NONE;
}
*/
else
{
atten = ATTN_NORM;
}
if (toucher != NULL && toucher.CheckLocalView(consoleplayer))
{
chan = CHAN_PICKUP|CHAN_NOPAUSE;
}
else
{
chan = CHAN_PICKUP;
}
toucher.A_PlaySound(PickupSound, chan, 1, false, atten);
}
//===========================================================================
//
@ -393,13 +827,153 @@ class Inventory : Actor native
virtual void AbsorbDamage (int damage, Name damageType, out int newdamage) {}
//===========================================================================
//
// AInventory :: SpecialDropAction
//
// Called by P_DropItem. Return true to prevent the standard drop tossing.
// A few Strife items that are meant to trigger actions rather than be
// picked up use this. Normal items shouldn't need it.
//
//===========================================================================
virtual bool SpecialDropAction (Actor dropper)
{
return false;
}
}
class DehackedPickup : Inventory native
//===========================================================================
//
//
//
//===========================================================================
class DehackedPickup : Inventory
{
Inventory RealPickup;
bool droppedbymonster;
private native class<Inventory> DetermineType();
override bool TryPickup (in out Actor toucher)
{
let type = DetermineType ();
if (type == NULL)
{
return false;
}
RealPickup = Inventory(Spawn (type, Pos, NO_REPLACE));
if (RealPickup != NULL)
{
// The internally spawned item should never count towards statistics.
RealPickup.ClearCounters();
if (!bDropped)
{
RealPickup.bDropped = false;
}
// If this item has been dropped by a monster the
// amount of ammo this gives must be adjusted.
if (droppedbymonster)
{
RealPickup.ModifyDropAmount(0);
}
if (!RealPickup.CallTryPickup (toucher))
{
RealPickup.Destroy ();
RealPickup = NULL;
return false;
}
GoAwayAndDie ();
return true;
}
return false;
}
override String PickupMessage ()
{
if (RealPickup != null)
return RealPickup.PickupMessage ();
else return "";
}
override bool ShouldStay ()
{
if (RealPickup != null)
return RealPickup.ShouldStay ();
else return true;
}
override bool ShouldRespawn ()
{
if (RealPickup != null)
return RealPickup.ShouldRespawn ();
else return false;
}
override void PlayPickupSound (Actor toucher)
{
if (RealPickup != null)
RealPickup.PlayPickupSound (toucher);
}
override void DoPickupSpecial (Actor toucher)
{
Super.DoPickupSpecial (toucher);
// If the real pickup hasn't joined the toucher's inventory, make sure it
// doesn't stick around.
if (RealPickup != null && RealPickup.Owner != toucher)
{
RealPickup.Destroy ();
}
RealPickup = null;
}
override void OnDestroy ()
{
if (RealPickup != null)
{
RealPickup.Destroy ();
RealPickup = null;
}
Super.OnDestroy();
}
}
class FakeInventory : Inventory native
//===========================================================================
//
//
//
//===========================================================================
class FakeInventory : Inventory
{
native bool Respawnable;
bool Respawnable;
property respawns: Respawnable;
override bool ShouldRespawn ()
{
return Respawnable && Super.ShouldRespawn();
}
override bool TryPickup (in out Actor toucher)
{
let success = toucher.A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]);
if (success)
{
GoAwayAndDie ();
return true;
}
return false;
}
override void DoPickupSpecial (Actor toucher)
{
// The special was already executed by TryPickup, so do nothing here
}
}

View file

@ -13,7 +13,7 @@ class StateProvider : Inventory native
action native void A_ResetReloadCounter();
}
class CustomInventory : StateProvider native
class CustomInventory : StateProvider
{
Default
{
@ -36,7 +36,8 @@ class CustomInventory : StateProvider native
deprecated action void A_Lower() {}
deprecated action void A_Raise() {}
deprecated action void A_CheckReload() {}
native bool CallStateChain (Actor actor, State state);
//===========================================================================
//
// ACustomInventory :: SpecialDropAction

View file

@ -24,6 +24,7 @@ class CajunTrace : Actor
}
}
struct Bot native
class Bot native
{
native Actor dest;
}