Added client-side item pick ups

Includes feature to disable Actor rendering locally (this cannot be checked from the playsim) and options for disabling co-op only things.
This commit is contained in:
Boondorl 2024-01-28 15:30:03 -05:00 committed by Rachael Alexanderson
parent c3ca564cfc
commit c1539c2286
8 changed files with 140 additions and 13 deletions

View file

@ -575,6 +575,10 @@ CUSTOM_CVAR(Int, dmflags3, 0, CVAR_SERVERINFO | CVAR_NOINITCALL)
CVAR(Flag, sv_noplayerclip, dmflags3, DF3_NO_PLAYER_CLIP);
CVAR(Flag, sv_coopsharekeys, dmflags3, DF3_COOP_SHARE_KEYS);
CVAR(Flag, sv_localitems, dmflags3, DF3_LOCAL_ITEMS);
CVAR(Flag, sv_nolocaldrops, dmflags3, DF3_NO_LOCAL_DROPS);
CVAR(Flag, sv_nocoopitems, dmflags3, DF3_NO_COOP_ONLY_ITEMS);
CVAR(Flag, sv_nocoopthings, dmflags3, DF3_NO_COOP_ONLY_THINGS);
//==========================================================================
//

View file

@ -180,7 +180,11 @@ enum : unsigned
enum : unsigned
{
DF3_NO_PLAYER_CLIP = 1 << 0, // Players can walk through and shoot through each other
DF3_COOP_SHARE_KEYS = 1 << 1, // Keys will be given to all players in coop
DF3_COOP_SHARE_KEYS = 1 << 1, // Keys and other core items will be given to all players in coop
DF3_LOCAL_ITEMS = 1 << 2, // Items are picked up client-side rather than fully taken by the client who picked it up
DF3_NO_LOCAL_DROPS = 1 << 3, // Drops from Actors aren't picked up locally
DF3_NO_COOP_ONLY_ITEMS = 1 << 4, // Items that only appear in co-op are disabled
DF3_NO_COOP_ONLY_THINGS = 1 << 5, // Any Actor that only appears in co-op is disabled
};
// [RH] Compatibility flags.

View file

@ -904,6 +904,9 @@ public:
// Returns true if this view is considered "local" for the player.
bool CheckLocalView() const;
// Allows for enabling/disabling client-side rendering in a way the playsim can't access.
void DisableLocalRendering(const unsigned int pNum, const bool disable);
bool ShouldRenderLocally() const;
// Finds the first item of a particular type.
AActor *FindInventory (PClassActor *type, bool subclass=false);
@ -1125,6 +1128,7 @@ public:
uint32_t RenderRequired; // current renderer must have this feature set
uint32_t RenderHidden; // current renderer must *not* have any of these features
bool NoLocalRender; // DO NOT EXPORT THIS! This is a way to disable rendering such that the playsim cannot access it.
ActorRenderFlags renderflags; // Different rendering flags
ActorRenderFlags2 renderflags2; // More rendering flags...
ActorFlags flags;

View file

@ -970,6 +970,43 @@ DEFINE_ACTION_FUNCTION(AActor, CheckLocalView)
ACTION_RETURN_BOOL(self->CheckLocalView());
}
void AActor::DisableLocalRendering(const unsigned int pNum, const bool disable)
{
if (pNum == consoleplayer)
NoLocalRender = disable;
}
static void DisableLocalRendering(AActor* const self, const unsigned int pNum, const int disable)
{
self->DisableLocalRendering(pNum, disable);
}
DEFINE_ACTION_FUNCTION_NATIVE(AActor, DisableLocalRendering, DisableLocalRendering)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_UINT(pNum);
PARAM_INT(disable);
DisableLocalRendering(self, pNum, disable);
}
bool AActor::ShouldRenderLocally() const
{
return !NoLocalRender;
}
static int ShouldRenderLocally(const AActor* const self)
{
return self->ShouldRenderLocally();
}
DEFINE_ACTION_FUNCTION_NATIVE(AActor, ShouldRenderLocally, ShouldRenderLocally)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_INT(ShouldRenderLocally(self));
}
//============================================================================
//
// AActor :: IsInsideVisibleAngles
@ -1049,6 +1086,9 @@ bool AActor::IsVisibleToPlayer() const
// [BB] Safety check. This should never be NULL. Nevertheless, we return true to leave the default ZDoom behavior unaltered.
if (p == nullptr || p->camera == nullptr )
return true;
if (!ShouldRenderLocally())
return false;
if (VisibleToTeam != 0 && teamplay &&
(signed)(VisibleToTeam-1) != p->userinfo.GetTeam() )
@ -5701,15 +5741,26 @@ AActor *FLevelLocals::SpawnMapThing (FMapThing *mthing, int position)
const AActor *info = GetDefaultByType (i);
// don't spawn keycards and players in deathmatch
if (deathmatch && info->flags & MF_NOTDMATCH)
return NULL;
// Don't spawn keycards and players in deathmatch.
if (deathmatch && (info->flags & MF_NOTDMATCH))
return nullptr;
// don't spawn extra things in coop if so desired
if (multiplayer && !deathmatch && (dmflags2 & DF2_NO_COOP_THING_SPAWN))
// Don't spawn extra things in co-op if desired.
if (multiplayer && !deathmatch)
{
if ((mthing->flags & (MTF_DEATHMATCH|MTF_SINGLE)) == MTF_DEATHMATCH)
return NULL;
// Don't spawn DM-only things in co-op.
if ((dmflags2 & DF2_NO_COOP_THING_SPAWN) && (mthing->flags & (MTF_DEATHMATCH|MTF_SINGLE)) == MTF_DEATHMATCH)
return nullptr;
// Having co-op only functionality is a bit odd, but you never know.
if (!mthing->special && !mthing->thingid && (mthing->flags & (MTF_COOPERATIVE | MTF_SINGLE)) == MTF_COOPERATIVE)
{
// Don't spawn co-op only things in general.
if (dmflags3 & DF3_NO_COOP_ONLY_THINGS)
return nullptr;
// Don't spawn co-op only items.
if ((dmflags3 & DF3_NO_COOP_ONLY_ITEMS) && i->IsDescendantOf(NAME_Inventory))
return nullptr;
}
}
// [RH] don't spawn extra weapons in coop if so desired

View file

@ -1688,6 +1688,8 @@ OptionMenu CoopOptions protected
Title "$GMPLYMNU_COOPERATIVE"
Option "$GMPLYMNU_MULTIPLAYERTHINGS", "sv_nothingspawn", "NoYes"
Option "$GMPLYMNU_COOPTHINGS", "sv_nocoopthings", "NoYes"
Option "$GMPLYMNU_COOPITEMS", "sv_nocoopitems", "NoYes"
Option "$GMPLYMNU_MULTIPLAYERWEAPONS", "sv_noweaponspawn", "NoYes"
Option "$GMPLYMNU_LOSEINVENTORY", "sv_cooploseinventory", "YesNo"
Option "$GMPLYMNU_KEEPKEYS", "sv_cooplosekeys", "NoYes"
@ -1699,6 +1701,8 @@ OptionMenu CoopOptions protected
Option "$GMPLYMNU_SPAWNWHEREDIED", "sv_samespawnspot", "YesNo"
Option "$GMPLYMNU_NOPLAYERCLIP", "sv_noplayerclip", "YesNo"
Option "$GMPLYMNU_SHAREKEYS", "sv_coopsharekeys", "YesNo"
Option "$GMPLYMNU_LOCALITEMS", "sv_localitems", "YesNo"
Option "$GMPLYMNU_NOLOCALDROP", "sv_nolocaldrops", "YesNo"
Class "GameplayMenu"
}

View file

@ -503,6 +503,8 @@ class Actor : Thinker native
virtual native void FallAndSink(double grav, double oldfloorz);
private native void Substitute(Actor replacement);
native ui void DisplayNameTag();
native clearscope void DisableLocalRendering(uint playerNum, bool disable);
native ui bool ShouldRenderLocally(); // Only clients get to check this, never the playsim.
// Called by inventory items to see if this actor is capable of touching them.
// If true, the item will attempt to be picked up. Useful for things like

View file

@ -10,6 +10,8 @@ class Inventory : Actor
const BLINKTHRESHOLD = (4*32);
const BONUSADD = 6;
private bool pickedUp[MAXPLAYERS]; // If items are set to local, track who already picked it up.
deprecated("3.7") private int ItemFlags;
Actor Owner; // Who owns this item? NULL if it's still a pickup.
int Amount; // Amount of item this instance has
@ -65,6 +67,7 @@ class Inventory : Actor
flagdef IsHealth: ItemFlags, 22;
flagdef AlwaysPickup: ItemFlags, 23;
flagdef Unclearable: ItemFlags, 24;
flagdef NeverLocal: ItemFlags, 25;
flagdef ForceRespawnInSurvival: none, 0;
flagdef PickupFlash: none, 6;
@ -768,12 +771,17 @@ class Inventory : Actor
override void Touch (Actor toucher)
{
bool localPickUp;
let player = toucher.player;
// If a voodoo doll touches something, pretend the real player touched it instead.
if (player != NULL)
if (player)
{
// If a voodoo doll touches something, pretend the real player touched it instead.
toucher = player.mo;
// Client already picked this up, so ignore them.
if (HasPickedUpLocally(toucher))
return;
localPickUp = CanPickUpLocally(toucher);
}
bool localview = toucher.CheckLocalView();
@ -781,9 +789,23 @@ class Inventory : Actor
if (!toucher.CanTouchItem(self))
return;
Inventory give = self;
if (localPickUp)
{
give = Inventory(Spawn(GetClass()));
if (!give)
return;
}
bool res;
[res, toucher] = CallTryPickup(toucher);
if (!res) return;
[res, toucher] = give.CallTryPickup(toucher);
if (!res)
{
if (give != self)
give.Destroy();
return;
}
// This is the only situation when a pickup flash should ever play.
if (PickupFlash != NULL && !ShouldStay())
@ -829,6 +851,9 @@ class Inventory : Actor
ac.GiveSecret(true, true);
}
if (localPickUp)
PickUpLocally(toucher);
//Added by MC: Check if item taken was the roam destination of any bot
for (int i = 0; i < MAXPLAYERS; i++)
{
@ -1014,6 +1039,37 @@ class Inventory : Actor
SetStateLabel("HoldAndDestroy");
}
}
// Check if the Actor can recieve a local copy of the item instead of outright taking it.
clearscope bool CanPickUpLocally(Actor other) const
{
return other && other.player
&& multiplayer && !deathmatch && sv_localitems
&& !bNeverLocal && (!bDropped || !sv_nolocaldrops);
}
// Check if a client has already picked up this item locally.
clearscope bool HasPickedUpLocally(Actor client) const
{
return pickedUp[client.PlayerNumber()];
}
// When items are dropped, clear their local pick ups.
void ClearLocalPickUps()
{
DisableLocalRendering(consoleplayer, false);
for (int i; i < MAXPLAYERS; ++i)
pickedUp[i] = false;
}
// Client picked up this item. Mark it as invisible to that specific player and
// prevent them from picking it up again.
protected void PickUpLocally(Actor client)
{
int pNum = client.PlayerNumber();
pickedUp[pNum] = true;
DisableLocalRendering(pNum, true);
}
//===========================================================================
//

View file

@ -291,6 +291,8 @@ extend class Actor
{
Inventory drop = item.CreateTossable(amt);
if (drop == null) return NULL;
drop.ClearLocalPickUps();
drop.bNeverLocal = true;
drop.SetOrigin(Pos + (0, 0, 10.), false);
drop.Angle = Angle;
drop.VelFromAngle(5.);