#mod_development
1 messages · Page 231 of 1
I was trying to modify sodas for multiplayer to give a fatigue reduction, using the sodas have caffeine mod as an example, but it looks like it doesn't work for multiplayer
is there a simple way to modify an items stats that works in multiplayer?
I don't know if this helps
https://github.com/FWolfe/Zomboid-Modding-Guide/tree/master/structure
local function myFunc1(Name, Property, Value)
local Item = ScriptManager.instance:getItem(Name)
if Item then
Item:DoParam(Property.." = "..Value)
end
end
myFunc1("Base.Ghillie_Top", "FabricType", "Leather");
myFunc1("Base.JacketLong_Santa", "FabricType", "Cotton");
myFunc1("Base.JacketLong_SantaGreen", "FabricType", "Cotton");
myFunc1("Base.Jacket_Fireman", "FabricType", "Denim");
myFunc1("Base.Jacket_Padded", "FabricType", "Leather");
myFunc1("Base.Jacket_PaddedDOWN", "FabricType", "Leather");
media/lua/client/
Could anyone help me compile java? I decombiled the game using beautiful-java and made changes to baseVehicle.java but when I compile it it says packages missing.
how to open console log into -debug mode?
working ten second trigger
local last_event = 0;
local secundProshlo = 0;
local function ServerTestHuynya()
local now = os.time();
if now - last_event > 10 then -- where TEN - it's realtime sec
print("-- sec");
print("10 sec");
print("-- sec");
secundProshlo = secundProshlo + 10;
print("Sec past:" .. secundProshlo);
last_event = now
-- Start func from here!
end
end
Events.OnTick.Add(ServerTestHuynya);
by Star
I don't have the build button anymore
ok time to delete all files
fresh start
which file do I change?
- BaseVehicle from the sources folder
- BaseVehicle from the decompiled folder
@red tiger Could you please help me ?
20-Apr-24 12:26 PM - Build completed with 100 errors and 0 warnings in 5 sec, 632 ms
i want to create here new button like table, with voice toggle, for
https://steamcommunity.com/sharedfiles/filedetails/?id=2984481769
You mean you want to add a button to the character creation panel?
I do not fully understand your comment.
slot with table, lower than character model something like that, but:
Voice
______________________________
*tables with voice choices* [button with play this voice]
something like that
but for voice
Ohhhhh
Well that's a mod so you would need to figure out how they added those windows, and then repurpose them for audio demonstration and draw them below the bottom of those modded areas of the screen.
And if you're going to have to reuse or mod any of their code to do it, it would be good to ask.
yep... some day...
never tried work with UI
I don't know how they add those tbh... So many character creation panel mods add chunks with no gamepad support that I've come to avoid them all reflexively.
ahh yep... Once I wanted to add support for gamepads to Better Lockpicking. But same problem - no skill, no gamepad for test etc xD
Yeah I don't mind having a mod here and there on my server that doesn't support us, but the character creation panel and other essential panels gotta be supported or I don't add em
Not gonna break support for gamepad on menus that are unavoidable to gamepad players on my server.
Not worth what little is gained in doing so to me
The vanilla hair menu always worked fine for me lol
I ended up just making my own personal mod for my friends and for me that simply adjusts the spacing of elements on that page so that things can actually be read by gamepad players and long entries don't get chopped off on large fonts, and I avoid all other edits to that menu.
Good morning.
Hello there, for once, I want to create a proper tiles pack. Is there a guide that explains how to set each property for each created tile ? I guess it must be done in the .tiles file, if it is not in the .pack itself.
Quick video on getting custom tiles in the project zomboid editors (buildinged, tiled and worlded)
If you like what im doing please consider donating to my Patreon.
https://www.patreon.com/DaddyDirkieDirk
would this get the trait its desired recipes or do I have to put into its own function/lua file
https://www.projectzomboid.com/modding/zombie/characters/traits/TraitFactory.html#addTrait(java.lang.String,java.lang.String,int,java.lang.String,boolean)
addTrait returns a TraitFactory.Trait and to that you must add the recipes
require('NPCs/MainCreationMethods' );
local function AddPipebombTrait()
local chemMunitionsTrait = TraitFactory.addTrait("ChemicalMunitions Proficiency", "Chemical Munitions Proficiency", ...
chemMunitionsTrait:getFreeRecipes():add("Make Aerosol bomb");
chemMunitionsTrait:getFreeRecipes():add("Make Flame bomb");
chemMunitionsTrait:getFreeRecipes():add("Make Pipe bomb");
chemMunitionsTrait:getFreeRecipes():add("Make Noise generator");
chemMunitionsTrait:getFreeRecipes():add("Make Smoke Bomb");
end
Events.OnGameBoot.Add(AddPipebombTrait);
declaration: package: zombie.characters.traits, class: TraitFactory
ah ok thank you
is it possible to include tile definitions on farm tiles? 
hm nevermind
I would love to see a fully dark mode. The big white inputs feel a bit off for me. You generally don't expect black inputs on a light theme app, so perhaps that's a bit of what feels off to me about it. But also it's just such a bright color lol. I could do tan. White is very white. I know it probably seems minor but I would bet you I'm just going to be among the first and far from the only people who comment about this when they first experience it.
I agree. =)
Blocking is a phase in UI design. This is where I'm at currently.
Heard.
I'm currently working on the left side nav. It's going to have responsive color changes when interacted.
That completely makes sense
The majority of the UI is blocked which is good.
The program also has implemented code so it's not just a shell at this point either.
The design is to allow for the site to be hosted on GitHub so anyone with a computer browser can access and use it.
Good afternoon.
No surprise. It likely means that everyone's busy modding.
I'd ask about growing farm tiles that have properties (for e.g. trees, fences..) but looking into the lua I'm stumped. When I grow stuff, they have no properties. Just plain walk-through tiles
I keep thinking there is a file somewhere in vanilla that is doing that
Kinda don't want to add a manual lua check for tile properties either because knowing players they'd build big farms and that'd be a nightmare
Say me pls how do I find out the state of the variable for the second player? The server sends commands to two players to create UI windows and then sets Window.instance = true for this player
-- client
Window = ISPanel:derive("Window")
Window.instance = false
function createrequesterwindow()
-- code
Window.instance = true
end
function createrecipientwindow()
-- code
Window.instance = true
end
what do I need to add to this line before creating a context menu to check the player that I clicked for Window.instance?
if not Window.instance then
-- create context menu when click on Isoplayer
end
or do I need to create a table with the data of the first and second players? If this is so, then please tell me how I can implement this?
Now we're talkin
Oh yeah I'm a nerd about dark themes.
Perfect
I'll fine-tune the palette last.
This is about the look and feel contrast I want.
The tool is already 75ish percent working.
Need to use the player's display name or online id to find matching player object when the command is received. Server should include that info in packet it send to the players.
local player = nil
for index = 0, getNumActivePlayers() - 1 do
player = getSpecificPlayer(index)
if player:getOnlineID() == packet.id then break else player = nil end
end```
Something like this @muted garnet
A lot cleaner.
Uniform spacing and coloring.
=)
Oh and this is how the parameter list scales.
@thick karma
Looks very clean. Good shit.
Thanks.
Hi, I have a small question about TraitFactory.setMutualExclusive(a, b)
I thought that this means you cannot have one and the other, only one OR the other. But I did some test and I can have both at the same time and multiple of the same trait
to fix this I just did some checks when I add the trait but does anyone know what this actually does since my interpretation was wrong.
I think that's what it's supposed to do and I have seen mods successfully achieve mutual exclusivity in menu, but I've never been 100% sure how. I always assumed they used that. I imagined you could dynamically add two mutually exclusive traits in theory, but you couldn't choose them in the creation menu.
Did you find that you could add them at character creation or dynamically?
Or both?
I don't know what you mean but if I take you literally, adding the traits at character creation (the game handles it for me) and dynamically works just fine
wait I think I know what you mean, I couldn't add them both in the trait selection menu. but you can still dynamically add them both so I had to do
if data.daysNotBrushed >= sandboxVars.BadTraitCount and player:HasTrait("FoulBrusher") == false then
if player:HasTrait("GoldenBrusher") then
traits:remove("GoldenBrusher")
end
traits:add("FoulBrusher")
elseif data.daysBrushedToMax >= sandboxVars.GoodTraitCount and player:HasTrait("GoldenBrusher") == false then
if player:HasTrait("FoulBrusher") then
traits:remove("FoulBrusher")
end
traits:add("GoldenBrusher")
end
but you've (indirectly) just brought an unrelated issue to light which is even if you add my trait which costs 2 points, you will still lose it after a day if you don't brush
To anyone who is having the "failed to update workshop item, result=2" issue, try and disable your firewall. I tried a million things and nothing worked but disabling firewall did the trick. Make sure to enable it once you're finished.
work only in create character menu
its mean Mutual Exclusive can be pickable in create menu, but ingame u can get both
I see, thanks
Good morning.
Anyone know how to add a weapon to the home pistol gun cases?
well actually more accurately
what the container ID is and how they always manage to pair ammo mag and pistol when they appear together
Oh... Nevermind, I see they are all just different cases with the same appearance
require 'Items/ItemPicker'
require 'Items/Distributions'
table.insert(Distributions.PistolCase1.rolls, 1);
table.insert(Distributions.PistolCase1.items, "yourModule.yourItemId");
table.insert(Distributions.PistolCase1.items, 1);
table.insert(Distributions.PistolCase1.junk, "yourModule.yourItemId");
table.insert(Distributions.PistolCase1.junk, 1);
PistolCase1 - it's Base.PistolCase1 for example
Case like Distribution
CaseLike item for spawn in ProceduralDistribution
so i'd need to make an item that always has items inside it
And then add it to the bedroom drop tables?
yep, something like that, you create new item same like GunCase (container, or bag), put this item somewhere in ProceduralDistribution, put in this item another items
If something is value 200 it's guaranteed right?
if im not wrong, it's based on ROLLS + loot setting.
for example like:
Rolls = 4
Item1 // SpawnWeight 5
Item2 // SpawnWeight 20
Item3 // SpawnWeight 200
Item4 // SpawnWeight 25
-------------------------
Total weight 250
devides on
0 / 5 / 25 / 225 / 250
Randomize from 0 to 250
from 0 to 5 -- it's first item
from 5 to 25 -- item2
from 25 to 225 -- item3
from 225 to 250 -- item4
Randomizer start 4 times and get item in pool list.
200 - it's not spawn chance in total
it's weight on pool list
This is the most simplified logic diagram, I am sure that in reality it is more complicated
But this has 1 roll and spawns all 3 items usually
Or does each entry get a roll
i think it's roll 3 times and get one items per roll
i mean, not 3, i mean roll count
like this
But you have sandbox option with loot setting
more or less
it's should upgrade rolls count
Good evening.
Good morning.
I don’t quite understand, please explain in more detail how I can get window.instance for a specific player?
server:
if command == 'GetPlayerID' then
args.id = args[1]
sendServerCommand("nil", "PlayerObjID", args)
end
client:
function()
local clickedPlayerID = clickedPlayer:getOnlineID()
sendClientCommand("123", "GetPlayerID", {clickedPlayerID})
-- code to create context menu when click on player
how can I implement here check to window.instance for clickedplayer?
end
Events.OnServerCommand.Add(function(module, command, args)
if command == 'PlayerObjID' then
local player = nil
for index = 0, getNumActivePlayers() - 1 do
player = getSpecificPlayer(index)
if player:getOnlineID() == args.id then
if player and Window.instance then
print("123playerwindow")
end
break
else player = nil end
end
end
end)
Check uncapped fps with neither, one, and then the other, and compare? If their performance impacts are significant you might see something there. Also could watch them from task manager for spikes of network data transfer if that's relevant.
it's hard...
because my fps...
It jumps in both cases, but I'm wondering which code-level mod is easier

Show us the code diff perhaps ?
Subscribe to my Jumper mod and you can see how that works. It brings people along for a teleport who are standing near you. The feature is associated with the keyword "Buddies" or "Buddy" iirc, so you can search for that part of the code. You can refactor anything you need from Jumper (change function and module names but keep the structure you need).
You can just use the same logic to find the right singular player and have them do something when another player triggers the action
thanks, I'll get to it right now
Idk what you mean by "it jumps in both cases" nor do I see the connection between "performance" and "easier" (those may be opposite answers)
Specifically, if you want to be able to write packet.id when the packet arrives, you need to form the packet like so:
{ id = player:getOnlineID() }
If you don't write the id = part, it'll by default be stored using 1 as the key (it'll store what you're storing as an index of an indexed array)
{ foo, bar } => { [1] = foo, [2] = bar }
Follow? @muted garnet
not quite, what do you mean by packet and it`s storing? I don't quite understand how to use this package, like, I can write:
args.playerID = player:getOnlineID()
args.clickedplayerID = clickedplayer:getOnlineID()
and then use this players id for both players?
You can, sure, assuming both of those player objects are correct and args = {} (a table) before you try storing stuff in it and sending it. I just refer to args as a packet when it's for sending arguments over a network. I'm just referring to the args table that you can pass from client to server and back.
And I would in your case use the ID serverside to decide which players to send a response
Do you acquire the player objects on client or on server? And have you printed them somehow, to make sure you correctly obtain two different IsoPlayer objects?
Players are obtained from the client (playerObj and click Player) and then sent to the server, I have no problems transferring the Ids of players to each other, now I am trying, using your method, to set up the transfer of the value of a specific player Window.instance for all players
What do you mean by the value of that Window.instance? You can only pass a copy of the Lua-based table data of the instance. The actual instance cannot be passed between computers. You can only pass that kind of thing by reference from one place to another on the same client.
You could pass what they need to instantiate their own copy of Window.instance
how to ZombRand but something like double in result?
Also I think I saw the other day that your Window module is a global object; meant to tell you I would discourage that. Not only would it be better to make that local, but if you ARE committed to making it global, "Window" is a terribly unsafe global name for a variable because of the likelihood of reuse by other new modders.
ZombRandFloat?
I think ZombRand can return a float somehow but idr how.
Or there is some other function that can
Yes, this works actually.
ZombRandFloat(1, 2) => 1.43492429
Just worked for me
thanks :3
Good morning.
This is a coding high that not many people get to experience. Great job.
I hate that TIS forces me to use a casino-grade pseudo-random number generator without options.
I personally would rather use a mid-quality PRNG like a Xor128 shift one.
lol harsh but I hear you. I think speed is usually the most important goal of a pseudorandom generator for most gaming purposes, rather than appearing closest to truly random. I do not know their algorithm tbh because I've never checked that part of the code, but I would hope it was chosen for speed.
it's the opposite...
Really?
It's more honest than harsh because the perspective comes from optimization. Not everything needs a high-quality prng. Depending on what calls the PRNG, the game could run faster on weaker systems with less calls to the high-quality PRNG and more to something weaker but still good enough to get things done.
Which is the opposite? Their goal or their choice?
RJ's reasoning why he wants the high-quality PRNG was for zombie simulation to be better. This is what he told me in the late 2010's.
@thick karma
In my context, Window.instance - it`s only test name for variable, that set in true for client player, when a modal window from other player appears on his screen and vice versa:
client side:
Window.instance = false
function Window:OnSentRequest()
-- creating modal window to player, that sended request
Window.instance = true -- set this variable in true for client
end
function Window:OnRecieveRequest()
-- creating modal window to player, that recieved request from player, that sended it
Window.instance = true -- set this variable in true for client
end
creating context menu to send request to player, that we clicked, client side:
function 123()
if clickedPlayer then
if not Window.instance then -- here code only checking client variable (Window.instance) and I wanna check clickedPlayer Window.instance (like: if not Window.instance(clickedPlayer))
creating context menu for client player to send request
end
end
end
Events.OnFillWorldObjectContextMenu.Add(123)
I asking you about way to get clickedPlayer Window.instance and transfer it to requesterPlayer and vice versa
Oh so they intentionally chose an excessively hardworking pseudorandom process?
Instead of a faster one?
For sure. It was entirely intentional. What I'm saying is that apart from zombie simulations and other areas they deem necessary to use such a quality PRNG, we should use something more optimized.
I released a mod during build 30 and 31 that replaced their PRNG with something even faster than java.awt.Random and it helped people who had dated technology play smoother.
ZombRand does have a footprint at the scale it's being used although I can argue that it's less of a footprint in 2024.
I seeeeeeeeee...
It's good that they're aware of it.
Well then.
Lookin good. Is this tool aiming to improve upon and replace the online javadoc many of us use? Because I would totally use it.
lol it is pretty.
Shoutout to @bronze yoke for being an absolute brute in this channel - always helpful and quick responses - amazing!
Almost anything I search albion has a good answer for it, very helpful! 👌
This tool is a form-based tool that allows me to add to a format I created that interfaces with Umbrella, the tool that forwards those things, so yes.
I wrote Umbrella. @bronze yoke maintains it.
She worked on the events typings originally and we formed a union to provide this platform for documentation to be possible.
I will always say that modders have so much power right now to improve the quality of life for modding at large thanks to these tools and I'm working right now to make this a reality.
@red tiger can you compare the code and tell me which one is better for performance, please? ^^'
I can try.
my:
So I plan to personally document the ISUI library. This is the big baddy for most people.
Alright so knowing that the functionality is the same I can safely say:
- You do not repeat the same calls to get inner references which is the biggest gripe I have for all modding code in PZ. Good job.
- Your code does not index randomly or shift.
- Your code contains documentation which is rare.
I would document for code that does state switching or resetting just to let the reader know why.
Your code is otherwise trying to follow standards and this should be encouraged at large.
The code you're posting is a bit too pseudo to know whether you are doing things in a way that makes any sense, tbh.
I would not recommend using "Window.instance" as the name of the field that is simply true or false based on whether a player can see the window, because .instance has a precedent in Zomboid as referencing an actual instance, meaning a specific implementation of an object that can have multiple versions (e.g., there is a Zombie-type object class and their are instances of that class, which we tend to call zombie objects or just zombies in practice). Meaning, it doesn't just reference a boolean variable, traditionally, in Project Zomboid vanilla files or in mods.
Your pseudocode example function takes no parameters and suddenly you say you have access to "clickedPlayer" but we have no clue how, so I'm not really sure how far along your code is in terms of correctly accomplishing the "sub-goals" of your overall goal.
It would help if you post actual code so we can see where you're at. What do your client and server files actually look like?
The most immediate way to improve PZ Lua and Java code is to follow DRY code principle. (Do-not Repeat Yourself)
Take the time and make a line to assign variables before going into code.
It will make your code so much better.
Simple and legible code leads to optimized code practices.
Shortcuts only hurts you and the bottom-dollar with performant code.
y mean like all local should be in header into function?
More like if you
globalObject.someStaticReference.innerVar:executeFunc(..)
and you call this more than once, store innerVar as a local.
I'll try to tell you in more detail:
this is client side code, that summon context menu option, when you click on other player, it grabbing clickedPlayer from vanilla ISWorldObjectContextMenu function
ISWorldObjectContextMenu.Window = function(player, context)
local playerObj = getSpecificPlayer(player)
if clickedPlayer and clickedPlayer ~= playerObj and not clickedPlayer:isAsleep() and isClient() then
print(playerObj:getModData().Windowing)
print(clickedPlayer:getModData().Windowing)
if not WindowUI.instance then
local option = context:addOption(getText("ContextMenu_Trade1", clickedPlayer:getDisplayName()), context, ISWorldObjectContextMenu.Windowing, playerObj, clickedPlayer) --
if math.abs(playerObj:getX() - clickedPlayer:getX()) > 2 or math.abs(playerObj:getY() - clickedPlayer:getY()) > 2 then
local tooltip = ISWorldObjectContextMenu.addToolTip();
option.notAvailable = true;
tooltip.description = getText("ContextMenu_GetCloserToTrade1", clickedPlayer:getDisplayName());
option.toolTip = tooltip;
end
end
end
end
ISWorldObjectContextMenu.Windowing = function(context, playerObj, clickedPlayer)
local playerID = playerObj:getOnlineID()
local clickedPlayerID = clickedPlayer:getOnlineID()
if not WindowUI.instance then
print("12345678")
sendClientCommand("123", "TradeRequestSent", {playerID, clickedPlayerID})
end
end
Events.OnFillWorldObjectContextMenu.Add(ISWorldObjectContextMenu.Window)
this is server side code to send commands to appear UI windows for clickedPlayer and player
Events.OnClientCommand.Add(function(module, command, player, args)
if command == "TradeRequestSent" then
if args[1] and args[2] then
playerID = args[1]
playerObj = getPlayerByOnlineID(playerID)
clickedPlayerID = args[2]
clickedPlayer = getPlayerByOnlineID(clickedPlayerID)
sendServerCommand("123", 'PlayersDataWindowTransfer', {playerID, clickedPlayerID})
sendServerCommand(clickedPlayer, "123", 'TradeRequestReceived', {playerID})
sendServerCommand(playerObj, "123", 'TradeRequestSent', {clickedPlayerID})
end
end
and this is client side code to create UI windows for recipient(clickedPlayer) and requester(PlayerObj):
WindowUI = ISPanel:derive("WindowUI");
local modal
local playerObj
local clickedPlayer
WindowUI.instance = false
function WindowUI:onSentWindowRequest()
modal = ISModalDialog:new(getCore():getScreenWidth() / 2 - 175, getCore():getScreenHeight() / 2 - 75, 350, 150, getText("IGUI_WindowUI_SentWindowRequest to:" .. " " .. clickedPlayer:getDisplayName()), nil, true, WindowUI.onAnswerWindowRequest);
modal:initialise()
modal:addToUIManager()
modal:removeChild(modal.ok);
modal.moveWithMouse = true;
modal.ok = nil;
local btnWid = 100
local btnHgt = math.max(25, getTextManager():getFontHeight(UIFont.Small) + 3 * 2)
local padBottom = 10
modal.cancel = ISButton:new((modal:getWidth() / 2) - btnWid / 2, modal:getHeight() - padBottom - btnHgt, btnWid, btnHgt, getText("UI_Cancel"), modal, ISModalDialog.onClick);
modal.cancel.internal = "CANCEL";
modal.cancel.anchorTop = false;
modal.cancel.anchorBottom = true;
modal.cancel:initialise();
modal.cancel:instantiate();
modal.cancel.borderColor = {r=1, g=1, b=1, a=0.1};
modal:addChild(modal.cancel);
print(WindowUI.instance)
WindowUI.instance = true
end
function WindowUI:onReceiveWindowRequest()
modal = ISModalDialog:new(getCore():getScreenWidth() / 2 - 175,getCore():getScreenHeight() / 2 - 75, 350, 150, getText("IGUI_WindowUI_WindowRequest by:" .. " " .. playerObj:getDisplayName()), true, nil, WindowUI.onAnswerWindowRequest);
modal:initialise()
modal:addToUIManager()
modal.requester = playerObj;
modal.moveWithMouse = true;
print(WindowUI.instance)
WindowUI.instance = true
end
Events.OnServerCommand.Add(function(module, command, args)
if command == 'PlayersDataWindowTransfer' then
playerObj = getPlayerByOnlineID(args[1])
clickedPlayer = getPlayerByOnlineID(args[2])
end
if command == 'TradeRequestSent' then
WindowUI:onSentWindowRequest()
end
if command == 'TradeRequestReceived' then
WindowUI:onReceiveWindowRequest()
end
end)
Where does clickedPlayer come from in ISWorldObjectContextMenu.Window?
from vanilla
cuz I created function window in ISWorldObjectContextMenu
clickedPlayer transfer correctly
cuz UI windows appear
but problem is Window.instance work only for client, so if clickedPlayers Window.instance == true, than clickedPlayers context option will not appear, but if other, 3-rd player will click to clickedPlayer, his context option will apear
I cant get clickedPlayers Window.instance
to make check not only for playerObj, but and for player that we click
how can I make to avoid appearing context option on player, that have opened UI window?
What is the purpose of this? Why send data to all other players on the server about something that can only possibly impact these two?
sendServerCommand("123", 'PlayersDataWindowTransfer', {playerID, clickedPlayerID})
Ohhhh you want to prevent interruption this way?
So players cannot interrupt other people's interactions?
I tried to use it with the intention of setting the value for a specific player to prevent the creation of a context menu when clicking on that player if they have a UI window open
this is currently used to set the clickedPlayer and playerobj variables
in client side creating UI
You mean you want to expand if not WindowUI.instance then to stop players on any client from interrupting an interaction between two busy clients?
YES
YES
At the moment when the players have a UI window open, I want to make sure that other players do not have the opportunity to send a request to any of them
Hmmm... I'm not sure you can rely on their OnlineID to remain the same if players log out in the middle of this interaction. I have not tested that. Someone else will have to confirm. I am however much more confident that their display name would remain the same throughout their interaction, so using display names as a key for preventing this behavior might be wise, first of all.
You could create a table on every client, which I would not call "instance" but perhaps "busy" would be a good choice, like so:
Window.busy = {}
And then when players successfully call Windowing:
ISWorldObjectContextMenu.Windowing = function(context, playerObj, clickedPlayer)
local playerID = playerObj:getOnlineID()
local clickedPlayerID = clickedPlayer:getOnlineID()
local playerName = playerObj:getDisplayName()
local clickedPlayerName = clickedPlayer:getDisplayName()
Window.busy[playerName] = true
Window.busy[clickedPlayerName] = true
if not WindowUI.instance then
print("12345678")
sendClientCommand("123", "TradeRequestSent", {playerID, clickedPlayerID})
end
end
And then also follow up with a sync to all other clients that establish those player names as busy the same way.
And then if not WindowUI.instance then becomes if not (Window.busy[playerName] or Window.busy[clickedPlayerName]) then
And then when they finish the interaction, Window.busy[playerName] = nil.
Also, I would strongly recommend throwing local on the variable lines here:
playerID = args[1]
playerObj = getPlayerByOnlineID(playerID)
clickedPlayerID = args[2]
clickedPlayer = getPlayerByOnlineID(clickedPlayerID)
Because you're creating those as global variables serverside
And especially if Zomboid uses clickedPlayer as a global from time to time, I would be concerned about artificially setting it.
If online IDs never change when people log out, feel free to use those instead of names; I would need to test that.
Alternatively, and perhaps this would be better than sending the data out to everyone, you could track busy players serverside, and simply display a local message if the server responds that a player is busy when someone tries to activate the function that triggers the window creation on both clients.
So you just let them try to activate the interaction, the request goes to server, server checks Window.busy[playerName] and Window.busy[clickedPlayerName], and if server concludes one of them is busy, just respond to the requester with a command that tells them to :Say("That player seems busy...")
@muted garnet
That would honestly be the more robust way to go than trying to disable the option clientside, because even if you do succeed in sending a command out to everyone to disable that function clientside, there will be a fraction of a second before they receive the command during which they could still fire the command.
like this?
local playerName = playerObj:getDisplayName()
local clickedPlayerName = clickedPlayer:getDisplayName()
if not (WindowUI.busy[playerName] or WindowUI.busy[clickedPlayerName]) then
sendServerCommand("123", 'PlayersDataWindowTransfer', {playerID, clickedPlayerID})
sendServerCommand(clickedPlayer, "123", 'TradeRequestReceived', {playerID})
sendServerCommand(playerObj, "123", 'TradeRequestSent', {clickedPlayerID})
end
Serverside, yes, and, again, then you could do this instead:
and do I need to create a table on server side?
Yes, you would need WindowUI.busy to exist serverside if you plan to use it there. I would recommend something like this:
if WindowUI.busy[clickedPlayerName] then
sendServerCommand(playerObj, "123", 'BusyPlayerDetected', {name = clickedPlayerName})
else
sendServerCommand(clickedPlayer, "123", 'TradeRequestReceived', {playerID})
sendServerCommand(playerObj, "123", 'TradeRequestSent', {clickedPlayerID})
end
And then use normal option disabling to stop players who are themselves busy from sending the command in the first place.
Do you see what I'm saying?
(I can see English is probably not your first language, so let me know if I am not clear to you.)
I.e., use a local WindowUI.busy for determining local players who are busy clientside after they send a command so they can't send two, and use WindowUI.busy serverside for preventing the same player from receiving two commands.
Assuming this is a shared object that is available on both sides.
Or perhaps use WindowClient.busy and WindowServer.busy to make things more explicit in your code. (Would of course need to create those parent modules (WindowClient and WindowServer).
maybe not everything, but thank you, I'll quickly test it now and let you know the result
By the way, can I assume 123 is a placeholder name for your module? It's an unusually simple choice but I guess most people won't reuse it. Most people would use the name of their most prominent or fundamental module or the ID of their mod as the "module" in those commands.
And generally they would also confirm the module in addition to the command for safety.
I am getting server error when try to create WindowUI.busy = {}
attempted index of non-table
WindowUI.busy = {}
Events.OnClientCommand.Add(function(module, command, player, args)
if command == "TradeRequestSent" then
if args[1] and args[2] then
playerID = args[1]
playerObj = getPlayerByOnlineID(playerID)
clickedPlayerID = args[2]
clickedPlayer = getPlayerByOnlineID(clickedPlayerID)
local playerName = playerObj:getDisplayName()
local clickedPlayerName = clickedPlayer:getDisplayName()
if not (WindowUI.busy[playerName] or WindowUI.busy[clickedPlayerName]) then
sendServerCommand("123", 'PlayersDataWindowTransfer', {playerID, clickedPlayerID})
sendServerCommand(clickedPlayer, "123", 'TradeRequestReceived', {playerID})
sendServerCommand(playerObj, "123", 'TradeRequestSent', {clickedPlayerID})
end
end
end
Because WindowUI doesn't exist there.
I know, but for now this is just for testing
Let me give you a quick example of how I would personally redo your server file.
given module name 123 for testing
please, teacher
so how can I create this table on server?
I need to derive it, like in client? WindowUI = ISPanel:derive("WindowUI");
That would be weird tbh.
Having any access to a UI serverside is a bit wonky.
Just a strange concept.
Give me a minute.
I am offering a concrete example of what serverside could look like.
@muted garnet
This slightly relabels some stuff according to how I would recommend writing some of this; I use lowercase names for functions in alignment with Java standards since Lua has no standards, and I prefer using player for the player object in alignment with getPlayer, OnCreatePlayer, getSpecificPlayer, clickedPlayer, and a million other examples in and outside of your code where player is the object. I use playerIndex for the index. I never refer to playerIndex using player, even if the example on which I am basing my code does that.
If you sent it the correct commands and received the correct commands clientside, I believe this would be entirely functional, though I'd have to write the corresponding clientside to be 100% sure through some quick testing that I haven't made any minor mistakes.
I use clear labels for data in packets instead of using [1], [2]. [3] etc.
I use packet.clickedPlayerID, e.g., not packet[3] or something similarly unclear.
maybe you meant {} instead of [] in WindowServer.busy = []? cuz umbrella shows this line like error
Yes I did
Typo
Didn't hold shift there apparently.
WindowServer.busy = {} is correct
for what this part of code?:
-- Command Processor:
-- The following command would be used to trigger events in this
-- file (assumes "player" and "clickedPlayer" are IsoPlayer objects):
--
-- local playerID = player:getOnlineID()
-- local playerName = player:getDisplayName()
-- local clickedPlayerID = player:getOnlineID()
-- local clickedPlayerName = player:getDisplayName()
-- local packet = {
-- playerID = playerID,
-- playerName = playerName,
-- clickedPlayerID = clickedPlayerID,
-- clickedPlayerName = clickedPlayerName,
-- }
-- sendClientCommand(player, modID, "createWindowRequest", packet)
That is how you would interface with the commands I wrote.
That's how you would send the command that server would process
in determining whether to accept or reject the request to make a window.
You would just let anyone who isn't busy send the request, and then the server would go, "Okay, is the target busy?" If so, tell the caller, "No"; but if no one is busy, go ahead and activate the procedure that opens windows on both clients.
You would of course need to add another command later for clearing these busy variables. When the players are done with those windows.
@muted garnet You would need something like this to catch commands in client:
-- So this doesn't run clientside in Multiplayer, but could
-- do so if your mod had any purpose in Singleplayer:
if isServer() then return end
-- Filewide Module:
local WindowClient = {}
-- To avoid writing (and later changing) a literal 123 everywhere:
local modID = "123"
-- Commands:
WindowClient.completeRejection = function(packet)
-- set clientside .busy variable for this player back to nil
end
WindowClient.tradeRequestSent = function(packet)
-- finish the request for the sender
end
WindowClient.tradeRequestReceived = function(packet)
-- finish the request for the receiver
end
-- For compatibility with SP and MP:
WindowClient.processServerCommand = function(module, command, packet)
if not (module == modID and WindowClient[command]) then return end
WindowClient[command](packet)
end
Events.OnServerCommand.Add(WindowClient.processServerCommand)
return WindowClient
I can't show you more right this second because I gotta go work, but try to see if you can make sense of integrating these two files and I'll pop in from time to time if you're still stuck.
thank you very much, I don’t fully understand your code yet, but I’ll try to figure it out on my own
The one with WindowClient goes in some kind of client file, the one with WindowServer goes in some kind of server file, I made them to work if they're both in lua/shared but you can put them in other places with success, too (e.g. you could move the client one to lua/client if you wanted to.
could you pls say me difference between shared / server folder? shared folder - it`s folder where can run client and server files both?
for some reason the ui image for the trait refuses to show up
I put the image in media/ui/Traits
but it just doesn't show up
I looked at other mods and it seemed to me that the variable name was corresponding to that, alright I'll try it like that
local yourname = TraitFactory it's for work with trait in future, like:
for add recipes, xp boost, etc
Reason: Bad word usage
Yes, shared files will run on client and server.
https://gist.github.com/Konijima/7e6bd1adb6f69444e7b620965a611b74
Reason: Bad word usage
I haven’t tested it yet, I’m currently setting up the code and connecting other UI options
No worries. I'm sure it'll take you some time to get everything implemented and make sense of what I sent. Feel free to reach out if you feel you've made as much sense as you can and you're still stuck.
thank you very much for your help and support!
As far as I know, client literally runs on the client in multiplayer -- that is literally where that code is executed. Client code, as far as I know, is not executed on the server. Is that what you mean when regarding where things get executed?
But asking that question is not annoying at all; I welcome anyone to correct anything I say wrong. I've surely said a lot of things wrong. Someone (e.g., albion) tends to chime in when I do (lol). The experienced ones usually only let me talk when I'm not wrong.
😅
This part of your link is correct:
"Simply putting code into server doesn't mean it is executed on the server and not on the client."
But it doesn't work the other way
Simply putting code in client DOES mean it is executed on client and not on server.
That can be very confusing to many people but it's a recurring issue here.
(If you're playing in MP.)
I'm sure albion will chime in asap if I'm wrong... lmao
You can easily test this by creating a global variable in client and trying to send it to yourself from server in a packet.
Should come out nil.
This is also correct from your link, FYI: In single player for example all code is executed no matter in which folder you placed it.
This is not entirely correct:
"These folder's purpose is code organisation."
The folders also impact load order.
shared does load before client.
I do believe server files run whenever a function in server files is called in SP but tbh I have not explicitly tested that. I would assume that is accurate.
That may be a bit misleading in the Konijima link I offered.
(Since my server files serve a hybrid purpose of simply forwarding commands to where they need to go in SP, I tend to put my server file in shared, so this doesn't come up for me.)
I only put distribution files and such in server; stuff that explicitly extends server files.
Who know how to set up clothes to zombies like variables between two items?
for example, i have 4 helmets, but i want use only one, like random between this helmets
Doggy might know about dressing zombies. I know they did a lot of work on modding zeds.
If you see them in here or the other server I might ask them.
But that white background... Oof... 😉
my light in black room
If I want to mod in an entirely black room that's my prerogative (he said wearing tie dye)
lol f.r. I like color but white is entirely too much color for me.
Yeah that's very fair. The server files run but the server in other senses does not exist (e.g., you cannot call sendServerCommand afaik), and in other senses you are arguably your own server.
But you are sort of the client of the server that sort of does not exist in SP (in a sense) because you can call sendClientCommand
It's all very confusing, certainly lol
@thick karma It seems like I figured out the code a little and was able to display UI windows for players, parameters and values, this is an interesting and useful experience for me, very thank you, but please explain in more detail what these lines are used for:
if isClient() then return end -- This is used to check that this is a server file, right?
how this function works?:
WindowServer.directClients = function(module, command, packet, player)
if not isClient() and not isServer() then
triggerEvent("OnServerCommand", module, command, packet) -- Singleplayer
else
if player then
sendServerCommand(player, module, command, packet) -- Multiplayer -> One Client
else
sendServerCommand(module, command, packet) -- Multiplayer -> All Clients
end
end
end
Why do need to return the table value? return WindowServer
Question 1: Yes.
Question 2: The first branch of the outer conditional determines whether you're in singleplayer mode. If you're not, it decides whether to send the command to 1 client or all of them on the basis of whether you provide an optional player argument to that function.
Question 3: Consideration for other modders who may want to decorate your work someday in an unintrusive patch.
and why in this line uses clickedPlayerID = packet.clickedPlayerID? clickedPlayerID = packet.clickedPlayerID
WindowServer.directClients(modID, 'tradeRequestSent', { clickedPlayerID = packet.clickedPlayerID }, player)
can`t I use instead it? WindowServer.directClients(modID, 'tradeRequestSent', {packet.clickedPlayerID}, player)
It assigns packet.clickedPlayerID to a key in a new table (which is also called clickedPlayerID.
that is, it updates the value of packet.clickedplayer?
If you use the latter example, you'll access the packet data by saying packet[1] instead of packet.clickedPlayerID. I prefer the latter (packet.clickedPlayerID) because it's descriptive.
I avoid using indexes without explicitly described meanings unless the list is an arbitrary indexed list of same-type same-purpose objects, rather than a set of specific data where each entry has a specific purpose.
That line actually replaces the packet with a new packet that only containers 1 key-value pair, such that packet["clickedPlayerID"] (a.k.a. packet.clickedPlayerID) references the actual id of the clicked player
Question 2 -> does the player argument mean only the client or the client and clickedplayer?
But honestly, at second glance, for your purposes, it may be more convenient to change that
And forward the whole packet.
WindowServer.directClients(modID, 'tradeRequestSent', packet, player)
WindowServer.directClients(modID, 'tradeRequestReceived', packet, clickedPlayer)
should be plenty fast enough and also you may want easy access to the name variables, or the online id of the other player.
The player argument is for an actual player object (it'll be either the clickedPlayer or the sending player depending on which call is being made to directClients). That player object is used by the server to decide which client will receive the command.
Where client = the player's Zomboid app on their computer, not exactly the player (just to be super clear).
what do you mean?
What's the Difference?
I mean that even though we write the command as sendServerCommand(player, module, command, packet), the client code has to guarantee that the right player on that client receives the command. The server only guarantees it'll get to the right PC. If you're sending a command to a client that has 2 players playing in splitscreen, you'll need to use player online ID or name to find the right player object.
sendServerCommand(player etc. does not actually guarantee that player receives that command, despite how it may appear.
See what I'm saying?
I got it, thanks
Side thought not directly related to the other stuff we're discussing: I'm guessing this mod you're making currently has no singleplayer purpose, but if it ever acquires one, you may need to consider what to do about commands involving OnlineIDs, as they may fail to provide the results needed for your code to work. Just FYI.
I have not tested the results of trying to request or use online IDs for anything in SP.
I would expect some nil return values somewhere along the way.
no, this mod will only be for multiplayer, cuz all the functionality is interaction between two players
Makes sense.
Then for what it's worth directClients is overkill; I just teach people to use it because it's convenient when working with mods that function in SP and MP.
You could skip that function and directly use the sendServerCommands to reply to the server.
Just for your own knowledge.
I think it's good to know long-term because it can make the transition between SP and MP functionality very easy.
do you mean this checks for the client and the player?
if not isClient() and not isServer() then
triggerEvent("OnServerCommand", module, command, packet) -- Singleplayer
else
if player then
sendServerCommand(player, module, command, packet) -- Multiplayer -> One Client
else
sendServerCommand(module, command, packet) -- Multiplayer -> All Clients
end
end
Right, the purpose of calling that function is to check whether you're in singleplayer in order to forward commands differently in singleplayer. If no one is ever running this mod in singleplayer, there is not much purpose to using the directClients function to forward your command (aside from perhaps learning to use it for other situations later, it's useless here).
It's a simple hack to use the same messaging setup as multiplayer basically.
sendServerCommand(module, command, packet) triggers OnServerCommand on every client with the provided module, command, packet. And that's also exactly what triggerEvent("OnServerCommand", module, command, packet) does, but triggerEvent only works clientside and in this case it's just simulating a server response by calling what the server would have called.
got it
Meanwhile, sendServerCommand(player, module, command, packet) uses player to pick one client, and then it effectively activates triggerEvent("OnServerCommand", module, command, packet) on that client.
The value of directClients above (for me) is that it moves the optional player parameter to the end of the function parameters.
So 1 function can trigger all 3 needed outcomes.
however sendServerCommand is already designed to handle 2 of them in MP mode.
So you don't need directClients for using 1 command in multiple ways if you aren't making a mod that has any value in SP.
Events are stupid simple. All you'll need to do is keep track of your game's states.
That part's on you.
Otherwise it's just another event to fire / trigger.
When designing LuaNet commands, be sure not to overload them.
I sympathize, but still worth noting that simplicity is subjective and we benefit from speaking the dominant language of this sort of programming as a first language. If it doesn't seem so simple to you, that is understandable.
I felt like it was worth saying because those individuals can get lost in the wording and not what it actually represents, which is an event waiting to fire.
It says 'Server' In it but it doesn't mean you can't use it in singleplayer, etc.
For sure. It's 100% not meant to be complicated.
triggerEvent(eventName, arg, arg, arg, arg) makes an event with given name call some functions that [hopefully] expect those args. The end.
Then again, things that are not meant to be complicated can get complicated in programming, lol. Strings are not meant to be complicated, but there are numerous complexities to working with them in the countless programming contexts that exist.
lol but yeah let's not worry about that for now... Haha poor M.
Every new Zomboid programmer: "How do I make a UI?"
is handed a book
If not for that programming wouldn't be fun.
Also yeah I focus heavily on UI here as part of my niche.
Before multi-tab chat and discord intrgration was official I had my mod. =)
That's lit
I wrote my own UI library ontop of ISUI and ISChat.
I got sucked into the UI stuff early by my compulsion to hide it and the lack of a quick gamepad shortcut for doing so.
And then it spiraled completely out of control.
Not as out of control as you, of course.
lol
Hahaaaaa classic.
Also before the JVM flag java.awt.headless=true was passed to the game on launch by default, you used to be able to open urls from the lua side. I wrote a discord invite button into a server at one point.
Was nifty.
You have no idea how insanely convenient that was.
People could join the discord server from inside the game itself.
If you set that flag to false on launch and use openUrl('..') in Lua, it'll work.
openUrl still works to my knowledge
Hello all. I have a question regarding the "Mod Options" mod, and trying to get settings to update on user change/apply. It does not seem that I can get the game to recognize anything has changed, which is frustrating. Even copying other code from working mods does not function the same. Does anyone have any insight into this? It seems so straightforward, yet isn't working for me. Thanks in advance.
Do you have a local and workshop version installed at the same time ?
The mod is just a local mod for now, testing out implementing mod options so I can use it for another mod of mine.
the game can't recognize anything has changed as in your options ?
Like whenever you change an option it doesn't update ?
Yes. I have a basic checkbox, and a simple "print" command to show the data of that checkbox. And upon checking/unchecking the box, and hitting Apply>Accept, it still does not show any change, or reflect it anywhere else trying to use that variable.
Depends how you access that data tbf
I was wondering if its just an issue with accessing it correctly, but I've litereally copy>pasted other code and it doesnt function for me.
We'll need more info on that one to be able to help you ngl
Like how you use that option, how you access its status, when, what's the code you're doing, where the code is located in your folders if it's in shared or client
Make sure ModOption is loaded before if your code runs from shared
All the code is in the same .lua under Client. Basically just trying to print out the variable data gathered from the settings:getModOptions table.
Send the code
item zReV2_1ECO_fullhelmet1A
{
Type = Clothing,
DisplayName = zReV2_1ECO_fullhelmet1A,
ClothingItem = zReV2_1ECO_fullhelmet1A,
ClothingItemExtra = zReV2_1ECO_fullhelmet1,
ClothingItemExtraOption = zReECOWear_1,
BodyLocation = MaskEyes,
Icon = zReV2_1ECO_fullhelmet1A,
CanHaveHoles = false,
BloodLocation = FullHelmet,
BiteDefense = 100,
ScratchDefense = 100,
BulletDefense = 0,
Insulation = 0.55,
WindResistance = 0.40,
WaterResistance = 1.0,
Weight = 0.4,
Tags = GasMask,
WorldStaticModel = zReV2_1ECO_fullhelmet1_Ground,
}
item zReV2_1ECO_fullhelmet2A
{
Type = Clothing,
DisplayName = zReV2_1ECO_fullhelmet2A,
ClothingItem = zReV2_1ECO_fullhelmet2A,
ClothingItemExtra = zReV2_1ECO_fullhelmet2,
ClothingItemExtraOption = zReECOWear_1,
clothingExtraSubmenu = Wear,
BodyLocation = MaskEyes,
Icon = zReV2_1ECO_fullhelmet2A,
CanHaveHoles = false,
BloodLocation = FullHelmet,
BiteDefense = 100,
ScratchDefense = 100,
BulletDefense = 100,
Insulation = 0.55,
WindResistance = 0.40,
WaterResistance = 1.0,
Weight = 1,
Tags = GasMask,
WorldStaticModel = zReV2_1ECO_fullhelmet2_Ground,
}
can u add this two item in suspec overhaul
GasMask
Yeah filters
Alright doing that
I was about to push an update soon anyway ;)
Revamping the hotkeys to require Mod Options instead of Eggon's Hotkeys
These are for vaccin 2.0 right ?
yep
👌
local alertVolume = 0.05
local nsEnabled = true
if ModOptions and ModOptions.getInstance then
local function onModOptionsApply(optionValues)
local value = optionValues.settings.options_data.enable_notify_sound
nsEnabled = value
if nsEnabled ~= nil then
nsEnabled:onModOptionsApply(value)
end
print("checkbox1 = ", SETTINGS.options_data.enable_notify_sound)
end
local SETTINGS = {
options_data = {
enable_notify_sound = {
name = "Enable Notification Sound",
default = true,
OnApplyMainMenu = onModOptionsApply,
OnApplyInGame = onModOptionsApply,
}
},
mod_id = 'GN84-UCN-IM',
mod_shortname = 'UCN + IM Sound',
mod_fullname = 'UCN + IM Sound',
}
local optionsInstance = ModOptions:getInstance(SETTINGS)
ModOptions:loadFile()
Events.OnGameStart.Add(function()
print("checkbox1 = ", settings.options_data.enable_notify_sound)
end)
end
So you took that from another mod ?
Yes, this is one variant I've used and copied to try to get a working version. I've tried the examples on the actual workshop guide for Mod Options as well.
I can get the options to show up correctly in Mod Options > Mod Settings page, and don't get any errors thrown, but it won't show anything updates when I check the box. Even a simple boolean true>false doesn't show any change.
And you sure the original mod you copied it from worked ?
Yes, absolutely. I tried a couple mods, with very simple implementation, similar to the actual examples on the guide page.
I feel like I am just missing something really simple, or something is out of place causing the issue. I just cant pinpoint it
I'm not familiar enough with Mod Options to debug that properly ngl
I appreciate your help Sir Doggy.
I'm not familiar with the functions you're trying to override or your style of updating Mod Options, but I can provide you with a working example that you can refactor to your heart's content.
That would be greatly appreciated. I've tried the direct guide methods, as well as a few other mods that work and are simple implementations. So something in my setup is wrong, and would be great to have a known solid model to start with. Thank you!
This is an example from Wookiee Gamepad Support.
I give you this one because it has several different kinds of options going on
Including some mutually exclusive ones.
It relies on the existence of WGS = {} elsewhere in the code, and a handful of variables declared outside this chunk
But hopefully it can give you a feel for what needs to happen.
If you want a super easy one I have easy ones too. The options for Better Automatic Movement are pretty easy.
Thank you Burryaga, that is great. I'll dig at it for a few and see if I can get a basic version working for my mod. Is there anything specific you know that needs to be set up with Events, or a way to access when settings get updated?
You can refactor any of my Mod Options mods on the workshop freely.
Most of my mods use it.
You can also borrow a copy of Exterminator
Which you'll learn you'll want
I honestly only need a boolean On/Off checkbox, and a Dropdown with 3 Items, of High, Medium, Low for a sound volume setting. Super simple.
It's a tiny module in all my Mod Options mods
It looks like this:
-- Protects Against a Known Options Bug
if not Exterminator then
Exterminator = {}
Exterminator.onEnterFromGame = MainScreen.onEnterFromGame
function MainScreen:onEnterFromGame()
Exterminator.onEnterFromGame(self)
-- Guarantee that when you ENTER the options menu, the game does not think you've already changed your damn options.
MainOptions.instance.gameOptions.changed = false
end
end
Just copy-paste as is and you'll be fine.
At the top of the code, prior to Mod Options Implementation and Other Code?
Okay. Thank you very much. That's an awesome and simple little bug fix!
Good luck!
The reasoning behind my structure btw
Is that if you store it all in YourModule.mod
oky doki understood, im fix it tommorow
You end up accessing your mod option states by typing YourModule.mod.options.theOptionYouWant
Which I like for legibility
Obviously if you plan to use it in a loop, store it in a function-local variable (to avoid repeated table accesses if performance is a concern)
I've seen this version in other mods, which seems more intuitive, but wasn't sure if I was getting the state from the loaded .lua file, or from actual saved settings.
The settings change the state of that variable
And the functions available return it
Update out
I use mod options more than I drink water, so if it didn't work that way, I'd know by now. 😉
LoL
Also
Would you be so kind as to send me a very simplified version, using the same concept? I'm looking over the one you sent, and its very detailed, but would be interested in seeing a barebones version in play if possible.
Shameless self-plug 🤪
https://steamcommunity.com/sharedfiles/filedetails/?id=3199203573
Just to show you
I am pretty serious
About my Mod Options.
Well you are definitely the person to talk to! haha
I'm serious about documenting code.
SO serious that I document other people's code.
And then I sleep.
local Trash = {}
Trash.mod = {
options = {
volubility = 2,
range = 6,
mouthy = false,
sneaky = false,
context = false
},
names = {
volubility = "Volubility",
range = "Sound Range",
mouthy = "Feeling Mouthy",
sneaky = "Sneaky Insults",
context = "Context Option"
},
mod_id = "TrashTalker",
mod_shortname = "Trash Talker"
}
local none = 0
Trash.whisperShortcut = {
key = none,
name = "WhisperTrash"
}
Trash.talkShortcut = {
key = none,
name = "TalkTrash"
}
Trash.shoutShortcut = {
key = none,
name = "ShoutTrash"
}
Trash.category = "[Trash Talker]"
if ModOptions and ModOptions.getInstance then
ModOptions:AddKeyBinding(Trash.category, Trash.talkShortcut)
ModOptions:AddKeyBinding(Trash.category, Trash.whisperShortcut)
ModOptions:AddKeyBinding(Trash.category, Trash.shoutShortcut)
Trash.modSettings = ModOptions:getInstance(Trash.mod)
local volubility = Trash.modSettings:getData("volubility")
local range = Trash.modSettings:getData("range")
local mouthy = Trash.modSettings:getData("mouthy")
local sneaky = Trash.modSettings:getData("sneaky")
local context = Trash.modSettings:getData("context")
volubility.tooltip = "How much trash should a trash talker talk? By default, trash talkers will say something snarky after 20% of killing blows."
range.tooltip = "How loud is your voice? By default (unless modified by your host), trash talking must make some noise. The default range of this scale is 2 to 20 squares, but this can be modified by the host. Notice that 10 is not the maximum. The number goes to eleven. Is it any louder? Well, it's one louder, isn't it? It's not ten. If you need that extra push over the cliff, you know what you do? Exactly. Put it up to eleven."
mouthy.tooltip = "If this is enabled, your character will talk trash even if they do NOT have the trait. This setting does nothing if your character is already a trash talker by nature (i.e., if they already have the trait)."
sneaky.tooltip = "If this is enabled, you will whisper trash talk when you are sneaking."
context.tooltip = "If this is enabled, a Trash menu will be added to your world object context menu (the right-click menu) allowing you to mutter, talk, or battlecry random trash. Whispers will have half of the range (or volume) that is set above; shouts will have double the normal range."
for index = 1, 11 do
volubility[index] = getText("IGUI_TrashTalker_Volubility_" .. tostring(index))
range[index] = getText("IGUI_TrashTalker_Range_" .. tostring(index))
end
function volubility:OnApply(value)
Trash.volubilify(value)
end
function range:OnApply(value)
Trash.rangify(value)
end
end
Kind of Important: I cut the function of Trash.volubilify and Trash.rangify for simplicity.
Also this mod is old and doesn't use translations.
When you upload text as a file it keeps it short on the message history. Helpful for future code-pasting here. =)
Not mad or anything just helpful tips that goes a long way.
For sure, I was just trying to let them skim it in-app with color
I greatly appreciate the help. I am sure this will help someone else as well with the same issue.
Right on
Thank you very much @thick karma . I will tinker with it for a few and let you know if I have any other questions. 🙂
Good luck!
Well, I've got it working now. Thanks a million! Now just to figure out how to implement a dropdown menu option in the style that you sent me last.
is there a dedicated tutorial anywhere for making a VHS? i have the perfect idea for one
hmmmhhhmmmm
one min
i went for an energy drink
nice
seventh... I hope this shiet doesn't kill me...
I do this with coffee.
As a newly diagnosed type-1 diabetic, coffee is now more of a lifeline than ever.
haha
I had a sugar-free Redbull though and that was a nice switch-up.
first type req insuline dose?
Yup.
shiet...
They found the rock in my brain because I came in from symptoms of both that and the t1d.
=)
or something like drop generated in zombie?
like in total pool list. Like i would be able to find it as vhs store loot or in a bookshelf/tv.
structure like:
RecMedia = RecMedia or {}
-- Media: VHS: Unknown Tape [id: 18271e35-d316-47c1-ae05-eda2fa515b01]
RecMedia["GUID"] = {
itemDisplayName = "RM_GUID2", -- VHS: Неизвестная кассета
title = "RM_GUID3", -- Неизвестная кассета
subtitle = "RM_GUID4", -- «Лекарство реально!» (с) Доктор Керхус
author = nil,
extra = nil,
spawning = 1, -- nil not spawn
category = "Retail-VHS", -- Retail - profi Tape, "Home-VHS" - homemade tape --
lines = {
{ text = "RM_GUID5", r = 0.79, g = 0.79, b = 0.79, codes = "BOR-1" },
{ text = "RM_GUID6", r = 1.00, g = 0.82, b = 0.27, codes = "BOR-1" },
etc
},
};
wheres GUID# - it's generated guid
Free Online GUID / UUID Generator
r g b - color of text
https://csscolor.ru/?hex=265894
just any random GUID would do?
yep
As long as it doesn't collide with others you should be fine.
The dropdown list basically works by providing an index instead of a true-false state. Then you set the label text for each index. When people set value you convert the index to a useful value - that was what e.g. the volubility function was for.
I often use a middleman variable that gets updated onApply to handle dropdown variables in Mod Options
Recorded_Media_EN.txt translate file
structure looks like:
/* zRe VAC2 */
RM_GUID1 = "VHS: Unknown tape",
RM_GUID2 = "Unknown tape",
RM_GUID3 = "«Cure is real!» (с) Dr. Kerhus",
/* Ambient */
RM_GUID4 = "*Something falls in the background...* <fzzt>",
/* kERHUS */
RM_GUID5 = "-It works?! The record is going? Fine...",
RM_GUID6 = "Greetings, survivor... You may have found this tape at one of my assistants...",
RM_GUID7 = "Or at what left of him... *sigh*...",
etc
this is just guid, without translate, without RM_
Also just to save you time on a dead-end road, you may get it in your head for one reason or another to delay calling the function that loads mod options until some later event - don't do that, because it'll retrigger mod options processes and result in all the options for mods AFTER yours failing to load.
this is stuff change stats, get xp, or recipes, for example:
"RCP=RecipeID" -- get recipe
Ahh, that is very strange. Do I use the Events.OnGameStart function for that?
I used secondary variables _alertEnabled, etc. to use in my actual function, and ran it from the alertEnabled:OnApply(value) function.
i just paste this structure in the EN and generate some GUIDs for it right?
this is another codes
like
"DOC+1" - get xp to doctor
yep yep
thath all
guide end
the game just knows the dialogue im adding corresponds to the VHS im adding?
No, call it immediately at file load time just like I sent it
No event
Right after declaring it.
Sounds right.
Can you provide an example? I used your code as a base, and it works fine. My brain is a bit of a jumble from twiddling around with this for hours last night lol
Also, the volubility function wasn't supplied in your code, otherwise I'd take a look at it to see how you did a dropdown index.
Do you know how to pull mod code from your folders? Like where it's saved? If so, subscribe to Trash Talker real quick
I am on mobile or I'd send the whole file
Volubility is in there and the process used for initializing mod options is safe
I will take a look, thanks!
Okay. Looks like you wrapped the if ModOptions and ModOptions.getInstance then set of code in a function XXX.loadModOptions = function(), and then called that at the end of the file? I'm assuming that is the initiliazation bit?
Yeah that's it
Doesn't need to be end of file in yours
That was probably context dependent decision
Awesome, thats helpful.
I often put all the mod options shit together near the top
Trash Talker has a hybrid options loading process sort of
Now I didnt see any dropdown list in your code to pull from. I'll have to dig at the guides again to figure out how to make a 3 option table to use and pull from.
Volubility is a dropdown list
Iirc
I'm pretty sure it corresponds to percent chances
What is the default value of volubility in Trash.mod?
Remind me just in case I'm wrong
In Trash.mod.options
It looks like it's set to 2
I'm trying to find the index though down in the code, where you have that detailed.
That means it's a dropdown
fixed
I probably used a loop
Look for the word index
Yes, it seems that its the selection on the dropdown. Just can't find where it initializes that list in the mod options menu.
Yes, I can see that now. It looks like its referencing a translation file for it, which I don't have implemented in mine yet.
Got that mad scientist energy going..
Got some vscode open and some honey-mustard pretzel twists and a plan.
Also hello, Notepad++ community.
x)
To be honest, I've never been the greatest with tables in programming, though it seems it's the bread and butter of Lua scripting.
So cool thing about Lua is everything is literally a table. If you learn what you can do with tables you learn the entire language in concept.
Thats good to know. I was looking up how to do a switch case setup, and it just said to use table indexes.
Lua (when compared to other languages), is stupid simple. It doesn't mean one is dumb not to know it but rather that it's easy to get a grasp of what you work with.
Ive always strayed away from stacked If statements though, ever since the old basic days, and not having the usage of { } to outline functions kind of murders my organization attempts lol
It's good that a focus for you is organization.
Yeah, Ive actually been enjoying how simple it is though.
Find small projects to push yourself and use that knowledge to further your work.
I think after reading my old code on projects and not being able to read it, I went out of my way to organize and comment and refactor stuff as much as possible to make it workable at later dates.
I am actually reworking part of someone else's mod for my server that I run, to fix a few bugs it had and update it to be more useful. This little project alone has helped me along with numerous small details, which is very nice to say the least.
I miss working on PZ servers.
I'm a Java developer first.
I wrote an entire framework that gave rise to Java JAR plugins that hooked into events, commands, and layered API for things in the PZ engine for servers. This also is the framework that my anti-cheat sits on.
Thats awesome
I'm much more comfortable with the C/Java styling of syntax, so going back to stuff like Lua isn't difficult, but makes me double think what I'm doing and whether the same things work or not.
I still use semi-colons. That's not going away.
Yeah, I do too. Does that affect Lua code, or is it treated the same way?
Did you know that you can make unsigned integers from tables in Lua and make them function?
It's syntax for sure but isn't needed except for I think one or two situations.
I didnt know that, good to know though
I recently wrote bitwise emulation in the PZ Lua environment to implement AES support.
So I'm free to dump semi-colons like confetti sprinkles on a birthday cake then? haha
Now I can encrypt LuaNet commands.
Been doing it since 2014. Go wild.
Very cool
I code in several languages so I don't want to screw up in another.
I can't imagine needing to encrypt anything in a PZ mod, but cool that its possible.
It was hell to do but it's useful. I did it on purpose so that I didn't need to modify the client for players and provide it so that I can encrypt my anti-cheat code.
Also because it's all in lua that means it's jumbled in the dynamo VM that Lua is in PZ.
Less vulnerable to interception.
Thats a good point. I can see that need case.
Also you can make tables 'read-only' in Lua natively using metatables so more protection even.
What sort of Anit-Cheat do you have going? Better than the vanilla stuff? Or any specific use cases?
You can write code that checks to see if a table is altered before and after passing it to an event.
People have relied on my anti-cheat since 41 came out. =)
I HAVE SO MANY GUIDS TO GENERATE
I wrote a separate, more specific and aggressive alternative that runs in Lua through bootloaders and encryption.
you dont have to go into great detail about it, but just curious of the basics of your anti-cheat
BUT THIS MOD IS GUNNA RULE
Do you know if you can do asm in lua? I wonder if it'd be possible to do like byte patching, that would open up a new way to do things
All good. I'm just crazy about it.
You can emulate it.
That's it.
OMG I THOUGHT I LOST THE DIALOGUE
You can't affect memory beyond conventional assignment.
I JUST DIDNT SAVE THE NOTEPAD FILE
that sucks
What specifically is your anti-cheat blocking?
It checks for footprints from common cheats.
There's a variety of ways to check for the explicit presence of cheats for all except one specific one.
I'm not well versed in the available cheats for PZ, so I'm curious to what is out there, and how it would affect my server. Having to wipe and reset is miserable, and I don't want to have to whitelist anything unless absolutely necessary.
I'm also deploying a bootloader approach where the workshop provides a skeleton bootloader function that then loads, encrypts, and distributes local code on the server machine. This allows for servers to customize and improve on the encryption and payload of their own anti-cheat, making it harder for cheaters to write blanket code that bypasses the anti-cheat.
It's going to make cheaters' lives 
lol
Diverse security is best.
I'd imagine you could just not choose to load the anticheat's client lua (unless it's done on serverside only)
Allowing the server to modify or replace the payload and encrypt it is the best approach we have.
The server didn't get a response? Okay well that's not normal. Kick.
fair
Do you get a lot of false positives with your anti-cheat?
This is an aggressive model where the one people use now that I made is a passive network monitor.
Because of the nature of mods in PZ yes, but because of that.
It's going to be the same with the vanilla one.
The difference between the vanilla one and mine is I use YAML for the config and literally everything is configurable, even down to the logs and their nature.
Thats cool. I wonder if your work will help improve the ability for mod data to be secured simply by running data through your functions, and then to the server. Then you'd be able to increase the effectiveness of the vanilla anti-cheat without having to turn down the sensitivity of them to allow mods to function correctly.
I write everything as 'utility-first', meaning that if I can make it into a tool on its own, I will.
So everything in this 2nd anti-cheat is modular.
You can use the bitwise ops library independently, the AES one independently, and even the packet encryption service API.
When I get it done. The model is already proven and working.
Just getting it feature complete which is the easiest step.
# Client >
LOG : General , 1700627414926> 4,358,007> sendClientCommand(module='mod_loader', command='_', args='{"options":{"encryption":{"keyLength":16,"mode":3,"priority":1}},"_":"?C──:?╦╡?|9┼─r┘\u0003-V±\u0011▒'εxS|s\u0002#;)▓i?)c╪5\u0003╩\u0017Ñ≤\u001b▓?F?4`?\u0000?dª\u0017\u001fK═?;╠*?╥»ñ'V?*╦└▌?╖hN└╜"}'
)
# Server >
2023-11-21 22:30:15.692> General > ### {"_":{"id":"greet_message","data":{"message":"Hello, Server!"}}}
2023-11-21 22:30:15.693> General > onReceivePacket(module='mod_loader', player='zombie.characters.IsoPlayer@4efb13f1', id='greet_message', data=table 0x1610818058)
sendServerCommand(
module='mod_loader',
command='_',
args='{"options":{"encryption":{"keyLength":16,"mode":3,"priority":1}},"_":"cj6φ:?╦╡?|9┼─r┘\u0003-V±\u0011▒'εxS|s\u0002#;)▓i?)c╪5\u0003╩\u0017Ñ≤\u001b▓?F?4`?\u0000?dª\u0017\u000fB╓?0╩*?╥»┬└Θ?┌\u0005a√}H▀▒Θ▒"}'
);
# Client >
LOG : General , 1700627417288> 4,360,370> ### {"_":{"id":"greet_message","data":{"message":"Hello, Client!"}}}
LOG : General , 1700627417317> 4,360,398> onReceivePacket(module='mod_loader', player='nil', id='greet_message', data=table 0x889524107)
This is showing it working.
Fancy fancy
Well if you need any guinea pigs for testing on live servers, let me know, and I'd be glad to help.
Thanks. DM me if you want a link to my personal server where my stuff lives.
=)
The anticheat is under the name 'Crafthammer' but that's the framework name. I built the anti-cheat into the framework.
Sure thing. I appreciate the help and advice from anyone willing to share
Awesome.
One day we'll all be writing in Lua modules.
Oh and if you know TypeScript, it's possible to mod PZ using it.
Just a head's up.
The project is dated and I need to upgrade it but yeah that's an option.
Cool
the GUIDs are all done
gonna check out what that happy gilmore mod did to add its VHS. this shit is going to the workshop babyyy
holy shit this is nothing
its that easy!
I got my mod issues figured out. Greatly appreciate the help!
Good work!
Tell 4x font users to grab MORE on the Workshop! Haha
If you turn on 4x font and check the page with and without it you'll really see
Esp if you enable a few Mod Options mods
It's p messy at large font
Without MORE
Hey @thick karma , do you know how to implement Sandbox options in mods as well for server side changes? That's my next learning goal.
Yeah most of my mods use them, you can refer to True Music Jukebox for my latest best practices on that.
i wrote a guide on them https://github.com/demiurgeQuantified/PZModdingGuides/blob/main/guides/SandboxOptions.md
I'm new to the modding community, but didn't know you made that mod. What a god you are. haha
Thanks albion!
LOL! I actually have your github site up, but hadn't scrolled to that page yet. What a coincedence
@bronze yoke @verbal yew thank you guys even tho it was nothing major I got 2 more mods published today
I'm trying to remember how I helped
It was some traits stuff
Haha I am but a minion but I am glad you're an enjoyer of TMJ.
I am trying to create my first item and have put my item and model in items.txt and models_items.txt but they aren't showing up in the debug items list. I think I may be doing something wrong but I am not sure. I will share a snippet.
items.txt
module AoqiaToothbrushMod {
item ToothbrushRed {
DisplayCategory = Junk,
Type = Normal,
DisplayName = Toothbrush,
Icon = ToothbrushRed,
Weight = 0.1,
WorldStaticModel = ToothbrushRed,
}
}
models_items.txt
module AoqiaToothbrushMod {
model ToothbrushRed {
mesh = WorldItems/Toothbrush,
texture = WorldItems/ToothbrushRed,
scale = 0.6,
}
}
Where can I find a list of places to insert Items/ProceduralDistributions
Like if I wanted to add a pistol to the police locker drop table for example
ProjectZomboid\media\lua\server\Items\ProceduralDistributions.lua
imports {base}
ty my broder
you only need to import base if you reference something from base
which you aren't doing
are your files in media/scripts/?
if so, your files aren't being loaded whatsoever, because otherwise they would overwrite the vanilla files and you would see half the items go missing 😅
the actual scripts seem good to me so the issue is probably something to do with where the file is placed causing it to not be read
yes
but I reference the base game Tootbhrush mesh and also the displayname in the item.txt
neither of those things are tied to modules
modules only exist within the scope of script objects, the mesh is just a file path
displaynames are actually basically an unused feature at this point (but you *do* have to specify one or the translation system fails 🙃) but they were never tied to modules either, they're just a string
so the mesh for the toothbrushred model would be valid without importing base?
where can I find this again?
It's just that I have no way to test my item in game because the debug itemlist only shows vanilla items
so I'm going to just add it to loot table
if it's not in the debug item list, your mod is not working
o
I've created a new case item
my plan is to add the pistol mag and ammo box to its loot table
and then force insert this new case into bedroom drawers etc
should work
you only need to import a module if you're using script objects from that module - it's mostly useful for things like recipes where you're going to reference items from other modules, the only reason i can think you would need it for an item definition is if you used a vanilla Model script (your scripts create a new model script in your own module that uses the same mesh)
I see
also would you happen to know where some docs are on loot tables/procedural loot generation? I have never d one this kind of thing. If not, I will have a look at some mods
it's just a bunch of nested tables, you can see them in server/Items/ProceduralDistributions.lua
I should've known to look at the game's lua, ty
the format is item, chance repeating, so you find the loot table you want to add the item to and do ```lua
local lootTable = ProceduralDistributions.list.Antiques.items
table.insert(lootTable, "MyModule.MyItem")
table.insert(lootTable, 5) -- set this to whatever spawn chance seems appropriate
okay i got it
require 'Items/ItemPicker'
require 'Items/Distributions'
require 'Items/ProceduralDistributions'
local zReProcedural_InstrumentalDistr = {
'ArmySurplusTools',
'ArmyHangarTools',
'GarageCarpentry',
'GarageMetalwork',
'GarageMechanics',
'GarageTools',
}
for _, v in pairs(zReProcedural_InstrumentalDistr) do
table.insert(ProceduralDistributions["list"][v].items, "MyModule.MyItem1")
table.insert(ProceduralDistributions["list"][v].items, 0.5)
table.insert(ProceduralDistributions["list"][v].items, "MyModule.MyItem2")
table.insert(ProceduralDistributions["list"][v].items, 1)
end
ty!
PoliceStorageGuns
SecurityLockers
PoliceLockers
On this
how do I make the game read this?
there was something at the bottom of the distritibutions file but
require 'Items/ItemPicker'
require 'Items/Distributions'
table.insert(Distributions.PistolCaseMakarov.rolls, 1);
table.insert(Distributions.PistolCaseMakarov.items, "yourModule.yourItemId");
table.insert(Distributions.PistolCaseMakarov.items, 1);
require 'Items/ItemPicker'
require 'Items/Distributions'
table.insert(Distributions.PistolCaseMakarov.rolls, 1);
table.insert(Distributions.PistolCaseMakarov.items, "yourModule.yourItemId");
table.insert(Distributions.PistolCaseMakarov.items, 1);
PistolCaseMakarov = {
rolls = 1,
items = {
"Base.Makarov", 200,
"Base.9x18mmClip", 200,
"Base.9x18mmClip", 10,
"Base.Bullets9x18mmBox", 200,
},
junk = {
rolls = 1,
items = {
}
}
},
idk how to copy paste code in a brick like that
if you're adding a new loot table you don't need to use table.insert, just do```lua
Distributions.PistolCaseMakarov = {
rolls = 1,
items = {
"Base.Makarov", 200,
"Base.9x18mmClip", 200,
"Base.9x18mmClip", 10,
"Base.Bullets9x18mmBox", 200,
},
junk = {
rolls = 1,
items = {
}
}
}
(and in fact table.insert will not work)
rly?!
oh
hmm okay that's what I wanted
Now I've got to use
require 'Items/ProceduralDistributions'
table.insert
to insert this thing
into like closets and drawers right?
yeah, although keep in mind that requiring vanilla files does literally nothing
What about the warm fuzzy feelings it gives them?
there's value in that
❤️
praying this works
test time
nooo
Okay it works
But there's nothing inside
oh... nvm
Am I missing something?
so im looking at the happy gilmore vhs mod as a reference and omg
i dont need this guid stuff
i can just use the text str8 in the file
hell all i need is just a RecordedMedia file for the tape dialogue
Quick question: Is their an easy to follow list of distribution tables, available areas, etc?
Does anyone know why some textures are centered, and others are not, for example here upper containers are on top in Tiled, but when drawn in game (with drawTextureScaledAspect) in BrushTools they are centered. Corner counters also, in the Tiled they are the bottom of the tile and drawn in game centered.
Hello, here I propose a way to get compatibility between metal welding mods. https://steamcommunity.com/sharedfiles/filedetails/?id=3229360391
hello
i want to ask about Jump mod:
Why is the OnPlayerUpdate check used?
Isn't it possible to use environment check around player at the moment of key clicking?
Or is it all that complicated...
Question regarding event performance...
or key pressing, btw
when the key is held down - check the terrain - release - jump, if all is well
This is for historical reasons. I initially used 'e' key with specific tempo that needed a constant callback.
Does anyone know how I can intercept servermsg ?
Like if I do "servermsg testing" in console, how can I check what was sent via lua, if possible?
@untold orbit Server-side handles this all in Java space.
Only client-side is exposed via ISChat.lua
You can figure out how to send this via servercommand to the server and then handle it via file I/O with a separate process to pick up or use a Java mod for the server like one of mine to intercept it in Java and handle it however needed.
I want to intercept even if noone is online so client wouldn't work 🤔
Wouldn't java mod mean others can't play unless they install that updated file?
No. If you modify the server's Java you should be fine as long as it doesn't crash or error.
ooooh really?! So if I decompile, edit and compile classes directly on the server it will work? 👀
Yes.
You're speaking to the most experienced modder for this specific niche.
I've spent thousands of hours in this code doing this for servers in the past.
I answered you because that. =P
You have any guides? I've done java before 👍
I had this very specific need for server msgs.
So uhh IntelliJ IDEA is your friend.
Build an environment where you zip up the class files for PZ and then make it a jar file. Reference this. Reference the other libraries in the game's folder.
Double-click on the class you want to modify, copy its contents then paste it in an identical package in the src of your project.
Clean it up, modify it, compile it, inject it back into your files and test.
Then you're set.
Nothing much to a guide other than the nuances of decompilers and how to understand issues with them when they show up.
I've got IntelliJ setup already, what do you mean exactly with zip up the class files for pz and make a jar file?
Jars are zip files with the .jar extension.
Zip up the .class files from PZ and then rename the ext to .jar.
It will work as a dependency.
do you know how to get realtime sec ingame? same as os.time(), but this is stuff changes on pause and not scale with game speed
or ms
There's an official API call for it. ZombMillis or something
It's there.
I don't know it by memory but I know it's there because I needed it a lot.
So I get the files under the java folder of PZ and zip that?
Exactly.
ehhh...
declaration: package: zombie.util, class: PZCalendar
@LuaMethod(name = "getTimeInMillis", global = true)
public static long getTimeInMillis() {
return System.currentTimeMillis();
}
Ok I have those files zipped, IntelliJ started with the startup screen, now what 😄
local timeMS = getTimeInMillis();
If you know Java then the rest should feel natural to you. Make a project.
getGameTime():getUnmoddedMultiplier()
You know that double-clicking a .class file in IDEA will prompt fernflower to decompile it.
Set this in the same package in your source control folder.
thanks, i found something too :з
You modify and then compile it against the game's Jar file you made.
Take your compile and paste it in and run the server.
Then it's development and production my friend.
If you develop in Kotlin it's doable with PZ modding too.
Are there any plugins to speed things up or should I just create an empty project?
I personally use Gradle but a maven project is pretty quick.
GitHub
Contribute to asledgehammer/PipeWrench-Java-Generator development by creating an account on GitHub.
Here's a gradle project that uses a folder lib/* that has the pz Jar in it locally.
If you're comfortable with that, you could use something like this to compile and take your compiled files and patch a copy of the server.
I have more advanced tools for jar injections for patches for the server but since there's no demand for that here I don't push it out.
What decompiler do you recommend?
I stick with fernflower. I've used it since 2011.
So IDEA packages and provides fernflower.
Just double-click on a .class file in your IDE and it'll prompt you a agreement to sign and then it'll work.
If I just want this for personal use, for my own server, is it really necessary to use Maven/Gradle? I assume a normal IntelliJ build would do when I create the project?
No. I use it because I'm simply comfortable with it.
I linked it as an option for you if that's something you're interested in.
Ok so I edit and then click build then upload the file I edited to the server, restart server and enjoy the crash? 😄
Pretty much.
You build against the original compiled files.
It's a remarkably simple process for how drummed up the act of modding the core of the game is.
The complexity comes from the knowledge of the codebase and what's being done.
Yeah I get that. Got to start somewhere tho!
looool
what's...
hmmm
why here exist two stuff to transmit Rust on vehicle
like transmitRust()
or sendClientCommand(getSpecificPlayer(getPlayer():getPlayerNum()), "vehicle", "setRust", { vehicle = vehicle:getId(), rust = NUMBER })
whats better to use for mp?
local zReRust = 1.0
sendClientCommand(getSpecificPlayer(getPlayer():getPlayerNum()), "vehicle", "setRust", { vehicle = vehicle:getId(), rust = zReRust })
or
vehicle:setRust(zReRust)
vehicle:transmitRust()
???
hmm... better to throw lua on serverside and
vehicle:setRust(zReRust)
vehicle:transmitRust()
i apologize...
hi is there any guide on how to start modding? never modded a game before but i would like to learn ^^ i know java if it helps
I pinged them into the resource thread.
java.lang.UnsupportedClassVersionError: zombie/chat/ChatBase has been compiled by a more recent version of the Java Runtime (class file version 63.0), this version of the Java Runtime only recognizes class file versions up to 61.0
What version does PZ use?
I use sdk 19?
Use the latest and compile it to 18 iirc.
Used same JDK as the server and it works now. Cool. Time to dive into the Java code...
If you have non-intensive questions I may answer.
Been busy myself here in TypeScript.
Thanks! I need time to test and if I get stuck I'll shoot a message in here.
Is the author of the Dogs mod here? I was wondering if it's compatible with superb survivors, or if it's just compatible with superb survivors continued
if you want the basics i can sure help with that aspect XD just started working on my 3rd mod now and doing it from scratch, do you have an idea for what you want to add to the game?
https://github.com/FWolfe/Zomboid-Modding-Guide Other than me just helping there is a nice guide iv used before and it helped me get started pretty well
i still go back to it quite often if i get stuck or need help but dont want to bother anyone here lol
ty! i dont have any specific idea atm but would like to learn and this will help i believe ^^
im also new at project zomboid in general xd
a good place to start might be looking at adding every day items, i started at making a vape mod got way over my head and ended up learning so much but in the end it came together very nicely! a big thing that can help is download a mod from the workshop, and look at its files and see how its structured and how they built it, and if you find out what you wanna do find mods that are like what your trying to do and see if you can get any hits from what they did
but never copy and paste code if you dont know what it does
its written in java right?
and ty ill try it xd
mostly what mods are written in is Lua, but if you understand java you will have no issue with Lua, the game its self does run java code though
also text documents lol
on the left is the text document part and the right is lua, small ss of what iv worked on but didnt really ever have to touch java
im sure there are mods that do touch the java code but i wouldnt know, as iv only ever done small mods
that's very cool tbh. you also needed to add textures for the vape i guess?
depends how far you wanna go with the mod, i wanted mine to have the vape in hand when smoking, and since i already do 3D models for my hobby, figured it was easy enough, so every item in my mod has a model and a texture to go with it
im actually working on a model as we speak, its a mil crate, needs a texture and a model for this item, it will sit on the ground for players and they will be able to use crafting to trigger its random roll at loot
also you dont have to go as detailed as i am, for the most part the less there is the better for fps reasons, but i dont post my mods they are for me to have fun with so i go alittle over kill
wow that's nice, i guess there are random free textures i can use somewhere
well if you ever need models there are plenty of people in here that can teach you that, (my self included) and textures are the easy part lol
i actually just finished it XD, but after learning all of this you dont really forget it, i took a break for over a year then just started doing this one
thanks hehe , what software is used for making these? blender? (the only software that i know of lol)
yes i am using blender to make the 3D models, i use a few programs i can give you a list they are all free
Blender = 3D Models
Krita = Paint Program
Visual Studio Code = Coding Software
oh blender free cool i might check it sometime, thanks 🫶🏻
not a problem at all, if you ever need help let me know!
Got a question! Without manually scaling an item, in this case a rifle, down in blender what else can I do to make a model fit in game without significant texture problems?
one sec i have something for that
iirc it is something that goes in to the module section like:
mesh
texture
scale
I can't seem to get the scaling to work, however.
Hence the blender method that... doesn't work all that wonderfully.
Yup, I have something like that but I'm having issues with it. I'm certain it's a me issue though. I'm new to this so I'm bound to have done something dumb.
Once I can get back home I'll show what I got.
👍
hell yea very nice!
Hi All! Was hoping for a little direction on where to look. I want to start with something super simple (or should be) that bugs me to get into PZ modding. The saved dropdown on the trait selection screen, I want to sort that. I'm trying to figure out where to start honestly, to modify that specific sropdown, if anyone has a good idea! Thanks!
https://youtu.be/N6tZujOPnDw This video shows how to add a custom drink object to the game, and should give you a good introduction to a lot of different elements of the development process-- all under 1 hour.
This tutorial video will show you how to make a simple drink in Project Zomboid. I will go into the steps involved from start to finish. Read more below.
0:00 intro
1:08 Start
1:44 Searching for images to use in game
2:30 Searching for the Sound Effect
3:06 Creating mod file directory
3:54 Creating mod.info
4:34 Creating a poster
7:14 Adding ne...
Thanks!
Not a problem; there's a lot of resources here as well, both to start from and to continue your learning after.
apparently iv been gone to long, im trying to get the items to load in game but its acting like the mods not installed at all, i checked it is am i doing something wrong if anyone can point it out in this photo
im
Hey all, in the procedural lists for the distributions, what does "weight chance" refer to?
For example
procedural = true,
procList = {
{name="ClothingStoresDress", min=0, max=99, weightChance=100},
{name="ClothingStoresShirts", min=0, max=99, weightChance=100},```
From what I gather, ```ClothingStoresDress = {
rolls = 4,
items = {
"DressKnees_Straps", 10,
"Dress_Knees", 10,``` indicates that containers labelled "ClothingStoresDress" make 4 separate checks, each time giving the listed items their respectively numbered chance of spawning.
I would imagine the "weightChance" of the procedural lists determines which of the lists is chosen? ie. if ClothingStoresShirts had a weight chance of 50, a clothing rack container would be twice as likely to contain dresses instead of shirts?
You're right, "weightChance" influences the probability of a specific item list (or procedural list) being chosen when the game decides what items to spawn in a particular container. Each procedural item list in a container can have a different "weightChance", which acts as a weighting factor for the likelihood of that list being selected during the item generation process.
Here’s how it generally works:
- when the game needs to populate a container with items, it considers all procedural lists available for that container.
- it calculates the total weight by adding up the "weightChance" values of all procedural lists assigned to that container.
- each list's chance of being selected is proportional to its "weightChance" relative to the total weight. For example, if you have two lists — one with a "weightChance" of 100 and another with a "weightChance" of 50 — the first list has a 2:1 likelihood of being chosen compared to the second.
So in your example, if "ClothingStoresDress" and "ClothingStoresShirts" both have a "weightChance" of 100, they have equal probability of being chosen. If "ClothingStoresShirts" had a "weightChance" of 50, then "ClothingStoresDress" would indeed be twice as likely to be chosen
i keep getting this bug log when i try to place my object on the ground
I'm trying to override (clamp) the java getWidth return value to try to fix the issue with too wide UI panels for ultrawide+ resos, anyone messed around with that?
trying to find where the magic factor is located that's used for stretching the panels based on screen resolution
So my keycard model shows up and works in game, but the crate does not they are both set up the same to the T i over looked everything and there are no mistakes please someone help
anyone able to voice chat to help me figure this out, i see nothing wrong with it and yet still cant place it
Doublecheck the distribution .lua for your mod, give it a 100% spawn rate somewhere and see if it spawns, as a means of isolating the issue
the crate in question, i was talking about when it is placed on the ground... for some reason every other model works but this one
i go to place it and the game yells at me saying that error i put in chat
all the models are exported as FBX but my models that have more going on they wont load in the game at all
Hi there everyone....Im new to PZ MODDING but have been doing some DayZ mods for some time now .....any tips for where to start learning....or maybe a few YouTube links
I shared those exact things a couple hours back to a pickle rick profile picture
@verbal yew just saw your mod
https://steamcommunity.com/sharedfiles/filedetails/?id=3228809000
If you need any info about zombies I can help you with that 👌
?!
doesn't seem to be not required for me... at this time
yeah just letting you know since I've worked a lot on zombies in the past months ;)
At least I don't have any ideas yet...
I'm smearing myself with mods. I'm looking at someone else's code. rewriting some... polishing my build modpack etc
Okay so now I'm looking for pickle Rick thank you lol
This tutorial video will show you how to make a simple drink in Project Zomboid. I will go into the steps involved from start to finish. Read more below.
0:00 intro
1:08 Start
1:44 Searching for images to use in game
2:30 Searching for the Sound Effect
3:06 Creating mod file directory
3:54 Creating mod.info
4:34 Creating a poster
7:14 Adding ne...
Thanks a heap mate
@TIS devs or whoever modder : When I change many squares in one session (from a mod), When I quit the game, it does not quit successfully. last log (in debug) is 'EXITDEBUG: IngameState.exit 7'. This also occures if I limit down to 5 the number of changes per OnPlayerUpdate. I did not test lower frequency. Has anyone any hints on what I could do to prevent this problem ? Note that it seems the save went OK as I can reload the game and my changes being saved I'll never get the problem with that same save.
Reason: Bad word usage
another note: with 5 max, I never get that kind of warning: 'IsoRegion Warning -> xx squares have been changed in one tick.' Still the game quit fails.
Good afternoon.
Hi, I'm thinking about to make a mod that brings a window to players 5 minutes before the scheduled restart of the dedicated server. Do you think it's doable ?
We just use /servermsg
and give warnings at 10 min, 5 min, 1 min, 30 sec
you use /servemsg with RCON scheduled tasks ? or a mod can send those messages ?
We have a little program which checks for mod updates, then send those over RCON
then issues the quit command
I found out how to make the character in PZ actually look sick when they get bitten
By using the bite texture but editing
Other parts of the body like the face
For my Boiling Rash mod they bleed a black substance from the eyes and mouth
Definitely a fun thing to mess around with
👀
That's amazing
Too bad this kind of thing is very small, so you could make a character visually sick from basic infections like having red eyes and shit like that but would probably be impossible to see
In your case it fits pretty well
Thank you
I'm honestly super excited to use this more
To add different physical symptoms
I did use red eyes
They do work fairly well
But you have to make them more bright to make them easier to see
I did that with the bleeding eyes for my Knox Plague mod
And it worked pretty well
The symptoms are of course easier to see in game as the image compression pixelates the texture
Just really hard to get a good picture of
yeah
This is the colors I used for the Knox plague version
They are visible
Though the brighter it is the better you can see the color
anyone know the name of the maintenance skill in the game files?
Thanks.
I'm looking for someone who could help me using sandboxvars options for my mod, anyone ?
Your mod pages would appreciate a small relooking 👀
?
Like to make them cooler
I'm not familiar with the art style of the original creation of your zombie stuff
But like your lore text you coul put it into a cool image alongside your custom zombies put in scene
(if they come from other games or else at least)
I have been trying to figure out how to use images and what sizes they would need to be in order to use them for that
I have 2 original plagues
And 2 that come from other media
👌
Hmm you can take example on my Susceptible Overhaul modpage
For the sizes at least
I often like to use a very government informative style
And I was trying earlier to figure out how to make a little vector image human
So I can make some symptoms charts
To put of the modpage
Kinda like the ones the movie cargo used
That would be helpful
Yeah you could take example on Susceptible's modpage for the kind of government informative style
So here on my modpage for example:
https://steamcommunity.com/sharedfiles/filedetails/?id=3204615438
And Susceptible (images aren't in the description for them tho)
https://steamcommunity.com/sharedfiles/filedetails/?id=2795677303
Now this helps alot
I can see the exact sizes I would need for different additions to the modpage
Thank you very much
