[TOTPP] Lua Scenario Template

I've updated the General Library with a series of functions for tile visibility. I haven't updated the documentation webpage for these changes yet.
For the next version of my Medieval Millennium utility libraries, I'm going to be adding a function syncKnownTileImprovements(tile, tribe) which basically just sets tile.visibleImprovements[tribe] = tile.improvements . You went the route of adding support for charting/uncharting each possible improvement separately, but (unless I missed it) I didn't see a function that would just give a tribe all of the most up-to-date info for the tile. Do you think that would be valuable to add?

I ultimately chose the throw error if the unit isn't a settler route, but that error to be suppressed with a second argument. This seemed simple and sensible, and a more complicated function would leave simple functionality of just changing a flag absent.
I didn't want to weigh in on this without giving people actively using the library the chance to respond first. But I think this is a good route -- much better than changing a unit type's role, which feels like it ought to be accomplished by calling a function that more explicitly intends that.

makeObject.lua no longer asks for the number of terrain types for each map. Instead, pcall is used for arguments of civ.getTerrain and civ.getBaseTerrain. These throw an error with arguments out of bounds, rather than simply returning nil, which is why I previously asked the user for the number of terrain types.
I wasn't aware of pcall, thanks for linking to that info. I was pretty inconsistent with error handling in the Medieval Millennium code, and it's been on my list of things to research further.

Regarding number of terrain types, I've also encountered the issue of needing scenario-specific code to pass in this value to generic (scenario-independent) functions. I think it would be ideal if TOTPP exposed all of the new COSMIC2 parameters in Lua as a civ.cosmic2.* table, using the same keys as in Rules.txt. Using error handling here is a good solution in the meantime, though.

I have added "discrete events" functionality. This is so that events can be written using a similar structure to the old macro system, where each trigger is its own self contained code, regardless of the location of code for similar triggers. There is a discreteEvents.lua file, but the events could be written in any file.

For example:
...
I think people have commented in the past that the "write all events for a given execution point at the same place" it a sticking point when starting to learn Lua, so this could help solve that.

Also, this should make it easier to offer help writing code, since you can provide an entire self contained event.
This seems like a pretty useful feature. Just curious about the technical details here: are you handling this basically the same way as your legacy event engine? Where each discrete event function gets stored in a table (or, I suppose, a separate table for each trigger), and then processed in a loop in the main block of code that contains all of the triggers?

But I'm a little confused by your example. How does the second definition (or third, etc.) of function discreteEvents.onTurn(turn) avoid overwriting the first one? I could envision a setup where calling discreteEvents.onTurn() and passing in a function would add that function parameter to the correct table, but the syntax you provided confuses me. What am I missing? (I know I could download the library and dig into the details, so please don't feel you have to write a long explanation if that's what I need to be doing. :lol:)
 
Last edited:
For the next version of my Medieval Millennium utility libraries, I'm going to be adding a function syncKnownTileImprovements(tile, tribe) which basically just sets tile.visibleImprovements[tribe] = tile.improvements . You went the route of adding support for charting/uncharting each possible improvement separately, but (unless I missed it) I didn't see a function that would just give a tribe all of the most up-to-date info for the tile. Do you think that would be valuable to add?

Yes, it makes sense to add that. I was just copying the corresponding list of functions for changing actual tile improvements, so a general sync function didn't occur to me. I'll add a chartCity call if applicable to the tile as well.

This seems like a pretty useful feature. Just curious about the technical details here: are you handling this basically the same way as your legacy event engine? Where each discrete event function gets stored in a table (or, I suppose, a separate table for each trigger), and then processed in a loop in the main block of code that contains all of the triggers?

But I'm a little confused by your example. How does the second definition (or third, etc.) of function discreteEvents.onTurn(turn) avoid overwriting the first one? I could envision a setup where calling discreteEvents.onTurn() and passing in a function would add that function parameter to the correct table, but the syntax you provided confuses me. What am I missing? (I know I could download the library and dig into the details, so please don't feel you have to write a long explanation if that's what I need to be doing. :lol:)

I'm using a metatable here. One of the available metatable functions is
Code:
__newindex
which lets you run code if you are assigning a value to a table index, when there isn't already a value for that index. The __newindex function I wrote stores the new event in a different table, and that table is accessed when calling
Code:
discreteEvents.performEventTrigger(arg1,arg2)

Do you think having this syntax is likely to lead to too much confusion?
 
I'm using a metatable here. One of the available metatable functions is
Code:
__newindex
which lets you run code if you are assigning a value to a table index, when there isn't already a value for that index. The __newindex function I wrote stores the new event in a different table, and that table is accessed when calling
Code:
discreteEvents.performEventTrigger(arg1,arg2)
Ah, thanks. I'm vaguely aware of metatables but I've never really dug into the topic or tried to modify any of the default behaviors. That's pretty clever. (This is related to how you set up the scenario template to default to blocking new global variables, is that right?)

Do you think having this syntax is likely to lead to too much confusion?
Well, maybe most people will just follow the instructions and be happy, and I'm the odd one who gets confused by thinking about it too much and trying to actually understand what's happening behind the curtain. :) From my perspective,
Code:
discreteEvents.onTurn (function(turn)
   civ.ui.text("discrete on turn event 1")
end)

discreteEvents.onTurn (function(turn)
   civ.ui.text("discrete on turn event 2")
end)
would be a more familiar syntax, since it directly copies the syntax of the native civ.scen.onTurn() trigger. Then your discreteEvents.onTurn() function would just take the function it receives as a parameter and append it to the different table you referenced, and nothing else would need to change. I guess the annoying thing is that you'd have to create discreteEvents.onTurn(), discreteEvents.onUnitKilled(), etc. for every trigger -- unless you wanted to do something like
Code:
discreteEvents.addEvent ("onTurn", function(turn)
   civ.ui.text("discrete on turn event 1")
end)

I wasn't intending to challenge your approach though -- just understand it a little better. Personally I don't have any concerns if you prefer to leave it as you originally set it up. If you can get feedback from designers who are actively working with the template, that's probably a lot more valuable.
 
Ah, thanks. I'm vaguely aware of metatables but I've never really dug into the topic or tried to modify any of the default behaviors. That's pretty clever. (This is related to how you set up the scenario template to default to blocking new global variables, is that right?)

For quite a while, I also thought of metatables as a sort of mysterious advanced feature, and had vaguely read about them when looking for a way to disable globals (which I eventually did, but a substantial time after I first learned of metatables). Eventually, with some more experience, I came back to them and they were accessible. You can also use them to make custom data types, such as threshold tables.

Well, maybe most people will just follow the instructions and be happy, and I'm the odd one who gets confused by thinking about it too much and trying to actually understand what's happening behind the curtain. :) From my perspective,
Code:
discreteEvents.onTurn (function(turn)
civ.ui.text("discrete on turn event 1")
end)

discreteEvents.onTurn (function(turn)
civ.ui.text("discrete on turn event 2")
end)
would be a more familiar syntax, since it directly copies the syntax of the native civ.scen.onTurn() trigger. Then your discreteEvents.onTurn() function would just take the function it receives as a parameter and append it to the different table you referenced, and nothing else would need to change. I guess the annoying thing is that you'd have to create discreteEvents.onTurn(), discreteEvents.onUnitKilled(), etc. for every trigger -- unless you wanted to do something like

In the scenario template, I hide the civ.scen.onTrigger calls in events.lua (which is a sort of organisation file that brings together all the other files), and have a trigger file that looks something like this in its most basic form:

Code:
local consolidated = require("consolidatedEvents")
local discreteEvents = require("discreteEventsRegistrar")
local afterProdEvents = {}
function afterProdEvents.afterProduction(turn,tribe)
    consolidated.afterProduction(turn,tribe)
    discreteEvents.performAfterProduction(turn,tribe)
   -- civ.ui.text("After Production for turn "..tostring(turn).." and tribe "..tribe.name)
end
return afterProdEvents

Part of the reason for this is that, for example, afterProduction leverages civ.scen.onActivateUnit, but I want to hide that machinery from the scenario designer.

In this sense, the syntax I've created for the discrete events actually mirrors what designers will use elsewhere in the scenario template. My main concern is that copying over a key in discreteEvents doesn't overwrite data, whereas it does for other events. At the moment, however, I'm inclined to simply make an immutable table data type with metatables, and apply it to tables that shouldn't allow overwriting of key values.
 
In this sense, the syntax I've created for the discrete events actually mirrors what designers will use elsewhere in the scenario template. My main concern is that copying over a key in discreteEvents doesn't overwrite data, whereas it does for other events.
Well, that was really my concern too, although I wasn't very clear because I approached it from the standpoint of syntax rather than behavior:
Code:
discreteEvents.onTurn (function(turn)
  civ.ui.text("discrete on turn event 1")
end)
... is clearly a function call, so seeing it repeated later on in the code doesn't seem strange -- just another call to a static function. Whereas
Code:
function discreteEvents.onTurn(turn)
   civ.ui.text("discrete on turn event 1")
end
... appears to be a function definition, and seeing that repeated makes me pause, since (as you said) it looks like it ought to overwrite the previous instance, but doesn't.

But if this syntax is similar to the way designers will interact with other aspects of the scenario template, then it should be fine. Like I said, I'm probably the only one who's going to be curious about the mechanics of this, instead of focusing on the practical applications.
 
Well, that was really my concern too, although I wasn't very clear because I approached it from the standpoint of syntax rather than behavior:
Code:
discreteEvents.onTurn (function(turn)
civ.ui.text("discrete on turn event 1")
end)
... is clearly a function call, so seeing it repeated later on in the code doesn't seem strange -- just another call to a static function. Whereas
Code:
function discreteEvents.onTurn(turn)
civ.ui.text("discrete on turn event 1")
end
... appears to be a function definition, and seeing that repeated makes me pause, since (as you said) it looks like it ought to overwrite the previous instance, but doesn't.

But if this syntax is similar to the way designers will interact with other aspects of the scenario template, then it should be fine. Like I said, I'm probably the only one who's going to be curious about the mechanics of this, instead of focusing on the practical applications.

Maybe someone who has learned Lua a bit more recently than us should weigh in about what they think is more confusing. It totally get your point that
Code:
discreteEvents.onTurn (function(turn)
civ.ui.text("discrete on turn event 1")
end)
makes it clearer about what exactly is going on. On the other hand, the concept of using functions as arguments to other functions might take a bit of getting used to, so this might be considered "easier" to get started with (and it does match the syntax I use elsewhere in the template). However, this method might cause more confusion in the long run, since in most circumstances this syntax would overwrite the previous function, but here it doesn't.
 
For the next version of my Medieval Millennium utility libraries, I'm going to be adding a function syncKnownTileImprovements(tile, tribe) which basically just sets tile.visibleImprovements[tribe] = tile.improvements . You went the route of adding support for charting/uncharting each possible improvement separately, but (unless I missed it) I didn't see a function that would just give a tribe all of the most up-to-date info for the tile. Do you think that would be valuable to add?
I added this function.
Code:
-- gen.chartTruthfully(tile,tribe) --> void
It reveals the tile, charts the existing improvements, and shows the city (with the current size) if one exists.

I implemented a new data type, the 'dataTable'. This functions as a regular table, but with the ability to disable overwriting existing keys, writing to new keys, and accessing absent keys (those that would return nil), throwing an error when you do so.

Code:
-- gen.makeDataTable(inputTable={},tableName="unnamed data table") --> dataTable
-- gen.forbidReplacement(dataTable) --> void
-- gen.allowReplacement(dataTable) --> void
-- gen.forbidNewKeys(dataTable) --> void
-- gen.allowNewKeys(dataTable) --> void
-- gen.forbidNilValueAccess(dataTable) --> void
-- gen.allowNilValueAccess(dataTable) --> void

I also updated the makeObject.lua script to forbid replacement, and forbid nil value access. The nature of the object table means that you probably don't want to overwrite values, and probably don't want to access values that don't exist (there may be exceptions here, so just comment out the line). There are probably other 'data' tables which could benefit from being 'locked down', which is why I'm calling these dataTables.
 
I've been writing an html/JavaScript page to automate the creation of code for canBuildSettings.lua. Most features are included, a notable exception at the moment being "alternateParameters" for having multiple valid conditions for building an item.

The idea behind this is that you run a Lua script to generate a JavaScript data file for your scenario (similar to making an object.lua file), then open the html page in your web browser to play around and generate code, which you can cut and paste into canBuildSettings.lua.

I still have to write the script to make the data file, and double check that the generated code actually works, but you can try out the page here on my testing data.
 
I would like to test this with an actual LUA file, if possible. I've played a little bit around with your page and it looks very promising.
I'm pretty sure it will make creating the canBuildSettings much more easier.
 
I would like to test this with an actual LUA file, if possible. I've played a little bit around with your page and it looks very promising.
I'm pretty sure it will make creating the canBuildSettings much more easier.

Extract ScriptsFiles.zip into the Scripts directory of your scenario. Extract FlagCounterUpdate.zip into LuaCore (this step is supposed to be optional if you don't want to use flags for canBuildSettings, but I didn't test with older files).

Using the Lua Console, run makeObjectJS.lua, which will crate an XXXXXXXXobject.js file in your ToT directory. Move that file to the Scripts directory for your scenario, and rename it to replace the existing object.js file, which is just a testing file. Once this is done, you can open autoCanBuild.html with a web browser, and everything should work. Note, however, that there isn't a way to save your work between sessions.
 

Attachments

I've added new stuff to the template.

There is now a "City Processed" execution point, which runs immediately before a city is processed for production at the start of the turn.

I have also implemented limits to the number of units and buildings that can be produced for canBuildSettings, though I haven't added them to the automatic code generator yet.

The limit takes into account existing production choices. So if you have a limit of 4 engineers, and already have 2, you will only be allowed to have 2 engineers under construction in your cities.

Limits can be both for individual tribes, and for all tribes together ('global' limits). Multiple units can share the same limit as well.
 
Is this all backward compatible? I have a project I'm quite far along with I don't want to destroy, but the canBuildSettings change in particular seems like it would make life much easier than the old way you described in your getting started with lua thread.

As for City Processed, could you give an example of why one would use this as opposed to afterProduction? What other things did you envision when you added this?
 
Is this all backward compatible? I have a project I'm quite far along with I don't want to destroy, but the canBuildSettings change in particular seems like it would make life much easier than the old way you described in your getting started with lua thread.

I think this will get the canBuild update working:

In events.lua, change the civ.scen.onCanBuild lines to this:

Code:
civ.scen.onCanBuild(function(defaultBuildFunction,city,item)
    local ignoreInitialization = true
    if item.id == 0 and civ.isUnitType(item) then
        ignoreInitialization = false
    end
    return canBuildFunctions.customCanBuild(defaultBuildFunction,city,item, ignoreInitialization)
end)

As for City Processed, could you give an example of why one would use this as opposed to afterProduction? What other things did you envision when you added this?

Then, in LuaCore, replace canBuild.lua with the file provided in the attachment. canBuildSettings.lua doesn't need to be changed, but I've included it for the added documentation.

If this doesn't work, send me your files, and I'll put it together.

As for City Processed, could you give an example of why one would use this as opposed to afterProduction? What other things did you envision when you added this?

Suppose you want the library improvement to give +50 science to the research box. You could do it all at once either before or after production, but that would limit the bonus to one technology. By adding the +50 just before each city is processed, the player could discover a technology during their turn and continue to get the bonus for the next technology. If you intend to have a short scenario, this sort of thing could be particularly important.

More than that, it may in some circumstances simply be convenient to think about something happening just before a city is processed, rather than running a loop over all cities in a beforeProduction event.

If anyone would like this for a scenario they're currently working on, they can send me their code and I'll implement it for them.
 

Attachments

I added flag and counter support for canBuild production limits.

Here is the added canBuild documentation for the new additions:
Code:
--      .maxNumberTribe = nil or integer or table with the keys and values specified below
--                          or function(tribe,item) --> number
--          if nil, there is no maximum number of the item that can be built
--          if integer, the number is the maximum of the item that can be built by that tribe
--          if function, the returned value is the number of the item the tribe can build
--          if table, consider these keys and values, rounding down at the end of the calculation
--          .base = integer or nil
--              base number 
--              nil means 0
--          .max = integer or nil
--              maximum number even if calculation would be larger
--              nil means no limit
--          .min = integer or nil
--              minimum number even if calculation would be larger
--              nil means no limit
--          .tribeTotals = nil or {[luaObject or "cities" or "population"] = number or nil}
--              for each luaObject the tribe owns, increment the limit by the corresponding value
--              nil means 0
--              if unitType, increment by the number of units owned
--              if improvement, increment by the number of cities that have that improvement
--              if wonder, increment if the wonder is owned by the tribe
--              if technology, increment if tribe has the technology
--              if "cities", increment for each city owned by the tribe
--              if "population", increment for each population point of the tribe
--
--          .globalTotals = nil or {[luaObject or "cities" or "population"] = number or nil}
--              for each luaObject in the world, increment the limit by the corresponding value
--              nil means 0
--              if unitType, increment by the number of units owned by all players
--              if improvement, increment by the number of cities that have that improvement among all players
--              if wonder, increment if the wonder is owned by any tribe
--              if technology, increment for each tribe that has the technology
--              if "cities", increment for each city in the game
--              if "population", increment for each population point of all cities in the game
--         
--          .activeWondersTribe = nil or  {[wonderObject] = number or nil}
--              if the tribe owns the wonder, and it is not expired, add the increment
--          .activeWondersForeign = nil or  {[wonderObject] = number or nil}
--              if another tribe owns the wonder, and it is not expired, add the increment
--          .discoveredTechs = nil or {[techObject] = number or nil}
--              if the tech is discovered by any tribe, add the increment
--
--      .tribeJointMaxWith  = nil or {[luaObject] = number or nil}
--              each of the tribe's instance of luaObject in the table uses up a portion of the ownership
--              limit for the item in question, with that portion given by the value
--              e.g. unitTypeBuild[object.uSettlers.id] = {maxNumberTribe = 6, 
--                  tribeJointMaxWith = {[object.uEngineers]=2}}
--              and unitTypeBuild[object.uEngineers.id] = {maxNumberTribe = 3,
--                  tribeJointMaxWith = {[object.uSettlers] = 0.5}}
--              Here, the limit is 6 settlers, or 3 engineers, with an engineer using up 2 settler
--              allotments, and a settler using up 0.5 engineer allotments.
--              By default, the item we're checking if we can produce is given a cost of 1,
--              but we can specify a different number instead.  Here is another way to have
--              6 settlers or 3 engineers:
--              e.g. unitTypeBuild[object.uSettlers.id] = {maxNumberTribe = 6, 
--                  tribeJointMaxWith = {[object.uSettlers] = 1, [object.uEngineers]=2}}
--              and unitTypeBuild[object.uEngineers.id] = {maxNumberTribe = 6,
--                  tribeJointMaxWith = {[object.uSettlers] = 1, [object.uEngineers]=2}}
--          .trueFlags = nil or {[flagKey] = number or nil}
--              if the flag associated with flagKey is true, add the value to the production limit
--          .counterValues = nil or {[counterKey] = number or nil}
--              for each counter specified by counterKey, multiply the value of the counter by the
--              number specified, and add that product to the production limit
--             
--
--
--      .maxNumberGlobal = nil or integer or table with the keys and values specified below
--                          or function(tribe,item) --> number
--          if nil, there is no maximum number of the item that can be built
--          if integer, the number is the maximum of the item that can be built by all tribes together
--          if function, the returned value is the number of the item that can be built in the world
--          if table, consider these keys and values, rounding down at the end of the calculation
--          for the maximum number for all tribes together
--          .base = integer or nil
--              base number 
--              nil means 0
--          .max = integer or nil
--              maximum number even if calculation would be larger
--              nil means no limit
--          .min = integer or nil
--              minimum number even if calculation would be larger
--              nil means no limit
--          .globalTotals = nil or {[luaObject or "cities" or "population"] = number or nil}
--              for each luaObject in the world, increment the limit by the corresponding value
--              nil means 0
--              if unitType, increment by the number of units owned by all players
--              if improvement, increment by the number of cities that have that improvement among all players
--              if wonder, increment if the wonder is owned by any tribe
--              if technology, increment for each tribe that has the technology
--              if "cities", increment for each city in the game
--              if "population", increment for each population point of all cities in the game
--         
--          .activeWonders = nil or {[wonderObject] = number or nil}
--              if the wonder is built and it is not expired, add the increment
--          .discoveredTechs = nil or {[techObject] = number or nil}
--              if the tech is discovered by any tribe, add the increment
--          .trueFlags = nil or {[flagKey] = number or nil}
--              if the flag associated with flagKey is true, add the value to the production limit
--          .counterValues = nil or {[counterKey] = number or nil}
--              for each counter specified by counterKey, multiply the value of the counter by the
--              number specified, and add that product to the production limit
--
--      .globalJointMaxWith  = nil or {[luaObject] = number or nil}
--              each instance of luaObject in the table uses up a portion of the ownership
--              limit for the item in question, with that portion given by the value
--              e.g. unitTypeBuild[object.uSettlers.id] = {maxNumberGlobal = 6, 
--                  globalJointMaxWith = {[object.uEngineers]=2}}
--              and unitTypeBuild[object.uEngineers.id] = {maxNumberGlobal = 3,
--                  globalJointMaxWith = {[object.uSettlers] = 0.5}}
--              Here, the limit is 6 settlers, or 3 engineers, with an engineer using up 2 settler
--              allotments, and a settler using up 0.5 engineer allotments.
--              By default, the item we're checking if we can produce is given a cost of 1,
--              but we can specify a different number instead.  Here is another way to have
--              6 settlers or 3 engineers:
--              e.g. unitTypeBuild[object.uSettlers.id] = {maxNumberGlobal = 6, 
--                  globalJointMaxWith = {[object.uSettlers] = 1, [object.uEngineers]=2}}
--              and unitTypeBuild[object.uEngineers.id] = {maxNumberGlobal = 6,
--                  globalJointMaxWith = {[object.uSettlers] = 1, [object.uEngineers]=2}}

Here is some sample code that was used in testing:
Code:
flag.define('TestFlag',false)
counter.define('TestCounter',0)
local unitTypeBuild = {}
unitTypeBuild[object.uSettlers.id] = {
    maxNumberTribe = {
        base=4,
        tribeTotals={
            [object.iAqueduct] = 1,
            [object.uDiplomat] = 1,
            [object.aTrade] = 1,
        },
        globalTotals={
            [object.uTransport] = 1,
            [object.iFactory] = 1,
            [object.wWomensSuffrage] = 1,
            [object.aIndustrialization] = 1,
        },
        activeWondersTribe={
            [object.wGreatWall]=1,
            [object.wLighthouse]=1,
        },
        activeWondersForeign={
            [object.wGreatLibrary]=1,
            [object.wMarcoPolosEmbassy]=1,
        },
    },
    tribeJointMaxWith = {[object.uSettlers] = 1,},
}
unitTypeBuild[object.uPhalanx.id] = {
    maxNumberTribe = {
        base=2,
        trueFlags = {["TestFlag"] = 2},
        counterValues = {["TestCounter"] = 0.5,},
    },
}
unitTypeBuild[object.uMusketeers.id] = {
    maxNumberGlobal = {
        base=0,
        trueFlags = {["TestFlag"] = 2},
        counterValues = {["TestCounter"] = 0.5,},
    },
}
unitTypeBuild[object.uTransport.id] = {
    maxNumberGlobal = {
        base = 1,
        min = 4,
        globalTotals = {[object.aIndustrialization]=1, [object.iFactory] = 1, [object.uArtillery]=1,
            [object.wMarcoPolosEmbassy]=1,["population"]=0.01, ["cities"]=0.1},
        max = 8,
        activeWonders = {[object.wGreatLibrary] = 1,},
        discoveredTechs = {[object.aCommunism] = 1},
    },
    globalJointMaxWith = {[object.uArmor] = 1},
}
 

Attachments

I made a few more changes to the limited ownership mechanic in canBuildSettings. You can now increment based on player tribes, so different tribes can at least have a different production base line without having to use alternate parameters. Some other additions were made as well, including a turn based increment.

The can build code generator now includes limited ownership for each tribe. Global limited ownership still has to be implemented (I imagine there are fewer cases for that, however).
 
I have added a "readRules" module, which reads the information in a rules.txt file (can be named differently), and stores each "entry" (value between commas) as a string in a (nested) table. This could be useful for finding rule information that doesn't have a lua access, or by automatically recording default values of parameters that are modified.

Code:
-- Provides a function to read a Civ II ToT rules text file, and saves each value
-- (text between commas) in a table, for easier reference in game
-- Everything after the first semicolon in a line is ignored
-- Numerical values are saved as strings, tonumber can be used if a numerical
-- value is required
-- Leading and trailing whitespace are removed from each entry
-- Output table is as follows:
-- rulesTable[@SECTION][lineNumber][columnNumber] = columnValueString
-- Note: If the rules have @Section or @section, it is rendered to upper case
-- for storage in the rules table
-- line and column number both start at 0, since id values start at 0.
--
-- readRules.readRules(filePath) --> table
--      creates a table with rules.txt entries based on the file from filePath
-- readRules.loadedRules (table)
--      the result of readRules(scenarioFolder\rules.txt)
--      that is, the rules file that the game will load automatically
-- note: use gen.getScenarioDirectory() to get your scenario's directory
-- in order to read rules files.  E.g.
-- readRules.readRules(gen.getScenarioDirectory().."\\AltRules.txt")

If you are looking to use this module, you will also have to update the General Library, and make a change to events.lua. If anyone is interested in this for a current project, let me know and I'll provide the files. For that matter, let me know if you have need of rules data that is processed into a more usable form, and I can write the code for that, too.
 
I now have a video tutorial for using the code generator for the Can Build Settings. It's rather long, but I go over a decent number of things you can do, and I've divided the video into chapters.


The Can Build functionality has had a couple minor updates, including the completion of the last section of the code generator.

In the canBuildSettings.lua file, there is now a function addBuildConditions

Code:
-- addBuildConditions(item,buildabilityParameters)
--      adds the buildabilityParameters to the appropriate table and index for item.  If the item
--      already has buildabilityParameters registered, the new set of parameters are automatically
--      added to the alternateParameters table
-- addBuildConditions(tableOfItems,buildabilityParameters)
--      for each item in the table, register the buildability parameters as above

The main purpose of this is to get around the awkward "alternateParameters" system if you want to have more than one valid condition for building a unit. Now, you can just call addBuildConditions multiple times for the same itemObject, and the "alternateParameters" key work will be done behind the scenes. This was the easiest way to make the code generator handle multiple valid conditions. The previous method of defining buildability settings still works, for anyone who prefers that.

As a bonus, this function allows easy assignment of the same parameters to multiple items.
 
@Prof. Garfield Sorry, but can you clarify where I should go to download the most recent version of your General Library? When I go here I see a General Library project, but I'm pretty sure that's outdated. Then I also found generalLibary.lua within this project -- it's larger than a version I downloaded on Nov 11, 2020, but it says the last commit was earlier, on Sep 17, 2020. :confused: And in this thread, you mentioned updating the library just this past August. So where I do find that most recent version?

If I need to download a zip of the entire LST to get it, that's fine (except I'm not entirely sure where to get the latest version of that either!), but all I'm really looking for is the General Library file.

You've probably provided this info in multiple threads and posts before, but I never know exactly what to search for. Plus I think your repository configuration has changed over time, so even if I did find an old post with a link, I'd wonder if it was correct. I apologize for bringing this up yet again. Perhaps you could update your signature with one or more links to the "latest and greatest" versions of everything that you've made publicly available, so all someone (like me) has to do is find any post from you and click a link there. That would be super convenient. (There are four links there now, but not to this.)
 
Last edited:
If I need to download a zip of the entire LST to get it, that's fine (except I'm not entirely sure where to get the latest version of that either!), but all I'm really looking for is the General Library file.

Yes, you need to go to the Github Repository and download the entire thing. I left a link to that at the top of the first post to this thread.

Perhaps you could update your signature with one or more links to the "latest and greatest" versions of everything that you've made publicly available, so all someone (like me) has to do is find any post from you and click a link there. That would be super convenient. (There are four links there now, but not to this.)

Good point. I'll make the change.
 
Back
Top Bottom