91 Commits

Author SHA1 Message Date
ZioPao
68605ddb35 Bump to mod version 2024-10-19 18:37:02 +02:00
ZioPao
132ce24328 Added confirmation for amputations 2024-10-17 23:50:51 +02:00
ZioPao
4af127196c removed useless require 2024-10-17 23:22:08 +02:00
ZioPao
39bccaf895 Disabled other stuff 2024-10-12 19:51:21 +02:00
ZioPao
54c5958715 Disabled some non-functioning overrides 2024-10-12 19:17:46 +02:00
Pao
6b79ebf003 Merge pull request #139 from pllq/main
Ukrainian translation
2024-10-12 18:51:22 +02:00
ZioPao
75d5cb180d Fix to familiarity timing bug 2024-10-12 18:48:52 +02:00
pllq
245f81be26 updated Sandbox_UA.txt
added new translation for ukrainian langauge in Sandbox_UA.txt
2024-10-05 00:11:41 +03:00
pllq
1f950e0663 Delete media/lua/shared/Translate/UA/Sandbox_UA.txt
deleted old Sandbox_UA.txt
2024-10-05 00:11:05 +03:00
pllq
aecb5448ea translation for the UI file
added translation for the UI file
2024-10-05 00:09:44 +03:00
Pao
c02ac7dacc Merge pull request #123 from ZioPao/main
backmerge to dev since i'm an idiot part 2
2024-08-23 04:50:16 +02:00
ZioPao
332e02d9fd Fix to force reinit breaking actions in SP 2024-08-23 04:48:15 +02:00
ZioPao
a2b3db6793 Fix to unequip 2024-08-23 04:48:03 +02:00
Pao
6883c81322 Merge pull request #121 from ZioPao/dev
v2.1.3
2024-08-23 03:04:44 +02:00
ZioPao
600c55f5f8 Bump to mod version 2024-08-23 03:03:01 +02:00
ZioPao
bf20aec94f Fixed admin commands in sp 2024-08-23 02:53:04 +02:00
Pao
26c269e8ba Update README.md 2024-08-21 22:21:19 +02:00
ZioPao
db42feed35 Added exceptions for various equipping actions 2024-08-21 17:23:12 +02:00
ZioPao
10f97cffa1 Fixed picking up broken glass with item in off hand 2024-08-21 13:21:09 +02:00
ZioPao
22e6621300 Removed unused param 2024-08-21 12:36:04 +02:00
ZioPao
4d2b62e4a1 Error check for OnProsthesisEquipped 2024-08-21 01:55:59 +02:00
Pao
dd0d48cd08 Merge pull request #114 from ZioPao/dev
v2.1.2
2024-08-17 19:27:58 +02:00
ZioPao
2de512b447 Bump mod version 2024-08-17 19:26:41 +02:00
ZioPao
1765d82ad9 Implemented Forced Amputation and some additional safety checks 2024-08-17 19:15:58 +02:00
ZioPao
c52cca09f1 Merge branch 'dev' of https://github.com/ZioPao/The-Only-Cure into dev 2024-08-17 18:26:14 +02:00
ZioPao
545d5c7573 Implemented random limb side for Zombie Amputations 2024-08-17 18:26:13 +02:00
ZioPao
99d0847caa Updated workshop.txt 2024-08-17 16:51:37 +02:00
Pao
3b7fe2ec58 Merge pull request #113 from ZioPao/dev
v2.1.1
2024-08-17 16:48:59 +02:00
Pao
92fc80095d Merge pull request #109 from Pgmbru/Pgmbru-patch-FR
Pgmbru patch fr
2024-08-17 16:48:09 +02:00
ZioPao
751e36cba7 Fixed zombie amputations not enabling 2024-08-17 16:47:28 +02:00
Pgmbru
487e616801 Update Sandbox_FR.txt
Add Translate + Update Sandbox_FR
2024-08-12 03:04:07 +01:00
Pao
d5915cd287 Merge pull request #105 from ZioPao/dev
v2.1
2024-08-11 16:15:53 +02:00
ZioPao
d1f63ab55f Removed broken linter 2024-08-11 16:15:38 +02:00
ZioPao
857515576e Disabled zombies amputations by default 2024-08-11 16:14:05 +02:00
ZioPao
69c6177be5 Test linter 2024-08-11 13:32:28 +02:00
Pgmbru
38fa63324c Add Translate FR 2024-08-06 00:05:11 +01:00
ZioPao
940f5486aa Added toggle for Zombie amputations 2024-07-15 12:19:11 +02:00
ZioPao
11622795e1 Working zombies amputations once again 2024-07-15 12:15:35 +02:00
ZioPao
cb7603feea Bump to mod.info (2.1) 2024-07-15 10:59:40 +02:00
ZioPao
7b5e8e15d5 Added failsafe for iMeds compat 2024-07-15 10:59:15 +02:00
ZioPao
9a74710c1e Added preliminary compatibility with iMeds 2024-07-15 10:54:34 +02:00
ZioPao
3f7bf56be2 Added translation credits 2024-07-13 19:33:07 +02:00
Pao
501c2fbabd Merge pull request #93 from ZioPao/dev
v2.0.15
2024-07-13 19:30:30 +02:00
ZioPao
3dad820014 Fixed cicatrization force-applying with traits 2024-07-13 19:27:55 +02:00
ZioPao
2f82021b5d Fixed bandage\stitches action after amputation applying to wrong body part 2024-07-13 19:13:18 +02:00
ZioPao
a3287bb2bc Fixed suture needle causing an error 2024-07-13 19:07:17 +02:00
ZioPao
6c82f1a96f Add logs for traits 2024-07-13 18:57:16 +02:00
ZioPao
f2446c0781 Added script to automatically bump version 2024-07-13 18:57:10 +02:00
ZioPao
4d6ee68054 bump to mod.info 2024-07-13 17:27:48 +02:00
ZioPao
854238ef98 Merge branch 'dev' of https://github.com/ZioPao/The-Only-Cure into dev 2024-07-13 17:26:06 +02:00
ZioPao
2b41745a05 Added Korean translation 2024-07-13 17:26:04 +02:00
Pao
c2e15151a6 Merge pull request #84 from pllq/main
Added Ukrainian localization
2024-06-09 23:28:33 +02:00
pllq
b25f52a40d Add files via upload
Ukrainian localization for the TOC mod
2024-06-09 23:54:59 +03:00
Pao
145ec33620 Merge pull request #80 from ZioPao/dev
v2.0.14
2024-06-04 09:53:14 +02:00
ZioPao
307e19fd52 Fixed bug with saws 2024-06-04 09:48:17 +02:00
Pao
9eb9ea0263 Merge pull request #79 from ZioPao/dev
v2.0.13
2024-06-03 21:59:48 +02:00
ZioPao
f8fc8e7f60 Merge branch 'dev' of https://github.com/ZioPao/The-Only-Cure into dev 2024-06-03 21:59:15 +02:00
ZioPao
3d1cfba85c bump to mod.info 2024-06-03 21:59:13 +02:00
Pao
1f6fc63f46 Merge pull request #78 from Rinary1/main
RU Translate For This Mod
2024-06-03 21:57:45 +02:00
Rinary
3931c0ed4d Add files via upload 2024-06-03 20:29:43 +03:00
Pao
74eb7763cf Merge pull request #75 from ZioPao/dev
v2.0.12
2024-05-31 10:53:56 +02:00
ZioPao
e0e65c022f bump to mod.info 2024-05-31 10:52:24 +02:00
ZioPao
574526aca0 Fix: traits handling 2024-05-31 10:50:51 +02:00
ZioPao
29f21decb4 hotfix to traits 2024-05-31 10:37:39 +02:00
ZioPao
b70dd619a4 Disabled insensitive trait for now 2024-05-28 10:01:03 +02:00
Pao
0d5814dad1 Merge pull request #72 from ZioPao/dev
v2.0.11
2024-05-15 13:14:52 +02:00
ZioPao
5f64ead354 Fixed disabling of Interact Key 2024-05-15 13:13:27 +02:00
ZioPao
80c8b22faa lock for HandleDamage not disengaging in some cases 2024-05-15 11:52:45 +02:00
ZioPao
696edc1f1d Fixed wrong skin color for amputations 2024-05-15 11:47:28 +02:00
ZioPao
a1336c8d2e Destroying the instance will break stuff 2024-05-15 11:38:08 +02:00
Pao
1187f1a1b6 Merge pull request #60 from ZioPao/dev
disabled useless log
2024-05-09 14:52:00 +02:00
ZioPao
dc921e4c04 disabled useless log 2024-05-09 14:50:48 +02:00
Pao
bff6a2d4d6 Merge pull request #59 from ZioPao/dev
v2.0.10
2024-05-09 14:47:52 +02:00
ZioPao
36c3418a3e bump to mod version 2024-05-09 14:47:28 +02:00
ZioPao
a7e32bc69b Fix to HandleDamage breaking when getIsIgnoredPartInfected==true 2024-05-09 14:46:31 +02:00
Pao
139ad5828a Merge pull request #55 from ZioPao/dev
v2.0.9
2024-05-07 17:14:53 +02:00
ZioPao
1636ca7e13 bump to mod version 2024-05-07 17:13:56 +02:00
ZioPao
46a8ddc207 Added some tests 2024-05-07 17:13:27 +02:00
ZioPao
26e0324f4a Fixed bug related to BodyLocations that would break equipping prosthesis 2024-05-07 17:04:32 +02:00
ZioPao
c1faeeaabf Potential fix to bleeding issue 2024-05-07 16:58:55 +02:00
Pao
a93c8a56bb Merge pull request #51 from ZioPao/dev
v2.0.8
2024-05-06 22:07:00 +02:00
ZioPao
b33cee7271 hotfix 2024-05-06 22:06:26 +02:00
Pao
6e68e12b60 Merge pull request #49 from ZioPao/dev
v2.0.7
2024-05-06 17:15:27 +02:00
Pao
99dbaea143 Update issue templates 2024-05-06 10:15:31 +02:00
Pao
a09a1520a1 Update issue templates 2024-05-06 01:28:35 +02:00
Pao
6458988cc8 Merge pull request #45 from ZioPao/dev
v2.0.6
2024-05-05 18:05:06 +02:00
Pao
ba4f161122 Merge pull request #44 from ZioPao/dev
v2.0.5
2024-05-05 17:02:45 +02:00
Pao
df8591b1ef Merge pull request #42 from ZioPao/dev
Added ProstFamiliarity in adjustMaxTime function
2024-05-05 13:57:31 +02:00
Pao
91a6eb7763 Merge pull request #41 from ZioPao/dev
v2.0.4
2024-05-05 13:52:02 +02:00
Pao
1a754f4012 Merge pull request #38 from ZioPao/dev
bump to modversion
2024-05-05 01:01:35 +02:00
Pao
74bb34bbc8 Merge pull request #37 from ZioPao/dev
v2.0.3
2024-05-05 00:50:22 +02:00
63 changed files with 1415 additions and 387 deletions

24
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,24 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ZioPao
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Logs**
Please upload your game logs too. Server logs are useful too.
**Additional context**
Add any other context about the problem here.

View File

@@ -1,5 +1,6 @@
{ {
"todo-tree.tree.scanMode": "workspace", "todo-tree.tree.scanMode": "workspace",
"mod_id": "3236152598",
"zomboid_user_folder": "C:/Users/picch/Zomboid", "zomboid_user_folder": "C:/Users/picch/Zomboid",
"zomboid_folder": "E:\\Steam\\steamapps\\common\\ProjectZomboid", "zomboid_folder": "E:\\Steam\\steamapps\\common\\ProjectZomboid",
"zomboid_server_folder": "E:\\Steam\\steamapps\\common\\Project Zomboid Dedicated Server", "zomboid_server_folder": "E:\\Steam\\steamapps\\common\\Project Zomboid Dedicated Server",
@@ -8,4 +9,8 @@
"zombie", "zombie",
"_" "_"
], ],
"Lua.format.defaultConfig": {
"indent_style": "space",
"indent_size": "2"
}
} }

15
.vscode/tasks.json vendored
View File

@@ -9,6 +9,12 @@
"options": {"statusbar": {"label": "$(combine) Assemble Mod"}}, "options": {"statusbar": {"label": "$(combine) Assemble Mod"}},
"command": "python ${config:zomboid_user_folder}/PaosCrap/make_workshop_pack.py picch ${workspaceFolderBasename}", "command": "python ${config:zomboid_user_folder}/PaosCrap/make_workshop_pack.py picch ${workspaceFolderBasename}",
}, },
{
"label": "Bump Mod Version",
"type": "shell",
"options": {"statusbar": {"label": "$(arrow-up) Bump Mod Version"}},
"command": "python ${config:zomboid_user_folder}/PaosCrap/bump_version.py media/lua/client/TOC/Main.lua",
},
{ {
"label": "Run Zomboid Debug No Steam", "label": "Run Zomboid Debug No Steam",
"type": "shell", "type": "shell",
@@ -59,6 +65,15 @@
"problemMatcher": [ "problemMatcher": [
"$eslint-stylish" "$eslint-stylish"
] ]
},
{
"label": "Run Zomboid Test Server 3",
"options": {"statusbar": {"label": "$(run) Zomboid Server (TOC+FH+BH+iMedsFixed)"}},
"type": "shell",
"command":"\"${config:zomboid_server_folder}\\StartServer64_nosteam_custom.bat\" TOC_FH_BH_imeds",
"problemMatcher": [
"$eslint-stylish"
]
} }
] ]
} }

View File

@@ -12,8 +12,6 @@ Wait until you succumb to the virus or take matters into your hands. Cut off tha
This version of **The Only Cure** has been rebuilt from scratch to support future additions and to feel as close as possible as a vanilla mechanic. This version of **The Only Cure** has been rebuilt from scratch to support future additions and to feel as close as possible as a vanilla mechanic.
**The older version will be delisted shortly and it will not be supported anymore.**
Supports **Single Player** and **Multiplayer**! Supports **Single Player** and **Multiplayer**!
# Setup # Setup

View File

@@ -1,56 +0,0 @@
0.9.15
- Hotfix to sound not stopping when performing an amputation on another player
0.9.14
- Optimizations
- New icon for the mod
- Added a dirtyness malus for cicatrization, you'll have to keep your wound clean while it's healing (wash it you animal)
- When trying to equip a prosthesis from the inventory you'll get a message to let you know that you're doing it wrong
- Added explicit support to Left Is Right, making it work a lot better when you've got an amputated right limb
0.9.13
- Some little optimizations
- Fixed a bug that made Fancy Handwork compat not work as intended
0.9.12
- Fixed traits icons
- Some cleaning to the code
0.9.11
- Hotfix to cicatrization visuals
0.9.10
- Modified textures once again
0.9.9
- Modified textures for amputations
- Fixed a bug that caused some problems when amputating an already amputated limb
0.9.8
- New textures for amputations
- When cicatrized, an amputation will not be bloody anymore (visual thing only)
0.9.7
- Now amputations should spawn in every case, damn hairs. Thanks Dev for the tip!
0.9.6
- Fixed a bug when handling zombie hitting amputated limbs
- Fixed sound looping incessantly after amputating a limb
- Changed how phantom pain occurs
- Fixes to new hook models
0.9.5
- No more Right Click menu. You'll have to use the TOC Menu from the Medical Check, except for Cheats
- Fixes to how amputations are handled
before and after
- Compatibility fixes with older versions
- Various fixes to Traits
- New heavily WIP models for metal and wooden hooks
- Readded sounds during amputations
- Fixed banages not getting applied after an amputation when they were in the player's inventory

View File

@@ -0,0 +1,32 @@
#!/bin/bash
# Open the Main.lua file in read mode
while IFS= read -r line; do
# Check if the current line contains _version
if [[ $line == *_version* ]]; then
# Get the current version number from the line
current_version=$(echo $line | cut -d '"' -f 2)
# Increment the version number by 1
new_version=$((current_version + 1))
# Replace the old version number with the new one in the file
echo "$line" | sed "s/$current_version/$new_version/g" > media/lua/client/Main.lua
fi
done < media/lua/client/Main.lua
# Open the mod.info file in read mode
while IFS= read -r line; do
# Check if the current line contains modversion
if [[ $line == *modversion* ]]; then
# Get the current version number from the line
current_version=$(echo $line | cut -d '"' -f 2)
# Increment the version number by 1
new_version=$((current_version + 1))
# Replace the old version number with the new one in the file
echo "$line" | sed "s/$current_version/$new_version/g" > mod.info
fi
done < mod.info

View File

@@ -1,4 +1,5 @@
local CommandsData = require("TOC/CommandsData") local CommandsData = require("TOC/CommandsData")
local ClientRelayCommands = require("TOC/ClientRelayCommands")
local StaticData = require("TOC/StaticData") local StaticData = require("TOC/StaticData")
local DataController = require("TOC/Controllers/DataController") local DataController = require("TOC/Controllers/DataController")
------------------- -------------------
@@ -7,7 +8,8 @@ local DataController = require("TOC/Controllers/DataController")
---@param context ISContextMenu ---@param context ISContextMenu
---@param worldobjects table ---@param worldobjects table
local function AddAdminTocOptions(playerNum, context, worldobjects) local function AddAdminTocOptions(playerNum, context, worldobjects)
if not isAdmin() then return end
if (isClient() and not isDebugEnabled()) or isAdmin() then return end
local players = {} local players = {}
for _, v in ipairs(worldobjects) do for _, v in ipairs(worldobjects) do
@@ -40,9 +42,35 @@ local function AddAdminTocOptions(playerNum, context, worldobjects)
context:addSubMenu(option, subMenu) context:addSubMenu(option, subMenu)
subMenu:addOption(getText("ContextMenu_Admin_ResetTOC"), nil, function() subMenu:addOption(getText("ContextMenu_Admin_ResetTOC"), nil, function()
sendClientCommand(CommandsData.modules.TOC_RELAY, CommandsData.server.Relay.RelayExecuteInitialization, if isClient() then
{ patientNum = clickedPlayerNum }) sendClientCommand(CommandsData.modules.TOC_RELAY, CommandsData.server.Relay.RelayExecuteInitialization,
{ patientNum = clickedPlayerNum })
else
-- TODO ugly
ClientRelayCommands.ReceiveExecuteInitialization()
end
end) end)
-- Force amputation
local forceAmpOption = subMenu:addOption(getText("ContextMenu_Admin_ForceAmputation"), nil, nil)
local forceAmpSubMenu = ISContextMenu:getNew(subMenu)
context:addSubMenu(forceAmpOption, forceAmpSubMenu)
for i = 1, #StaticData.LIMBS_STR do
local limbName = StaticData.LIMBS_STR[i]
local limbTranslatedName = getText("ContextMenu_Limb_" .. limbName)
forceAmpSubMenu:addOption(limbTranslatedName, nil, function()
if isClient() then
sendClientCommand(CommandsData.modules.TOC_RELAY, CommandsData.server.Relay.RelayForcedAmputation,
{ patientNum = clickedPlayerNum, limbName = limbName })
else
ClientRelayCommands.ReceiveExecuteAmputationAction({surgeonNum=clickedPlayerNum, limbName=limbName, damagePlayer=false})
-- todo ugly
end
end)
end
end end
end end
Events.OnFillWorldObjectContextMenu.Add(AddAdminTocOptions) Events.OnFillWorldObjectContextMenu.Add(AddAdminTocOptions)

View File

@@ -1,5 +1,6 @@
local CommandsData = require("TOC/CommandsData") local CommandsData = require("TOC/CommandsData")
local AmputationHandler = require("TOC/Handlers/AmputationHandler") local AmputationHandler = require("TOC/Handlers/AmputationHandler")
local DataController = require("TOC/Controllers/DataController")
-------------------------------------------- --------------------------------------------
local ClientRelayCommands = {} local ClientRelayCommands = {}
@@ -25,8 +26,14 @@ end
---Creates a new handler and execute the amputation function on this client ---Creates a new handler and execute the amputation function on this client
---@param args receiveExecuteAmputationActionParams ---@param args receiveExecuteAmputationActionParams
function ClientRelayCommands.ReceiveExecuteAmputationAction(args) function ClientRelayCommands.ReceiveExecuteAmputationAction(args)
-- Check if player already doesn't have that limb or it's a dependant limb.
-- Mostly a check for admin forced amputations more than anything else, since this case is handled in the GUI already.
local dcInst = DataController.GetInstance()
if dcInst:getIsCut(args.limbName) then return end
local handler = InitAmputationHandler(args.limbName, args.surgeonNum) local handler = InitAmputationHandler(args.limbName, args.surgeonNum)
handler:execute(true) handler:execute(args.damagePlayer)
end end
@@ -46,6 +53,15 @@ function ClientRelayCommands.ReceiveExecuteInitialization()
LocalPlayerController.InitializePlayer(true) LocalPlayerController.InitializePlayer(true)
end end
---Creates a new handler and execute the amputation function on this client
---@param args receiveForcedCicatrizationParams
function ClientRelayCommands.ReceiveForcedCicatrization(args)
local dcInst = DataController.GetInstance()
--dcInst:setCicatrizationTime(args.limbName, 1)
dcInst:setIsCicatrized(args.limbName, true)
dcInst:apply()
end
------------------------- -------------------------
local function OnServerRelayCommand(module, command, args) local function OnServerRelayCommand(module, command, args)
@@ -56,3 +72,6 @@ local function OnServerRelayCommand(module, command, args)
end end
Events.OnServerCommand.Add(OnServerRelayCommand) Events.OnServerCommand.Add(OnServerRelayCommand)
-- TODO temporary
return ClientRelayCommands

View File

@@ -26,7 +26,7 @@ end
---Returns full name for the side, to be used with BodyLocations ---Returns full name for the side, to be used with BodyLocations
---@param side string ---@param side string
---@return string ---@return string?
function CommonMethods.GetSideFull(side) function CommonMethods.GetSideFull(side)
if side == 'R' then if side == 'R' then
return "Right" return "Right"

View File

@@ -1,39 +1,75 @@
---@class Compat
---@field handlers table<string, {fun : function, isActive : boolean}>
local Compat = {
handlers = {}
}
local function HandleModCompatibility() --- Brutal hands has a TOC_COMPAT but its check is wrong and uses an old API.
function Compat.BrutalHandwork()
BrutalHands = BrutalHands or {}
BrutalHands.TOC = require("TOC/API")
end
--- Was handled inside old TOC
local activatedMods = getActivatedMods() function Compat.FancyHandwork()
TOC_DEBUG.print("Checking for mods compatibility") require("TimedActions/FHSwapHandsAction")
local og_FHSwapHandsAction_isValid = FHSwapHandsAction.isValid
--[[ function FHSwapHandsAction:isValid()
Brutal hands has a TOC_COMPAT but its check is wrong and uses an old API. local tocApi = require("TOC/API")
]] if tocApi.hasBothHands(self.character) then
if activatedMods:contains('BrutalHandwork') then return og_FHSwapHandsAction_isValid(self)
TOC_DEBUG.print("found BrutalHandwork, activating compat module") else
BrutalHands = BrutalHands or {} return false
BrutalHands.TOC = require("TOC/API")
end
--[[
Was handled inside old TOC
]]
if activatedMods:contains('FancyHandwork') then
TOC_DEBUG.print("found FancyHandwork, activating compat module")
require("TimedActions/FHSwapHandsAction")
local og_FHSwapHandsAction_isValid = FHSwapHandsAction.isValid
function FHSwapHandsAction:isValid()
local tocApi = require("TOC/API")
if tocApi.hasBothHands(self.character) then
return og_FHSwapHandsAction_isValid(self)
else
return false
end
end end
end end
end end
Events.OnGameStart.Add(HandleModCompatibility)
function Compat.iMeds()
require("Component/Interface/Service/ContextMenu/Menu/HealthPanel/HealthPanelMenuInitializer")
-- placeholder, in case we need to do something more drastic with it.
end
------------------------------
Compat.handlers = {
["BrutalHandwork"] = {
fun = Compat.BrutalHandwork,
isActive = false},
["FancyHandwork"] = {
fun = Compat.FancyHandwork,
isActive = false},
-- either or
['iMeds'] = {
fun = Compat.iMeds,
isActive = false},
['iMedsFixed'] = {
fun = Compat.iMeds,
isActive = false}
}
function Compat.RunModCompatibility()
local activatedMods = getActivatedMods()
TOC_DEBUG.print("Checking for mods compatibility")
for k, modCompatHandler in pairs(Compat.handlers) do
if activatedMods:contains(k) then
TOC_DEBUG.print("Found " .. k .. ", running compatibility handler")
modCompatHandler.fun()
modCompatHandler.isActive = true
end
end
end
Events.OnGameStart.Add(Compat.RunModCompatibility)
return Compat

View File

@@ -97,6 +97,7 @@ function DataController:setup(key)
-- self.tocData.isInitializing = false -- self.tocData.isInitializing = false
-- ModData.add(key, self.tocData) -- ModData.add(key, self.tocData)
triggerEvent("OnSetupTocData")
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
@@ -381,9 +382,9 @@ function DataController.ReceiveData(key, data)
TOC_DEBUG.print("ReceiveData for " .. key) TOC_DEBUG.print("ReceiveData for " .. key)
if data == nil or data.limbs == nil then -- if data == nil or data.limbs == nil then
TOC_DEBUG.print("Data is nil, new character or something is wrong") -- 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
local username = key:sub(5) local username = key:sub(5)
@@ -407,7 +408,7 @@ function DataController.ReceiveData(key, data)
end end
handler:applyOnlineData(data) handler:applyOnlineData(data)
elseif username == getPlayer():getUsername() then elseif username == getPlayer():getUsername() then
TOC_DEBUG.print("loading local data") TOC_DEBUG.print("Trying to load local data or no data is available")
handler:tryLoadLocalData(key) handler:tryLoadLocalData(key)
end end
@@ -415,8 +416,11 @@ function DataController.ReceiveData(key, data)
handler:setIsResetForced(false) handler:setIsResetForced(false)
handler:setIsDataReady(true) handler:setIsDataReady(true)
--TOC_DEBUG.print("Finished ReceiveData, triggering OnReceivedTocData")
triggerEvent("OnReceivedTocData", handler.username) triggerEvent("OnReceivedTocData", handler.username)
-- TODO We need an event to track if initialization has been finalized
-- if username == getPlayer():getUsername() and not handler.isResetForced then -- if username == getPlayer():getUsername() and not handler.isResetForced then

View File

@@ -17,10 +17,12 @@ ItemsController.Player = {}
---@return number ---@return number
---@private ---@private
function ItemsController.Player.GetAmputationTexturesIndex(playerObj, isCicatrized) function ItemsController.Player.GetAmputationTexturesIndex(playerObj, isCicatrized)
-- FIX Broken
local textureString = playerObj:getHumanVisual():getSkinTexture() local textureString = playerObj:getHumanVisual():getSkinTexture()
local isHairy = textureString:sub(-1) == "a" local isHairy = textureString:sub(-1) == "a"
local matchedIndex = tonumber(textureString:match("%d$")) or 0 local matchedIndex = tonumber(textureString:match("%d%d")) -- it must always be at least 1
TOC_DEBUG.print("Texture string: " .. tostring(textureString))
if isHairy then if isHairy then
matchedIndex = matchedIndex + 5 matchedIndex = matchedIndex + 5
@@ -30,7 +32,7 @@ function ItemsController.Player.GetAmputationTexturesIndex(playerObj, isCicatriz
matchedIndex = matchedIndex + (isHairy and 5 or 10) -- We add 5 is it's the texture, else 10 matchedIndex = matchedIndex + (isHairy and 5 or 10) -- We add 5 is it's the texture, else 10
end end
TOC_DEBUG.print("isCicatrized= " .. tostring(isCicatrized)) TOC_DEBUG.print("isCicatrized = " .. tostring(isCicatrized))
TOC_DEBUG.print("Amputation Texture Index: " .. tostring(matchedIndex)) TOC_DEBUG.print("Amputation Texture Index: " .. tostring(matchedIndex))
return matchedIndex - 1 return matchedIndex - 1
end end
@@ -154,6 +156,9 @@ function ItemsController.Zombie.SpawnAmputationItem(zombie, amputationFullType)
-- Spawn the item too in the inventory to keep track of stuff this way. It's gonna get deleted when we reload the game -- Spawn the item too in the inventory to keep track of stuff this way. It's gonna get deleted when we reload the game
local zombieInv = zombie:getInventory() local zombieInv = zombie:getInventory()
zombieInv:AddItem(amputationFullType) zombieInv:AddItem(amputationFullType)
-- TODO Remove objects in that part of the body to prevent items floating in mid air
end end
function ItemsController.Zombie.GetAmputationTexturesIndex(zombie) function ItemsController.Zombie.GetAmputationTexturesIndex(zombie)

View File

@@ -32,12 +32,17 @@ function ISBaseTimedAction:adjustMaxTime(maxTime)
local time = og_ISBaseTimedAction_adjustMaxTime(self, maxTime) local time = og_ISBaseTimedAction_adjustMaxTime(self, maxTime)
-- Exceptions handling, if we find that parameter then we just use the original time -- Exceptions handling, if we find that parameter then we just use the original time
local queue = ISTimedActionQueue.getTimedActionQueue(getPlayer()) local actionsQueue = ISTimedActionQueue.getTimedActionQueue(getPlayer())
if queue and queue.current and queue.current.skipTOC then return time end
if actionsQueue and actionsQueue.current and actionsQueue.skipTOC then
--TOC_DEBUG.print("Should skip TOC stuff")
return time
end
-- Action is valid, check if we have any cut limb and then modify maxTime -- Action is valid, check if we have any cut limb and then modify maxTime
local dcInst = DataController.GetInstance() local dcInst = DataController.GetInstance()
if time ~= -1 and dcInst and dcInst:getIsAnyLimbCut() then if time ~= -1 and dcInst and dcInst:getIsAnyLimbCut() then
--TOC_DEBUG.print("Overriding adjustMaxTime")
local pl = getPlayer() local pl = getPlayer()
local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(pl:getUsername()) local amputatedLimbs = CachedDataHandler.GetAmputatedLimbs(pl:getUsername())
@@ -54,9 +59,22 @@ function ISBaseTimedAction:adjustMaxTime(maxTime)
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) TOC_DEBUG.print("Perk Level: " .. tostring(perkLevel))
TOC_DEBUG.print("OG time: " .. tostring(time))
-- Modified Time shouldn't EVER be lower compared to the og one.
local modifiedTime = time * (StaticData.LIMBS_TIME_MULTIPLIER_IND_NUM[limbName] - perkLevelScaled)
if modifiedTime >= time then
time = modifiedTime
end
--TOC_DEBUG.print("Modified time: " .. tostring(time))
end end
end end
--TOC_DEBUG.print("New time with amputations: " .. tostring(time))
return time return time
end end
@@ -74,7 +92,7 @@ function ISBaseTimedAction:perform()
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 local xp = self.maxTime / 100
for k, _ in pairs(amputatedLimbs) do for k, _ in pairs(amputatedLimbs) do
local limbName = k local limbName = k
@@ -92,7 +110,6 @@ function ISBaseTimedAction:perform()
if dcInst:getIsProstEquipped(limbName) then if dcInst:getIsProstEquipped(limbName) then
LocalPlayerController.playerObj:getXp():AddXP(Perks["ProstFamiliarity"], xp) LocalPlayerController.playerObj:getXp():AddXP(Perks["ProstFamiliarity"], xp)
end end
end end
end end
end end
@@ -115,6 +132,10 @@ function ISEquipWeaponAction:isValid()
local isPrimaryHandValid = CachedDataHandler.GetHandFeasibility(StaticData.SIDES_IND_STR.R) local isPrimaryHandValid = CachedDataHandler.GetHandFeasibility(StaticData.SIDES_IND_STR.R)
local isSecondaryHandValid = CachedDataHandler.GetHandFeasibility(StaticData.SIDES_IND_STR.L) local isSecondaryHandValid = CachedDataHandler.GetHandFeasibility(StaticData.SIDES_IND_STR.L)
-- Both hands are cut off, so it's impossible to equip in any way -- Both hands are cut off, so it's impossible to equip in any way
--TOC_DEBUG.print("isPrimaryHandValid : " .. tostring(isPrimaryHandValid))
--TOC_DEBUG.print("isSecondaryHandValid : " .. tostring(isSecondaryHandValid))
if not isPrimaryHandValid and not isSecondaryHandValid then if not isPrimaryHandValid and not isSecondaryHandValid then
isValid = false isValid = false
end end
@@ -123,9 +144,7 @@ function ISEquipWeaponAction:isValid()
end 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 function ISEquipWeaponAction:performWithAmputation()
function ISEquipWeaponAction:performWithAmputation(dcInst)
TOC_DEBUG.print("running ISEquipWeaponAction performWithAmputation") TOC_DEBUG.print("running ISEquipWeaponAction performWithAmputation")
local hand = nil local hand = nil
local otherHand = nil local otherHand = nil
@@ -204,12 +223,15 @@ local og_ISEquipWeaponAction_perform = ISEquipWeaponAction.perform
function ISEquipWeaponAction:perform() function ISEquipWeaponAction:perform()
og_ISEquipWeaponAction_perform(self) og_ISEquipWeaponAction_perform(self)
-- TODO Can we do it earlier?
--if self.character == getPlayer() then
local dcInst = DataController.GetInstance(self.character:getUsername()) local dcInst = DataController.GetInstance(self.character:getUsername())
-- Just check it any limb has been cut. If not, we can just return from here -- Just check it any limb has been cut. If not, we can just return from here
if dcInst:getIsAnyLimbCut() == true then if dcInst:getIsAnyLimbCut() then
self:performWithAmputation(dcInst) self:performWithAmputation()
end end
--end
end end
function ISInventoryPaneContextMenu.doEquipOption(context, playerObj, isWeapon, items, player) function ISInventoryPaneContextMenu.doEquipOption(context, playerObj, isWeapon, items, player)
@@ -269,6 +291,29 @@ function ISWorldObjectContextMenu.createMenu(player, worldobjects, x, y, test)
---@type ISContextMenu ---@type ISContextMenu
local ogContext = og_ISWorldObjectContextMenu_createMenu(player, worldobjects, x, y, test) local ogContext = og_ISWorldObjectContextMenu_createMenu(player, worldobjects, x, y, test)
-- goddamn it, zomboid devs. ogContext could be a boolean...
-- TBH, I don't really care about gamepad support, but all this method can break stuff. Let's just disable thisfor gamepad users.
if type(ogContext) == "boolean" or type(ogContext) == "string" then
return ogContext
end
-- The vanilla game doesn't count an item in the off hand as "equipped" for picking up glass. Let's fix that here
local brokenGlassOption = ogContext:getOptionFromName(getText("ContextMenu_RemoveBrokenGlass"))
if brokenGlassOption then
local playerObj = getSpecificPlayer(player)
if (CachedDataHandler.GetHandFeasibility(StaticData.SIDES_IND_STR.R) and playerObj:getPrimaryHandItem()) or
(CachedDataHandler.GetHandFeasibility(StaticData.SIDES_IND_STR.L) and playerObj:getSecondaryHandItem())
then
brokenGlassOption.notAvailable = false
brokenGlassOption.toolTip = nil -- This is active only when you can't do the action.
end
end
-- check if no hands, disable various interactions -- check if no hands, disable various interactions
if not CachedDataHandler.GetBothHandsFeasibility() then if not CachedDataHandler.GetBothHandsFeasibility() then
TOC_DEBUG.print("NO hands :((") TOC_DEBUG.print("NO hands :((")
@@ -283,8 +328,7 @@ function ISWorldObjectContextMenu.createMenu(player, worldobjects, x, y, test)
return ogContext return ogContext
end end
--* DISABLE WEARING CERTAIN ITEMS WHEN NO LIMB
--* DISABLE WEARING CERTAIN ITEMS WHEN NO LIMB
local function CheckLimbFeasibility(limbName) local function CheckLimbFeasibility(limbName)
local dcInst = DataController.GetInstance() local dcInst = DataController.GetInstance()
@@ -322,4 +366,4 @@ local og_ISClothingExtraAction_isValid = ISClothingExtraAction.isValid
---@diagnostic disable-next-line: duplicate-set-field ---@diagnostic disable-next-line: duplicate-set-field
function ISClothingExtraAction:isValid() function ISClothingExtraAction:isValid()
return WrapClothingAction(self, og_ISClothingExtraAction_isValid, InventoryItemFactory.CreateItem(self.extra)) return WrapClothingAction(self, og_ISClothingExtraAction_isValid, InventoryItemFactory.CreateItem(self.extra))
end end

View File

@@ -6,6 +6,8 @@ require("TOC/Events")
----------- -----------
-- Handle ONLY stuff for the local client -- Handle ONLY stuff for the local client
---@class LocalPlayerController ---@class LocalPlayerController
@@ -33,9 +35,6 @@ 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,24 +46,37 @@ function LocalPlayerController.InitializePlayer(isForced)
SetHealthPanelTOC() SetHealthPanelTOC()
end end
---Handles the traits ---Handles the traits
---@param playerObj IsoPlayer function LocalPlayerController.ManageTraits()
function LocalPlayerController.ManageTraits(playerObj)
-- Local player
local playerObj = getPlayer()
local AmputationHandler = require("TOC/Handlers/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 since they're exclusive -- Once we find one, we should be done since they're exclusive
TOC_DEBUG.print("Player has amputation trait " .. k .. ", executing it")
local tempHandler = AmputationHandler:new(v, playerObj) 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 -- The wound should be already cicatrized
local dcInst = DataController.GetInstance()
LocalPlayerController.HandleSetCicatrization(DataController.GetInstance(), playerObj, v) LocalPlayerController.HandleSetCicatrization(DataController.GetInstance(), playerObj, v)
dcInst:apply()
return return
end end
end end
end end
-- We need to manage traits when we're done setupping everything
-- It shouldn't be done every single time we initialize the player, fetching data, etc.
Events.OnSetupTocData.Add(LocalPlayerController.ManageTraits)
---------------------------------------------------------- ----------------------------------------------------------
--* Health *-- --* Health *--
@@ -127,8 +139,14 @@ function LocalPlayerController.TryRandomBleed(character, limbName)
if chance > normCicTime then if chance > normCicTime then
TOC_DEBUG.print("Triggered bleeding from non cicatrized wound") TOC_DEBUG.print("Triggered bleeding from non cicatrized wound")
local adjacentBodyPartType = BodyPartType[StaticData.LIMBS_ADJACENT_IND_STR[limbName]] local adjacentBodyPartType = BodyPartType[StaticData.LIMBS_ADJACENT_IND_STR[limbName]]
character:getBodyDamage():getBodyPart(adjacentBodyPartType):setBleeding(true)
character:getBodyDamage():getBodyPart(adjacentBodyPartType):setBleedingTime(20) -- we need to check if the wound is already bleeding before doing anything else to prevent issues with bandages
local bp = character:getBodyDamage():getBodyPart(adjacentBodyPartType)
bp:setBleedingTime(20) -- TODO Should depend on cicatrization instead of a fixed time
-- ADD Could break bandages if bleeding is too much?
--character:getBodyDamage():getBodyPart(adjacentBodyPartType):setBleeding(true)
end end
end end
@@ -141,9 +159,13 @@ LocalPlayerController.hasBeenDamaged = false
---Check if the player has in infected body part or if they have been hit in a cut area ---Check if the player has in infected body part or if they have been hit in a cut area
---@param character IsoPlayer|IsoGameCharacter ---@param character IsoPlayer|IsoGameCharacter
function LocalPlayerController.HandleDamage(character) function LocalPlayerController.HandleDamage(character)
-- TOC_DEBUG.print("Player got hit!") --TOC_DEBUG.print("Player got hit!")
-- TOC_DEBUG.print(damageType) -- TOC_DEBUG.print(damageType)
if character ~= getPlayer() then return end if character ~= getPlayer() then
-- Disable lock before doing anything else
LocalPlayerController.hasBeenDamaged = false
return
end
local bd = character:getBodyDamage() local bd = character:getBodyDamage()
local dcInst = DataController.GetInstance() local dcInst = DataController.GetInstance()
local modDataNeedsUpdate = false local modDataNeedsUpdate = false
@@ -173,18 +195,17 @@ function LocalPlayerController.HandleDamage(character)
-- Check other body parts that are not included in the mod, if there's a bite there then the player is fucked -- 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 -- 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 if not dcInst:getIsIgnoredPartInfected() then
for i = 1, #StaticData.IGNORED_BODYLOCS_BPT do
for i = 1, #StaticData.IGNORED_BODYLOCS_BPT do local bodyPartType = StaticData.IGNORED_BODYLOCS_BPT[i]
local bodyPartType = StaticData.IGNORED_BODYLOCS_BPT[i] local bodyPart = bd:getBodyPart(bodyPartType)
local bodyPart = bd:getBodyPart(bodyPartType) if bodyPart and (bodyPart:bitten() or bodyPart:IsInfected()) then
if bodyPart and (bodyPart:bitten() or bodyPart:IsInfected()) then dcInst:setIsIgnoredPartInfected(true)
dcInst:setIsIgnoredPartInfected(true) modDataNeedsUpdate = true
modDataNeedsUpdate = true end
end end
end end
-- TODO in theory should sync modData, but it's gonna be expensive as fuck. Figure it out
if modDataNeedsUpdate then if modDataNeedsUpdate then
dcInst:apply() dcInst:apply()
end end
@@ -193,17 +214,13 @@ function LocalPlayerController.HandleDamage(character)
LocalPlayerController.hasBeenDamaged = false LocalPlayerController.hasBeenDamaged = false
end end
---Setup HandleDamage, triggered by OnPlayerGetDamage ---Setup HandleDamage, triggered by OnPlayerGetDamage. To prevent a spam caused by this awful event, we use a bool lock
---@param character IsoPlayer|IsoGameCharacter ---@param character IsoPlayer|IsoGameCharacter
---@param damageType string ---@param damageType string
---@param damageAmount number ---@param damageAmount number
function LocalPlayerController.OnGetDamage(character, damageType, damageAmount) function LocalPlayerController.OnGetDamage(character, damageType, damageAmount)
-- TODO Check if other players in the online triggers this
if LocalPlayerController.hasBeenDamaged == false then if LocalPlayerController.hasBeenDamaged == false then
-- Start checks -- Start checks
-- TODO Add a timer before we can re-enable this bool?
LocalPlayerController.hasBeenDamaged = true LocalPlayerController.hasBeenDamaged = true
LocalPlayerController.HandleDamage(character) LocalPlayerController.HandleDamage(character)
end end
@@ -296,7 +313,7 @@ end
---@param playerObj IsoPlayer ---@param playerObj IsoPlayer
---@param limbName string ---@param limbName string
function LocalPlayerController.HandleSetCicatrization(dcInst, playerObj, limbName) function LocalPlayerController.HandleSetCicatrization(dcInst, playerObj, limbName)
TOC_DEBUG.print(tostring(limbName) .. " is cicatrized") TOC_DEBUG.print("Setting cicatrization to " .. tostring(limbName))
dcInst:setIsCicatrized(limbName, true) dcInst:setIsCicatrized(limbName, true)
dcInst:setCicatrizationTime(limbName, 0) dcInst:setCicatrizationTime(limbName, 0)

View File

@@ -2,3 +2,4 @@
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("OnProsthesisUnequipped")
LuaEventManager.AddEvent("OnReceivedTocData") -- Triggered when TOC data is ready LuaEventManager.AddEvent("OnReceivedTocData") -- Triggered when TOC data is ready
LuaEventManager.AddEvent("OnSetupTocData") -- Triggered when TOC has been setupped

View File

@@ -89,9 +89,12 @@ end
---@param stitchesItem InventoryItem ---@param stitchesItem InventoryItem
---@return ISStitch ---@return ISStitch
function AmputationHandler.PrepareStitchesAction(prevAction, limbName, surgeonPl, patientPl, stitchesItem) function AmputationHandler.PrepareStitchesAction(prevAction, limbName, surgeonPl, patientPl, stitchesItem)
local bptEnum = StaticData.LIMBS_TO_BODYLOCS_IND_BPT[limbName]
local bd = patientPl:getBodyDamage() local bd = patientPl:getBodyDamage()
local bodyPart = bd:getBodyPart(bptEnum)
-- we need the adjacent one, not the actual one
local adjacentLimb = StaticData.LIMBS_ADJACENT_IND_STR[limbName]
local bodyPart = bd:getBodyPart(BodyPartType[adjacentLimb])
local stitchesAction = ISStitch:new(surgeonPl, patientPl, stitchesItem, bodyPart, true) local stitchesAction = ISStitch:new(surgeonPl, patientPl, stitchesItem, bodyPart, true)
ISTimedActionQueue.addAfter(prevAction, stitchesAction) ISTimedActionQueue.addAfter(prevAction, stitchesAction)
@@ -106,9 +109,11 @@ end
---@param bandageItem InventoryItem ---@param bandageItem InventoryItem
---@return ISApplyBandage ---@return ISApplyBandage
function AmputationHandler.PrepareBandagesAction(prevAction, limbName, surgeonPl, patientPl, bandageItem) function AmputationHandler.PrepareBandagesAction(prevAction, limbName, surgeonPl, patientPl, bandageItem)
local bptEnum = StaticData.LIMBS_TO_BODYLOCS_IND_BPT[limbName]
local bd = patientPl:getBodyDamage() local bd = patientPl:getBodyDamage()
local bodyPart = bd:getBodyPart(bptEnum) -- we need the adjacent one, not the actual one
local adjacentLimb = StaticData.LIMBS_ADJACENT_IND_STR[limbName]
local bodyPart = bd:getBodyPart(BodyPartType[adjacentLimb])
local bandageAction = ISApplyBandage:new(surgeonPl, patientPl, bandageItem, bodyPart, true) local bandageAction = ISApplyBandage:new(surgeonPl, patientPl, bandageItem, bodyPart, true)
ISTimedActionQueue.addAfter(prevAction, bandageAction) ISTimedActionQueue.addAfter(prevAction, bandageAction)
@@ -151,7 +156,9 @@ function AmputationHandler:damageAfterAmputation(surgeonFactor)
patientStats:setStress(baseDamage - surgeonFactor) patientStats:setStress(baseDamage - surgeonFactor)
end end
---Execute the amputation ---Execute the amputation. This method doesn't check if the upper limb has been amputated or not, so if
--- somehow the method gets triggered and we're trying to cut off a part that doesn't really exist anymore,
--- it will still be executed. This is by design, additional checks must be made BEFORE running the AmputationHandler
---@param damagePlayer boolean ---@param damagePlayer boolean
function AmputationHandler:execute(damagePlayer) function AmputationHandler:execute(damagePlayer)
local surgeonFactor = self.surgeonPl:getPerkLevel(Perks.Doctor) * SandboxVars.TOC.SurgeonAbilityImportance local surgeonFactor = self.surgeonPl:getPerkLevel(Perks.Doctor) * SandboxVars.TOC.SurgeonAbilityImportance

View File

@@ -22,8 +22,6 @@ end
---Will calculate all the values that we need ---Will calculate all the values that we need
function CachedDataHandler.CalculateCacheableValues(username) function CachedDataHandler.CalculateCacheableValues(username)
CachedDataHandler.CalculateHighestAmputatedLimbs(username) CachedDataHandler.CalculateHighestAmputatedLimbs(username)
-- FIX This should be run ONLY on the actual client, never on other clients. Just a placeholder fix for now
if getPlayer():getUsername() == username then if getPlayer():getUsername() == username then
CachedDataHandler.CalculateBothHandsFeasibility() CachedDataHandler.CalculateBothHandsFeasibility()
end end
@@ -31,7 +29,6 @@ end
--* Amputated Limbs caching *-- --* Amputated Limbs caching *--
CachedDataHandler.amputatedLimbs = {} CachedDataHandler.amputatedLimbs = {}
@@ -120,11 +117,22 @@ CachedDataHandler.handFeasibility = {}
function CachedDataHandler.CalculateHandFeasibility(limbName) function CachedDataHandler.CalculateHandFeasibility(limbName)
local dcInst = DataController.GetInstance() local dcInst = DataController.GetInstance()
local side = CommonMethods.GetSide(limbName) local side = CommonMethods.GetSide(limbName)
-- TODO if we re run this too early, it might break everything after a forced re-init
CachedDataHandler.handFeasibility[side] = not dcInst:getIsCut(limbName) or dcInst:getIsProstEquipped(limbName) CachedDataHandler.handFeasibility[side] = not dcInst:getIsCut(limbName) or dcInst:getIsProstEquipped(limbName)
TOC_DEBUG.print("Calculated hand feasibility: " .. tostring(side))
end end
---@param side string Either "L" or "R"
---@return boolean
function CachedDataHandler.GetHandFeasibility(side) function CachedDataHandler.GetHandFeasibility(side)
-- FIX horrendous workaround, but with a forced init we run the caching too early and it breaks this, setting it to nil.
if CachedDataHandler.handFeasibility[side] == nil then
CachedDataHandler.CalculateBothHandsFeasibility()
end
return CachedDataHandler.handFeasibility[side] return CachedDataHandler.handFeasibility[side]
end end
@@ -133,16 +141,21 @@ function CachedDataHandler.CalculateBothHandsFeasibility()
CachedDataHandler.CalculateHandFeasibility("Hand_L") CachedDataHandler.CalculateHandFeasibility("Hand_L")
CachedDataHandler.CalculateHandFeasibility("Hand_R") CachedDataHandler.CalculateHandFeasibility("Hand_R")
local interactStr = "Interact" local interactStr = "Interact"
if CachedDataHandler.interactKey == nil or CachedDataHandler.interactKey == 0 then
CachedDataHandler.interactKey = getCore():getKey(interactStr)
end
if not CachedDataHandler.GetBothHandsFeasibility() then if not CachedDataHandler.GetBothHandsFeasibility() then
TOC_DEBUG.print("Disabling interact key") TOC_DEBUG.print("Disabling interact key")
TOC_DEBUG.print("Cached current key for interact: " .. tostring(CachedDataHandler.interactKey))
-- Cache the current key
CachedDataHandler.interactKey = getCore():getKey(interactStr)
getCore():addKeyBinding(interactStr, Keyboard.KEY_NONE) getCore():addKeyBinding(interactStr, Keyboard.KEY_NONE)
else else
TOC_DEBUG.print("Re-enabling interact key") TOC_DEBUG.print("Re-enabling interact key")
TOC_DEBUG.print("Cached current key for interact: " .. tostring(CachedDataHandler.interactKey))
if not CachedDataHandler.interactKey then CachedDataHandler.interactKey = getCore():getKey(interactStr) end
getCore():addKeyBinding(interactStr, CachedDataHandler.interactKey) getCore():addKeyBinding(interactStr, CachedDataHandler.interactKey)
end end
end end

View File

@@ -132,36 +132,26 @@ function ISClothingExtraAction:isValid()
return HandleProsthesisValidation(testItem, isEquippable) return HandleProsthesisValidation(testItem, isEquippable)
end end
--[[
local og_ISClothingExtraAction_perform = ISClothingExtraAction.perform
function ISClothingExtraAction:perform()
local extraItem = InventoryItemFactory.CreateItem(self.extra)
ProsthesisHandler.SearchAndSetupProsthesis(extraItem, true)
og_ISClothingExtraAction_perform(self)
end
local og_ISUnequipAction_perform = ISUnequipAction.perform
function ISUnequipAction:perform()
--[[
Horrendous workaround Horrendous workaround
To unequp items, the java side uses WornItems.setItem, which has To unequp items, the java side uses WornItems.setItem, which has
a check for multiItem. Basically, if it's active, it won't actually remove the item, a check for multiItem. Basically, if it's active, it won't actually remove the item,
fucking things up. So, to be 100% sure that we're removing the items, we're gonna fucking things up. So, to be 100% sure that we're removing the items, we're gonna
disable and re-enable the multi-item bool for the Unequip Action. disable and re-enable the multi-item bool for the Unequip Action.
]] ]]
local og_ISClothingExtraAction_perform = ISClothingExtraAction.perform
function ISClothingExtraAction:perform()
local extraItem = InventoryItemFactory.CreateItem(self.extra)
local isProst = ProsthesisHandler.SearchAndSetupProsthesis(extraItem, true)
local group
if isProst then
group = BodyLocations.getGroup("Human")
group:setMultiItem("TOC_ArmProst", false)
end
og_ISClothingExtraAction_perform(self)
if isProst then
group:setMultiItem("TOC_ArmProst", true)
end
end
local og_ISUnequipAction_perform = ISUnequipAction.perform
function ISUnequipAction:perform()
local isProst = ProsthesisHandler.SearchAndSetupProsthesis(self.item, false) local isProst = ProsthesisHandler.SearchAndSetupProsthesis(self.item, false)
local group local group
if isProst then if isProst then
@@ -178,7 +168,11 @@ function ISUnequipAction:perform()
local highestAmputatedLimbs = CachedDataHandler.GetHighestAmputatedLimbs(getPlayer():getUsername()) local highestAmputatedLimbs = CachedDataHandler.GetHighestAmputatedLimbs(getPlayer():getUsername())
if highestAmputatedLimbs then if highestAmputatedLimbs then
local hal = highestAmputatedLimbs[side] local hal = highestAmputatedLimbs[side]
triggerEvent("OnProsthesisUnequipped", hal) if hal then
-- This could break if amputated limbs aren't cached for some reason
triggerEvent("OnProsthesisUnequipped", hal)
end
end end
end end
end end

View File

@@ -6,7 +6,7 @@ require("TOC/Events")
---@class Main ---@class Main
local Main = { local Main = {
_version = "2.0.7" _version = "2.1.4"
} }
function Main.Start() function Main.Start()
@@ -58,8 +58,9 @@ function Main.WipeData(player)
-- Let's wipe the instance too just to be sure -- Let's wipe the instance too just to be sure
local DataController = require("TOC/Controllers/DataController") -- TODO This can break things I guess
DataController.DestroyInstance(username) --local DataController = require("TOC/Controllers/DataController")
--DataController.DestroyInstance(username)
end end

View File

@@ -145,6 +145,14 @@ end)
TestFramework.registerTestModule("Various", "Player", function() TestFramework.registerTestModule("Various", "Player", function()
local Tests = {} local Tests = {}
function Tests.BleedTest()
local pl = getPlayer()
--pl:getBodyDamage():getBodyPart(BodyPartType.ForeArm_R):setBleeding(true)
pl:getBodyDamage():getBodyPart(BodyPartType.ForeArm_R):setBleedingTime(20)
end
function Tests.Kill() function Tests.Kill()
getPlayer():Kill(getPlayer()) getPlayer():Kill(getPlayer())
end end

View File

@@ -1,10 +1,91 @@
-- TODO This section must be overhauled
-- local DataController = require("TOC/Controllers/DataController")
-- local StaticData = require("TOC/StaticData")
---@diagnostic disable: duplicate-set-field ---@diagnostic disable: duplicate-set-field
-- Bunch of actions shouldn't be modified by the adjusted time -- Bunch of actions shouldn't be modified by the adjusted time
-----------------------------------------------
---* Some actions have specific maxTime calculations and we must account for that
---ISAttachItemHotbar
---ISDetachItemHotbar
---ISEquipWeaponAction
---ISUnequipAction
-- --- We're forced to re-run this crap to fix it
-- ---@param action ISBaseTimedAction
-- local function HandleSpeedSpecificAction(action, time)
-- action.skipTOC = true
-- action.maxTime = time
-- action.animSpeed = 1.0
-- end
-- local og_ISAttachItemHotbar_new = ISAttachItemHotbar.new
-- function ISAttachItemHotbar:new(character, item, slot, slotIndex, slotDef)
-- local action = og_ISAttachItemHotbar_new(self, character, item, slot, slotIndex, slotDef)
-- HandleSpeedSpecificAction(action, -1)
-- return action
-- end
-- local og_ISDetachItemHotbar_new = ISDetachItemHotbar.new
-- function ISDetachItemHotbar:new(character, item)
-- local action = og_ISDetachItemHotbar_new(self, character, item)
-- HandleSpeedSpecificAction(action, -1)
-- return action
-- end
-- local og_ISEquipWeaponAction_new = ISEquipWeaponAction.new
-- function ISEquipWeaponAction:new(character, item, time, primary, twoHands)
-- local action = og_ISEquipWeaponAction_new(self, character, item, time, primary, twoHands)
-- TOC_DEBUG.print("Override ISEquipWeaponAction New")
-- -- check if right arm is cut off or not. if it is, penality shall apply
-- -- if we got here, the action is valid, so we know that we have a prosthesis.
-- local dcInst = DataController.GetInstance()
-- if not dcInst:getIsCut(StaticData.LIMBS_IND_STR.Hand_R) then
-- action.skipTOC = true
-- action.maxTime = time
-- action.animSpeed = 1.0
-- TOC_DEBUG.print("Skipping TOC for ISEquipWeaponAction new")
-- end
-- -- if not twoHands then
-- -- TOC_DEBUG.print("Not a two handed action, re-adding skip TOC")
-- -- HandleSpeedSpecificAction(action)
-- -- end
-- return action
-- end
-- local og_ISUnequipAction_new = ISUnequipAction.new
-- function ISUnequipAction:new(character, item, time)
-- local action = og_ISUnequipAction_new(self, character, item, time)
-- ---@cast item InventoryItem
-- -- For some reason (I have no clue why), if we re-run the method it breaks basically every unequip clothing action. Not for weapons though.
-- if instanceof(item, 'HandWeapon') then
-- --print("Running handlespeedspecificaction")
-- HandleSpeedSpecificAction(action)
-- end
-- return action
-- end
------------------------------------------------------
--- Normal cases
local og_ISEatFoodAction_new = ISEatFoodAction.new local og_ISEatFoodAction_new = ISEatFoodAction.new
function ISEatFoodAction:new(character, item, percentage) function ISEatFoodAction:new(character, item, percentage)
local action = og_ISEatFoodAction_new(self, character, item, percentage) local action = og_ISEatFoodAction_new(self, character, item, percentage)
TOC_DEBUG.print("Override ISEatFoodAction") --TOC_DEBUG.print("Override ISEatFoodAction")
action.skipTOC = true action.skipTOC = true
return action return action
end end
@@ -12,7 +93,7 @@ end
local og_ISReadABook_new = ISReadABook.new local og_ISReadABook_new = ISReadABook.new
function ISReadABook:new(character, item, time) function ISReadABook:new(character, item, time)
local action = og_ISReadABook_new(self, character, item, time) local action = og_ISReadABook_new(self, character, item, time)
TOC_DEBUG.print("Override ISReadABook") --TOC_DEBUG.print("Override ISReadABook")
action.skipTOC = true action.skipTOC = true
return action return action
end end
@@ -20,7 +101,7 @@ end
local og_ISTakePillAction_new = ISTakePillAction.new local og_ISTakePillAction_new = ISTakePillAction.new
function ISTakePillAction:new(character, item, time) function ISTakePillAction:new(character, item, time)
local action = og_ISTakePillAction_new(self, character, item, time) local action = og_ISTakePillAction_new(self, character, item, time)
TOC_DEBUG.print("Override ISTakePillAction") --TOC_DEBUG.print("Override ISTakePillAction")
action.skipTOC = true action.skipTOC = true
return action return action
end end
@@ -28,7 +109,7 @@ end
local og_ISTakeWaterAction_new = ISTakeWaterAction.new local og_ISTakeWaterAction_new = ISTakeWaterAction.new
function ISTakeWaterAction:new(character, item, waterUnit, waterObject, time, oldItem) function ISTakeWaterAction:new(character, item, waterUnit, waterObject, time, oldItem)
local action = og_ISTakeWaterAction_new(self, character, item, waterUnit, waterObject, time, oldItem) local action = og_ISTakeWaterAction_new(self, character, item, waterUnit, waterObject, time, oldItem)
TOC_DEBUG.print("Override ISTakeWaterAction") --TOC_DEBUG.print("Override ISTakeWaterAction")
action.skipTOC = true action.skipTOC = true
return action return action
end end
@@ -36,7 +117,7 @@ end
local og_ISDrinkFromBottle_new = ISDrinkFromBottle.new local og_ISDrinkFromBottle_new = ISDrinkFromBottle.new
function ISDrinkFromBottle:new(character, item, uses) function ISDrinkFromBottle:new(character, item, uses)
local action = og_ISDrinkFromBottle_new(self, character, item, uses) local action = og_ISDrinkFromBottle_new(self, character, item, uses)
TOC_DEBUG.print("Override ISDrinkFromBottle") --TOC_DEBUG.print("Override ISDrinkFromBottle")
action.skipTOC = true action.skipTOC = true
return action return action
end end
@@ -44,7 +125,7 @@ end
local og_ISFinalizeDealAction_new = ISFinalizeDealAction.new local og_ISFinalizeDealAction_new = ISFinalizeDealAction.new
function ISFinalizeDealAction:new(player, otherPlayer, itemsToGive, itemsToReceive, time) function ISFinalizeDealAction:new(player, otherPlayer, itemsToGive, itemsToReceive, time)
local action = og_ISFinalizeDealAction_new(self, player, otherPlayer, itemsToGive, itemsToReceive, time) local action = og_ISFinalizeDealAction_new(self, player, otherPlayer, itemsToGive, itemsToReceive, time)
TOC_DEBUG.print("Override ISFinalizeDealAction") --TOC_DEBUG.print("Override ISFinalizeDealAction")
action.skipTOC = true action.skipTOC = true
return action return action
end end
@@ -52,7 +133,7 @@ end
local og_ISCampingInfoAction_new = ISCampingInfoAction.new local og_ISCampingInfoAction_new = ISCampingInfoAction.new
function ISCampingInfoAction:new(character, campfireObject, campfire) function ISCampingInfoAction:new(character, campfireObject, campfire)
local action = og_ISCampingInfoAction_new(self, character, campfireObject, campfire) local action = og_ISCampingInfoAction_new(self, character, campfireObject, campfire)
TOC_DEBUG.print("Override ISCampingInfoAction") --TOC_DEBUG.print("Override ISCampingInfoAction")
action.skipTOC = true action.skipTOC = true
return action return action
end end

View File

@@ -0,0 +1,116 @@
---@class ConfirmationPanel : ISPanel
local ConfirmationPanel = ISPanel:derive("ConfirmationPanel")
---Starts a new confirmation panel
---@param x number
---@param y number
---@param width number
---@param height number
---@param alertText string
---@param onConfirmFunc function
---@return ConfirmationPanel
function ConfirmationPanel:new(x, y, width, height, alertText, parentPanel, onConfirmFunc)
local o = ISPanel:new(x, y, width, height)
setmetatable(o, self)
self.__index = self
o:initialise()
o.alertText = alertText
o.onConfirmFunc = onConfirmFunc
o.parentPanel = parentPanel
ConfirmationPanel.instance = o
---@cast o ConfirmationPanel
return o
end
function ConfirmationPanel:createChildren()
ISPanel.createChildren(self)
self.borderColor = { r = 1, g = 0, b = 0, a = 1 }
self.textPanel = ISRichTextPanel:new(0, 0, self.width, self.height)
self.textPanel:initialise()
self:addChild(self.textPanel)
self.textPanel.defaultFont = UIFont.Medium
self.textPanel.anchorTop = true
self.textPanel.anchorLeft = false
self.textPanel.anchorBottom = true
self.textPanel.anchorRight = false
self.textPanel.marginLeft = 0
self.textPanel.marginTop = 10
self.textPanel.marginRight = 0
self.textPanel.marginBottom = 0
self.textPanel.autosetheight = false
self.textPanel.background = false
self.textPanel:setText(self.alertText)
self.textPanel:paginate()
local yPadding = 10
local xPadding = self:getWidth() / 4
local btnWidth = 100
local btnHeight = 25
local yButton = self:getHeight() - yPadding - btnHeight
self.btnYes = ISButton:new(xPadding, yButton, btnWidth, btnHeight, "Yes", self, self.onClick)
self.btnYes.internal = "YES"
self.btnYes:initialise()
self.btnYes.borderColor = { r = 1, g = 0, b = 0, a = 1 }
self.btnYes:setEnable(true)
self:addChild(self.btnYes)
self.btnNo = ISButton:new(self:getWidth() - xPadding - btnWidth, yButton, btnWidth, btnHeight, "No", self,
self.onClick)
self.btnNo.internal = "NO"
self.btnNo:initialise()
self.btnNo:setEnable(true)
self:addChild(self.btnNo)
end
function ConfirmationPanel:onClick(btn)
if btn.internal == 'YES' then
self.onConfirmFunc(self.parentPanel)
self:close()
elseif btn.internal == 'NO' then
self:close()
end
end
-------------------------
---@param alertText string
---@param x any
---@param y any
---@param parentPanel any
---@param onConfirmFunc any
---@return ConfirmationPanel
function ConfirmationPanel.Open(alertText, x, y, parentPanel, onConfirmFunc)
local width = 500
local height = 120
local screenWidth = getCore():getScreenWidth()
local screenHeight = getCore():getScreenHeight()
-- Check for oversize
if x+width > screenWidth then
x = screenWidth - width
end
if y+height > screenHeight then
y = screenHeight - height
end
local panel = ConfirmationPanel:new(x, y, width, height, alertText, parentPanel, onConfirmFunc)
panel:initialise()
panel:addToUIManager()
panel:bringToTop()
return panel
end
function ConfirmationPanel.Close()
ConfirmationPanel.instance:close()
end
return ConfirmationPanel

View File

@@ -1,7 +1,7 @@
local StaticData = require("TOC/StaticData") local StaticData = require("TOC/StaticData")
local CommandsData = require("TOC/CommandsData")
local DataController = require("TOC/Controllers/DataController") local DataController = require("TOC/Controllers/DataController")
local CachedDataHandler = require("TOC/Handlers/CachedDataHandler") local CachedDataHandler = require("TOC/Handlers/CachedDataHandler")
local Compat = require("TOC/Compat")
local CutLimbInteractionHandler = require("TOC/UI/Interactions/CutLimbInteractionHandler") local CutLimbInteractionHandler = require("TOC/UI/Interactions/CutLimbInteractionHandler")
local WoundCleaningInteractionHandler = require("TOC/UI/Interactions/WoundCleaningInteractionHandler") local WoundCleaningInteractionHandler = require("TOC/UI/Interactions/WoundCleaningInteractionHandler")
@@ -11,6 +11,9 @@ local WoundCleaningInteractionHandler = require("TOC/UI/Interactions/WoundCleani
local isReady = false local isReady = false
function SetHealthPanelTOC() function SetHealthPanelTOC()
-- depending on compatibility
isReady = true isReady = true
end end
@@ -128,23 +131,23 @@ function ISHealthPanel:render()
end end
local og_ISHealthPanel_update = ISHealthPanel.update -- local og_ISHealthPanel_update = ISHealthPanel.update
function ISHealthPanel:update() -- function ISHealthPanel:update()
og_ISHealthPanel_update(self) -- og_ISHealthPanel_update(self)
-- TODO Listen for changes on other player side instead of looping this -- -- TODO Listen for changes on other player side instead of looping this
-- FIX Re-enable it, just for test -- -- FIX Re-enable it, just for test
if self.character then -- if self.character then
local locPlUsername = getPlayer():getUsername() -- local locPlUsername = getPlayer():getUsername()
local remPlUsername = self.character:getUsername() -- local remPlUsername = self.character:getUsername()
if locPlUsername ~= remPlUsername and self:isReallyVisible() then -- if locPlUsername ~= remPlUsername and self:isReallyVisible() then
-- Request update for TOC DATA -- -- Request update for TOC DATA
local key = CommandsData.GetKey(remPlUsername) -- local key = CommandsData.GetKey(remPlUsername)
--ModData.request(key) -- --ModData.request(key)
end -- end
end -- end
end -- end
@@ -252,10 +255,10 @@ end
local og_ISHealthPanel_getDamagedParts = ISHealthPanel.getDamagedParts local og_ISHealthPanel_getDamagedParts = ISHealthPanel.getDamagedParts
function ISHealthPanel:getDamagedParts() function ISHealthPanel:getDamagedParts()
-- TODO Overriding it is a lot easier, but ew -- check for imeds or if TOC is ready to display its stuff on the health panel
if isReady == false or Compat.handlers['iMeds'].isActive or Compat.handlers['iMedsFixed'].isActive then
if isReady then return og_ISHealthPanel_getDamagedParts(self)
elseif isReady then
local result = {} local result = {}
local bodyParts = self:getPatient():getBodyDamage():getBodyParts() local bodyParts = self:getPatient():getBodyDamage():getBodyParts()
if isClient() and not self:getPatient():isLocalPlayer() then if isClient() and not self:getPatient():isLocalPlayer() then
@@ -274,8 +277,5 @@ function ISHealthPanel:getDamagedParts()
end end
end end
return result return result
else
return og_ISHealthPanel_getDamagedParts(self)
end end
end end

View File

@@ -1,11 +1,12 @@
local BaseHandler = require("TOC/UI/Interactions/HealthPanelBaseHandler") local BaseHandler = require("TOC/UI/Interactions/HealthPanelBaseHandler")
local StaticData = require("TOC/StaticData") local StaticData = require("TOC/StaticData")
local DataController = require("TOC/Controllers/DataController") local DataController = require("TOC/Controllers/DataController")
local ConfirmationPanel = require("TOC/UI/ConfirmationPanel")
local CutLimbAction = require("TOC/TimedActions/CutLimbAction") local CutLimbAction = require("TOC/TimedActions/CutLimbAction")
--------------------- ---------------------
-- TODO Add interaction to cut and bandage!
--* Various functions to help during these pesky checks --* Various functions to help during these pesky checks
@@ -13,7 +14,8 @@ local CutLimbAction = require("TOC/TimedActions/CutLimbAction")
---Check if the item type corresponds to a compatible saw ---Check if the item type corresponds to a compatible saw
---@param itemType string ---@param itemType string
local function CheckIfSaw(itemType) local function CheckIfSaw(itemType)
return itemType:contains(StaticData.SAWS_TYPES_IND_STR.saw) or itemType:contains(StaticData.SAWS_TYPES_IND_STR.gardenSaw) return itemType == StaticData.SAWS_TYPES_IND_STR.saw
or itemType == StaticData.SAWS_TYPES_IND_STR.gardenSaw
end end
---Return a compatible bandage ---Return a compatible bandage
@@ -37,9 +39,8 @@ local function GetStitchesConsumableItem(player)
-- Suture needle has priority -- Suture needle has priority
local sutureNeedle = plInv:FindAndReturn("Base.SutureNeedle") local sutureNeedle = plInv:FindAndReturn("Base.SutureNeedle")
---@cast sutureNeedle DrainableComboItem
if sutureNeedle and sutureNeedle:getUsedDelta() > 0 then if sutureNeedle then
return sutureNeedle return sutureNeedle
else else
-- Didn't find the suture one, so let's search for the normal one + thread -- Didn't find the suture one, so let's search for the normal one + thread
@@ -61,6 +62,12 @@ local function GetStitchesConsumableItem(player)
end end
local textConfirmAmp = getText("IGUI_Confirmation_Amputate")
local textAmp = getText("ContextMenu_Amputate")
local textAmpBandage = getText("ContextMenu_Amputate_Bandage")
local textAmpStitch = getText("ContextMenu_Amputate_Stitch")
local textAmpStitchBandage = getText("ContextMenu_Amputate_Stitch_Bandage")
---Add the action to the queue ---Add the action to the queue
---@param limbName string ---@param limbName string
---@param surgeon IsoPlayer ---@param surgeon IsoPlayer
@@ -69,24 +76,32 @@ end
---@param stitchesItem InventoryItem? ---@param stitchesItem InventoryItem?
---@param bandageItem InventoryItem? ---@param bandageItem InventoryItem?
local function PerformAction(surgeon, patient, limbName, sawItem, stitchesItem, bandageItem) local function PerformAction(surgeon, patient, limbName, sawItem, stitchesItem, bandageItem)
-- get saw in hand
-- todo primary or secondary depending on amputation status of surgeon
ISTimedActionQueue.add(ISEquipWeaponAction:new(surgeon, sawItem, 50, true, false))
local lHandItem = surgeon:getSecondaryHandItem()
if lHandItem then
ISTimedActionQueue.add(ISUnequipAction:new(surgeon, lHandItem, 50))
end
ISTimedActionQueue.add(CutLimbAction:new(surgeon, patient, limbName, sawItem, stitchesItem, bandageItem)) local x = (getCore():getScreenWidth() - 500) / 2
local y = getCore():getScreenHeight() / 2
ConfirmationPanel.Open(textConfirmAmp, x, y, nil, function()
-- get saw in hand
-- todo primary or secondary depending on amputation status of surgeon
ISTimedActionQueue.add(ISEquipWeaponAction:new(surgeon, sawItem, 50, true, false))
local lHandItem = surgeon:getSecondaryHandItem()
if lHandItem then
ISTimedActionQueue.add(ISUnequipAction:new(surgeon, lHandItem, 50))
end
ISTimedActionQueue.add(CutLimbAction:new(surgeon, patient, limbName, sawItem, stitchesItem, bandageItem))
end)
end end
local textAmp = getText("ContextMenu_Amputate")
local textAmpBandage = getText("ContextMenu_Amputate_Bandage")
local textAmpStitch = getText("ContextMenu_Amputate_Stitch")
local textAmpStitchBandage = getText("ContextMenu_Amputate_Stitch_Bandage")
---Adds the actions to the inventory context menu ---Adds the actions to the inventory context menu
---@param player IsoPlayer ---@param player IsoPlayer
@@ -214,6 +229,18 @@ function CutLimbInteractionHandler:checkItem(item)
end end
end end
---@param x number
---@param y number
---@param type any
function CutLimbInteractionHandler:openConfirmation(x, y, type)
ConfirmationPanel.Open(textConfirmAmp, x, y, nil, function()
self.onMenuOptionSelected(self, type)
end)
end
---@param context ISContextMenu ---@param context ISContextMenu
function CutLimbInteractionHandler:addToMenu(context) function CutLimbInteractionHandler:addToMenu(context)
--TOC_DEBUG.print("CutLimbInteractionHandler addToMenu") --TOC_DEBUG.print("CutLimbInteractionHandler addToMenu")
@@ -221,8 +248,12 @@ function CutLimbInteractionHandler:addToMenu(context)
local patientUsername = self:getPatient():getUsername() local patientUsername = self:getPatient():getUsername()
if #types > 0 and StaticData.LIMBS_TO_BODYLOCS_IND_BPT[self.limbName] and not DataController.GetInstance(patientUsername):getIsCut(self.limbName) then if #types > 0 and StaticData.LIMBS_TO_BODYLOCS_IND_BPT[self.limbName] and not DataController.GetInstance(patientUsername):getIsCut(self.limbName) then
TOC_DEBUG.print("addToMenu, types > 0") TOC_DEBUG.print("addToMenu, types > 0")
local x = (getCore():getScreenWidth() - 500) / 2
local y = getCore():getScreenHeight() / 2
for i=1, #types do for i=1, #types do
context:addOption(getText("ContextMenu_Amputate"), self, self.onMenuOptionSelected, types[i]) context:addOption(getText("ContextMenu_Amputate"), self, self.openConfirmation, x, y, types[i])
end end
end end
end end

View File

@@ -1,151 +1,157 @@
-- todo activate after some more testing require "lua_timers"
local ItemsController = require("TOC/Controllers/ItemsController")
local StaticData = require("TOC/StaticData")
local CommandsData = require("TOC/CommandsData")
-------------------------------
---@param zombie IsoZombie|IsoGameCharacter|IsoMovingObject|IsoObject
---@return integer trueID
local function GetZombieID(zombie)
-- Big love to Chuck and Sir Doggy Jvla for this code
---@diagnostic disable-next-line: param-type-mismatch
local pID = zombie:getPersistentOutfitID()
local bits = string.split(string.reverse(Long.toUnsignedString(pID, 2)), "")
while #bits < 16 do bits[#bits + 1] = 0 end
-- trueID
bits[16] = 0
local trueID = Long.parseUnsignedLong(string.reverse(table.concat(bits, "")), 2)
return trueID
end
-------------------------------
-- local ItemsController = require("TOC/Controllers/ItemsController") ---@param item InventoryItem
-- local StaticData = require("TOC/StaticData") local function PredicateAmputationItems(item)
-- local CommandsData = require("TOC/CommandsData") return item:getType():contains("Amputation_")
-- ------------------------------- end
-- ---@param item InventoryItem ---@param item InventoryItem
-- local function PredicateAmputationItems(item) local function PredicateAmputationItemLeft(item)
-- return item:getType():contains("Amputation_") return item:getType():contains("Amputation_") and item:getType():contains("_L")
-- end end
---@param item InventoryItem
local function PredicateAmputationItemRight(item)
return item:getType():contains("Amputation_") and item:getType():contains("_R")
end
---@param zombie IsoZombie
local function SpawnAmputation(zombie, side)
local index = ZombRand(1, #StaticData.PARTS_STR)
local limb = StaticData.PARTS_STR[index] .. "_" .. side
local amputationFullType = StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limb
ItemsController.Zombie.SpawnAmputationItem(zombie, amputationFullType)
-- local function PredicateAmputationItemLeft(item)
-- return item:getType():contains("Amputation_") and item:getType():contains("_L")
-- end
-- local function PredicateAmputationItemRight(item)
-- return item:getType():contains("Amputation_") and item:getType():contains("_R")
-- end
-- ---@param zombie IsoZombie|IsoGameCharacter|IsoMovingObject|IsoObject
-- ---@return integer trueID
-- local function GetZombieID(zombie)
-- -- Big love to Chuck and Sir Doggy Jvla for this code
-- ---@diagnostic disable-next-line: param-type-mismatch
-- local pID = zombie:getPersistentOutfitID()
-- local bits = string.split(string.reverse(Long.toUnsignedString(pID, 2)), "")
-- while #bits < 16 do bits[#bits+1] = 0 end
-- -- trueID
-- bits[16] = 0
-- local trueID = Long.parseUnsignedLong(string.reverse(table.concat(bits, "")), 2)
-- return trueID
-- end
-- ---@param zombie IsoZombie -- Add reference and transmit it to server
-- local function SpawnAmputation(zombie, side) local pID = GetZombieID(zombie)
-- local index = ZombRand(1, #StaticData.PARTS_STR) local zombieKey = CommandsData.GetZombieKey()
-- local limb = StaticData.PARTS_STR[index] .. "_" .. side local zombiesMD = ModData.getOrCreate(zombieKey)
-- local amputationFullType = StaticData.AMPUTATION_CLOTHING_ITEM_BASE .. limb if zombiesMD[pID] == nil then zombiesMD[pID] = {} end
zombiesMD[pID][side] = amputationFullType
ModData.add(zombieKey, zombiesMD)
ModData.transmit(zombieKey)
end
-------------------------------
local bloodAmount = 10
-- ItemsController.Zombie.SpawnAmputationItem(zombie, amputationFullType) ---@param player IsoGameCharacter
---@param zombie IsoZombie
---@param handWeapon HandWeapon
local function HandleZombiesAmputations(player, zombie, handWeapon, damage)
if not SandboxVars.TOC.EnableZombieAmputations then return end
if not instanceof(zombie, "IsoZombie") or not instanceof(player, "IsoPlayer") then return end
if player ~= getPlayer() then return end
-- Check type of weapon. No hands, only knifes or such
local weaponCategories = handWeapon:getScriptItem():getCategories()
if not (weaponCategories:contains("Axe") or weaponCategories:contains("LongBlade")) then return end
local isCrit = player:isCriticalHit()
local randomChance = ZombRand(0, 100) > (100 - SandboxVars.TOC.ZombieAmputationDamageChance)
if (damage > SandboxVars.TOC.ZombieAmputationDamageThreshold and randomChance) or isCrit then
TOC_DEBUG.print("Amputating zombie limbs - damage: " .. tostring(damage))
local zombieInv = zombie:getInventory()
-- Check left or right
local randSide = ZombRand(2) -- Random side
local preferredSide = randSide == 0 and "L" or "R"
local alternateSide = preferredSide == "L" and "R" or "L"
local predicatePreferred = preferredSide == "L" and PredicateAmputationItemLeft or PredicateAmputationItemRight
local predicateAlternate = alternateSide == "L" and PredicateAmputationItemLeft or PredicateAmputationItemRight
if not zombieInv:containsEval(predicatePreferred) then
SpawnAmputation(zombie, preferredSide)
elseif not zombieInv:containsEval(predicateAlternate) then
SpawnAmputation(zombie, alternateSide)
end
TOC_DEBUG.print("Amputating zombie limbs - damage: " .. tostring(damage) .. ", preferred limb side: " .. preferredSide)
-- add blood splat every couple of seconds for a while
addBloodSplat(getCell():getGridSquare(zombie:getX(), zombie:getY(), zombie:getZ()), bloodAmount)
local timerName = tostring(GetZombieID(zombie)) .. "_timer"
timer:Create(timerName, 1, 10, function()
addBloodSplat(getCell():getGridSquare(zombie:getX(), zombie:getY(), zombie:getZ()), bloodAmount)
end)
end
end
Events.OnWeaponHitCharacter.Add(HandleZombiesAmputations)
-----------------------------
local localOnlyZombiesMD
local function SetupZombiesModData()
if not SandboxVars.TOC.EnableZombieAmputations then return end
local zombieKey = CommandsData.GetZombieKey()
localOnlyZombiesMD = ModData.getOrCreate(zombieKey)
end
Events.OnInitGlobalModData.Add(SetupZombiesModData)
-- -- Add reference and transmit it to server ---@param zombie IsoZombie
-- local pID = GetZombieID(zombie) local function ReapplyAmputation(zombie)
-- local zombieKey = CommandsData.GetZombieKey() if not SandboxVars.TOC.EnableZombieAmputations then return end
-- local zombiesMD = ModData.getOrCreate(zombieKey)
-- if zombiesMD[pID] == nil then zombiesMD[pID] = {} end
-- zombiesMD[pID][side] = amputationFullType
-- ModData.add(zombieKey, zombiesMD)
-- ModData.transmit(zombieKey)
-- end
-- ------------------------------- local pID = GetZombieID(zombie)
-- ---@param player IsoGameCharacter
-- ---@param zombie IsoZombie
-- ---@param handWeapon HandWeapon
-- function HandleZombiesAmputations(player, zombie, handWeapon, damage)
-- if not instanceof(zombie, "IsoZombie") or not instanceof(player, "IsoPlayer") then return end
-- if player ~= getPlayer() then return end
-- -- TODO Check type of weapon. No hands, only knifes or such
-- if damage < 3 or ZombRand(0,100) < 25 then return end
-- TOC_DEBUG.print(handWeapon:getName())
-- local zombieInv = zombie:getInventory()
-- -- Check left or right
-- local leftItem = zombieInv:containsEval(PredicateAmputationItemLeft)
-- if not leftItem then
-- SpawnAmputation(zombie, "L")
-- return
-- end
-- local rightItem = zombieInv:containsEval(PredicateAmputationItemRight)
-- if not rightItem then
-- SpawnAmputation(zombie, "R")
-- return
-- end
-- end
-- Events.OnWeaponHitCharacter.Add(HandleZombiesAmputations)
-- -----------------------------
-- local localOnlyZombiesMD
-- local function SetupZombiesModData()
-- local zombieKey = CommandsData.GetZombieKey()
-- localOnlyZombiesMD = ModData.getOrCreate(zombieKey)
-- end
-- Events.OnInitGlobalModData.Add(SetupZombiesModData)
-- ---@param zombie IsoZombie
-- local function ReapplyAmputation(zombie)
-- local pID = GetZombieID(zombie)
-- if localOnlyZombiesMD[pID] ~= nil then
-- -- check if zombie has amputation
-- local zombiesAmpData = localOnlyZombiesMD[pID]
-- local zombieInv = zombie:getInventory()
-- local foundItem = zombieInv:containsEvalRecurse(PredicateAmputationItems)
-- if foundItem then
-- return
-- else
-- local leftAmp = zombiesAmpData['L']
-- if leftAmp then
-- ItemsController.Zombie.SpawnAmputationItem(zombie, leftAmp)
-- end
-- local rightAmp = zombiesAmpData['R']
-- if rightAmp then
-- ItemsController.Zombie.SpawnAmputationItem(zombie, rightAmp)
-- end
-- -- Removes reference, local only
-- localOnlyZombiesMD[pID] = nil
-- end
-- end
-- end
-- Events.OnZombieUpdate.Add(ReapplyAmputation)
if localOnlyZombiesMD[pID] ~= nil then
-- check if zombie has amputation
local zombiesAmpData = localOnlyZombiesMD[pID]
local zombieInv = zombie:getInventory()
local foundItem = zombieInv:containsEvalRecurse(PredicateAmputationItems)
if foundItem then
return
else
local leftAmp = zombiesAmpData['L']
if leftAmp then
ItemsController.Zombie.SpawnAmputationItem(zombie, leftAmp)
end
local rightAmp = zombiesAmpData['R']
if rightAmp then
ItemsController.Zombie.SpawnAmputationItem(zombie, rightAmp)
end
-- Removes reference, local only
localOnlyZombiesMD[pID] = nil
end
end
end
Events.OnZombieUpdate.Add(ReapplyAmputation)

View File

@@ -0,0 +1,216 @@
-- Made by Vyshnia
-- Workshop ID: 2875394066
-- Mod ID: LuaTimers
local os_time = os.time
local table_insert = table.insert
local table_remove = table.remove
local assert = assert
local type = type
local pairs = pairs
timer = {
Timers = {},
SimpleTimers = {}
}
function timer:Simple(delay, func)
assert(type(delay) == "number", "Delay of timer should be a number type")
assert(type(func) == "function", "Func of timer should be a function type (lol)")
table_insert(self.SimpleTimers, {
EndTime = os_time() + delay,
Func = func
})
end
function timer:Create(name, delay, repetitions, func)
assert(type(name) == "string", "ID of timer should be a string type")
assert(type(delay) == "number", "Delay of timer should be a number type")
assert(type(repetitions) == "number", "Repetitions of timer should be a number type")
assert(type(func) == "function", "Func of timer should be a function type (lol)")
self.Timers[name] = {
Delay = delay,
StartRepetitions = repetitions,
Repetitions = repetitions,
Infinity = repetitions == 0,
LastFuncTime = os_time(),
Func = func,
Paused = false,
}
end
local function timerUpdate()
local cur_time = os_time()
for k,v in pairs(timer.Timers) do
if not v.Paused then
if cur_time >= v.LastFuncTime + v.Delay then
v.Func()
v.LastFuncTime = cur_time
if not v.Infinity then
v.Repetitions = v.Repetitions - 1
if v.Repetitions <= 0 then
timer.Timers[k] = nil
end
end
end
end
end
local simple_timers = timer.SimpleTimers
for i = #simple_timers, 1, -1 do
local t = simple_timers[i]
if t.EndTime <= cur_time then
t.Func()
table_remove(simple_timers, i)
end
end
end
Events.OnTickEvenPaused.Add(timerUpdate)
function timer:Remove(name)
local t = self.Timers[name]
if not t then return false end
self.Timers[name] = nil
return true
end
function timer:Exists(name)
return self.Timers[name] and true or false
end
function timer:Start(name)
local t = self.Timers[name]
if not t then return false end
t.Repetitions = t.StartRepetitions
t.LastFuncTime = os_time()
t.Paused = false
t.PausedTime = nil
return true
end
function timer:Pause(name)
local t = self.Timers[name]
if not t then return false end
if t.Paused then return false end
t.Paused = true
t.PausedTime = os_time()
return true
end
function timer:UnPause(name)
local t = self.Timers[name]
if not t then return false end
if not t.Paused then return false end
t.Paused = false
return true
end
timer.Resume = timer.UnPause
function timer:Toggle(name)
local t = self.Timers[name]
if not t then return false end
t.Paused = not t.Paused
return true
end
function timer:TimeLeft(name)
local t = self.Timers[name]
if not t then return end
if t.Paused then
return (t.Repetitions - 1) * t.Delay + (t.LastFuncTime + t.Delay - t.PausedTime)
else
return (t.Repetitions - 1) * t.Delay + (t.LastFuncTime + t.Delay - os_time())
end
end
function timer:NextTimeLeft(name)
local t = self.Timers[name]
if not t then return end
if t.Paused then
return t.LastFuncTime + t.Delay - t.PausedTime
else
return t.LastFuncTime + t.Delay - os_time()
end
end
function timer:RepsLeft(name)
local t = self.Timers[name]
return t and t.Repetitions
end

View File

@@ -3,14 +3,9 @@ local StaticData = require("TOC/StaticData")
local CommandsData = require("TOC/CommandsData") local CommandsData = require("TOC/CommandsData")
------------------------ ------------------------
local ServerDataHandler = {} local ServerDataHandler = {}
ServerDataHandler.modData = {} ServerDataHandler.modData = {}
---Get the server mod data table containing that player TOC data ---Get the server mod data table containing that player TOC data
---@param key string ---@param key string
---@return tocModDataType ---@return tocModDataType

View File

@@ -24,7 +24,7 @@ function ServerRelayCommands.RelayExecuteAmputationAction(surgeonPl, args)
local surgeonNum = surgeonPl:getOnlineID() local surgeonNum = surgeonPl:getOnlineID()
---@type receiveDamageDuringAmputationParams ---@type receiveDamageDuringAmputationParams
local params = {surgeonNum = surgeonNum, limbName = args.limbName} local params = {surgeonNum = surgeonNum, limbName = args.limbName, damagePlayer = true}
sendServerCommand(patientPl, CommandsData.modules.TOC_RELAY, CommandsData.client.Relay.ReceiveExecuteAmputationAction, params) sendServerCommand(patientPl, CommandsData.modules.TOC_RELAY, CommandsData.client.Relay.ReceiveExecuteAmputationAction, params)
end end
@@ -38,6 +38,21 @@ function ServerRelayCommands.RelayExecuteInitialization(adminObj, args)
end end
---Relay a forced amputation to another client.
---@param adminObj IsoPlayer
---@param args relayForcedAmputationParams
function ServerRelayCommands.RelayForcedAmputation(adminObj, args)
local patientPl = getPlayerByOnlineID(args.patientNum)
local adminNum = adminObj:getOnlineID()
---@type receiveDamageDuringAmputationParams
local ampParams = {surgeonNum = adminNum, limbName = args.limbName, damagePlayer = false} -- the only difference between relayExecuteAmputationAction and this is the damage
sendServerCommand(patientPl, CommandsData.modules.TOC_RELAY, CommandsData.client.Relay.ReceiveExecuteAmputationAction, ampParams)
-- Automatic cicatrization
sendServerCommand(patientPl, CommandsData.modules.TOC_RELAY, CommandsData.client.Relay.ReceiveForcedCicatrization, {limbName = args.limbName})
end
------------------------- -------------------------

View File

@@ -11,14 +11,14 @@ CommandsData.modules = {
CommandsData.client = { CommandsData.client = {
Relay = { Relay = {
ReceiveDamageDuringAmputation = "ReceiveDamageDuringAmputation", ---@alias receiveDamageDuringAmputationParams { limbName : string} ReceiveDamageDuringAmputation = "ReceiveDamageDuringAmputation", ---@alias receiveDamageDuringAmputationParams { limbName : string}
ReceiveExecuteAmputationAction = "ReceiveExecuteAmputationAction", ---@alias receiveExecuteAmputationActionParams {surgeonNum : number, limbName : string} ReceiveExecuteAmputationAction = "ReceiveExecuteAmputationAction", ---@alias receiveExecuteAmputationActionParams {surgeonNum : number, limbName : string, damagePlayer : boolean}
--* APPLY *-- --* APPLY *--
ReceiveApplyFromServer = "ReceiveApplyFromServer", ReceiveApplyFromServer = "ReceiveApplyFromServer",
--* ADMIN ONLY --* --* ADMIN ONLY --*
ReceiveExecuteInitialization = "ReceiveExecuteInitialization" ReceiveExecuteInitialization = "ReceiveExecuteInitialization",
ReceiveForcedCicatrization = "ReceiveForcedCicatrization" ---@alias receiveForcedCicatrizationParams {limbName : string}
} }
} }
@@ -31,8 +31,10 @@ CommandsData.server = {
Relay = { Relay = {
RelayDamageDuringAmputation = "RelayDamageDuringAmputation", ---@alias relayDamageDuringAmputationParams {patientNum : number, limbName : string} RelayDamageDuringAmputation = "RelayDamageDuringAmputation", ---@alias relayDamageDuringAmputationParams {patientNum : number, limbName : string}
RelayExecuteAmputationAction = "RelayExecuteAmputationAction", ---@alias relayExecuteAmputationActionParams {patientNum : number, limbName : string} RelayExecuteAmputationAction = "RelayExecuteAmputationAction", ---@alias relayExecuteAmputationActionParams {patientNum : number, limbName : string}
--* ADMIN ONLY *-- --* ADMIN ONLY *--
RelayExecuteInitialization = "RelayExecuteInitialization" ---@alias relayExecuteInitializationParams {patientNum : number} RelayExecuteInitialization = "RelayExecuteInitialization", ---@alias relayExecuteInitializationParams {patientNum : number}
RelayForcedAmputation = "RelayForcedAmputation" ---@alias relayForcedAmputationParams {patientNum : number, limbName : string}
} }
} }

View File

@@ -5,7 +5,7 @@ local TRAITS = {
Amputee_Hand = "Amputee_Hand", Amputee_Hand = "Amputee_Hand",
Amputee_ForeArm = "Amputee_ForeArm", Amputee_ForeArm = "Amputee_ForeArm",
Amputee_UpperArm = "Amputee_UpperArm", Amputee_UpperArm = "Amputee_UpperArm",
Insensitive = "Insensitive" --Insensitive = "Insensitive" -- TODO Disabled for now, until we reintroduce it
} }
@@ -37,7 +37,7 @@ local function SetupTraits()
t:addXPBoost(Perks.Strength, -1) t:addXPBoost(Perks.Strength, -1)
end end
TraitFactory.addTrait(TRAITS.Insensitive, GetTraitText(TRAITS.Insensitive), 6, GetTraitDesc(TRAITS.Insensitive), false, false) --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_ForeArm)
TraitFactory.setMutualExclusive(TRAITS.Amputee_Hand, TRAITS.Amputee_UpperArm) TraitFactory.setMutualExclusive(TRAITS.Amputee_Hand, TRAITS.Amputee_UpperArm)

View File

@@ -15,4 +15,6 @@ IG_UI_EN = {
IGUI_HealthPanel_WoundDirtyness = "Wound Dirtyness", IGUI_HealthPanel_WoundDirtyness = "Wound Dirtyness",
IGUI_HealthPanel_ProstEquipped = "Prosthesis Equipped", IGUI_HealthPanel_ProstEquipped = "Prosthesis Equipped",
IGUI_Confirmation_Amputate = " <CENTRE> Do you really want to amputate that limb?"
} }

View File

@@ -3,5 +3,8 @@ Sandbox_EN = {
Sandbox_TOC_CicatrizationSpeed = "Cicatrization Speed", Sandbox_TOC_CicatrizationSpeed = "Cicatrization Speed",
Sandbox_TOC_WoundDirtynessMultiplier = "Wound Dirtyness Multiplier", Sandbox_TOC_WoundDirtynessMultiplier = "Wound Dirtyness Multiplier",
Sandbox_TOC_SurgeonAbilityImportance = "Relevance of surgeon doctor ability", Sandbox_TOC_SurgeonAbilityImportance = "Relevance of surgeon doctor ability",
Sandbox_TOC_EnableZombieAmputations = "Enable Zombie amputations",
Sandbox_TOC_ZombieAmputationDamageThreshold = "Zombie amputations damage treshold",
Sandbox_TOC_ZombieAmputationDamageChance = "Zombie amputations damage chance",
} }

View File

@@ -0,0 +1,29 @@
ContextMenu_FR = {
ContextMenu_Amputate = "Amputer",
ContextMenu_Amputate_Bandage = "Amputer et bander",
ContextMenu_Amputate_Stitch = "Amputer et suturer",
ContextMenu_Amputate_Stitch_Bandage = "Amputer, suturer et bander",
ContextMenu_Cauterize = "Cautériser",
ContextMenu_Limb_Hand_L = "Main gauche",
ContextMenu_Limb_ForeArm_L = "Avant-bras gauche",
ContextMenu_Limb_UpperArm_L = "Bras supérieur gauche",
ContextMenu_Limb_Hand_R = "Main droite",
ContextMenu_Limb_ForeArm_R = "Avant-bras droit",
ContextMenu_Limb_UpperArm_R = "Bras supérieur droit",
ContextMenu_InstallProstRight = "Installer une prothèse sur le bras droit",
ContextMenu_InstallProstLeft = "Installer une prothèse sur le bras gauche",
ContextMenu_PutTourniquetArmLeft = "Mettre un garrot sur le bras gauche",
ContextMenu_PutTourniquetLegL = "Mettre un garrot sur la jambe gauche",
ContextMenu_PutTourniquetArmRight = "Mettre un garrot sur le bras droit",
ContextMenu_PutTourniquetLegR = "Mettre un garrot sur la jambe droite",
ContextMenu_CleanWound = "Nettoyer la plaie",
ContextMenu_Admin_TOC = "TOC",
ContextMenu_Admin_ResetTOC = "Réinitialiser les amputations",
ContextMenu_Admin_ForceAmputation = "Forcer l'amputation",
}

View File

@@ -0,0 +1,18 @@
IG_UI_FR = {
IGUI_perks_Amputations = "Amputations",
IGUI_perks_Side_R = "Côté droit",
IGUI_perks_Side_L = "Côté gauche",
IGUI_perks_Prosthesis = "Prothèse",
IGUI_perks_ProstFamiliarity = "Familiarité",
IGUI_ItemCat_Prosthesis = "Prothèse",
IGUI_ItemCat_Surgery = "Chirurgie",
IGUI_ItemCat_Amputation = "Amputation",
IGUI_HealthPanel_Cicatrization = "Cicatrisation",
IGUI_HealthPanel_Cicatrized = "Cicatrisé",
IGUI_HealthPanel_Cauterized = "Cautérisé",
IGUI_HealthPanel_WoundDirtyness = "Saleté de la plaie",
IGUI_HealthPanel_ProstEquipped = "Prothèse équipée",
}

View File

@@ -0,0 +1,11 @@
ItemName_FR = {
ItemName_TOC.Surg_Arm_Tourniquet_L = "Garrot",
ItemName_TOC.Surg_Arm_Tourniquet_R = "Garrot",
ItemName_TOC.Prost_NormalArm_L = "Bras prothétique",
ItemName_TOC.Prost_NormalArm_R = "Bras prothétique",
ItemName_TOC.Prost_HookArm_L = "Bras prothétique - Crochet",
ItemName_TOC.Prost_HookArm_R = "Bras prothétique - Crochet",
}

View File

@@ -0,0 +1,5 @@
Recipes_FR = {
Recipe_Craft_Prosthetic_Arm = "Fabriquer un bras prothétique",
Recipe_Craft_Prosthetic_Hook = "Fabriquer un crochet prothétique",
}

View File

@@ -0,0 +1,10 @@
Sandbox_FR = {
Sandbox_TOC = "Le Seul Remède",
Sandbox_TOC_CicatrizationSpeed = "Vitesse de cicatrisation",
Sandbox_TOC_WoundDirtynessMultiplier = "Multiplicateur de saleté de la plaie",
Sandbox_TOC_SurgeonAbilityImportance = "Importance de la compétence du chirurgien",
Sandbox_TOC_EnableZombieAmputations = "Activer les amputations de zombies",
Sandbox_TOC_ZombieAmputationDamageThreshold = "Seuil de dégâts pour amputations de zombies",
Sandbox_TOC_ZombieAmputationDamageChance = "Probabilité d'amputations de zombies",
}

View File

@@ -0,0 +1,10 @@
Tooltip_FR = {
Tooltip_Surgery_CantCauterize = "Vous ne pouvez pas cautériser la plaie",
Tooltip_Surgery_And = " et ",
Tooltip_Surgery_TempTooLow = "La température est encore trop basse",
Tooltip_Surgery_Coward = "Vous n'avez pas le courage de le faire",
Tooltip_Surgery_LimbNotFree = "Vous devez d'abord retirer la prothèse",
}

View File

@@ -0,0 +1,16 @@
UI_FR = {
UI_trait_Amputee_Hand = "Main gauche amputée",
UI_trait_Amputee_Hand_desc = "",
UI_trait_Amputee_ForeArm = "Avant-bras gauche amputé",
UI_trait_Amputee_ForeArm_desc = "",
UI_trait_Amputee_UpperArm = "Bras supérieur gauche amputé",
UI_trait_Amputee_UpperArm_desc = "",
UI_trait_Insensitive = "Insensible",
UI_trait_Insensitive_desc = "",
UI_Say_CantEquip = "Je ne peux pas l'équiper comme ça..."
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,33 @@
ContextMenu_RU = {
ContextMenu_Amputate = "Àìïóòèðîâàòü",
ContextMenu_Amputate_Bandage = "Àìïóòèðîâàòü è ïåðåâÿçàòü",
ContextMenu_Amputate_Stitch = "Àìïóòèðîâàòü è íàëîæèòü øâû",
ContextMenu_Amputate_Stitch_Bandage = "Àìïóòèðîâàòü, íàëîæèòü øâû è ïåðåâÿçàòü",
ContextMenu_Cauterize = "Ïðèæå÷ü",
ContextMenu_Limb_Hand_L = "Ëåâàÿ ðóêà",
ContextMenu_Limb_ForeArm_L = "Ëåâàÿ ïðåäïëå÷üå",
ContextMenu_Limb_UpperArm_L = "Ëåâîå ïëå÷î"
ContextMenu_Limb_Hand_R = "Ïðàâàÿ ðóêà",
ContextMenu_Limb_ForeArm_R = "Ïðàâàÿ ïðåäïëå÷üå",
ContextMenu_Limb_UpperArm_R = "Ïðàâàÿ ïëå÷î",
ContextMenu_InstallProstRight = "Óñòàíîâèòü ïðîòåç íà ïðàâóþ ðóêó",
ContextMenu_InstallProstLeft = "Óñòàíîâèòü ïðîòåç íà ëåâóþ ðóêó",
ContextMenu_PutTourniquetArmLeft = "Íàëîæèòü æãóò íà ëåâóþ ðóêó",
ContextMenu_PutTourniquetLegL = "Íàëîæèòü æãóò íà ëåâóþ íîãó",
ContextMenu_PutTourniquetArmRight = "Íàëîæèòü æãóò íà ïðàâóþ ðóêó",
ContextMenu_PutTourniquetLegR = "Íàëîæèòü æãóò íà ïðàâóþ íîãó",
ContextMenu_CleanWound = "Î÷èñòèòü ðàíó",
ContextMenu_Admin_TOC = "TOC",
ContextMenu_Admin_ResetTOC = "Ñáðîñèòü àìïóòàöèè",
ContextMenu_Admin_ForceAmputation = "Ìîìåíòàëüíî àìïóòèðîâàòü",
}

View File

@@ -0,0 +1,18 @@
IG_UI_RU = {
IGUI_perks_Amputations = "Ампутации",
IGUI_perks_Side_R = "Правая сторона",
IGUI_perks_Side_L = "Левая сторона",
IGUI_perks_Prosthesis = "Протез",
IGUI_perks_ProstFamiliarity= "Приспособленность",
IGUI_ItemCat_Prosthesis = "Протез",
IGUI_ItemCat_Surgery = "Хирургия",
IGUI_ItemCat_Amputation = "Ампутация"
IGUI_HealthPanel_Cicatrization = "Заживление",
IGUI_HealthPanel_Cicatrized = "Заживлено",
IGUI_HealthPanel_Cauterized = "Прижженно",
IGUI_HealthPanel_WoundDirtyness = "Загрезнённая рана",
IGUI_HealthPanel_ProstEquipped = "Протез экипирован",
}

View File

@@ -0,0 +1,11 @@
ItemName_RU = {
ItemName_TOC.Surg_Arm_Tourniquet_L = "Æãóò",
ItemName_TOC.Surg_Arm_Tourniquet_R = "Æãóò",
ItemName_TOC.Prost_NormalArm_L = "Ïðîòåç ðóêè",
ItemName_TOC.Prost_NormalArm_R = "Ïðîòåç ðóêè",
ItemName_TOC.Prost_HookArm_L = "Ïðîòåç ðóêè - Êðþê",
ItemName_TOC.Prost_HookArm_R = "Ïðîòåç ðóêè - Êðþê",
}

View File

@@ -0,0 +1,4 @@
Recipes_RU = {
Recipe_Craft_Prosthetic_Arm = "如泐蝾忤螯 镳铗彗 痼觇",
Recipe_Craft_Prosthetic_Hook = "如泐蝾忤螯 镳铗彗 忖桎<E5BF96><>赅",
}

View File

@@ -0,0 +1,7 @@
Sandbox_RU = {
Sandbox_TOC = "The Only Cure",
Sandbox_TOC_CicatrizationSpeed = "Ñêîðîñòü çàæèâëåíèÿ",
Sandbox_TOC_WoundDirtynessMultiplier = "Ìíîæèòåëü çàãðÿçíåíèÿ ðàí",
Sandbox_TOC_SurgeonAbilityImportance = "Çíà÷èìîñòü ñïîñîáíîñòåé âðà÷à-õèðóðãà",
}

View File

@@ -0,0 +1,10 @@
Tooltip_RU = {
Tooltip_Surgery_CantCauterize = "Нельзя прижигать рану",
Tooltip_Surgery_And = " и "
Tooltip_Surgery_TempTooLow = "Температура все еще слишком низкая",
Tooltip_Surgery_Coward = "У тебя не хватит смелости сделать это",
Tooltip_Surgery_LimbNotFree = "Сначала нужно снять протез",
}

View File

@@ -0,0 +1,16 @@
UI_RU = {
UI_trait_Amputee_Hand = "Ампутированная левая рука",
UI_trait_Amputee_Hand_desc = "Вы начинаете с ампутированной левой рукой.",
UI_trait_Amputee_ForeArm = "Ампутированное левое предплечье",
UI_trait_Amputee_ForeArm_desc = "Вы начинаете с ампутированным левым предплечьем.",
UI_trait_Amputee_UpperArm = "Ампутированное левое плечо",
UI_trait_Amputee_UpperArm_desc = "Вы начинаете с ампутированнвм левым плечём.",
UI_trait_Insensitive = "Нечувствительный",
UI_trait_Insensitive_desc = "",
UI_Say_CantEquip = "Я не могу его так экипировать..."
}

View File

@@ -0,0 +1,33 @@
ContextMenu_UA = {
ContextMenu_Amputate = "Àìïóòóâàòè",
ContextMenu_Amputate_Bandage = "Àìïóòóâàòè òà ïåðåâ'ÿçàòè",
ContextMenu_Amputate_Stitch = "Àìïóòóâàòè òà íàêëàñòè øâè",
ContextMenu_Amputate_Stitch_Bandage = "Àìïóòóâàòè, íàêëàñòè øâè òà ïåðåâ'ÿçàòè",
ContextMenu_Cauterize = "Ïðèïåêòè",
ContextMenu_Limb_Hand_L = "˳âà Ðóêà",
ContextMenu_Limb_ForeArm_L = "˳âå Ïåðåäïë³÷÷ÿ",
ContextMenu_Limb_UpperArm_L = "˳âå Ïëå÷å"
ContextMenu_Limb_Hand_R = "Ïðàâà Ðóêà",
ContextMenu_Limb_ForeArm_R = "Ïðàâå Ïåðåäïë³÷÷ÿ",
ContextMenu_Limb_UpperArm_R = "Ïðàâå Ïëå÷å",
ContextMenu_InstallProstRight = "Óñòàíîâèòè ïðîòåç íà ïðàâó ðóêó",
ContextMenu_InstallProstLeft = "Óñòàíîâèòè ïðîòåç íà ë³âó ðóêó",
ContextMenu_PutTourniquetArmLeft = "Íàêëàñòè äæãóò íà ë³âó ðóêó",
ContextMenu_PutTourniquetLegL = "Íàêëàñòè äæãóò íà ë³âó íîãó",
ContextMenu_PutTourniquetArmRight = "Íàêëàñòè äæãóò íà ïðàâó ðóêó",
ContextMenu_PutTourniquetLegR = "Íàêëàñòè äæãóò íà ïðàâó íîãó",
ContextMenu_CleanWound = "Î÷èñòèòè ðàíó",
ContextMenu_Admin_TOC = "TOC",
ContextMenu_Admin_ResetTOC = "Ñêèíóòè Àìïóòàö³¿",
ContextMenu_Admin_ForceAmputation = "Ïðèìóñîâî Àìïóòóâàòè",
}

View File

@@ -0,0 +1,18 @@
IG_UI_UA = {
IGUI_perks_Amputations = "Ŕěďóňŕöłż",
IGUI_perks_Side_R = "Ďđŕâŕ ńňîđîíŕ",
IGUI_perks_Side_L = "Ëłâŕ ńňîđîíŕ",
IGUI_perks_Prosthesis = "Ďđîňĺç",
IGUI_perks_ProstFamiliarity= "Ďđčńňîńîâŕíłńňü",
IGUI_ItemCat_Prosthesis = "Ďđîňĺç",
IGUI_ItemCat_Surgery = "Őłđóđăł˙",
IGUI_ItemCat_Amputation = "Ŕěďóňŕöł˙"
IGUI_HealthPanel_Cicatrization = "Đóáöţâŕíí˙",
IGUI_HealthPanel_Cicatrized = "Çŕđóáöüîâŕíŕ",
IGUI_HealthPanel_Cauterized = "Ďđčďĺ÷ĺíŕ",
IGUI_HealthPanel_WoundDirtyness = "Çŕáđóäíĺíŕ đŕíŕ",
IGUI_HealthPanel_ProstEquipped = "Ďđîňĺç óńňŕíîâëĺíî",
}

View File

@@ -0,0 +1,11 @@
ItemName_UA = {
ItemName_TOC.Surg_Arm_Tourniquet_L = "Äæãóò",
ItemName_TOC.Surg_Arm_Tourniquet_R = "Äæãóò",
ItemName_TOC.Prost_NormalArm_L = "Ïðîòåç Ðóêè",
ItemName_TOC.Prost_NormalArm_R = "Ïðîòåç Ðóêè",
ItemName_TOC.Prost_HookArm_L = "Ïðîòåç Ðóêè - Ãàê",
ItemName_TOC.Prost_HookArm_R = "Ïðîòåç Ðóêè - Ãàê",
}

View File

@@ -0,0 +1,4 @@
Recipes_UA = {
Recipe_Craft_Prosthetic_Arm = "Створити Протез Руки",
Recipe_Craft_Prosthetic_Hook = "Створити Протез Руки - Гак",
}

View File

@@ -0,0 +1,10 @@
Sandbox_UA = {
Sandbox_TOC = "The Only Cure",
Sandbox_TOC_CicatrizationSpeed = "Øâèäê³ñòü ðóáöþâàííÿ",
Sandbox_TOC_WoundDirtynessMultiplier = "Ìíîæíèê çàáðóäíåííÿ ðàíè",
Sandbox_TOC_SurgeonAbilityImportance = "Âàæëèâ³ñòü ìåäè÷íèõ íàâè÷îê ë³êàðÿ",
Sandbox_TOC_EnableZombieAmputations = "Óâ³ìêíóòè àìïóòàö³¿ çîìá³",
Sandbox_TOC_ZombieAmputationDamageThreshold = "Ïîð³ã øêîäè ïðè àìïóòàö³¿ çîìá³",
Sandbox_TOC_ZombieAmputationDamageChance = "Øàíñ íàíåñåííÿ øêîäè ïðè àìïóòàö³¿ çîìá³",
}

View File

@@ -0,0 +1,10 @@
Tooltip_UA = {
Tooltip_Surgery_CantCauterize = "Не можна припекти рану",
Tooltip_Surgery_And = " та "
Tooltip_Surgery_TempTooLow = "Температура занадто низька",
Tooltip_Surgery_Coward = "Вам не вистачає сміливості зробити це",
Tooltip_Surgery_LimbNotFree = "Спочатку необхідно зняти протез",
}

View File

@@ -0,0 +1,16 @@
UI_UA = {
UI_trait_Amputee_Hand = "Àìïóòîâàíà ˳âà Ðóêà",
UI_trait_Amputee_Hand_desc = "",
UI_trait_Amputee_ForeArm = "Àìïóòîâàíå ˳âå Ïåðåäïë³÷÷ÿ",
UI_trait_Amputee_ForeArm_desc = "",
UI_trait_Amputee_UpperArm = "Àìïóòîâàíå ˳âå Ïëå÷å",
UI_trait_Amputee_UpperArm_desc = "",
UI_trait_Insensitive = "Íå÷óòëèâèé",
UI_trait_Insensitive_desc = "",
UI_Say_CantEquip = "ß íå ìîæó âñòàíîâèòè öå òàêèì ÷èíîì..."
}

View File

@@ -28,3 +28,31 @@ option TOC.SurgeonAbilityImportance
translation = TOC_SurgeonAbilityImportance, translation = TOC_SurgeonAbilityImportance,
} }
option TOC.EnableZombieAmputations
{
type = boolean,
default = false,
page = TOC,
translation= TOC_EnableZombieAmputations,
}
option TOC.ZombieAmputationDamageThreshold
{
type = integer,
min = 0,
max = 10,
default = 4,
page = TOC,
translation = TOC_ZombieAmputationDamageThreshold,
}
option TOC.ZombieAmputationDamageChance
{
type = integer,
min = 0,
max = 100,
default = 25,
page = TOC,
translation = TOC_ZombieAmputationDamageChance,
}

View File

@@ -4,5 +4,5 @@ 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.7 modversion=2.1.4
pzversion=41.65 pzversion=41.65

View File

@@ -5,17 +5,23 @@ description=[img]https://raw.githubusercontent.com/ZioPao/The-Only-Cure/551125bb
description= description=
description=
description=[h1]You're bitten. You have two choices.[/h1] description=[h1]You're bitten. You have two choices.[/h1]
description=Wait until you succumb to the virus or take matters into your hands. description=Wait until you succumb to the virus or take matters into your hands. Cut off that infected part and live to die another day.
description= description=
description=This version of [b]The Only Cure[/b] has been rebuilt from scratch to support future additions. description=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.
description= description=
description=Supports [b]Single Player[/b] and [b]Multiplayer[/b]! description=[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]
description=[b]The older version will be delisted shortly and it will not be supported anymore.[/b]
description=[h1]Supports [b]Single Player[/b] and [b]Multiplayer[/b]. Host Mode is currently [b]UNSUPPORTED![/b][/h1]
description= description=
@@ -33,6 +39,14 @@ description=[/list]
description= description=
description=Place them [b]BEFORE[/b] The Only Cure in your mod list!
description=
description=[hr][/hr]
description=
description=[h1]Quick guide[/h1] description=[h1]Quick guide[/h1]
description=[h2]Amputation[/h2] description=[h2]Amputation[/h2]
@@ -41,11 +55,17 @@ description=Get a [i]Saw[/i] or a [i]Garden Saw[/i], right click on it, and choo
description= description=
description=If you have a tourniquet, place it on the correct side to dampen the amount of damage you will take after you're done amputating the limb. description=If you have some [i]bandages[/i] and\or [i]stitches[/i] in your inventory you will automatically use them, multiplying the chances of your survival.
description=If you have a [i]tourniquet[/i], place it on the correct side to dampen the amount of damage you will take after you're done amputating the limb.
description= description=
description=Keep in mind that if you amputate your upper arm, you won't be able to equip any prosthesis. description=Keep in mind that if you amputate your [b]upper arm[/b], you won't be able to equip any prosthesis.
description=
description=After you've amputated a limb, you will gain [b]skill points[/b] for the amputated side, making timed actions faster in due time.
description= description=
@@ -59,7 +79,7 @@ description=If your limb isn't completely cicatrized, you can still equip prosth
description= description=
description=[h2]Prosthesis[/h2] description=[h2]Prosthetics[/h2]
description=If you're missing a hand, you won't be able to do a lot of things, such as equipping two-handed weapons. With prosthetics limbs, you can fix that. description=If you're missing a hand, you won't be able to do a lot of things, such as equipping two-handed weapons. With prosthetics limbs, you can fix that.
@@ -77,22 +97,32 @@ description=
description=The main difference between the twos is that your actions will take longer with the Hook Prosthesis. description=The main difference between the twos is that your actions will take longer with the Hook Prosthesis.
description=When you equip a prosthetic limb, you will slowly gain skill points in the [b]Prosthesis Familiarity[/b] perk, making actions more speedy in due time.
description= description=
description=[h1]Issues[/h1] description=[h2]Admin tools[/h2]
description=Got any issues? Report them on GitHub! description=If something strange happened, an admin can reset TOC mechanics on any player by right clicking on them and select [i]"Reset Amputations"[/i]. They could also do the opposite by clicking on [i]"Force Amputation"[/i] for each amputable limb.
description=
description=[hr][/hr]
description=
description=[h1]Issues and bugs?[/h1]
description=Got any issues or found some pesky bugs? Report them on GitHub!
description=
description=[url=https://github.com/ZioPao/The-Only-Cure][img]https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white[/img][/url] description=[url=https://github.com/ZioPao/The-Only-Cure][img]https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white[/img][/url]
description= description=
description=
description=[h1]Credits[/h1] description=[h1]Credits[/h1]
description=
description=[table] description=[table]
description= [tr] description= [tr]
@@ -127,11 +157,19 @@ description= [th]Compatibility API[/th]
description= [/tr] description= [/tr]
description= [tr]
description= [th]JCloudJalix, Rinary1, pllq, ttaeo, pgmbru[/th]
description= [th]Translations[/th]
description= [/tr]
description=[/table] description=[/table]
description= description=
description=[hr] description=[hr][/hr]
description= description=
@@ -141,7 +179,7 @@ description=[url=https://ko-fi.com/M4M7IERNW][img]https://storage.ko-fi.com/cdn/
description= description=
description=[hr] description=[hr][/hr]
tags=Build 41;Balance;Interface;Items;Misc;Multiplayer;Realistic;Textures tags=Build 41;Balance;Interface;Items;Misc;Multiplayer;Realistic;Textures
visibility=private visibility=public