[TOTPP] Lua Scenario Template

A few minor changes to the template.

Contributions

A couple more functions from @Pablostuka . I modified gen.getBearing to work on round maps and to accept units, cities, and coordinate triples.

Code:
-- gen.getBearing(compassPoint,compassCentre) --> string | Inspired by Pablostuka
-- Returns one of "N","S","NW","NE","SW","SE" based on the locations
-- of the compassPoint and compassCentre
-- e.g. gen.getBearing(Madrid,Paris) --> SW
--      Madrid is South-West of Paris
--      We're finding the location of Madrid relative to Paris, hence
--      Paris is at the compass centre and we're looking for the
--      bearing of the compass point in the direction of Madrid
--      gen.getBearing(Paris,Madrid) --> NE
--      Paris is North-East of Madrid
-- compassPoint and compassCentre can be units, cities, or tiles

-- text.initCap(string) --> string | By Pablostuka
-- returns the first letter of each word in uppercase, all other letters in lowercase

Feature Update

munitions.lua no longer requires air units to be left with 1 movement point when generating a munition. The code will deduct range if applicable.

Behind The Scenes

Everything relating to the eventTools.lua module has been commented out (not yet removed on the off chance it has to be restored). All it ever did was guarantee that a unit will be around to enable events dependent on onActivateUnit. Improvements in both TOTPP and the Template have rendered this obsolete.

delayedAction.doInFuture now generates an error if you schedule the event in the past.
 
@Prof. Garfield, in my scenario I'm using diplomacy restrictions for the player. During my playtests I noticed that the AI never get into war to each other, even not if I use the codes 'diplomacy.war...'.
Would it be possible, that the AI still gets into war with each other or at least when I set it via events?
 
During my playtests I noticed that the AI never get into war to each other, even not if I use the codes 'diplomacy.war...'.
Would it be possible, that the AI still gets into war with each other or at least when I set it via events?
Are their treaties being reset to peace, or are they just not attacking each other? You could try following up diplomacy.setWar with

makeAggression
civ.makeAggression(who, whom) -> void

Cancels any peace treaties between tribe `who` and tribe `whom`, and make `who` declare war on `whom`.

Perhaps you could send me the current version of the scenario, along with a save shortly before you expect a war.
 
Instead of 'civ.makeAggression(who, whom) -> void' I used 'diplomacy.clearPeaceTreaty(tribe1,tribe2)'. I changed this and now the AI starts war against each other after the event was fired.
Many thanks Prof. :thumbsup:
 
Instead of 'civ.makeAggression(who, whom) -> void' I used 'diplomacy.clearPeaceTreaty(tribe1,tribe2)'. I changed this and now the AI starts war against each other after the event was fired.
All "clearPeaceTreaty" does is make it so the tribes don't have a peace treaty. It doesn't make them at war. In fact, if diplomacy restrictions are in place, the tribes can't attack each other, since they have contact and are not at war. If you want to declare war between tribes with disabled treaty changes, make sure to change their treaties with diplomacy.setWar/diplomacy.clearPeaceTreaty/etc. so they aren't reverted.
 
That`s good to know. I only restricted diplomacy for the player controlled civs. I didn`t know that this would also affect negotiations between AI controlled civs. This would explain why I never saw any kind of warlike actions from the AI controlled civs.
 
That`s good to know. I only restricted diplomacy for the player controlled civs. I didn`t know that this would also affect negotiations between AI controlled civs. This would explain why I never saw any kind of warlike actions from the AI controlled civs.
Hi @civ2units ,if it helps you I have this on both onTribeTurnBegin() and onTribeTurnEnd() to ensure my diplomacy doesn't change. I'm forcing aggressions and vendettas/changes of attitude to ensure the level of "hate" doesn't change. You can change the tribes to whatever situation meets your scenario needs:

SQL:
-- ClearDiplomacy
helper.resetDiplomacyStatus()

where:

SQL:
function helper.resetDiplomacyStatus()    
   
    -- Reset diplomacy status for all tribes
    -- To be run on each tribe's turn to ensure the AI does not negotiate
    -- I need to be able to use the Foreign Minister screen so can't use diplomacy.setEventTreatiesOnly as this
    -- disables the entire screen in ToT. See https://forums.civfanatics.com/threads/small-questions-thread.660320/page-4#post-16326130
   
    -- Basic reset of war, vendetta and attitudes so they don't negotiate
    civ.makeAggression(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.setWar(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.setVendettaWith(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.clearEmbassyWith(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.clearContact(object.pNationalistSpanish,object.pRepublicanSpanish)
    object.pNationalistSpanish.attitude[object.pRepublicanSpanish]  = 100
    object.pNationalistSpanish.reputation[object.pRepublicanSpanish]  = 100
   
    civ.makeAggression(object.pRepublicanSpanish,object.pNationalistSpanish)
    diplomacy.setWar(object.pRepublicanSpanish,object.pNationalistSpanish)
    diplomacy.setVendettaWith(object.pRepublicanSpanish,object.pNationalistSpanish)
    diplomacy.clearEmbassyWith(object.pRepublicanSpanish,object.pNationalistSpanish)
    diplomacy.clearContact(object.pRepublicanSpanish,object.pNationalistSpanish)
    object.pRepublicanSpanish.attitude[object.pNationalistSpanish]  = 100
    object.pRepublicanSpanish.reputation[object.pNationalistSpanish]  = 100
   
    -- local function disableTreatyChanges(tribe1,tribe2)
    -- Disable treaty changes between all tribes
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pFrench)
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pPortuguese)
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pBritish)
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pTangierIntZone)
    diplomacy.disableTreatyChanges(object.pRepublicanSpanish,object.pFrench)
    diplomacy.disableTreatyChanges(object.pRepublicanSpanish,object.pPortuguese)
    diplomacy.disableTreatyChanges(object.pRepublicanSpanish,object.pBritish)
    diplomacy.disableTreatyChanges(object.pRepublicanSpanish,object.pTangierIntZone)
    diplomacy.disableTreatyChanges(object.pFrench,object.pPortuguese)
    diplomacy.disableTreatyChanges(object.pFrench,object.pBritish)
    diplomacy.disableTreatyChanges(object.pFrench,object.pTangierIntZone)
    diplomacy.disableTreatyChanges(object.pPortuguese,object.pBritish)
    diplomacy.disableTreatyChanges(object.pPortuguese,object.pTangierIntZone)
    diplomacy.disableTreatyChanges(object.pBritish,object.pTangierIntZone)
   
end

Regards,
Pablo
 
Last edited:
Hi @Prof. Garfield I want to customize combat between different unit types, so I believe I should use your combat group rules functionality. However I see your traits module could achieve a similar goal. What usage do you envise for the traits functionality?
The combat group rules functionality is only so that you can modify combat using rules_lst.txt. Rules_lst.txt is a non-programming way to get some functionality, and is meant to be easier, rather than better or recommended. If something in rules_lst.txt provides adequate flexibility for your needs, then, by all means, use it. If not, change the relevant settings file directly.

Traits, on the other hand, are meant to be a general purpose way of assigning stuff to groups, so that you can check if something is in the group later. You can, indeed, use them to govern combat modifiers that you program yourself, and I think that was my initial reason for designing them in the first place. Assigning a unit to a "combat group" in rules_lst.txt, on the other hand, only affects that section of the rules.
 
Hi @civ2units ,if it helps you I have this on both onTribeTurnBegin() and onTribeTurnEnd() to ensure my diplomacy doesn't change. I'm forcing aggressions and vendettas/changes of attitude to ensure the level of "hate" doesn't change. You can change the tribes to whatever situation meets your scenario needs:

SQL:
-- ClearDiplomacy
helper.resetDiplomacyStatus()

where:

SQL:
function helper.resetDiplomacyStatus()   
  
    -- Reset diplomacy status for all tribes
    -- To be run on each tribe's turn to ensure the AI does not negotiate
    -- I need to be able to use the Foreign Minister screen so can't use diplomacy.setEventTreatiesOnly as this
    -- disables the entire screen in ToT. See https://forums.civfanatics.com/threads/small-questions-thread.660320/page-4#post-16326130
  
    -- Basic reset of war, vendetta and attitudes so they don't negotiate
    civ.makeAggression(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.setWar(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.setVendettaWith(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.clearEmbassyWith(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.clearContact(object.pNationalistSpanish,object.pRepublicanSpanish)
    object.pNationalistSpanish.attitude[object.pRepublicanSpanish]  = 100
    object.pNationalistSpanish.reputation[object.pRepublicanSpanish]  = 100
  
    civ.makeAggression(object.pRepublicanSpanish,object.pNationalistSpanish)
    diplomacy.setWar(object.pRepublicanSpanish,object.pNationalistSpanish)
    diplomacy.setVendettaWith(object.pRepublicanSpanish,object.pNationalistSpanish)
    diplomacy.clearEmbassyWith(object.pRepublicanSpanish,object.pNationalistSpanish)
    diplomacy.clearContact(object.pRepublicanSpanish,object.pNationalistSpanish)
    object.pRepublicanSpanish.attitude[object.pNationalistSpanish]  = 100
    object.pRepublicanSpanish.reputation[object.pNationalistSpanish]  = 100
  
    -- local function disableTreatyChanges(tribe1,tribe2)
    -- Disable treaty changes between all tribes
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pRepublicanSpanish)
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pFrench)
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pPortuguese)
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pBritish)
    diplomacy.disableTreatyChanges(object.pNationalistSpanish,object.pTangierIntZone)
    diplomacy.disableTreatyChanges(object.pRepublicanSpanish,object.pFrench)
    diplomacy.disableTreatyChanges(object.pRepublicanSpanish,object.pPortuguese)
    diplomacy.disableTreatyChanges(object.pRepublicanSpanish,object.pBritish)
    diplomacy.disableTreatyChanges(object.pRepublicanSpanish,object.pTangierIntZone)
    diplomacy.disableTreatyChanges(object.pFrench,object.pPortuguese)
    diplomacy.disableTreatyChanges(object.pFrench,object.pBritish)
    diplomacy.disableTreatyChanges(object.pFrench,object.pTangierIntZone)
    diplomacy.disableTreatyChanges(object.pPortuguese,object.pBritish)
    diplomacy.disableTreatyChanges(object.pPortuguese,object.pTangierIntZone)
    diplomacy.disableTreatyChanges(object.pBritish,object.pTangierIntZone)
  
end

Regards,
Pablo

FYI, there is a line in diplomacySettings.lua that disables non-event treaty changes if you uncomment it:
Code:
--diplomacy.setEventTreatiesOnly("You may have received a message about a change in diplomacy.  This change has been undone.")
Every time a unit is activated, the game will check if the treaties are changed, and revert to the the pre-existing setup. The documentation on the diplomacy module is both bad and incomplete. Looking at the code, the procedure for having unchanging treaties is:

In diplomacy settings
Code:
diplomacy.setEventTreatiesOnly("You may have received a message about a change in diplomacy.  This change has been undone.")
diplomacy.alwaysEnableTreatyChanges(tribe1ThatCanAlwaysChangeTreaties,tribe2ThatCanAlwaysChangeTreaties)

Then, if you want to enable or disable treaty changes between tribes during the game (assuming they haven't been enabled above), you use these events
Code:
diplomacy.enableTreatyChanges
diplomacy.disableTreatyChanges
 
Currently I've restricted the diplomacy of human controlled civs and this works fine. For example, this code prevents AI and human controlled civs to negotiate with the Neutral Alliance (always AI controlled):

Code:
if turn >= 1 then
    diplomacy.setAlliance(object.pOttomanEmpire,object.pNeutralAlliance)
    diplomacy.setAlliance(object.pSpanishHabsburgEmpire,object.pNeutralAlliance)
    diplomacy.setAlliance(object.pFrench,object.pNeutralAlliance)
    diplomacy.setAlliance(object.pProtestantLeague,object.pNeutralAlliance)
    diplomacy.setAlliance(object.pIndependentNations,object.pNeutralAlliance)
    diplomacy.setAlliance(object.pMesoamericanAlliance,object.pNeutralAlliance)
end

Code:
if talker == object.pOttomanEmpire and listener == object.pNeutralAlliance then
    return false
end
if talker == object.pNeutralAlliance and listener == object.pOttomanEmpire then
    return false
end   
...

I would like to set diplomacy always enabled between the AI controlled civs, like in vanilla games.
When I use the code 'diplomacy.alwaysEnableTreatyChanges(tribe1ThatCanAlwaysChangeTreaties,tribe2ThatCanAlwaysChangeTreaties)' for the AI controlled civs, doesn't it affects my other restrictions?
 
When I use the code 'diplomacy.alwaysEnableTreatyChanges(tribe1ThatCanAlwaysChangeTreaties,tribe2ThatCanAlwaysChangeTreaties)' for the AI controlled civs, doesn't it affects my other restrictions?
I didn't make any way to distinguish between human and ai tribes in the diplomacy module. If you want the AI to be able to change its treaties, you will put
Code:
diplomacy.setEventTreatiesOnly("You may have received a message about a change in diplomacy.  This change has been undone.")
in diplomacySettings. Then, on turn 1 or 2,
Code:
for tribe1ID=1,7 do
    for tribe2ID=1,7 do
        local tribe1 = civ.getTribe(tribe1ID)
        local tribe2 = civ.getTribe(tribe2ID)
        if not (tribe1.isHuman or tribe2.isHuman) then
            diplomacy.enableTreatyChanges(tribe1,tribe2)
        end
    end
end
Note that allowing treaty changes between tribes doesn't stop you from changing the treaty by events, it just means they don't have to stick with the change.
 
FYI, there is a line in diplomacySettings.lua that disables non-event treaty changes if you uncomment it:
Code:
--diplomacy.setEventTreatiesOnly("You may have received a message about a change in diplomacy.  This change has been undone.")
Every time a unit is activated, the game will check if the treaties are changed, and revert to the the pre-existing setup.
Yeah I know, but that was not suitable for my requirements as this disables the entire foreign minister screen and I wanted to use for espionage.
 
Yeah I know, but that was not suitable for my requirements as this disables the entire foreign minister screen and I wanted to use for espionage.
The setEventTreatiesOnly function doesn't stop those things, at least not directly. It may stop them indirectly by cancelling contact/embassy "treaties", but you just have to set those treaties correctly in events. Tribes can talk to each other and do diplomacy, however any changes in treaty status will be undone when the next unit activates.

Stopping negotiation entirely is handled by EventsFiles\onNegotiation.lua and the equivalents in the consolidated and discrete events, by returning false.
 
I didn't make any way to distinguish between human and ai tribes in the diplomacy module. If you want the AI to be able to change its treaties, you will put
Code:
diplomacy.setEventTreatiesOnly("You may have received a message about a change in diplomacy.  This change has been undone.")
in diplomacySettings. Then, on turn 1 or 2,
Code:
for tribe1ID=1,7 do
    for tribe2ID=1,7 do
        local tribe1 = civ.getTribe(tribe1ID)
        local tribe2 = civ.getTribe(tribe2ID)
        if not (tribe1.isHuman or tribe2.isHuman) then
            diplomacy.enableTreatyChanges(tribe1,tribe2)
        end
    end
end
Note that allowing treaty changes between tribes doesn't stop you from changing the treaty by events, it just means they don't have to stick with the change.

I think I will go with the 'civ.makeagression' code. The Age of Reformation was a very conflictual time, so the more wars between the AI controlled civs, the better it is.
 
Hi @Prof. Garfield I haven't found anything regarding the "state" table capacity. How many flags and counters can we store safely for persistence in the savegame?

How is Lua handling all code in memory? Any limit in the number of modules we can define/import without breaking anything?

I was thinking that maybe we can also write data to a plain text file, and then have the OnLoad function read from it whatever we needed to hard store, what do you think?

Thanks,

Pablo
 
Hi @Prof. Garfield I haven't found anything regarding the "state" table capacity. How many flags and counters can we store safely for persistence in the savegame?

How is Lua handling all code in memory? Any limit in the number of modules we can define/import without breaking anything?

I was thinking that maybe we can also write data to a plain text file, and then have the OnLoad function read from it whatever we needed to hard store, what do you think?

Thanks,

Pablo
The only limit I know of in Lua is that a function (and a Lua file acts as a function in this respect) can't have more than 200 local variables. This is per function, so you can define local functions within other functions and get another 200 each. I'm sure I ran into stack overflows for bugged code, but I don't know of any limitation that adequately written code is going to hit.

The state table is converted to text (using civlua.serialize) , and (I think) appended to the end of a saved game. We had some pretty massive state tables in OTR (we had cloud patterns crossing the map, so we had to save the underlying terrain type and terrain improvements), so I found a compression algorithm online (LuaCore\lualzw.lua) to compress/decompress the string and saved hundreds of kilobytes per saved OTR game. @Knighttime saved information about worked tiles in Medieval Millennium, so that's a pretty large chunk of state data as well.

If you're curious about what the limits are, I'd suggest running an experiment. There's a pretty basic events file attached to this post, so you could run an experiment, doubling the size of the state table until something breaks.
 
Back
Top Bottom