[TOTPP] Lua Scenario Template

In Napoleon, we also use 'Tab' as the context-sensitive help key. 'Backspace' along with the number keys '1' through '6' provide various status reports, or allow you to access static help/info screens. (As you noted in regards to 'y', though, number keys have other effects in cheat mode.)

In the current major project that I'm working on, and hope to release shortly, I chose to use 'Backspace' as the context-sensitive help key, displaying info about the active unit and/or tile. 'Tab' is a "menu" key that brings up a dialog box allowing you to pick from a list of options which are not tied to the active unit. Most of these are status reports of one type or another, but a few allow you to initiate in-game actions that are not unit-specific. I guess I took this approach because I'm familiar with several other apps where 'Tab' is used to access an auxiliary menu, and that seemed the most natural or instinctive choice.

So overall, I'd say I like having both 'Tab' and 'Backspace' be available for help and supplemental functionality, and I'd be in favor of your proposal of using 'k' and 'u' as munitions keys -- which, if they're providing related functionality, have the added advantage of being close together on the keyboard.
 
Last edited:
I suppose this is probably better-placed here than in the Boudicca thread.

I've made more progress on the Designer Readme. I figured we'd need it, and it also gives me a useful reminder of what events I need to write, because I need to have an example for everything. I have "the high level" stuff done I think, but need specific usage examples still (kind of like I did with the object and parameter settings). I'm not wed to this structure necessarily but I think it makes sense (high level of what things do, then nitty gritty of how to do it with a matching example from the scenario for people to look to/copy/paste).

@Prof. Garfield if you would be so kind as to review page 9 and specifically the unit defeated/other situations and opine if there is a better way to describe those, I'd appreciate it. I'm sure you had specific usages in mind for the group but I don't fully understand them and could use a small paragraph that is better than mine for each.
 

Attachments

IMPORTANT: This will not fire unless there is a unit that can activate, so it may be inappropriate for sparsely populated scenarios, or ones where you feel all units are likely to be fortified. I’ve been befuddled more than once by events that would not fire because I was testing them in situations where all my units were on sentry.

As part of the template, I've added in functionality to avoid these situations in my Event Tools.

Code:
eventTools.guaranteeUnitActivation(tribe)

This makes sure there will be an active unit for the tribe. This is run afterProduction for each tribe, and if the guaranteed activating unit is destroyed, another one is created. The parameters file sets the location where these guaranteeing units will be created, and what unit type they are. For testing, I chose the North East side of the map, and the arrows unit.

unitDefeated–This will run if a unit is defeated in combat, or via events (for example via the CombatResolution module discussed above.

You probably shouldn't destroy units in the combat resolution function. For the unit you want to lose, set its hitpoints to 0 and then return false. A better example would be a unit that is defeated by a reaction event.

unitDeath–this fires when a unit dies, in or out of combat, just so long as it isn’t replaced by something else.

unitDeleted–this happens if a unit is deleted via combat or some other event (but not via disbanding).

It is probably worth mentioning that the unitDefeated, unitDeath, and unitDeleted functions exist in part to tie into other events, especially pre-made modules. For example, gen.defeatUnit is used in the reaction module, so putting code in the unitDefeated trigger allows that code to be run if a unit is destroyed in the reaction module. Otherwise, the player would have to "open up" the reaction module if they want stuff to happen when a unit is killed by a reaction.

Also FYI, I'm thinking it might make sense to add a unitDeathOutsideCombat function. It might make sense to want different events to occur if a unit is killed in combat versus if the unit dies of thirst in the desert, and that isn't straightforward to do with the current setup (though I think it would be possible).

3Note that we prefix each unit with a “u,” and that each unit corresponds to its UnitType integer (where settlers in the base game, for example is unitType 0, and the phalanx is unitTyp3 3). The “u” prefix is simply done for organization’s sake –it isn’t absolutely necessary –but it is helpful if you were to have, for example, a technology, unit, and improvement that all shared the same name.

It might be worth mentioning that the prefix helps when trying to use your text editor's autocomplete, since it filters out the other stuff. In fact, that was my original reason for "recommending" the prefixes in the first place.

Code:
..tostring(loser.type.name)..
FYI, loser.type.name is already a string, so you don't need to use tostring for that. It doesn't hurt anything, but it does make the lines longer.

Code:
onSchism–Out of thebox, prevents a civilization from splitting in two when its capital is taken. No modification is necessary unless you want the split to occur.

This is worth double checking, but I think this could be used to trigger an event any time a schism might occur, even if the schism doesn't actually happen.
 
Can we expand on this function please? It seems to be more limited than in the base game in that it only recognizes "defender" and not "conqueror" as well. Having different events occur depending on who conquers the city is quite important (consider if the Allies reach Berlin before the Russians).

Code:
function triggerEvents.onCityTaken(city,defender)
    context[getContext()]["onCityTaken"](city,defender)
    universal["onCityTaken"](city,defender)
    legacy.doCityTakenEvents(city,defender)
 
Can we expand on this function please? It seems to be more limited than in the base game in that it only recognizes "defender" and not "conqueror" as well. Having different events occur depending on who conquers the city is quite important (consider if the Allies reach Berlin before the Russians).

I think you would use city.owner to get the conqueror. If you think it would be helpful, I can change the code to provide an 'attacker' argument, and just feed it city.owner in the events.lua file, hiding the details from the end user.
 
I just tested it and it works - a bit counterintuitive but something that we could explain in the designer guide too. When I think city.owner I'm thinking the defender because it's in the onCityTaken - well, it has to be taken from the owner, right?

I guess I'd see what others think if they wish to speak up...
 
I've attached the current progress of the documentation for the text module. Let me know if you think there are any problems with what I have thus far, since I'll probably do more in this style.

I just tested it and it works - a bit counterintuitive but something that we could explain in the designer guide too. When I think city.owner I'm thinking the defender because it's in the onCityTaken - well, it has to be taken from the owner, right?

I guess I'd see what others think if they wish to speak up...

The change isn't that hard to make, and I can't really see any drawbacks to doing it.
 

Attachments

I don't think there are any issues with it at all - it's quite helpful actually. I'd press on.
 
I noticed a minor bug in munition settings:

local function secondaryAttack(generatingUnit)
return munitions.spawnUnit(generatingUnit,primaryAttackTable,gen.getActivationFunction())
end
munitionSettings.secondaryAttack = secondaryAttack

Should be

local function secondaryAttack(generatingUnit)
return munitions.spawnUnit(generatingUnit,secondaryAttackTable,gen.getActivationFunction())
end
munitionSettings.secondaryAttack = secondaryAttack

I fixed it in my copy for Boudicca but if you're keeping a master copy for template distribution it should be fixed as well.
 
The negotiation settings is not very intuitive for me. What do I need to do to simply change things so no one can negotiate (which I'd assume many scenarios would want)? Is this something that must be done via the legacy engine?


Code:
local legacy = require("legacyEventEngine")
local negotiationSettings = {}
function negotiationSettings.negotiation(talker,listener)
    legacy.doNegotiationEvents(talker,listener)
    return legacy.canNegotiate(talker,listener)
    --return true
end
return negotiationSettings
 
The negotiation settings is not very intuitive for me. What do I need to do to simply change things so no one can negotiate (which I'd assume many scenarios would want)? Is this something that must be done via the legacy engine?

Just return false, instead of returning legacy.canNegotiate(talker,listener). I should probably review that. Either the legacy events aren't working for forbidding diplomacy, or there is a problem with the lua portion.

I fixed it in my copy for Boudicca but if you're keeping a master copy for template distribution it should be fixed as well.

My current plan is to take the Boudicca scenario, and remove the Boudicca specific stuff. Otherwise, I'm likely to miss something.
 
I believe you mentioned that the way you have things situated, the events know to look at various folders for each other. Does this mean that if I had a reason to move a file into, say, the main scenario folder, I could? On another project I'm tinkering with I have multiple rules files which means multiple attack values for units. To get attack bonus settings working properly, I need to have an attackBonusSettings for each rules file (as I believe I need to define the attack rating within that file).

Would it hurt anything to put the attackBonusSettings in the main scenario folder so that I can easily write a batch file to swap them? I am not sure how to write the file to search the folder to swap them/don't want to break something.
 
Yes, you could lua files into the main scenario folder, or any of LuaCore, LuaParameterFiles, LuaRulesEvents, LuaTriggerEvents. You can't put them into any subfolders, unless you specify that folder in your use of the require function. The Folders are mostly for organization. The only stuff where location matters are the universal and context trigger files. Those must stay where they are (though I suppose you could move the entire folders if you wanted to for some reason).
 
In the case below with the delayed action, is "turn" seeking a specific turn, or a number of turns after the trigger? I'm hoping it's the latter as I think it would allow design to become more fluid. You might have a trigger that you want to grant a specific thing "5 turns from the trigger" but that could mean "turn 10, 15, 20, 183 etc."

Is this what this aims to do or is that incorrect?

Code:
--  If you want an event to take place at some point further into the future,
--  in the onTurn event phase for that turn, use the following function
--
--  delayedAction.doInFuture(delayKey,argTable,turn)
--
--      delayKey    is the string that you've associated with the function you
--                  wish to delay
--
--      argTable    is the table that should be provided as an argument
--
--      turn        is the turn you wish the event to take place
 
In the case below with the delayed action, is "turn" seeking a specific turn, or a number of turns after the trigger? I'm hoping it's the latter as I think it would allow design to become more fluid. You might have a trigger that you want to grant a specific thing "5 turns from the trigger" but that could mean "turn 10, 15, 20, 183 etc."
It shall be any of these two on choice if I understand well the description.

Either the turn numbered x, then allocate x.
Either x turn after the actual turn, then allocate civ.getTurn() + x

;)
 
Last edited:
You'll note that after the 'object." there is a little prefix of c, t, x, and u. These are just naming conventions for the object file. It would make sense to standardize them, so I've adopted what Garfield gave me (I added x for text) but they could be anything that makes sense to you.

c = city
t = tribe
x = text
u = unit
Trying to stick to that.
I also used "i" for improvement object. I saw you (all) used a function to return by name comparing a string each time. Is there a purpose for this ?
 
I saw you (all) used a function to return by name comparing a string each time. Is there a purpose for this ?

I'm not sure what you mean. Can you post a relevant portion of code that shows what you are asking about?
 
I'm not sure what you mean. Can you post a relevant portion of code that shows what you are asking about?
Well, in Napoleon for exemple, the improvements are looked after by a function findImprovementByName(string), which then look each time in all 40 improvements for such a named improvement to return it.
Didn't put my head in recent codes howether, like OTR.

I wondered if this system using strings as reference had an advantage I may miss (nooby with lua after all) in compare with an early allocation like made with units, tribes and cities ?
 
I like the integer based system because it allows players to tweak city names, for example, if they'd like. In OTR, you can rename your cities whatever you wish. If we used a string, you could only rename cities to something that is also referenced, or the event wouldn't work.

Also, if you mess around with multiple rules files, I'd argue that integers allow more flexibility.
 
Back
Top Bottom