Skip to content

Instantly share code, notes, and snippets.

@daftmugi
Last active September 22, 2023 07:55
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
Patch: Add Frob to Use World Item
diff --git game/Grabber.cpp game/Grabber.cpp
index b3c08eb..c70baf8 100755
--- game/Grabber.cpp
+++ game/Grabber.cpp
@@ -1612,10 +1612,48 @@ bool CGrabber::ToggleEquip( void )
return rc;
}
+bool CGrabber::EquipFrobEntity( idPlayer *player )
+{
+ idEntity* frobEnt = player->m_FrobEntity.GetEntity();
+
+ // If attachment, such as head, get its body.
+ idEntity* ent = (frobEnt && frobEnt->IsType(idAFAttachment::Type))
+ ? static_cast<idAFAttachment*>(frobEnt)->GetBindMaster()
+ : frobEnt;
+
+ if (!(ent && ent->spawnArgs.GetBool("equippable")))
+ return false;
+
+ if (EquipEntity(ent))
+ {
+ if (ent->IsType(idAFEntity_Base::Type))
+ {
+ // If the body ent frob state is not set to 'false' after shouldering,
+ // the body will be stuck in a highlighted state after dropping it
+ // until the player highlights the body again. So, ensure 'false'.
+ ent->SetFrobbed(false);
+ }
+ else
+ {
+ // Unless shouldering a body, make sure nothing is equipped.
+ Forget(ent);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
bool CGrabber::Equip( void )
{
- idStr str;
idEntity *ent = m_dragEnt.GetEntity();
+ return EquipEntity(ent);
+}
+
+bool CGrabber::EquipEntity( idEntity *ent )
+{
+ idStr str;
if( !ent || !ent->spawnArgs.GetBool("equippable") )
return false;
diff --git game/Grabber.h game/Grabber.h
index c09bcdd..b68aeba 100755
--- game/Grabber.h
+++ game/Grabber.h
@@ -147,9 +147,16 @@ public:
**/
bool ToggleEquip( void );
/**
+ * Daft Mugi #6316
+ * Try to Use/Equip frob-highlighted item without holding it.
+ * Examples: bodies, candles, lanterns, and food
+ **/
+ bool EquipFrobEntity( idPlayer *player );
+ /**
* Actual functions for equipping and dequipping
**/
bool Equip( void );
+ bool EquipEntity( idEntity *ent );
bool Dequip( void );
/**
diff --git game/Player.cpp game/Player.cpp
index 2973772..df1d67f 100644
--- game/Player.cpp
+++ game/Player.cpp
@@ -706,6 +706,11 @@ idPlayer::idPlayer() :
multiloot = false;
multiloot_lastfrob = 0;
+ // Daft Mugi #6316
+ frobholdEntity = NULL;
+ frobholdDraggedBodyEntity = NULL;
+ frobholdLastFrob = 0;
+
// greebo: Initialise the frob trace contact material to avoid
// crashing during map save when nothing has been frobbed yet
memset(&m_FrobTrace, 0, sizeof(trace_t));
@@ -2423,6 +2428,12 @@ void idPlayer::Restore( idRestoreGame *savefile ) {
multiloot = false;
multiloot_lastfrob = 0;
+ // Daft Mugi #6316: Frob Hold to use world item
+ // The frobhold values don't get saved, but reset on load.
+ frobholdEntity = NULL;
+ frobholdDraggedBodyEntity = NULL;
+ frobholdLastFrob = 0;
+
savefile->ReadInt( buttonMask );
savefile->ReadInt( oldButtons );
savefile->ReadInt( oldFlags );
@@ -6021,13 +6032,7 @@ void idPlayer::PerformKeyRepeat(int impulse, int holdTime)
case IMPULSE_INVENTORY_USE: // Inventory Use Item
{
- const CInventoryCursorPtr& crsr = InventoryCursor();
- CInventoryItemPtr it = crsr->GetCurrentItem();
-
- if (it != NULL && it->GetType() != CInventoryItem::IT_DUMMY)
- {
- UseInventoryItem(ERepeat, it, holdTime, false);
- }
+ InventoryUseKeyRepeat(holdTime);
}
break;
}
@@ -8818,6 +8823,30 @@ void idPlayer::SetInfluenceFov( float fov ) {
influenceFov = fov;
}
+/*
+================
+idPlayer::IsFrobholdEnabled
+================
+*/
+bool idPlayer::IsFrobholdEnabled( void )
+{
+ // Frob hold delay of 0 matches TDM v2.11 (and prior) behavior
+ return cv_frobhold_delay.GetInteger() > 0;
+}
+
+/*
+================
+idPlayer::IsFrobholdActionable
+================
+*/
+bool idPlayer::IsFrobholdActionable( void )
+{
+ int delay = cv_frobhold_delay.GetInteger();
+ // Is frobhold enabled and has enough time elapsed?
+ return (delay > 0)
+ && (gameLocal.time - frobholdLastFrob >= delay);
+}
+
/*
================
idPlayer::OnLadder
@@ -9893,6 +9922,17 @@ float idPlayer::GetMovementVolMod( void )
return returnval;
}
+void idPlayer::InventoryUseKeyRepeat(int holdTime)
+{
+ const CInventoryCursorPtr& crsr = InventoryCursor();
+ CInventoryItemPtr it = crsr->GetCurrentItem();
+
+ if (it != NULL && it->GetType() != CInventoryItem::IT_DUMMY)
+ {
+ UseInventoryItem(ERepeat, it, holdTime, false);
+ }
+}
+
void idPlayer::InventoryUseKeyRelease(int holdTime)
{
const CInventoryCursorPtr& crsr = InventoryCursor();
@@ -11457,7 +11497,7 @@ void idPlayer::PerformFrob(EImpulseState impulseState, idEntity* target, bool al
if ( (GetImmobilization() & EIM_FROB_COMPLEX) && !target->m_bFrobSimple )
{
// TODO: Rename this "uh-uh" sound to something more general?
- StartSound( "snd_drop_item_failed", SND_CHANNEL_ITEM, 0, false, NULL );
+ StartSound( "snd_drop_item_failed", SND_CHANNEL_ITEM, 0, false, NULL );
return;
}
@@ -11465,7 +11505,7 @@ void idPlayer::PerformFrob(EImpulseState impulseState, idEntity* target, bool al
// Retrieve the entity before trying to add it to the inventory, the pointer
// might be cleared after calling AddToInventory().
idEntity* highlightedEntity = m_FrobEntity.GetEntity();
-
+
if (impulseState == EPressed)
{
// Fire the STIM_FROB response on key down (if defined) on this entity
@@ -11503,10 +11543,9 @@ void idPlayer::PerformFrob(EImpulseState impulseState, idEntity* target, bool al
// Inventory item could not be used with the highlighted entity, proceed with ordinary frob action
- // These actions are only applicable for EPressed buttonstate
+ // Try to add world item to inventory
if (impulseState == EPressed || ((impulseState == ERepeat) && multiloot))
{
-
// First we have to check whether that entity is an inventory
// item. In that case, we have to add it to the inventory and
// hide the entity.
@@ -11548,49 +11587,137 @@ void idPlayer::PerformFrob(EImpulseState impulseState, idEntity* target, bool al
// grayman #3011 - is anything sitting on this inventory item?
target->ActivateContacts();
+
+ // Item added to inventory, so skip other frob code
+ return;
}
- if (impulseState == EPressed)
+ }
+
+ // Item could not be added to inventory, so handle body and moveable light frob.
+
+ const bool grabableType = target->spawnArgs.GetBool("grabable", "1"); // allow override
+ const bool usableType = target->spawnArgs.GetBool("equippable", "0");
+ const bool bodyType = grabableType
+ && target->IsType(idAFEntity_Base::Type)
+ || target->IsType(idAFAttachment::Type);
+ const bool moveableType = grabableType
+ && target->IsType(idMoveable::Type)
+ || target->IsType(idMoveableItem::Type);
+ const bool shoulderableBodyType = usableType
+ && bodyType;
+ const bool extinguishableType = usableType
+ && moveableType
+ && target->spawnArgs.FindKey("extinguished");
+
+ const bool canFrobBody = impulseState == EPressed
+ && bodyType
+ && !IsFrobholdEnabled();
+ const bool canFrobPickUp = impulseState == EPressed
+ && moveableType
+ || canFrobBody;
+ const bool canFrobholdBegin = impulseState == EPressed
+ && IsFrobholdEnabled();
+ const bool canFrobholdSpecialAction = impulseState == ERepeat
+ && highlightedEntity == frobholdEntity
+ && IsFrobholdActionable();
+ const bool canFrobholdPickUp = impulseState == EReleased
+ && highlightedEntity == frobholdEntity
+ && IsFrobholdEnabled();
+
+ if (bodyType)
+ {
+ // Do not pick up live, conscious AI
+ if (target->IsType(idAI::Type))
+ {
+ idAI* AItarget = static_cast<idAI*>(target);
+ if ((AItarget->health > 0) && !AItarget->IsKnockedOut())
+ return;
+ }
+
+ // Daft Mugi #6257: Auto-Search Bodies
+ if (cv_tdm_autosearch_bodies.GetBool()
+ // delay > 0, new behavior (on EReleased)
+ && canFrobholdPickUp
+ // delay == 0, TDM v2.11 (and prior) behavior (on EPressed)
+ || canFrobBody)
{
- // Grab it if it's a grabable class
- if (target->IsType(idMoveable::Type) || target->IsType(idAFEntity_Base::Type) ||
- target->IsType(idMoveableItem::Type) || target->IsType(idAFAttachment::Type))
+ // If attachment, such as head, get its body.
+ idEntity* body = target->IsType(idAFAttachment::Type)
+ ? static_cast<idAFAttachment*>(target)->GetBindMaster()
+ : target;
+
+ // If looted body this time, do not shoulder/pick up body.
+ // NOTE: The body being frobbed might not be an idAI.
+ if (body
+ && body->IsType(idAFEntity_Base::Type)
+ && body->AddAttachmentsToInventory(this))
{
- // allow override of default grabbing behavior
- if (!target->spawnArgs.GetBool("grabable", "1"))
- {
- return;
- }
+ return;
+ }
+ }
- // Do not pick up live, conscious AI
- if (target->IsType(idAI::Type))
- {
- idAI* AItarget = static_cast<idAI*>(target);
- if ((AItarget->health > 0) && !AItarget->IsKnockedOut())
- {
- return;
- }
- }
+ if (canFrobholdBegin) // EPressed
+ {
+ // Store body entity and start time tracking.
+ frobholdEntity = highlightedEntity;
+ frobholdDraggedBodyEntity = NULL;
+ frobholdLastFrob = gameLocal.time;
+ return;
+ }
- if (cv_tdm_autosearch_bodies.GetBool())
- {
- // If attachment, such as head, get its body.
- idEntity* body = target->IsType(idAFAttachment::Type) ?
- static_cast<idAFAttachment*>(target)->GetBindMaster() :
- target;
+ if (canFrobholdSpecialAction) // ERepeat
+ {
+ // Drag body if enough time has passed.
+ gameLocal.m_Grabber->Update(this, false, true);
+ frobholdEntity = NULL;
+ // Store grabber entity of dragged body, so the body can be released later.
+ frobholdDraggedBodyEntity = gameLocal.m_Grabber->GetSelected();
+ return;
+ }
- // NOTE: The body being looted might not be an idAI.
- if (body && body->IsType(idAFEntity_Base::Type))
- {
- // Daft Mugi #6257
- // If looted body this time, do not pick up.
- if (body->AddAttachmentsToInventory(this))
- return;
- }
- }
+ if (canFrobholdPickUp) // EReleased
+ {
+ // Shoulder/pick up body
+ gameLocal.m_Grabber->EquipFrobEntity(this);
+ frobholdEntity = NULL;
+ return;
+ }
+ }
- gameLocal.m_Grabber->Update(this, false, true); // preservePosition = true #4149
- }
+ if (extinguishableType)
+ {
+ if (canFrobholdBegin) // EPressed
+ {
+ // Store extinguishable entity and start time tracking.
+ frobholdEntity = highlightedEntity;
+ frobholdLastFrob = gameLocal.time;
+ return;
}
+
+ if (canFrobholdSpecialAction) // ERepeat
+ {
+ // Extinguish (or toggle on/off) moveable light source, if enough time has passed.
+ gameLocal.m_Grabber->EquipFrobEntity(this);
+ frobholdEntity = NULL;
+ return;
+ }
+
+ if (canFrobholdPickUp) // EReleased
+ {
+ // Pick up moveable light source, since it was not extinguished (or toggled on/off).
+ gameLocal.m_Grabber->Update(this, false, true);
+ frobholdEntity = NULL;
+ return;
+ }
+ }
+
+ // Item could not be added to inventory, so try to pick it up.
+
+ if (canFrobPickUp) // EPressed
+ {
+ // Grab it if it's a grabable class and not overridden
+ gameLocal.m_Grabber->Update(this, false, true); // preservePosition = true #4149
+ return;
}
}
@@ -11602,9 +11729,22 @@ void idPlayer::PerformFrob()
return;
}
- // if the grabber is currently holding something and frob is pressed,
+ idEntity* grabberEnt = gameLocal.m_Grabber->GetSelected();
+
+ // If holding a candle/lantern, begin tracking frob for later
+ // extinguish or drop.
+ if (IsFrobholdEnabled()
+ && grabberEnt
+ && grabberEnt->spawnArgs.FindKey("extinguished"))
+ {
+ frobholdEntity = grabberEnt;
+ frobholdLastFrob = gameLocal.time;
+ return;
+ }
+
+ // If the grabber is currently holding something and frob is pressed,
// release it. Do not frob anything new since you're holding an item.
- if ( gameLocal.m_Grabber->GetSelected() )
+ if (grabberEnt)
{
gameLocal.m_Grabber->Update( this );
return;
@@ -11613,6 +11753,16 @@ void idPlayer::PerformFrob()
// Get the currently frobbed entity
idEntity* frob = m_FrobEntity.GetEntity();
+ // If there is nothing highlighted and shouldering a body,
+ // drop the body.
+ if (IsFrobholdEnabled()
+ && !frob
+ && IsShoulderingBody())
+ {
+ gameLocal.m_Grabber->Dequip();
+ return;
+ }
+
// Relay the function to the specialised method
PerformFrob(EPressed, frob, true);
}
@@ -11625,6 +11775,19 @@ void idPlayer::PerformFrobKeyRepeat(int holdTime)
return;
}
+ idEntity* grabberEnt = gameLocal.m_Grabber->GetSelected();
+
+ // If holding a candle/lantern, use it if frob held long enough.
+ if (frobholdEntity
+ && grabberEnt
+ && grabberEnt->spawnArgs.FindKey("extinguished")
+ && IsFrobholdActionable())
+ {
+ gameLocal.m_Grabber->ToggleEquip();
+ frobholdEntity = NULL;
+ return;
+ }
+
// Get the currently frobbed entity
idEntity* frob = m_FrobEntity.GetEntity();
@@ -11643,12 +11806,37 @@ void idPlayer::PerformFrobKeyRelease(int holdTime)
{
// Obsttorte: multilooting
multiloot = false;
+
// Ignore frobs if player-frobbing is immobilized.
if ( GetImmobilization() & EIM_FROB )
{
return;
}
+ idEntity* grabberEnt = gameLocal.m_Grabber->GetSelected();
+
+ // If currently dragging a body and frob is released, stop dragging.
+ // When frobhold delay is 0, behavior matches TDM v2.11 (and prior).
+ if (IsFrobholdEnabled()
+ && frobholdDraggedBodyEntity
+ && frobholdDraggedBodyEntity == grabberEnt)
+ {
+ gameLocal.m_Grabber->Update( this );
+ frobholdDraggedBodyEntity = NULL;
+ return;
+ }
+
+ // If currently holding a candle/lantern, drop it.
+ if (IsFrobholdEnabled()
+ && frobholdEntity
+ && grabberEnt
+ && grabberEnt->spawnArgs.FindKey("extinguished"))
+ {
+ gameLocal.m_Grabber->Update( this );
+ frobholdEntity = NULL;
+ return;
+ }
+
// Get the currently frobbed entity
idEntity* frob = m_FrobEntity.GetEntity();
diff --git game/Player.h game/Player.h
index b4f2fd5..8e32089 100644
--- game/Player.h
+++ game/Player.h
@@ -827,6 +827,10 @@ public:
bool IsShoulderingBody( void ) { return m_bShoulderingBody; };
+ // Daft Mugi #6316: Frob Hold to use world item
+ bool IsFrobholdEnabled( void );
+ bool IsFrobholdActionable( void );
+
bool OnLadder( void ) const;
// Virtal override of idActor::OnElevator()
virtual CMultiStateMover* OnElevator(bool mustBeMoving) const;
@@ -871,6 +875,11 @@ public:
bool multiloot;
int multiloot_lastfrob;
+ // Daft Mugi #6316: Frob Hold to use world item
+ idEntity* frobholdEntity;
+ idEntity* frobholdDraggedBodyEntity;
+ int frobholdLastFrob;
+
// angua: Set ideal crouch state
void EvaluateCrouch();
@@ -946,6 +955,9 @@ public:
**/
bool DropToHands( idEntity *ent, CInventoryItemPtr item = CInventoryItemPtr() );
+ // Performs the inventory action for onButtonRepeat
+ void InventoryUseKeyRepeat(int holdTime);
+
// Performs the inventory action for onButtonRelease
void InventoryUseKeyRelease(int holdTime);
diff --git game/gamesys/SysCvar.cpp game/gamesys/SysCvar.cpp
index f689060..f6b7173 100644
--- game/gamesys/SysCvar.cpp
+++ game/gamesys/SysCvar.cpp
@@ -332,6 +332,15 @@ idCVar cv_frobhelper_ignore_size( "tdm_frobhelper_ignore_size", "40.0", CVAR_G
// Obsttorte: #5984 (multilooting)
idCVar cv_multiloot_min_interval("tdm_multiloot_min_interval", "300", CVAR_GAME | CVAR_ARCHIVE | CVAR_FLOAT | CVAR_NOCHEAT, "The minimum interval between two consecutive frobs when multifrobbing.");
idCVar cv_multiloot_max_interval("tdm_multiloot_max_interval", "2000", CVAR_GAME | CVAR_ARCHIVE | CVAR_FLOAT | CVAR_NOCHEAT, "The amount of time after which multilooting gets disabled again.");
+
+// Daft Mugi #6316: Frob Hold to use world item
+idCVar cv_frobhold_delay(
+ "tdm_frobhold_delay", "200", CVAR_GAME | CVAR_ARCHIVE | CVAR_INTEGER | CVAR_NOCHEAT,
+ "The frob hold delay (in ms) before drag or extinguish.\n"
+ "Set to 0 for TDM v2.11 (and prior) behavior.",
+ 0, 2000
+);
+
// #4289
idCVar cv_pm_blackjack_indicate("tdm_blackjack_indicate", "1", CVAR_GAME | CVAR_ARCHIVE | CVAR_BOOL | CVAR_NOCHEAT, "Set to 1 to activate blackjack indicator.", 0, 1);
diff --git game/gamesys/SysCvar.h game/gamesys/SysCvar.h
index 6c92078..c4cbd92 100644
--- game/gamesys/SysCvar.h
+++ game/gamesys/SysCvar.h
@@ -275,6 +275,9 @@ extern idCVar cv_frobhelper_ignore_size;
extern idCVar cv_multiloot_min_interval;
extern idCVar cv_multiloot_max_interval;
+// Daft Mugi #6316: Frob Hold to use world item
+extern idCVar cv_frobhold_delay;
+
extern idCVar cv_weapon_next_on_empty;
// physics
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment