Utilities -- Modular Script Loading

Vicevirtuoso

The Modetta Man
Joined
May 14, 2013
Messages
775
Location
The Wreckage, Brother
Download:
Steam Workshop
Mediafire

Replaces the following base game files:
  • CovertOperationsSystem.lua
  • MinorPowersSystem.lua
  • PersonalitySystem.lua
  • PlotBonusSystem.lua
  • QuestSystem.lua*

    * no longer necessary, and is only included for backwards compatibility with non-updated mods

While Firaxis allowed for significantly more modularity with systems such as Quests and Covert Operations due to most of their logic being handled through Lua, there is a critical issue with the implementation. By default, the game only checks for scripts in the game's assets folder and does not check for mods to add scripts to these systems. This mod replaces the core files such that mods are able to add new scripts to the above systems without requiring the Lua files to be placed in the game's assets folder.

How to implement in your mods:
  • In your mod, either add a dependency to this mod, or copy all of this mod's replacement Lua files to your mod and set them to VFS = true.
  • When defining a new item, such as a new Covert Operation, make sure the <ScriptName> tag is set to be your Lua filename without its extension.
  • Ensure your Lua file is set to VFS = true, but you must also add the Lua file through the Content tab, and you must use a Type appropriate to the type of content you are adding. Note that you must manually enter in the Types listed below, as they are not part of the default types in ModBuddy. The Types are:
    • Covert Operations: CovertOperationsAddin
    • Minor Power Scripts: MinorPowersAddin
    • Personality Scripts: PersonalityAddin
    • Plot Bonus Scripts: PlotBonusAddin
 
I would love for my mod to be as independent and non-conflicting as possible. It's a faction and modifies some functions in the CovertOperationSystem.lua. I'm having a little trouble understanding the intents and limitations of your framework. Is it possible for me to implement a CovertOperationAddin that overloads a function in original CovertOperationSystem? Thanks for any help you can provide. I attempted to add you on steam to ask this question.
 
First, you'll want to update the entry for the Covert Operation in the <CovertOperations> table so that the <ScriptName> tag is the filename of your new script minus extension. For example, let's say you're replacing the script for the Dirty Bomb operation with a script you've named MyScript_DirtyBomb.lua:

Code:
<GameInfo>
  <CovertOperations>
    <Update>
      <Where Type="COVERT_OPERATION_DIRTY_BOMB"/>
      <Set ScriptName="MyScript_DirtyBomb"/>
    </Update>
  <CovertOperations>
</Update>


Then, in your mod properties, in the Content tab, you would insert your MyScript_DirtyBomb.lua file as a CovertOperationsAddin type (manually enter this into the type field). Additionally, set MyScript_DirtyBomb.lua to be VFS = true.
 
Yes, it's only for new scripts to be loaded by CovertOperationsSystem and the other mentioned files. The mod itself replaces CovertOperationsSystem.lua, but the only change made to it is the search for ScriptNames in loaded mods prior to looking in the game's Assets folder.
 
There has to be a way of using the Personality scripts... I wonder if they could have been used to make an AI insta war a player when it kills an alien or something? Scripted personalities would add much to the leaders.
 
It really seems like it was an idea they planned on implementing, but scrapped due to time constraints and left the skeleton code in.

It's far past time I went back to working on my Civ 5 mods. Otherwise, I'd play around with it myself.
 
I've been trying to use this utility to have the game recognize and implement my slightly different version of KillSiegeWormQuest.lua, for Alien Strains, but so far to no avail. I've grabbed only QuestSystem.lua and PlotBonusSystem.lua from this mod, and had to modify the latter a bit so that the death of any type of Siege Worm could generate an Alien Skeleton. I haven't really tested that, but the main problem for me is still the quest, which doesn't trigger and in the best of cases just somehow has QuestSystem.lua produce a lot of errors.

I have QuestSystem.lua plugged in via VFS = true, as indicated, and KillSiegeWormQuest_AST.lua via VFS = True and QuestAddin. If I make a separate entry for the quest in <Quests>, it gets me a lot of errors on my Lua.log. If I instead Update the original quest entry to point to my own Lua, then the game just doesn't run the script at all.
 
I've heard that scripts won't load unless they're in the root directory of your mod (which is something I'm going to be looking into). Is your Lua script in the root of your mod folder?
 
I've heard that scripts won't load unless they're in the root directory of your mod (which is something I'm going to be looking into). Is your Lua script in the root of your mod folder?
It was in a Lua subfolder, but I moved it to root and the whole thing worked. Well, partially.

The quest activates whenever one of the three Siege Worm strains is spotted, as expected. However, I have to make the objective look something like this:
Code:
local objective = AddObjective(quest, "QUEST_OBJECTIVE_KILL_UNIT_TYPE", WORM_UNIT_TYPE[1] or WORM_UNIT_TYPE[2] or WORM_UNIT_TYPE[3], 1);
...And the script, I theorize, seems to relay only a single WORM_UNIT_TYPE (the first) to KillUnitTypeQuestObjective.lua, which only seems to contemplate a single, fixed UnitType for its calculations, instead of one out of n possible. Therefore, only killing a particular Siege Worm type completes the quest. The "any of three" issue has been my bane as far as this quest is concerned, and the reason why I've been struggling to make all these modifications to the vanilla version.

I realize this post goes off-topic, since the utility's now working correctly and the aforementioned problem is unrelated. So we can continue this via PM or on Steam, if you'd like. Any help would be appreciated.
 
Most likely, you'd need to edit KillUnitTypeQuestObjective.lua to allow for it to be passed multiple values. That or have three separate quests.
Three separate yet almost identical quests would be messy. I'll look into tweaking KillUnitTypeQuestObjective.lua.

Thank you for your life-saving utility. :)
 
I've heard that scripts won't load unless they're in the root directory of your mod (which is something I'm going to be looking into).

Basically the problem is that the "Addin" content system uploads the files including in its data their paths.
When your script compares that data with the script names in the Database they do not match (because one is the name of the file without the extension and the other is the name of a file without extension and its path in the mod folder) so the check fails.

This is however an issue that can be easily overcome without the need to put everything in the main directory.

All you need to do is to include the same path in the XML that redirects to the script.
For example:

Code:
	<QuestObjectives>
		<Update>
			<Where Type="QUEST_OBJECTIVE_CLEAR_FOREST_FROM_PLOTS" />
			<Set ScriptName="[B]Quests/QuestObjectives/ClearForestFromPlotsQuestObjective_fix[/B]" />
		</Update>
	</QuestObjectives>

In other words the same thing that you find in the modinfo file

Code:
    <EntryPoint type="QuestObjectiveAddin" file="[B]Quests/QuestObjectives/ClearForestFromPlotsQuestObjective_fix[/B].lua">

minus the extension.

As long as those two match, your script works fine.
 
All you need to do is to include the same path in the XML that redirects to the script.
For example:

Code:
	<QuestObjectives>
		<Update>
			<Where Type="QUEST_OBJECTIVE_CLEAR_FOREST_FROM_PLOTS" />
			<Set ScriptName="[B]Quests/QuestObjectives/ClearForestFromPlotsQuestObjective_fix[/B]" />
		</Update>
	</QuestObjectives>

In other words the same thing that you find in the modinfo file

Code:
    <EntryPoint type="QuestObjectiveAddin" file="[B]Quests/QuestObjectives/ClearForestFromPlotsQuestObjective_fix[/B].lua">

minus the extension.

As long as those two match, your script works fine.

I'd like to avoid that if at all possible, to make it easier and more flexible for the mod authors.

Here's where the major problem comes in. For some reason, Path isn't defined in any of these scripts. Path APIs work fine in other contexts, but not any of the ones replaced in this mod. It's preventing me from using the critical GetFileNameWithoutExtension function I'd need to make this easier.

It's a really clunky way of going about it, but I suppose I could have a secondary script as an InGameUIAddin for the sole purpose of adding GetFileNameWithoutExtension as a LuaEvent.
 
Well one way that I thought of solving the problem, before finding the simple solution above, was to modify your code so that it splits the path+file into tokens and take only the last one.

Example:

Code:
local file;
local line = "Quests/QuestObjectives/ClearForestFromPlotsQuestObjective_fix.lua"; 
for token in string.gmatch(line, "[^/]+") do 
file = token;
end

At the end of the cycle "file" will be equal to "ClearForestFromPlotsQuestObjective_fix.lua"
 
I ended up doing it this way since I didn't take a look at the thread while I was working on it:

Code:
function VVGetScriptName(arg)
	--First get rid of the .lua file extension
	local str = string.sub(arg, 1, string.find(arg, ".lua") - 1)
	--Then get rid of the path by reversing the string and deleting everything past the slash
	str = string.reverse(str)
	str = string.sub(str, 1, string.find(str, "/") - 1)
	--Re-reverse it and we should have our filename minus path and extension
	str = string.reverse(str)
	return str
end

Appreciate the suggestion, though. I'll be publishing an update which does this plus adds more verbose logging.



EDIT: I didn't take into consideration that it would break files which already specify an exact path, and would also remove the ability of the user to specify a path if desired. Perhaps I'll just check for the ScriptName to be contained in the entire Path.


EDIT 2: Update posted.
 
All right good job!
I've been trying your updated version while testing another mod I'm currently developing. Everything is working fine.
 
I've noticed this error happening several times:

Code:
 Runtime Error: bad argument #1 to 'func' (Argument 1 for Event.Remove must NOT be nil.)
stack traceback:
	[C]: in function 'func'
	assets\Gameplay\Lua\Quests\HostileTakeoverStationQuest.lua:145: in function 'QuestScript.OnUnregisterListeners'
	C:\Users\Mir\Documents\My Games\Sid Meier's Civilization Beyond Earth\MODS\Utilities -- Modular Script Loading (v 1)\QuestSystem.lua:534: in function 'OnQuestComplete'
	[C]: in function '(anonymous)'

It systematically occurs almost everytime this line is involved:

Code:
questScript.OnUnregisterListeners(wrapper);

This occurs for various quests in various situations that call the OnUnregesterListeners function.
I am using a modified quest, but so far firetuner only shows me errors about quests that have been untouched.

It seems that "wrapper" is nil when it's supposed to contain data.

I'm not quite sure if it is an error due to your script or from the default file.
 
Top Bottom