Files
The-Only-Cure/media/lua/client/TOC/Controllers/LocalPlayerController.lua
2024-03-29 02:15:42 +01:00

334 lines
11 KiB
Lua

local DataController = require("TOC/Controllers/DataController")
local CommonMethods = require("TOC/CommonMethods")
local CachedDataHandler = require("TOC/Handlers/CachedDataHandler")
local StaticData = require("TOC/StaticData")
require("TOC/Events")
-----------
-- Handle ONLY stuff for the local client
---@class LocalPlayerController
---@field playerObj IsoPlayer
---@field username string
---@field hasBeenDamaged boolean
local LocalPlayerController = {}
--* Initialization
---Setup the Player Handler and modData, only for local client
---@param isForced boolean?
function LocalPlayerController.InitializePlayer(isForced)
local playerObj = getPlayer()
local username = playerObj:getUsername()
TOC_DEBUG.print("Initializing local player: " .. username)
DataController:new(username, isForced)
LocalPlayerController.playerObj = playerObj
LocalPlayerController.username = username
--Setup the CicatrizationUpdate event and triggers it once
Events.OnAmputatedLimb.Add(LocalPlayerController.ToggleUpdateAmputations)
LocalPlayerController.ToggleUpdateAmputations()
-- Since isForced is used to reset an existing player data, we're gonna clean their ISHealthPanel table too
if isForced then
local ItemsController = require("TOC/Controllers/ItemsController")
ItemsController.Player.DeleteAllOldAmputationItems(playerObj)
CachedDataHandler.Setup(username)
end
-- Set a bool to use an overriding GetDamagedParts
SetHealthPanelTOC()
end
---Handles the traits
---@param playerObj IsoPlayer
function LocalPlayerController.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 *--
---Used to heal an area that has been cut previously. There's an exception for bites, those are managed differently
---@param bodyPart BodyPart
function LocalPlayerController.HealArea(bodyPart)
bodyPart:setFractureTime(0)
bodyPart:setScratched(false, true)
bodyPart:setScratchTime(0)
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
---@param bodyDamage BodyDamage
---@param bodyPart BodyPart
---@param limbName string
---@param dcInst DataController
function LocalPlayerController.HealZombieInfection(bodyDamage, bodyPart, limbName, dcInst)
if bodyDamage:isInfected() == false then return end
bodyDamage:setInfected(false)
bodyDamage:setInfectionMortalityDuration(-1)
bodyDamage:setInfectionTime(-1)
bodyDamage:setInfectionLevel(-1)
bodyPart:SetInfected(false)
dcInst:setIsInfected(limbName, false)
dcInst:apply()
end
---@param character IsoPlayer
---@param limbName string
function LocalPlayerController.TryRandomBleed(character, limbName)
-- Chance should be determined by the cicatrization time
local cicTime = DataController.GetInstance():getCicatrizationTime(limbName)
if cicTime == 0 then return end
-- 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
-------------------------
--* Damage handling *--
--- Locks OnPlayerGetDamage event, to prevent it from getting spammed constantly
LocalPlayerController.hasBeenDamaged = false
---Check if the player has in infected body part or if they have been hit in a cut area
---@param character IsoPlayer|IsoGameCharacter
function LocalPlayerController.HandleDamage(character)
-- TOC_DEBUG.print("Player got hit!")
-- TOC_DEBUG.print(damageType)
if character ~= getPlayer() then return end
local bd = character:getBodyDamage()
local dcInst = DataController.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 dcInst:getIsCut(limbName) then
-- Generic injury, let's heal it since they already cut the limb off
if bodyPart:HasInjury() then
TOC_DEBUG.print("Healing area - " .. limbName)
LocalPlayerController.HealArea(bodyPart)
end
-- Special case for bites\zombie infections
if bodyPart:IsInfected() then
TOC_DEBUG.print("Healed from zombie infection - " .. limbName)
LocalPlayerController.HealZombieInfection(bd, bodyPart, limbName, dcInst)
end
else
if bodyPart:bitten() or bodyPart:IsInfected() then
dcInst: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 dcInst: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
dcInst: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
dcInst:apply()
end
-- Disable the lock
LocalPlayerController.hasBeenDamaged = false
end
---Setup HandleDamage, triggered by OnPlayerGetDamage
---@param character IsoPlayer|IsoGameCharacter
---@param damageType string
---@param damageAmount number
function LocalPlayerController.OnGetDamage(character, damageType, damageAmount)
-- TODO Check if other players in the online triggers this
if LocalPlayerController.hasBeenDamaged == false then
-- Start checks
-- TODO Add a timer before we can re-enable this bool?
LocalPlayerController.hasBeenDamaged = true
LocalPlayerController.HandleDamage(character)
end
end
Events.OnPlayerGetDamage.Add(LocalPlayerController.OnGetDamage)
--* Amputation Loop handling *--
---Updates the cicatrization process, run when a limb has been cut. Run it every 1 hour
function LocalPlayerController.UpdateAmputations()
local dcInst = DataController.GetInstance()
if not dcInst:getIsDataReady() then
TOC_DEBUG.print("Data not ready for UpdateAmputations, waiting next loop")
return
end
if not dcInst:getIsAnyLimbCut() then
Events.EveryHours.Remove(LocalPlayerController.UpdateAmputations)
end
local pl = LocalPlayerController.playerObj
local visual = pl:getHumanVisual()
local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(pl:getUsername())
local needsUpdate = false
for k, _ in pairs(amputatedLimbs) do
local limbName = k
local isCicatrized = dcInst:getIsCicatrized(limbName)
if not isCicatrized then
needsUpdate = true
local cicTime = dcInst:getCicatrizationTime(limbName)
TOC_DEBUG.print("Updating cicatrization for " .. tostring(limbName))
--* Dirtyness of the wound
-- We need to get the BloodBodyPartType to find out how dirty the zone is
local bbptEnum = BloodBodyPartType[limbName]
local modifier = 0.01 * SandboxVars.TOC.WoundDirtynessMultiplier
local dirtynessVis = visual:getDirt(bbptEnum) + visual:getBlood(bbptEnum)
local dirtynessWound = dcInst:getWoundDirtyness(limbName) + modifier
local dirtyness = dirtynessVis + dirtynessWound
if dirtyness > 1 then
dirtyness = 1
end
dcInst:setWoundDirtyness(limbName, dirtyness)
TOC_DEBUG.print("Dirtyness for this zone: " .. tostring(dirtyness))
--* Cicatrization
local cicDec = SandboxVars.TOC.CicatrizationSpeed - dirtyness
if cicDec <= 0 then cicDec = 0.1 end
cicTime = cicTime - cicDec
dcInst:setCicatrizationTime(limbName, cicTime)
TOC_DEBUG.print("New cicatrization time: " .. tostring(cicTime))
if cicTime <= 0 then
TOC_DEBUG.print(tostring(limbName) .. " is cicatrized")
dcInst:setIsCicatrized(limbName, true)
end
end
end
if needsUpdate then
TOC_DEBUG.print("updating modData from cicatrization loop")
dcInst:apply() -- TODO This is gonna be heavy. Not entirely sure
else
TOC_DEBUG.print("Removing UpdateAmputations")
Events.EveryHours.Remove(LocalPlayerController.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 LocalPlayerController.ToggleUpdateAmputations()
TOC_DEBUG.print("Activating amputation handling loop (if it wasn't active before)")
CommonMethods.SafeStartEvent("EveryHours", LocalPlayerController.UpdateAmputations)
end
--* Tourniquet handling
function LocalPlayerController.HandleTourniquet()
-- TODO Add it!!!
-- TODO Triggered at startup or when Clothing Updates.
-- local playerObj = getPlayer()
-- for bl, ampGroup in pairs(StaticData.TOURNIQUET_BODYLOCS_TO_GROUPS_IND_STR) do
-- local item = playerObj:getWornItem(bl)
-- if item then
-- -- TODO Set to DataControlle
-- local cicTime = DataController.GetInstance():setTourniquet
-- end
-- end
-- for i=1, #tourniquetBodyLocations do
-- local bl = tourniquetBodyLocations[i]
-- if item and item:getFullType():contains("Surg_Arm_Tourniquet_")
-- end
-- local item = playerObj:getWornItem(bodyLocation)
-- local wornItems = playerObj:getWornItems()
-- for j=1,wornItems:size() do
-- local wornItem = wornItems:get(j-1)
-- end
end
Events.OnPuttingTourniquet.Add(LocalPlayerController.HandleTourniquet)
return LocalPlayerController