diff options
| author | Michael Tews <michael@tews.dev> | 2025-12-18 22:39:25 +0100 |
|---|---|---|
| committer | Michael Tews <michael@tews.dev> | 2026-04-12 11:21:47 +0200 |
| commit | f50bdd8103c2e4c6cb22f124e70124258aea0e7e (patch) | |
| tree | c722d42ffb375f60f7d793a467e064ad8b20e4e9 | |
Signed-off-by: Michael Tews <michael@tews.dev>
| -rw-r--r-- | .emmyrc.json | 23 | ||||
| -rw-r--r-- | .gitmodules | 3 | ||||
| m--------- | Umbrella | 0 | ||||
| -rw-r--r-- | build.lua | 161 | ||||
| -rw-r--r-- | mods/zombification-chance/42/media/lua/server/zombChance.lua | 119 | ||||
| -rw-r--r-- | mods/zombification-chance/42/media/lua/shared/zombChance_SandboxOverride.lua | 7 | ||||
| -rwxr-xr-x | mods/zombification-chance/42/media/sandbox-options.txt | 8 | ||||
| -rw-r--r-- | mods/zombification-chance/42/mod.info | 3 | ||||
| -rw-r--r-- | mods/zombification-chance/common/.gitkeep | 0 | ||||
| -rw-r--r-- | mods/zombification-chance/mod.info | 3 |
10 files changed, 327 insertions, 0 deletions
diff --git a/.emmyrc.json b/.emmyrc.json new file mode 100644 index 0000000..c2ac08a --- /dev/null +++ b/.emmyrc.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/refs/heads/main/crates/emmylua_code_analysis/resources/schema.json", + "workspace": { + "library": ["Umbrella/library"], + "workspaceRoots": [ + "mods/zombification-chance/42/media/lua", + "examples/JSSevereInfection42/42/media/lua", + "examples/3268487204/42.13/media/lua", + "examples/3581134738/42/media/lua" + ] + }, + "runtime": { + "requirePattern": [ + "shared/?.lua", + "client/?.lua", + "server/?.lua" + ], + "version": "Lua5.1" + }, + "diagnostics": { + "disable": ["unnecessary-if"] + } +}
\ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..88b822c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Umbrella"] + path = Umbrella + url = git@github.com:PZ-Umbrella/Umbrella.git diff --git a/Umbrella b/Umbrella new file mode 160000 +Subproject 17381967473cc2b40a7878f3c8aebe68210b63e diff --git a/build.lua b/build.lua new file mode 100644 index 0000000..9014b44 --- /dev/null +++ b/build.lua @@ -0,0 +1,161 @@ +local lfs = require("lfs") + +local enableDebug = true +local enableImGui = false +local flatpakSteam = true + +-- Name of your local workshop container +local workshopName = "MyModWorkshop" + +-- Mods live here inside the git repo +local repoModsDir = "mods" + +local isWindows = package.config:sub(1, 1) == "\\" +local sep = isWindows and "\\" or "/" + +local homeDir = isWindows and os.getenv("userprofile") or os.getenv("HOME") +local flatpakSteamVar = not isWindows and os.getenv("HOME") .. "/.var/app/com.valvesoftware.Steam" + +-- Launch via Steam +local pzCacheRoot +local pzExecutable +if isWindows then + pzCacheRoot = (homeDir .. "\\Zomboid") + pzExecutable = "steam.exe -applaunch 108600" +else + pzExecutable = flatpakSteam and "flatpak run com.valvesoftware.Steam -applaunch 108600" or "steam -applaunch 108600" + pzCacheRoot = flatpakSteam and (flatpakSteamVar .. "/Zomboid") or (homeDir .. "/Zomboid") +end + +-- Final destination: Workshop/<Name>/Contents/mods +local pzModsDir = pzCacheRoot .. sep .. "Workshop" .. sep .. workshopName .. sep .. "Contents" .. sep .. "mods" + +-- ========================= +-- File helpers +-- ========================= + +local function copyFile(src, dst) + local inFile = assert(io.open(src, "rb")) + local data = inFile:read("*all") + inFile:close() + + local outFile = assert(io.open(dst, "wb")) + outFile:write(data) + outFile:close() +end + +local function mkdir(path) + if isWindows then + os.execute(string.format('mkdir "%s" 2>nul', path)) + else + os.execute(string.format('mkdir -p "%s"', path)) + end +end + +local function copyDir(src, dst) + mkdir(dst) + for name in lfs.dir(src) do + if name ~= "." and name ~= ".." then + local srcPath = src .. sep .. name + local dstPath = dst .. sep .. name + local attr = lfs.attributes(srcPath) + if attr.mode == "directory" then + copyDir(srcPath, dstPath) + else + copyFile(srcPath, dstPath) + end + end + end +end + +-- ========================= +-- Safety helpers +-- ========================= + +local function normalizePath(path) + path = path:gsub("\\", "/") + return path:gsub("/+$", "") +end + +local function isRootPath(path) + if path == "/" then + return true + end + if path:match("^[A-Za-z]:$") then + return true + end + return false +end +local function isSubPath(child, parent) + child = normalizePath(child) + parent = normalizePath(parent) + return child:sub(1, #parent + 1) == parent .. "/" +end + +local function validatePath(target, root, home) + local t = normalizePath(target) + local r = normalizePath(root) + local h = normalizePath(home) + + assert(t ~= "", "Refusing to delete empty path") + assert(not isRootPath(t), "Refusing to delete root path") + assert(t ~= h, "Refusing to delete home directory") + assert(isSubPath(t, r), "Refusing to delete outside workshop mods") + assert(#t > #r + 1, "Refusing to delete mods root") +end + +-- ========================= +-- Discover mods dynamically +-- ========================= + +local mods = {} + +for name in lfs.dir(repoModsDir) do + if name ~= "." and name ~= ".." then + local path = repoModsDir .. sep .. name + local attr = lfs.attributes(path) + if attr and attr.mode == "directory" then + table.insert(mods, name) + end + end +end + +assert(#mods > 0, "No mods found in ./mods directory") + +-- ========================= +-- Copy mods +-- ========================= + +print("Installing mods into workshop:") +mkdir(pzModsDir) + +for _, modName in ipairs(mods) do + local src = repoModsDir .. sep .. modName + local dst = pzModsDir .. sep .. modName + print(" - " .. modName) + print(" - copied" .. src .. " to " .. dst) + validatePath(dst, pzModsDir, homeDir) + copyDir(src, dst) +end + +print("All mods copied successfully!") + +-- ========================= +-- Launch Project Zomboid +-- ========================= + +local params = {} +table.insert(params, "-modfolders workshop,mods") + +if enableDebug then + table.insert(params, "-debug") +end +if enableImGui then + table.insert(params, "-imgui") +end + +local cmd = string.format("%s %s", pzExecutable, table.concat(params, " ")) + +print("Launching:") +print(cmd) +os.execute(cmd) diff --git a/mods/zombification-chance/42/media/lua/server/zombChance.lua b/mods/zombification-chance/42/media/lua/server/zombChance.lua new file mode 100644 index 0000000..b32618d --- /dev/null +++ b/mods/zombification-chance/42/media/lua/server/zombChance.lua @@ -0,0 +1,119 @@ +local debug = false + +--- Appends an integer to a separated string +---@param str string current string +---@param separator string separator +---@param value integer integer to append +---@return string new string +local function appendIntToSeparatedString(str, separator, value) + if str == "" then + return tostring(value) + end + return str .. separator .. tostring(value) +end + +---@diagnostic disable-next-line +local zombificationChance = SandboxVars.ZombificationChance.chance + +local zombificationBodyPartsKey = "zombificationChance-infected" + +--- Splits a string at a given separator +---@param inputstr string +---@param separator string +---@return table<integer, string> +local function splitString(inputstr, separator) + assert(separator ~= "", "Separator must be provided") + local result = {} + for element in string.gmatch(inputstr, "([^" .. separator .. "]+)") do + table.insert(result, element) + end + return result +end + +--- Returns true if zombification should occur based on chance +---@return boolean +local function rollZombification() + return ZombRand(100) <= zombificationChance +end + +--- Event handler: called when player receives damage +---@param character IsoGameCharacter +---@param damage number +local function onPlayerReceiveDamage(character, damageType, damage) + local bodyDamage = character:getBodyDamage() + local usedBodyPartIndexes = splitString(character:GetVariable(zombificationBodyPartsKey), " ") + + for i = 0, bodyDamage:getBodyParts():size() - 1 do + local bodyPart = bodyDamage:getBodyParts():get(i) + + if bodyPart:IsFakeInfected() then + local alreadyProcessed = false + for _, usedIndex in ipairs(usedBodyPartIndexes) do + if tonumber(usedIndex) == i then + alreadyProcessed = true + break + end + end + + if not alreadyProcessed then + -- Mark this body part as processed + character:SetVariable( + zombificationBodyPartsKey, + appendIntToSeparatedString(character:GetVariable(zombificationBodyPartsKey), " ", i) + ) + + if rollZombification() then + if debug then character:Say("[PZC] Index: " .. + local i = 0 + return function() + i = i + 1 + if i <= #bodyPartIndexes then + return (bodyPartIndexes[i]) + end + end +end + +--- Removes an element from a separated string by value +---@param str string original string +---@param value string|number element to remove +---@param separator string separator (must be provided) +---@return string +function removeElementFromSeparatedString(str, value, separator) + assert(separator ~= "", "Separator must be provided") + value = tostring(value) + + + local result = {} + for element in string.gmatch(str, "([^" .. separator .. "]+)") do + if element ~= value then + table.insert(result, element) + end + end + + return table.concat(result, separator) +end + +--- Event handler: called every update for a player +---@param player IsoPlayer +local function forgetOldFakeInfections(player) + local bodyDamage = player:getBodyDamage() + + for bodyPartIndex in ProcessedBodyPartIndeciesIterator(player) do + local bodyPart = bodyDamage:getBodyParts():get(bodyPartIndex) + if not bodyPart:IsFakeInfected() then + if debug then player:Say("[PZC] Index: " .. bodyPart:getIndex() .. ", isNotFakeInfected, now forgotton") end + player:SetVariable( + zombificationBodyPartsKey, + removeElementFromSeparatedString( + player:GetVariable(zombificationBodyPartsKey), + bodyPartIndex, + " " + ) + ) + end + end +end + +-- Register events +Events.OnPlayerUpdate.Add(forgetOldFakeInfections) +Events.OnPlayerGetDamage.Add(onPlayerReceiveDamage)
\ No newline at end of file diff --git a/mods/zombification-chance/42/media/lua/shared/zombChance_SandboxOverride.lua b/mods/zombification-chance/42/media/lua/shared/zombChance_SandboxOverride.lua new file mode 100644 index 0000000..7dcff9b --- /dev/null +++ b/mods/zombification-chance/42/media/lua/shared/zombChance_SandboxOverride.lua @@ -0,0 +1,7 @@ +function zombChanceSandboxOverride() + getSandboxOptions():set("ZombieLore.Mortality", 7); +end + +Events.OnGameStart.Add(zombChanceSandboxOverride) +Events.OnGameBoot.Add(zombChanceSandboxOverride) +Events.OnLoad.Add(zombChanceSandboxOverride)
\ No newline at end of file diff --git a/mods/zombification-chance/42/media/sandbox-options.txt b/mods/zombification-chance/42/media/sandbox-options.txt new file mode 100755 index 0000000..b28e4ce --- /dev/null +++ b/mods/zombification-chance/42/media/sandbox-options.txt @@ -0,0 +1,8 @@ +VERSION = 1,
+
+option ZombificationChance.chance
+{
+ type = double, min = 0, max = 100, default = 20,
+ page = ZombificationChance,
+ translation = ZombificationChance_chance,
+}
\ No newline at end of file diff --git a/mods/zombification-chance/42/mod.info b/mods/zombification-chance/42/mod.info new file mode 100644 index 0000000..4a00b36 --- /dev/null +++ b/mods/zombification-chance/42/mod.info @@ -0,0 +1,3 @@ +name=Zombification-chance +id=ZombificationChance +description=Makes Zombification occur randomly when bitten
\ No newline at end of file diff --git a/mods/zombification-chance/common/.gitkeep b/mods/zombification-chance/common/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mods/zombification-chance/common/.gitkeep diff --git a/mods/zombification-chance/mod.info b/mods/zombification-chance/mod.info new file mode 100644 index 0000000..4a00b36 --- /dev/null +++ b/mods/zombification-chance/mod.info @@ -0,0 +1,3 @@ +name=Zombification-chance +id=ZombificationChance +description=Makes Zombification occur randomly when bitten
\ No newline at end of file |
