29 Commits
v2.0 ... v2.0.7

Author SHA1 Message Date
ZioPao
013f852e7e Fixed huge bug that would break things after a player died 2024-05-06 17:14:45 +02:00
ZioPao
a7a064119d Added destroy instance in DataController after death 2024-05-05 21:07:33 +02:00
ZioPao
9b1876b235 mod.info and version bump 2024-05-05 18:04:10 +02:00
ZioPao
0d9ee4203c Revert "Revert "Added tests for blood on limbs""
This reverts commit 2ea03601f5.
2024-05-05 18:03:39 +02:00
ZioPao
2ea03601f5 Revert "Added tests for blood on limbs"
This reverts commit 3d4a54418c.
2024-05-05 18:03:08 +02:00
ZioPao
3d4a54418c Added tests for blood on limbs 2024-05-05 18:02:48 +02:00
ZioPao
17718cbbca bit of cleaning 2024-05-05 17:02:14 +02:00
ZioPao
f9ad597d2b bump to mod.info 2024-05-05 17:01:40 +02:00
ZioPao
c8b6a8c5ed Wash Yourself override 2024-05-05 16:58:16 +02:00
ZioPao
9478e0faa1 Added ProstFamiliarity in adjustMaxTime function 2024-05-05 13:56:58 +02:00
ZioPao
d512f0ba81 bump to mod.info and _version 2024-05-05 13:51:03 +02:00
ZioPao
31995965f9 Added leveling for ProstFamiliarity, dynamic xp 2024-05-05 13:50:47 +02:00
ZioPao
d35840d825 Added level scaling for prosthetics 2024-05-05 13:27:20 +02:00
ZioPao
e270b4f73b updated steam desc 2024-05-05 12:38:20 +02:00
ZioPao
4757d9dfa8 bump to modversion 2024-05-05 01:00:49 +02:00
ZioPao
1b235ebaa4 bump to mod.info 2024-05-05 00:49:05 +02:00
Pao
44d486dfeb Merge pull request #36 from ZioPao/main
backmerge to dev since I'm an idiot
2024-05-05 00:48:40 +02:00
ZioPao
5cc982188a Fixed prosthesis not working correctly 2024-05-05 00:47:50 +02:00
ZioPao
aadbe02df4 Fixed sync with server after setup to prevent issues with medical check 2024-05-05 00:14:37 +02:00
Pao
20bed84910 Merge pull request #33 from ZioPao/dev
v2.0.2
2024-05-04 18:00:40 +02:00
ZioPao
fc3113f243 Bump to mod info and description 2024-05-04 17:59:53 +02:00
ZioPao
be368738ba Fix to cicatrization visuals with traits 2024-05-04 17:11:38 +02:00
ZioPao
4209f690a8 Added IT translation 2024-05-04 16:45:57 +02:00
ZioPao
a3a2614124 Fixed traits 2024-05-04 16:27:46 +02:00
ZioPao
50f6db9344 Added check to prevent incompatibilities with other mods 2024-05-04 15:35:25 +02:00
Pao
17d554d269 Merge pull request #28 from ZioPao/main
back merge from main to dev
2024-05-03 14:25:25 +02:00
ZioPao
1bef713de5 Merge branch 'main' of https://github.com/ZioPao/The-Only-Cure 2024-05-03 14:20:59 +02:00
ZioPao
beee6f409c bump to mod.info 2024-05-03 14:20:57 +02:00
ZioPao
540f510eb0 Fixed wearing items bug 2024-05-03 14:20:46 +02:00
30 changed files with 427 additions and 95 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

View File

@@ -6,9 +6,9 @@ Wait until you succumb to the virus or take matters into your hands. Cut off tha
This version of [b]The Only Cure[/b] has been rebuilt from scratch to support future additions and to feel as close as possible as a vanilla mechanic. This version of [b]The Only Cure[/b] has been rebuilt from scratch to support future additions and to feel as close as possible as a vanilla mechanic.
[b]If you're using an older version of The Only Cure and want to switch with this, you're gonna need to create a new character\save to prevent issues.[/b]
[b]The older version will be delisted shortly and it will not be supported anymore.[/b] [b]The older version will be delisted shortly and it will not be supported anymore.[/b]
[h1]Supports [b]Single Player[/b] and [b]Multiplayer[/b]. Host Mode is currently [b]UNSUPPORTED![/b][/h1]
Supports [b]Single Player[/b] and [b]Multiplayer[/b]!
[h1]Setup[/h1] [h1]Setup[/h1]
Use it with the following mods for the intended experience: Use it with the following mods for the intended experience:
@@ -17,6 +17,8 @@ Use it with the following mods for the intended experience:
[*] [url=https://steamcommunity.com/sharedfiles/filedetails/?id=2934621024]Brutal Handwork[/url] [*] [url=https://steamcommunity.com/sharedfiles/filedetails/?id=2934621024]Brutal Handwork[/url]
[/list] [/list]
Place them [b]BEFORE[/b] The Only Cure in your mod list!
[hr][/hr] [hr][/hr]
[h1]Quick guide[/h1] [h1]Quick guide[/h1]
@@ -74,6 +76,10 @@ Got any issues or found some pesky bugs? Report them on GitHub!
[th]dhert[/th] [th]dhert[/th]
[th]Compatibility API[/th] [th]Compatibility API[/th]
[/tr] [/tr]
[tr]
[th]JCloudJalix[/th]
[th]German translation[/th]
[/tr]
[/table] [/table]
[hr][/hr] [hr][/hr]

View File

@@ -57,6 +57,7 @@ function DataController:setup(key)
---@type tocModDataType ---@type tocModDataType
self.tocData = { self.tocData = {
-- Generic stuff that does not belong anywhere else -- Generic stuff that does not belong anywhere else
isInitializing = true,
isIgnoredPartInfected = false, isIgnoredPartInfected = false,
isAnyLimbCut = false, isAnyLimbCut = false,
limbs = {}, limbs = {},
@@ -86,9 +87,16 @@ function DataController:setup(key)
} }
end end
-- Add it to global mod data -- Add it to client global mod data
ModData.add(key, self.tocData) ModData.add(key, self.tocData)
-- Sync with the server
self:apply()
-- -- Disable lock
-- self.tocData.isInitializing = false
-- ModData.add(key, self.tocData)
end end
---In case of desync between the table on ModData and the table here ---In case of desync between the table on ModData and the table here
@@ -100,7 +108,7 @@ function DataController:applyOnlineData(tocData)
end end
---@param key string ---@param key string
function DataController:loadLocalData(key) function DataController:tryLoadLocalData(key)
self.tocData = ModData.get(key) self.tocData = ModData.get(key)
--TOC_DEBUG.printTable(self.tocData) --TOC_DEBUG.printTable(self.tocData)
@@ -354,7 +362,7 @@ end
function DataController:apply() function DataController:apply()
TOC_DEBUG.print("Applying data for " .. self.username) TOC_DEBUG.print("Applying data for " .. self.username)
ModData.transmit(CommandsData.GetKey(self.username)) ModData.transmit(CommandsData.GetKey(self.username))
-- if getPlayer():getUsername() ~= self.username then -- if getPlayer():getUsername() ~= self.username then
-- sendClientCommand(CommandsData.modules.TOC_RELAY, CommandsData.server.Relay.RelayApplyFromOtherClient, {patientUsername = self.username} ) -- sendClientCommand(CommandsData.modules.TOC_RELAY, CommandsData.server.Relay.RelayApplyFromOtherClient, {patientUsername = self.username} )
-- -- force request from the server for that other client... -- -- force request from the server for that other client...
@@ -368,10 +376,13 @@ end
function DataController.ReceiveData(key, data) function DataController.ReceiveData(key, data)
-- During startup the game can return Bob as the player username, adding a useless ModData table -- During startup the game can return Bob as the player username, adding a useless ModData table
if key == "TOC_Bob" then return end if key == "TOC_Bob" then return end
if not luautils.stringStarts(key, StaticData.MOD_NAME .. "_") then return end
TOC_DEBUG.print("ReceiveData for " .. key) TOC_DEBUG.print("ReceiveData for " .. key)
if data == {} or data == nil then
error("Data is nil, new character or something is wrong") if data == nil or data.limbs == nil then
TOC_DEBUG.print("Data is nil, new character or something is wrong")
end end
-- Get DataController instance if there was none for that user and reapply the correct ModData table as a reference -- Get DataController instance if there was none for that user and reapply the correct ModData table as a reference
@@ -387,14 +398,17 @@ function DataController.ReceiveData(key, data)
-- TODO Add update from server scenario -- TODO Add update from server scenario
if handler.isResetForced then if handler.isResetForced then
TOC_DEBUG.print("Forced reset")
handler:setup(key) handler:setup(key)
elseif data then elseif data and data.limbs then
-- Let's validate that the data structure is actually valid to prevent issues
if data.isUpdateFromServer then if data.isUpdateFromServer then
TOC_DEBUG.print("Update from the server") TOC_DEBUG.print("Update from the server")
end end
handler:applyOnlineData(data) handler:applyOnlineData(data)
elseif username == getPlayer():getUsername() then elseif username == getPlayer():getUsername() then
handler:loadLocalData(key) TOC_DEBUG.print("loading local data")
handler:tryLoadLocalData(key)
end end
@@ -434,7 +448,7 @@ Events.OnReceiveGlobalModData.Add(DataController.ReceiveData)
--- SP Only initialization --- SP Only initialization
---@param key string ---@param key string
function DataController:initSinglePlayer(key) function DataController:initSinglePlayer(key)
self:loadLocalData(key) self:tryLoadLocalData(key)
if self.tocData == nil or self.isResetForced then if self.tocData == nil or self.isResetForced then
self:setup(key) self:setup(key)
end end
@@ -462,4 +476,12 @@ function DataController.GetInstance(username)
end end
end end
function DataController.DestroyInstance(username)
if DataController.instances[username] ~= nil then
DataController.instances[username] = nil
end
end
return DataController return DataController

View File

@@ -15,8 +15,12 @@ local StaticData = require("TOC/StaticData")
-- a prosthesis on, that can trigger random bleeds. -- a prosthesis on, that can trigger random bleeds.
local function CheckHandFeasibility(limbName) local function CheckHandFeasibility(limbName)
TOC_DEBUG.print("Checking hand feasibility: " .. limbName)
local dcInst = DataController.GetInstance() local dcInst = DataController.GetInstance()
return not dcInst:getIsCut(limbName) or dcInst:getIsProstEquipped(limbName)
local isFeasible = not dcInst:getIsCut(limbName) or dcInst:getIsProstEquipped(limbName)
TOC_DEBUG.print("isFeasible: " .. tostring(isFeasible))
return isFeasible
end end
@@ -39,13 +43,18 @@ function ISBaseTimedAction:adjustMaxTime(maxTime)
for k, _ in pairs(amputatedLimbs) do for k, _ in pairs(amputatedLimbs) do
local limbName = k local limbName = k
--if dcInst:getIsCut(limbName) then local perkAmp = Perks["Side_" .. CommonMethods.GetSide(limbName)]
local perk = Perks["Side_" .. CommonMethods.GetSide(limbName)] local perkLevel = pl:getPerkLevel(perkAmp)
local perkLevel = pl:getPerkLevel(perk)
if dcInst:getIsProstEquipped(limbName) then
-- TODO We should separate this in multiple perks, since this is gonna be a generic familiarity and could make no actual sense
local perkProst = Perks["ProstFamiliarity"]
perkLevel = perkLevel + pl:getPerkLevel(perkProst)
end
local perkLevelScaled local perkLevelScaled
if perkLevel ~= 0 then perkLevelScaled = perkLevel / 10 else perkLevelScaled = 0 end if perkLevel ~= 0 then perkLevelScaled = perkLevel / 10 else perkLevelScaled = 0 end
time = time * (StaticData.LIMBS_TIME_MULTIPLIER_IND_NUM[limbName] - perkLevelScaled) time = time * (StaticData.LIMBS_TIME_MULTIPLIER_IND_NUM[limbName] - perkLevelScaled)
--end
end end
end end
return time return time
@@ -59,22 +68,31 @@ function ISBaseTimedAction:perform()
og_ISBaseTimedAction_perform(self) og_ISBaseTimedAction_perform(self)
TOC_DEBUG.print("Running ISBaseTimedAction.perform override") TOC_DEBUG.print("Running ISBaseTimedAction.perform override")
TOC_DEBUG.print("max time: " .. tostring(self.maxTime))
local dcInst = DataController.GetInstance() local dcInst = DataController.GetInstance()
if not dcInst:getIsAnyLimbCut() then return end if not dcInst:getIsAnyLimbCut() then return end
local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(LocalPlayerController.username) local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(LocalPlayerController.username)
local xp = self.maxTime/100
for k, _ in pairs(amputatedLimbs) do for k, _ in pairs(amputatedLimbs) do
local limbName = k local limbName = k
-- We're checking for only "visible" amputations to prevent from having bleeds everywhere -- We're checking for only "visible" amputations to prevent from having bleeds everywhere
if dcInst:getIsCut(limbName) and dcInst:getIsVisible(limbName) then if dcInst:getIsCut(limbName) and dcInst:getIsVisible(limbName) then
local side = CommonMethods.GetSide(limbName) local side = CommonMethods.GetSide(limbName)
LocalPlayerController.playerObj:getXp():AddXP(Perks["Side_" .. side], 1) -- TODO Make it dynamic LocalPlayerController.playerObj:getXp():AddXP(Perks["Side_" .. side], xp)
if not dcInst:getIsCicatrized(limbName) and dcInst:getIsProstEquipped(limbName) then if not dcInst:getIsCicatrized(limbName) and dcInst:getIsProstEquipped(limbName) then
TOC_DEBUG.print("Trying for bleed, player met the criteria") TOC_DEBUG.print("Trying for bleed, player met the criteria")
LocalPlayerController.TryRandomBleed(self.character, limbName) LocalPlayerController.TryRandomBleed(self.character, limbName)
end end
-- Level up prosthesis perk
if dcInst:getIsProstEquipped(limbName) then
LocalPlayerController.playerObj:getXp():AddXP(Perks["ProstFamiliarity"], xp)
end
end end
end end
end end
@@ -107,6 +125,8 @@ end
---A recreation of the original method, but with amputations in mind ---A recreation of the original method, but with amputations in mind
---@param dcInst DataController ---@param dcInst DataController
function ISEquipWeaponAction:performWithAmputation(dcInst) function ISEquipWeaponAction:performWithAmputation(dcInst)
TOC_DEBUG.print("running ISEquipWeaponAction performWithAmputation")
local hand = nil local hand = nil
local otherHand = nil local otherHand = nil
local getMethodFirst = nil local getMethodFirst = nil
@@ -130,6 +150,10 @@ function ISEquipWeaponAction:performWithAmputation(dcInst)
setMethodSecond = self.character.setSecondaryHandItem setMethodSecond = self.character.setSecondaryHandItem
end end
local isFirstValid = CheckHandFeasibility(hand)
local isSecondValid = CheckHandFeasibility(otherHand)
if not self.twoHands then if not self.twoHands then
if getMethodFirst(self.character) and getMethodFirst(self.character):isRequiresEquippedBothHands() then if getMethodFirst(self.character) and getMethodFirst(self.character):isRequiresEquippedBothHands() then
setMethodFirst(self.character, nil) setMethodFirst(self.character, nil)
@@ -145,10 +169,10 @@ function ISEquipWeaponAction:performWithAmputation(dcInst)
setMethodSecond(self.character, nil) setMethodSecond(self.character, nil)
-- TODO We should use the CachedData indexable instead of dcInst -- TODO We should use the CachedData indexable instead of dcInst
if not dcInst:getIsCut(hand) then if isFirstValid then
setMethodSecond(self.character, self.item) setMethodSecond(self.character, self.item)
-- Check other HAND! -- Check other HAND!
elseif not dcInst:getIsCut(otherHand) then elseif isSecondValid then
setMethodFirst(self.character, self.item) setMethodFirst(self.character, self.item)
end end
end end
@@ -156,13 +180,10 @@ function ISEquipWeaponAction:performWithAmputation(dcInst)
setMethodFirst(self.character, nil) setMethodFirst(self.character, nil)
setMethodSecond(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("First Hand: " .. tostring(hand))
-- TOC_DEBUG.print("Prost Group: " .. tostring(prostGroup)) -- --TOC_DEBUG.print("Prost Group: " .. tostring(prostGroup))
-- TOC_DEBUG.print("Other Hand: " .. tostring(otherHand)) -- TOC_DEBUG.print("Other Hand: " .. tostring(otherHand))
-- TOC_DEBUG.print("Other Prost Group: " .. tostring(otherProstGroup)) -- --TOC_DEBUG.print("Other Prost Group: " .. tostring(otherProstGroup))
-- TOC_DEBUG.print("isPrimaryHandValid: " .. tostring(isFirstValid)) -- TOC_DEBUG.print("isPrimaryHandValid: " .. tostring(isFirstValid))
-- TOC_DEBUG.print("isSecondaryHandValid: " .. tostring(isSecondValid)) -- TOC_DEBUG.print("isSecondaryHandValid: " .. tostring(isSecondValid))
@@ -268,7 +289,7 @@ end
local function CheckLimbFeasibility(limbName) local function CheckLimbFeasibility(limbName)
local dcInst = DataController.GetInstance() local dcInst = DataController.GetInstance()
local isFeasible = not dcInst:getIsCut(limbName) or dcInst:getIsProstEquipped(limbName) local isFeasible = not dcInst:getIsCut(limbName) or dcInst:getIsProstEquipped(limbName)
TOC_DEBUG.print("isFeasible="..tostring(isFeasible)) --TOC_DEBUG.print("isFeasible="..tostring(isFeasible))
return isFeasible return isFeasible
end end

View File

@@ -33,6 +33,9 @@ function LocalPlayerController.InitializePlayer(isForced)
Events.OnAmputatedLimb.Add(LocalPlayerController.ToggleUpdateAmputations) Events.OnAmputatedLimb.Add(LocalPlayerController.ToggleUpdateAmputations)
LocalPlayerController.ToggleUpdateAmputations() LocalPlayerController.ToggleUpdateAmputations()
-- Manage their traits
LocalPlayerController.ManageTraits(playerObj)
-- Since isForced is used to reset an existing player data, we're gonna clean their ISHealthPanel table too -- Since isForced is used to reset an existing player data, we're gonna clean their ISHealthPanel table too
if isForced then if isForced then
local ItemsController = require("TOC/Controllers/ItemsController") local ItemsController = require("TOC/Controllers/ItemsController")
@@ -47,13 +50,16 @@ end
---Handles the traits ---Handles the traits
---@param playerObj IsoPlayer ---@param playerObj IsoPlayer
function LocalPlayerController.ManageTraits(playerObj) function LocalPlayerController.ManageTraits(playerObj)
local AmputationHandler = require("Handlers/TOC_AmputationHandler") local AmputationHandler = require("TOC/Handlers/AmputationHandler")
for k, v in pairs(StaticData.TRAITS_BP) do for k, v in pairs(StaticData.TRAITS_BP) do
if playerObj:HasTrait(k) then if playerObj:HasTrait(k) then
-- Once we find one, we should be done. -- Once we find one, we should be done since they're exclusive
local tempHandler = AmputationHandler:new(v) local tempHandler = AmputationHandler:new(v, playerObj)
tempHandler:execute(false) -- No damage tempHandler:execute(false) -- No damage
tempHandler:close() tempHandler:close()
-- The wound should be already cicatrized
LocalPlayerController.HandleSetCicatrization(DataController.GetInstance(), playerObj, v)
return return
end end
end end
@@ -257,14 +263,11 @@ function LocalPlayerController.UpdateAmputations()
cicTime = cicTime - cicDec cicTime = cicTime - cicDec
dcInst:setCicatrizationTime(limbName, cicTime)
TOC_DEBUG.print("New cicatrization time: " .. tostring(cicTime)) TOC_DEBUG.print("New cicatrization time: " .. tostring(cicTime))
if cicTime <= 0 then if cicTime <= 0 then
TOC_DEBUG.print(tostring(limbName) .. " is cicatrized") LocalPlayerController.HandleSetCicatrization(dcInst, pl, limbName)
dcInst:setIsCicatrized(limbName, true) else
-- Set visual dcInst:setCicatrizationTime(limbName, cicTime)
local ItemsController = require("TOC/Controllers/ItemsController")
ItemsController.Player.OverrideAmputationItemVisuals(pl, limbName, true)
end end
end end
end end
@@ -285,6 +288,23 @@ function LocalPlayerController.ToggleUpdateAmputations()
CommonMethods.SafeStartEvent("EveryHours", LocalPlayerController.UpdateAmputations) CommonMethods.SafeStartEvent("EveryHours", LocalPlayerController.UpdateAmputations)
end end
--* Cicatrization and cicatrization visuals *--
---Set the boolean and cicTime in DCINST and the visuals for the amputated limb
---@param dcInst DataController
---@param playerObj IsoPlayer
---@param limbName string
function LocalPlayerController.HandleSetCicatrization(dcInst, playerObj, limbName)
TOC_DEBUG.print(tostring(limbName) .. " is cicatrized")
dcInst:setIsCicatrized(limbName, true)
dcInst:setCicatrizationTime(limbName, 0)
-- Set visuals for the amputation
local ItemsController = require("TOC/Controllers/ItemsController")
ItemsController.Player.OverrideAmputationItemVisuals(playerObj, limbName, true)
end
--* Object drop handling when amputation occurs --* Object drop handling when amputation occurs
@@ -334,10 +354,20 @@ function LocalPlayerController.DropItemsAfterAmputation(limbName)
end end
end end
end end
-- TODO Consider 2 handed weapons too
-- equipped items too
if side == "R" then
pl:setPrimaryHandItem(nil)
elseif side == "L" then
pl:setSecondaryHandItem(nil)
end
end end
Events.OnAmputatedLimb.Add(LocalPlayerController.DropItemsAfterAmputation) Events.OnAmputatedLimb.Add(LocalPlayerController.DropItemsAfterAmputation)
Events.OnProsthesisUnequipped.Add(LocalPlayerController.DropItemsAfterAmputation)

View File

@@ -71,11 +71,13 @@ function TourniquetController.WrapClothingAction(obj, wrappedFunc)
group:setMultiItem(TourniquetController.bodyLoc, false) group:setMultiItem(TourniquetController.bodyLoc, false)
end end
wrappedFunc(obj) local ogValue = wrappedFunc(obj)
if isTourniquet then if isTourniquet then
group:setMultiItem(TourniquetController.bodyLoc, true) group:setMultiItem(TourniquetController.bodyLoc, true)
end end
return ogValue -- Needed for isValid
end end
@@ -98,12 +100,12 @@ end
local og_ISWearClothing_isValid = ISWearClothing.isValid local og_ISWearClothing_isValid = ISWearClothing.isValid
function ISWearClothing:isValid() function ISWearClothing:isValid()
TourniquetController.WrapClothingAction(self, og_ISWearClothing_isValid) return TourniquetController.WrapClothingAction(self, og_ISWearClothing_isValid)
end end
local og_ISUnequipAction_perform = ISUnequipAction.perform local og_ISUnequipAction_perform = ISUnequipAction.perform
function ISUnequipAction:perform() function ISUnequipAction:perform()
TourniquetController.WrapClothingAction(self, og_ISUnequipAction_perform) return TourniquetController.WrapClothingAction(self, og_ISUnequipAction_perform)
end end

View File

@@ -1,3 +1,4 @@
--* Setup Events *-- --* Setup Events *--
LuaEventManager.AddEvent("OnAmputatedLimb") --Triggered when a limb has been amputated LuaEventManager.AddEvent("OnAmputatedLimb") --Triggered when a limb has been amputated
LuaEventManager.AddEvent("OnProsthesisUnequipped")
LuaEventManager.AddEvent("OnReceivedTocData") -- Triggered when TOC data is ready LuaEventManager.AddEvent("OnReceivedTocData") -- Triggered when TOC data is ready

View File

@@ -111,6 +111,8 @@ end
--* Hand feasibility caching *-- --* Hand feasibility caching *--
CachedDataHandler.handFeasibility = {} CachedDataHandler.handFeasibility = {}

View File

@@ -70,11 +70,10 @@ function ProsthesisHandler.CheckIfEquippable(fullType)
return false return false
end end
---Handle equipping or unequipping prosthetics ---Handle equipping or unequipping prosthetics
---@param item InventoryItem ---@param item InventoryItem
---@param isEquipping boolean ---@param isEquipping boolean
---@return boolean ---@return boolean
function ProsthesisHandler.SearchAndSetupProsthesis(item, isEquipping) function ProsthesisHandler.SearchAndSetupProsthesis(item, isEquipping)
if not ProsthesisHandler.CheckIfProst(item) then return false end if not ProsthesisHandler.CheckIfProst(item) then return false end
@@ -100,7 +99,7 @@ local function HandleProsthesisValidation(item, isEquippable)
local isProst = ProsthesisHandler.CheckIfProst(item) local isProst = ProsthesisHandler.CheckIfProst(item)
if not isProst then return isEquippable end if not isProst then return isEquippable end
local fullType = item:getFullType() -- use fulltype for side local fullType = item:getFullType() -- use fulltype for side
if isEquippable then if isEquippable then
isEquippable = ProsthesisHandler.CheckIfEquippable(fullType) isEquippable = ProsthesisHandler.CheckIfEquippable(fullType)
else else
@@ -133,8 +132,6 @@ function ISClothingExtraAction:isValid()
return HandleProsthesisValidation(testItem, isEquippable) return HandleProsthesisValidation(testItem, isEquippable)
end end
--[[ --[[
Horrendous workaround Horrendous workaround
@@ -148,8 +145,8 @@ end
local og_ISClothingExtraAction_perform = ISClothingExtraAction.perform local og_ISClothingExtraAction_perform = ISClothingExtraAction.perform
function ISClothingExtraAction:perform() function ISClothingExtraAction:perform()
--local extraItem = InventoryItemFactory.CreateItem(self.extra) local extraItem = InventoryItemFactory.CreateItem(self.extra)
local isProst = ProsthesisHandler.SearchAndSetupProsthesis(self.item, true) local isProst = ProsthesisHandler.SearchAndSetupProsthesis(extraItem, true)
local group local group
if isProst then if isProst then
group = BodyLocations.getGroup("Human") group = BodyLocations.getGroup("Human")
@@ -161,7 +158,6 @@ function ISClothingExtraAction:perform()
if isProst then if isProst then
group:setMultiItem("TOC_ArmProst", true) group:setMultiItem("TOC_ArmProst", true)
end end
end end
local og_ISUnequipAction_perform = ISUnequipAction.perform local og_ISUnequipAction_perform = ISUnequipAction.perform
@@ -176,8 +172,15 @@ function ISUnequipAction:perform()
if isProst then if isProst then
group:setMultiItem("TOC_ArmProst", true) group:setMultiItem("TOC_ArmProst", true)
-- we need to fetch the limbname associated to the prosthesis
local side = CommonMethods.GetSide(self.item:getFullType())
local highestAmputatedLimbs = CachedDataHandler.GetHighestAmputatedLimbs(getPlayer():getUsername())
if highestAmputatedLimbs then
local hal = highestAmputatedLimbs[side]
triggerEvent("OnProsthesisUnequipped", hal)
end
end end
end end
return ProsthesisHandler return ProsthesisHandler

View File

@@ -6,39 +6,11 @@ require("TOC/Events")
---@class Main ---@class Main
local Main = { local Main = {
_version = 2.0 _version = "2.0.7"
} }
---Setups the custom traits
function Main.SetupTraits()
-- Perks.Left_Hand is defined in perks.txt
local traitsTable = {
[1] = TraitFactory.addTrait("Amputee_Hand", getText("UI_trait_Amputee_Hand"), -8, getText("UI_trait_Amputee_Hand_desc"), false, false),
[2] = TraitFactory.addTrait("Amputee_LowerArm", getText("UI_trait_Amputee_LowerArm"), -10, getText("UI_trait_Amputee_LowerArm_desc"), false, false),
[3] = TraitFactory.addTrait("Amputee_UpperArm", getText("UI_trait_Amputee_UpperArm"), -20, getText("UI_trait_Amputee_UpperArm_desc"), false, false)
}
for i=1, #traitsTable do
---@type Trait
local t = traitsTable[i]
---@diagnostic disable-next-line: undefined-field
t:addXPBoost(Perks.Left_Hand, 4)
t:addXPBoost(Perks.Fitness, -1)
t:addXPBoost(Perks.Strength, -1)
end
TraitFactory.addTrait("Insensitive", getText("UI_trait_Insensitive"), 6, getText("UI_trait_Insensitive_desc"), false, false)
TraitFactory.setMutualExclusive("Amputee_Hand", "Amputee_LowerArm")
TraitFactory.setMutualExclusive("Amputee_Hand", "Amputee_UpperArm")
TraitFactory.setMutualExclusive("Amputee_LowerArm", "Amputee_UpperArm")
end
function Main.Start() function Main.Start()
TOC_DEBUG.print("running Start method") TOC_DEBUG.print("Starting The Only Cure version " .. tostring(Main._version))
Main.SetupTraits()
Main.SetupEvents() Main.SetupEvents()
end end
@@ -47,6 +19,7 @@ function Main.SetupEvents()
Events.OnReceivedTocData.Add(CachedDataHandler.CalculateCacheableValues) Events.OnReceivedTocData.Add(CachedDataHandler.CalculateCacheableValues)
end end
function Main.InitializePlayer() function Main.InitializePlayer()
---Looop until we've successfully initialized the mod ---Looop until we've successfully initialized the mod
local function TryToInitialize() local function TryToInitialize()
@@ -63,16 +36,15 @@ function Main.InitializePlayer()
CommonMethods.SafeStartEvent("OnTick", TryToInitialize) CommonMethods.SafeStartEvent("OnTick", TryToInitialize)
end end
---Clean the TOC table for that SP player, to prevent from clogging it up ---Clean the TOC table for that SP player, to prevent it from clogging ModData up
---@param player IsoPlayer ---@param player IsoPlayer
function Main.WipeData(player) function Main.WipeData(player)
TOC_DEBUG.print("Wiping data after death") local username = player:getUsername()
local key = CommandsData.GetKey(player:getUsername()) TOC_DEBUG.print("Wiping data after death: " .. username)
local key = CommandsData.GetKey(username)
--ModData.remove(key) --ModData.remove(key)
if not isClient() then if not isClient() then
-- For SP, it's enough just removing the data this way -- For SP, it's enough just removing the data this way
ModData.remove(key) ModData.remove(key)
@@ -81,7 +53,14 @@ function Main.WipeData(player)
-- at the next character by passing an empty mod data -- at the next character by passing an empty mod data
ModData.add(key, {}) ModData.add(key, {})
ModData.transmit(key) ModData.transmit(key)
end end
-- Let's wipe the instance too just to be sure
local DataController = require("TOC/Controllers/DataController")
DataController.DestroyInstance(username)
end end
--* Events *-- --* Events *--

View File

@@ -11,7 +11,6 @@ local StaticData = require("TOC/StaticData")
TestFramework.registerTestModule("LocalPlayerController", "Setup", function() TestFramework.registerTestModule("LocalPlayerController", "Setup", function()
local Tests = {} local Tests = {}
function Tests.InitializePlayer() function Tests.InitializePlayer()
local pl = getPlayer()
LocalPlayerController.InitializePlayer(true) LocalPlayerController.InitializePlayer(true)
end end
return Tests return Tests
@@ -154,6 +153,38 @@ TestFramework.registerTestModule("Various", "Player", function()
end) end)
TestFramework.registerTestModule("Various", "Visuals", function()
local Tests = {}
function Tests.AddBloodLeftForearm()
local playerObj = getPlayer()
local limbName = "ForeArm_L"
local fullType = StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName
local item = playerObj:getInventory():FindAndReturn(fullType)
if instanceof(item, "Clothing") then
---@cast item Clothing
print("Found limb to add blood onto")
item:setBloodLevel(100)
local coveredParts = BloodClothingType.getCoveredParts(item:getBloodClothingType())
if coveredParts then
for j=0,coveredParts:size()-1 do
item:setBlood(coveredParts:get(j), 100)
item:setDirt(coveredParts:get(j), 100)
end
end
end
playerObj:resetModelNextFrame()
end
return Tests
end)
-------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------
if not getActivatedMods():contains("PerfTestFramework") or not isDebugEnabled() then return end if not getActivatedMods():contains("PerfTestFramework") or not isDebugEnabled() then return end

View File

@@ -1,5 +1,6 @@
require "TimedActions/ISBaseTimedAction" require "TimedActions/ISBaseTimedAction"
local DataController = require("TOC/Controllers/DataController") local DataController = require("TOC/Controllers/DataController")
local LocalPlayerController = require("TOC/Controllers/LocalPlayerController")
--------------- ---------------
---@class CauterizeAction : ISBaseTimedAction ---@class CauterizeAction : ISBaseTimedAction
@@ -72,11 +73,14 @@ function CauterizeAction:perform()
local dcInst = DataController.GetInstance() local dcInst = DataController.GetInstance()
dcInst:setCicatrizationTime(self.limbName, 0) dcInst:setCicatrizationTime(self.limbName, 0)
dcInst:setIsCicatrized(self.limbName, true)
dcInst:setIsCauterized(self.limbName, true) dcInst:setIsCauterized(self.limbName, true)
-- we don't care about the depended limbs, since they're alread "cicatrized" -- Set isCicatrized and the visuals in one go, since this action is gonna be run only on a single client
LocalPlayerController.HandleSetCicatrization(dcInst, self.character, self.limbName)
-- TODO Add specific visuals for cauterization
-- we don't care about the depended limbs, since they're alread "cicatrized"
dcInst:apply() dcInst:apply()
ISBaseTimedAction.perform(self) ISBaseTimedAction.perform(self)

View File

@@ -0,0 +1,80 @@
local CachedDataHandler = require("TOC/Handlers/CachedDataHandler")
local StaticData = require("TOC/StaticData")
-- Since amputations are actually clothing items, we need to override ISWashYourself to account for that
-- TODO Clean this up
local og_ISWashYourself_perform = ISWashYourself.perform
function ISWashYourself:perform()
TOC_DEBUG.print("ISWashYourself override")
---@type IsoPlayer
local pl = self.character
local plInv = pl:getInventory()
-- Search for amputations and clean them here
local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(pl:getUsername())
for limbName, _ in pairs(amputatedLimbs) do
TOC_DEBUG.print("Checking if " .. limbName .. " is in inventory and washing it")
-- get clothing item
local foundItem = plInv:FindAndReturn(StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName)
if foundItem and instanceof(foundItem, "Clothing") then
TOC_DEBUG.print("Washing " .. limbName)
---@cast foundItem Clothing
foundItem:setWetness(100)
foundItem:setBloodLevel(0)
foundItem:setDirtyness(0) -- TODO Integrate with other dirtyness
local coveredParts = BloodClothingType.getCoveredParts(foundItem:getBloodClothingType())
for j=0, coveredParts:size() - 1 do
foundItem:setBlood(coveredParts:get(j), 0)
foundItem:setDirt(coveredParts:get(j), 0)
end
end
end
og_ISWashYourself_perform(self)
end
local og_ISWashYourself_GetRequiredWater = ISWashYourself.GetRequiredWater
---@param character IsoPlayer
---@return integer
function ISWashYourself.GetRequiredWater(character)
local units = og_ISWashYourself_GetRequiredWater(character)
local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(character:getUsername())
local plInv = character:getInventory()
for limbName, _ in pairs(amputatedLimbs) do
TOC_DEBUG.print("Checking if " .. limbName .. " is in inventory and washing it")
-- get clothing item
local item = plInv:FindAndReturn(StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limbName)
if item and instanceof(item, "Clothing") then
local coveredParts = BloodClothingType.getCoveredParts(item:getBloodClothingType())
if coveredParts then
for i=1,coveredParts:size() do
local part = coveredParts:get(i-1)
if item:getBlood(part) > 0 then
units = units + 1
end
end
end
end
end
return units
end

View File

@@ -29,12 +29,15 @@ function ServerDataHandler.AddTable(key, table)
--TOC_DEBUG.printTable(table) --TOC_DEBUG.printTable(table)
-- Set that the data has been modified and it's updated on the server -- Set that the data has been modified and it's updated on the server
table.isUpdateFromServer = true table.isUpdateFromServer = true -- FIX this is useless as of now
ModData.add(key, table) -- Add it to the server mod data ModData.add(key, table) -- Add it to the server mod data
ServerDataHandler.modData[key] = table ServerDataHandler.modData[key] = table
-- Check integrity of table. if it doesn't contains toc data, it means that we received a reset
if table.limbs == nil then return end
-- Since this could be triggered by a different client than the one referenced in the key, we're gonna -- Since this could be triggered by a different client than the one referenced in the key, we're gonna
-- apply the changes back to the key client again to be sure that everything is in sync -- apply the changes back to the key client again to be sure that everything is in sync
local username = CommandsData.GetUsername(key) local username = CommandsData.GetUsername(key)

View File

@@ -2,7 +2,7 @@
---@alias limbsTable {Hand_L : partDataType, ForeArm_L : partDataType, UpperArm_L : partDataType, Hand_R : partDataType, ForeArm_R : partDataType, UpperArm_R : partDataType } ---@alias limbsTable {Hand_L : partDataType, ForeArm_L : partDataType, UpperArm_L : partDataType, Hand_R : partDataType, ForeArm_R : partDataType, UpperArm_R : partDataType }
---@alias prosthesisData {isProstEquipped : boolean, prostFactor : number } ---@alias prosthesisData {isProstEquipped : boolean, prostFactor : number }
---@alias prosthesesTable {Top_L : prosthesisData, Top_R : prosthesisData } -- TODO add Bottom_L and Bottom_R ---@alias prosthesesTable {Top_L : prosthesisData, Top_R : prosthesisData } -- TODO add Bottom_L and Bottom_R
---@alias tocModDataType { limbs : limbsTable, prostheses : prosthesesTable, isIgnoredPartInfected : boolean, isAnyLimbCut : boolean, isUpdateFromServer : boolean } ---@alias tocModDataType { limbs : limbsTable, prostheses : prosthesesTable, isIgnoredPartInfected : boolean, isAnyLimbCut : boolean, isUpdateFromServer : boolean, isInitializing : boolean}
--------------------------- ---------------------------
@@ -232,9 +232,9 @@ end
-- Link a trait to a specific body part -- Link a trait to a specific body part
StaticData.TRAITS_BP = { StaticData.TRAITS_BP = {
AmputeeHand = "Hand_L", Amputee_Hand = "Hand_L",
AmputeeLowerArm = "ForeArm_L", Amputee_ForeArm = "ForeArm_L",
AmputeeUpeerArm = "UpperArm_L" Amputee_UpperArm = "UpperArm_L"
} }
----------------- -----------------

View File

@@ -0,0 +1,49 @@
---Setups the custom TOC traits
local TRAITS = {
Amputee_Hand = "Amputee_Hand",
Amputee_ForeArm = "Amputee_ForeArm",
Amputee_UpperArm = "Amputee_UpperArm",
Insensitive = "Insensitive"
}
local function GetTraitText(trait)
return getText("UI_trait_" .. trait)
end
local function GetTraitDesc(trait)
return getText("UI_trait_" .. trait .. "_desc")
end
local function SetupTraits()
-- Perks.Left_Hand is defined in perks.txt
local traitsTable = {
[1] = TraitFactory.addTrait(TRAITS.Amputee_Hand, GetTraitText(TRAITS.Amputee_Hand), -8, GetTraitDesc(TRAITS.Amputee_Hand), false, false),
[2] = TraitFactory.addTrait(TRAITS.Amputee_ForeArm, GetTraitText(TRAITS.Amputee_ForeArm), -10, GetTraitDesc(TRAITS.Amputee_ForeArm), false, false),
[3] = TraitFactory.addTrait(TRAITS.Amputee_UpperArm, GetTraitText(TRAITS.Amputee_UpperArm), -20, GetTraitDesc(TRAITS.Amputee_UpperArm), false, false)
}
for i=1, #traitsTable do
---@type Trait
local t = traitsTable[i]
---@diagnostic disable-next-line: undefined-field
t:addXPBoost(Perks.Left_Hand, 4)
t:addXPBoost(Perks.Fitness, -1)
t:addXPBoost(Perks.Strength, -1)
end
TraitFactory.addTrait(TRAITS.Insensitive, GetTraitText(TRAITS.Insensitive), 6, GetTraitDesc(TRAITS.Insensitive), false, false)
TraitFactory.setMutualExclusive(TRAITS.Amputee_Hand, TRAITS.Amputee_ForeArm)
TraitFactory.setMutualExclusive(TRAITS.Amputee_Hand, TRAITS.Amputee_UpperArm)
TraitFactory.setMutualExclusive(TRAITS.Amputee_ForeArm, TRAITS.Amputee_UpperArm)
end
Events.OnGameBoot.Add(SetupTraits)

View File

@@ -1,8 +1,8 @@
UI_DE = { UI_DE = {
UI_trait_Amputee_Hand = "Amputierte linke Hand", UI_trait_Amputee_Hand = "Amputierte linke Hand",
UI_trait_Amputee_Hand_desc = "", UI_trait_Amputee_Hand_desc = "",
UI_trait_Amputee_LowerArm = "Amputierter linker Unterarm", UI_trait_Amputee_ForeArm = "Amputierter linker Unterarm",
UI_trait_Amputee_LowerArm_desc = "", UI_trait_Amputee_ForeArm_desc = "",
UI_trait_Amputee_UpperArm = "Amputierter linker Oberarm", UI_trait_Amputee_UpperArm = "Amputierter linker Oberarm",
UI_trait_Amputee_UpperArm_desc = "", UI_trait_Amputee_UpperArm_desc = "",
UI_trait_Insensitive = "Unempfindlich", UI_trait_Insensitive = "Unempfindlich",

View File

@@ -2,8 +2,8 @@ UI_EN = {
UI_trait_Amputee_Hand = "Amputated Left Hand", UI_trait_Amputee_Hand = "Amputated Left Hand",
UI_trait_Amputee_Hand_desc = "", UI_trait_Amputee_Hand_desc = "",
UI_trait_Amputee_LowerArm = "Amputated Left Forearm", UI_trait_Amputee_ForeArm = "Amputated Left Forearm",
UI_trait_Amputee_LowerArm_desc = "", UI_trait_Amputee_ForeArm_desc = "",
UI_trait_Amputee_UpperArm = "Amputated Left Upper arm", UI_trait_Amputee_UpperArm = "Amputated Left Upper arm",
UI_trait_Amputee_UpperArm_desc = "", UI_trait_Amputee_UpperArm_desc = "",

View File

@@ -0,0 +1,33 @@
ContextMenu_IT = {
ContextMenu_Amputate = "Amputa",
ContextMenu_Amputate_Bandage = "Amputa e fascia",
ContextMenu_Amputate_Stitch = "Amputa e metti i punti",
ContextMenu_Amputate_Stitch_Bandage = "Amputate, metti i punti e fascia",
ContextMenu_Cauterize = "Cauterizza",
ContextMenu_Limb_Hand_L = "Mano Sinistra",
ContextMenu_Limb_ForeArm_L = "Avambraccio Sinistro",
ContextMenu_Limb_UpperArm_L = "Braccio Superiore Sinistro",
ContextMenu_Limb_Hand_R = "Mano Destra",
ContextMenu_Limb_ForeArm_R = "Avambraccio Destro",
ContextMenu_Limb_UpperArm_R = "Braccio Superiore Destro",
ContextMenu_InstallProstRight = "Installa protesi sul braccio destro",
ContextMenu_InstallProstLeft = "Installa protesi sul braccio sinistro",
ContextMenu_PutTourniquetArmLeft = "Metti laccio emostatico sul braccio sinistro",
ContextMenu_PutTourniquetLegL = "Metti laccio emostatico sulla gamba sinistra",
ContextMenu_PutTourniquetArmRight = "Metti laccio emostatico sul braccio destro",
ContextMenu_PutTourniquetLegR = "Metti laccio emostatico sulla gamba destra",
ContextMenu_CleanWound = "Pulisci ferita",
ContextMenu_Admin_TOC = "TOC",
ContextMenu_Admin_ResetTOC = "Reset Amputations",
ContextMenu_Admin_ForceAmputation = "Force Amputation",
}

View File

@@ -0,0 +1,18 @@
IG_UI_IT = {
IGUI_perks_Amputations = "Amputazioni",
IGUI_perks_Side_R = "Parte destra",
IGUI_perks_Side_L = "Parte sinistra",
IGUI_perks_Prosthesis = "Protesi",
IGUI_perks_ProstFamiliarity= "Familiarità",
IGUI_ItemCat_Prosthesis = "Protesi",
IGUI_ItemCat_Surgery = "Operazioni mediche",
IGUI_ItemCat_Amputation = "Amputazione"
IGUI_HealthPanel_Cicatrization = "Cicatrizzazione",
IGUI_HealthPanel_Cicatrized = "Cicatrizzata",
IGUI_HealthPanel_Cauterized = "Cauterizzata",
IGUI_HealthPanel_WoundDirtyness = "Sporcizia della ferita",
IGUI_HealthPanel_ProstEquipped = "Protesi installata",
}

View File

@@ -0,0 +1,11 @@
ItemName_IT = {
ItemName_TOC.Surg_Arm_Tourniquet_L = "Laccio emostatico",
ItemName_TOC.Surg_Arm_Tourniquet_R = "Laccio emostatico",
ItemName_TOC.Prost_NormalArm_L = "Braccio Prostetico",
ItemName_TOC.Prost_NormalArm_R = "Braccio Prostetico",
ItemName_TOC.Prost_HookArm_L = "Braccio prostetico - Uncino",
ItemName_TOC.Prost_HookArm_R = "Braccio prostetico - Uncino",
}

View File

@@ -0,0 +1,4 @@
Recipes_IT = {
Recipe_Craft_Prosthetic_Arm = "Costruisci un braccio prostetico",
Recipe_Craft_Prosthetic_Hook = "Costruisci un braccio prostetico con uncino",
}

View File

@@ -0,0 +1,7 @@
Sandbox_IT = {
Sandbox_TOC = "The Only Cure",
Sandbox_TOC_CicatrizationSpeed = "Velocità cicatrizzazione",
Sandbox_TOC_WoundDirtynessMultiplier = "Moltiplicatore sporcizia ferita",
Sandbox_TOC_SurgeonAbilityImportance = "Importanza abilità medico",
}

View File

@@ -0,0 +1,10 @@
Tooltip_IT = {
Tooltip_Surgery_CantCauterize = "Non puoi cauterizzare la ferita",
Tooltip_Surgery_And = " e "
Tooltip_Surgery_TempTooLow = "La temperatura è troppo bassa",
Tooltip_Surgery_Coward = "Non sei abbastanza coraggioso",
Tooltip_Surgery_LimbNotFree = "Devi rimuovere la protesi",
}

View File

@@ -0,0 +1,16 @@
UI_IT = {
UI_trait_Amputee_Hand = "Mano Sinistra Amputata",
UI_trait_Amputee_Hand_desc = "",
UI_trait_Amputee_ForeArm = "Avambraccio Sinistro Amputato",
UI_trait_Amputee_ForeArm_desc = "",
UI_trait_Amputee_UpperArm = "Parte superiore del Braccio Sinistro Amputato",
UI_trait_Amputee_UpperArm_desc = "",
UI_trait_Insensitive = "Insensibile",
UI_trait_Insensitive_desc = "",
UI_Say_CantEquip = "Non posso equipaggiarlo..."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

View File

@@ -1,8 +1,8 @@
name=The Only Cure name=The Only Cure
poster=poster.png poster=poster.png
description=Bitten? Not a problem! description=You've been bitten. You have only two choices.
id=TheOnlyCure id=TheOnlyCure
icon=icon.png icon=icon.png
url=https://github.com/ZioPao/The-Only-Cure url=https://github.com/ZioPao/The-Only-Cure
modversion=2.0 modversion=2.0.7
pzversion=41.65 pzversion=41.65