Jump to content

RoboMat's Modding Utilities


RoboMat

Recommended Posts

RoboMat's Modding Utilities

- Utility functions by modders for modders -



For Players

If you want to play a mod, which requires the modding utilities you simply have to download them from the link below and install them like any other mod you use. If you need help just leave a reply in this thread.

Download from pz-mods.net

8o6exC7.png








For Modders

The idea behind the modding utils was to have a common hub for modders to use. Modders can suggest and add new functions to the utils. Why? Primarily to get rid of redundant code (dry code) and to offer a place for new modders to go and learn from. Anyway, you probably want to know how to use the mod utils.

First of all you have "open" the utils to your mod. You can do that by adding this line to your mod.info file
require=RMUtility
Now you can access everything that is contained in the modding utils. There are four main libs that contain a bunch of useful functions such as concatenating two tables or trimming a string.

The mod utils hook into the standard libs of lua namely table, math and string. Using them is quite intuitive now:
-- Localize string librarylocal string = string;local testString = "        this Is a     test string  ";testString = string.limitWhitespace(testString);print(testString);-- > this Is a test string-- You can use this too:testString = testString:removeWhitespace();print(testString);-- > thisIsateststring
I added a fourth library called 'zomboid' which contains miscellaneous functionality for Project Zomboid. It can be used the same way as the above libs.
-- Localize zomboid lib.local zomboid = zomboid;zomboid:getExactTimeOfDay()-- > returns current hour, minutes and seconds ingame.
I added extensive documentation to all of the libs and I hope it is clear enough for others to understand. If not, just leave a reply.

Last but not least there are several utility 'classes' such as a file reader and a lua benchmarker in the utils atm. If you are familiar with lua you shouldn't have problems with accessing and using them.

A (very bad) example for the benchmarker would be something like this:
	local benchmarker = LuaBenchmark:new('LoopCounter');	benchmarker:start();	print("Starting " .. benchmarker:getName() .. "!");	local no = 0;	for i=0, 10000 do		no = no + 1;	end	benchmarker:stop();	print("");	print("... finished in " .. benchmarker:getResult() / 1000 .. " seconds!\n");        -- > Starting LoopCounter!        -- >... finished in 0.016 seconds!





MODS_02.png


Changelog


Version 2.0.0
- Completely rewritten code
=> Divided the former utils in seperate sub libraries
-> LibMath
-> LibString
-> LibTable
-> LibZomboid
=> Usage-Instructions can be found here: http://theindiestone.com/forums/index.php/topic/1395-
- New documentation of each function.
- Added IO classes
=> FileIO
-> Can read any file
-> Basic tag reading functionality
=> IniIO (WIP)
-> Can read .ini files
-> Can write .ini files
- Rewritten LuaBenchmarker
- RMUtility.getVersion() can be used to see if the user has installed the correct version of the Utils.

Version 1.9.0
- Added Lua Benchmarker
=> measures time between start() and stop()
=> untested memory management stuff
- Added Utility.split()
=> Splits a file at a certain delimiter e.g. "/"
- Updated readFile(...)
=> now can scan for unlimited amount of tags (e.g. <title>)
it will store the content of the line in the
table of the file, where you can use the tag
to access it later on. (e.g. file.title)

Version 1.8.0
- Added readFile(...)
- Added getStringLength(...)
- Added getStringHeight(...)
- Added wrapString(...) - WIP
- Readded info on startup

Version 1.7.0
- Updated to work with the latest modloader
- Added round(...)
- Added isInInventory(...)
- Added isInHand(...)
- Added trim(...)
- Added startsWith(...)
- Added weaponLowerCondition(...)
- Changed getRndValue(...) - returns index
- Changed lower(...) - added cap parameter
- Changed increase(...) - default values
- Removed lowerCondition(...)
- Fixed filter(...)
- Lots of refactoring & new documentation
- Updated for new modloader functions

Version 1.6.0
- Added equipItems(...)
- Added walkToObject(...)
- Added getExactTimeOfDay(...)
- Added tCalculateAverage(...)
- Tweaked attractZedsToPlayer(...)

Version 1.5.0
- Added convertArrayList(...)
- Added info to make sure users have installed the
most recent version.

Version 1.4.0
- Added replaceNestedIfs(...)

Version 1.3.2
- Fixed bug with centered okModal(...)

Version 1.3.1
- Changed okModal(...) - can be centered now

Version 1.3.0
- Added okModal(...)
- Added lowerCondition(...)

Version 1.2.0
- Added attractZedsToPlayer(...)
- Added filter(...)
- Changed increase(...) - added cap parameter

Version 1.0.0
- Added
=> isString()
=> contains()
=> rnd()
=> getRndValue()
=> tConcat()
=> tPrint()
=> tContains()
=> lower()
=> increase()

Link to comment
Share on other sites

:D so you finally decided to release your utility scripts :D

:/ is there a easy way to translate strings to tables and from tables to strings?

Yeah I thought "now or never" hehe :D

Not sure if it is what you are looking for, but I guess you could use an iterator factory to feed different strings into a table (and let it return that table when it receives a nil reference?), but an "easy" way... no idea :(

http://www.lua.org/pil/7.1.html

Link to comment
Share on other sites

nha was more searching for a serialize kind of transformer

that way I can overcome the only strings or numbers into the moddata tables

(I'm currently making some util scripts for my ongoing project and the project requires a lot of table-structures to be saved, and I don't want to save them in separated key value pairs into the moddata table.)

Link to comment
Share on other sites

nha was more searching for a serialize kind of transformer

that way I can overcome the only strings or numbers into the moddata tables

(I'm currently making some util scripts for my ongoing project and the project requires a lot of table-structures to be saved, and I don't want to save them in separated key value pairs into the moddata table.)

I've got good news for you: Since 2.9.9.11 the getModData() now also works for booleans and nested tables. I'm writing a short tutorial about it at this moment :)

Link to comment
Share on other sites

 

nha was more searching for a serialize kind of transformer

that way I can overcome the only strings or numbers into the moddata tables

(I'm currently making some util scripts for my ongoing project and the project requires a lot of table-structures to be saved, and I don't want to save them in separated key value pairs into the moddata table.)

I've got good news for you: Since 2.9.9.11 the getModData() now also works for booleans and nested tables. I'm writing a short tutorial about it at this moment :)

 

 

oooh does this work? :-| we had save / load bugs traced to it so I thought I temporarily removed it :D

 

if your writing tutorial is preemptive, be warned it doesn't work at the moment (but will readd it) if you've had it working, er I guess I put it back in. :D

Link to comment
Share on other sites

oooh does this work? :-| we had save / load bugs traced to it so I thought I temporarily removed it :D

 

if your writing tutorial is preemptive, be warned it doesn't work at the moment (but will readd it) if you've had it working, er I guess I put it back in. :D

NOOOOO :cry: Well I use booleans in my lockpicking mod (IIRC) and there are no complaints so far, but it's a minor thing I saved, so maybe it actually doesn't work and I just didn't notice. Will test it tomorrow ;)

Link to comment
Share on other sites

o damn and I was creating a hole set of functions to transform tables to strings ad visaversa so I could just save my tables

:/ why did I do that if it (is/was/will) implemented into the moddata itself (again)

yet knowing that will speedup my mod a bit again :D

Link to comment
Share on other sites

  • 1 month later...

Updated. Say hello to my little friends:

 

getExactTimeOfDay

 

----- Calculates the current time of day. Returns-- current hour, current minute and total seconds.-- by RoboMatfunction Utility.getExactTimeOfDay()    local seconds = math.floor(getGameTime():getTimeOfDay() * 3600);    local hours = math.floor(seconds / 3600);    local minutes = math.floor(seconds / 60) - (hours * 60);    return hours, minutes, seconds;end

 

equipItems

 

----- This function tries to equip the passed items as-- primary or secondary items. It will return the items-- that had been originally equipped.-- Alternatively you can pass on Strings (e.g.: "Base.Screwdriver") and-- the function will try to find that item in the player's inventory and equip it.---- @param _player - The player who equips the items.-- @param _primItemToEquip - The item to equip in the primary slot.-- @param _scndItemToEquip - The item to equip in the secondary slot.-- by RoboMatfunction Utility.equipItems(_player, _primItemToEquip, _scndItemToEquip)    local player = _player;    local primItem = _primItemToEquip;    local scndItem = _scndItemToEquip;    -- If we didn't receive an actual item we use the string    -- to find an appropriate item in the inventory.    if type(primItem) == "string" then        primItem = player:getInventory():FindAndReturn(primItem);    end    if type(scndItem) == "string" then        scndItem = player:getInventory():FindAndReturn(scndItem);    end    -- Store the current equipped items.    local storePrim = player:getPrimaryHandItem();    local storeScnd = player:getSecondaryHandItem();    -- Equip the new items if necessary.    if primItem then        if not storePrim or storePrim ~= primItem then            ISTimedActionQueue.add(ISEquipWeaponAction:new(player, primItem, 25, true));        end    end    if scndItem then        if not storeScnd or storeScnd ~= scndItem then            ISTimedActionQueue.add(ISEquipWeaponAction:new(player, scndItem, 25, false));        end    end    -- Return the stored items in case we want to re-equip them later on.    return storePrim, storeScnd;end

 

walkToObject

 

----- Based on the walkTo function from RJ's luautils.-- @param _player - Player who should move his buttocks.-- @param _object - The object / tile to walk to.-- @param _cancelTA - Determines wether timed actions should be canceled-- before the player starts to walk.-- by RoboMatfunction Utility.walkToObject(_player, _object, _cancelTA)    local player = _player;    local object = _object;    local tile = _object:getSquare();    -- Abort all current Timed Actions.    if _cancelTA then        ISTimedActionQueue.clear(player);    end    -- Pathfinding and starting the actual walking.    if not AdjacentFreeTileFinder.isTileOrAdjacent(player:getCurrentSquare(), tile) then        local adjacent = AdjacentFreeTileFinder.FindWindowOrDoor(tile, object);        if adjacent then            ISTimedActionQueue.add(ISWalkToTimedAction:new(player, adjacent));            return true;        else            return false;        end    else        return true;    endend
 

tCalculateAverage

 

----- Calculates the average of numerical values stored in a table.-- @param _table - The table containing the values.-- by RoboMatfunction Utility.tCalculateAverage(_table)    local total = 0;    local average = 0;    for items, value in ipairs(_table) do        total = total + value;        average = total / items;    end    return average;end
Link to comment
Share on other sites

Can you add a language switcher in this? So the mod users can set one config file tag for their language, then have the individual mods use their own language tags (mod language default if no language tags) for individual languages.

This would be a totally different topic than my utilities here :D I know 7roses has been working on something like it, so you might want to check out his utility topic ;)

Link to comment
Share on other sites

Wait a second, I don't exist in this part of the forum.

local function createPancakez()    local delicious = true;    local amountOfCakez = 3;    local sirup = true;    local pancakez = Pancakez.new("pancakez", amountOfCakez, declicious, sirup);    return pancakez;endEvents.OnForumVisit.Add(createPancakez);
You do now.
Link to comment
Share on other sites

  • 2 weeks later...

Updated to Version 1.7.0:

 

round

----- Round the given numerical value. Be careful: The rounding-- might not be exact. By default it leaves 1 decimal.---- @param _number - The value to be rounded.-- @param _decimal - (optional) The number of decimals to leave.---- @author RoboMat-- @since 1.7.0--function Utility.round(_number, _decimal)	local num = _number;	local dec = _decimal or 1;	local temp = dec and 10 ^ dec or 1;	return math.ceil(num * temp - 0.5) / temp;end

 

isInInventory

----- Checks if a item is already in the player's inventory,-- or if it has to be transferred.---- @param _item - The item to check for.-- @param _player - The player who's inventory to check.---- @author RoboMat-- @since 1.7.0--function Utility.isInInventory(_item, _player)	return _item:getContainer() ~= _player:getInventory();end

 

isInHand

----- Checks if the item is equipped as the primary or-- secondary weapon of the player. Returns 1 if the-- weapon is the primary, 2 if it is the the secondary-- and zero if it is not equipped.---- @param _item - The item to check for.-- @param _player - The player to search through.---- @author RoboMat-- @since 1.7.0--function Utility.isInHand(_item, _player)	local p = _player;	local i = _item;	if p:getPrimaryHandItem() == i then		return 1;	elseif p:getSecondaryHandItem() == i then		return 2;	end	return 0;end

 

trim

----- Removes leading and trailing whitespace from a string.-- Copied from RJ's luautils but now uses proper tail call.---- @param _string - The string to trim.---- @author RoboMat-- @since 1.7.0--function Utility.trim(_string)	return _string:match("^%s*(.-)%s*$");end

 

startsWith

----- This function can be used to test if a string-- starts with a certain pattern.---- @param _string - The string to test.-- @param _start - The string to test for.-- @param _ignoreCase - (Optional) If the case should be ignored.---- @author RoboMat-- @since 1.7.0--function Utility.startsWith(_string, _start, _ignoreCase)	local str = _string;	local start = _start;	-- Exit early if we don't have strings to compare.	if not Utility.isString(str) or not Utility.isString(start) then		return;	end	-- If we ignore the case.	local case = _ignoreCase or false;	if case then		str = str:lower();		start = start:lower();	end	return str:sub(1, start:len()) == start;end

 

weaponLowerCondition

----- Ported the WeaponLowerCondition(...) function of the-- SwipeState.class to lua. Use this to damage your weapons.-- If the weapon breaks during the execution of this function-- it checks in which hand the item was equipped and tries to-- replace it with the next best weapon.---- @param _weapon - The weapon / item to damage.-- @param _character - The player who is carrying the weapon.-- @param _replace - (Optional) Wether or not to replace the item if it is broken.-- @param _chance - (Optional) The chance for the weapon to be damage.--                  If omitted it uses the conditionLowerChance of the item.---- @author RoboMat-- @since 1.7.0--function Utility.weaponLowerCondition(_weapon, _character, _replace, _chance)	local weapon = _weapon;	local chance = _chance or weapon:getConditionLowerChance();	-- Random chance to damage the weapon based on the stats.	if ZombRand(chance) == 0 then		local replace = _replace or true;		local condition = weapon:getCondition() - 1;		weapon:setCondition(condition);		-- If the weapon breaks unequip it and get a new one instead.		if condition <= 0 and replace then			local char = _character;			local descriptor = char:getDescriptor();			local newWeapon = char:getInventory():getBestWeapon(descriptor);			-- Checks in which hand the item is equipped.			local pos = Utility.isInHand(weapon, char);			-- Replace it with the new weapon.			if pos == 1 then				ISTimedActionQueue.add(ISEquipWeaponAction:new(char, newWeapon, 25, true));			elseif pos == 2 then				ISTimedActionQueue.add(ISEquipWeaponAction:new(char, newWeapon, 25, false));			else				ISTimedActionQueue.add(ISEquipWeaponAction:new(char, newWeapon, 25, false));			end		end	endend
Link to comment
Share on other sites

  • 2 weeks later...

You need to place the file you want to read in /Users/Zomboid/lua - That's the same folder where the keys.ini is saved. Besides that I will upload a slightly improved version shortly, which allows you to concatenate the lines of a file after reading.

Link to comment
Share on other sites

  • 2 weeks later...

Next update will sport a heavily modified file reader function:

 

----- Reads a file line by line until EOF is reached. The content of the file-- is saved as a lua table.---- @param _fileName - The name of the file to read (must include extension).-- @param _createNewFile - Create a new file if none exists already.-- @param _concatenate - Wether to concatenate the content after reading or not.-- @param _tags - A table of different tags to search for - e.g. { "<xmplTag>" }-- @param _delim - The delimiter that seperates multiple tags on one line.---- @return - A table which contains the file name and the (concatenated) content.---- @author RoboMat-- @since 1.9.0--function Utility.readFile(_fileName, _createNewFile, _concatenate, _tags, _delim)	local name = _fileName;	local useNewFile = _createNewFile;	local concatenate = _concatenate;	local tags = _tags;	local delim = _delim or "/";	-- Load the reader.	local reader = getFileReader(name, useNewFile);	local file = {};	file.path = nil;	file.content = nil;	if reader then		local content = {}; -- Used to store the content of the read file.		local line; -- Stores the current read line.		-- Read file until EOF.		while true do			-- Read the current line.			line = reader:readLine();			-- Close file if we reach end of file and			-- stop the loop if we have no more lines			-- to read.			if not line then				reader:close();				break;			end			-- Look for tags if the user specified them.			if tags then				for _, tag in ipairs(tags) do					-- Check if the line starts with the current tag.					if Utility.startsWith(line, tag) then						-- Remove "< >" from tag.						local tagEdit = string.sub(tag, 2, string.len(tag) - 1);						-- Remove tag from beginning of line.						line = string.sub(line, string.len(tag) + 1);						-- Check if we have multiple values for that tag.						-- If so we split up the line into a table.						if Utility.contains(line, delim) then							file[tagEdit] = Utility.split(line, delim);						else							file[tagEdit] = line;						end					else						-- Store the read line.						table.insert(content, line);					end				end			else -- Store line directly if there aren't any tags.				table.insert(content, line);			end		end		-- Concatenate all read lines.		if concatenate then			content = table.concat(content, "\n");		end		file.path = name;		file.content = content;		return file;	else		file.path = "ERROR: FileNotFound at " .. name .. ".";		file.content = "ERROR: No content at " .. name .. ".";		return file;	endend
The file reader is now capable of reading files and concatenating them correctly. But the real feature is that it now can read an unlimited amount of tags and store them in a table.

For example you could have a file like this

-- Monster.txt<name>Monster<weapon>Teeth<skill>stomp/mutilate/clawA fierce monster that will munch on your face.-- yourLua.lua...local tags = {"<name>","<weapon>","<skill>"};local file = Utility.readFile("Monster.txt", false, false, tags);...
The table output of the above file would look like this:

file.path = "Monster.txt";file.content = "A fierce monster that will munch on your face.";file.name = "Monster";file.weapon = "Teeth";file.skill = {"stomp", "mutilate", "claw"};
Link to comment
Share on other sites

Updated:

 

Version 1.9.0
- Added Lua Benchmarker
    => measures time between start() and stop()
    => untested memory management stuff
- Added Utility.split()
    => Splits a file at a certain delimiter e.g. "/"
- Updated readFile(...)
    => now can scan for unlimited amount of tags (e.g. <title>)
        it will store the content of the line in the
        table of the file, where you can use the tag
        to access it later on. (e.g. file.title)
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...