Reorganizing

This commit is contained in:
ZioPao
2023-11-11 17:53:04 +01:00
parent a754ae70c4
commit 35ead001b0
18 changed files with 33 additions and 27 deletions

View File

@@ -0,0 +1,104 @@
local ModDataHandler = require("TOC/Handlers/ModDataHandler")
local ItemsHandler = require("TOC/Handlers/ItemsHandler")
local PlayerHandler = require("TOC/Handlers/PlayerHandler")
local StaticData = require("TOC/StaticData")
---------------------------
-- TODO Add Bandages, Torniquet, etc.
--- This will be run EXCLUSIVELY on the client which is getting the amputation
---@class AmputationHandler
---@field patient IsoPlayer
---@field limbName string
---@field bodyPartType BodyPartType
---@field surgeonPl IsoPlayer?
local AmputationHandler = {}
---@param limbName string
---@param surgeonPl IsoPlayer?
---@return AmputationHandler
function AmputationHandler:new(limbName, surgeonPl)
local o = {}
setmetatable(o, self)
self.__index = self
o.patient = getPlayer()
o.limbName = limbName
o.bodyPartType = BodyPartType[self.limbName]
if surgeonPl then
o.surgeonPl = surgeonPl
else
o.surgeonPl = o.patient
end
AmputationHandler.instance = o
return o
end
--* Main methods *--
---Starts bleeding from the point where the saw is being used
function AmputationHandler:damageDuringAmputation()
print("TOC: Damage patient")
local bodyDamage = self.patient:getBodyDamage()
local bodyDamagePart = bodyDamage:getBodyPart(self.bodyPartType)
bodyDamagePart:setBleeding(true)
bodyDamagePart:setCut(true)
bodyDamagePart:setBleedingTime(ZombRand(10, 20))
end
---Execute the amputation
---@param damagePlayer boolean?
function AmputationHandler:execute(damagePlayer)
-- TODO Calculate surgeonStats
-- TODO Cap it to a certain amount, it shouldn't be more than ...?
local surgeonFactor = 1
if damagePlayer == nil then damagePlayer = true end -- Default at true
if damagePlayer then
local patientStats = self.patient:getStats()
local bd = self.patient:getBodyDamage()
local bodyPart = bd:getBodyPart(self.bodyPartType)
local baseDamage = StaticData.LIMBS_BASE_DAMAGE[self.limbName]
-- Set the bleeding and all the damage stuff in that part
bodyPart:AddDamage(baseDamage - surgeonFactor)
bodyPart:setAdditionalPain(baseDamage - surgeonFactor)
bodyPart:setBleeding(true)
bodyPart:setBleedingTime(baseDamage - surgeonFactor)
bodyPart:setDeepWounded(true)
bodyPart:setDeepWoundTime(baseDamage - surgeonFactor)
patientStats:setEndurance(surgeonFactor)
patientStats:setStress(baseDamage - surgeonFactor)
end
-- Set the data in modData
ModDataHandler.GetInstance():setCutLimb(self.limbName, false, false, false, surgeonFactor)
-- Give the player the correct amputation item
ItemsHandler.DeleteOldAmputationItem(self.patient, self.limbName)
ItemsHandler.SpawnAmputationItem(self.patient, self.limbName)
-- Add it to the list of cut limbs
PlayerHandler.AddLocalAmputatedLimb(self.limbName)
-- Set the highest amputation and caches them.
ISHealthPanel.GetHighestAmputation()
end
---Deletes the instance
function AmputationHandler:close()
AmputationHandler.instance = nil
end
--* Events *--
function AmputationHandler.UpdateCicatrization()
if ModDataHandler.GetInstance():getIsAnyLimbCut() == false then return end
end
return AmputationHandler

View File

@@ -0,0 +1,117 @@
local StaticData = require("TOC/StaticData")
local CommonMethods = require("TOC/CommonMethods")
---------------------------
--- Submodule to handle spawning the correct items after certain actions (ie: cutting a hand)
---@class ItemsHandler
local ItemsHandler = {}
---Returns the correct index for the textures of the amputation
---@param isCicatrized boolean
---@return number
---@private
function ItemsHandler.GetAmputationTexturesIndex(playerObj, isCicatrized)
local textureString = playerObj:getHumanVisual():getSkinTexture()
local isHairy = string.find(textureString, "a$")
-- Hairy bodies
if isHairy then
textureString = textureString:sub(1, -2) -- Removes b at the end to make it compatible
end
local matchedIndex = string.match(textureString, "%d$")
-- TODO Rework this
if isHairy then
matchedIndex = matchedIndex + 5
end
if isCicatrized then
if isHairy then
matchedIndex = matchedIndex + 5 -- to use the cicatrized texture on hairy bodies
else
matchedIndex = matchedIndex + 10 -- cicatrized texture only, no hairs
end
end
return matchedIndex - 1
end
---Main function to delete a clothing item
---@param playerObj IsoPlayer
---@param clothingItem InventoryItem?
---@return boolean
---@private
function ItemsHandler.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
print("TOC: 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.DeleteOldAmputationItem(playerObj, limbName)
local side = CommonMethods.GetSide(limbName)
for partName, _ in pairs(StaticData.PARTS_STRINGS) 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.RemoveClothingItem(playerObj, othClothingItem) then return end
end
end
---Deletes all the old amputation items, used for resets
---@param playerObj IsoPlayer
function ItemsHandler.DeleteAllOldAmputationItems(playerObj)
for i=1, #StaticData.LIMBS_STRINGS do
local limbName = StaticData.LIMBS_STRINGS[i]
local clothItemName = StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName
local clothItem = playerObj:getInventory():FindAndReturn(clothItemName)
---@cast clothItem InventoryItem
ItemsHandler.RemoveClothingItem(playerObj, clothItem)
end
end
---Spawns and equips the correct amputation item to the player.
function ItemsHandler.SpawnAmputationItem(playerObj, limbName)
print("Clothing name " .. StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName)
local clothingItem = playerObj:getInventory():AddItem(StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName)
local texId = ItemsHandler.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
--------------------------
--* Overrides *--
local og_ISInventoryPane_refreshContainer = ISInventoryPane.refreshContainer
---Get the list of items for the container and remove the reference to the amputation items
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
--print("TOC: current item is an amputation, removing it from the list")
table.remove(self.itemslist, i)
end
end
end
return ItemsHandler

View File

@@ -0,0 +1,179 @@
local StaticData = require("TOC/StaticData")
----------------
---@alias partData { isCut : boolean?, isInfected : boolean?, isOperated : boolean?, isCicatrized : boolean?, isCauterized : boolean?, isVisible : boolean?, cicatrizationTime : number }
---@alias tocModData {Hand_L : partData, ForeArm_L : partData, UpperArm_L : partData, Hand_R : partData, ForeArm_R : partData, UpperArm_R : partData, isIgnoredPartInfected : boolean, isAnyLimbCut : boolean}
----------------
-- TODO This class should handle all the stuff related to the mod data
---@class ModDataHandler
---@field playerObj IsoPlayer
---@field tocData tocModData
local ModDataHandler = {}
---@param playerObj IsoPlayer
---@return ModDataHandler
function ModDataHandler:new(playerObj)
local o = {}
setmetatable(o, self)
self.__index = self
o.playerObj = playerObj
o.tocData = playerObj:getModData()[StaticData.MOD_NAME]
ModDataHandler.instance = o
return o
end
---Setup a newly instanced ModDataHandler
---@param force boolean?
function ModDataHandler:setup(force)
local tocData = self.playerObj:getModData()[StaticData.MOD_NAME]
if force or tocData == nil or tocData.Hand_L == nil or tocData.Hand_L.isCut == nil then
self:createData()
end
-- TODO Check compatibility or do we just skip it at this point?
end
function ModDataHandler:createData()
print("TOC: createData")
local modData = self.playerObj:getModData()
modData[StaticData.MOD_NAME] = {
-- Generic stuff that does not belong anywhere else
isIgnoredPartInfected = false,
isAnyLimbCut = false
}
-- Set a reference to TOC data in ModData
self.tocData = self.playerObj:getModData()[StaticData.MOD_NAME]
---@type partData
local defaultParams = {isCut = false, isInfected = false, isOperated = false, isCicatrized = false, isCauterized = false, isVisible = false}
-- Initialize limbs
for i=1, #StaticData.LIMBS_STRINGS do
local limbName = StaticData.LIMBS_STRINGS[i]
modData[StaticData.MOD_NAME][limbName] = {}
self:setLimbParams(StaticData.LIMBS_STRINGS[i], defaultParams, 0)
end
end
-----------------
--* Setters *--
---Set a generic boolean that toggles varies function of the mod
---@param isAnyLimbCut boolean
function ModDataHandler:setIsAnyLimbCut(isAnyLimbCut)
self.tocData.isAnyLimbCut = true
end
---Set isCut
---@param limbName string
---@param isCut boolean
function ModDataHandler:setIsCut(limbName, isCut)
self.tocData[limbName].isCut = isCut
end
---Set isInfected
---@param limbName string
---@param isInfected boolean
function ModDataHandler:setIsInfected(limbName, isInfected)
self.tocData[limbName].isInfected = isInfected
end
---Set isIgnoredPartInfected
---@param isIgnoredPartInfected boolean
function ModDataHandler:setIsIgnoredPartInfected(isIgnoredPartInfected)
self.tocData.isIgnoredPartInfected = isIgnoredPartInfected
end
-----------------
--* Getters *--
---Set a generic boolean that toggles varies function of the mod
---@return boolean
function ModDataHandler:getIsAnyLimbCut()
return self.tocData.isAnyLimbCut
end
---Get isCut
---@param limbName string
---@return boolean
function ModDataHandler:getIsCut(limbName)
return self.tocData[limbName].isCut
end
---Get isIgnoredPartInfected
---@return boolean
function ModDataHandler:getIsIgnoredPartInfected()
return self.tocData.isIgnoredPartInfected
end
---Get isVisible
---@return boolean
function ModDataHandler:getIsVisible(limbName)
return self.tocData[limbName].isVisible
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[limbName] - surgeonFactor
end
---@type partData
local params = {isCut = true, isInfected = false, isOperated = isOperated, isCicatrized = isCicatrized, isCauterized = isCauterized, isVisible = true}
self:setLimbParams(limbName, params, cicatrizationTime)
for i=1, #StaticData.LIMBS_DEPENDENCIES[limbName] do
local dependedLimbName = StaticData.LIMBS_DEPENDENCIES[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
self:setLimbParams(dependedLimbName, {isCut = true, isInfected = false, isVisible = false}, 0)
end
-- Set that a limb has been cut, to activate some functions without having to loop through the parts
self:setIsAnyLimbCut(true)
end
---Internal use only, set a limb data
---@param limbName string
---@param ampStatus partData {isCut, isInfected, isOperated, isCicatrized, isCauterized, isVisible}
---@param cicatrizationTime integer?
---@private
function ModDataHandler:setLimbParams(limbName, ampStatus, cicatrizationTime)
local limbData = self.tocData[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.isVisible ~= nil then limbData.isVisible = ampStatus.isVisible end
if cicatrizationTime ~= nil then limbData.cicatrizationTime = cicatrizationTime end
end
---@return ModDataHandler
function ModDataHandler.GetInstance()
if ModDataHandler.instance ~= nil then
return ModDataHandler.instance
else
return ModDataHandler:new(getPlayer())
end
end
return ModDataHandler

View File

@@ -0,0 +1,154 @@
local ModDataHandler = require("TOC/Handlers/ModDataHandler")
local CommonMethods = require("TOC/CommonMethods")
local StaticData = require("TOC/StaticData")
-----------
-- TODO We should instantiate this anyway if we want to keep track of cut limbs here. Doing so, we would be able to handle other players too
-- 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 modDataHandler ModDataHandler
---@field playerObj IsoPlayer
local PlayerHandler = {}
---Setup player modData
---@param _ nil
---@param playerObj IsoPlayer
---@param isForced boolean?
function PlayerHandler.InitializePlayer(_, playerObj, isForced)
PlayerHandler.modDataHandler = ModDataHandler:new(playerObj) -- TODO This isn't gonna work for MP purposes
PlayerHandler.modDataHandler:setup(isForced)
PlayerHandler.playerObj = playerObj
-- Calculate amputated limbs at startup
PlayerHandler.amputatedLimbs = {}
for i=1, #StaticData.LIMBS_STRINGS do
local limbName = StaticData.LIMBS_STRINGS[i]
if PlayerHandler.modDataHandler:getIsCut(limbName) then
PlayerHandler.AddLocalAmputatedLimb(limbName)
end
end
-- Since isForced is used to reset an existing player data, we're gonna clean their ISHealthPanel table too
if isForced then
ISHealthPanel.highestAmputations = {}
local ItemsHandler = require("TOC/Handlers/ItemsHandler")
ItemsHandler.DeleteAllOldAmputationItems(playerObj)
end
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
---Cache the currently amputated limbs
---@param limbName string
function PlayerHandler.AddLocalAmputatedLimb(limbName)
print("TOC: added " .. limbName .. " to known amputated limbs")
table.insert(PlayerHandler.amputatedLimbs, limbName) -- TODO This should be player specific, not generic
end
--* Getters *--
---Get a table with the strings of the cached amputated limbs
---@return table
function PlayerHandler.GetAmputatedLimbs()
return PlayerHandler.amputatedLimbs or {}
end
--* Events *--
---Check if the player has an infected (as in, zombie infection) body part
---@param character IsoGameCharacter
function PlayerHandler.CheckInfection(character)
-- This fucking event barely works. Bleeding seems to be the only thing that triggers it
if character ~= getPlayer() then return end
local bd = character:getBodyDamage()
for i=1, #StaticData.LIMBS_STRINGS do
local limbName = StaticData.LIMBS_STRINGS[i]
local bptEnum = StaticData.BODYPARTSTYPES_ENUM[limbName]
local bodyPart = bd:getBodyPart(bptEnum)
if bodyPart:bitten() or bodyPart:IsInfected() then
if PlayerHandler.modDataHandler:getIsCut(limbName) then
bodyPart:SetBitten(false)
else
PlayerHandler.modDataHandler:setIsInfected(limbName, 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 PlayerHandler.modDataHandler:getIsIgnoredPartInfected() then return end
for i=1, #StaticData.IGNORED_PARTS_STRINGS do
local bodyPartType = BodyPartType[StaticData.IGNORED_PARTS_STRINGS[i]]
local bodyPart = bd:getBodyPart(bodyPartType)
if bodyPart and (bodyPart:bitten() or bodyPart:IsInfected()) then
PlayerHandler.modDataHandler:setIsIgnoredPartInfected(true)
end
end
end
Events.OnPlayerGetDamage.Add(PlayerHandler.CheckInfection)
--* Overrides *--
local og_ISBaseTimedAction_adjustMaxTime = ISBaseTimedAction.adjustMaxTime
--- Adjust time
function ISBaseTimedAction:adjustMaxTime(maxTime)
local time = og_ISBaseTimedAction_adjustMaxTime(self, maxTime)
local modDataHandler = ModDataHandler.GetInstance()
if time ~= -1 and modDataHandler and modDataHandler:getIsAnyLimbCut() then
local pl = getPlayer()
for i=1, #PlayerHandler.amputatedLimbs do
local limbName = PlayerHandler.amputatedLimbs[i]
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[limbName] - perkLevelScaled)
end
end
end
return time
end
local og_ISBaseTimedAction_perform = ISBaseTimedAction.perform
--- After each action, level up perks
function ISBaseTimedAction:perform()
og_ISBaseTimedAction_perform(self)
if PlayerHandler.modDataHandler:getIsAnyLimbCut() then
for side, _ in pairs(StaticData.SIDES_STRINGS) do
local limbName = "Hand_" .. side
if ModDataHandler.GetInstance():getIsCut(limbName) then
PlayerHandler.playerObj:getXp():AddXP(Perks["Side_" .. side], 2) -- TODO Make it dynamic
end
end
end
end
return PlayerHandler

View File

@@ -0,0 +1,58 @@
local CommonMethods = require("TOC/CommonMethods")
local PlayerHandler = require("TOC/Handlers/PlayerHandler")
-------------------------
---@class ProsthesisHandler
local ProsthesisHandler = {}
---Cache the correct texture for the Health Panel for the currently equipped prosthesis
function ProsthesisHandler.SetHealthPanelTexture()
-- TODO do it
end
---Check if a prosthesis is equippable. It depends whether the player has a cut limb or not on that specific side. There's an exception for Upper arm, obviously
---@param bodyLocation string
---@return boolean
function ProsthesisHandler.CheckIfEquippable(bodyLocation)
print("Current item is a prosthesis")
local side = CommonMethods.GetSide(bodyLocation)
for i=1, #PlayerHandler.amputatedLimbs do
local limbName = PlayerHandler.amputatedLimbs[i]
if string.contains(limbName, side) and not string.contains(limbName, "UpperArm") then
return true
end
end
-- No acceptable cut limbs
getPlayer():Say("I can't equip this")
return false
end
--* Overrides *--
function ISWearClothing:isValid()
local bodyLocation = self.item:getBodyLocation()
if not string.contains(bodyLocation, "TOC_ArmProst") then
return true
else
return ProsthesisHandler.CheckIfEquippable(bodyLocation)
end
end
local og_ISClothingExtraAction_isValid = ISClothingExtraAction.isValid
function ISClothingExtraAction:isValid()
local bodyLocation = self.item:getBodyLocation()
if og_ISClothingExtraAction_isValid(self) and not string.contains(bodyLocation, "TOC_ArmProst") then
return true
else
return ProsthesisHandler.CheckIfEquippable(bodyLocation)
end
end
return ProsthesisHandler