A Lua based solution to change Stalker Anomaly LTX Files on the fly once the game starts.
This Library Collects changes from several 3rd party scriptfiles and applies them once on game start.
Note this is a Pre-Release Version, so things are subject to change until 1.0.0 is done and as such Semantic Versioning (important for Mod Developers only) does not apply yet - this Library is a proof of concept for now
If you have a problem, please copy the output of your Log and create an Issue
As an End-User you only need this Library if a Mod you downloaded actually requires you to install it.
If you have no such mod installed, this Library does nothing and you dont need it.
Mod Developers who want to make changes to existing LTX files (vanilla or other mods) while being minimally invasive (so no more overwriting entire ltx files just to change a few properties) - thus improving mod cross-compatibility for mods that make use of this Library.
Currently works with LTX Files that are either registered through the system.ltx or on trader files
Mods that edit exactly the same properties of the same section still "conflict", albeit that here simply the last loaded mod wins (at least for now, see Roadmap )
But PLEASE do NOT use this Library to add new items (aka "sections" - for example weapons). Vanilla Anomaly already has this feature since at least 1.5.1 (at least for items)
The comment in vanilla file configs\items\items\base.ltx
reads as follows:
;; NOTICE FOR MODDERS ;;
; unless you need to edit already existing items, do NOT edit the "items_*" files but make a new one that defines your new items
; eg. "items_mymod.ltx"
; it will be automatically included and won't cause conflict with other mods that add/edit items
This Library itself (without having any other Mods installed that make use of it) can be safely added / removed anytime (just follow the Uninstall instructions)
In general, when I refer to something like "manual way" or "manual installation" I mean that you copied files manually into the Anomaly Directory without using JSGME or MO2.
While you can generally use this Library with Manual Installations or JSGME, I do not recommend it due to the complex Uninstall or Troubleshooting process that it may require from you, the user.
Mod Organizer 2 (Version 2.4 and up) is currently the easiest way to handle this (given that you only install Mods via MO2 and not manually aswell)
gamedata\configs\script.ltx
The ONLY vanilla file that is being shipped / overriden is gamedata\configs\script.ltx
- this simply has added ONE entry at the very beginning of class_registrators
which is autoloader.register
To my knowledge there is no addon for anomaly out there that touches this file (and is rarely touched itself), and there really is no need for any mod to do so currently. As such it is compatible with any other mod currently out there and will not conflict.
If you need to install, remove or update a mod that touches LTX files, you are required to do the following if you have started the Game with my Library and Mods that use my Library at least once
*.backup
and *.temp
files based on your Installed / Removed / Updated LTX FilesThis is required because my Library "copies" the vanilla / modded LTX in question on first startup to *.backup
- this backup file will be used on subsequent starts as a baseline for the modifications.
The reason that the Library doesn't copy the the vanilla / modded LTX each gamestart is that at when you quit the game it writes back the vanilla / modded LTX from *.backup
. However the problem arises when the game crashes - now the vanilla / modded LTX is not the "original" anymore but the one modified by the Library, which is why *.backup
is used as a basefile.
Either use a Mod Manager like JSGME or MO2 or install into the Anomaly folder (where the "gamedata" folder resides) like any other addon.
If you have other Mods installed that do not make use of this Library, it is required that you follow LTX specific requirements when you update those.
This is to ensure that the Modifications this Library makes, is made on the correct ltx file.
Deactivate the Library in your Mod Manager and then follow Remove LTX Files for your use-case
If you installed mods the manual way, you need to either remove the following files or start with a fresh gamedata folder - whatever you feel more comfortable with.
gamedata\configs\script.ltx
gamedata\scripts\config\Change.lua
gamedata\scripts\config\Changeset.lua
gamedata\scripts\config\ChangesetCollection.lua
gamedata\scripts\config\ChangesetLoader.lua
gamedata\scripts\config\ChangeWriter.lua
gamedata\scripts\config\File.lua
gamedata\scripts\config\FileLoader.lua
gamedata\scripts\config\Ini.lua
gamedata\scripts\autoloader.script
gamedata\scripts\ltx_autoload.script
gamedata\scripts\trader_autoload.script
The LTX files will be at the bottom of the load order as part of a mod called Overwrite
.
In general among the vanilla file that was modified, there will be two more files with the following pattern (*
would be the vanilla filename)
*.backup
*.temp
If you have not installed mods manually into the Anomaly directory
Overwrite
mod.If you have installed mods manually into the Anomaly directory
Overwrite
to bring up a filelist and manually delete all files with the pattern mentioned aboveOverwrite
and then reinstall the mods you installed manually afterwardsThere may be a third file (a cachefile) in there if you use the Anomaly debug mode which can be safely deleted aswell.
You need to manually go to your gamedata\configs
directory and remove the files from there.
gamedata\configs
directory and all available subdirectories
*.backup
*.temp
system.backup
and system.temp
also delete system.ltx
Install the Library like you would any other Mod (follow Install basically)
Notice the examples here are intentionally very verbose - you could just cram everything in the function into one line without using any variables, but that is not really good code (well at least if you try to make use of guidelines from e.g. "Clean Code: A Handbook of Agile Software Craftsmanship" to keep your code easy to read and maintain - hard to read / maintain code is not good code)
If you have problems, the first thing you can do is check the Console (or the logfile in the directory appdata\logs
if you quit the game already) - the Library generates Messages that start with LTX-LIBRARY
.
authorname_modname_system_mod.script
- the filename needs to end on _system_mod.script
Change.lua
(see Change) and Changeset.lua
(see Changeset) by using require inside the file you just created
local Change = require "gamedata\\scripts\\config\\Change"
local Changeset = require "gamedata\\scripts\\config\\Changeset"
registerSystemLtxModifications
- this function has no parametersswitch_distance
property of the alife
section to 20
and we want to change the property inv_weight
of the section bolt
to 1
local switchDistance = Change("alife", "switch_distance", 20)
local boltWeight = Change("bolt", "inv_weight", 1)
return Changeset({switchDistance, boltWeight}, "My Unique Changeset Name")
The complete example for authorname_modname_system_mod.script
would look like this
local Change = require "gamedata\\scripts\\config\\Change"
local Changeset = require "gamedata\\scripts\\config\\Changeset"
function registerSystemLtxModifications()
local switchDistance = Change("alife", "switch_distance", 20)
local boltWeight = Change("bolt", "inv_weight", 1)
return Changeset({switchDistance, boltWeight}, "My Unique Changeset Name")
end
authorname_modname_trader_mod.script
- the filename needs to end on _trader_mod.script
Change.lua
(see Change) and Changeset.lua
(see Changeset) by using require inside the file you just created
local Change = require "gamedata\\scripts\\config\\Change"
local Changeset = require "gamedata\\scripts\\config\\Changeset"
registerTraderLtxModifications
- this function has no parameterslocal pdaV1 = Change("supplies_1", "device_pda_1", nil)
local pdaV2 = Change("supplies_1", "device_pda_2", "1, 1")
local pdaV3 = Change("supplies_1", "device_pda_3", "1, 1")
return Changeset({pdaV1, pdaV2, pdaV3}, "My Unique Trader Changeset Name", "items\\trade\\trade_stalker_sidorovich.ltx")
The complete example for authorname_modname_trader_mod.script
would look like this
local Change = require "gamedata\\scripts\\config\\Change"
local Changeset = require "gamedata\\scripts\\config\\Changeset"
function registerTraderLtxModifications()
local pdaV1 = Change("supplies_1", "device_pda_1", nil)
local pdaV2 = Change("supplies_1", "device_pda_2", "1, 1")
local pdaV3 = Change("supplies_1", "device_pda_3", "1, 1")
return Changeset({pdaV1, pdaV2, pdaV3}, "My Unique Trader Changeset Name", "items\\trade\\trade_stalker_sidorovich.ltx")
end
But what if you want to change a file under configs\scripts\
instead? Well simple
local Change = require "gamedata\\scripts\\config\\Change"
local Changeset = require "gamedata\\scripts\\config\\Changeset"
function registerTraderLtxModifications()
local someChange = Change("logic@bar_barman", "trade", "items\\trade\\some_file.ltx") -- if you "trade" with barman he would have no items, because that trade file does not exist in this example
return Changeset({someChange}, "My Unique Trader Changeset Name", "scripts\\bar\\bar_barman.ltx")
end
This can be done by using the ChangesetCollection
Example based on the Trader Files
local Change = require "gamedata\\scripts\\config\\Change"
local Changeset = require "gamedata\\scripts\\config\\Changeset"
local ChangesetCollection = require "gamedata\\scripts\\config\\ChangesetCollection"
function registerTraderLtxModifications()
local pdaV1 = Change("supplies_1", "device_pda_1", nil)
local pdaV2 = Change("supplies_1", "device_pda_2", "1, 1")
local pdaV3 = Change("supplies_1", "device_pda_3", "1, 1")
local ChangesetA = Changeset({pdaV1}, "My Unique Sidorovich Changeset Name", "items\\trade\\trade_stalker_sidorovich.ltx")
local ChangesetB = Changeset({pdaV2, pdaV3}, "My Unique Barman Changeset Name", "items\\trade\\bar_barman.ltx")
return ChangesetCollection({ChangesetA, ChangesetB})
end
This "class" takes three required parameters and one optional parameter
section
(type: string
, required)property
(type: string
, required)value
(type: any except function
, required)
some_scriptname.someScriptFunction
(as used by vanilla anomaly in some instances, e.g. for custom item functors etc.)nil
then the property will be removed, pass an empty string if you want the property to be empty.[myitem]:parent
) cannot be used at this point, because the system.ltx
has already been processed, so if you remove a required property the game crashes even if the property is defined in the "parent" sectionoptional
(type: boolean
, optional)
optional
setting will override ittrue
then the change will always be optional (even if the changeset is set to non optional explicitly)false
then the change will always be non-optional (even if the changeset is set to optional explicitly)This "class" takes two required parameters and two optional parameters
changes
(type: table
, required)
changesetName
(type: string
, required)
ltx
(type: string
, optional)
items\\trade\\trade_stalker_sidorovich.ltx
optional
(type: boolean
, optional)
true
then the Changes will be optional by default (unless a Change is explicitly set to false
)false
then the Changes will be non-optional by default (unless a Change is explicitly set to true
)This "class" takes one required parameter
changesets
(type: table
, required)
As you may or may not know, Anomaly has a rudimentary way to "autoload" scriptfiles by adding a function called on_game_start
to a custom script file and then using RegisterScriptCallback
(see axr_main.script
for available callbacks)
While this mostly works, it will NOT work for e.g. the main_menu_on_init
callback.
Reason being that on_game_start
does not get run when you startup the game itself, but when you start a new game OR load a saved game. At this point main_menu_on_init
has already been fired and as such it is impossible to use this callback in the intended way in vanilla anomaly.
You can simply create a new file named something like authorname_mymod_autoload.script
(the script just has to end on _autoload.script
so you can name it however you want) and contain a function called register
.
Inside the register
function you can simply register the callback. For Example
authorname_mymod_autoload.script
function main_menu_on_init()
printf("hello ui init") -- see console output
end
function register()
RegisterScriptCallback("main_menu_on_init", main_menu_on_init)
end
The reason this works is because the autoloader.script
included in gamedata\configs\script.ltx
is being executed on game startup (see How it works for details) and searches for scripts ending on _autoload.script
to execute.
A proper fix to the callback System would need to be done in vanilla anomaly (that is not in the scope of this project), but at that point the way the callback system works should probably be refactored aswell.
What do I mean by refactoring? For example if two Authors want to create NEW Callbacks for their Mods (so 3rd parties can extend their mods through the use of callbacks) those two Authors currently have to MANUALLY edit axr_main.script
thus having a hard conflict (one of the two mod authors needs to maintain compatibility patches for this file). This of course gets worse the more mod authors want to add their own callbacks.
See Changelog here
See Roadmap
Due to the additional file being "registered" in gamedata\configs\script.ltx
the function register
in scriptfile autoloader.script
is executed on game start (when entering the main menu, so before loading or starting any game)
The autoloader.script
then searches for scripts named *_autoload.script
and executes the register
function inside them. Currently there are two autoloaders
ltx_autoload.script
*_system_mod.script
and executes the function registerSystemLtxModifications
inside themtrader_autoload.script
*_trader_mod.script
and executes the function registerTraderLtxModifications
inside themBoth functions that are called need to return a Changeset, which itself contains at least one or more "instances" of Change.
Basically:
Both autoloaders ensure that LTX files which are modified will be backed up - said backup will be called *.backup
.
When the autoloaders apply the changes, the Backup will be copied to a new file called *.temp
- this is the file the changes will be applied to.
This file will be recreated everytime you start the game (to ensure the Changes are always written to the last "known good" vanilla / modded LTX that was backed up)
When the Changesets have been completely applied, the *.temp
is saved and THEN will overwrite the original vanilla file.
Finally both autoloaders clear the ini cache and reload the system.ini
When you quit the game, the original LTX files will be restored from *.backup
- the *.temp
files remain as is (but get overwritten anyway on subsequent game starts)
Please check my Donations Repository for options to donate