Lua Scripting Possibilities

I've answered this here, since this is a more general Q&A and discussion thread than the Lua Function Reference.

To add a wonder to a city, you set the 'city' property of the wonder

city (get/set)
wonder.city -> city

Returns the city that has built the wonder, `nil` if not built yet or destroyed.

That is, for example
Code:
local myCity = civ.getCity(0)
civ.getWonder(0).city = myCity
This sets the Pyramids (I believe) to the first city built (Id numbers start at 0, hence the first city and first wonder have ID 0).

Although wonders and improvements are sometimes interchangeable in the rules and macro style events, they are distinct in Lua.

Thanks for the reply, so yes, it's appaerently easy to force creation of specific wonders by event. This is just what i wanted :)

I've also deleted my post in the reference thread to keep it clean.
 
Just my 2 cents in case anybody else needs to do the same (maybe it's been answered before, there are many posts on Lua that I haven't checked yet):

How to move a city from one place to another:

1st obtain the id of the city you want. You can open the city view, hit the Lua console and type this:

Code:
civ.getOpenCity().id

This will return an integer which is ther internal id of the city.

2nd close the city screen, go to the tile you want, ensure there's a road on it (shift + F8 to edit terrain) and then hit this in the Lua console:

Code:
civ.getCity(XX):relocate(civ.getCurrentTile())

Where XX is the city id obtained in the previous step.

How to paint a river on a tile:

As you know, the cheat mode doesn't allow you to add rivers to tiles. This is the quickest option I was able to find, you only need to navigate to the tile you want the river and hit this in the Lua console:

Code:
civ.getCurrentTile().river = true

That's it, If you want to remove a river, just change "true" to "false".

Hope it helps.

Pablo
 
As you know, the cheat mode doesn't allow you to add rivers to tiles. This is the quickest option I was able to find, you only need to navigate to the tile you want the river and hit this in the Lua console:

In TOTPP, CTRL+9 adds and removes rivers. Still, that's a useful example of using the tile.river property. I made links for both questions in the Great Lua Library thread.

Per the extra terrain hotkeys patch description.
Code:
Adds keyboard shortcuts for changing terrain / tile improvements. In cheat mode, change terrain with 0 (Desert) to 9 (Jungle) and Ctrl-3 (Ocean) to Ctrl-8 (Extra terrain 5). Use Ctrl-9 to add/remove a river.
For tile improvements, the following keys can be used in cheat mode when no unit is selected:
r: Road
o: Railroad
i: Irrigation
m: Mine
l: Farmland
f: Fortress
e: Airbase
p: Pollution
 
Thanks for that, as you can see I still have loads of stuff to catch up, big learning curve coming from MGE!!

Hi Pablo,

In case you don't have it, I've attached the TOTPP v0.18 PDF reference guide from the Scenario Creation Excel Sheet thread located here. The pdf contains all the features to be found within TNO's TOTPP project.
 

Attachments

  • TOTPP v018.pdf
    1,006.6 KB · Views: 37
Last edited:
Hi Pablo,

In case you don't have it, I've attached the TOTPP v0.18 PDF reference guide from the Scenario Creation Excel Sheet thread located here. The pdf contains all the features to be found within TNO's TOTPP project.

That link is pure gold, thanks!
 
Can someone please help me out with a really stupid question? I have the below code (in after production) which aims to remove trade routes from cities that shouldn't have them, but really that's not what this code is doing obviously.

What I want is to have any cities that are not in Japan to be precluded from trading with one another, with a penalty if it is attempted. What this say is simply that half of the trade route is always going to be removed (unless it happens to be between two Japanese mainland cities).

Now, I think what i want to do is add another check between lines 4 and 5 below where I use the:

from (get)
traderoute.from -> city

and

to (get/set)
traderoute.to -> city

but I'm just drawing a blank of how I'd actually write this in the way that makes most sense. I'd really like to avoid having to go through and do one event for every single city that I don't want to be able to trade. Also I'm not totally certain how to write the actual line "traderoute.to (the -> city part is throwing me).

Hopefully it's another one of my stupid "it's been a long day" questions that isn't too hard for one of our experts to explain :) Thank you all!

Code:
1if tribe == object.pJapanese then
  2  for city in civ.iterateCities() do 
    3        if city.owner == object.pJapanese and (city ~= object.cNagasaki and city ~= object.cKagoshima and city ~= object.cKumamoto and city ~= object.cUita and city ~= object.cKitakyushu and city ~= object.cHiroshima) then
      4          if city.tradeRoutes[0] then
        5        city:removeTradeRoute(0)
                civ.ui.text("HOUSE RULES: You may only trade with Japan itself.  Any cities with a trade route on the mainland will have that route deleted each turn.  As you violated this house rule, a -25 gold penalty has been taken.")
                object.pJapanese.money = object.pJapanese.money - 25
                end
                if city.tradeRoutes[1] then
                city:removeTradeRoute(1)
                civ.ui.text("HOUSE RULES: You may only trade with Japan itself.  Any cities with a trade route on the mainland will have that route deleted each turn.  As you violated this house rule, a -25 gold penalty has been taken.")
                object.pJapanese.money = object.pJapanese.money - 25
                end
                if city.tradeRoutes[2] then
                city:removeTradeRoute(2)
                civ.ui.text("HOUSE RULES: You may only trade with Japan itself.  Any cities with a trade route on the mainland will have that route deleted each turn.  As you violated this house rule, a -25 gold penalty has been taken.")
                object.pJapanese.money = object.pJapanese.money - 25
                end
            end 
    end
end
 
I haven't worked with the traderoute stuff yet, but I'm pretty sure this will work.

Code:
local function isNotInJapan(city)
    -- return true if a city is not in japan,
    -- and false otherwise
    return city ~= object.cNagasaki and city ~= object.cKagoshima and city ~= object.cKumamoto and city ~= object.cUita and city ~= object.cKitakyushu and city ~= object.cHiroshima
end
if tribe == object.pJapanese then
    for city in civ.iterateCities() do
        for tradeRouteID=2,0,-1 do
            if city.tradeRoutes[tradeRouteID] then
                local route = city.tradeRoutes[tradeRouteID]
                if isNotInJapan(route.from) or isNotInJapan(route.to) then
                    civ.ui.text("HOUSE RULES: You may only trade with Japan itself.  Any cities with a trade route on the mainland will have that route deleted each turn.  As you violated this house rule, a -25 gold penalty has been taken.")
                    object.pJapanese.money = math.max(object.pJapanese.money - 25,0)
                    city.removeTradeRoute(tradeRouteID)
                end
            end
        end
    end
end
We check the 3 possible trade routes in reverse order, since removing a trade route shifts the IDs of the remaining routes. That is, if you remove route 0, route 1 now takes on the ID of 0. With a reverse order check, we don't need to worry about that.

I also removed the trade route last, in case you want to use city names in the text. I'm not sure I would call this a "House" Rule when a penalty is levied in game.

Let me know if you want more explanation, or if this doesn't work.
 
This threw up an issue with the last line before all the ends (city.removeTradeRoute(tradeRouteID) - looks like it wanted a city instead?

Code:
Enter console.commands() to see a list of keys in the console table.  Some give access to functions in modules, others will run event code.
...Scenario\1937-Japan Invades China\consolidatedEvents.lua:709: bad argument #1 to 'removeTradeRoute' (civ.city expected, got number)
stack traceback:
    [C]: in field 'removeTradeRoute'
    ...Scenario\1937-Japan Invades China\consolidatedEvents.lua:709: in function 'consolidatedEvents.onCityProcessingComplete'
    ...est of Time\Scenario\1937-Japan Invades China\events.lua:444: in function <...est of Time\Scenario\1937-Japan Invades China\events.lua:436>

I haven't worked with the traderoute stuff yet

I personally think this is one of the more interesting recent things... Sanctions, trade treaties and such are all now possible.
 
This threw up an issue with the last line before all the ends (city.removeTradeRoute(tradeRouteID) - looks like it wanted a city instead?

No, I should have put a colon instead of a period.
Code:
city:removeTradeRoute(tradeRouteID)

I think you get the "argument #1, civ.city" expected because the ':' operator puts the item to the left of the colon as the first argument of the function.

I personally think this is one of the more interesting recent things... Sanctions, trade treaties and such are all now possible.

I agree.
 
That did work to remove the traderoute. It's showing the text multiple times (one for each trade route) but when I have some time tomorrow I can play around with where to put that and the money cost to make sure it only fires once per turn. Thanks for your help!
 
Hello TheNamelessOne.

I just came across an issue in a hotseat game yesterday where this function was used :

To make it simple, bonuses that were meant for the ai got provided to a human player.
This code was located in the "onTurn" section, before tribes play.

Does this function in multiplayer games only works at the playing player's tribe turn ?
Does it not work at all in multiplayer game ?

Much thanks.

I've replied here, so we don't clutter the Function Reference Thread.

Can you please post the relevant code? I quickly started a hotseat game, and tried this

Code:
function discreteEvents.onTurn(turn) 
    for i=0,7 do
        if not civ.getTribe(i).isHuman then
            civ.ui.text(civ.getTribe(i).name.." is not human.")
        end
    end
end

and it worked as expected.
 
I've replied here, so we don't clutter the Function Reference Thread.

Can you please post the relevant code? I quickly started a hotseat game, and tried this

Code:
function discreteEvents.onTurn(turn)
    for i=0,7 do
        if not civ.getTribe(i).isHuman then
            civ.ui.text(civ.getTribe(i).name.." is not human.")
        end
    end
end

and it worked as expected.
Thx prof.

Guess I understood the issue with the used code (from the "old" EpicCasualWWII" scenario) :

Code:
       if civ.getPlayerTribe() ~= Nazi then
           LocationList = {{129,121,0},{135,115,0},{140,124,0}}
           civlua.createUnit(UnitTable.NazInf, Nazi, LocationList,{count = 21, randomize = true, homeCity = nil, veteran = false})
           civlua.createUnit(UnitTable.TanknM, Nazi, LocationList,{count = 12, randomize = true, homeCity = nil, veteran = false})
           civlua.createUnit(UnitTable.ArtillM, Nazi, LocationList,{count = 9, randomize = true, homeCity = nil, veteran = false})
       end
As one of the player wasn't the nazi, it thus triggered the function.

My first assumption was false (I believed I used the isHuman() function to code such a thing), revealing I changed the way I coded :)

I stupidly didn't take time to check the code before asking. My deep mistake there.

Sorry for eating some of your time ! I edited the question post so it won't bother anyone else.
 
Hi all, quick silly question but I haven't been able to find it in the Lua reference: is there a way to add/remove pollution into/from a tile using Lua code?

Thanks

The general library has code to do this:

Code:
gen.hasPollution(tile)-->boolean
Returns true if a tile has pollution.
Returns false othrewise.

gen.placePollution(tile)-->void
Places pollution, unless the tile has a city, airbase or transporter.

gen.placePollutionForce(tile)-->void
Places pollution, unless the tile has a city.
Transporters and airbases are removed, if present.

gen.removePollution(tile)-->void
Checks if tile has pollution, and if so, removes it.

Pollution is a tile "improvement", so you can do bitwise operations yourself if you really want to.

improvements (get/set)
tile.improvements -> integer

Returns the tile's improvements (bitmask).

From @Catfish 's saved game format:

1
2ndByteTile Improvements
0x00Nothing
0x01Unit Present
0x02City Present
0x04Irrigation
0x08Mining
0x0CFarmland
0x10Road
0x30Railroad (+ Road)
0x40Fortress
0x42Airbase
0x80Pollution
0x82Transporter
 
Building off the above, it was suggested one may be able to cheat global warming by wiping on-map pollution at the end of the turn cycle and then reapplying it at the start of the next. The framework seems simple enough: Scan the map at onTribeTurnEnd (7), flag every tile containing pollution, wipe them, then reverse the process onTribeTurnBegin (0), bypassing the calculation that presumably happens during onTurn. Poking around the Lua Template, it looks like I can use tileData to flag (and unflag) the individual tiles without needing to track them with a separate array (which I haven't figured out how to do in the first place).

The first issue is making sure there's a failsafe for civs that are defeated: I'm not sure what the best logic is for verifying the next available tribe in the processing sequence, let alone doing it efficiently.

The second issue is, I haven't touched Lua in TOTPP since my first furtive foray years ago stalled due to untraced errors trying to load my custom events, so I don't actually know how to code any of this. :crazyeye:
 
Building off the above, it was suggested one may be able to cheat global warming by wiping on-map pollution at the end of the turn cycle and then reapplying it at the start of the next. The framework seems simple enough: Scan the map at onTribeTurnEnd (7), flag every tile containing pollution, wipe them, then reverse the process onTribeTurnBegin (0), bypassing the calculation that presumably happens during onTurn. Poking around the Lua Template, it looks like I can use tileData to flag (and unflag) the individual tiles without needing to track them with a separate array (which I haven't figured out how to do in the first place).

The first issue is making sure there's a failsafe for civs that are defeated: I'm not sure what the best logic is for verifying the next available tribe in the processing sequence, let alone doing it efficiently.

The second issue is, I haven't touched Lua in TOTPP since my first furtive foray years ago stalled due to untraced errors trying to load my custom events, so I don't actually know how to code any of this. :crazyeye:

Before we get into coding, you might be able to hex edit out of this problem:

50 amount of current pollution. 7F is maximum, and will certainly cause global temperature rising at the end of the turn. A value of 80 till FF is a negative amount (still shows the icon for global rising), but it will be reset to 00 at the end of the turn.

51 amount of times that a global temperature rising has been happening this far in the game. 7F is maximum and will make the whole world a big swamp when the next global temperature rising occurs. 80 or higher will prevent any global rising from happening at all. At the moment when terrain should be changed, nothing happens, and byte 32 is restored to 00, though a message still pops up. Byte 33 will remain at what it was, so it is not reset. This is a fool-proof way to switch off pollution without the restrictions of the normal cheat-menu switching off of pollution. The message “global warming occurs/will occur soon” of course will need to be changed.

(Libre Office warns me that this document contains macros, and disables them. I don't know why it would have needed them.)

If you choose to go the coding route, I think your strategy will work.

The code you need (untested, so let me know if you have trouble), which can be placed in discreteEvents.lua, is this:

Code:
local tileData = require("tileData")
tileData.defineFlag("hasPollution",false,"never")

local function isLastActiveTribe(tribe)
    for i=7,0,-1 do
        local possibleLastTribe = civ.getTribe(i) --[[@as tribeObject]]
        if possibleLastTribe.active and tribe == possibleLastTribe then
            return true
        elseif possibleLastTribe.active then
            return false
        end
    end
    error("isLastActiveTribe didn't find an active tribe.")
end

discreteEvents.onTribeTurnEnd(function(turn,tribe)
    if not isLastActiveTribe(tribe) then
        return
    end
    for tile in civlua.iterateTiles() do
        if gen.hasPollution(tile) then
            tileData.flagSetTrue(tile,"hasPollution")
            gen.removePollution(tile)
        else
            tileData.flagReset(tile,"hasPollution")
        end
    end
end)

discreteEvents.onTribeTurnBegin(function(turn,tribe)
    if tribe.id ~= 0 then
        return
    end
    for tile in civlua.iterateTiles() do
        if tileData.flagGetValue(tile,"hasPollution") then
            gen.placePollution(tile)
            tileData.flagReset(tile,"hasPollution")
        end
    end
end)

The first section of this video tells how to add the Lua template to a scenario. The directory structure changed a bit since I made this video. legacyEvents.txt should now be moved into the EventsFiles directory, and the canBuildSettings.lua file is now in the MechanicsFiles directory.

 

Attachments

  • Hex Editing.doc
    98 KB · Views: 11
50 amount of current pollution. 7F is maximum, and will certainly cause global temperature rising at the end of the turn. A value of 80 till FF is a negative amount (still shows the icon for global rising), but it will be reset to 00 at the end of the turn.

51 amount of times that a global temperature rising has been happening this far in the game. 7F is maximum and will make the whole world a big swamp when the next global temperature rising occurs. 80 or higher will prevent any global rising from happening at all. At the moment when terrain should be changed, nothing happens, and byte 32 is restored to 00, though a message still pops up. Byte 33 will remain at what it was, so it is not reset. This is a fool-proof way to switch off pollution without the restrictions of the normal cheat-menu switching off of pollution. The message “global warming occurs/will occur soon” of course will need to be changed.
Ooh, I didn't know it was possible to trick the game like this. I'll try both and figure out which I prefer; with Lua I'm thinking normal warming calculations can resume once pollution is back down to a manageable level.
 
@Thorvald of Lym

Hold on, you might not need to hex edit, based on this post.

These commands didn't get properly documented, but they were found by looking for text in the TOTPP program.
Code:
civ.game.globalWarming
civ.game.globalWarmingCycle
civ.game.pollution
civ.game.pollutionNearCities

I just tested (to confirm my memory), and civ.game.pollution doesn't increment when you add or remove pollution using gen.place/removePollution (those functions change tile.improvements). So, there's a good chance that removing and replacing the pollution won't actually change anything as far as global warming goes (that will have to be tested).

But, it looks like you could just set civ.game.globalWarmingCycle to 0 each turn, to stop global warming, or set civ.game.pollution to 0 at the start of the game, or something.
 
Top Bottom