Difference between revisions of "Loading Level Objects When Level Starts"
m |
(Improved/moved note about avoiding object-order problems) |
||
| Line 3: | Line 3: | ||
To do this, you can add code to the Lua functions which construct objects from their ...Instances tables, so that whenever the game calls those functions at the start of a level, new object constructors are inserted and/or existing objects are changed before the objects start getting constructed. | To do this, you can add code to the Lua functions which construct objects from their ...Instances tables, so that whenever the game calls those functions at the start of a level, new object constructors are inserted and/or existing objects are changed before the objects start getting constructed. | ||
Another option is to add code to the embedded (Object Type)Instances.lua files themselves, and make them call a function which adds/edits level objects in the table or simply loads a completely new table from a non-embedded file. Some level object types seem to be built with currently-inaccessible C functions instead of Lua functions, and this might be the only way to change those tables. | Another option is to add code to the embedded (Object Type)Instances.lua files themselves, and make them call a function which adds/edits level objects in the table or simply loads a completely new table from a non-embedded file. Some level object types seem to be built with currently-inaccessible C functions instead of Lua functions, and this might be the only way to change those tables. | ||
'''Important note-''' To avoid causing new glitches in your game, and ''especially'' to avoid causing them in the games of non-modding companions, new objects which weren't in the original game apparently need to: | |||
*be inserted at the end of their (Object)Instances tables | |||
*have ObjectNames that ''do not'' start with any numbers or the letters A through F (either uppercase or lowercase). | |||
So for example instead of naming something "ABC" or "123", then name it at least "gABC" or "g123". | |||
<div class="toccolours mw-collapsible mw-collapsed"> | |||
<div style="font-weight:bold;line-height:1.6;">Explanation</div> | |||
<div class="mw-collapsible-content"> | |||
Further testing is needed, but it appears that some things in the game code look for objects based on the order they were created from the table and/or their place in the alphabetical order of ObjectNames, instead of just looking for the ObjectName in the Names table. This seems especially to happen when the game is sending updates about networked objects to your companion. | |||
So, if your objects are ordered differently than they should be because you put something new into the middle of the ordered list, then stuff won't work right, for example because the game will try to activate "object # 50" but the correct object is now "object # 55", or because it's "object # 55" in your game but is "object # 50" in your companion's game. | |||
Alphabetical order seems important, but if so then fortunately all the game's original ObjectNames are in hex, so they only contain numbers and the letters "a" through "f" - meaning that any new ObjectName starting with a letter after "f" will come in alphabetical order after all the original objects. (Technically anything after "ff" also works, like "fgName") | |||
There is a chance that actually ''any'' new objects will disrupt some order and cause issues in online play if companion doesn't have the exact same objects, but tests done so far had no visible problems as long as the new objects were at the end of the table and had "safe" object names. | |||
</div></div> | |||
=Loading objects with Lua functions= | =Loading objects with Lua functions= | ||
| Line 430: | Line 446: | ||
*"Add" object constructors will create new, extra objects. | *"Add" object constructors will create new, extra objects. | ||
*"Edit" object constructors will replace the original constructor that has the same ObjectName, this can be used to change objects from the original game as well as new objects made with the "Add" tables. | *"Edit" object constructors will replace the original constructor that has the same ObjectName, this can be used to change objects from the original game as well as new objects made with the "Add" tables. | ||
Revision as of 15:12, 23 September 2021
So far, it seems the most stable way to add level objects that will interact with absolutely everything properly - especially by adding non-temporary Triggers - is to include them in the "(Object Type)Instances" tables that get loaded at the very start of the level. This is also one of the best times to edit any level object that you want to be changed before the level actually begins to run, and might be the only time you can change Triggers from one Type to another.
To do this, you can add code to the Lua functions which construct objects from their ...Instances tables, so that whenever the game calls those functions at the start of a level, new object constructors are inserted and/or existing objects are changed before the objects start getting constructed. Another option is to add code to the embedded (Object Type)Instances.lua files themselves, and make them call a function which adds/edits level objects in the table or simply loads a completely new table from a non-embedded file. Some level object types seem to be built with currently-inaccessible C functions instead of Lua functions, and this might be the only way to change those tables.
Important note- To avoid causing new glitches in your game, and especially to avoid causing them in the games of non-modding companions, new objects which weren't in the original game apparently need to:
- be inserted at the end of their (Object)Instances tables
- have ObjectNames that do not start with any numbers or the letters A through F (either uppercase or lowercase).
So for example instead of naming something "ABC" or "123", then name it at least "gABC" or "g123".
Further testing is needed, but it appears that some things in the game code look for objects based on the order they were created from the table and/or their place in the alphabetical order of ObjectNames, instead of just looking for the ObjectName in the Names table. This seems especially to happen when the game is sending updates about networked objects to your companion.
So, if your objects are ordered differently than they should be because you put something new into the middle of the ordered list, then stuff won't work right, for example because the game will try to activate "object # 50" but the correct object is now "object # 55", or because it's "object # 55" in your game but is "object # 50" in your companion's game.
Alphabetical order seems important, but if so then fortunately all the game's original ObjectNames are in hex, so they only contain numbers and the letters "a" through "f" - meaning that any new ObjectName starting with a letter after "f" will come in alphabetical order after all the original objects. (Technically anything after "ff" also works, like "fgName")
There is a chance that actually any new objects will disrupt some order and cause issues in online play if companion doesn't have the exact same objects, but tests done so far had no visible problems as long as the new objects were at the end of the table and had "safe" object names.
Loading objects with Lua functions
Many level objects are constructed out of (ObjectType)Instances tables by Lua functions which the game calls right after it loads the table; so to add/edit objects, you just need to put extra code into those functions to change the table before anything gets constructed, but make sure that only happens when the game's C code is calling the function to initialize a level.
Example script: LoadModLevelObjects
Here is some code that can be injected or embedded to modify the original Lua functions for most level object types, providing a rough framework for adding/editing level objects when the level starts:
--Create necessary tables
Mod =
{ LevelObjects =
{ Add =
{ CameraKeyFrame = {}
, Clump = {}
, DecorationMesh = {}
, DynamicNode = {}
, EnvNode = {}
, Hull = {}
, Jet = {}
, Marker = {}
, ParticleEmitter = {}
, Rail = {}
, Sign = {}
, SoundEmitter = {}
, Timeline = {}
, Trigger = {}
}
, Edit =
{ CameraKeyFrame = {}
, Clump = {}
,DecorationMesh = {}
, DynamicNode = {}
, EnvNode = {}
,Hull = {}
, Jet = {}
, Marker = {}
, ParticleEmitter = {}
, Rail = {}
, Sign = {}
, SoundEmitter = {}
, Timeline = {}
, Trigger = {}
}
, Null =
{ CameraKeyFrame = {}
, Clump = { NULL = [[Objects = {} ]]}
,DecorationMesh = {}
, DynamicNode = {}
, EnvNode = {}
, Hull = {}
, Jet = {}
, Marker = {}
, ParticleEmitter = {}
, Rail = {}
, Sign = {}
, SoundEmitter = {}
, Timeline = { NULL = [[Triggers = {}, Length = 0, LoopStart = 0, LoopEnd = 0, PauseTime = 0, Looping = false, NonGracefulLooping = false, Name = ""]]}
, Trigger = { NULL = [[Type = "NOP", Vars = {}]] }
}
}
}
--converts object constructor strings to tables, inserts them at end of Instances table
local function AddObjects(objType,lvl)
local tbl = Mod.LevelObjects.Add[objType][lvl]
if tbl then
for k,v in pairs(tbl) do
local obj = load("table.insert("..objType.."Instances, {"..v.."})")
obj()
end
end
end
--checks if an object constructor in Instances table has same name as one in Edit table, if so it converts constructor string to table and uses that to overwrite original constructor
local function EditObjects(objType,lvl)
local tbl = Mod.LevelObjects.Edit[objType][lvl]
if tbl then
for i,v in ipairs(_G[objType.."Instances"]) do
local n = v.ObjectName
if tbl[n] then
local obj = load(objType.."Instances["..tostring(i).."] = {"..tbl[n].."}")
obj()
end
end
end
end
-- checks if constructor in Instances table has ObjectName matching an Edit table key that does not equal false/nil, if so overwrites it with a premade "null" constructor
-- only bothered to make this work for triggers/clumps/timelines so far
local function NullObjects(objType,lvl)
local tbl = Mod.LevelObjects.Null[objType]
if tbl[lvl] then
for i,v in ipairs(_G[objType.."Instances"]) do
local n = v.ObjectName
if tbl[lvl][n] then
local obj = load(objType.."Instances["..tostring(i).."] = {ObjectName = [["..n.."]], "..tbl["NULL"].."}")
obj()
end
end
end
end
-- if lua = false (which should only happen when C code calls function, if your lua script calls it make sure it's = true)
-- then it does the add/edit/null functions with the "all levels" table and then the current level's table
function LoadModLevelObjects(objType,lua)
if not lua then
local uid = tostring(game:GetCurrentLevelNumber())
AddObjects(objType,"All")
AddObjects(objType,uid)
EditObjects(objType,"All")
EditObjects(objType,uid)
NullObjects(objType,"All")
NullObjects(objType,uid)
end
end
-- These functions are set up so that objects are stored as strings and then converted to tables when needed:
-- if simply stored as tables, then any variable defined as equal to Names["Something"] or a formula or other
-- non-static value will always be equal to whatever it was at the time the table was created, which can
-- very likely be a nil value or something else wrong, so it breaks or crashes the game.
-- There's probably better ways to do this then storing strings, but this works for now.
-- The only modification done to these functions was adding the "lua" function variable (which is automatically false when function is called by C code),
-- and putting "LoadModLevelObjects((the object type as string),lua)" at the start of the function.
-- CAMERA KEY FRAMES, from CameraInit.lua
function LoadCameraKeyFrames( cameraSys, lua )
LoadModLevelObjects("CameraKeyFrame",lua)
for i, desc in ipairs( CameraKeyFrameInstances ) do
local camKey = cameraSys:AllocateCameraKey()
CameraKeyFrameInitialize( cameraSys, camKey, desc )
-- Now triggers can refer to gardener-placed camera keyframes
Names[ desc.ObjectName ] = camKey
end
CameraKeyFrameInstances = {}
end
-- CLUMPS, from ClumpLoader.lua
function LoadClumps( clumpBarn, lua )
LoadModLevelObjects("Clump",lua)
--print("LoadClumps \n")
--print( #ClumpInstances )
for i,ClumpDesc in ipairs( ClumpInstances ) do
local clumpPtr = clumpBarn:CreateClump( )
--clumpBarn:AllocateClumpStorage( clumpPtr, #ClumpDesc.Objects )
--Fill in the name table so triggers can refer to clumps
Names[ ClumpDesc.ObjectName ] = clumpPtr
end
end
--build clumps out of the resolved name table
function SetClumpMembers( clumpBarn, lua )
LoadModLevelObjects("Clump",lua)
for i,ClumpDesc in ipairs( ClumpInstances ) do
-- Traverse the clump table and compute how many objects will be in the clump
local clumpSize = 0
for j,ClumpMember in ipairs( ClumpDesc.Objects ) do
if type( Names[ ClumpMember ] ) == "table" then
clumpSize = clumpSize + #Names[ ClumpMember ]
else
clumpSize = clumpSize + 1
end
end
clumpBarn:AllocateClumpStorage( Names[ ClumpDesc.ObjectName ], clumpSize )
local idx = 0
for j,ClumpMember in ipairs( ClumpDesc.Objects ) do
-- @Added by Nick on 3.17.08 to support multiple objects at one Names table entry
if type( Names[ ClumpMember ] ) == "table" then
for k,SubMember in ipairs( Names[ ClumpMember] ) do
local classPtr = Class.cast( SubMember )
Names[ ClumpDesc.ObjectName ]:SetAt( idx, classPtr )
idx = idx + 1
end
else
local classPtr = Class.cast( Names[ ClumpMember ] )
Names[ ClumpDesc.ObjectName ]:SetAt( idx, classPtr )
idx = idx + 1
end
end
end
--Free the memory taken by the ClumpInstances table
ClumpInstances = {}
end
-- DECORATION MESHES, from DecorationMeshes.lua(?)
-- needs more work to fit in LoadModLevelObjects function, since it's not loaded the same way with a "DecorationMeshInstances" etc
-- is loaded from .bin's that are already outside the .exe and editable anyways
-- DYNAMIC NODES/CREATURES, from Creatures.lua
function CreateCreatures( creatureBarn, lua )
LoadModLevelObjects("DynamicNode",lua)
for i, v in ipairs( DynamicNodeInstances ) do
if v.Type == "Creature" then
Names[ v.ObjectName ] = creatureBarn:CreateCreature()
end
end
end
function ResolveCreatureNames( creatureBarn, lua )
LoadModLevelObjects("DynamicNode",lua)
for i, v in ipairs( DynamicNodeInstances ) do
if v.Type == "Creature" then
CreatureInitialize( Names[ v.ObjectName ], v )
Names[ v.ObjectName ]:gardenerName( v.GardenerName )
end
end
end
-- ENVIRONMENT NODES, from Environment.lua
function LoadEnvNodes( env, lua )
LoadModLevelObjects("EnvNode",lua)
for i, desc in ipairs( EnvNodeInstances ) do
local envNode
if desc.IsZNode then
envNode = env:AddZNode( desc.ZPos, desc.Radius )
else
local ignoreLightDir = true
if desc.Env_IgnoreLightDir ~= nil then
ignoreLightDir = desc.Env_IgnoreLightDir
end
envNode = env:AddVolumeNode( desc.MinBoundingBox.Position, desc.MinBoundingBox.Extent, desc.MaxBoundingBox.Extent, desc.FadeDuration, ignoreLightDir )
end
if envNode == nil then
print( "*** Error loading env node "..desc.ObjectName )
else
EnvNodeInitialize( envNode, desc )
--load the envNode into the name table to be accessible to the trigger system
Names[ desc.ObjectName ] = envNode
end
end
--For some reason, this causes a crash.. not fixing right now
--EnvNodeInstances = {}
end
-- HULLS/REGIONS, from HullBarn.lua(?)
-- needs more work to fit in LoadModLevelObjects function, since it's not loaded the same way with a "HullInstances" etc
-- is loaded from .bin's that are already outside the .exe and editable anyways
-- JETS, from Jets.lua
function LoadJets( JetBarn, lua )
LoadModLevelObjects("Jet",lua)
for i, v in ipairs( JetInstances ) do
local jetPtr = JetBarn:CreateJet()
JetInitialize( jetPtr, v )
--for Events/Triggers
Names[ v.ObjectName ] = jetPtr
end
JetInstances = {}
end
-- MARKERS, from Markers.lua
function LoadMarkers( MarkerBarn, lua )
LoadModLevelObjects("Marker",lua)
for i, v in ipairs( MarkerInstances ) do
local markerPtr = MarkerBarn:CreateMarker()
MarkerInitialize( markerPtr, v )
Names[ v.ObjectName ] = markerPtr
end
MarkerInstances = { }
end
-- PARTICLE EMITTERS, from EnvFX.lua and/or ParticleBarn.lua(?)
-- the obvious function in EnvFX.lua is not actually used, not sure how to rig something together from other Lua-accessible stuff yet
-- for now, need to put LoadModLevelObjects("ParticleEmitter") at the very end of embedded ParticleEmitterInstances.lua's
-- RAILS, from RailBarn.lua
function LoadRails( railBarn, lua )
LoadModLevelObjects("Rail",lua)
for i, desc in ipairs( RailInstances ) do
local rail = railBarn:CreateRail()
railBarn:AllocateRailPointStorage( rail, #desc.RailDataPoints )
railBarn:AllocateRailArcParam( rail )
RailInitialize( rail, desc )
--Fill in the name table so triggers and clumps can refer to rails
Names[ desc.ObjectName ] = rail
end
end
-- SIGNS/SCROLLING TEXT, from SignBarn.lua
-- this only lets you change position/appearance of Signs, not the actual text
-- text comes from SignData.lua which goes into "Credits" table, not "SignInstances"
-- need to rework LoadModLevelObjects functions to accomodate that; low priority so skipped it for now
function InitializeSignBarn( signBarn, signData, signInstances, lua )
LoadModLevelObjects("Sign",lua)
local credSec, textEnt
-- load the text entries from SignData.lua
for i,sec in ipairs( Credits ) do
credSec = signBarn:AddCreditSection( sec.SectionName, #sec.Entries )
for j,ent in ipairs( sec.Entries ) do
textEnt = credSec:GetEntry( j-1 )
signBarn:SetTextEntry( textEnt, ent.Text )
end
end
-- SignInstances.lua
-- iterate through the sign instances and tie the signs to the credit entries
--local color = { 255, 128, 64, 255 }
for i,signInst in ipairs( SignInstances ) do
if signInst.secName ~= "" then
textEnt = signBarn:FindTextEntry( signInst.secName, signInst.entNum )
else
textEnt = signBarn:GetTextEntry( signInst.secNum, signInst.entNum )
end
local sign = signBarn:AddSignTextEntry( textEnt )
SignInitialize( sign, signInst )
Names[ signInst.ObjectName ] = sign
end
SignInstances = {}
Credits = {}
end
-- SOUND EMITTERS, from SoundBarn.lua(?)
-- the obvious function in SoundBarn.lua is not actually used, not sure how to rig something together from other Lua-accessible stuff yet
-- for now, need to put LoadModLevelObjects("SoundEmitter") at the very end of embedded SoundEmitterInstances.lua's
-- TIMELINES, from TimelineBarn.lua
function LoadTimelines( timelineBarn, lua )
LoadModLevelObjects("Timeline",lua)
for i, info in ipairs( TimelineInstances ) do
local timeline = timelineBarn:CreateTimeline()
TimelineInitialize( timeline, info )
Names[ info.ObjectName ] = timeline
end
end
--TRIGGERS/EVENTS, from EventSetter.lua
function CreateTriggers( eventBarn, lua )
LoadModLevelObjects("Trigger",lua)
-- This double loop is necessary so Triggers can refer to other Triggers by name
for i,TrigDesc in ipairs( TriggerInstances ) do
local event = eventBarn:MetaAddEvent( TrigDesc.Type )
-- grab the actual function out of the global variable table
local eventImplPtr = _G[ TrigDesc.Type ].cast( event )
Names[ TrigDesc.ObjectName ] = eventImplPtr
end
end
--reload the TriggerInstances.Lua file before this gets called
function ResolveTriggerNames( eventBarn, clumpBarn, lua )
LoadModLevelObjects("Trigger",lua)
TriggerShortcuts = {}
for i,TrigDesc in ipairs( TriggerInstances ) do
TriggerInitialize( Names[ TrigDesc.ObjectName ], TrigDesc )
end
--To forcibly consume the Triggers
TriggerInstances = {}
end
Run the main script, and the script which modifies the object constructor functions (or modify the embedded Lua files containing the object constructor functions so that they look like they do here, and run the main script before the game starts loading Level_Graveyard for the first time).
- Note: if your other Lua scripts run those functions after the level has started running, make sure you pass it a non-false value for "lua", for example:
CreateTriggers(game:eventBarn(),true)
Create/assign tables of level objects inside the Mod.LevelObjects... tables that are created by the main script.
- "Add" object constructors will create new, extra objects.
- "Edit" object constructors will replace the original constructor that has the same ObjectName, this can be used to change objects from the original game as well as new objects made with the "Add" tables.
- "Null" ObjectName keys will safely "delete" the object with the same ObjectName, by changing it into an object that exists but does nothing.
- This change to "nothing" takes priority over any changes done in Edit tables.
- The script on this page only works with triggers/timelines/clumps so far, haven't bothered figuring out "null" constructors for other object types yet.
For tables that should only load in a specific level, use ["(the level UID).0"] as the table key, for example adding triggers to the default Chapter Select/Level_Graveyard would be:
Mod.LevelObjects.Add.Trigger["0.0"]
For tables that should load in all levels, use All/["All"] as the table key, for example:
Mod.LevelObjects.Add.Trigger.All
Mod.LevelObjects.Add.Trigger["All"]
You can simply build things in the main tables, running code like:
Mod.LevelObjects.X.Y["Z"] = { Object1, Object2, Object3 }
But it might be more convenient to store the tables elsewhere and just swap them around, like:
Table1 = { Object1, Object2, Object3 }
Table2 = { Object4, Object5, Object6 }
Mod.LevelObjects.X.Y["Z"] = Table1
-- Then later you swap it:
Mod.LevelObjects.X.Y["Z"] = Table2
To make a level stop using any table for a function, run code like:
Mod.LevelObjects.X.Y["Z"] = nil
You must format the tables a little differently from what is used in (Object Type)Instances.lua's, since this function uses strings of tables rather than actual tables (the only way I found so far to avoid errors with persistent tables).
For example, (ObjectType)Instances tables are formatted like this:
SomethingInstances =
{ ObjectType = "SomethingInstances"
, { ObjectName = "ExampleName1"
, Variable1 = "whatever"
, Variable2 = "etc."
}
, { ObjectName = "ExampleName2"
, all the other stuff
}
}
'But' the Mod.LevelObjects tables must be formatted like 'this':
ExampleTable =
{ ExampleName1 = [[ ObjectName = "ExampleName1"
, Variable1 = "whatever"
, Variable2 = "etc."
]]
, ExampleName2 = [[ ObjectName = "ExampleName2"
, all the other stuff
]]
}
( I know doubling the ObjectName in the key is redundant, but using the ObjectName as the key makes the code more efficient (I think) because there's less for/do loops needed; and keeping the ObjectName in the constructor too will make it easier to convert already-made lists back into normal tables once it's figured out how to make the function just use normal tables without breaking. )
Objects in Null tables can either be formatted the same as Add/Edit, or simply as:
ExampleName = true
Also, if any table key starts with a number (which many of the original ObjectNames do) it seems the key needs to be wrapped in [" "], like:
["123ExampleName"] = all the stuff
Note that this does not yet work for Hulls or Decoration Meshes. Also, in order to work for Particle Emitters and Sound Emitters, the embedded (thing)EmitterInstances.lua's need to be edited so that they appear like:
(thing)EmitterInstances =
{
keep this table the way it is
}
LoadModLevelObjects("(thing)Emitter")
Run the LoadModLevelObjects scripts.
Run this code to create a table containing triggers that will make text appear on your screen when you start a level:
TextAtStart =
{ TriggerTheText = [[ ObjectName = "TriggerTheText"
, Type = "EventOnLevelStart"
, Vars = { triggers = Names["ShowText"] }
]]
, ShowText = [[ ObjectName = "ShowText"
, Type = "DisplayText"
, Vars = { duration = 7
, text = "Hello world!"
}
]]
}
Run this code to assign that table, so that the text appears at the start of every level:
Mod.LevelObjects.Add.Trigger.All = TextAtStart
Run this code to create a table containing triggers that will make different text appear, and it instead happens the first time in a level that you reach max scarf length:
TextOnMaxScarf =
{ TriggerTheText = [[ ObjectName = "TriggerTheText"
, Type = "EventOnCapeLength"
, Vars = { capeLength = 30
, effects = Names["ShowText"]
, fireOnce = true
}
]]
, ShowText = [[ ObjectName = "ShowText"
, Type = "DisplayText"
, Vars = { duration = 7
, text = "Hello scarf!"
}
]]
}
Run this code to assign that table, so that different text appears and it happens when you get max scarf, but only in Paradise:
Mod.LevelObjects.Edit.Trigger["7.0"] = TextOnMaxScarf
Run this code to make a table simply for "removing" the trigger object which shows the text:
NoText = { ShowText = true }
Run this code to assign that table, to "remove" that text trigger in all levels - although the activation trigger still exists, so for example if it instead was made to activate a clump that contained that text trigger, everything else in the clump besides the text trigger would still work normally:
Mod.LevelObjects.Null.Trigger.All = NoText
Loading objects by editing (Object)Instances.lua
Rather than putting code into existing Lua functions, you can put a small amount of code into the embedded (Object)Instances.lua files to change the table immediately when it's loaded, though you would have to do this for each different level and each object type you wanted to manipulate this way.
This is inconvenient but right now seems to be the only way to edit Particle Emitters or Sound Emitters, since the function that converts their (Object)Instances table into level objects seems to be done through C code instead of Lua functions... some Lua functions exist which seem built for this task but the game never calls them, and no way has been found yet to access the necessary Barn class objects to make the functions work.
See the "[PC] Lua Script Editing / Injection" thread in "How to Mod" on Journey fan forum ( https://thatgamecompanyfan.boards.net/board/9/modding ) for details of how to actually change the embedded Lua files.
The files should be edited so that at least these comments at the start of the file are deleted:
-- THIS FILE IS AUTOMATICALLY GENERATED.
-- DO NOT MODIFY THIS FILE!
That gives you just enough free space to fit in some useful code, especially if you use the maximum possible compression.
To get even more space, you could delete:
- unnecessary spaces/indents/newlines etc
- GardenerName variables, which seem to either be totally unused or do nothing that affects gameplay
- at least some variables (like Vars for Triggers) that say "variable = false" or "variable = 0", which will not affect how the object gets constructed, unless those variables have default values other than false/zero (can check that in MetaSystem)
Once you have enough space, you can add some code at the end to call something which modifies the (Object)Instances table right after it gets made, for example if using the LoadModLevelObjects script above you would change the code to something like:
ObjectTypeInstances =
{
(this whole table stays the same)
}
LoadModLevelObjects("ObjectType")
Or you could set a condition to just load a completely different table instead, for example:
if UseDifferentTable == true then ObjectTypeInstances = DifferentTable
-- OR: if UseDifferentTable = true then dofile("a modified ObjectTypeInstances.lua file outside the .exe")
-- although that by itself would be insecure and most likely a bad idea for mods you plan to distribute publicly
else ObjectTypeInstances =
{
(this whole table stays the same)
}
end