bane_
Howardianism High-Priest
- Joined
- Nov 27, 2013
- Messages
- 1,559
DISCLAIMER
This is isn't a Guide for Lua programming, but a very lean begginer's guide. It will only be useful if the following statements are both true:1) You don't understand the basics of Lua already.
2) You don't want to use this tutorial as an entry level for Lua Programming, but for Civ5 Lua Basic Modding.
I'll try to be more layman friendly than precise, because the hardest part in learning to program in Lua for Civ5 is the first step; it is usually a brand new world and no guides to help you discover and conquer new lands.
With that said, if you want to add something, please say so. Thank you.
==================================================
Let's start with a question that I got here, which was:
How would i get it to check whether a mountain is in your territory[?]
The answer, the short answer, is this:
Code:
for i = 0, pCity:GetNumCityPlots() - 1, 1 do
local pPlot = pCity:GetCityIndexPlot(i)
if pPlot:GetPlotType() == PlotTypes.PLOT_MOUNTAIN then
--rest of the code here
end
end
Note, however, that this is just a part of the code, the variable pCity must be defined earlier in that code. If you are iterating through all cities from a player each turn, you can do it like this:
Code:
[COLOR="Red"]Line #01[/COLOR] function CheckCityMountains(iPlayer)
[COLOR="red"]Line #02[/COLOR] local pPlayer = Players[iPlayer]
[COLOR="red"]Line #03[/COLOR] for pCity in pPlayer:Cities() do
[COLOR="red"]Line #04[/COLOR] for i = 0, pCity:GetNumCityPlots() - 1, 1 do
[COLOR="red"]Line #05[/COLOR] local pPlot = pCity:GetCityIndexPlot(i)
[COLOR="red"]Line #06[/COLOR] if pPlot:GetPlotType() == PlotTypes.PLOT_MOUNTAIN then
[COLOR="red"]Line #07[/COLOR] --rest of the code here
[COLOR="red"]Line #08[/COLOR] end
[COLOR="red"]Line #09[/COLOR] end
[COLOR="red"]Line #10[/COLOR] end
[COLOR="red"]Line #11[/COLOR] end
[COLOR="red"]Line #12[/COLOR]
[COLOR="red"]Line #13[/COLOR] GameEvents.PlayerDoTurn.Add(CheckCityMountains)
See, now everything was defined earlier than it was used (rightly so). And look down there! There is a new element we didn't knew before. That is a 'hook', something to tell the game when to run your code. They are found in that first site I told you about, but there are a few more here (I don't know if there is an exaustive list anywhere though, sorry).
Note that the Red-colored line numbers aren't anything you'll ever put into your actual code. They're shown so that it will be easier to understand which individual line is being discussed. (Thank you LeeS) Ok, so, looking at the code line by line independently we get:
Functions are the heart of this tutorial and modding, almost everything you'll do with Lua is through these named functions.Line #01 function CheckCityMountains(iPlayer)
This is the first line of every function you will design. It follows this instruction:
function Identifier(ARGUMENTS)
- function must always be there.
- The Identifier is the name that will, well, identify your function. It is your choice. Although, they cannot start with a digit, be one of the reserved Lua keywords¹. It is also good to avoid identifiers starting with underscores that also follow with uppercase letters, like _VERSION.
- ARGUMENTS are passed to you through the hook you chose OR when you call the function yourself (not needed right now). In this case, we can see here that the GameEvents merely passes to us the Player ID.
This is how you define a variable.Line #02 local pPlayer = Players[iPlayer]
('local' OR nothing) Identifier = function
- local means it will only be used by anything that goes below, until it's own 'end' statement. In our case here, pPlayer will be a valid variable until before the very last 'end', which means it'll be usable anywhere in this function.
- Again: The Identifier is the name that will, well, identify your variable. It is your choice. Although, they cannot start with a digit, be one of the reserved Lua keywords¹. It is also good to avoid identifiers starting with underscores that also follow with uppercase letters, like _VERSION.
- I don't know if 'function' is the proper use here, I don't study Lua nor programming, but for the sake of this post, it is now. Here you'll tell the game: "Hey game, go get this information for me". In our case, we look at a table called "Players" that has information on which players is which based on their ID, and we have the Player whose turn just began because the hook gave it to us (iPlayer)! So it'll compare to the list it already haves and return the correct pointer to the Player we want.
'for' is a statement²; you use it when you need a loop, in our case here, you want to iterate through all cities from a player.Line #03 for pCity in pPlayer:Cities() do
This is the model (for Civ5 modding, keep in mind that! The number of identifiers for this kind of 'for' loop is defined by the iterator, so it varies):
for Identifier, [Identifier, Identifier, ...] in function do
- The Identifier is the name that will identify the variable you will call later. You do a loop to use/identify the values of that table, so it is a good thing to name it consistently. Remember of the reserved Lua keywords¹ and earlier exposition about Identifier. This is a variable.
- function will be the method you'll be using to make this iteration. We have pairs() and ipairs() as native iterators to Lua; the Civ5 API has many more: Player:Cities() and Player:Units() are two good examples.
We are defining pCity here through the iterator defined by Fireaxis that goes through all your cities so you can use it later.
Now, imagined reader, you say: But wait! You already told us about 'for' loops!Line #04 for i = 0, pCity:GetNumCityPlots() - 1, 1 do
Yes, I did, but the prior was to get through an array of items. In this case, we want to iterate through numbers. Here it is:
for Identifier = startValue, endValue, progression do
- The Identifier is the name that will identify the variable you will call later. Remember of the reserved Lua keywords¹ and earlier exposition about Identifier. This is a variable.
- startValue Loops increase incrementaly until they reach a value, they must start from somewhere, this is the somewhere.
- endValue As said above, this loop stops when it reaches a value, the endValue represents that. It is important to note that
- progression is the amount by which the startValue will be increased until it reaches the endValue; this increase happens each time you reach the end of that part of the chunk, in this case, riiiight before the third 'end' statement.
What we see here is a loop through ALL plots that city has. For each plot in pCity (defined in the first loop), do something, it is very straigthforward.
This time we are also creating a variable, in this line's case, i. We use 'i' to indicate an 'integer', some people use it before a variable to indicate that variable will be an integer - this convention is called Hungarian Notation - that identifies a plot.
We already talked about defining variables, but let's see how we got this one:Line #05 local pPlot = pCity:GetCityIndexPlot(i)
pCity was defined earlier, it will change in each step of the first iteration by which city is the target now.
:GetCityIndexPlot this is a function, but a special one, it is called 'method'² - every method is preceded by a ":". You'll use plenty of methods when programming, they are described in the Lua API References and the other link in the sources that redirects to a post here in CivFanatics.
(i) is the plot you want to get, defined earlier in the second 'for' loop.
'if' statements are the most common you will find here. It is a condition to keep the code going, if the 'if' doesn't hold - if it is not true - everything between the if statement and it's 'end' statement do not happen. So, if you, say, define a variable within 'if' and call it from outside, you will get a 'nil' value.Line #06 if pPlot:GetPlotType() == PlotTypes.PLOT_MOUNTAIN then
Here we have the following:
Remember when we defined pPlayer, imagined reader? I told you that we used a table with all player's IDs to compare, this time we are using another table, PlotTypes (the most common table to use is 'GameInfoTypes' that contains"if the plot I defined earlier has a type equal to the one I'm comparing to, then go on with the code"
Why zero? Oh, you combative imagination... Here, take a look."Is pPlot's type = 0?"
Ok, with that said, we finished our work. What? We didn't? Oh, right, we didn't!
What about the phrase after the if statement? What about the ends?
In Lua, when you input '--', anything after the doubly-hyfen is a comment - will have no effect in the code. This is a 'short commentary', when you want to comment only a certain part of, or multiple lines of a code, you use --[[]]; like this:
Code:
if pPlot:GetPlotType() [COLOR="DeepSkyBlue"]--[[Fireaxis and Mountains, such a great friendship!]][/COLOR] == PlotTypes.PLOT_MOUNTAIN then
[...]
and
[...]
[COLOR="DeepSkyBlue"]--[[this is a comment
this is part of the same comment!
No effect in the code]][/COLOR]
Now, the 'end' statement serves to end a chunk (this is the actual name for it) of a code. Bringing it to our case we have 4 ends, being:
- The last one, present at Line #11, or the 'higher' one is closing the whole function, as soon as you reach it, the function is over.
- The third one (Line #10) - as already exposed - closes the first loop (Line #03), it is only reached as soon as the 'for' loop reaches a 'nil' value, which means it'll keep looping until ALL your cities were accounted for, then it ends his participation.
- The second one at Line #09 closes the second loop (Line #04), the numerical 'for', it is reached as soon as all plots from the city (defined in the first loop) are accounted for.
- The very first one right at Line #08, the lowest of the ends, is closing the if statement (Line #06). Since it isn't a loop, if statements are read in order until the 'end' statement and really end there.
========================================
I'll expand on this later, but if you need some more assistance
please post below and I'll tackle on it. The more information I
gather from new Lua-programmers (and experienced as well!)
the better this guide will be for everybody and more people
will join us in modding Civ5!
========================================
I'll expand on this later, but if you need some more assistance
please post below and I'll tackle on it. The more information I
gather from new Lua-programmers (and experienced as well!)
the better this guide will be for everybody and more people
will join us in modding Civ5!
========================================
Sources
Lua Manual 5.1 (the version used in Civ5)
Lua API
More Lua GameEvents (found by bc1)
¹ and / break / do / else / elseif / end / false / for / function / if / in / local / nil / not / or / repeat / return / then / true / until / while -- Remember, though, that Lua is case-sensitive, which means that while 'else' is a reserved keyword 'eLse', 'elsE', 'Else' and 'elSe' aren't; yet, it is good to avoid using these for the sake of simplicity.
² What that means is outside of the scope of this tutorial.