Lua Style Guide

Hambil

Emperor
Joined
Oct 16, 2006
Messages
1,100
The purpose of this thread, and anyone can add to it or comment on it, is to open a debate with the goal of producing concrete examples and actionable items, with the end result being the creation of a coding style guide for lua.

This guide will clearly be optional, as we couldn't force anyone anyway.

I'm going to suggest we start by following the general style and syntax the DLC uses (which I have not been doing, but will be updating all my code to that style soon). Edit: I refuse to update my code to the style of 'random'. I guess we'll have to start from scratch :)

We should begin by listing examples of a guideline. Try to keep it to one per post, but readability and clarity alway should take consideration.

I'm going to reserve the first couple posts so that they, and this one, can be put to a better use as this guide fleshes out.


Moderator Action: Thread moved to the Tutorials & Reference section, please use the comments thread for discussion
 
Semicolons at the end of lines

The DLC is inconsistent in how it uses this. Lua doesn't care. The DLC uses it more often than not, but I think I'm going to break with that and my first style recommendation is that we do not use semicolons at the end of lines.

In a perfect world, I'd say we shouldn't use extra things that aren't needed unless they add significant readability value, which I don't think this one does. Debate is open, of course. *In javascript, due to it's multiline statements, you can run into edge cases where a ; is needed. If such is the case in lua I need an expert to let me know so I can update this.
 
pcall

NOTE: pcall is a very useful debugging tool, but should never be included in your final release. It's value is in catching bugs you might otherwise miss. It's just adds overhead, sometimes significant, to an already debugged release.

As long as GameEvents is broken in it's bug reporting, and perhaps for other reasons as well, I going to recommend pcall be used for GameEvents. There are many ways to do this, and I'll link to a thread discussing it in a moment, but here is a simple code sample of one way, first.

Code:
function MyEvent(arg1, arg2)
  -- do stuff
end

function P_MyEvent(arg1, arg2)
  print(pcall(MyEvent, arg1, arg2))
end

GameEvent.SomeGameEvent.Add(P_MyEvent)

If you wish to use the same code in debug and release, with as few changes as possible you can use a solution given by Whoward:

Code:
local bDebug = false

...

GameEvents.SomeGameEvent.Add(bDebug and P_MyEvent or MyEvent)

Pcall in depth
 
White Space
There are many different uses for whitespace. In general whitespace is a good thing that breaks up logical areas of your code, or lines up columns when assigning a bunch of values:

Code:
for item in Items() do
end

-- The whitespace above makes things more readable
for this in That() do
end

Code:
local bBoolean  = true
local sString   = 'Hello World'
local iInt      = 0

I don't think we need to be to anal about white space, however there is one area I have stronger feelings about, and that's function calls.

Code:
-- I have always disliked this style
function MyFunction( arg1, arg2 )
  SomeFunctionCall( arg1 )
end

-- I prefer the more condensed
function MyFunction(arg1, arg2)
  SomeFunctionCall(arg1)
end

I have never found the whitespace around parameters valuable in reading code. If anything it annoys me. But, I can be reasoned with if you have strong feelings :)
 
Variable naming
I may miss some, so please add.

Stating a variable with certain specific syntax can make your code much easier to read and therefore less prone to errors. I recommend the following conventions:

g_ for true globals (shared in some way between chunks, files, whatever): g_MyGlobal = pMyUnit

b for booleans: bDeadYet = false
i for integers: iCounter = 0
p for base game objects or objects you create yourself: pUnit = player:GetUnitByID(iUnitID)
s for strings: sTooltip = 'This is a tooltip'

I am also capitalizing each word in the variable, and trying to make the name of the variable as descriptive as possible. e.g. iR is not as good as iRandom which in turn is not as good as iRandomUnit. You can go overboard with this, obviously, so all things in moderation.

Some programmers prefer camel case (infoPanel, unitType, etc.) with no hungarian notation at all. The developers of the API use both, and sometimes for the same things; iPlayer or playerID. The best suggestion I can make is that (unlike Firaxes) you try to maintain some consistency in your code as to how you name your variables.
 
GameInfoTypes

Many example in the wiki use an inefficient and outdated method of getting to game defines, which you need to do often. The styles you generally see in the wiki are as follows:
Code:
local id = GameInfo.UnitClasses.UNITCLASS_WORKER.ID
local id = GameInfo["UnitClasses"].["UNITCLASS_WORKER"].ID
local id = GameInfo.UnitClasses{Type="UNITCLASS_WORKER"}().ID

In all cases where you can you should use the newer GameInfoTypes instead. This executes faster. There is an excellent guide to optimization you can read here.
Code:
GameInfoTypes.UNITCLASS_WORKER

It can also be used dynamically, as in the example:
Code:
local sUnitType = "UNIT_WORKER"
local iUnitID = GameInfoTypes[sUnitType]
 
Indenting

Mod Buddy's default is a standard 4 space tab. Everything within a [begin]-end should be indented; Where [begin] can be many things (function/for/if...) and end is always just end.

Code:
function MyFunction
    -- four space indent (one tab)
    if (bTest)
        -- another four space indent (two tabs)
    end
end

Again, by default settings Mod Buddy does not convert tabs to spaces so if you are using another editor I'd suggest making sure it's not set to do that.
 
Constants (Magic Numbers)

With very rare exceptions using a plain old number in your code is not ideal. A simple case of i + 1 is fine, but the logic behind it matters. If you are incrementing a counter in a loop, that's pretty straight forward. If for some reason not obvious you want to assign -1 to a value (so later you can check if it's -1 and know it's invalid, for example) then i = -1 is less informative.

It becomes what is known informally as a 'magic number'. A number with little or no meaning at first glance.

The way this is usually handled is by creating a constant. Since lua doesn't have true constants we use a variable and just treat it as a constant (not ideal but still better than magic numbers). Constants by convention are all uppercase.

Code:
local INVALID = -1

i = INVALID

This can be useful especially in the case of sparsely populated arrays, because assigning nil to an element deletes it. So...

Code:
local myTable = {}
local DISABLE = nil
local ENABLE = 1
local iKey = 1

myTable[iKey] = ENABLE -- adds an entry into myTable with a value of 1
myTable[iKey] = DISABLE -- removes the entry from the table
 
Are you maintaining a community Civ5 Lua style guide? It seems like this is over a year old and didn't find anything else.

I'd love to see some recommendations put down. It would make it easier for me at least.

Here are some examples of conventions that I stick to when I am not madly coding away.


Comments

File Beginning
Code:
--[[
-- @file: Main_Pre.lua
-- @author: carlos f hernandez [ apshai | carloscodex ]
-- @date: 2014-01-13
-- @modified: 2014-05-28
-- @module: main
-- @desc: main lua file for Civiliztion V Prehistory Mod
-- @src: https://github.com/carlosfhernandez/civ5-prehistoric
-- 
-- @desc:
-- We are all nothing but gum on the bottom of the great shoe. It is there 
-- that we live out lives, one step at a time, being squashed and carried 
-- about. We live on the hardness of pavement or the comfortable grating of 
-- sand. We are rushed to stopped, but we have not control. And sometimes we 
-- combine with another, to form a larger union, but are quickly picked off, 
-- being too much to bare for the great wearer of the shoe...
--
-- @usage:
--
--
--
-- @example:
--
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
end--]]
  1. "end" is not necessary, but provides an extra bit of information. This is a good place to label your comments, as an example, if a comment is to be continued.
  2. I keep the width to 78 characters or below
  3. I don't use more than two dashes in a row (last row is an example). It's just my convention and not necessary.
  4. I add the extra "-- " at the beginning of the lines which is not necessary.

This is perfectly valid:
Code:
--[[
I say, Mrs. Howell...
--]]


Methods
Code:
--
-- SerialEvenUnitCreated_Pre
--
-- @params [[ params from SerialEvenUnitCreated ]]
--
-- Attaches event to *SerialEventUnitCreated* to add additional checks for a 
-- newly created unit. This is used to detected the creation of a NEW UNIT.
--
-- This is typically used as a trigger for a newly created building. Since it
-- seems that there is no trigger detection for a newly created building
-- I worked around it by giving a free dummy unit and using the unit creation
-- trigger. The dummy unit references the building and any other actions.
--
function SerialEvenUnitCreated_Pre( playerID, unitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState, selected, military, notInvisible )
-- ...
end

--
-- FuncEeColdMedina
--
-- @param {Integer} param1 
-- @param {String} param2 
-- @param {Bool} param3
--
-- Does absolutely nothing, really.
--
-- @return {Integer}
--
function FuncEeColdMedina( param1, param2, param3 )
  return 0
end
Again, just my conventions.



And sometimes I break all sorts of coding rules and do this:
Code:
--[[
                                           


                  ██╗   ██╗███╗   ██╗██╗████████╗███████╗
                  ██║   ██║████╗  ██║██║╚══██╔══╝██╔════╝
                  ██║   ██║██╔██╗ ██║██║   ██║   ███████╗
                  ██║   ██║██║╚██╗██║██║   ██║   ╚════██║
                  ╚██████╔╝██║ ╚████║██║   ██║   ███████║
                   ╚═════╝ ╚═╝  ╚═══╝╚═╝   ╚═╝   ╚══════╝


        
                                  ,dM
                                 dMMP
                                dMMM'
                                \MM/
                                dMMm.
                               dMMP'_\---.
                              _| _  p ;88;`.
                            ,db; p >  ;8P|  `.
                           (``T8b,__,'dP |   |
                           |   `Y8b..dP  ;_  |
                           |    |`T88P_ /  `\;
                           :_.-~|d8P'`Y/    /
                            \_   TP    ;   7`\
                 ,,__        >   `._  /'  /   `\_
                 `._ """"~~~~------|`\;' ;     ,'
                    """~~~-----~~~'\__[|;' _.-'  `\
                            ;--..._     .-'-._     ;
                           /      /`~~"'   ,'`\_ ,/
                          ;_    /'        /    ,/
                          | `~-l         ;    /
                          `\    ;       /\.._|
                            \    \      \     \
                            /`---';      `----'
                           (     /            fsc
                            `---'
    
    
--]]
 
Back
Top Bottom