#mod_development
1 messages · Page 263 of 1
I wonder if another modder is using this to poison the lua environment somehow
because I remember some players having issues trying to join servers because of the mods that they'd loaded previously (then reset the lua env) and then loaded the server mods
which somehow caused the old loaded mods to load again alongside the server mods
now that I'm thinking about it, its nothing to do with loadstring but a weird quirk
Oh what I'm talking about is insanely outside of the normal Lua coding / modding PZ has.
now that's epic
You can use the LVM as an additional layer of obscurity from say Java-level tampering in a possibly compromised client.
LVM? What is that acronym
If you stop code from executing on the client that needs to run in order to keep say a multiplayer client from running properly on a server then make it so that data sent back is unique, obfuscated, encrypted and that it needs to respond to the server. Otherwise, kick em.
Lua Virtual Machine.
Anyways I'm rambling about fun side-projects. xD
You can use anonymous functions to keep from tampering / poisoning the global table and for security purposes.
Nope. It's Kahlua.
because I thought lua was interpr
oh I'm dumb lol
Java implementation of lia stupid irosjrbsobd
I'm simply applying the "code-cave" security mindset to this environment where Lua code is modularly compiled and stored in Kahlua's virtual machine inside Java's runtime.
Kahlua 2 mindfuck last update 1 decade ago stupid orange juice code
still better than anything i can do but it really gives you a run for your money mentally ill admit
It's all experimental security stuff which has roughly anything to do with 99.999% modding in pz. xD
But if servers with home-brewed mods want to secure both their works and protect against cheaters, this stuff becomes relevant.
the rule to go by is lua never ever implicitly copies anything except primitives (as they aren't objects to begin with)
isn't lua pass by ref but func args are by value?
I might have read that randomly somewhere or for a different language idk
it's always by ref
that's nice to know
is there any way to:
- force the game to reload lua without changing the mod list?
- clear the console.txt without having to close the game first to unlock the file?
you can reload lua from the main menu when in debug mode
how do i go into debug mode again?
launch the game with the -debug argument
thanks
Do you think this is a good mod poster?
I see ternery assigment is not possible in LUA is there anything equivalent to this?
local offset = isDrainable ? 20 : 0
it's a little weird looking, but local offset = isDrainable and 20 or 0
thats actually very similar to `const someProp = condition && valueA || valueB
hummm out of curiosity.... does local someVariable = value or defaultValue work in lua?
yeah
cool cool
the exploitable thing here is that and returns false if the first argument doesn't evaluate to true, but if it does it just returns the second argument, not whether that evalutes to true
would 0 and '' default to the or value then? or only false does it?
only false and nil evaluate to false in lua
ha! so no falsy checks got it
anyone mind explaining me what this .. operator actually does here? is it a string concatnator converting a into to string?
'' .. self.item:getDrainableUsesInt()
it's just the operator for string concatenation yeah
is it just being used like that on its own here? O_O i guess it is meant to be converting
kk so my assumption seems to have been correct 🙂
it usually implicitly converts, except when it doesn't
would there a more correct form of doing this in lua though?
yeah, tostring(something)
ha! i'll use that instead then
if you're referencing the vanilla lua keep in mind a lot of it is substandard and generally very weird
especially the older stuff gives the impression the devs may not have used lua before
nah this was lifted from another mod
same thing goes for mods as most are made by people new to programming in general and tend to be inspired by vanilla lua
there is this mod that lets you see the usages left for matches and batery levels, i'm using it as a template to make my own wich will show usages to all drainable items instead
but yeh i'm also refactoring the original code as it looked far to convoluted
Given my limited LUA understanding.... is there anything else that could be optimized here?
require 'ISUI/ISInventoryPane'
local parentRender = ISToolTipInv.render
function ISToolTipInv:render()
if not (self.item and instanceof(self.item, 'DrainableComboItem')) then return parentRender(self) end
local parentSetHeight = self.setHeight
local usagesValue = tostring(self.item:getDrainableUsesInt())
local font = UIFont[getCore():getOptionTooltipFont()]
local lineHeight = getTextManager():MeasureStringX(font, ' ')
local usagesValueWidth = getTextManager():MeasureStringX(font, usagesValue)
local margin = 6
self.setHeight = function(self, height, ...) return parentSetHeight(self, height + margin + lineHeight + margin, ...) end
parentRender(self)
local currentWidth = self.tooltip:getWidth()
local posX = currentWidth - usagesValueWidth - margin
local currentHeight = self.tooltip:getHeight()
local posY = currentHeight - lineHeight;
self.tooltip:DrawText(font, 'Usages left:', 5, posY, 1, 1, 0.8, 1)
self.tooltip:DrawText(font, usagesValue, posX, posY, 1, 1, 1, 1)
self.setHeight = parentSetHeight;
end
Evident
A lot of C# / Pascal-influenced devs worked on the Java end.
It explains the inconsistent casing across the code base.
Wasn't there a mod called Toxic Airstrike? Did the author delete it? I was so looking forward to playing that.
I've been banned from the project zomboid reddit
from this image
The least important part of my pack A FUCKING MANJI.
Manjis go the other way - that's a swastika
Probably a good idea to correct that 😅
Also maybe don't include something that's 90% similar 🤷♂️
I didn't know the are so many examples of that icon in different settings
Litterally the reference i used
I would of deleted it and corrected it from the pack if its really an issue for people but i got perma banned already
People are generally not fans of Nazi symbols
Not sure if it was an automod (doubtful) but your sprite also doesn't make it clear it's crossed out either
or the finnish aviation
Well whoever spray painted that did it wrong then
I just used it for realism and now im busted for referencing real grafeetti
You're not really helping your case
Well now you know that they don't like nazi symbolism—surprise—and you can correct it & maybe reach out and appeal
I don't know how to sppeal on reddit
That's because you accidentally used a swastika for your mod. I get that it wasn't your intention but it's your fault for not checking if that was the right way to depict a manji.
Probably by contacting one of the reddit mods, idk how reddit works
I just want to share my new tile 😦
Yeah we know
You still fucked up lol
15 minutes to ruin 4-5 days of work
It was an accident man, just get an appeal and fix the symbol in your mod
I'ma just call a spade a spade and say - its not a manji - just own up to wanting to include edgy imagery and move on to appealing.
you could try contacting spiffo bot , but yeah i would message them because its def the wrong way for a manji when it comes to symbols like that you really need to do proper research on them and i would always double check with the moderation team here before posting anything like that for clarification
👆
Its realism not edgy
lol
You've acknowledged that it's not a manji, right
Yes, the manji thing was bs
not yet i thought it was a manji
Okay, there's the ticket. You being mistaken does not change reality
It isn't a manji. Do a google search & then stop reposting an image with a swastika in it
Dude, I guarantee that most people aren't gonna get the reference and will assume that its a swastika, because that's what it looks like. Just change the way it looks so it's actually depicted correctly.
I'll just remove it. its not really important to the tile set.
I was just pissed with a perma
I'm sure they'll understand that accidents happen if you explain, especially if you intend to remove it
I just want feedback for my tiles
The is also the possibility i mirrored a manji i created
It looks good, fits the style nicely.
thanks G guess that one way to get feedback XD
rant in the discord = feedback on tiles 🙂
There are certainly better ways to get feedback 😅 I do like them though
Well heres some wall tiles
Oh and floor tiles
ANd this unfortunately the Swastika is in it 😦
Fixxed
Nice—unfortunately I don't have much feedback since I don't do much on the art side, but everything looks neat
I imagine the folks over in #mapping would have more to say
Oh hey I forgot about ya.
Hope all is well.
Haven't been frequenting this channel much, same to you
Been so damn busy as of late.
I'm kicking around some python stuff to fix a reload issue with Pythoid.
Don't worry though, I'm still just as chaotic and explosive as you remember. =)
Always working on something crazy.
I'm working on things squarely in the realm of sanity, but nice to hear Pythoid is making progress

Zomboid remade in Python?
Zomboid python mods support.
I attached a Python interpreter that works in Java environments to PZ and adapted some stuff to make it work nicely.
Oh nice! I wonder if that could be used to tackle a similar issue I had; off loading some server processes for multiplayer to multithreaded environments
It has unlimited access to the JRE API so maybe?
Good luck on it! It sounds like it took a lot to get working 😅
It's more about getting funny situations fixed than getting it working. I already have a few examples on GitHub with a installer / updater for the patch.
The current one has an issue with reloading so I'm trying to get it fixed.
Been busy IRL so it's taken a long time to get back.
I feel that, sometimes irl stuff can drag away from the fun projects. And I'll check out the git some day soon!
FART
Yes thats the type of stuff people write on the walls
are you a smart fella or a fart smella?
Literally references from this
a smart smella
no wait i mean fart fella
wait im lost
is there a way to get a item "class" name as a string?
in LUA i mean
essentially i'm looking for the means to figure out what item "class name" is to be used with the instanceof instruction
you can print it if you just need to see what class it is
if you need to do it at runtime for some reason you need to tostring it and extract the class name with pattern matching
This is a simple one and I'm kind of embarrassed to even ask as I have edited that file before, but I have been searching for the past two hours and I can't find it for the life of me.
I'm looking for the file that lets me edit the text of the health tab. It was just a simple file that had that info that I could easily edit with Notepad++ in like five seconds.
Ok i tried printing the item into the tooltip and from the results i'm getting i seem to be off mark, essentially i'm trying to extend RepairAnyMod with my own mod to include the items added by Yaki's makeshifit clothing... now looking at the first one all i seem to have to do is to map those objects through this:
TweakItem("Base.ArmsProtectionLower_01-Black", "FabricType", "Leather");
but how do i figure out what the ID is for the items of Yaki's mod? my plan was to somehow inspect them in game....
you can see that with item:getFullType() but i'd probably just look at the item scripts at that point
Given my lack of knowledge on how to properly mod PZ what should i be looking for in Yakis mod? Lua files?
what are you trying to do
Add yaki's clothing / armor to the RepairAny mod
idk what repair any mod does
hey glytcher that guy in your steam comments about the bus thing, it's true, at least with his modlist I could reproduce the issue
I'll have to look to see why it could be happening. but idk
thnx but i fixed this like 7 hrs ago
its cuz i checked for stuff earlier than i should have
i didnt have to do it that way cuz it will always return true when any of the feature is on even if theres no bus sprite
o nice I was just letting u know
i appreciate it ! thank you
apparently issue still persists, ill have to check myself but yer
world war 2 zombies
i just read im dumb i pasted on different folder
i pasted mods folder outside
I have a question about some "voice line mod"
Somebody ever tried to make a mod that put real voice lines on the TV Programms to do more imersion? I know some actors friends (myself included) that could help to make brazilian portuguese voice lines to the TV programs like WBLN Reports.
I just want to know if somebody already have this idea or are putting efforts to make this a real thing.
vanilla btw
the other is from theworkshop
would it allow me to actually finally put more tools on the tool belt?
Does anyone have any idea which file that could be? I know I've edited it before, but I can't freaking find it again.:I
My first mod (Overpowered for people who want to chill on zomboid with no worries of dying)
How do I make a player eat a food item faster?
Yes
There is a project, tho idk if it's still active
Hey guys, do you know if it's somehow possible to rotate annotations on the map? I'd like to allow text to be displayed diagonally to match the grid but so far I didn't find a solution
Hey, where i can find vanilla "AlcoholPower" script?
What are you looking for exactly?
how alcohol made players tired over time
I see, I'm gonna try to find something
I don't think this mechanism is exposed to lua unfortunately, you might have to poke into the java side
sad, but thanks
You might want to check the ISEatFoodAction:new method, specifically the maxTime variable
can you show me an example
Adding o.maxTime = o.maxTime / 10 before the return o at the end of the method would make it 10 times faster for every food item (drinks and cigarettes included).
Hello, a question, how can I make a weapon reload faster? I modified the ReloadTime parameter to a lower number but the weapon still reloaded the weapon as if I had not modified anything.
the duration of most actions like that is tied directly to the animation
ooooh ok thanks a lot jajajh
I checked the Java implementation and couldn't figure it out, so please let me know if you know.
If a zombie is nearby while sleeping, it will not attack until the player wakes up.
Do you know the implementation reason for this?
is IsoThumpable is used to make something that's built by the player that's health can only be modified by zombie thumping or I guess a sledge hammer
been mostly browsing through the lua code but there's a lot of java functions mixed in, so im just trying to make sense of it since im not on my desktop and most of my ram is being gobbled up by Zed so I doubt i'd have any luck using intellijs decomp stuff in the app
If I remember correctly vanilla reload time is only speed up by reloading skill and tied to animation itself and reload time on gun does nothing, however firearms b41(at least original version) made it that reload time on guns actually matters so you might take look at it?
Ah yes, childhood trauma converted into a weapon of terror on zombies
im not sure if theres a vanilla way of the sound automatically lowered but heres what i did and would like to share to you guys
dynamic volume based on distance
function PsychoZedMod.VolumeHandler(dist, range)
if dist >= range then
return 0
elseif dist <= 0 then
return 1
else
return 1 - (dist / range)
end
end
function PsychoZedMod.getCrySfx()
return 'PsychoCry_'..tostring(ZombRand(1,7))
end
function PsychoZedMod.getScreamSfx()
return 'PsychoScream_'..tostring(ZombRand(1,3))
end
function PsychoZedMod.getLaughSfx()
return 'PsychoLaugh_'..tostring(ZombRand(1,8))
end
function PsychoZedMod.playSFX(zed, sfx)
if not PsychoZedMod.isPsychoZed(zed) then return end
local range = 50
local pl = getPlayer(); if not pl then return end
if PsychoZedMod.isClosestPl(pl, zed) then
local sq = zed:getSquare()
if sq then
local vol = PsychoZedMod.VolumeHandler(PsychoZedMod.checkDist(pl, zed), range)
if luautils.stringStarts(sfx, "PsychoScream") or string.find(sfx, "PsychoScream") then
if SandboxVars.PsychoZed.DeathScreamAttract then
addSound(zed, zed:getX(), zed:getY(), zed:getZ(), 50, vol);
end
end
getSoundManager():PlayWorldSound(tostring(sfx), sq, 0, 50, vol, false);
end
end
end
does anyone know which file the vanilla professions are defined in?
shared/NPCs/MainCreationMethods.lua
appreciate it 💪
someone knows how can i learn to make UI´s ?
and if IntelliJ IDEA helps of learning how to mod ?
@tranquil kindle you´re my hope hahaha
@smoky tendon
Never have i ever touched UI and IntelliJ sounds like some kind of cpu to me XD
hahaha
but like i see the ui.lua examples in the game files
and i dont understand nothing
do u know where is the debug mode scripts ?
and UIS
i think its more ez to learn seeying the debug menus
nah , im just going to give up , no ones explain nothing.
I can't explain something that i just don't know nothing about
i know , its not your fault
im searching about learning how to make a single ui/menu for 1 hour and i didint found anything
THANKS ! @indigo pasture
any sound with a distanceMax will do this automatically and trying to do it manually will interfere with that
Hey everyone, does the Red Error Box is so hard coded that no one ever managed to make a mod to disable it? I know the devs won't disable it but I wonder why no one made a mod for it (that I know of)
You're supposed to fix them
Heresy
Thanks!
is ZombRand() some kind of "predictable pseudo-random" generator function that returns same values on server + on client? or what's its purpose?
it's just a random number generator
because lua standard libraries don't have one?
they're supposed to, but kahlua doesn't implement it
ok, thanks
But we have a seeded random
Ah nvm read that wrong
Regular Lua does too (just for the record, in case that was intended to be contrastive)
it's actually a really weirdly high quality random number generator that arguably should not have been used for nearly anything the game uses zombrand for
you can call on java's native random function with```lua
local rand = newrandom()
rand:random(min, max)
Yeah I use that now
Hey guys, I'm trying to figure out how evolved recipes work, and it looks like my recipe for a protein shake doesn't have any valid ingredients. What should I put into the evolvedrecipe parameter for the game to recognize the protein powder as an ingredient?
I've got it ste to JunkiezProteinShake, which is the name of both the resulting item and the evolvedrecipe
EvolvedRecipe = RecipeName:HungerAmount e.g. EvolvedRecipe = JunkiezProteinShake:10 if you wanted it to add 10 hunger from the ingredient
Thank you for the info! I have it following that format but it looks like there's something I'm still misunderstanding. Would you be willing to help me look through the code block?
Is it possible to defined a recipe created item to have less them 100% condition?
ok so i figured out how to change the created item via binding the onCreate handler with a lua script.... now is it possible to change the created item name? i tried both of this with no success:
result:setDisplayName('Improvised ' .. result:getDisplayName())
result:setName('Improvised ' .. result:getName())
btw is there any public API documentation for this stuff anywhere?
nvm i had a bug in my code wich was actually preventing the newer version from runing the setName actually worked
go ahead and post it, but i can't promise i'll know the solution, i don't do too much work with recipes
imports {
Base
}
/** Comments can be multiline like this
and can appear anywhere in the file, even in the
middle of a { block }
**/
item JunkiezProteinPowder
{
DisplayName = Protein Powder,
DisplayCategory = Food,
Type = Food,
Weight = 1.0,
Icon = JunkiezProteinPowder,
CantBeFrozen = TRUE,
EvolvedRecipe = Soup:5;JunkiezProteinShake:5;Beverage:5;Beverage2:5;Cake:10;Sandwich:2;Sandwich Baguette:2;FruitSalad:2;HotDrink:10;HotDrinkRed:10;HotDrinkWhite:10;HotDrinkSpiffo:10;HotDrinkTea:10;Pancakes:10;Waffles:10;ConeIcecream:5;PieSweet:10;Toast:5;Oatmeal:10,
Packaged = TRUE,
HungerChange = -40,
Calories = 2440,
Carbohydrates = 48,
Lipids = 120,
Proteins = 480,
UnhappyChange = 20,
ThirstChange = 50,
}
item JunkiezProteinShake
{
DisplayName = Protein Shake,
DisplayCategory = Food,
Type = Food,
Weight = 0.8,
CustomContextMenu = Drink,
Icon = JunkiezProteinShake,
CantBeFrozen = TRUE,
HungerChange = -15,
Calories = 250,
Carbohydrates = 18,
Lipids = 12,
Proteins = 120,
ThirstChange = -20,
ReplaceOnDeplete = WaterBottleEmpty,
CustomEatSound = DrinkingFromBottlePlastic,
StaticModel = WaterBottle,
WorldStaticModel = WaterBottleFull,
UseDelta = 0.1,
UseWhileEquipped = FALSE,
}
evolvedrecipe JunkiezProteinShake
{
BaseItem:WaterBottleFull,
MaxItems:4,
ResultItem:JunkiezMoreSupplements.JunkiezProteinShake,
AddIngredientSound:AddItemInBeverage,
Name:Prepare Protein Shake,
CanAddSpicesEmpty:true,
Template:JunkiezProteinShake,
}
}
Thank you @bronze yoke , I appreciate the extra set of eyes
It works for all of the other evolved recipes, just not the protein shake one
hmm, let me check something
ughh this is dumb, i completely forgot about this
the EvolvedRecipe = thing is hardcoded to only ever search the Base module
Oh! Does that mean I need to set it to JunkiezMoreSupplements.JunkiezProteinShake:5?
unfortunately that'll just make it look for Base.JunkiezMoreSupplements.JunkiezProteinShake
i think from looking at this you can probably attach an item to an evolved recipe using lua but the straight forward solution would just be to move the evolved recipe into base (everything else can stay in your module though)
I'll give that a try! Let me move that over now
Looks like we finally have some progress! I don't have the option of making the shake if I right click on the powder or the bottle, but the craft helper finally recognizes it as an ingredient
cuz my experience with audio is that the distance only affects whether you hear it or not. and not actually adjust the volume.. but i could be wrong
Use ContextMenu 
you can just adjust the volume in the clip like volume = 0.25,
also distance affects the volume too: "Between the minimum and maximum distance, the sound volume will gradually decrease."
so for example, if the sound is like this:
sound mysound {
...
is3D = true,
clip {
file = media/sound/somefile.ogg,
distanceMin = 10,
distanceMax = 100,
volume = 1.0,
}
}
Feel free to correct me, but I believe that at distanceMin tiles away, the volume will be 1.0 - and at distanceMax tiles away the volume will be 0.0 (and anywhere between is the volume scaled linearly based on the position between min and max).
Or, if you don't specify distanceMin, then I presume it just scales the volume from the sound's position to distanceMax instead of from distanceMin to distanceMax.
the only correction i have is that it isn't scaled linearly, dropoff is exponential
ah ok, I did assume that it was linear my bad
i considered nitpicking this in the code example as linear would probably sound very unconvincing
true, I wish the dev could change the exponent
like to have the curve change
on a side-tangent, what would be a decent way to auto-detect what script you are editing?
I don't think textmate grammar has that complicated of features, I thought maybe looking at the filename to see if it ends in a suffix like guns.item.txt or metal.recipes.txt something like that but that seems too hacky
actually ignore what I said as scripts can have multiple things in them ;-;
I am thinking with my "encapsulation is good practice" brain instead of "what is possible" brain
i do actually like the idea proposed here though, an annoyance with the zedscript plugin is that there isn't an extension for it so it can't enable itself
yeah I have to manually do it for each script and it's so frustrating
i'm sure the game would still accept e.g. items.zs.txt
and when I restart ide I have to do it again
ya
I will check
it does load it
when i posted the code about the volume it was for a different mod
but im doing this now which is also related to sounds.. i couldnt fetch all the audio scripts. i tried various syntax and closest i got to retrieve an array was using
GameSounds.getCategories
but the categories returned empty arrays
getScriptManager():getAllGameSounds()
this lags when i try it
getName() doesnt work 😦
Modding noob question... the client, server and shared folders inside most mods lua folder is it by convention or is there effectively a difference when placing lua files on each of those folders?
local sounds = getScriptManager():getAllGameSounds()
for i = 0, sounds:size()-1 do
local sfx = sounds:get(i)
print(sfx)
end
There is
Client side, all 3 folders are loaded
Server side, only shared and server are loaded
I've a very simple idea for a mod (very niche but probably easy to code). If anyone's interested lmk
Thanks! this is interesting so what's the point of the shared folder since both it and the server folder are loaded for the client side??
Is it possible to "listen" to player actions like dig grave and have an event triggered with a reference to the item used to do such action?
to add on to jvla's answer, the loading goes like this:
Client
Load lua files inside of shared
Load lua files inside of client
Load lua files inside of server
Server
Load lua files inside of shared
Load lua files inside of server
you bring up a good point that what's the point with the folders, most people including me just use the folders as categorisation, since like you mention the client loads all 3 anyway
but here's the general gist of how I structure my mods:
- All client code goes in
clientfolder like normal - All server code goes in
serverfolder, but since it can be loaded on the client, prevent the entrypoint from running by checking if the client is loading it and returning (will be different based on how you structure your mod's lua code) - All shared code (code that server and client both use) goes in
sharedfolder like mod metadata (id, version, dummy structs/tables, type definitions, etc)
Of course you could just put the client and server code in the client folder, but then the server wouldn't load it
You could also put all the client code in the server/shared folder and there are no downsides, but it's kinda counter-productive and harder on yourself lol
tl;dr it's kinda stupid
thanks for the in deepth answer, though i'm still at a stage that i can't even figure out what should go just for the client and just for the server so for now i'll just stick to the shared folder :x ::ducks incoming modding experts anger::
well it depends what you want
right now just playing with creating items based on the vanila versions
a LOT (and I mean it!) of your code will be client sided just because that is how the game has been made
the only things that would need to go in server folder are stuff involving either code that syncs stuff between players or somethn else I can't remember
for example i have a new recipe to create an improvised shovel out of branches, sheet strips and sharp stones, however the generated item is a copy of the vanilla shovel with just 2 max condition
would this sort of code require to be server side?
module Base {
recipe Improvise Shovel {
TreeBranch/MetalPipe/MetalBar=1,
LeatherStrips=2,
SharpedStone=1,
Result:ImprovisedShovel,
Time:300,
Sound:PutItemInBag,
Category:Improvised Tools
}
}
like that
ImprovisedShovel is just a copy of the vanilla one with less condition
btw im curious, why did you pick Base module instead of your own one?
I see literally every modder do that, but I can't understand it
module Base {
item ImprovisedShovel {
(...)
ConditionMax=2,
(...)
DisplayCategory=Improvised,
DisplayName=Improvised Shovel,
(...)
}
}
just learning my away around... it's far less problemtic to change existing stuff then create it from the ground up without knowing what i'm doing 😦
These are all in item scripts, if what you mean by code are just these script definitions for the item and recipe, they're technically loaded on both the client and server, and aren't involved with the Lua folder (unless you use some callbacks like OnCreate)
that's my next step
so what i wanted to do now is for example had a high chance of that item breaking when used to TakeDirt or DigGrave
Oh, if you want to do that then I'd put the callbacks in the shared folder
I am trying to think for how you would get the takedirt/diggrave action thing
yeh been digging through the vanilla lua to try to figuera that one out
i was looking at this one right now:
lua\server\BuildingObjects\ISShovelGroundCursor.lua
function ISShovelGroundCursor:create(x, y, z, north, sprite)
local playerObj = self.character
local square = getWorld():getCell():getGridSquare(x, y, z)
local groundType,object = self:getDirtGravelSand(square)
local fullType,emptyItem = self:getEmptyItem()
if luautils.walkAdj(playerObj, square, true) then
ISWorldObjectContextMenu.transferIfNeeded(playerObj, emptyItem)
ISWorldObjectContextMenu.equip(playerObj, playerObj:getPrimaryHandItem(), predicateShovel, true, true)
ISTimedActionQueue.add(ISShovelGround:new(playerObj, emptyItem, object, "blends_natural_01_64", fullType))
end
end
would it be feasable to override this and somehow get a ref of the item used to trigger this and break it?
Ideally you'd override the ISShovelGround action function that is after the action is performed
because if you did it in this function create, if the player walks away your code would be executed
So I'd personally hook ISShovelGround:perform because perform function is called when the action is completed, then you can calculate the things and damage the shovel
hummm is that action in this file though? still learning how to read this code
Most likely not, they usually put their actions in a separate file
lua\client\BuildingObjects\TimedActions\ISShovelGround.lua it seems
yes
from what little i can understand from this code i'll have to get both items on the character hands and then figure out which one is the shovel right?
btw is there any public api documentation anywhere we can consult regarding this item, character and other objects?
You hook it like this (as an example)
-- Caching global variables for faster code speed
local getPlayer = getPlayer
local ISShovelGround = ISShovelGround
-- Fancy mmodule thing
local hooks = {}
--- Our perform function
function hooks.perform()
-- Do code at start of vanilla perform
print("hello at start")
-- Let vanilla perform code run
o_perform()
-- Do code at end of vanilla perform
print("hello at end!")
local player = getPlayer()
local shovel = player:getPrimaryHandItem()
-- Do your stuff here!
end
function hooks.init()
local o_perform = ISShovelGround.perform
ISShovelGround.perform = hooks.perform
end
return hooks
the whole hooks variable is just a fancy thing for a module, I think lua's PIL has it somewhere you can learn about
in this case, you'd just call hooks.init() when you want to hook it, like OnGameBoot
THOUGH getting the item is a whole separate thing
Timed actions are done on the client so you can just do getPlayer() to get the player (I will edit the example to demonstrate)
since getPlayer() function will return the client player (the one you are playing) but it will return nothing if the code is running on the server (because the server isn't playing as any player in the game)
ok ok i'm listening...
let me see how the action works one second
Do you know if the item has to be equipped at all for the action to be performed?
so we don't need to do something like:
require 'BuildingObjects/TimedActions/ISShovelGround' to ref ISShovelGround ?
No, lets see why:
i think so 🤔
I call project zomboid's lua implementation PZ Lua for this reason
it's not actually fully lua, it's Kahlua
its very wacky in it's own way
ok and does the lua file where i have this code have to follow any naming convetion other then live inside one of the 3 predefined client / shared / server folders?
You see how ISShovelGround is a global variable and isValid is a function which is being added to the ISShovelGround table?
is that on line 7 creating a global ?
yes
ha!!!!!
derive more or less returns a table
and as you probably know, you can reference globals from anywhere without referencing anything
so once delcared as a global it's available on all other scripts i would assume?
yep
kk got it!!!
btw I am not familiar with ISShovelGround action as I mod the game more than play it so what is the bag for?
It's referencing emptyBag and I assume it's like a bag to put the dirt in right?
so how do i get that init to trigger again you mentioned the onGameBoot event?
yeh i think for this action you need an item with 'takeDirt' like a shovel and an empty sack or a non empty sack of the same type
and then it will fill the bag with dirt, gravel or sand
depending on the tile
Well assuming the file is in the client folder (which it should be, action code is client-side and just get synced to server), you just need a file in the client folder like this:
-- Requiring our module from before
local hooks = require("hooks")
-- Caching globals
local Events = Events
local events = {}
function events.on_game_boot()
-- Call our function
hooks.init()
end
function events.init()
Events.OnGameBoot.Add(events.on_game_boot)
end
-- Just call events.init literally anywhere, even here!
events.init()
return events
this is how i'd do it so modify it as you wish
but try to learn from it instead of copy pasting, that's all I ask
Events is a global too, you can look at all the events here: https://github.com/demiurgeQuantified/PZEventDoc/blob/develop/docs/Events.md
kk thank you very much
so my new file with the override doesn't have to follow any particular name / path convetions other then living inside one of the 3 predefined folders right?
I will try to figure out how to get the shovel the player is using the best way I know of, I just gotta see how the action works
yep
but if you plan on requiring it, you need to use it's file name like I did with require("hooks") where the file name for the hooks file was hooks.lua
kk thanks i'm experiementing it my self as well but by all means let me know if you find it out
ok and the path for the require is always the path starting after client | shared | server right? regardless of being a vanilla file or a mod file?
yes
kk thanks
though do know that if you name files the same, the require will act weird
like if you have same named files in both client and shared, and you require one of them from another file in client for example, it will not know whether to use the client file or the shared file so I think it defaults to the last loaded one which would be the client file
It looks like it tells the shovel to be equipped into the primary hand so this is easy
I've updated the example, but it's pretty straight-forward
yeh i suspected that but haven't confirmed it yet
is there a way to check if the player is sneaking or sitting?
does this makes any sence in LUA?
improvisedToolsOverrides = improvisedToolsOverrides or {}
local shovelGround = improvisedToolsOverrides.shovelGround = {}
The idea is to lazely create a global called improvisedToolsOverrides and then ref shovelGround localy while creating it as a "prop" for the global...
you might have to do that in two statements as I don't think that will work
kk so no chaining assigments then
you could do ```lua
improvisedToolsOverrides.shovelGround = {}
local shovelGround = improvisedToolsOverrides.shovelGround
yea no multiple assignment
unless it's a function's return values
improvisedToolsOverrides = improvisedToolsOverrides or {}
local shovelGround = {}
improvisedToolsOverrides.shovelGround = shovelGround
i was going with this but yeh same idea
player:isSneaking() and player:isSitOnGround()
also from your example it seems like local vars are actually available on all functions in the same file regardless of where they are declared??
looking at this one in particular:
function shovelGround.perform()
originalPerform()
local player = getPlayer()
local shovel = player:getPrimaryHandItem()
end
function shovelGround.init()
local originalPerform = ISShovelGround.perform
ISShovelGround.perform = shovelGround.perform
end
Yes, anything local is accessable by the whole file (depending on scope too), but not outside the file
that is why I don't use local variables/functions inside my modules
so I can access them outside of the file by requiring the file
got the sneaking part right, thanks!
hummm i though you didn't had to require a file to have access to non local vars 😕
yes they are technically local but not local as you would think
-- can't access outside of file (local)
local var = "test"
-- can access outside of file (global)
var = "test"
-- can't access outside of file unless you require the module, since the module is local and returned by the file
local file = {}
file.var = "test"
return file
-- in a separate file
-- wont work
print(var)
-- will work since it's global
print(var) -- "test"
-- will work but only if you require the module
local file = require("file")
print(file.var)
Ha ok i get it
i mean i see you used the same name for both local and local var's but i get what you mean
anyway i'm getting an error with your example @grizzled fulcrum
it looks like getPlayer is not defined there?
Don't rely on the green file viewer line thing to show you where the error happened, it usually is on the line after the error and not the line that caused the error
if you read the error, it says line # 12
so your originalPerform function is nil
hummmmm
did you add it to your module?
local getPlayer = getPlayer
local ISShovelGround = ISShovelGround
improvisedToolsOverrides = improvisedToolsOverrides or {}
improvisedToolsOverrides.shovelGround = {}
local shovelGround = improvisedToolsOverrides.shovelGround
function shovelGround.perform()
print('--------> IMPROVISED TOOLS: shovelGround.perform START')
originalPerform()
local player = getPlayer()
local shovel = player:getPrimaryHandItem()
if shovel ~= nil then
print('--------> IMPROVISED TOOLS: Type ' .. shovel:getFullType())
print('--------> IMPROVISED TOOLS: Name ' .. shovel:getName())
end
print('--------> IMPROVISED TOOLS: shovelGround.perform END ')
end
function shovelGround.init()
print('--------> IMPROVISED TOOLS: shovelGround.init')
local originalPerform = ISShovelGround.perform
ISShovelGround.perform = shovelGround.perform
end
return improvisedToolsOverrides.shovelGround
and
local shovelGround = require("overrides/shovel-ground")
local Events = Events
local events = {}
function onGameBoot()
print('--------> IMPROVISED TOOLS: OnGameBoot')
shovelGround.init()
end
Events.OnGameBoot.Add(onGameBoot)
return events
line 28 in your shovelGround.init
is it because it's declared as a local?
yes, you will not be able to use it outside of the function scope
put it in your module and reference it like that
but i did ask this before and you did have it like that on your example... so local vars are resctricted to the context of functions after all right?
yes they are, sorry if I said otherwise, let me see
o I do have that in my example sorry lol
nah it's fine that actually makes more sence i was very susprised that local function variables were accessable from other functions
I am typing in discord off memory so it's a bit wack but most of it should be work
lol nah it;s all good the universe actualy makes sence again he he
If you want to see an actual example of what I do here is what I do:
local hooks = {}
hooks.o_get_text = nil
--- @param ... any
--- @return string
--- @overload fun(text: string): string
--- @overload fun(text: string, ...: any): string
function hooks.get_text(...)
-- code hereeeeeeeeee
end
function hooks.register()
logger:debug("Hooking getText...")
hooks.o_get_text = getText
getText = hooks.get_text
end
return hooks
so you can tell if I hooked something by checking if hooks.o_get_text is nil, and when we unhook just set those back to nil after setting the original function back to the original reference
like unhooking (if you ever need it which you shouldn't because unhooking technically happens with lua reload anyway) you would do this
function hooks.cleanup()
getText = hooks.o_get_text
hooks.o_get_text = nil
end
basically just reversing the process
actually wait nevermind that wouldn't work I don't think, that's tricky damn
since variables are references to the values, this would set getText to the original but then also set it to nil, just forget I said anyting lol
but you don't need to worry about cleanup for hooks unless you really specifically need to*
just a practicality, but this is an example showing that i am stupid:
local test = 123
myvar = test
print(myvar .. " " .. test) -- 123 123
test = 456
print(myvar .. " " .. test) -- 123 456
so it turns out you don't know as much as you should (in my case)
"Tables, functions, threads, and (full) userdata values are objects: variables do not actually contain these values, only references to them. Assignment, parameter passing, and function returns always manipulate references to such values; these operations do not imply any kind of copy." so I was right, I am just tripping over myself every step of the way im going to crawl in a hole now
ok i'm getting another error but this one seems trickier :x
local getPlayer = getPlayer
local ISShovelGround = ISShovelGround
local originalISShovelGroundPerform = nil
improvisedToolsOverrides = improvisedToolsOverrides or {}
improvisedToolsOverrides.shovelGround = {}
local shovelGround = improvisedToolsOverrides.shovelGround
function shovelGround.perform()
print('--------> IMPROVISED TOOLS: shovelGround.perform START')
originalISShovelGroundPerform()
-- local player = getPlayer()
-- local shovel = player:getPrimaryHandItem()
-- if shovel ~= nil then
-- print('--------> IMPROVISED TOOLS: Type ' .. shovel:getFullType())
-- print('--------> IMPROVISED TOOLS: Name ' .. shovel:getName())
-- end
print('--------> IMPROVISED TOOLS: shovelGround.perform END ')
end
function shovelGround.init()
print('--------> IMPROVISED TOOLS: shovelGround.init')
originalISShovelGroundPerform = ISShovelGround.perform
ISShovelGround.perform = shovelGround.perform
end
return improvisedToolsOverrides.shovelGround
what is line 12 in ur file
so is it possible the original function is losing it;s context and so the self keyward is nil?
huh
it's an empty line Oo
I think it's referencing the original call I have no idea
do lua functions work like JS functions where a function context is based on the caller unless bound?
I know why
yeah
but self isn't nil, it's your module
at least I think so, it's so mind-fucky
I'm not the best with js but function context is based on what the function belongs to
well it depends actual nomral functiosn will have the this point to the context of the caller
while bound or arrow functions have this point to the declaration context
for example, if the function is part of a module and you use : (like the original perform func does) then self belongs to the module of the perform like this:
function myModule:myFunction()
-- self is basically context of myModule here
end
-- above is the same as doing below
function myModule.myFunction(self)
-- self is context of myModule still
end
so I think that when we hook the function, our context's self overwrites the original self value in the original function
I am trying to think, maybe you could do
should i use the : operator instead then?
I think not
waaaaaaait
should this:
originalISShovelGroundPerform = ISShovelGround.perform
be
originalISShovelGroundPerform = ISShovelGround:perform
self in lua is basically a normal variable
no
I don't even think that's valid syntax
: is only used when you are defining a function or calling a function with self as the first arg
since that is a variable with a reference to the original perform function, you don't want to call it or use :
humm i did find another mod that does something similar and they do this:
parentRender(self)
myModule:myFunction()
myModule.myFunction(myModule --[[basically self]])
``` are the same
but this inside this:
function ISToolTipInv:render()
so function deplared with : parent refenrenced with . and then invoked with name(self)
ye, it's just syntax sugar
btw you can set self in lua since it's just a normal variable
kk
I was thinking maybe could set self to the original one but I think it's not as hard
I've just gotta find it
trying something as well need a minute
yep this worked like a charm, no errors!
local getPlayer = getPlayer
local ISShovelGround = ISShovelGround
local originalISShovelGroundPerform = nil
improvisedToolsOverrides = improvisedToolsOverrides or {}
improvisedToolsOverrides.shovelGround = {}
local shovelGround = improvisedToolsOverrides.shovelGround
function shovelGround:perform()
print('--------> IMPROVISED TOOLS: shovelGround.perform START')
originalISShovelGroundPerform(self)
-- local player = getPlayer()
-- local shovel = player:getPrimaryHandItem()
-- if shovel ~= nil then
-- print('--------> IMPROVISED TOOLS: Type ' .. shovel:getFullType())
-- print('--------> IMPROVISED TOOLS: Name ' .. shovel:getName())
-- end
print('--------> IMPROVISED TOOLS: shovelGround.perform END ')
end
function shovelGround.init()
print('--------> IMPROVISED TOOLS: shovelGround.init')
originalISShovelGroundPerform = ISShovelGround.perform
ISShovelGround.perform = shovelGround.perform
end
return improvisedToolsOverrides.shovelGround
for example, calling ModuleA.test(self) inside of ModuleB would mean self is ModuleB
shovelGround:perform() + originalISShovelGroundPerform(self)
lolol
it sounds like : declarations are unbound declarations that get their self assigned when invoked
yes, whne you use : in func declaring it basically just does local self = module at the start of the function behind the scenes
while . declarations seem to either have their self bound to the declartion scope or needs it to be passed in as argument
I want to test so many things I am confused as HECK I will cook
Success:
thanks @grizzled fulcrum you have been invaluable in helping me understand how to mod this!!!
think i'm going to dive into it for a bit now 😄
what's weird is self doesn't change after hooking
albion where are you lool
main: self = table: 0x1bf0eb0
shovel: self = table: 0x1bf0eb0
so basically you're passing your own table to the original function as self, but it still works???
I am actually baffled
seriously baffled
local main = {}
main.test = 36536536
main.o_perform = nil
function main.perform(self)
print("main: self = " .. tostring(self) .. " | test = " .. tostring(self.test))
main.o_perform(self)
end
function main.init()
main.o_perform = ISShovelGround.perform
ISShovelGround.perform = main.perform
end
main.init()
ISShovelGround.perform(ISShovelGround)
return main
ISShovelGround = {}
ISShovelGround.test = 123
function ISShovelGround:perform()
print("shovel: self = " .. tostring(self) .. " | test = " .. tostring(self.test))
end
main: self = table: 0x13b4e20 | test = 123
shovel: self = table: 0x13b4e20 | test = 123
both pointing to the ISShovelGround table as self
albion if you ever see this please help me my brain is cooking like a bbq
Shared is loaded first, usually utility is thrown there so it can be used in both server and client
is it possible to define a recipe's ingredient min or max condition to be eligible?
Yes definitely
With OnTest I believe or smthg like that you can link to a lua function, which allows you to return true or false based on conditions of items
was just about to ask what the onTest event was for lol
if ontest returns false the option on wont show up
or if it doesnt return anything
i mean false
is the onTest aplied to all possible ingredients then?
sorry
Pretty sure it gives you a list of items to check ?
only to recipe
looking at a vanilla example it seems to be invoked on a per item basis:
function Recipe.OnTest.IsNotWorn(item)
if instanceof(item, "Clothing") then
return not item:isWorn()
end
return true
end
function Recipe.OnTest.FullLiquor(item)
if not item:hasTag("Petrol") then return true end
return item:getUsedDelta() == 1
end
and it seems true means it's not valid?
from vanilla
so if doesnt have the tag and its empty
then option will showup
getUsedDelta is the amount consumed
so 1 means its empty
iirc
you could test it by making similar functions
i could be wrong
also is it possible for the Result to be a lua script as well? like this: keep [Recipe.GetItemTypes.Scissors]
not item:Isworn()
so if its worn then it will become false
which is what you want
cuz you dont want to wear if its already worn
not sure i follow
that part means dont despawn the item that has Scissors tag
if there are multiple scissor items you need one to do the recipe
and so whatever that item is
keep it
removing the keep will despawn that item
i understand that, my question was a bit different
in this case keep could either be given a / sperated list of item ids or in this case is given a lua script which returns the list
what i mean to know is can we do something similar with the 'Result'
you can do that using getAllItems()
if i understand you correctly
what are you trying to achieve
whats the list for?
i would like to have a recipe that generates different results depending on the ingredient used
so something like: Result: Recipe.GetItemTypeBasedOnIngredient
you can put everything on an array then use oncreate
the item you will spawn will be based on
array[ZombRand()]
local array ={"Base.item",}
local count = #array
local toSpawn=array[ZombRand(count)]
getPlayer():getInventory():AddItem(toSpawn)
note im typing this via mobile without reference so check the syntax
sure and i understand this goes on the onCreate event that's all fine but what about the Result keyword on the recipe? do i just supress it? or does it have to have a default item generated by the recipe?
yes
you add to the recipe script removeResult item or something
so that the default result wont spawn
and you then add whatever you want using oncreate
you cant dynamically change the scripts result
as far as i know
but i havent explore recipe manager
so there might be a way
did this answer the question?
result is important there should always be result i think
just block it using that removeresult or whatever the actual syntax is
ill try to find exact syntax
yep i think it does just wish we could use a lua file as the result
i think thats right
RemoveResultItem
RemoveResultItem:true,
yes
declaring a function with : implicitly adds a self parameter, but it doesn't set the value of it - the value is passed by the caller as normal
calling the function with : implicitly passes the object to the left as self, so all vanilla calls still pass an actual object to the overridden function, regardless of what table you define it in
so when you call a function that doesnt have a self within its function
you can do so normally outside the metatable?
it doesnt matter if its . or : right?
it matters, if you call a function with : e.g. table:function() this is equivalent to table.function(table)
there's no actual magical meaning to the self parameter, it behaves exactly like any other function parameter, it's just that : hides it
function t:foo() and function t.foo(self) compile to exactly the same bytecode
hi! I didn't expect you to respond -w-
I understand the syntax but I am just confused on where the self is, I will have to read about it more because it just confuses me more
From what I thought, when calling a function with self (see main.perform where I call the original function with the self value of the new perform function), I thought this would be equal to main table because we are calling it from main but it is somehow equal to ISShovelGround
you call it with ISShovelGround.perform(ISShovelGround), as you didn't use : the first argument (self) is ISShovelGround
I assume self to be the container that is calling the function (usually the module or file, idk) right?
in that case where I pass ISShovelGround as self to perform, I don't see how it is transferring
like the o_perform variable that holds the reference to the original function doesn't store the value of self, so how does it know that ISShovelGround is self in the new perform function and not main?
local t = {}
-- exactly equivalent:
function t:foo()
function t.foo(self)
function t.foo(bar)
-- also exactly equivalent:
t.foo(t)
t:foo()
since you call perform with only the argument ISShovelGround, ISShovelGround is self and that's what's passed along
wait I think it clicked lol
im so stupid wth
I am thinking like it's a different lang
I forgot that o_perform variable is literally the reference to the function and not the reference to a function that calls the original perform function
ahhh i see
once I realised you can just replace the ISShovelGround.perform(ISShovelGround) with main.perform(ISShovelGround) it made much more sense lol
I was thinking with the concept that it should be like this instead: main.perform(main)
this is why I try to avoid self stuff in every language 😂
except java, it's unavoidable I swear
that's one of the reasons i've never given it a chance LOL
it's useful for a total of like 2.5% of your entire codebase
it's like the one thing you use because it's convenient
I guess it's more confusing with Lua (for me) especially because it doesn't really follow OOP as other languages do, so self in Java you can atleast follow along more whereas Lua it's imo harder to follow
yeah, i consider it the most unintuitive part of lua
since all the feature really does is hide what's actually happening
it wants you to believe that lua supports oop when it really doesn't
oop is overrated anyway ::hides::
some people say that, but I don't understand anything but OOP
it's like ingrained into my brain after coding for so long
like if I don't encapsulate every single thing and abstract everything into their own little sections I die
but hey at least I can sleep easier than the sick individuals who put 17,000 lines of code into ONE FILE as their whole entire mod
(really happened)
i'm an oop defender but my counter argument to most criticisms of it is just 'well yeah, you shouldn't use oop for that'
I also do this... using functional programing
thats just following the single responsibility principal has nothing to do with the paradigm used
I guess I mix them if the google definition is anything to go by
like in rust I use functional programming a lot because it's just easier and more convenient (I think the rust std has a bias towards that or even solely based around that)
but things like java c++ c# it's hard to use functional programming because a lot if not all of the codebase already exists as OOP
me when I tell people I never use recursion
ok i'm missing something again.... so i got this recipe with the following:
OnTest:Recipe.OnTest.IsBrokenImprovisedTool,
OnCreate:Recipe.OnCreate.SalvageImprovisedTool,
OnGiveXP:Recipe.OnGiveXP.SalvageImprovisedToolXP
and under my lua/client/recipes i got a lua file with the following:
Recipe.OnTest = Recipe.OnTest or {};
Recipe.OnCreate = Recipe.OnCreate or {};
Recipe.OnGiveXP = Recipe.OnGiveXP or {};
function Recipe.OnTest.IsBrokenImprovisedTool(item)
print('--------> TEST Start');
if item == nil then return false end;
print('--------> ' .. item:getName() .. ', ' .. item.getCondition());
print('--------> TEST End');
return item.getCondition() < 1;
end
function Recipe.OnCreate.SalvageImprovisedTool(items, result, player)
print('--------> CREATE Start');
end
function Recipe.OnGiveXP.SalvageImprovisedToolXP(recipe, ingredients, result, player)
print('--------> EXP Start');
end
yet in game i'm getting the following error when selecting the recipe:
either define them in server or in a different table
the Recipe table is created by a server lua file, if you define it before that it'll just be deleted
ok still having trouble to understand what should go in client and what should go in server 😕
in this case it is basically completely arbitrary
the vanilla ones are in server because they are
oooooook so Recipe.x should go under server is that correct?
yeah
kk
you can also just put them in e.g. MyModRecipe and define it wherever you want
the oncreate just needs to be the name of the function in the global namespace
hummm it's still a good exercise to get a grip on how to mod PZ
but good to know it doesn't have to be in Recipe.x
i don't know exactly what the standards are in other languages, but in what i'm familiar with 'true' recursion has performance issues, proneness to crashes, and i can't help but notice that i never see it in anyone's codebases
when you *really* need it for things like e.g. iterating through a tree, iterating over a stack is the same thing with less of those issues
in c++ clang tidy just straight up tells you off for even using it
good to know, I always put them in the shared folder just in case the client needed to access them
wait, no wonder...
as far as i know it's only needed by the client
but usually when i say this someone says the server needs it too or anticheat will go off which i have never investigated
clang-tidy lol, I'm the type of person to enable all of the warnings and then spend hours fixing them 😭
wait I am confused, you say if it is defined before it will be deleted right? never trust the folder names
yeah, the server folder goes Recipe = {} so anything in there is getting dropped
you called it like a month ago, im still used to client meaning client and server meaning server
pycharm when a line is 121 characters long
yes
you know what boils my blood more than anything else on this planet
when im using the black formatter for py and it limits my code to like 88 chars, so when a print statement is 89 chars for verbosity it formats it to this attrocity:
print(
"SOMEREALLYLONGPRINTSTATEMENTOHMYWHYISTHERESOMUCHTEXTHOWAREYOUREADINGIT"
)
like just don't even put the brackets on 3 whole new lines
😭
Just to make sure, is there any situation where server code isn't loaded?
or rather lua files in the server folder aren't loaded
in singleplayer, server doesn't actually load until you start loading a game
that is, it's not loaded on the main menu
It feels really wrong to put my recipe stuff in the server folder when the client uses it lol
but I can't put it in shared where I expect it to be because it gets overwritten right?
if you just don't use the Recipe table you don't need to worry
is there a way to check if an item has a method defined?
something similar to:
if item:getCondition == nil then return true end;
also I'll probably put that stuff I was talking about before into zedscript extension, I think I'll just end up making my own due to how his extension works
i still haven't actually gotten around to trying it LOL so i don't have any specific attachment to it
if item.getCondition == nil
couldn't you just do exactly that?
ye lol
that throws an error saying it expects an argument
because you used : instead of .
but also, if you're checking for methods to determine the type like IsoObject or IsoWorldInventoryObject then use instanceof(obj, "IsoObject") instead!
( i only say this bc I've never seen someone check if methods exist before on items)
only use : when calling/declaring functions
trying to avoid a situation where soe of the items don't seem to have the getCondition method Oo
maybe the items are like ContextMenuItemStack and Item?
you definitely have some unexpected data in there, getCondition is declared in InventoryItem which is the base class for items
I do this to check for the Item itself (but this is only checking one)
--- @param player_idx integer
--- @param context ISContextMenu
--- @param items InventoryItem[] | ContextMenuItemStack[]
function context_menu.create_inventory_menu(player_idx, context, items)
--- @type SandboxVarsDummy
--- @diagnostic disable-next-line: assign-type-mismatch
local sandbox_vars = SandboxVars[mod_constants.MOD_ID]
--- @type InventoryItem
local water_item = nil
for i = 1, #items do
local item = items[i]
if instanceof(item, "InventoryItem") then
--- @cast item InventoryItem
if item:isWaterSource()
and item:isTaintedWater() == false
and item:getCurrentUses() >= sandbox_vars.BrushTeethRequiredWater then
water_item = item
break
end
else
--- @cast item ContextMenuItemStack
for j = 1, #item.items do
local real_item = item.items[j]
if real_item:isWaterSource()
and real_item:isTaintedWater() == false
and real_item:getCurrentUses() >= sandbox_vars.BrushTeethRequiredWater then
water_item = real_item
break
end
end
end
end
if water_item == nil then return end
context_menu.do_context_menu(water_item, player_idx, context)
end
just copy pasted it from my vscode but basically you check if the item you're looping through is an InventoryItem
if not, you can assume it's a ContextMenuItemStack which is just a table of some stuff (I think)
it's the item stacks in your inventory, even a single item is a stack unless you specifically open the stack and select the item
yes that is correct I am just bad at game
the way it's implemented is kind of gross
I don't play the game much so I end up asking other people how things work in the game myself lol
stack.items[1] == stack.items[2] because that's the way the game displays the stack when you open it
that is not epic 😦
tostring()
for what it's worth this is absolutely not intuitive even if you play the game, i have never seen anyone just get how this works without it being explained
I've probably said this before but I switch between camelcase and the underscore thingy like a schizo switches their meds so I end up refactoring my whole codebase every now and then for absolutely no benefitial gains other than my monke brain going "i like that more now"
there is so much to be learned and so little information to learn from that I am in a cycle of melt in the sun for multiple days before asking help
i refactored an entire codebase into snake case when i realised that's what python users like 😔
but having access to the java has been probably the most helpful thing I'd say fullstop
assuming you can read java 😮
honestly at least the java is typed
yes
deciphering these weird ass table structures takes years off my life
I love my lua ArrayList type ✨
damn i forgot to document the weird objects OnFillWorldObjectContextMenu actually passes and i already forgot
though sumneko's ls is not bad, it feels clunky in that you can't really do in depth typings before it starts to crumble
but it can only do so much considering it's lua
i'm trying to remember if you can type 'tuple' tables or if i dreamed that
a table that is a tuple
0____0
maybe you can just do a class definition and then ---@field [1] type actually lol
how does that work in lua, not even in typings
it's not really a language feature and i may be making assumptions based on how tuples work in python
(and bear in mind that i have barely used tuples in python LOL)
because from what I know
a table is a table
a function that returns 2 values just returns 2 values, no tuple
I don't even think tuples exist
yeah i just mean a table that behaves like one
can you not just annotate it like a table? or you need specific typings for specific indexes
my thinking is if they aren't all the same type
the best example i can give is distributions tables
they go string, number, string, number
ohh
when it's a class with named fields you just declare a class but for numeric indexes i don't remember if that was even real
come to think of it whether i dreamed it or not i remember it not really working very well LOL
so you can do
--- @type { [1]: string, [2]: number }
or some scuffed thing like that, but thye problem is that it only covers those two indexes specifically and not all of them
I am trying to run through all the things I know
i don't know how many times i've scrolled past this table without paying attention to it
oh i found where i used it and it's not exactly what i remembered
i think the distributions typing thing did work too (if i didn't dream it) but was essentially useless because any indexing just returned string|number
tuples are the devil
i'd never seen the python style dictionary annotation, i do like that a lot, really bugs me how much i write table< in my annotations
the table literal style might also cut down on how many weird/fake class declarations i need to do for events typings
though i guess you can't comment fields that way so maybe not
Awaiting the utopian society when all the weird quirks like https://github.com/LuaLS/lua-language-server/pull/2864 are worked out of LuaLS
(Idt this change has made it to the extension yet, or at least it hadn't the last time I checked)
widening casts and actual generics support would be lifesavers
Sometimes I visit the generics super issue on the repo just to dream
is there any way of inspecting all methods of a lua object? just tried something like this but aparently my item value is not a table:
-- for key,value in pairs(item) do
-- print("found member " .. key);
-- end
it's a userdata
in kahlua you should(?) still be able to iterate over it
however pairs will not find its methods
use getmetatable(item).__index and iterate over that
ok I'm starting to slowly lose my will to live after running in circles trying to pull this one off...
I've got this recipe to salvage broken improvised tools:
module Base {
recipe Salvage Improvised Tool {
ImprovisedShovel/ImprovisedSledgehammer/ImprovisedTongs/ImprovisedScissors=1,
keep [Recipe.GetItemTypes.Scissors]/[Recipe.GetItemTypes.SharpKnife],
Result:RippedSheets,
RemoveResultItem:true,
Time:300,
Sound:PutItemInBag,
Category:Improvised Tools,
OnTest:ImprovisedTool.OnTest.IsBroken,
OnCreate:ImprovisedTool.OnCreate.Salvage,
OnGiveXP:ImprovisedTool.OnGiveXP.SalvageXP
}
}
Tried to use the onTest event to only enable the recepie for improved tools that are effectively broken with:
function ImprovisedTool.OnTest.IsBroken(item)
if item == nil then return false end;
if item:getDisplayCategory() ~= 'Improvised' then return true end;
if not item:isBroken() then return false end;
print('--------> ' .. item:getName() .. ' is valid ');
-- for key,value in pairs(getmetatable(item).__index) do
-- print("found member " .. key);
-- end
return true;
end
now here's the fun bit.... when i right click a broken improved tool on my inventory i do get that print but no crafting recipe is listed on the context menu
and when i inspect the recipe on the crafting window nothing gets printed which seems to point that in that screen no broken items are ever evaluated and i get this:
if i move that print a few checks up it will trigger for all the other items but not for the broken ones
add AllowDestroyedItem:true to your recipe script
ha!!!!! thanks!
yep that did it thank you so very much @bronze yoke
so now looking at the list of methods we have available for this items there isn't any reference to what ingredients were used to craft them right?
no, they don't store that at all
if you need to you can add relevant data to the item's mod data
hummm i did create this items on my mod so i guess i could do that
i'm assuming this needs to be on the onCreate event of the items i want to later know how they were made right?
yeah
@bronze yoke if you're still around i managed to print the items used during the recipe with:
function ImprovisedTool.OnCreate.Tool(items, result, player)
for i = 0, items:size() - 1 do
local item = items:get(i);
local type = item:getFullType();
print('--------> ' .. type);
end
end
mind teaching me how i can now inject those types into the result?
the result is a:
module Base {
item ImprovisedShovel {
( ... copy of the vanila shovel )
}
}
local modData = result:getModData()
modData.MyMod = {}
modData.MyMod.itemsUsed = {}
for i = 0, items:size() -1 do
table.insert(modData.MyMod.itemsUsed, items:get(i):getFullType())
end
ha! so is this modData like some sort of meta data?
yeah, it's literally just a table attached to an object
kk thank you very much!
it's persistent so it's best to only put primitive types in it
things like objects won't persist
sounds reasonable yeh
i suspected that much ence why i was planning on storying the types of the ingredients rather the ingredients them selves
alright so i thought of a good "expansion" to the idea of the spiffo redux mod
does anyone know how i can replace the zombie models with skeletons, but still allow them to wear clothes?
i'll just copy paste from the other server and add extra context
it's mostly for when people go to remove the suit/mask from the spiffo zed if/when they do it so they don't just see a person underneath
it's kind of a side idea that i'm fine if it doesn't work out, but it'd be really really nice if i can manage it because at this rate this is half a gimmick mod and half something i'm serious about lmfao
i love serious gimmick mods
huh... so lua has no "continue" to skip an iteration on a for loop?
luau does
is that relevant for PZ modding?
nope
-.-
Can anyone tell me how can I generate random numbers? I'm trying to make one of my mods generate a random time of the day.
I have tried everything and I haven't been able to make lua generate random numbers.
ZombRand
Or
Better in performance (if you cache the first part)
local rand = newrandom()
local value = rand:random(n,m)
Awesome! Thank you so much! ❤️
ok what am i doing wrong here? is it something obvious that my dyslexia is preventing me from seeing?
function ImprovisedTool.OnCreate.Salvage(items, result, player)
for i = 0, items:size() - 1 do
local item = items:get(i);
local metaData = item:getModData();
local improvisedTool = metaData.improvisedTool or {};
local materials = improvisedTool.materials;
if materials ~= nill then
print('--==--==> Type: ' .. type(materials));
print('--==--==> Size: ' .. materials:size());
-- for j = 0, materials:size() - 1 do
-- local material = materials:get(i);
-- local roll = ZombRand(1, 21);
-- print('--==--==> Material: ' .. material .. ' roled a ' .. roll);
-- end
end
end
end
it's throwing an error on the second print, the one with the size
also the type print works as i can see the ouput on the console window as table
are you sure this is the right error? it doesn't really look like anything in your code
nop the error that was being shown then didint even match the actual error but any case it was throwing an error with that :size on a table
i had to iterate through it like this instead:
for _, material in pairs(materials) do
local roll = ZombRand(1, 21);
print('--==--==> Material: ' .. material .. ' roled a ' .. roll);
end
but i dont understand why when the items was iterated with a regular incremental for
from what I understand, Umbrella is just the demcompiled code cut up to just show the java functions and parameters, you install it by adding it as a library with whatever IDE you use, but everytime I download it, either cloning through git or downloading the zip and extracting it, it's empty, Im assuming im missing something so if anyones got an idea where im stuck that would be great.
materials is a lua table, items is a java arraylist
hummm ok and the way to iterate a table is though pairs?
or a numeric for using this syntax:```lua
for i = 1, #t do
local v = t[i]
-- do something with v
end
if your table uses only consecutive numeric keys (so it acts like an array) this is much faster
what is the # for?
it gets the length of the table
ha ok ok thanks
if you're cloning you need to clone with --recurse-submodules
you can also download a zip from releases or use the addon manager if you're using vscode
function ImprovisedTool.OnCreate.Salvage(items, result, player)
print('--==--==> Type: ' .. type(items));
for i = 0, items:size() - 1 do
local item = items:get(i);
local metaData = item:getModData();
local improvisedTool = metaData.improvisedTool or {};
local materials = improvisedTool.materials;
if materials ~= nill then
for j = 1, #materials do
local material = materials[j];
local roll = ZombRand(1, 21);
print('--==--==> Material: ' .. material .. ' roled a ' .. roll);
if roll > 15 then
print('--==--==> Material: ' .. material .. ' needs to be spawn in the character inventory ');
end
end
end
end
end
this worked like a charm 😄
tomorrow's goal: spawn the actual item
oh, i see you're checking if materials ~= nill then, should be nil
probably still works because there is nothing defined as nill anyway
Months in PZ go from 0 to 11 or 1 to 12? 🤔
0 to 11
Thank you very much! ❤️
thanks @bronze yoke you rock!
no --recurse-submodules flag but I did end up downloading from release, did that previously but downloaded the linux sourcecode which was empty, just downloaded the zip this time and it worked, any idea why the source files would be empty?
got it working and all thanks for that just not sure why they'd post windows and linux specific files that're empty
we don't post those, github generates them automatically
umbrella is a collection of three separate typings projects which are added as submodules, none of the stubs actually reside within the umbrella repository itself
fair, thanks
where are the textures for the grass dirt roads ect located
wanna convert them to mars textures
think i mightve found them
Hello!
Any lua wizards want to waste their time teaching me how to create a mod I have in mind?
Modding docs I found are too confusing for my non coder brain to figure out. 
I have experience creating lua mods for other game, but this time I feel as lost as I felt starting out the first time...
i want to make a mod where i could make a moodle based on being indoors or not to have a oxygen timer when outside and inside buildings without a door or windows or both, and when inside a building with doors and windows intact theres no oxygen timers (at end of timer u suffocate to simulate mars and a habitat),,, where should i look to learn things about how they doo the moodles is there some script files i should look at? where would be a good starting point? if that didnt make sense please do tell me lol.
maybe i have to look at the claustraphobia trait
could use generator logic as a base
ohh cuz it has to connect to a house?
Not really, but could also turn generators into "oxygen generators"
ohh thats a good idea yea
their logic already deals with "air tight" indoors, so you could just invert it
welp that's where my knowledge ends
I'd research the docs
C:\Users\mtwri\Pictures\1ART REF\Zomboid\3D modeling stuff\New Models\Props\Props All\FBXs\Generator.FBXd
the descriptions for folder names here at spiffo are fantastic
Morning folks!
I understand your pain but i think it works best when you ask specific questions around here
I need help in general :D.
But I specifically want to learn how zombie targeting works. I don't know how to force a zombie to attack another zombie.
my strategy has been to scour other mods and sift through the vanilla media folder until i get stuck then i pester the good folk here about it
Want to make a mod where some zombies get confused and attack other zombies.
this will help you inspect some objects btw:
-- for key,value in pairs(getmetatable(item).__index) do
-- print("found member " .. key);
-- end
also runing the game in debug mod is a massive help as well
I also barely started with modding PZ but with 20+ years of experience coding i'll try my best to help you when i can 🙂
can't say i have even looked into anything related to zombies though
Yeah and I have 0 real expirience so Ill take any help at all.
It seems like there are literally 0 mods that do this sort of thing, so it's either impossible or noone likes the idea much
are you using any coding IDE? like VSCode?
sublime text
its a text editor
if that's what you mean. I dont use any engines or whatever is the right name
ok not my first choice so not sure if this is possible in sublime but if you "create a project" with the vanilla media folder you can then search the source code for stuff which can speed you your investigation
so using www.projectzomboid.com isnt a good idea then?
I usually google docs of the game Im trying to mod
my project in VSCode includes the user's zomboid/workshop where i develop my mods, the steam workshop folder 108600 (PZ mods folder) and the steam/steampps/common/projectzomboid/media (the vanilla code)
Ngl I had it open once, but one day the tab went away and I didn't try to open it up ever since...
Trying to find this tab atm
Should look something like this?
it's from other game i mod
ok just to recap:
- Vanilla: \steamapps\common\ProjectZomboid\media
- Other Mods: \steamapps\workshop\content\108600
- Your Mods: Users[username]\Zomboid\Workshop
i don't use sublime so not sure that's the right panel but that file structure does not look familiar to me
I think I can only open/link 1 folder to my script
just get vscode mate it's free
on VSCode you start by click this blue button and pick one of those 3 folders:
afterwards just right cleck an empty area on the same panel and pick the add folder to workspace and add the rest:
I have to create my mod's folder first to link it right? Or vscode will just make one up
nah i do that my self, though all it takes is to create a folder inside that workshop folder i mentioned in your users folder
once you've set this up you can simply right click any folder and pick this find in folder option to search it's source code:
which switches to this panel:
hope that helps 😉
Thanks, hopefully Ill find examples of zombie's agro behavior
should get the community mods (debug tools) https://github.com/Project-Zomboid-Community-Modding/pz-community-modding
specifically the debugtools submod because the others aren't needed
it lets you right click and inspect any object and it's java methods (among other things), its great
btw finally managed to reach my first goal, ability to create improvised shovels, this are extremly flimsy (condition 2) and will break 75% of the time when collecting dirt. Once broken it can be salvaged with a small chance to recover each of the materials used to create it originaly 😄
next step is to apply this to other tools and activities and then get skills to play a roll on that condition and breaking / material recovering chances
lastly get some exp gaining when creating / salvaging those tools
i may also dabble with icons eventualy to make them distinguishable from vanilla tools other then through their name
Sounds pretty cool
yeah debug tool doesn't really help me to understand what exactly is the agro logic
I guess it's hard coded for the zombies to attack/react to player only?
I think so, they have a target IsoPlayer
so I don't think the attack target can be anything but
Unfortunate... I really wanted to make it happen
The Idea was like this: Zombie gets "hungry", jumps other zombie and starts to eat it. Hopefully in build 42 we'll get that option in the expanded mod support.
It can be any IsoMovingObject or IsoGameCharacter
(I don't remember which, I believe it's IsoMovingObject but it can only target players naturally, however you can set their target to a zombie)
IsMovingObject
You can't make them naturally target other zombies
interesting
I didn’t see a single usage of it in targeting. Gotta do some experiments. But I don’t know how to even start doing that
wdym ?
IsoMovingObject ?
That's bcs zombies are never set to target IsoMovingObject, but IsoPlayer, which is an IsoMovingObject
Yeah it’s described in the docs, but I haves seen a single real application of it as an attack target
But they can in fact target IsoZombie
Welp didn’t see either
I’m not a coder. Usually I look up working examples.
So I’ll try and experiment with it my self. Never used bandits mods. Gonna download it later. Thanks for the tip
They probably use the targeting too tho I don't have confirmation of that
As they might not use the default targeting system at all
As they handle every actions of the zombies to act human like
I’ve looked up superb survivors, those used some janky scripts, so I expect the same from bandits
Superb Survivor is absolutely trash, also it doesn't use IsoZombies
It uses IsoPlayer
It creates IsoPlayers
Which is the reason it's incompatible with MP
Should be interchangeable tho, no?
Bandits is not janky
Bandits is actually quite good, it's not perfect of course like nothing but it is quite really good
Not lmao
Aight sounds like great news to me.
IsoPlayer and IsoZombie do not work the same at all
I meant in targeting sense not as a controller or whatever the pawns are called
No
I'd be surprised
An IsoPlayer doesn't have a target
Javadoc Project Zomboid Modding API declaration: package: zombie.characters, class: IsoZombie
Hmm
setTarget is an IsoZombie method
Not an IsoGameCharacter or below
Thus it can't be applied to players
Aight. I never looked up IsoPlayer so I guess that’s the reason for jank
Most definitely
I'm even surprised IsoPlayers can be created and made as NPC as the class was made for players, not any form of AI ?
Not that uncommon tbh. Same can be done in ground branch. It’s just an empty controller/pawn that can be forced to do anything.
Guess it was better for better survivors, because of the player like actions foraging/farming/doc etc
Yeah well PZ is quite a special game in how it handles things I feel like, so while yes an empty character can be created in other games, it usually is given player controls in some way. The way I saw IsoPlayer is what gave the controls, but you can't just create an empty IsoMovingObject or IsoGameCharacter, that's not how it works, you have to use a sub class
Oh well. I’m to dumb to understand the nitty gritty
does this allow us to reload lua files without leaving the current save?
I wouldn't recommend that even if it did. You'd have more issues doing hot reloading than just going to menu and reloading lua there.
oh and you can do it even without the debug tools, in the F1 menu
actualy i just tested and worked pretty well i do imagine it can be problematic for large structural changes for tiny tweeks seems to work great and it does cut down on delay between changes dramatically
yeah, for example if you have a file that registers events it will call add again when reloading
but you can do some stuff around that, I just dont like how it makes things
it's good to be aware of that but like i said it's awesome when just tinkering with minor tweeks
is there a lua equivalent to js string.contains(string) ?
google is your friend for the questions about Lua 😄
but I will say, string.find(str, pattern, start?) will help
or alternate syntax which I think is cleaner, str:find(pattern, start?) where start is an optional parameter specifying the start to find from
btw is there anyway to clear the console without exiting to desktop?
it returns the start and end index of the found substring
not really, if you use a log viewer you can clear it like that but it wont clear in the in-game error viewer
alternatively, there is string.match(str, pattern, start) which returns the groups found (kinda like regex with () and %1)
and remember, Lua doesn't use regex, it uses it's own regex-like pattern matching syntax
kk
I'd like to start making mods as a hobby. Can anyone help? I need a roadmap for Lua.
well there's actually a fourth param which basically acts like match, like this:
local start_idx, end_idx = ("Hello, world!"):find("Hello", 1, false)
where 1 is just the start index (which is an optional arg that is default to 1 if not specified)
Yes
and false is another optional arg whether to use pattern matching, so if you have it to default (false) it will use pattern matching and if not it will only find literals
Please you got to stop telling people that ... There's so many codes that require you to reload a bunch to test things and this is way too useful to tell people to not approach it
I can understand you guys develop mods that don't require it but there are way too many instances of it being extremely useful, especially for the little work it requires you in organizing your files
The thing you need to care about mostly are events, as your functions will be added multiple times
I am just recommending things from my personal experience. I've had literal whole mods break in testing while hot reloading lua, and if you're the type to put everything in the same file it gets worse than if you split things up
It really depends on how you structure your mod, and you have to go out of your way to prevent these unexpected things from happening when hot-reloading, so I just outright just not recommend it. I am not saying they can't use it, just recommending that they stay away from it.
Organizing your files should be a priority for your personal experience, separating events in a separate files is quite a good way of organizing bcs you can easily see where your functions link
Also, you can easily just avoid it by using smaller save maps, like the time it takes to reload lua I can just reload the lua and the save and be where I was before without any issues.
I organize my files this way
-- myMod_module.lua
local myModule = {
-- put data which is set here
}
return myModule
-- myMod_main.lua
-- main functions here
-- myMod_events.lua
-- add the functions in the events here
This is a bit of a stretch, but even so, most people don't have 200 concurrent issues that they need to solve straight away. It's much easier to manage one or two.
yes I use modules myself
reloading lua can still break stuff, it just depends on how you set up stuff
This simple organization is enough to reload in-game without causing any problems
Of course, but I never had to sacrifice anything for in-game reload
In fact I gained quite a fuck ton, losing some bad habits and properly organizing files
and most noob modders (no offense) don't even know that hot-reloading can break stuff, so when you clear them to just use it and then they come back wondering why their scripts break or why their code is just completely off, it's no wonder
sorry to interrupt the ongoing best practices discussion but can you guys help me understand if i'm doing something wrong here... now that i'm using this community developer tools, when inspecting my new items i just noticed they have no javafields whatsoever as opposed to the vanilla objects that do have a bunch of java proprieties on them...
am i messing this up somehow?
When I started modding, I literally quadrupled my time spent on modding and not reloading my game simply by discovering the community debug tools and reloading in-game
The debug menu is dogshit too so gl finding out about it with this
yeah thats kinda weird actually
Today I constantly swap between my code, write some bits, long or short, test things directly in-game, by just reloading in-game
Smthg breaks ? I don't need to quit, reload lua, join back
I can just look at the code, fix it, instantly reload and done
I literally gained fucking hours of my life
I guess it depends on experiences then, because I'm the definition of polar opposite. I was worked up for literal WEEKS wondering why my mods were ending up completely broken and weird script issues, functions being hooked multiple times, everything. Then albion asked if I was using hot-reloading and then I stopped using that, my problems didn't occur again
I'm absolutely onboard with you'r approach @bright fog as this is what i experience as well
And I find it a terrible habit to keep telling people that they shouldn't do it. Just at least please be objective that it can be extremely useful but they have to be aware of how it works
I think the secret is to be aware of the shortcomings of reloading scripts without a full reset
And YES it can't be applied to every code types, there's things that do require quit and reload lua
Like patching for example
70% - 80% of the time it fine to just reload
yea, even if you reload fast, it adds up SOOOOO much sometimes
Especially when you test bits
in any case can i get some help on why on earth my new items are losing their java props?
java props ?
No idea ngl
You sure that shows your item ?
You right clicked your item and inspected it ?
yep
this is my recipe:
recipe Improvise Scissors {
Twigs=1,
Result:ImprovisedNeedle,
Time:300,
Sound:PutItemInBag,
Category:Improvised Tools,
OnCreate:ImprovisedTool.OnCreate.Tool
}
}```
and the item:
module Base {
item ImprovisedNeedle {
ConditionMax=2,
DisplayCategory=Improvised,
DisplayName=Improvised Needle,
Icon=Needle,
Medical=TRUE,
MetalValue=2,
SurvivalGear=TRUE,
Tags=SewingNeedle,
Type=Normal,
Weight=0.1,
WorldStaticModel=Needle
}
}
read the Programming In Lua guide (google it) and also read the pins
and the onCreate:
function ImprovisedTool.OnCreate.Tool(items, result, character)
-- local metaData = result:getModData();
-- metaData.improvisedTool = {};
-- metaData.improvisedTool.materials = {};
local metalValue = 0;
for i = 0, items:size() - 1 do
local item = items:get(i);
local type = item:getFullType();
if string.find(type, "Metal") then metalValue = metalValue + 10 end
-- table.insert(metaData.improvisedTool.materials, type);
end
-- result:setMetalValue(metalValue);
end
missing comma at the end
you need comma on all param lines
really? 😮
yes
ok let me try that then
that changed nothing and as i suspected this is the absurd trend of expecting interperters to be fault tolerent regarding dangling comas -.-
ok this must be some issue related with the community dev tools because when salvaging the item print('--==--==> Metal: ' .. item:getMetalValue()); returns the expected value. so even though it's not showing on that inspector window the field is def set
this happens because of some limitations with kahlua's reflection api and how items are implemented
it's unavoidable
kahlua's reflection api only allow us to inspect fields declared by the actual class of the object, not its parent classes
99% of item fields are declared by a common parent class so none of those are ever visible with this tool
most items are ComboItems which declares literally nothing: https://projectzomboid.com/modding/zombie/inventory/types/ComboItem.html
this is why you don't see anything at all
ok ok so nothing for me to worry about then
I actually don't use the inspection tool that much personally