[ToTPP] Lua Library

Lua Event Macro Specification

GOAL: Provide a template events.lua file and associated "library" files such that all a scenario maker has to do is fill in tables in events.lua in order to achieve functionality. This library should provide similar functionality to the "Test of Time Macro Language," but the functionality might not be exactly the same. This will also seek to expand "macro" functionality to take advantage of features enabled by Lua.

Specification overview:

An event will be defined by a table that has "trigger conditions" and "action details" described by entries in a table. Not all keys will be used for all events (in fact, most events will probably only use a small part of available conditions and actions), so events should always do sensible things when an event key is missing.

Events will be divided into "categories" based on when they are checked for triggering.

Most event categories will be based on the "civ.scen" function that triggers them

https://forums.civfanatics.com/threads/totpp-lua-function-reference.557527/#civ.scen

but some triggers might be broken up into multiple categories if that makes more sense (e.g. it might be reasonable to split key press events into several categories).

The first order of business is to specify the functionality that the "Lua Macro" will have. It makes sense to split the specification of trigger checking into the different "categories," but we shouldn't duplicate the "action details" effort for every category.

So we have two classes of specifications to make:
1. Trigger Checks for each desired Category
2. Action Details for each Action Type (display text, create unit, etc.)

Which leaves the question of where to specify Action Details that only apply to a single category, such as "create unit at location of combat." I'm not sure of the answer to that.

Here is my proposed specification for Unit killed event triggers:

Spoiler Unit Killed Event Trigger Specification :

Each unit killed event will be specified by its own table, with the following possible keys

======================================================================================
Trigger condition keys

defeatedUnit
Possible Values
absent (i.e. nil), true, --> any defeated unit fires the trigger
integer --> i.d. number of the unittype that fires the trigger
unittype (lua object) --> lua unittype object of the unit that fires the trigger
string --> name of the unittype that fires the trigger
table of integers, unittypes, strings --> everything in table fires the trigger

killingUnit
Possible Values
absent (i.e. nil), true ==>> any attacking unit fires the trigger
integer ==>> i.d. number of the unittype that fires the trigger
unittype (lua object) ==>> lua unittype object of the unit that fires the trigger
string ==>> name of the unittype that fires the trigger
table of integers, unittypes, strings ==>> everything in table fires the trigger

defeatedDefenderOnly
Possible Values
absent ==>> same as false
true ==>> The defeated unit must have been the defender in combat
false ==>> The defeated unit can be either attacker or defender

defeatedAttackerOnly
Possible Values
absent ==> same as false
true ==> The defeated unit must have been the attacker in combat
false ==> The defeated unit can be either attacker or defender

defeatedUnitTribe
Possible Values
absent (i.e. nil), true ==> The defeated unit can be from any tribe
integer ==> defeated unit must be from tribe with this id number
tribe (lua object)==> tribe object of the tribe that must be defeated
string ==> defeated unit's tribe must have this name
table of integers, tribes, strings ==> everything in table fires the trigger

killingUnitTribe
Possible Values
absent (i.e. nil), true ==> The killing unit can be from any tribe
integer ==> killing unit must be from tribe with this id number
tribe (lua object) ==> tribe object of the tribe that must be killing
string ==> killing unit's tribe must have this name
table of integers, tribes, strings ==> everything in table fires the trigger

beforeTurn
Possible Values
absent (i.e. nil), true ==> event can be triggered until end of scenario
integer ==> event can only be triggered on this turn or before it

afterTurn
Possible Values
absent (i.e. nil), true ==>event can be triggered since the start of the scenario
integer ==> event can only be triggered on this turn or later

justOnce
Possible Values
absent(i.e. nil) ==> same as false
true ==> this event can happen only once (separate tribes need separate events)
false ==> this event happens every time the conditions are met

oncePerTurn
Possible Values
absent (i.e. nil) ==> same as false
true ==> this event can happen only once per turn (separate tribes need separate events)
false ==> this event happens every time the conditions are met

flagTable
Possible Values
absent (i.e. nil) ==> no flags to be checked
table ==> This is the table where flags for this event are stored

allFlags
let thisFlagTable be the table specified by the flagTable parameter
Possible Values
absent (i.e. nil) ==> nothing to check, so event happens as other conditions dictate
string ==> event happens if thisFlagTable[string] is true
table of strings ==> event happens if thisFlagTable[stringInAllFlagsTable] is true for all strings in the table

map
Possible Values
absent (i.e. nil), true ==> The battle can take place on any map
0,1,2,3 ==> number of the map the battle can take place on
table of 0,1,2,3 ==> battle can take place on any map specified

locationAttacker
Possible Values
absent (i.e. nil) ==> same as false
true ==> location must be true for the tile the attack was made from
false ==> battle location can be true for either attacker's tile or defender's tile

locationDefender
Possible Values
absent (i.e. nil) ==> same as false
true ==> location must be true for the defender's tile
false ==> battle location can be true for either attacker's tile or defender's tile

location
Possible Values
absent (i.e. nil) ==> The battle can take place on any tile (subject to map restriction)
tile (lua object) ==> The battle must take place on this tile
{integer X,integer Y} ==> The battle must be on tile (X,Y), but for any allowable map
{integer X, integer Y, integer Z} ==> battle must take place on tile (X,Y,Z)
{integer xMin, integer xMax, integer yMin, integer yMax}
==> a battle tile must satisfy xMin <= x <= xMax and yMin <= y <= yMax
table of (other specification types) ==> battle must satisfy one of the specified locations
function (attackerTile (lua object),defenderTile (lua object)) --> boolean
==> if true, location conditions satisfied, if false, they are not. Overrides locationAttacker and locationDefender

defeatedUnitHomeCity
absent (i.e. nil), true ==> the unit can have any home city
tile (lua object) ==> the unit's home city must be on this tile
integer ==> the unit's home city must have this id number
{integer X, integer Y} ==> the unit's home city must be at (X,Y) but on any map
{integer X, integer Y, integer Z} ==> the unit's home city is at tile (X,Y,Z)
string ==> the unit's home city must have exactly this name
false ==> the unit must have no home city
table of (other specification types)


killingUnitHomeCity
absent (i.e. nil), true ==> the unit can have any home city
tile (lua object) ==> the unit's home city must be on this tile
integer ==> the unit's home city must have this id number
{integer X, integer Y} ==> the unit's home city must be at (X,Y) but on any map
{integer X, integer Y, integer Z} ==> the unit's home city is at tile (X,Y,Z)
string ==> the unit's home city must have exactly this name
false ==> the unit must have no home city
table of (other specification types)

defendingUnitHomeCity
absent (i.e. nil), true ==> the unit can have any home city
tile (lua object) ==> the unit's home city must be on this tile
integer ==> the unit's home city must have this id number
{integer X, integer Y} ==> the unit's home city must be at (X,Y) but on any map
{integer X, integer Y, integer Z} ==> the unit's home city is at tile (X,Y,Z)
string ==> the unit's home city must have exactly this name
false ==> the unit must have no home city
table of (other specification types)

attackingUnitHomeCity
absent (i.e. nil), true ==> the unit can have any home city
tile (lua object) ==> the unit's home city must be on this tile
integer ==> the unit's home city must have this id number
{integer X, integer Y} ==> the unit's home city must be at (X,Y) but on any map
{integer X, integer Y, integer Z} ==> the unit's home city is at tile (X,Y,Z)
string ==> the unit's home city must have exactly this name
false ==> the unit must have no home city
table of (other specification types)


specialCondition
Possible Values
absent (i.e. nil) ==> no special conditions
function(killingUnit,defeatedUnit)--> boolean ==> trigger only works if this function returns true




I would like the input both of those likely to make (or convert to lua) scenarios as well as those willing to help write this library. At the moment I'm assuming that the Lua Macro Library will have similar functionality to the ToT Macro Language, but might differ on exact details (since it may be more straightforward to program something slightly different). If any scenario maker/converter feels that the idiosyncrasies of the ToT Macro Language are important, please speak up, specify exactly what the idiosyncrasies are, and preferably cite the use case.

I would like everyone interested to help design these specifications, so that they reflect what people actually want, not what I think they might want (and my "lua time" at the moment is focused on Over the Reich anyway).

What kind of functionality should be included? At the moment I'm thinking functionality likely to apply to a wide range of scenarios. For example, I won't be including the carrier and radar functionality I'm making for Over the Reich, since they only apply to modern scenarios, but I'll consider including my 'help key' functionality, since a lot of people might want that (but it might be better to leave it as a stand alone library). 'K'-Units functionality will probably be included, since that's applicable to a lot of scenarios.
 
@techumseh , @tootall_2012 , @McMonkey , @CurtSibling , @Patine , @Tanelorn

As you're all actively building scenarios I'd highly suggest reading Prof. Garfield's post above and considering this. Trust me, lua can be exceptionally frustrating, but I do honestly believe that once a few more examples of functional scenarios are out there, we all should be able to start creating the scenarios we've always dreamed of.

As to your request @Prof. Garfield , one thing I would caution is against saying things like "they only apply to modern scenarios" - scenario makers are by definition creative people and I can assure you that any thing we have developed can be used elsewhere. With that said, just going by your unit killed specification it looks like this is a ton of work so it absolutely makes sense to focus on the more-likely-broad stuff first, perhaps with a word document that highlights things that aren't in the library, but have been done in other scenarios and showing where to find them so the designer can research and add these on their own? One of the great things that the graphic artists have done is make compilations of their work which has significantly eased finding it and using it. We should probably do the same thing for the work of the lua designers.

As to idiosyncrasies, one that I've noticed that is different (default) in lua is that terrain changes don't automatically destroy the first unit, as they do with macro.txt. When you get to the create terrain action, I'd suggest allowing the option of "destroy all units at location, destroy first unit at location, destroy some units at location, destroy no units at location, random units destroyed at this location" to retain (if desired) macro.txt results but also offer significantly broader array.

As for functionality and what I think would be used in numerous scenarios:

-The broader use of state vs. flags more fully explained/easy to grasp, utilize, and enhance (most who moved to TOT --which wasn't easy in itself-- did so for the use of flags).

-Expansion of diplomacy - even though we haven't touched on in Over the Reich - the dialogue text boxes, the ability to transfer cities, units, funds, between computer or players at the click of a button in exchange for something else, etc. I think this could apply universally and would be very welcome.

-City Founded/CityCanBuild - I think that what we did in Over the Reich to expand on @Grishnach 's work in Caesar to force cities down certain "production paths" by tying certain improvements and units to other improvements (so that only certain cities can produce airplanes, and only other cities can produce naval units) will likely be utilized in many more scenarios going forward. Ways to expand the specialization of cities in general. Perhaps tying to what resources are near (especially since ToTPP allows the specific placement of the resources, effectively tripling the types of terrain on a single-map scenario).

-The Battle of the Atlantic/Need to keep Germans in France mechanism would be useful in numerous scenarios. Our "Battle of the Atlantic" is another man's "Revolt Risk" or "Supply Line." I think you'll find other scenario designers shrinking our "box" to a "line" and not allowing any enemy units on that line to allow resupply to certain areas. Supply lines have been wanted forever. Here they are.

-I do think @Knighttime 's strategic bombing mechanism is also useful. We're using single tile cities here but what is to stop a designer for a scenario on a smaller scale to build large, expansive cities over many tiles and have "districts" that need to be defended? Linking improvements to units and vice versa is useful.

I'm not sure if this is what you're looking for so I'll stop here but hopefully others will chime in. I'm interested to see what other people think. I think this library is a great idea and I'm very excited to see what my peers come up with for their first lua scenarios.
 
As to your request @Prof. Garfield , one thing I would caution is against saying things like "they only apply to modern scenarios" - scenario makers are by definition creative people and I can assure you that any thing we have developed can be used elsewhere. With that said, just going by your unit killed specification it looks like this is a ton of work so it absolutely makes sense to focus on the more-likely-broad stuff first, perhaps with a word document that highlights things that aren't in the library, but have been done in other scenarios and showing where to find them so the designer can research and add these on their own? One of the great things that the graphic artists have done is make compilations of their work which has significantly eased finding it and using it. We should probably do the same thing for the work of the lua designers.

It was late when I wrote this and I was trying to convey two things at once, so I don't think I did it very well. The idea for the "Lua Macro Library" was for it to be in basically every scenario that uses Lua events from here on, since the template would require the various files. For that reason, I wanted to narrow the focus to things "popular" enough to merit a file with that code going into every scenario distributed. I've had an idea, however, that we can use a lua script to make an "empty" events file that has the necessary "links" to the functionality in each file, so this is less important. Part of a new library submission will be to update the generating script.

The second thing I wanted to convey is that I don't want to have a lot of code written that is not going to be used very often. This is mostly to help make this project achievable in a reasonable period of time, but also to save lua coding time overall. Part of an event specification can be a user defined function (e.g. the specailCondition in my sample specification), which will allow a lot of flexibility for the end user. However the point of the library is to make it unnecessary for the scenario creator to write complicated functions.

I'm thinking of a rule of thumb for inclusion as: If it looks useful for three scenarios and can be specified with one or two parameters, then it is a reasonable candidate for inclusion. We'll have to see what other programmers and creators think.

As to idiosyncrasies, one that I've noticed that is different (default) in lua is that terrain changes don't automatically destroy the first unit, as they do with macro.txt. When you get to the create terrain action, I'd suggest allowing the option of "destroy all units at location, destroy first unit at location, destroy some units at location, destroy no units at location, random units destroyed at this location" to retain (if desired) macro.txt results but also offer significantly broader array.

This is the kind of information/idea I'm looking for with regards to idiosyncrasies. I wouldn't have known that was important.

I'm not sure if this is what you're looking for so I'll stop here but hopefully others will chime in. I'm interested to see what other people think. I think this library is a great idea and I'm very excited to see what my peers come up with for their first lua scenarios.

In part, yes. The end goal is a set of documents that will tell programmers the functionality they need to produce and creators how to use the functionality provided (sort of like what I did for event trigger specification). Once that exists, it can be divided into manageable chunks that programmers create when they have some time.
 
Here's a small project that should be done. Can someone make a lua file with a table of all the different key codes?

e.g.
keyCode = {}
keyCode.backspace = 214

or something similar.

Perhaps have a comment with it that mentions an in game function (if any).
 
If you can point me towards a website that has all of the keycodes, I can certainly handle something like this.
 
If you can point me towards a website that has all of the keycodes, I can certainly handle something like this.

I was assuming that someone would start a blank scenario and do something like

civ.scen.onKeyPress(function(keyID) civ.ui.text(tostring(keyID)) end)

and test each key.
 
Unfortunately with my present knowledge I´m not able to do such a task. :blush:
 
There are several different forms that a Lua "library" could take. In my mind, I see two key forms, both of which have a valid place and would be useful in different ways:

1. A set of functions (contained within one .lua file, or perhaps several) that are intended to be referenced (called) by a scenario's events, but not customized for that scenario in any way. In other words, all scenarios that use such a library could theoretically use the same file(s). These functions would be a great way to handle single, specific tasks such as "add a road to a tile" or "remove money from a tribe's treasury while protecting against a negative balance". They would provide a more convenient interface to changes that involve understanding bitmasks, bitwise operators, and things of that nature.

2. A set of modules that provide general functionality, but that aren't intended to support every nuance that a designer might imagine. Rather, these modules are intended to be building blocks that designers would combine together in unique ways to create/support a scenario, tweaking or perhaps reshaping them as necessary. However, by providing a general starting point of working code focused around a single concept, they prevent each designer from needing to either (a) reinvent the wheel from scratch, or (b) search through a giant event file from another scenario to find code relevant to a specific feature.

As an example of the second of those, you're welcome to check out a Supply Lines module that I just posted over in the Scenario Creation forum. Hopefully this is something the community finds useful and that some designer is able to incorporate into a scenario. Thanks!
 
Top Bottom