Working zombies amputations once again
This commit is contained in:
@@ -1,151 +1,147 @@
|
|||||||
-- 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")
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
-- local ItemsController = require("TOC/Controllers/ItemsController")
|
---@param zombie IsoZombie|IsoGameCharacter|IsoMovingObject|IsoObject
|
||||||
-- local StaticData = require("TOC/StaticData")
|
---@return integer trueID
|
||||||
-- local CommandsData = require("TOC/CommandsData")
|
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
|
||||||
|
|
||||||
-- ---@param item InventoryItem
|
-- trueID
|
||||||
-- local function PredicateAmputationItems(item)
|
bits[16] = 0
|
||||||
-- return item:getType():contains("Amputation_")
|
local trueID = Long.parseUnsignedLong(string.reverse(table.concat(bits, "")), 2)
|
||||||
-- end
|
|
||||||
|
|
||||||
|
return trueID
|
||||||
|
end
|
||||||
-- 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
|
|
||||||
-- 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)
|
|
||||||
|
|
||||||
|
|
||||||
-- -- Add reference and transmit it to server
|
|
||||||
-- local pID = GetZombieID(zombie)
|
|
||||||
-- local zombieKey = CommandsData.GetZombieKey()
|
|
||||||
-- 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
|
|
||||||
|
|
||||||
-- -------------------------------
|
|
||||||
|
|
||||||
-- ---@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()
|
---@param item InventoryItem
|
||||||
-- local zombieKey = CommandsData.GetZombieKey()
|
local function PredicateAmputationItems(item)
|
||||||
-- localOnlyZombiesMD = ModData.getOrCreate(zombieKey)
|
return item:getType():contains("Amputation_")
|
||||||
|
end
|
||||||
|
|
||||||
-- end
|
---@param item InventoryItem
|
||||||
|
local function PredicateAmputationItemLeft(item)
|
||||||
|
return item:getType():contains("Amputation_") and item:getType():contains("_L")
|
||||||
|
end
|
||||||
|
|
||||||
-- Events.OnInitGlobalModData.Add(SetupZombiesModData)
|
---@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
|
||||||
|
|
||||||
-- ---@param zombie IsoZombie
|
|
||||||
-- local function ReapplyAmputation(zombie)
|
|
||||||
-- local pID = GetZombieID(zombie)
|
|
||||||
|
|
||||||
-- if localOnlyZombiesMD[pID] ~= nil then
|
ItemsController.Zombie.SpawnAmputationItem(zombie, amputationFullType)
|
||||||
-- -- 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']
|
-- Add reference and transmit it to server
|
||||||
-- if rightAmp then
|
local pID = GetZombieID(zombie)
|
||||||
-- ItemsController.Zombie.SpawnAmputationItem(zombie, rightAmp)
|
local zombieKey = CommandsData.GetZombieKey()
|
||||||
-- 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
|
||||||
|
|
||||||
-- -- Removes reference, local only
|
-------------------------------
|
||||||
-- localOnlyZombiesMD[pID] = nil
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- Events.OnZombieUpdate.Add(ReapplyAmputation)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local bloodAmount = 10
|
||||||
|
|
||||||
|
|
||||||
|
---@param player IsoGameCharacter
|
||||||
|
---@param zombie IsoZombie
|
||||||
|
---@param handWeapon HandWeapon
|
||||||
|
local 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
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
if not zombieInv:containsEval(PredicateAmputationItemLeft) then
|
||||||
|
SpawnAmputation(zombie, "L")
|
||||||
|
elseif not zombieInv:containsEval(PredicateAmputationItemRight) then
|
||||||
|
SpawnAmputation(zombie, "R")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- 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()
|
||||||
|
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)
|
||||||
|
|||||||
216
media/lua/client/lua_timers.lua
Normal file
216
media/lua/client/lua_timers.lua
Normal 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
|
||||||
@@ -3,5 +3,7 @@ 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_ZombieAmputationDamageThreshold = "Zombie amputations damage treshold",
|
||||||
|
Sandbox_TOC_ZombieAmputationDamageChance = "Zombie amputations damage chance",
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,3 +28,22 @@ option TOC.SurgeonAbilityImportance
|
|||||||
translation = TOC_SurgeonAbilityImportance,
|
translation = TOC_SurgeonAbilityImportance,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user