From 1c990f3b9b92ae09b0fc690fee85cd6b2647b570 Mon Sep 17 00:00:00 2001 From: ZioPao Date: Thu, 21 Mar 2024 19:37:30 +0100 Subject: [PATCH] Caching hand feasibility, disabling interactions if there are no hands --- .../TOC/Controllers/ItemsController.lua | 3 +- .../Controllers/LimitActionsController.lua | 85 +++++++++++++++---- .../client/TOC/Handlers/AmputationHandler.lua | 3 +- .../client/TOC/Handlers/CachedDataHandler.lua | 58 ++++++++++--- .../client/TOC/Handlers/ProsthesisHandler.lua | 3 + media/lua/client/TOC/Main.lua | 2 +- .../client/TOC/Zombies/ZombiesAmputation.lua | 1 + .../lua/server/TOC/LimitActionsController.lua | 7 ++ media/lua/shared/TOC/BodyLocations.lua | 2 +- 9 files changed, 133 insertions(+), 31 deletions(-) create mode 100644 media/lua/server/TOC/LimitActionsController.lua diff --git a/media/lua/client/TOC/Controllers/ItemsController.lua b/media/lua/client/TOC/Controllers/ItemsController.lua index ea06424..847b648 100644 --- a/media/lua/client/TOC/Controllers/ItemsController.lua +++ b/media/lua/client/TOC/Controllers/ItemsController.lua @@ -42,6 +42,7 @@ function ItemsController.Player.RemoveClothingItem(playerObj, clothingItem) if clothingItem and instanceof(clothingItem, "InventoryItem") then playerObj:removeWornItem(clothingItem) + ---@diagnostic disable-next-line: param-type-mismatch playerObj:getInventory():Remove(clothingItem) -- Umbrella is wrong, can be an InventoryItem too TOC_DEBUG.print("found and deleted" .. tostring(clothingItem)) @@ -123,7 +124,7 @@ function ItemsController.Zombie.SpawnAmputationItem(zombie, amputationFullType) local itemVisual = ItemVisual:new() itemVisual:setItemType(amputationFullType) itemVisual:setTextureChoice(texId) - zombieVisuals:add(itemVisual) + if zombieVisuals then zombieVisuals:add(itemVisual) end zombie:resetModelNextFrame() -- Spawn the item too in the inventory to keep track of stuff this way. It's gonna get deleted when we reload the game diff --git a/media/lua/client/TOC/Controllers/LimitActionsController.lua b/media/lua/client/TOC/Controllers/LimitActionsController.lua index 00f93a7..a51d557 100644 --- a/media/lua/client/TOC/Controllers/LimitActionsController.lua +++ b/media/lua/client/TOC/Controllers/LimitActionsController.lua @@ -9,7 +9,7 @@ local StaticData = require("TOC/StaticData") --* TIMED ACTIONS *-- --- We want to be able to modify how long actions are gonna take, +-- We want to be able to modify how long actions are gonna take, -- depending on amputation status and kind of action. Also, when the -- player has not completely cicatrized their own wounds, and try to do any action with -- a prosthesis on, that can trigger random bleeds. @@ -56,7 +56,7 @@ local og_ISBaseTimedAction_perform = ISBaseTimedAction.perform --- After each action, level up perks ---@diagnostic disable-next-line: duplicate-set-field function ISBaseTimedAction:perform() - og_ISBaseTimedAction_perform(self) + og_ISBaseTimedAction_perform(self) TOC_DEBUG.print("Running ISBaseTimedAction.perform override") @@ -70,7 +70,7 @@ function ISBaseTimedAction:perform() -- We're checking for only "visible" amputations to prevent from having bleeds everywhere if dcInst:getIsCut(limbName) and dcInst:getIsVisible(limbName) then local side = CommonMethods.GetSide(limbName) - LocalPlayerController.playerObj:getXp():AddXP(Perks["Side_" .. side], 1) -- TODO Make it dynamic + LocalPlayerController.playerObj:getXp():AddXP(Perks["Side_" .. side], 1) -- TODO Make it dynamic if not dcInst:getIsCicatrized(limbName) and dcInst:getIsProstEquipped(limbName) then TOC_DEBUG.print("Trying for bleed, player met the criteria") LocalPlayerController.TryRandomBleed(self.character, limbName) @@ -96,10 +96,9 @@ local og_ISEquipWeaponAction_isValid = ISEquipWeaponAction.isValid ---@diagnostic disable-next-line: duplicate-set-field function ISEquipWeaponAction:isValid() local isValid = og_ISEquipWeaponAction_isValid(self) - local dcInst = DataController.GetInstance(self.character:getUsername()) - if isValid and dcInst:getIsAnyLimbCut() then - local isPrimaryHandValid = CheckHandFeasibility(primaryHand) - local isSecondaryHandValid = CheckHandFeasibility(secondaryHand) + if isValid then + local isPrimaryHandValid = CachedDataHandler.GetHandFeasibility(StaticData.SIDES_IND_STR.R) + local isSecondaryHandValid = CachedDataHandler.GetHandFeasibility(StaticData.SIDES_IND_STR.L) -- Both hands are cut off, so it's impossible to equip in any way if not isPrimaryHandValid and not isSecondaryHandValid then isValid = false @@ -137,10 +136,10 @@ function ISEquipWeaponAction:performWithAmputation(dcInst) if not self.twoHands then if getMethodFirst(self.character) and getMethodFirst(self.character):isRequiresEquippedBothHands() then setMethodFirst(self.character, nil) - -- if this weapon is already equiped in the 2nd hand, we remove it + -- if this weapon is already equiped in the 2nd hand, we remove it elseif (getMethodFirst(self.character) == self.item or getMethodFirst(self.character) == getMethodSecond(self.character)) then setMethodFirst(self.character, nil) - -- if we are equipping a handgun and there is a weapon in the secondary hand we remove it + -- if we are equipping a handgun and there is a weapon in the secondary hand we remove it elseif instanceof(self.item, "HandWeapon") and self.item:getSwingAnim() and self.item:getSwingAnim() == "Handgun" then if getMethodFirst(self.character) and instanceof(getMethodFirst(self.character), "HandWeapon") then setMethodFirst(self.character, nil) @@ -156,7 +155,6 @@ function ISEquipWeaponAction:performWithAmputation(dcInst) setMethodFirst(self.character, self.item) end end - else setMethodFirst(self.character, nil) setMethodSecond(self.character, nil) @@ -186,7 +184,6 @@ end local og_ISEquipWeaponAction_perform = ISEquipWeaponAction.perform ---@diagnostic disable-next-line: duplicate-set-field function ISEquipWeaponAction:perform() - og_ISEquipWeaponAction_perform(self) -- TODO Can we do it earlier? @@ -199,18 +196,17 @@ end function ISInventoryPaneContextMenu.doEquipOption(context, playerObj, isWeapon, items, player) -- check if hands if not heavy damaged - if (not playerObj:isPrimaryHandItem(isWeapon) or (playerObj:isPrimaryHandItem(isWeapon) and playerObj:isSecondaryHandItem(isWeapon))) and not getSpecificPlayer(player):getBodyDamage():getBodyPart(BodyPartType.Hand_R):isDeepWounded() and (getSpecificPlayer(player):getBodyDamage():getBodyPart(BodyPartType.Hand_R):getFractureTime() == 0 or getSpecificPlayer(player):getBodyDamage():getBodyPart(BodyPartType.Hand_R):getSplintFactor() > 0) then + if (not playerObj:isPrimaryHandItem(isWeapon) or (playerObj:isPrimaryHandItem(isWeapon) and playerObj:isSecondaryHandItem(isWeapon))) and not getSpecificPlayer(player):getBodyDamage():getBodyPart(BodyPartType.Hand_R):isDeepWounded() and (getSpecificPlayer(player):getBodyDamage():getBodyPart(BodyPartType.Hand_R):getFractureTime() == 0 or getSpecificPlayer(player):getBodyDamage():getBodyPart(BodyPartType.Hand_R):getSplintFactor() > 0) then -- forbid reequipping skinned items to avoid multiple problems for now local add = true if playerObj:getSecondaryHandItem() == isWeapon and isWeapon:getScriptItem():getReplaceWhenUnequip() then add = false end if add then - local equipOption = context:addOption(getText("ContextMenu_Equip_Primary"), items, ISInventoryPaneContextMenu.OnPrimaryWeapon, player) + local equipOption = context:addOption(getText("ContextMenu_Equip_Primary"), items, + ISInventoryPaneContextMenu.OnPrimaryWeapon, player) equipOption.notAvailable = not CheckHandFeasibility(StaticData.LIMBS_IND_STR.Hand_R) end - - end if (not playerObj:isSecondaryHandItem(isWeapon) or (playerObj:isPrimaryHandItem(isWeapon) and playerObj:isSecondaryHandItem(isWeapon))) and not getSpecificPlayer(player):getBodyDamage():getBodyPart(BodyPartType.Hand_L):isDeepWounded() and (getSpecificPlayer(player):getBodyDamage():getBodyPart(BodyPartType.Hand_L):getFractureTime() == 0 or getSpecificPlayer(player):getBodyDamage():getBodyPart(BodyPartType.Hand_L):getSplintFactor() > 0) then -- forbid reequipping skinned items to avoid multiple problems for now @@ -219,10 +215,65 @@ function ISInventoryPaneContextMenu.doEquipOption(context, playerObj, isWeapon, add = false end if add then - local equipOption = context:addOption(getText("ContextMenu_Equip_Secondary"), items, ISInventoryPaneContextMenu.OnSecondWeapon, player) + local equipOption = context:addOption(getText("ContextMenu_Equip_Secondary"), items, + ISInventoryPaneContextMenu.OnSecondWeapon, player) equipOption.notAvailable = not CheckHandFeasibility(StaticData.LIMBS_IND_STR.Hand_L) - end end +end + +local noHandsImpossibleActions = { + getText("ContextMenu_Add_escape_rope_sheet"), + getText("ContextMenu_Add_escape_rope"), + getText("ContextMenu_Remove_escape_rope"), + getText("ContextMenu_Barricade"), + getText("ContextMenu_Unbarricade"), + getText("ContextMenu_MetalBarricade"), + getText("ContextMenu_MetalBarBarricade"), + getText("ContextMenu_Open_window"), + getText("ContextMenu_Close_window"), + getText("ContextMenu_PickupBrokenGlass"), + getText("ContextMenu_Open_door"), + getText("ContextMenu_Close_door"), + +} + + +local og_ISWorldObjectContextMenu_createMenu = ISWorldObjectContextMenu.createMenu + +---@param player integer +---@param worldobjects any +---@param x any +---@param y any +---@param test any +function ISWorldObjectContextMenu.createMenu(player, worldobjects, x, y, test) + ---@type ISContextMenu + local ogContext = og_ISWorldObjectContextMenu_createMenu(player, worldobjects, x, y, test) + + -- check if no hands, disable various interactions + if not CachedDataHandler.GetBothHandsFeasibility() then + TOC_DEBUG.print("NO hands :((") + for i = 1, #noHandsImpossibleActions do + local optionName = noHandsImpossibleActions[i] + local option = ogContext:getOptionFromName(optionName) + if option then + option.notAvailable = true + end + end + end + return ogContext +end + + +local og_ISOpenCloseDoor_perform = ISOpenCloseDoor.perform +function ISOpenCloseDoor:perform() + + if CachedDataHandler.GetBothHandsFeasibility() then + og_ISOpenCloseDoor_perform(self) + else + --getCore():getKey("Interact") + + ISBaseTimedAction.perform(self) + end end \ No newline at end of file diff --git a/media/lua/client/TOC/Handlers/AmputationHandler.lua b/media/lua/client/TOC/Handlers/AmputationHandler.lua index 58cf2d6..0eb9703 100644 --- a/media/lua/client/TOC/Handlers/AmputationHandler.lua +++ b/media/lua/client/TOC/Handlers/AmputationHandler.lua @@ -154,7 +154,8 @@ function AmputationHandler:execute(damagePlayer) CachedDataHandler.AddAmputatedLimb(username, dependedLimbName) end - CachedDataHandler.CalculateHighestAmputatedLimbs(username) + -- Cache highest amputation and hand feasibility + CachedDataHandler.CalculateCacheableValues(username) -- If the part was actually infected, heal the player, if they were in time (infectionLevel < 20) if bd:getInfectionLevel() < 20 and bodyPart:IsInfected() and not dcInst:getIsIgnoredPartInfected() then diff --git a/media/lua/client/TOC/Handlers/CachedDataHandler.lua b/media/lua/client/TOC/Handlers/CachedDataHandler.lua index b26f774..3cc2bd3 100644 --- a/media/lua/client/TOC/Handlers/CachedDataHandler.lua +++ b/media/lua/client/TOC/Handlers/CachedDataHandler.lua @@ -11,9 +11,23 @@ local CachedDataHandler = {} ---@param username string function CachedDataHandler.Setup(username) CachedDataHandler.amputatedLimbs[username] = {} + -- username -> side CachedDataHandler.highestAmputatedLimbs[username] = {} + + + -- Local only, doesn't matter for Health Panel + CachedDataHandler.handFeasibility = {} end +---Will calculate all the values that we need +function CachedDataHandler.CalculateCacheableValues(username) + CachedDataHandler.CalculateHighestAmputatedLimbs(username) + CachedDataHandler.CalculateBothHandsFeasibility() +end + + + + --* Amputated Limbs caching *-- CachedDataHandler.amputatedLimbs = {} @@ -67,16 +81,6 @@ function CachedDataHandler.CalculateHighestAmputatedLimbs(username) return end - -- if CachedDataHandler.amputatedLimbs == nil or CachedDataHandler.amputatedLimbs[username] == nil then - -- --- This function gets ran pretty early, we need to account for the Bob stuff - -- -- if username == "Bob" then - -- -- TOC_DEBUG.print("skip, Bob is default char") - -- -- return - -- -- end - - -- TOC_DEBUG.print("Amputated limbs weren't calculated. Trying to calculate them now for " .. username) - -- CachedDataHandler.CalculateAmputatedLimbs(username) - -- end CachedDataHandler.CalculateAmputatedLimbs(username) local amputatedLimbs = CachedDataHandler.amputatedLimbs[username] @@ -103,4 +107,38 @@ end +--* Hand feasibility caching *-- +CachedDataHandler.handFeasibility = {} + +---@param limbName string +function CachedDataHandler.CalculateHandFeasibility(limbName) + local dcInst = DataController.GetInstance() + local side = CommonMethods.GetSide(limbName) + CachedDataHandler.handFeasibility[side] = not dcInst:getIsCut(limbName) or dcInst:getIsProstEquipped(limbName) +end + + +function CachedDataHandler.GetHandFeasibility(side) + return CachedDataHandler.handFeasibility[side] +end + + +function CachedDataHandler.CalculateBothHandsFeasibility() + CachedDataHandler.CalculateHandFeasibility("Hand_L") + CachedDataHandler.CalculateHandFeasibility("Hand_R") + + if not CachedDataHandler.GetBothHandsFeasibility() then + TOC_DEBUG.print("Disabling interact key") + getCore():addKeyBinding("Interact", Keyboard.KEY_NONE) + else + -- FIX DEFAULT ONE!!!!!!! + TOC_DEBUG.print("Re-enabling interact key") + getCore():addKeyBinding("Interact", Keyboard.KEY_E) + end +end + +function CachedDataHandler.GetBothHandsFeasibility() + return CachedDataHandler.handFeasibility["L"] or CachedDataHandler.handFeasibility["R"] +end + return CachedDataHandler \ No newline at end of file diff --git a/media/lua/client/TOC/Handlers/ProsthesisHandler.lua b/media/lua/client/TOC/Handlers/ProsthesisHandler.lua index f487689..46c16bc 100644 --- a/media/lua/client/TOC/Handlers/ProsthesisHandler.lua +++ b/media/lua/client/TOC/Handlers/ProsthesisHandler.lua @@ -68,6 +68,9 @@ function ProsthesisHandler.SearchAndSetupProsthesis(item, isEquipping) local dcInst = DataController.GetInstance() dcInst:setIsProstEquipped(group, isEquipping) dcInst:apply() + + -- Calculates hands feasibility once again + CachedDataHandler.CalculateBothHandsFeasibility() end ------------------------- diff --git a/media/lua/client/TOC/Main.lua b/media/lua/client/TOC/Main.lua index 2e03c4a..d6c220a 100644 --- a/media/lua/client/TOC/Main.lua +++ b/media/lua/client/TOC/Main.lua @@ -42,7 +42,7 @@ end function Main.SetupEvents() local CachedDataHandler = require("TOC/Handlers/CachedDataHandler") - Events.OnReceivedTocData.Add(CachedDataHandler.CalculateHighestAmputatedLimbs) + Events.OnReceivedTocData.Add(CachedDataHandler.CalculateCacheableValues) end function Main.InitializePlayer() diff --git a/media/lua/client/TOC/Zombies/ZombiesAmputation.lua b/media/lua/client/TOC/Zombies/ZombiesAmputation.lua index 97f6c75..8cf3ad8 100644 --- a/media/lua/client/TOC/Zombies/ZombiesAmputation.lua +++ b/media/lua/client/TOC/Zombies/ZombiesAmputation.lua @@ -23,6 +23,7 @@ end local function GetZombieID(zombie) -- Big love to Chuck and Sir Doggy Jvla for this code +---@diagnostic disable-next-line: param-type-mismatch local pID = zombie:getPersistentOutfitID() local bits = string.split(string.reverse(Long.toUnsignedString(pID, 2)), "") while #bits < 16 do bits[#bits+1] = 0 end diff --git a/media/lua/server/TOC/LimitActionsController.lua b/media/lua/server/TOC/LimitActionsController.lua new file mode 100644 index 0000000..26bf7f8 --- /dev/null +++ b/media/lua/server/TOC/LimitActionsController.lua @@ -0,0 +1,7 @@ +function ISObjectClickHandler.doClickCurtain(object, playerNum, playerObj) + TOC_DEBUG.print("Opening door") + + if not object:canInteractWith(playerObj) then return false end + object:ToggleDoor(playerObj) + return true +end diff --git a/media/lua/shared/TOC/BodyLocations.lua b/media/lua/shared/TOC/BodyLocations.lua index 6ca8693..76b9203 100644 --- a/media/lua/shared/TOC/BodyLocations.lua +++ b/media/lua/shared/TOC/BodyLocations.lua @@ -46,7 +46,7 @@ function TestBodyLocations() end --- TODO MultiItem causes a ton of issues... fucking hell +-- MultiItem causes a ton of issues... fucking hell BodyLocationsAPI.MoveOrCreateBeforeOrAfter("TOC_Arm", "FullTop", true) group:setMultiItem("TOC_Arm", true)