diff --git a/.vscode/settings.json b/.vscode/settings.json index 9d3a1e4..df66050 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "todo-tree.tree.scanMode": "workspace", "zomboid_user_folder": "C:/Users/picch/Zomboid", - "zomboid_folder": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\ProjectZomboid", + "zomboid_folder": "E:\\Steam\\steamapps\\common\\ProjectZomboid", "zomboid_server_folder": "E:\\Steam\\steamapps\\common\\Project Zomboid Dedicated Server", "Lua.diagnostics.globals": [ "ModOptions", diff --git a/media/lua/client/TOC/Handlers/CachedDataHandler.lua b/media/lua/client/TOC/Handlers/CachedDataHandler.lua index 9cf469f..a3ea289 100644 --- a/media/lua/client/TOC/Handlers/CachedDataHandler.lua +++ b/media/lua/client/TOC/Handlers/CachedDataHandler.lua @@ -19,8 +19,11 @@ CachedDataHandler.amputatedLimbs = {} ---Calculate the currently amputated limbs for a certain player ---@param username string function CachedDataHandler.CalculateAmputatedLimbs(username) + TOC_DEBUG.print("[CachedDataHandler] Calculating amputated limbs for " .. username) CachedDataHandler.amputatedLimbs[username] = {} local modDataHandler = ModDataHandler.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 @@ -29,6 +32,8 @@ function CachedDataHandler.CalculateAmputatedLimbs(username) end end + + ---Add an amputated limb to the cached list for that user ---@param username string ---@param limbName string @@ -52,6 +57,13 @@ CachedDataHandler.highestAmputatedLimbs = {} ---Calcualate the highest point of amputations achieved by the player ---@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) + 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 @@ -66,11 +78,6 @@ function CachedDataHandler.CalculateHighestAmputatedLimbs(username) local amputatedLimbs = CachedDataHandler.amputatedLimbs[username] CachedDataHandler.highestAmputatedLimbs[username] = {} --TOC_DEBUG.print("Searching highest amputations for " .. username) - local modDataHandler = ModDataHandler.GetInstance(username) - if modDataHandler == nil then - TOC_DEBUG.print("ModDataHandler not found for " .. username) - return - end for k, _ in pairs(amputatedLimbs) do local limbName = k @@ -82,6 +89,7 @@ function CachedDataHandler.CalculateHighestAmputatedLimbs(username) end end + ---Get the cached highest point of amputation for each side ---@param username string ---@return table diff --git a/media/lua/client/TOC/Handlers/ModDataHandler.lua b/media/lua/client/TOC/Handlers/ModDataHandler.lua index 552df5c..6d6f772 100644 --- a/media/lua/client/TOC/Handlers/ModDataHandler.lua +++ b/media/lua/client/TOC/Handlers/ModDataHandler.lua @@ -5,7 +5,9 @@ local StaticData = require("TOC/StaticData") --- Handle all mod data related stuff ---@class ModDataHandler ---@field username string ----@field tocData tocModData +---@field tocData tocModData +---@field isDataReady boolean +---@field isResetForced boolean local ModDataHandler = {} ModDataHandler.instances = {} @@ -14,6 +16,8 @@ ModDataHandler.instances = {} ---@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 @@ -24,32 +28,23 @@ function ModDataHandler:new(username, isResetForced) -- 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 - -- FIXME This is working like a placeholder at the moment, it's gonna get replaced later in reapplyTocData - o.tocData = ModData.get(key) - if isResetForced or o.tocData == nil or o.tocData.limbs == nil or o.tocData.limbs.Hand_L == nil or o.tocData.limbs.Hand_L.isCut == nil then - TOC_DEBUG.print("tocData in ModDataHandler for " .. username .. " is nil, creating it now") - o:setup(key) - end - - - -- Transmit it to the server - ModData.transmit(key) - TOC_DEBUG.print("initialized ModDataHandler for " .. username) - ModDataHandler.instances[username] = o return o end -function ModDataHandler:initialize() - -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 = { @@ -89,9 +84,9 @@ function ModDataHandler:setup(key) end ---In case of desync between the table on ModData and the table here ----@param key string ---@param tocData tocModData -function ModDataHandler:reapplyTocData(key, tocData) +function ModDataHandler:reapplyTocData(tocData) + local key = CommandsData.GetKey(self.username) ModData.add(key, tocData) self.tocData = ModData.get(key) end @@ -99,6 +94,10 @@ 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) @@ -171,6 +170,11 @@ 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() @@ -187,6 +191,8 @@ end ---@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 @@ -198,6 +204,8 @@ end ---@param limbName string ---@return boolean function ModDataHandler:getIsVisible(limbName) + if not self.isDataReady then return false end + return self.tocData.limbs[limbName].isVisible end @@ -314,12 +322,10 @@ function ModDataHandler.ReceiveData(key, table) if not isClient() then TOC_DEBUG.print("SP, skipping ModDataHandler.ReceiveData") end - TOC_DEBUG.print("receiving data from server") - -- 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("receive data for " .. key) + TOC_DEBUG.print("[ModDataHandler] ReceiveData for " .. key) if table == {} or table == nil then TOC_DEBUG.print("table is nil... returning") return @@ -327,8 +333,39 @@ function ModDataHandler.ReceiveData(key, table) -- Create ModDataHandler instance if there was none for that user and reapply the correct ModData table as a reference local username = key:sub(5) - ModDataHandler.GetInstance(username):reapplyTocData(key, table) + + -- FIXME Triggers an error at startup during initialization + + local handler = ModDataHandler.GetInstance(username) + + if handler.isResetForced then + handler:setup(key) + handler.isResetForced = false + else + handler:reapplyTocData(table) + 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) ------------------- @@ -341,6 +378,7 @@ function ModDataHandler.GetInstance(username) 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] diff --git a/media/lua/client/TOC/Handlers/PlayerHandler.lua b/media/lua/client/TOC/Handlers/PlayerHandler.lua index 23e72ea..019f399 100644 --- a/media/lua/client/TOC/Handlers/PlayerHandler.lua +++ b/media/lua/client/TOC/Handlers/PlayerHandler.lua @@ -22,15 +22,16 @@ local PlayerHandler = {} ---@param isForced boolean? function PlayerHandler.InitializePlayer(playerObj, isForced) local username = playerObj:getUsername() - TOC_DEBUG.print("initializing local player: " .. username) + + 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) + --CachedDataHandler.CalculateAmputatedLimbs(username) + --CachedDataHandler.CalculateHighestAmputatedLimbs(username) --Setup the CicatrizationUpdate event and triggers it once Events.OnAmputatedLimb.Add(PlayerHandler.ToggleUpdateAmputations) @@ -42,6 +43,10 @@ function PlayerHandler.InitializePlayer(playerObj, isForced) ItemsHandler.Player.DeleteAllOldAmputationItems(playerObj) CachedDataHandler.Reset(username) end + + -- Overrides various functions on the Health Panel ONLY after we're done initializing the player + OverrideHealthPanelForTOC() + end ---Handles the traits @@ -235,7 +240,15 @@ function PlayerHandler.UpdateAmputations() --local bodyPart = bd:getBodyPart(bptEnum) local modifier = 0.01 * SandboxVars.TOC.WoundDirtynessMultiplier - local dirtyness = visual:getDirt(bbptEnum) + visual:getBlood(bbptEnum) + modDataHandler:getWoundDirtyness(limbName) + modifier + + -------------- + -- 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 @@ -268,7 +281,7 @@ end ---Starts safely the loop to update cicatrzation function PlayerHandler.ToggleUpdateAmputations() - TOC_DEBUG.print("activating amputation handling loop (if it wasn't active before)") + TOC_DEBUG.print("[PlayerHandler] Activating amputation handling loop (if it wasn't active before)") CommonMethods.SafeStartEvent("EveryHours", PlayerHandler.UpdateAmputations) end diff --git a/media/lua/client/TOC/Main.lua b/media/lua/client/TOC/Main.lua index 7b14334..7732c91 100644 --- a/media/lua/client/TOC/Main.lua +++ b/media/lua/client/TOC/Main.lua @@ -46,13 +46,19 @@ end function Main.SetupEvents() --Triggered when a limb has been amputated LuaEventManager.AddEvent("OnAmputatedLimb") + + -- Triggered when data is ready + LuaEventManager.AddEvent("OnReceivedTocData") + local CachedDataHandler = require("TOC/Handlers/CachedDataHandler") + Events.OnReceivedTocData.Add(CachedDataHandler.CalculateHighestAmputatedLimbs) + end function Main.Initialize() ---Looop until we've successfully initialized the mod local function TryToInitialize() local pl = getPlayer() - TOC_DEBUG.print("Current username in TryToInitialize: " .. pl:getUsername()) + TOC_DEBUG.print("[Main] Current username in TryToInitialize: " .. pl:getUsername()) if pl:getUsername() == "Bob" then TOC_DEBUG.print("Username is still Bob, waiting") return diff --git a/media/lua/client/TOC/UI/HealthPanel.lua b/media/lua/client/TOC/UI/HealthPanel.lua index 42a0a8e..2bb6b62 100644 --- a/media/lua/client/TOC/UI/HealthPanel.lua +++ b/media/lua/client/TOC/UI/HealthPanel.lua @@ -3,249 +3,254 @@ local ModDataHandler = require("TOC/Handlers/ModDataHandler") local CachedDataHandler = require("TOC/Handlers/CachedDataHandler") local CutLimbHandler = require("TOC/UI/CutLimbInteractions") local WoundCleaningHandler = require("TOC/UI/WoundCleaningInteraction") ---------------------------------- - --- We're overriding ISHealthPanel to add custom textures to the body panel. --- By doing so we can show the player which limbs have been cut without having to use another menu --- We can show prosthesis too this way --- We also manage the drag'n drop of items on the body to let the players use the saw this way too ----@diagnostic disable: duplicate-set-field - ---ISHealthBodyPartPanel = ISBodyPartPanel:derive("ISHealthBodyPartPanel") - ---* Handling drag n drop of the saw *-- - -local og_ISHealthPanel_dropItemsOnBodyPart = ISHealthPanel.dropItemsOnBodyPart -function ISHealthPanel:dropItemsOnBodyPart(bodyPart, items) - og_ISHealthPanel_dropItemsOnBodyPart(self, bodyPart, items) - - TOC_DEBUG.print("override to dropItemsOnBodyPart running") - local cutLimbHandler = CutLimbHandler:new(self, bodyPart) - for _,item in ipairs(items) do - cutLimbHandler:checkItem(item) - end - if cutLimbHandler:dropItems(items) then - return - end - -end - -local og_ISHealthPanel_doBodyPartContextMenu = ISHealthPanel.doBodyPartContextMenu -function ISHealthPanel:doBodyPartContextMenu(bodyPart, x, y) - og_ISHealthPanel_doBodyPartContextMenu(self, bodyPart, x, y) - local playerNum = self.otherPlayer and self.otherPlayer:getPlayerNum() or self.character:getPlayerNum() - - -- To not recreate it but reuse the one that has been created in the original method - local context = getPlayerContextMenu(playerNum) - - local cutLimbHandler = CutLimbHandler:new(self, bodyPart) - self:checkItems({cutLimbHandler}) - cutLimbHandler:addToMenu(context) - - local woundCleaningHandler = WoundCleaningHandler:new(self, bodyPart, self.character:getUsername()) - self:checkItems({woundCleaningHandler}) - woundCleaningHandler:addToMenu(context) -end ---* Modifications and additional methods to handle visible amputation on the health menu *-- +function OverrideHealthPanelForTOC() -local og_ISHealthPanel_initialise = ISHealthPanel.initialise -function ISHealthPanel:initialise() - if self.character:isFemale() then - self.sexPl = "Female" - else - self.sexPl = "Male" - end + -- We're overriding ISHealthPanel to add custom textures to the body panel. + -- By doing so we can show the player which limbs have been cut without having to use another menu + -- We can show prosthesis too this way + -- We also manage the drag'n drop of items on the body to let the players use the saw this way too + ---@diagnostic disable: duplicate-set-field - local username = self.character:getUsername() - if username ~= "Bob" then - CachedDataHandler.CalculateHighestAmputatedLimbs(username) - self.highestAmputations = CachedDataHandler.GetHighestAmputatedLimbs(username) - end + --ISHealthBodyPartPanel = ISBodyPartPanel:derive("ISHealthBodyPartPanel") - og_ISHealthPanel_initialise(self) -end + --* Handling drag n drop of the saw *-- -local og_ISHealthPanel_setOtherPlayer = ISHealthPanel.setOtherPlayer ----@param playerObj IsoPlayer -function ISHealthPanel:setOtherPlayer(playerObj) - og_ISHealthPanel_setOtherPlayer(self, playerObj) - --CachedDataHandler.CalculateAmputatedLimbs(self.character:getUsername()) -end - ----Get a value between 1 and 0.1 for the cicatrization time ----@param cicTime integer ----@return integer -local function GetColorFromCicatrizationTime(cicTime, limbName) - local defaultTime = StaticData.LIMBS_CICATRIZATION_TIME_IND_NUM[limbName] - local delta = cicTime/defaultTime - return math.max(0.15, math.min(delta, 1)) -end - ----Try to draw the highest amputation in the health panel, based on the cicatrization time ----@param side string L or R ----@param username string -function ISHealthPanel:tryDrawHighestAmputation(side, username) - local redColor - local texture - - if TOC_DEBUG.enableHealthPanelDebug then - redColor = 1 - texture = getTexture("media/ui/test_pattern.png") - else - if self.highestAmputations[side] == nil then return end - local limbName = self.highestAmputations[side] - - local cicTime = ModDataHandler.GetInstance(username):getCicatrizationTime(limbName) - redColor = GetColorFromCicatrizationTime(cicTime, limbName) - texture = StaticData.HEALTH_PANEL_TEXTURES[self.sexPl][limbName] - end - - self:drawTexture(texture, self.healthPanel.x, self.healthPanel.y, 1, redColor, 0, 0) -end - -local og_ISHealthPanel_render = ISHealthPanel.render -function ISHealthPanel:render() - og_ISHealthPanel_render(self) - local username = self.character:getUsername() - self.highestAmputations = CachedDataHandler.GetHighestAmputatedLimbs(username) - - if self.highestAmputations ~= nil then - -- Left Texture - self:tryDrawHighestAmputation("L", username) - - -- Right Texture - self:tryDrawHighestAmputation("R", username) - else - -- Request caching data - TOC_DEBUG.print("highest amputated limbs was nil, calculating and getting it now for" .. username) - CachedDataHandler.CalculateHighestAmputatedLimbs(username) - end -end - --- We need to override this to force the alpha to 1 -local og_ISCharacterInfoWindow_render = ISCharacterInfoWindow.prerender -function ISCharacterInfoWindow:prerender() - og_ISCharacterInfoWindow_render(self) - self.backgroundColor.a = 1 -end - --- We need to override this to force the alpha to 1 for the Medical Check in particular -local og_ISHealthPanel_prerender = ISHealthPanel.prerender -function ISHealthPanel:prerender() - og_ISHealthPanel_prerender(self) - self.backgroundColor.a = 1 -end - ---- The medical check wrap the health panel into this. We need to override its color -local overrideBackgroundColor = true -local og_ISUIElement_wrapInCollapsableWindow = ISUIElement.wrapInCollapsableWindow ----@param title string ----@param resizable any ----@param subClass any ----@return any -function ISUIElement:wrapInCollapsableWindow(title, resizable, subClass) - local panel = og_ISUIElement_wrapInCollapsableWindow(self, title, resizable, subClass) - - if overrideBackgroundColor then - TOC_DEBUG.print("Overriding color for panel - " .. title) - self.backgroundColor.a = 1 - panel.backgroundColor.a = 1 - end - - return panel -end - --- This is run when a player is trying the Medical Check action on another player -local og_ISMedicalCheckAction_perform = ISMedicalCheckAction.perform -function ISMedicalCheckAction:perform() - local username = self.otherPlayer:getUsername() - TOC_DEBUG.print("creating instance for " .. username ) - ModDataHandler.GetInstance(username) - - -- We need to recalculate them here before we can create the highest amputations point - CachedDataHandler.CalculateAmputatedLimbs(username) - og_ISMedicalCheckAction_perform(self) -end - -local og_ISHealthBodyPartListBox_doDrawItem = ISHealthBodyPartListBox.doDrawItem -function ISHealthBodyPartListBox:doDrawItem(y, item, alt) - y = og_ISHealthBodyPartListBox_doDrawItem(self, y, item, alt) - y = y - 5 - local x = 15 - local fontHgt = getTextManager():getFontHeight(UIFont.Small) - - local username = self.parent.character:getUsername() - --local amputatedLimbs = CachedDataHandler.GetIndexedAmputatedLimbs(username) - - ---@type BodyPart - local bodyPart = item.item.bodyPart - - local bodyPartTypeStr = BodyPartType.ToString(bodyPart:getType()) - local limbName = StaticData.LIMBS_IND_STR[bodyPartTypeStr] - if limbName then - local modDataHandler = ModDataHandler.GetInstance(username) - if modDataHandler:getIsCut(limbName) and modDataHandler:getIsVisible(limbName) then - if modDataHandler:getIsCicatrized(limbName) then - if modDataHandler:getIsCauterized(limbName) then - self:drawText("- " .. getText("IGUI_HealthPanel_Cauterized"), x, y, 0.58, 0.75, 0.28, 1, UIFont.Small) - else - self:drawText("- " .. getText("IGUI_HealthPanel_Cicatrized"), x, y, 0.28, 0.89, 0.28, 1, UIFont.Small) - end - else - local cicaTime = modDataHandler:getCicatrizationTime(limbName) - - -- Show it in percentage - local maxCicaTime = StaticData.LIMBS_CICATRIZATION_TIME_IND_NUM[limbName] - local percentage = (1 - cicaTime/maxCicaTime) * 100 - self:drawText("- " .. getText("IGUI_HealthPanel_Cicatrization") .. string.format(" %.2f", percentage) .. "%", x, y, 0.89, 0.28, 0.28, 1, UIFont.Small) - y = y + fontHgt - - local scaledDirtyness = math.floor(modDataHandler:getWoundDirtyness(limbName) * 100) - self:drawText("- " .. getText("IGUI_HealthPanel_WoundDirtyness") .. string.format(" %d", scaledDirtyness) .. "%", x, y, 0.89, 0.28, 0.28, 1, UIFont.Small) - end - y = y + fontHgt + local og_ISHealthPanel_dropItemsOnBodyPart = ISHealthPanel.dropItemsOnBodyPart + function ISHealthPanel:dropItemsOnBodyPart(bodyPart, items) + og_ISHealthPanel_dropItemsOnBodyPart(self, bodyPart, items) + TOC_DEBUG.print("override to dropItemsOnBodyPart running") + local cutLimbHandler = CutLimbHandler:new(self, bodyPart) + for _,item in ipairs(items) do + cutLimbHandler:checkItem(item) + end + if cutLimbHandler:dropItems(items) then + return end end - y = y + 5 - return y -end + local og_ISHealthPanel_doBodyPartContextMenu = ISHealthPanel.doBodyPartContextMenu + function ISHealthPanel:doBodyPartContextMenu(bodyPart, x, y) + og_ISHealthPanel_doBodyPartContextMenu(self, bodyPart, x, y) + local playerNum = self.otherPlayer and self.otherPlayer:getPlayerNum() or self.character:getPlayerNum() -local og_ISHealthPanel_getDamagedParts = ISHealthPanel.getDamagedParts -function ISHealthPanel:getDamagedParts() + -- To not recreate it but reuse the one that has been created in the original method + local context = getPlayerContextMenu(playerNum) - -- TODO Overriding it is a lot easier, but ew - -- local result = og_ISHealthPanel_getDamagedParts(self) - -- local bodyParts = self:getPatient():getBodyDamage():getBodyParts() - -- if isClient() and not self:getPatient():isLocalPlayer() then - -- bodyParts = self:getPatient():getBodyDamageRemote():getBodyParts() - -- end - -- for i=1,bodyParts:size() do - -- local bodyPart = bodyParts:get(i-1) - -- table.insert(result, bodyPart) + local cutLimbHandler = CutLimbHandler:new(self, bodyPart) + self:checkItems({cutLimbHandler}) + cutLimbHandler:addToMenu(context) - -- end - - - -- return result - local result = {} - local bodyParts = self:getPatient():getBodyDamage():getBodyParts() - if isClient() and not self:getPatient():isLocalPlayer() then - bodyParts = self:getPatient():getBodyDamageRemote():getBodyParts() + local woundCleaningHandler = WoundCleaningHandler:new(self, bodyPart, self.character:getUsername()) + self:checkItems({woundCleaningHandler}) + woundCleaningHandler:addToMenu(context) end - for i=1,bodyParts:size() do - local bodyPart = bodyParts:get(i-1) + + + --* Modifications and additional methods to handle visible amputation on the health menu *-- + + local og_ISHealthPanel_initialise = ISHealthPanel.initialise + function ISHealthPanel:initialise() + if self.character:isFemale() then + self.sexPl = "Female" + else + self.sexPl = "Male" + end + + local username = self.character:getUsername() + if username ~= "Bob" then + --CachedDataHandler.CalculateHighestAmputatedLimbs(username) + self.highestAmputations = CachedDataHandler.GetHighestAmputatedLimbs(username) + end + + og_ISHealthPanel_initialise(self) + end + + local og_ISHealthPanel_setOtherPlayer = ISHealthPanel.setOtherPlayer + ---@param playerObj IsoPlayer + function ISHealthPanel:setOtherPlayer(playerObj) + og_ISHealthPanel_setOtherPlayer(self, playerObj) + --CachedDataHandler.CalculateAmputatedLimbs(self.character:getUsername()) + end + + ---Get a value between 1 and 0.1 for the cicatrization time + ---@param cicTime integer + ---@return integer + local function GetColorFromCicatrizationTime(cicTime, limbName) + local defaultTime = StaticData.LIMBS_CICATRIZATION_TIME_IND_NUM[limbName] + local delta = cicTime/defaultTime + return math.max(0.15, math.min(delta, 1)) + end + + ---Try to draw the highest amputation in the health panel, based on the cicatrization time + ---@param side string L or R + ---@param username string + function ISHealthPanel:tryDrawHighestAmputation(highestAmputations, side, username) + local redColor + local texture + + if TOC_DEBUG.enableHealthPanelDebug then + redColor = 1 + texture = getTexture("media/ui/test_pattern.png") + else + if highestAmputations[side] == nil then return end + local limbName = highestAmputations[side] + TOC_DEBUG.print("Drawing " .. tostring(limbName) .. " for " .. username) + + local cicTime = ModDataHandler.GetInstance(username):getCicatrizationTime(limbName) + redColor = GetColorFromCicatrizationTime(cicTime, limbName) + + local sexPl + if self.character:isFemale() then + sexPl = "Female" + else + sexPl = "Male" + end + + texture = StaticData.HEALTH_PANEL_TEXTURES[sexPl][limbName] + end + + self:drawTexture(texture, self.healthPanel.x, self.healthPanel.y, 1, redColor, 0, 0) + end + + local og_ISHealthPanel_render = ISHealthPanel.render + function ISHealthPanel:render() + og_ISHealthPanel_render(self) + local username = self.character:getUsername() + + --CachedDataHandler.CalculateHighestAmputatedLimbs(username) + local highestAmputations = CachedDataHandler.GetHighestAmputatedLimbs(username) + + -- TODO Client should send update to other client somehow + + if highestAmputations ~= nil then + -- Left Texture + self:tryDrawHighestAmputation(highestAmputations, "L", username) + + -- Right Texture + self:tryDrawHighestAmputation(highestAmputations, "R", username) + -- else + -- -- Request caching data + -- TOC_DEBUG.print("highest amputated limbs was nil, calculating and getting it now for " .. username) + -- CachedDataHandler.CalculateHighestAmputatedLimbs(username) + end + end + + -- We need to override this to force the alpha to 1 + local og_ISCharacterInfoWindow_render = ISCharacterInfoWindow.prerender + function ISCharacterInfoWindow:prerender() + og_ISCharacterInfoWindow_render(self) + self.backgroundColor.a = 1 + end + + -- We need to override this to force the alpha to 1 for the Medical Check in particular + local og_ISHealthPanel_prerender = ISHealthPanel.prerender + function ISHealthPanel:prerender() + og_ISHealthPanel_prerender(self) + self.backgroundColor.a = 1 + end + + --- The medical check wrap the health panel into this. We need to override its color + local overrideBackgroundColor = true + local og_ISUIElement_wrapInCollapsableWindow = ISUIElement.wrapInCollapsableWindow + ---@param title string + ---@param resizable any + ---@param subClass any + ---@return any + function ISUIElement:wrapInCollapsableWindow(title, resizable, subClass) + local panel = og_ISUIElement_wrapInCollapsableWindow(self, title, resizable, subClass) + + if overrideBackgroundColor then + TOC_DEBUG.print("Overriding color for panel - " .. title) + self.backgroundColor.a = 1 + panel.backgroundColor.a = 1 + end + + return panel + end + + -- This is run when a player is trying the Medical Check action on another player + local og_ISMedicalCheckAction_perform = ISMedicalCheckAction.perform + function ISMedicalCheckAction:perform() + local username = self.otherPlayer:getUsername() + TOC_DEBUG.print("Medical Action on " .. username ) + + -- We need to recalculate them here before we can create the highest amputations point + CachedDataHandler.CalculateAmputatedLimbs(username) + og_ISMedicalCheckAction_perform(self) + end + + local og_ISHealthBodyPartListBox_doDrawItem = ISHealthBodyPartListBox.doDrawItem + function ISHealthBodyPartListBox:doDrawItem(y, item, alt) + y = og_ISHealthBodyPartListBox_doDrawItem(self, y, item, alt) + y = y - 5 + local x = 15 + local fontHgt = getTextManager():getFontHeight(UIFont.Small) + + local username = self.parent.character:getUsername() + --local amputatedLimbs = CachedDataHandler.GetIndexedAmputatedLimbs(username) + + ---@type BodyPart + local bodyPart = item.item.bodyPart + local bodyPartTypeStr = BodyPartType.ToString(bodyPart:getType()) local limbName = StaticData.LIMBS_IND_STR[bodyPartTypeStr] + if limbName then + local modDataHandler = ModDataHandler.GetInstance(username) + if modDataHandler:getIsCut(limbName) and modDataHandler:getIsVisible(limbName) then + if modDataHandler:getIsCicatrized(limbName) then + if modDataHandler:getIsCauterized(limbName) then + self:drawText("- " .. getText("IGUI_HealthPanel_Cauterized"), x, y, 0.58, 0.75, 0.28, 1, UIFont.Small) + else + self:drawText("- " .. getText("IGUI_HealthPanel_Cicatrized"), x, y, 0.28, 0.89, 0.28, 1, UIFont.Small) + end + else + local cicaTime = modDataHandler:getCicatrizationTime(limbName) + + -- Show it in percentage + local maxCicaTime = StaticData.LIMBS_CICATRIZATION_TIME_IND_NUM[limbName] + local percentage = (1 - cicaTime/maxCicaTime) * 100 + self:drawText("- " .. getText("IGUI_HealthPanel_Cicatrization") .. string.format(" %.2f", percentage) .. "%", x, y, 0.89, 0.28, 0.28, 1, UIFont.Small) + y = y + fontHgt + + local scaledDirtyness = math.floor(modDataHandler:getWoundDirtyness(limbName) * 100) + self:drawText("- " .. getText("IGUI_HealthPanel_WoundDirtyness") .. string.format(" %d", scaledDirtyness) .. "%", x, y, 0.89, 0.28, 0.28, 1, UIFont.Small) + end + y = y + fontHgt + + end - local bodyPartAction = self.bodyPartAction and self.bodyPartAction[bodyPart] - if ISHealthPanel.cheat or bodyPart:HasInjury() or bodyPart:bandaged() or bodyPart:stitched() or bodyPart:getSplintFactor() > 0 or bodyPart:getAdditionalPain() > 10 or bodyPart:getStiffness() > 5 or ModDataHandler.GetInstance(self:getPatient():getUsername()):getIsCut(limbName) then - table.insert(result, bodyPart) end + + y = y + 5 + return y end - return result + + local og_ISHealthPanel_getDamagedParts = ISHealthPanel.getDamagedParts + function ISHealthPanel:getDamagedParts() + -- TODO Overriding it is a lot easier, but ew + + local result = {} + local bodyParts = self:getPatient():getBodyDamage():getBodyParts() + if isClient() and not self:getPatient():isLocalPlayer() then + bodyParts = self:getPatient():getBodyDamageRemote():getBodyParts() + end + + local patientUsername = self:getPatient():getUsername() + local mdh = ModDataHandler.GetInstance(patientUsername) + for i=1,bodyParts:size() do + local bodyPart = bodyParts:get(i-1) + local bodyPartTypeStr = BodyPartType.ToString(bodyPart:getType()) + local limbName = StaticData.LIMBS_IND_STR[bodyPartTypeStr] + + if ISHealthPanel.cheat or bodyPart:HasInjury() or bodyPart:bandaged() or bodyPart:stitched() or bodyPart:getSplintFactor() > 0 or bodyPart:getAdditionalPain() > 10 or bodyPart:getStiffness() > 5 or (mdh:getIsCut(limbName) and mdh:getIsVisible(limbName)) then + table.insert(result, bodyPart) + end + end + return result + end + end \ No newline at end of file diff --git a/media/lua/server/TOC/ServerDataHandler.lua b/media/lua/server/TOC/ServerDataHandler.lua index cbc63b5..37ef1e0 100644 --- a/media/lua/server/TOC/ServerDataHandler.lua +++ b/media/lua/server/TOC/ServerDataHandler.lua @@ -20,7 +20,7 @@ function ServerDataHandler.AddTable(key, table) -- Check if key is valid if not luautils.stringStarts(key, StaticData.MOD_NAME .. "_") then return end - TOC_DEBUG.print("received TOC ModData: " .. tostring(key)) + TOC_DEBUG.print("[ServerDataHandler] Received TOC ModData: " .. tostring(key)) ModData.add(key, table) -- Add it to the server mod data ServerDataHandler.modData[key] = table end