Rethinking structure a bit

This commit is contained in:
ZioPao
2024-01-08 10:57:01 +01:00
parent 6b1205e160
commit 312294edb8
19 changed files with 229 additions and 222 deletions

View File

@@ -1,7 +1,7 @@
local ModDataHandler = require("TOC/Handlers/ModDataHandler")
local ItemsHandler = require("TOC/Handlers/ItemsHandler")
local DataController = require("TOC/Controllers/DataController")
local ItemsController = require("TOC/Handlers/ItemsController")
local CachedDataHandler = require("TOC/Handlers/CachedDataHandler")
local PlayerHandler = require("TOC/Handlers/PlayerHandler")
local LocalPlayerController = require("TOC/Controllers/LocalPlayerController")
local StaticData = require("TOC/StaticData")
---------------------------
@@ -132,9 +132,9 @@ function AmputationHandler:execute(damagePlayer)
local surgeonFactor = self.surgeonPl:getPerkLevel(Perks.Doctor) * SandboxVars.TOC.SurgeonAbilityImportance
-- Set the data in modData
local modDataHandler = ModDataHandler.GetInstance()
modDataHandler:setCutLimb(self.limbName, false, false, false, surgeonFactor)
modDataHandler:apply() -- This will force rechecking the cached amputated limbs on the other client
local dcInst = DataController.GetInstance()
dcInst:setCutLimb(self.limbName, false, false, false, surgeonFactor)
dcInst:apply() -- This will force rechecking the cached amputated limbs on the other client
-- Heal the area, we're gonna re-set the damage after (if it's enabled)
local bd = self.patientPl:getBodyDamage()
@@ -142,8 +142,8 @@ function AmputationHandler:execute(damagePlayer)
PlayerHandler.HealArea(bodyPart)
-- Give the player the correct amputation item
ItemsHandler.Player.DeleteOldAmputationItem(self.patientPl, self.limbName)
ItemsHandler.Player.SpawnAmputationItem(self.patientPl, self.limbName)
ItemsController.Player.DeleteOldAmputationItem(self.patientPl, self.limbName)
ItemsController.Player.SpawnAmputationItem(self.patientPl, self.limbName)
-- Add it to the list of cut limbs on this local client
local username = self.patientPl:getUsername()
@@ -158,8 +158,8 @@ function AmputationHandler:execute(damagePlayer)
CachedDataHandler.CalculateHighestAmputatedLimbs(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 modDataHandler:getIsIgnoredPartInfected() then
PlayerHandler.HealZombieInfection(bd, bodyPart, self.limbName, modDataHandler)
if bd:getInfectionLevel() < 20 and bodyPart:IsInfected() and not dcInst:getIsIgnoredPartInfected() then
PlayerHandler.HealZombieInfection(bd, bodyPart, self.limbName, dcInst)
end
-- The last part is to handle the damage that the player will receive after the amputation

View File

@@ -1,5 +1,5 @@
local StaticData = require("TOC/StaticData")
local ModDataHandler = require("TOC/Handlers/ModDataHandler")
local DataController = require("TOC/Controllers/DataController")
local CommonMethods = require("TOC/CommonMethods")
---------------------------
@@ -21,12 +21,12 @@ CachedDataHandler.amputatedLimbs = {}
function CachedDataHandler.CalculateAmputatedLimbs(username)
TOC_DEBUG.print("[CachedDataHandler] Calculating amputated limbs for " .. username)
CachedDataHandler.amputatedLimbs[username] = {}
local modDataHandler = ModDataHandler.GetInstance(username)
local dcInst = DataController.GetInstance(username)
-- TODO If the data hasn't arrived, this won't work
for i=1, #StaticData.LIMBS_STR do
local limbName = StaticData.LIMBS_STR[i]
if modDataHandler:getIsCut(limbName) then
if dcInst:getIsCut(limbName) then
CachedDataHandler.AddAmputatedLimb(username, limbName)
end
end
@@ -58,9 +58,9 @@ CachedDataHandler.highestAmputatedLimbs = {}
---@param username string
function CachedDataHandler.CalculateHighestAmputatedLimbs(username)
TOC_DEBUG.print("[CachedDataHandler] Triggered CalculateHighestAmputatedLimbs")
local modDataHandler = ModDataHandler.GetInstance(username)
if modDataHandler == nil then
TOC_DEBUG.print("ModDataHandler not found for " .. username)
local dcInst = DataController.GetInstance(username)
if dcInst == nil then
TOC_DEBUG.print("DataController not found for " .. username)
return
end
@@ -83,7 +83,7 @@ function CachedDataHandler.CalculateHighestAmputatedLimbs(username)
for k, _ in pairs(amputatedLimbs) do
local limbName = k
local index = CommonMethods.GetSide(limbName)
if modDataHandler:getIsCut(limbName) and modDataHandler:getIsVisible(limbName) then
if dcInst:getIsCut(limbName) and dcInst:getIsVisible(limbName) then
TOC_DEBUG.print("[CachedDataHandler] Added Highest Amputation: " .. limbName)
CachedDataHandler.highestAmputatedLimbs[username][index] = limbName
end

View File

@@ -1,167 +0,0 @@
local StaticData = require("TOC/StaticData")
local CommonMethods = require("TOC/CommonMethods")
---------------------------
--- Submodule to handle spawning the correct items after certain actions (ie: cutting a hand). LOCAL ONLY!
---@class ItemsHandler
local ItemsHandler = {}
--* Player Methods *--
---@class ItemsHandler.Player
ItemsHandler.Player = {}
---Returns the correct index for the textures of the amputation
---@param playerObj IsoPlayer
---@param isCicatrized boolean
---@return number
---@private
function ItemsHandler.Player.GetAmputationTexturesIndex(playerObj, isCicatrized)
local textureString = playerObj:getHumanVisual():getSkinTexture()
local isHairy = textureString:sub(-1) == "a"
local matchedIndex = tonumber(textureString:match("%d$")) or 0
if isHairy then
matchedIndex = matchedIndex + 5
end
if isCicatrized then
matchedIndex = matchedIndex + (isHairy and 5 or 10) -- We add 5 is it's the texture texture, else 10
end
return matchedIndex - 1
end
---Main function to delete a clothing item
---@param playerObj IsoPlayer
---@param clothingItem InventoryItem?
---@return boolean
---@private
function ItemsHandler.Player.RemoveClothingItem(playerObj, clothingItem)
if clothingItem and instanceof(clothingItem, "InventoryItem") then
playerObj:removeWornItem(clothingItem)
playerObj:getInventory():Remove(clothingItem) -- Can be a InventoryItem too.. I guess? todo check it
TOC_DEBUG.print("found and deleted" .. tostring(clothingItem))
return true
end
return false
end
---Search and deletes an old amputation clothing item on the same side
---@param playerObj IsoPlayer
---@param limbName string
function ItemsHandler.Player.DeleteOldAmputationItem(playerObj, limbName)
local side = CommonMethods.GetSide(limbName)
for partName, _ in pairs(StaticData.PARTS_IND_STR) do
local othLimbName = partName .. "_" .. side
local othClothingItemName = StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. othLimbName
-- TODO FindAndReturn could return an ArrayList. We need to check for that
local othClothingItem = playerObj:getInventory():FindAndReturn(othClothingItemName)
-- If we manage to find and remove an item, then we should stop this function.
---@cast othClothingItem InventoryItem
if ItemsHandler.Player.RemoveClothingItem(playerObj, othClothingItem) then return end
end
end
---Deletes all the old amputation items, used for resets
---@param playerObj IsoPlayer
function ItemsHandler.Player.DeleteAllOldAmputationItems(playerObj)
for i=1, #StaticData.LIMBS_STR do
local limbName = StaticData.LIMBS_STR[i]
local clothItemName = StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName
local clothItem = playerObj:getInventory():FindAndReturn(clothItemName)
---@cast clothItem InventoryItem
ItemsHandler.Player.RemoveClothingItem(playerObj, clothItem)
end
end
---Spawns and equips the correct amputation item to the player.
---@param playerObj IsoPlayer
---@param limbName string
function ItemsHandler.Player.SpawnAmputationItem(playerObj, limbName)
TOC_DEBUG.print("clothing name " .. StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName)
local clothingItem = playerObj:getInventory():AddItem(StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName)
local texId = ItemsHandler.Player.GetAmputationTexturesIndex(playerObj, false)
---@cast clothingItem InventoryItem
clothingItem:getVisual():setTextureChoice(texId) -- it counts from 0, so we have to subtract 1
playerObj:setWornItem(clothingItem:getBodyLocation(), clothingItem)
end
--* Zombie Methods *--
---@class ItemsHandler.Zombie
ItemsHandler.Zombie = {}
---Set an amputation to a zombie
---@param zombie IsoZombie
function ItemsHandler.Zombie.SpawnAmputationItem(zombie)
-- TODO Set texture ID
local itemVisualsList = zombie:getItemVisuals()
local ignoredLimbs = {}
if itemVisualsList == nil then return end
for i=0, itemVisualsList:size() - 1 do
local itemVisual = itemVisualsList:get(i)
-- TODO Check body location of item and deletes potential amputation to apply
local clothingName = itemVisual:getClothingItemName()
--print(clothingName)
if clothingName and luautils.stringStarts(clothingName, StaticData.AMPUTATION_CLOTHING_ITEM_BASE) then
TOC_DEBUG.print("added " .. clothingName .. " to ignoredLimbs")
ignoredLimbs[clothingName] = clothingName
end
end
-- TODO Consider highest amputation
local usableClothingAmputations = {}
for i=1, #StaticData.LIMBS_STR do
local limbName = StaticData.LIMBS_STR[i]
local clothingName = StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName
if ignoredLimbs[clothingName] == nil then
table.insert(usableClothingAmputations, clothingName)
end
end
-- TODO Random index
local index = ZombRand(1, #usableClothingAmputations)
local itemVisual = ItemVisual:new()
itemVisual:setItemType(usableClothingAmputations[index])
zombie:getItemVisuals():add(itemVisual)
zombie:resetModelNextFrame()
end
--------------------------
--* Overrides *--
local og_ISInventoryPane_refreshContainer = ISInventoryPane.refreshContainer
---Get the list of items for the container and remove the reference to the amputation items
---@diagnostic disable-next-line: duplicate-set-field
function ISInventoryPane:refreshContainer()
og_ISInventoryPane_refreshContainer(self)
if TOC_DEBUG.disablePaneMod then return end
for i=1, #self.itemslist do
local cItem = self.itemslist[i]
if cItem and cItem.cat == "Amputation" then
TOC_DEBUG.print("Refreshing container - current item is an amputation, removing it from the list of the container")
table.remove(self.itemslist, i)
end
end
end
return ItemsHandler

View File

@@ -1,387 +0,0 @@
local CommandsData = require("TOC/CommandsData")
local StaticData = require("TOC/StaticData")
----------------
--- Handle all mod data related stuff
---@class ModDataHandler
---@field username string
---@field tocData tocModData
---@field isDataReady boolean
---@field isResetForced boolean
local ModDataHandler = {}
ModDataHandler.instances = {}
---Setup a new Mod Data Handler
---@param username string
---@param isResetForced boolean?
---@return ModDataHandler
function ModDataHandler:new(username, isResetForced)
TOC_DEBUG.print("[ModDataHandler] NEW for " .. username)
--error("TEST TRIGGER")
local o = {}
setmetatable(o, self)
self.__index = self
o.username = username
local key = CommandsData.GetKey(username)
-- We don't want to request ModData if we're in SP, or if we're forcing a reset
if isClient() and not isResetForced then
ModData.request(key)
o.isResetForced = isResetForced
o.isDataReady = false
elseif isResetForced then
self:setup(key)
o.isResetForced = false
o.isDataReady = true
end
ModDataHandler.instances[username] = o
return o
end
---Setup a new toc mod data data class
---@param key string
function ModDataHandler:setup(key)
TOC_DEBUG.print("[ModDataHandler] Running setup")
---@type tocModData
self.tocData = {
-- Generic stuff that does not belong anywhere else
isIgnoredPartInfected = false,
isAnyLimbCut = false,
limbs = {},
prostheses = {}
}
---@type partData
local defaultParams = {
isCut = false, isInfected = false, isOperated = false, isCicatrized = false, isCauterized = false,
woundDirtyness = -1, cicatrizationTime = -1,
isVisible = false
}
-- Initialize limbs
for i=1, #StaticData.LIMBS_STR do
local limbName = StaticData.LIMBS_STR[i]
self.tocData.limbs[limbName] = {}
self:setLimbParams(StaticData.LIMBS_STR[i], defaultParams, 0)
end
-- Initialize prostheses stuff
for i=1, #StaticData.PROSTHESES_GROUPS_STR do
local group = StaticData.PROSTHESES_GROUPS_STR[i]
self.tocData.prostheses[group] = {
isProstEquipped = false,
prostFactor = 0,
}
end
-- Add it to global mod data
ModData.add(key, self.tocData)
end
---In case of desync between the table on ModData and the table here
---@param tocData tocModData
function ModDataHandler:reapplyTocData(tocData)
local key = CommandsData.GetKey(self.username)
ModData.add(key, tocData)
self.tocData = ModData.get(key)
end
-----------------
--* Setters *--
function ModDataHandler:setIsDataReady(isDataReady)
self.isDataReady = isDataReady
end
---Set a generic boolean that toggles varies function of the mod
---@param isAnyLimbCut boolean
function ModDataHandler:setIsAnyLimbCut(isAnyLimbCut)
self.tocData.isAnyLimbCut = isAnyLimbCut
end
---Set isIgnoredPartInfected
---@param isIgnoredPartInfected boolean
function ModDataHandler:setIsIgnoredPartInfected(isIgnoredPartInfected)
self.tocData.isIgnoredPartInfected = isIgnoredPartInfected
end
---Set isCut
---@param limbName string
---@param isCut boolean
function ModDataHandler:setIsCut(limbName, isCut)
self.tocData.limbs[limbName].isCut = isCut
end
---Set isInfected
---@param limbName string
---@param isInfected boolean
function ModDataHandler:setIsInfected(limbName, isInfected)
self.tocData.limbs[limbName].isInfected = isInfected
end
---Set isCicatrized
---@param limbName string
---@param isCicatrized boolean
function ModDataHandler:setIsCicatrized(limbName, isCicatrized)
self.tocData.limbs[limbName].isCicatrized = isCicatrized
end
---Set isCauterized
---@param limbName string
---@param isCauterized boolean
function ModDataHandler:setIsCauterized(limbName, isCauterized)
self.tocData.limbs[limbName].isCauterized = isCauterized
end
---Set woundDirtyness
---@param limbName string
---@param woundDirtyness number
function ModDataHandler:setWoundDirtyness(limbName, woundDirtyness)
self.tocData.limbs[limbName].woundDirtyness = woundDirtyness
end
---Set cicatrizationTime
---@param limbName string
---@param cicatrizationTime number
function ModDataHandler:setCicatrizationTime(limbName, cicatrizationTime)
self.tocData.limbs[limbName].cicatrizationTime = cicatrizationTime
end
---Set isProstEquipped
---@param group string
---@param isProstEquipped boolean
function ModDataHandler:setIsProstEquipped(group, isProstEquipped)
self.tocData.prostheses[group].isProstEquipped = isProstEquipped
end
---Set prostFactor
---@param group string
---@param prostFactor number
function ModDataHandler:setProstFactor(group, prostFactor)
self.tocData.prostheses[group].prostFactor = prostFactor
end
-----------------
--* Getters *--
---comment
---@return boolean
function ModDataHandler:getIsDataReady()
return self.isDataReady
end
---Set a generic boolean that toggles varies function of the mod
---@return boolean
function ModDataHandler:getIsAnyLimbCut()
--if self.isDataReady == false then return false end
return self.tocData.isAnyLimbCut
end
---Get isIgnoredPartInfected
---@return boolean
function ModDataHandler:getIsIgnoredPartInfected()
return self.tocData.isIgnoredPartInfected
end
---Get isCut
---@param limbName string
---@return boolean
function ModDataHandler:getIsCut(limbName)
if not self.isDataReady then return false end
if self.tocData.limbs[limbName] then
return self.tocData.limbs[limbName].isCut
else
return false
end
end
---Get isVisible
---@param limbName string
---@return boolean
function ModDataHandler:getIsVisible(limbName)
if not self.isDataReady then return false end
return self.tocData.limbs[limbName].isVisible
end
---Get isCicatrized
---@param limbName string
---@return boolean
function ModDataHandler:getIsCicatrized(limbName)
return self.tocData.limbs[limbName].isCicatrized
end
---Get isCauterized
---@param limbName string
---@return boolean
function ModDataHandler:getIsCauterized(limbName)
return self.tocData.limbs[limbName].isCauterized
end
---Get woundDirtyness
---@param limbName string
---@return number
function ModDataHandler:getWoundDirtyness(limbName)
return self.tocData.limbs[limbName].woundDirtyness
end
---Get cicatrizationTime
---@param limbName string
---@return number
function ModDataHandler:getCicatrizationTime(limbName)
return self.tocData.limbs[limbName].cicatrizationTime
end
---Get isProstEquipped
---@param group string
---@return boolean
function ModDataHandler:getIsProstEquipped(group)
return self.tocData.prostheses[group].isProstEquipped
end
---Get prostFactor
---@param group string
---@return number
function ModDataHandler:getProstFactor(group)
return self.tocData.prostheses[group].prostFactor
end
--* Limbs data handling *--
---Set a limb and its dependend limbs as cut
---@param limbName string
---@param isOperated boolean
---@param isCicatrized boolean
---@param isCauterized boolean
---@param surgeonFactor number?
function ModDataHandler:setCutLimb(limbName, isOperated, isCicatrized, isCauterized, surgeonFactor)
local cicatrizationTime = 0
if isCicatrized == false or isCauterized == false then
cicatrizationTime = StaticData.LIMBS_CICATRIZATION_TIME_IND_NUM[limbName] - surgeonFactor
end
-- TODO Set WoundDirtyness here! For now it's just 0
---@type partData
local params = {isCut = true, isInfected = false, isOperated = isOperated, isCicatrized = isCicatrized, isCauterized = isCauterized, woundDirtyness = 0, isVisible = true}
self:setLimbParams(limbName, params, cicatrizationTime)
for i=1, #StaticData.LIMBS_DEPENDENCIES_IND_STR[limbName] do
local dependedLimbName = StaticData.LIMBS_DEPENDENCIES_IND_STR[limbName][i]
-- We don't care about isOperated, isCicatrized, isCauterized since this is depending on another limb
-- Same story for cicatrizationTime, which will be 0
-- isCicatrized is to true to prevent it from doing the cicatrization process
self:setLimbParams(dependedLimbName, {isCut = true, isInfected = false, isVisible = false, isCicatrized = true}, 0)
end
-- Set that a limb has been cut, to activate some functions without having to loop through the parts
self:setIsAnyLimbCut(true)
-- TODO In theory we should cache data from here, not AmputationHandler
end
---Set a limb data
---@param limbName string
---@param ampStatus partData {isCut, isInfected, isOperated, isCicatrized, isCauterized, isVisible}
---@param cicatrizationTime integer?
function ModDataHandler:setLimbParams(limbName, ampStatus, cicatrizationTime)
local limbData = self.tocData.limbs[limbName]
if ampStatus.isCut ~= nil then limbData.isCut = ampStatus.isCut end
if ampStatus.isInfected ~= nil then limbData.isInfected = ampStatus.isInfected end
if ampStatus.isOperated ~= nil then limbData.isOperated = ampStatus.isOperated end
if ampStatus.isCicatrized ~= nil then limbData.isCicatrized = ampStatus.isCicatrized end
if ampStatus.isCauterized ~= nil then limbData.isCauterized = ampStatus.isCauterized end
if ampStatus.woundDirtyness ~= nil then limbData.woundDirtyness = ampStatus.woundDirtyness end
if ampStatus.isVisible ~= nil then limbData.isVisible = ampStatus.isVisible end
if cicatrizationTime ~= nil then limbData.cicatrizationTime = cicatrizationTime end
end
--* Update statuses of a limb *--
---Decreases the cicatrization time
---@param limbName string
function ModDataHandler:decreaseCicatrizationTime(limbName)
self.tocData.limbs[limbName].cicatrizationTime = self.tocData.limbs[limbName].cicatrizationTime - 1
end
--* Global Mod Data Handling *--
function ModDataHandler:apply()
ModData.transmit(CommandsData.GetKey(self.username))
end
function ModDataHandler.ReceiveData(key, data)
if not isClient() then
TOC_DEBUG.print("SP, skipping ModDataHandler.ReceiveData")
end
-- During startup the game can return Bob as the player username, adding a useless ModData table
if key == "TOC_Bob" then return end
TOC_DEBUG.print("[ModDataHandler] ReceiveData for " .. key)
if data == {} or data == nil then
TOC_DEBUG.print("table is nil... returning")
return
end
-- Get ModDataHandler instance if there was none for that user and reapply the correct ModData table as a reference
local username = key:sub(5)
local handler = ModDataHandler.GetInstance(username)
if handler.isResetForced or data == nil or data == {} or data == false then
TOC_DEBUG.print("[ModDataHandler] Setup")
handler:setup(key)
handler.isResetForced = false
else
TOC_DEBUG.print("[ModDataHandler] Reapply")
handler:reapplyTocData(data)
end
-- if handler.isResetForced or handler.tocData == nil or handler.tocData.limbs == nil or handler.tocData.limbs.Hand_L == nil or handler.tocData.limbs.Hand_L.isCut == nil then
-- TOC_DEBUG.print("tocData in ModDataHandler for " .. handler.username .. " is nil, creating it now")
-- handler:setup(key)
-- handler.isResetForced = false
-- elseif table then
-- TOC_DEBUG.print("Reapply toc data for " .. handler.username)
-- handler:reapplyTocData(table)
-- end
handler:setIsDataReady(true)
-- Event
triggerEvent("OnReceivedTocData", handler.username)
-- Transmit it to the server
ModData.transmit(key)
TOC_DEBUG.print("[ModDataHandler] Transmitting data after receiving it for: " .. handler.username)
end
Events.OnReceiveGlobalModData.Add(ModDataHandler.ReceiveData)
-------------------
---@param username string?
---@return ModDataHandler
function ModDataHandler.GetInstance(username)
if username == nil or username == "Bob" then
username = getPlayer():getUsername()
end
if ModDataHandler.instances[username] == nil then
TOC_DEBUG.print("[ModDataHandler] Creating NEW instance for " .. username)
return ModDataHandler:new(username)
else
return ModDataHandler.instances[username]
end
end
return ModDataHandler

View File

@@ -1,534 +0,0 @@
local ModDataHandler = require("TOC/Handlers/ModDataHandler")
local CommonMethods = require("TOC/CommonMethods")
local CachedDataHandler = require("TOC/Handlers/CachedDataHandler")
local StaticData = require("TOC/StaticData")
-----------
-- THIS SHOULD BE LOCAL ONLY! WE'RE MANAGING EVENTS AND INITIALIZATION STUFF!
-- LIST OF STUFF THAT THIS CLASS NEEDS TO DO
-- Keep track of cut limbs so that we don't have to loop through all of them all the time
-- Update current player status (infection checks)
-- handle stats increase\decrease
---@class PlayerHandler
---@field playerObj IsoPlayer
---@field username string
---@field hasBeenDamaged boolean
local PlayerHandler = {}
---Setup the Player Handler and modData, only for local client
---@param playerObj IsoPlayer
---@param isForced boolean?
function PlayerHandler.InitializePlayer(playerObj, isForced)
local username = playerObj:getUsername()
TOC_DEBUG.print("[PlayerHandler] Initializing local player: " .. username)
ModDataHandler:new(username, isForced)
PlayerHandler.playerObj = playerObj
PlayerHandler.username = username
-- Calculate amputated limbs and highest point of amputations at startup
--CachedDataHandler.CalculateAmputatedLimbs(username)
--CachedDataHandler.CalculateHighestAmputatedLimbs(username)
--Setup the CicatrizationUpdate event and triggers it once
Events.OnAmputatedLimb.Add(PlayerHandler.ToggleUpdateAmputations)
PlayerHandler.ToggleUpdateAmputations()
-- Since isForced is used to reset an existing player data, we're gonna clean their ISHealthPanel table too
if isForced then
local ItemsHandler = require("TOC/Handlers/ItemsHandler")
ItemsHandler.Player.DeleteAllOldAmputationItems(playerObj)
CachedDataHandler.Reset(username)
end
-- Set a bool to use an overriding GetDamagedParts
SetHealthPanelTOC()
end
---Handles the traits
---@param playerObj IsoPlayer
function PlayerHandler.ManageTraits(playerObj)
local AmputationHandler = require("Handlers/TOC_AmputationHandler")
for k, v in pairs(StaticData.TRAITS_BP) do
if playerObj:HasTrait(k) then
-- Once we find one, we should be done.
local tempHandler = AmputationHandler:new(v)
tempHandler:execute(false) -- No damage
tempHandler:close()
return
end
end
end
--* Health management *--
---Used to heal an area that has been cut previously. There's an exception for bites, those are managed differently
---@param bodyPart BodyPart
function PlayerHandler.HealArea(bodyPart)
bodyPart:setBleeding(false)
bodyPart:setBleedingTime(0)
bodyPart:SetBitten(false)
bodyPart:setBiteTime(0)
bodyPart:setCut(false)
bodyPart:setCutTime(0)
bodyPart:setDeepWounded(false)
bodyPart:setDeepWoundTime(0)
bodyPart:setHaveBullet(false, 0)
bodyPart:setHaveGlass(false)
bodyPart:setSplint(false, 0)
end
---comment
---@param bodyDamage BodyDamage
---@param bodyPart BodyPart
---@param limbName string
---@param modDataHandler ModDataHandler
function PlayerHandler.HealZombieInfection(bodyDamage, bodyPart, limbName, modDataHandler)
if bodyDamage:isInfected() == false then return end
bodyDamage:setInfected(false)
bodyDamage:setInfectionMortalityDuration(-1)
bodyDamage:setInfectionTime(-1)
bodyDamage:setInfectionLevel(-1)
bodyPart:SetInfected(false)
modDataHandler:setIsInfected(limbName, false)
modDataHandler:apply()
end
---comment
---@param character IsoPlayer
---@param limbName string
function PlayerHandler.TryRandomBleed(character, limbName)
-- Chance should be determined by the cicatrization time
local cicTime = ModDataHandler.GetInstance():getCicatrizationTime(limbName)
if cicTime == 0 then return end
-- TODO Sometimes we get cicTime = 0... Shouldn't really do it
-- TODO This is just a placeholder, we need to figure out a better way to calculate this chance
local normCicTime = CommonMethods.Normalize(cicTime, 0, StaticData.LIMBS_CICATRIZATION_TIME_IND_NUM[limbName])/2
TOC_DEBUG.print("OG cicTime: " .. tostring(cicTime))
TOC_DEBUG.print("Normalized cic time : " .. tostring(normCicTime))
local chance = ZombRandFloat(0.0, 1.0)
if chance > normCicTime then
TOC_DEBUG.print("Triggered bleeding from non cicatrized wound")
local adjacentBodyPartType = BodyPartType[StaticData.LIMBS_ADJACENT_IND_STR[limbName]]
character:getBodyDamage():getBodyPart(adjacentBodyPartType):setBleeding(true)
character:getBodyDamage():getBodyPart(adjacentBodyPartType):setBleedingTime(20)
end
end
-------------------------
--* Events *--
--- Locks OnPlayerGetDamage event, to prevent it from getting spammed constantly
PlayerHandler.hasBeenDamaged = false
---Check if the player has in infected body part or if they have been hit in a cut area
---@param character IsoPlayer
function PlayerHandler.HandleDamage(character)
-- TOC_DEBUG.print("Player got hit!")
-- TOC_DEBUG.print(damageType)
if character ~= getPlayer() then return end
local bd = character:getBodyDamage()
local modDataHandler = ModDataHandler.GetInstance()
local modDataNeedsUpdate = false
for i=1, #StaticData.LIMBS_STR do
local limbName = StaticData.LIMBS_STR[i]
local bptEnum = StaticData.BODYLOCS_IND_BPT[limbName]
local bodyPart = bd:getBodyPart(bptEnum)
if modDataHandler:getIsCut(limbName) then
-- Generic injury, let's heal it since they already cut the limb off
if bodyPart:HasInjury() then
PlayerHandler.HealArea(bodyPart)
end
-- Special case for bites\zombie infections
if bodyPart:IsInfected() then
TOC_DEBUG.print("Healed from zombie infection " .. tostring(bodyPart))
PlayerHandler.HealZombieInfection(bd, bodyPart, limbName, modDataHandler)
end
else
if bodyPart:bitten() or bodyPart:IsInfected() then
modDataHandler:setIsInfected(limbName, true)
modDataNeedsUpdate = true
end
end
end
-- Check other body parts that are not included in the mod, if there's a bite there then the player is fucked
-- We can skip this loop if the player has been infected. The one before we kinda need it to handle correctly the bites in case the player wanna cut stuff off anyway
if modDataHandler:getIsIgnoredPartInfected() then return end
for i=1, #StaticData.IGNORED_BODYLOCS_BPT do
local bodyPartType = StaticData.IGNORED_BODYLOCS_BPT[i]
local bodyPart = bd:getBodyPart(bodyPartType)
if bodyPart and (bodyPart:bitten() or bodyPart:IsInfected()) then
modDataHandler:setIsIgnoredPartInfected(true)
modDataNeedsUpdate = true
end
end
-- TODO in theory should sync modData, but it's gonna be expensive as fuck. Figure it out
if modDataNeedsUpdate then
modDataHandler:apply()
end
-- Disable the lock
PlayerHandler.hasBeenDamaged = false
end
---Setup HandleDamage, triggered by OnPlayerGetDamage
---@param character IsoGameCharacter
---@param damageType string
---@param damageAmount number
function PlayerHandler.OnGetDamage(character, damageType, damageAmount)
-- TODO Check if other players in the online triggers this
if PlayerHandler.hasBeenDamaged == false then
-- Start checks
-- TODO Add a timer before we can re-enable this bool?
PlayerHandler.hasBeenDamaged = true
PlayerHandler.HandleDamage(character)
end
end
Events.OnPlayerGetDamage.Add(PlayerHandler.OnGetDamage)
---Updates the cicatrization process, run when a limb has been cut. Run it every 1 hour
function PlayerHandler.UpdateAmputations()
local modDataHandler = ModDataHandler.GetInstance()
if modDataHandler:getIsAnyLimbCut() == false then
Events.EveryHours.Remove(PlayerHandler.UpdateAmputations)
end
local pl = PlayerHandler.playerObj
local bd = pl:getBodyDamage()
local visual = pl:getHumanVisual()
local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(pl:getUsername())
local needsUpdate = false
for k, _ in pairs(amputatedLimbs) do
local limbName = k
local isCicatrized = modDataHandler:getIsCicatrized(limbName)
if not isCicatrized then
needsUpdate = true
local cicTime = modDataHandler:getCicatrizationTime(limbName)
TOC_DEBUG.print("updating cicatrization for " .. tostring(limbName))
-- TODO Check if bandaged, sutured, whatever
-- TODO Clean
-- TODO Check dirtyness of zone and add to it
--local bptEnum = StaticData.BODYLOCS_IND_BPT[limbName]
-- TODO Workaround
local bbptEnum = BloodBodyPartType[limbName]
--local bodyPart = bd:getBodyPart(bptEnum)
local modifier = 0.01 * SandboxVars.TOC.WoundDirtynessMultiplier
--------------
-- TEST SECTION
local dirtynessVis = visual:getDirt(bbptEnum) + visual:getBlood(bbptEnum)
local dirtynessWound = modDataHandler:getWoundDirtyness(limbName) + modifier
--------------
local dirtyness = dirtynessVis + dirtynessWound
if dirtyness > 1 then
dirtyness = 1
end
modDataHandler:setWoundDirtyness(limbName, dirtyness)
TOC_DEBUG.print("dirtyness for this zone: " .. tostring(dirtyness))
cicTime = cicTime - SandboxVars.TOC.CicatrizationSpeed
modDataHandler:setCicatrizationTime(limbName, cicTime)
TOC_DEBUG.print("new cicatrization time: " .. tostring(cicTime))
if cicTime <= 0 then
TOC_DEBUG.print(tostring(limbName) .. " is cicatrized")
modDataHandler:setIsCicatrized(limbName, true)
end
end
end
if needsUpdate then
TOC_DEBUG.print("updating modData from cicatrization loop")
modDataHandler:apply() -- TODO This is gonna be heavy. Not entirely sure
else
TOC_DEBUG.print("Removing UpdateAmputations")
Events.EveryHours.Remove(PlayerHandler.UpdateAmputations) -- We can remove it safely, no cicatrization happening here boys
end
TOC_DEBUG.print("updating cicatrization and wound dirtyness!")
end
---Starts safely the loop to update cicatrzation
function PlayerHandler.ToggleUpdateAmputations()
TOC_DEBUG.print("[PlayerHandler] Activating amputation handling loop (if it wasn't active before)")
CommonMethods.SafeStartEvent("EveryHours", PlayerHandler.UpdateAmputations)
end
--* Helper functions for overrides *--
local function CheckHandFeasibility(limbName)
local modDataHandler = ModDataHandler.GetInstance()
return not modDataHandler:getIsCut(limbName) or modDataHandler:getIsProstEquipped(StaticData.LIMBS_TO_PROST_GROUP_MATCH_IND_STR[limbName])
end
------------------------------------------
--* OVERRIDES *--
--* Time to perform actions overrides *--
local og_ISBaseTimedAction_adjustMaxTime = ISBaseTimedAction.adjustMaxTime
--- Adjust time
---@diagnostic disable-next-line: duplicate-set-field
function ISBaseTimedAction:adjustMaxTime(maxTime)
local time = og_ISBaseTimedAction_adjustMaxTime(self, maxTime)
-- Exceptions handling, if we find that parameter then we just use the original time
local queue = ISTimedActionQueue.getTimedActionQueue(getPlayer())
if queue and queue.current and queue.current.skipTOC then return time end
-- Action is valid, check if we have any cut limb and then modify maxTime
local modDataHandler = ModDataHandler.GetInstance()
if time ~= -1 and modDataHandler and modDataHandler:getIsAnyLimbCut() then
local pl = getPlayer()
local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(pl:getUsername())
for k, _ in pairs(amputatedLimbs) do
local limbName = k
--if modDataHandler:getIsCut(limbName) then
local perk = Perks["Side_" .. CommonMethods.GetSide(limbName)]
local perkLevel = pl:getPerkLevel(perk)
local perkLevelScaled
if perkLevel ~= 0 then perkLevelScaled = perkLevel / 10 else perkLevelScaled = 0 end
time = time * (StaticData.LIMBS_TIME_MULTIPLIER_IND_NUM[limbName] - perkLevelScaled)
--end
end
end
return time
end
--* Random bleeding during cicatrization + Perks leveling override *--
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)
local modDataHandler = ModDataHandler.GetInstance()
if not modDataHandler:getIsAnyLimbCut() then return end
local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(PlayerHandler.username)
for k, _ in pairs(amputatedLimbs) do
local limbName = k
if modDataHandler:getIsCut(limbName) then
local side = CommonMethods.GetSide(limbName)
PlayerHandler.playerObj:getXp():AddXP(Perks["Side_" .. side], 1) -- TODO Make it dynamic
local prostGroup = StaticData.LIMBS_TO_PROST_GROUP_MATCH_IND_STR[limbName]
if not modDataHandler:getIsCicatrized(limbName) and modDataHandler:getIsProstEquipped(prostGroup) then
TOC_DEBUG.print("Trying for bleed, player met the criteria")
-- TODO If we have cut a forearm, it will try to check the hand too, with cicatrization time = 0. We should skip this
PlayerHandler.TryRandomBleed(self.character, limbName)
end
end
end
end
--* Equipping items overrides *--
local primaryHand = StaticData.PARTS_IND_STR.Hand .. "_" .. StaticData.SIDES_IND_STR.R
local secondaryHand = StaticData.PARTS_IND_STR.Hand .. "_" .. StaticData.SIDES_IND_STR.L
local og_ISEquipWeaponAction_isValid = ISEquipWeaponAction.isValid
---Add a condition to check the feasibility of having 2 handed weapons or if both arms are cut off
---@return boolean
---@diagnostic disable-next-line: duplicate-set-field
function ISEquipWeaponAction:isValid()
local isValid = og_ISEquipWeaponAction_isValid(self)
local modDataHandler = ModDataHandler.GetInstance(self.character:getUsername())
if isValid and modDataHandler:getIsAnyLimbCut() then
local isPrimaryHandValid = CheckHandFeasibility(primaryHand)
local isSecondaryHandValid = CheckHandFeasibility(secondaryHand)
--TOC_DEBUG.print("isPrimaryHandValid: " .. tostring(isPrimaryHandValid))
--TOC_DEBUG.print("isSecondaryHandValid: " .. tostring(isSecondaryHandValid))
-- Both hands are cut off, so it's impossible to equip in any way
if not isPrimaryHandValid and not isSecondaryHandValid then
--TOC_DEBUG.print("Both hands invalid")
isValid = false
end
end
-- -- Equip primary and no right hand (with no prost)
-- if self.jobType:contains(equipPrimaryText) and not isPrimaryHandValid then
-- --TOC_DEBUG.print("Equip primary, no right hand, not valid")
-- isValid = false
-- end
-- -- Equip secondary and no left hand (with no prost)
-- if self.jobType:contains(equipSecondaryText) and not isSecondaryHandValid then
-- --TOC_DEBUG.print("Equip secondary, no left hand, not valid")
-- isValid = false
-- end
-- end
--TOC_DEBUG.print("isValid to return -> " .. tostring(isValid))
--print("_________________________________")
return isValid
end
---@class ISEquipWeaponAction
---@field character IsoPlayer
---A recreation of the original method, but with amputations in mind
---@param modDataHandler ModDataHandler
function ISEquipWeaponAction:performWithAmputation(modDataHandler)
-- TODO Simplify this
local hand = nil
local otherHand = nil
local getMethodFirst = nil
local setMethodFirst = nil
local getMethodSecond = nil
local setMethodSecond = nil
if self.primary then
hand = StaticData.LIMBS_IND_STR.Hand_R
otherHand = StaticData.LIMBS_IND_STR.Hand_L
getMethodFirst = self.character.getSecondaryHandItem
setMethodFirst = self.character.setSecondaryHandItem
getMethodSecond = self.character.getPrimaryHandItem
setMethodSecond = self.character.setPrimaryHandItem
else
hand = StaticData.LIMBS_IND_STR.Hand_L
otherHand = StaticData.LIMBS_IND_STR.Hand_R
getMethodFirst = self.character.getPrimaryHandItem
setMethodFirst = self.character.setPrimaryHandItem
getMethodSecond = self.character.getSecondaryHandItem
setMethodSecond = self.character.setSecondaryHandItem
end
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
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
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)
end
else
setMethodSecond(self.character, nil)
-- TODO We should use the CachedData indexable instead of modDataHandler
if not modDataHandler:getIsCut(hand) then
setMethodSecond(self.character, self.item)
-- Check other HAND!
elseif not modDataHandler:getIsCut(otherHand) then
setMethodFirst(self.character, self.item)
end
end
else
setMethodFirst(self.character, nil)
setMethodSecond(self.character, nil)
local isFirstValid = CheckHandFeasibility(hand)
local isSecondValid = CheckHandFeasibility(otherHand)
-- TOC_DEBUG.print("First Hand: " .. tostring(hand))
-- TOC_DEBUG.print("Prost Group: " .. tostring(prostGroup))
-- TOC_DEBUG.print("Other Hand: " .. tostring(otherHand))
-- TOC_DEBUG.print("Other Prost Group: " .. tostring(otherProstGroup))
-- TOC_DEBUG.print("isPrimaryHandValid: " .. tostring(isFirstValid))
-- TOC_DEBUG.print("isSecondaryHandValid: " .. tostring(isSecondValid))
if isFirstValid then
setMethodSecond(self.character, self.item)
end
if isSecondValid then
setMethodFirst(self.character, self.item)
end
end
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?
local modDataHandler = ModDataHandler.GetInstance(self.character:getUsername())
-- Just check it any limb has been cut. If not, we can just return from here
if modDataHandler:getIsAnyLimbCut() == true then
self:performWithAmputation(modDataHandler)
end
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
-- 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)
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
local add = true
if playerObj:getPrimaryHandItem() == isWeapon and isWeapon:getScriptItem():getReplaceWhenUnequip() then
add = false
end
if add then
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
return PlayerHandler

View File

@@ -1,6 +1,6 @@
local CommonMethods = require("TOC/CommonMethods")
local StaticData = require("TOC/StaticData")
local ModDataHandler = require("TOC/Handlers/ModDataHandler")
local DataController = require("TOC/Controllers/DataController")
local CachedDataHandler = require("TOC/Handlers/CachedDataHandler")
-------------------------
@@ -67,9 +67,9 @@ function ProsthesisHandler.SearchAndSetupProsthesis(item, isEquipping)
local group = ProsthesisHandler.GetGroup(item)
TOC_DEBUG.print("applying prosthesis stuff for " .. group)
local modDataHandler = ModDataHandler.GetInstance()
modDataHandler:setIsProstEquipped(group, isEquipping)
modDataHandler:apply()
local dcInst = DataController.GetInstance()
dcInst:setIsProstEquipped(group, isEquipping)
dcInst:apply()
end

View File

@@ -1,4 +1,4 @@
local ModDataHandler = require("TOC/Handlers/ModDataHandler")
local DataController = require("TOC/Controllers/DataController")
---@class SurgeryHandler
---@field type string
@@ -28,7 +28,7 @@ function SurgeryHandler:execute()
if self.type == "oven" then
ModDataHandler.GetInstance():setIsCauterized(self.limbName, true)
DataController.GetInstance():setIsCauterized(self.limbName, true)
end
end