[ToTPP] Lua Library

Prof. Garfield

Deity
Supporter
Joined
Mar 6, 2004
Messages
4,366
Location
Ontario
I think it would be useful to establish a library of code for scenario makers to use. Hopefully, such a library will help achieve the following objectives:

1. Reduce duplication of effort, since key functionality will only have to be written once.
2. Reduce bugs, since the code that is used will be used more often and scrutinized more closely.
3. Reduce "barrier to entry" for scenario making with lua, by providing key functionality.
4. Provide examples of good code to learn from.

The purpose of this thread, then, is:

1. Submit code for inclusion in the library.
2. Report bugs in existing library code.
3. Ask questions about how to use library code.
4. Make requests for code, especially code likely to be reusable.
5. Discuss possible "best practices" for lua code in the library (and maybe the wider scenario making community).

Here will follow a list of code in the library.

Periodically, library code will be attached to this post.
 
Great idea! There are so many different ways to do things in lua, but it will be great to see everyone take a look at, and refine, code that can be reused by others. Perhaps people could hash things out and you could copy/paste the finished/
"Agreed" product into one of your reserved posts?
 
Perhaps people could hash things out and you could copy/paste the finished/
"Agreed" product into one of your reserved posts?

That's exactly the idea. I was thinking of attaching the code as .lua files rather than copy/paste directly into the post, but the idea is the same.

My thoughts are that we eventually bundle the functions into a small number of .lua files and distribute those, possibly with TOTPP itself. Then, scenario makers use "require" at the top of their events.lua to access these functions. If bugs are found, then the library file has to be replaced, rather than requiring every scenario maker to update the code in their scenario.

What I want is to specify what these library functions should do, and let individual scenario makers rely on that specification. If someone finds that the library code doesn't work as specified, we fix the library code rather than working around it in each scenario. That should mean we can update the library without breaking scenarios. We'll have to see how many scenarios end up being made. "Copy and paste what you need" might end up being easier if not too many people build scenarios.

Since specific needs will vary, my suggestion is that we build general capabilities, and let users build "wrapper functions" around them. So, they would write something like this:

Code:
function mySpecialFunction(myInputOne,myInputTwo)
  return generalFunction(myInputOne, myInputTow, mySpecificNeedsTable)
end

An alternative method is to build fairly basic functionality, and let people modify the code if they need something more complicated. This would mean that people don't write and test super general code that never gets used, but means more bugs when people do need extra functionality. We'll have to see how things turn out.

Topic of discussion: Should we "standardize" keys to trigger events? For example, creating ammo for a unit seems like it might be a fairly common event. Should we "recommend" that all scenarios use the same key for this kind of functionality? If so, what key?
 
I've been referring to these as "k units" in honor of the person who first implemented this. (Grishnach's first name starts with a "k").

It's also unused by the base game and in the home row, making it ideal.
 
K is the order for automated settlers and engineers, so that means that settler type units can't create ammo. Well, they can, but they would then run off and do something you don't want them to do.
 
Definitely not v. That toggles the view mode, which is something that players (like me) would like to do.

Apparently j is some other settler order I don't know about. "y" toggles between covered map and revealed map in cheat mode, but doesn't seem to do anything else. Maybe Tab. I think all keyboards have Tab and that isn't associated with anything.

Maybe we can do 'k'. Depending on when the trigger fires, we might be able to clear the automation order before the settler moves.
 
@Prof. Garfield , I think this is a great idea. It was actually something I was thinking about proposing as well, but you beat me to it! :lol:

My thoughts are that we eventually bundle the functions into a small number of .lua files and distribute those, possibly with TOTPP itself. Then, scenario makers use "require" at the top of their events.lua to access these functions. If bugs are found, then the library file has to be replaced, rather than requiring every scenario maker to update the code in their scenario.

What I want is to specify what these library functions should do, and let individual scenario makers rely on that specification. If someone finds that the library code doesn't work as specified, we fix the library code rather than working around it in each scenario. That should mean we can update the library without breaking scenarios.
What do you think of the idea of a separate "helper" library for each of the major game objects that TOTPP has made available in Lua: city, improvement, tech, tile, tribe, etc.? In my efforts writing events for various ongoing projects (my own, plus those of other members with whom I'm currently collaborating), I've found things like this valuable on multiple occasions. It just slims down the code in my main events.lua file (and the main triggers there) so it's more readable, and focused on the steps that are truly unique to that situation, rather than reinventing the wheel with generic behaviors.

On the one hand, I like the idea of separate libraries because it provides better organization of all the helper functions and the areas of the game they address. A developer with a particular need (for example, taking money away from a tribe while making sure their treasury doesn't fall below zero) can quickly pull up the "util_tribe.lua" file and see if there is a function related to changing money. If all of the functions are in a single huge library, with widely varying degrees of complexity and specificity, that could be a lot more daunting.

On the other hand, more files means more version numbers to manage, and more files to keep up-to-date. I don't think that would be too difficult, but it's hard to anticipate. Maybe my conclusion is that I think there's going to be some level of "administrative overhead" with either approach.

Since specific needs will vary, my suggestion is that we build general capabilities, and let users build "wrapper functions" around them.
...
An alternative method is to build fairly basic functionality, and let people modify the code if they need something more complicated. This would mean that people don't write and test super general code that never gets used, but means more bugs when people do need extra functionality.
I think I would advocate for both approaches, actually! The first being true libraries that are always intended to live outside events.lua and be included via a "require" command, as described/discussed above. These should always contain general code that isn't specific to any one scenario. Most likely, these will contain functions that are pretty simple in nature -- focused on the most common operations and "pain points", or trapping for error conditions that can easily be overlooked.

Then separately, I'd propose a suite of "example" events files that aren't intended to be referenced by events.lua, but rather used as starting points for scenario developers to enhance and tailor for their own needs. This would let someone say, for example, "I want my scenario to include units that are directly linked to city improvements, so that building the improvement creates a unit, and killing the unit removes the improvement." But whereas one developer may want this to work with mobile land units on a single map, and tie special popup text to the death of a unit, another developer may want this to work with immobile air units across three maps and involve terrain changes on specific tiles! Instead of having a library that anticipates or tries to accommodate all of the implementations, I'd like to see example files that give me a basic working version, which I can copy and then freely edit or enhance. Hopefully when someone is going to start building their events.lua file, they would be able to take three or five of these basic example files, out of a library of a dozen or more, and merge them into their own events file to give them a great foundation, so they're not starting at ground zero.

Topic of discussion: Should we "standardize" keys to trigger events? For example, creating ammo for a unit seems like it might be a fairly common event. Should we "recommend" that all scenarios use the same key for this kind of functionality? If so, what key?

Personally, I'm concerned that attempting to "standardize" or "recommend" an approach, even with the best of intentions, could start to come across as heavy-handed. I think it's especially important that whatever we put together encourages creativity and experimentation, rather than stifling it or emphasizing conformity. That being said, if the library contains an example file for ranged units that uses "k" as the key to fire, many developers will just copy that and a de facto standard may arise. But if a developer has a reason why they don't want to use "k", it certainly seems like they should be able to make that decision.

For example, in my own project, I have a goal of building ranged units that the AI can fire! Clearly this would not rely on a key press at all, but much of the other code related to ranged units may still be applicable. The idea of being able to copy a basic implementation, and then customize it, seems ideal to me.

Thanks again for this idea and thread, I think this is a very useful topic to pursue.
 
Last edited:
Personally, I'm concerned that attempting to "standardize" or "recommend" an approach, even with the best of intentions, could start to come across as heavy-handed. I think it's especially important that whatever we put together encourages creativity and experimentation, rather than stifling it or emphasizing conformity. That being said, if the library contains an example file for ranged units that uses "k" as the key to fire, many developers will just copy that and a de facto standard may arise. But if a developer has a reason why they don't want to use "k", it certainly seems like they should be able to make that decision.

Obviously, we can't really force people to use specific keys. I was thinking that it might be easier for players if there were a standard. Imagine if every scenario changed the key bindings for settler functions? Maybe i should be for "industry" (actually a mine) and f should be for "farm" (actually irrigation) in a scenario. The scenario creator might think it is better, but the players would probably be more confused and have to keep referencing the readme to remind themselves what key bindings are used in this scenario. Also, maybe I decide to bind ";" to some function only to find out that some international keyboard doesn't have it or gives it a different number.
 
What do you think of the idea of a separate "helper" library for each of the major game objects that TOTPP has made available in Lua: city, improvement, tech, tile, tribe, etc.? In my efforts writing events for various ongoing projects (my own, plus those of other members with whom I'm currently collaborating), I've found things like this valuable on multiple occasions. It just slims down the code in my main events.lua file (and the main triggers there) so it's more readable, and focused on the steps that are truly unique to that situation, rather than reinventing the wheel with generic behaviors.

Yes, I think a helper library (or libraries) would be a very good idea.

On the one hand, I like the idea of separate libraries because it provides better organization of all the helper functions and the areas of the game they address. A developer with a particular need (for example, taking money away from a tribe while making sure their treasury doesn't fall below zero) can quickly pull up the "util_tribe.lua" file and see if there is a function related to changing money. If all of the functions are in a single huge library, with widely varying degrees of complexity and specificity, that could be a lot more daunting.

On the other hand, more files means more version numbers to manage, and more files to keep up-to-date. I don't think that would be too difficult, but it's hard to anticipate. Maybe my conclusion is that I think there's going to be some level of "administrative overhead" with either approach.

From an organizational perspective, I would like to have multiple libraries. However, if these libraries need to be updated frequently, then that starts to put a burden on the scenario users. It is much easier to replace one file with the current version in the ToTPP folder than to expect people to notice and manage say 10 libraries.

I think there are two ways to treat these libraries, and I'm not sure which is better. Perhaps different libraries should be treated differently.

1. The libraries are treated as part of TOTPP itself, and are "officially released" when @TheNamelessOne releases an update for TOTPP.

2. The libraries are treated as part of an individual scenario. The scenario author includes all the required libraries in the scenario folder, and is responsible for updating the scenario if bugs are discovered.

Option 1. makes it easier for the end user to make sure he's using up to date code, even if he downloaded a particular scenario a while ago. If functionality is in a ToTPP library, it will probably be easier for a scenario designer to use. However, it is harder to guarantee that the user has all the needed code, especially if the author intends to use code that will eventually be in a ToTPP library, but isn't yet.

Option 2. pretty much guarantees that the end user will have all the needed libraries, and the scenario author doesn't have to worry about whether he's using beta library code or released code. On the other hand, updating becomes a major headache for the end user.

I'm leaning towards option 2, at least to start. I figure that significant bugs are likely to be discovered relatively early in playtesting and can therefore be corrected. If a bug in a library doesn't show up in playtesting, it is probably relatively insignificant, and so it isn't a huge problem if it isn't patched. It shouldn't be too hard for an author to keep track of a few libraries during the development and active playtesting.
 
What are libraries and how will they work to help the designer? I've never heard of these.
 
A library is basically a collection of functions that you can use. At the top of your events.lua file, you write a "require" line that tells lua where to get the extra functions. If the library is updated to fix a bug, then replacing the library file will make your events use the new code without having to cut and paste it into events.lua.

If we do it properly, you should be able to use a function in the library the same way you would use civ.hasImprovement(city,improvement).

For example, maybe we find it really convenient to use hasImprovement with the integer id of the improvement type instead of the improvement type itself. Then, instead of writing a function yourself, you write, say

local id= require "identity"

id.hasImprovement(city, integer)

If id.hasImprovement has a bug, identity.lua is fixed, and anyone using the "identity" library updates identity.lua, and the code is fixed. You don't have to mess about finding where you defined id.hasImprovement in your code and fixing it yourself.
 
Obviously, we can't really force people to use specific keys. I was thinking that it might be easier for players if there were a standard. Imagine if every scenario changed the key bindings for settler functions? Maybe i should be for "industry" (actually a mine) and f should be for "farm" (actually irrigation) in a scenario. The scenario creator might think it is better, but the players would probably be more confused and have to keep referencing the readme to remind themselves what key bindings are used in this scenario. Also, maybe I decide to bind ";" to some function only to find out that some international keyboard doesn't have it or gives it a different number.
I think we're 99% in agreement. It might just be the word "standard" that makes me hesitate for a moment... if we used the word "default" instead, I think I'm just more comfortable with that.

To me the distinction comes in when considering the two types of libraries we're discussing: external ones that are referenced, vs. example ones that are copied. In a referenced library, I would prefer to see a keycode passed in as a parameter to a function, rather than hardcoded within the library. Maybe keycodes aren't even the best example, but just advocating for maximum flexibility in code that we don't intend each designer to edit. On the other hand, within an example event file, we obviously would have to pick something to trigger the event, as the default that we think would work well in most cases. That serves to promote consistency within scenarios based on that example, without enforcing it or constraining the developer.

I think there are two ways to treat these libraries, and I'm not sure which is better. Perhaps different libraries should be treated differently.

1. The libraries are treated as part of TOTPP itself, and are "officially released" when @TheNamelessOne releases an update for TOTPP.

2. The libraries are treated as part of an individual scenario. The scenario author includes all the required libraries in the scenario folder, and is responsible for updating the scenario if bugs are discovered.

Option 1. makes it easier for the end user to make sure he's using up to date code, even if he downloaded a particular scenario a while ago. If functionality is in a ToTPP library, it will probably be easier for a scenario designer to use. However, it is harder to guarantee that the user has all the needed code, especially if the author intends to use code that will eventually be in a ToTPP library, but isn't yet.

Option 2. pretty much guarantees that the end user will have all the needed libraries, and the scenario author doesn't have to worry about whether he's using beta library code or released code. On the other hand, updating becomes a major headache for the end user.

I'm leaning towards option 2, at least to start.
Well, I don't think we necessarily want to have library releases aligned with TOTPP updates, at least from a timing perspective. A new version of TOTPP may well result in the need for new or updated libraries, but not necessarily; and we may find value in releasing new library versions even if the TOTPP version doesn't change.

I agree, I mostly like option 2 better. I think it's the best experience for end users if they download a scenario and it's "complete", immediately playable, without retrieving ancillary files separately. And having the scenario designer be responsible for releasing updates is fine, but it's also worth pointing out that it isn't the only option. If a scenario leverages a library and that library is determined to have a bug, the end user could download an updated library file on their own, without waiting for the scenario designer to release an updated scenario version and (at least theoretically!) without requiring any changes in the primary events.lua file.

What are libraries and how will they work to help the designer? I've never heard of these.
civlua is an example of a library that TNO wrote and includes as part of TOTPP. There's a simple "require" line at the top of your events.lua, which lets you call any function in that external file just by prefacing the function name with "civlua.". The "reference" libraries I'm envisioning would work the same way. The "example" files I'm proposing would be different, though -- those would provide code you would copy and paste directly into your events.lua file, and then customize if necessary.
(EDIT: Prof. Garfield's example above goes into more and better detail!)
 
Last edited:
For example, in my own project, I have a goal of building ranged units that the AI can fire! Clearly this would not rely on a key press at all, but much of the other code related to ranged units may still be applicable. The idea of being able to copy a basic implementation, and then customize it, seems ideal to me.

Knighttime, did you find yet a solution for the AI to build and fire ranged units?
 
Prof. Garfield, thank you very much for doing this library. :thanx:

As it seems, an 'onMove' function is missing yet, is it possible to have a change sound.wav function in combination with the onActivateUnit function to have different sounds replacing the simple 'clack,clack' sound of the Movpiece wav?

Per example:

onActivateUnit (group Infantry) replace sound Movpiece.wav by Foot.wav
onActivateUnit (group Tank) replace sound Movpiece.wav by Tank.wav

and so on?
 
Prof. Garfield, thank you very much for doing this library. :thanx:

As it seems, an 'onMove' function is missing yet, is it possible to have a change sound.wav function in combination with the onActivateUnit function to have different sounds replacing the simple 'clack,clack' sound of the Movpiece wav?

Per example:

onActivateUnit (group Infantry) replace sound Movpiece.wav by Foot.wav
onActivateUnit (group Tank) replace sound Movpiece.wav by Tank.wav

and so on?

I had a quick look at the available "basic" TOTPP functions, and while there is an option to play sound when an event triggers, I didn't see an option to change the filename civ 2 looks for when playing an already coded in game sound. To achieve this, lua would have to be re-naming files in the sound directory, which, even if possible to do from the TOTPP lua interpreter, I wouldn't recommend for security reasons.

You could disable the "clack,clack" sound, and have a sound play when the unit activates and/or when a key is pressed while the unit is active.
 
Prof. Garfield, thank you very much for your answer. :)
 
Top Bottom