/r10449-add-frobhold.diff Secret
Last active
September 22, 2023 07:55
Star
You must be signed in to star a gist
Patch: Add Frob to Use World Item
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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