[TOTPP] Lua Scenario Template

In your canBuildSettings file, you have several lines like this
Code:
--unitTypeBuild[object.uBulgarianInfantry.id]={location=spanishCities}
--unitTypeBuild[object.uBulgarianTank.id]={location=spanishCities,allImprovements={object.iFactory}}
--unitTypeBuild[object.uBulgarianArtillery.id]={location=spanishCities,allImprovements={object.iFactory}}
--unitTypeBuild[object.uBulgarianFighter.id]={location=spanishCities,allImprovements={object.iFactory}}
--unitTypeBuild[object.uBulgarianBomber.id]={location=spanishCities,allImprovements={object.iFactory}}
--
--unitTypeBuild[object.uBulgarianInfantry.id]={location=turkishCities}
--unitTypeBuild[object.uBulgarianTank.id]={location=turkishCities,allImprovements={object.iFactory}}
--unitTypeBuild[object.uBulgarianArtillery.id]={location=turkishCities,allImprovements={object.iFactory}}
--unitTypeBuild[object.uBulgarianFighter.id]={location=turkishCities,allImprovements={object.iFactory}}
--unitTypeBuild[object.uBulgarianBomber.id]={location=turkishCities,allImprovements={object.iFactory}}
where you reassign build conditions to the Bulgarian Units. Croatia is like that, too, but I changed it in my investigations.

Also, you have a numbering problem between your rules and your object.lua. They're out by 1 at some point.

CroatianTank is 167 in your rules, and 168 in the object file. The error begins at RomanianTank. This looks like it will fix it:
Code:
object.uRomanianTank           =civ.getUnitType(140)
object.uRomanianArtillery       =civ.getUnitType(141)
object.uRomanianFighter           =civ.getUnitType(142)
object.uRomanianBomber           =civ.getUnitType(143)
object.uHungarianTank           =civ.getUnitType(144)
object.uHungarianArtillery       =civ.getUnitType(145)
object.uHungarianFighter       =civ.getUnitType(146)
object.uHungarianBomber           =civ.getUnitType(147) 
object.uFinnishTank               =civ.getUnitType(148)
object.uFinnishArtillery       =civ.getUnitType(149)
object.uFinnishFighter           =civ.getUnitType(150)
object.uFinnishBomber           =civ.getUnitType(151) 
object.uBulgarianTank           =civ.getUnitType(152)
object.uBulgarianArtillery       =civ.getUnitType(153)
object.uBulgarianFighter       =civ.getUnitType(154)
object.uBulgarianBomber           =civ.getUnitType(155)
object.uSpanishInfantry           =civ.getUnitType(156)
object.uSpanishTank               =civ.getUnitType(157)
object.uSpanishArtillery       =civ.getUnitType(158)
object.uSpanishFighter           =civ.getUnitType(159)
object.uSpanishBomber           =civ.getUnitType(160)
object.uTurkishInfantry           =civ.getUnitType(161)
object.uTurkishTank               =civ.getUnitType(162)
object.uTurkishArtillery       =civ.getUnitType(163)
object.uTurkishFighter           =civ.getUnitType(164)
object.uTurkishBomber           =civ.getUnitType(165)
object.uCroatianInfantry       =civ.getUnitType(166)
object.uCroatianTank           =civ.getUnitType(167)
object.uCroatianArtillery       =civ.getUnitType(168)
object.uCroatianFighter           =civ.getUnitType(169)
object.uCroatianBomber           =civ.getUnitType(170)
object.uIraqiInfantry           =civ.getUnitType(171)
object.uBritishDefenses           =civ.getUnitType(172)
object.uAmericanDefenses       =civ.getUnitType(173)
object.uSunderland               =civ.getUnitType(174)
object.uTito                   =civ.getUnitType(175)
object.uYugoslavPartisanI       =civ.getUnitType(176)
object.uYugoslavPartisanII       =civ.getUnitType(177)
object.uYugoslavTank           =civ.getUnitType(178)
object.uYugoslavFighter           =civ.getUnitType(179)
object.uPolishFighter           =civ.getUnitType(180)
object.uNewUnit182               =civ.getUnitType(181)
object.uNewUnit183               =civ.getUnitType(182)
object.uNewUnit184               =civ.getUnitType(183)
object.uNewUnit185               =civ.getUnitType(184)
object.uNewUnit186               =civ.getUnitType(185)
object.uNewUnit187               =civ.getUnitType(186)
object.uNewUnit188               =civ.getUnitType(187)

I'll look into this a bit more, since there still seems to be some issues.
 
I'm sorry for wasting your time on a stupid error on my part - I got pulled away and forgot I didn't finish the file. Then I guess I didn't scroll down far enough to catch the obvious error. I'll do the checking on the object file myself - but thank you! I see that you're right and everything works which is what's important.
 
I'm sorry for wasting your time on a stupid error on my part -

No problem. I wouldn't want you to waste hours if it was my mistake.

This should fix your code. At least it looks like it based on the quick check that I've done.

Code:
unitTypeBuild[object.uRomanianInfantry.id]={location=romanianCities}
unitTypeBuild[object.uRomanianTank.id]={location=romanianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uRomanianArtillery.id]={location=romanianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uRomanianFighter.id]={location=romanianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uRomanianBomber.id]={location=romanianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uBulgarianInfantry.id]={location=bulgarianCities}
unitTypeBuild[object.uBulgarianTank.id]={location=bulgarianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uBulgarianArtillery.id]={location=bulgarianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uBulgarianFighter.id]={location=bulgarianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uBulgarianBomber.id]={location=bulgarianCities,allImprovements={object.iFactory}}
--
unitTypeBuild[object.uCroatianInfantry.id]={location=croatianCities}
unitTypeBuild[object.uCroatianTank.id]={location=croatianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uCroatianArtillery.id]={location=croatianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uCroatianFighter.id]={location=croatianCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uCroatianBomber.id]={location=croatianCities,allImprovements={object.iFactory}}
--
unitTypeBuild[object.uSpanishInfantry.id]={location=spanishCities}
unitTypeBuild[object.uSpanishTank.id]={location=spanishCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uSpanishArtillery.id]={location=spanishCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uSpanishFighter.id]={location=spanishCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uSpanishBomber.id]={location=spanishCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uTurkishInfantry.id]={location=turkishCities}
unitTypeBuild[object.uTurkishTank.id]={location=turkishCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uTurkishArtillery.id]={location=turkishCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uTurkishFighter.id]={location=turkishCities,allImprovements={object.iFactory}}
unitTypeBuild[object.uTurkishBomber.id]={location=turkishCities,allImprovements={object.iFactory}}

Also, in your rules, the Germans can't build croatian infantry, and they can build allied defences.
 
I added support for onCanFoundCity, in the consolidated events, discrete events, and as a separate file. If any instance of these returns false, the unit can't build the city.

Returning true for onCanFoundCity does not override any of the other requirements for building a city (not on the ocean, not adjacent to another city, etc). It also does not prevent advanced tribes from huts (setting fertility to 0 does do that, however).
 
I've created a describe.txt generator module. The idea is that you call
Code:
local civilopedia = require("civilopedia")
civilopedia.describe(item,description)
In your code near where you create the feature that must be documented (outside the actual event function). This way, you document as you go, and producing the final version of the describe.txt file is a matter of going through and perhaps changing the order of things. Each call to civilopedia.describe adds extra text, but you can't easily control the order it is added, so you will have to do a final edit of describe.txt. Still, all the information will already be there.

All you have to do to start using this is drop the civlopedia.lua file into LuaCore, and require it in individual files. In fact, it shouldn't need the template to work (though I haven't tested this -- it does attempt to get the general library for the scenario directory).

To produce a describe.txt file (prepended with some numbers to avoid overwriting something else), use the console command
Code:
require("civilopedia").makeDescribeTxt()
or, with the template (as long as the file has been required at some point)
Code:
console.makeDescribeTxt()
If used with a sufficiently recent version of the Lua Scenario Template, the XXXXXXXXXdescribe.txt file will be sent to your scenario directory. Otherwise, it will go to your main test of time directory. A message telling you where it went will be printed to the console.

There is a samplePedia.lua file with this also, which has examples that I was using for testing, along with more detailed instructions. If you're going to try to make the sample describe.txt file, run the following line in the console
Code:
require("samplePedia")

I haven't done extensive testing on this yet, so you may find bugs if you try it (please report them). Take note, however, that there are some errors that describe how you've used the code wrong, so read the errors. Still, if they're unclear about what was done wrong, I'd like to know that, too.

EDIT: Removed attachment, reattached updated version to next post.
 
Last edited:
I've done some more testing, so the module is in good order (or at least as good as anything else I release). I thought I could influence the order of the concept descriptions, but I was incorrect, so I removed that stuff. I forgot to mention last night that I used the describe.txt from @Knighttime 's Medieval Millennium as my main guide, which was easier to follow and understand than the game's describe.txt file.

EDIT: Another change was made to the file. See the next post.
 
Last edited:
Somehow I forgot to test the feature of adding descriptions to multiple items by using a table...
 

Attachments

Updated the template to take advantage of totpp 0.18.2's addition of the advanced tribe parameter for onCanFoundCity.
 
Updated the Legacy Event Engine to account for the 'global' nature of continuous flags. For the Legacy Event Engine, adding this line to legacyEvents.txt (events.txt renamed for technical reasons)
Code:
@CONTINUOUSFLAGSPERTRIBE
will override the behaviour and have each flag individually continuous or not.

I also found a global variable that had to be made local in the Legacy Event Engine.
 
@Prof. Garfield Hi there. :)

I ran this one past Civ2Units on my thread, but asking here too, if that is OK.

I have Lua events that launch on discovering various techs.
Is there an efficient way to make these techs only researchable to the AI?

Example of an event below, preceeded by the afterProduction function;
(which I am not sure if is needed at this stage)

Code:
function triggerEvents.afterProduction(turn,tribe)
    context[getContext()]["afterProduction"](turn,tribe)
    universal["afterProduction"](turn,tribe)
    delay.doAfterProduction(turn,tribe)
--If the Soviets research "European Front Invasion", the event will spawn Red Army forces to attack German cities...And spawn some German defenders.
if civ.hasTech(object.tRussians, object.aSovEuropeanFront) then
        gen.justOnce("SovietsAttackGermany",function()
    civ.makeAggression(object.tRussians, object.tGermans)
    --Soviet North Invasion Army
        civlua.createUnit(object.uRedArmy, object.tRussians, {{19,33,0},{25,21,0},{27,17,0}}, {count=20, randomize=false, veteran=false})
        civlua.createUnit(object.uArtillery, object.tRussians, {{19,33,0},{25,21,0},{27,17,0}}, {count=10, randomize=false, veteran=false})
        civlua.createUnit(object.uLightTank, object.tRussians, {{19,33,0},{25,21,0},{27,17,0}}, {count=10, randomize=false, veteran=false})
        civlua.createUnit(object.uTB3, object.tRussians, {{19,33,0},{25,21,0},{27,17,0}}, {count=6, randomize=false, veteran=false})
    --Soviet Central Invasion Army
        civlua.createUnit(object.uRedArmy, object.tRussians, {{21,41,0},{30,30,0},{36,28,0}}, {count=20, randomize=false, veteran=false})
        civlua.createUnit(object.uArtillery, object.tRussians, {{21,41,0},{30,30,0},{36,28,0}}, {count=10, randomize=false, veteran=false})
        civlua.createUnit(object.uLightTank, object.tRussians, {{21,41,0},{30,30,0},{36,28,0}}, {count=10, randomize=false, veteran=false})
        civlua.createUnit(object.uTB3, object.tRussians, {{21,41,0},{30,30,0},{36,28,0}}, {count=6, randomize=false, veteran=false})
    --Soviet South Invasion Army
        civlua.createUnit(object.uRedArmy, object.tRussians, {{22,46,0},{28,46,0},{39,43,0}}, {count=20, randomize=false, veteran=false})
        civlua.createUnit(object.uArtillery, object.tRussians, {{22,46,0},{28,46,0},{39,43,0}}, {count=10, randomize=false, veteran=false})
        civlua.createUnit(object.uLightTank, object.tRussians, {{22,46,0},{28,46,0},{39,43,0}}, {count=10, randomize=false, veteran=false})
        civlua.createUnit(object.uTB3, object.tRussians, {{22,46,0},{28,46,0},{39,43,0}}, {count=6, randomize=false, veteran=false})
    civ.ui.text("German border posts report increased Soviet military traffic. The OKW mobilises reserve garrisons...")
    civ.playSound("RUS.wav")
    civ.ui.text("Stalin has daringly unleashed a concerted strike upon German positions. He hopes to dislocate any action from the fascists. An inspired move, or potential disaster? Time will tell......")
    civlua.createUnit(object.uGrenadiers, object.tGermans, {{16,32,0},{16,36,0},{17,41,0},{17,45,0},{22,40,0}}, {count=10, randomize=true, veteran=false})
    end)
end
 
@CurtSibling

This looks like something useful:

enableTechGroup
civ.enableTechGroup(tribe, techgroup, value) -> void

Sets the value of tech group `techgroup` (0-7) to value `value` (0-2, 0 = can research, can own, 1 = can't research, can own, 2 = can't research, can't own) for tribe `tribe`.

It looks like you should be able to designate a tech group to be the "ai only" techs. On the first turn, (or, perhaps every turn, in case someone plays with the cheat menu, or decides to join a multiplayer game), you check which tribes are human and ai, and change the value of the tech group for that tribe. I haven't worked with tech groups myself, so I can't give you exact code. Perhaps @Knighttime can, since I'm pretty sure he used this feature in Medieval Millennium.

You could also check if the tribe that will receive the bonus is human (object.tRussians.isHuman), and only bestow the bonus if the tribe is controlled by the AI. That wouldn't stop the player from researching the tech, but they wouldn't get anything for it.
 
The first method is great if the player is always the same civ, but I am looking for a Lua solution that basically "hides" the techs from a human player, but leaves them for AI.

I've looked at the rules a bit, so I think I can give you a better explanation of what I mean.

In @CIVILIZE2, you can give each technology a 'tech group' between 0 and 7. For this example, we designate 7 as the 'ai only' tech group. All the techs you only want to be researchable by the AI are assigned 7 here.

Then, in your onTurn event, you have this
Code:
function triggerEvents.onTurn(turn)
    for civID=1,7 do
        local tribe = civ.getTribe(civID)
        if tribe.isHuman then
           civ.enableTechGroup(tribe, 7, 2) -- tribe can't research and can't own techs in tech group 7 since it is human
        else
            civ.enableTechGroup(tribe, 7, 0) -- tribe can research and can own techs in tech group 7 since it is ai
        end
    end
end
 
That all seems right to me.

On the first turn, (or, perhaps every turn, in case someone plays with the cheat menu, or decides to join a multiplayer game), you check which tribes are human and ai, and change the value of the tech group for that tribe.
I would tend to put things like this in civ.scen.onScenarioLoaded(), but you pointed out some valid reasons why doing this every turn might be helpful.

Perhaps @Knighttime can, since I'm pretty sure he used this feature in Medieval Millennium.
In MM, I primarily used this to organize research into "eras" sort of like later versions of Civ did. There isn't any difference between AI vs. human tribes... instead I assigned each tech to a group (in @ CIVILIZE2) that matched its "Epoch" value (assigned in @ CIVILIZE). Then I set thresholds for a tribe to unlock the next group; for example, you have to learn 70% of the techs in Epoch 0 in order to unlock the techs in Epoch 1. This constrains each tribe to follow the historical pattern a little more closely -- you still have freedom for what to research, but within limits. You can't race ahead and learn late-Middle Ages military techs while neglecting things like Romanesque Architecture, Scholasticism, and Medicine. There are some other special-case situations, but that's the basic idea.
 
Thanks for the advice, chaps - The more I learn of Lua, the more doors open up for possibilities...
 
@JPetroski
@Prof. Garfield
Hey, guys - The new date plugin is very interesting indeed. Can it be set up to do the months as "Early, Mid, and Late"?

Here's a slightly more generic version of the code I wrote for @JPetroski .

Code:
local function getScenarioDate(turn,defaultString)
    local turnOffset = -1 -- the turn offset allows the code to be written as if
                          -- first 'week' of January is the 0th turn
    turn = turn + turnOffset
    local startYear = 1940
    local weeksPerMonth = 3 -- 'weeks' subdivide 'months' 
    local monthsPerYear = 12 -- 'months' subdivide 'years'
    local turnsPerYear = weeksPerMonth*monthsPerYear 
        -- weeksPerMonth*monthsPerYear is the number of turns in the year
        -- divide the turn by turnsPerYear and discard the fractional part
        -- to get the number of years since the startYear
        -- the turnsPerYear'th turn would be the first week of January again
    local yearsSinceStart = turn // turnsPerYear
    local year = startYear + yearsSinceStart
    local monthNumber = (turn % turnsPerYear)// weeksPerMonth -- turn % turnsPerYear removes the year part of the turn
        -- dividing by weeksPerMonth (discarding fractional part) gives the month number, with
        -- 0 as January, 11 as December (if using 12 month year)
    local weekNumber = turn % weeksPerMonth -- removes the month and year portion, leaving only the week number
    local monthNames = {[0] = "Jan","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec"} -- start with month 0, go to monthsPerYear-1
    local weekNames = {[0] = "Early", "Mid", "Late"} -- start with week 0, go to weeksPerMonth-1
    return weekNames[weekNumber].." "..monthNames[monthNumber]..", "..tostring(year)
end
civ.scen.onGetFormattedDate(getScenarioDate)

This should be easier than the original to modify if you have a different number of 'weeks' and 'months' in the year. You'll have to fiddle with the turn offset so that you start in the correct week and month of the year. An offset of -1 means turn 1 is the first week of the first month (or, in terms of the code, the 0th week of the 0th month).
 
Simply excellent breakthrough here. Just one thing to ask, what Lua folder and file would this code be added to?
 
Simply excellent breakthrough here. Just one thing to ask, what Lua folder and file would this code be added to?

Oh, sorry. You can put it in any file that is already "required" (directly or indirectly) by events.lua, or in events.lua itself. So, any file that already has code in it will do. The command civ.scen.onGetFormattedDate(getScenarioDate) registers the code. (Ordinarily, I "hide" the registration lines in events.lua, which is why code must go in particular places.)
 
Bravo on the new object script, by the way. I love how you've added a capture for the cities in the game and even separated them by tribe. Just thought I'd give some nice feedback that didn't involve a question (for once) lol.
 
Bravo on the new object script, by the way. I love how you've added a capture for the cities in the game and even separated them by tribe. Just thought I'd give some nice feedback that didn't involve a question (for once) lol.

Just in case you missed it, city definitions are 'locked' behind an "if false then" line, which should be changed to "if true then" if you want the definitions. You don't want to be using city definitions if there is a chance the city can be destroyed. There are equivalent tile ("location") definitions for each city.
 
Back
Top Bottom