Need help with Lua and creating a Trait

Harkodos

Warlord
Joined
Feb 9, 2016
Messages
197
I'm currently interested in trying to program some Lua for a custom Civilization that I'm working on, but I don't know how to program any Lua (I have experience with C++, but until I've been shown how to get my feet, I'll be clueless on the subject).

I want to add a trait to my Civ, where the player receives Faith whenever a unit pillages an enemy improvement. I'm certain this can't be done with the XML format, which is why I need help with the Lua.

Anyone mind helping me out with this?

P.S. A step-by-step explanation of what everything does would be most appreciated!
 
P.S. A step-by-step explanation of what everything does would be most appreciated!
A complete step-by-step would be well beyond anything I (or anyone else) would have time to do as an organized whole, but this thread is a decent intro to how lua scripts are structured: http://forums.civfanatics.com/showthread.php?t=533853

As a first step in creating an lua script you have to know what game-core 'event' will be best to use to run your code under. In Civ5, lua scripts need to be subscribed to one event at the least. The most common event is the one called PlayerDoTurn, which runs for every player in sequence (including City-States and Barbarians) when the "NEXT TURN" button is pressed. See here for a list of the most-commonly-used 'GameEvents' that lua scripts can be subscribed to: http://modiki.civfanatics.com/index.php?title=GameEvents_(Civ5_Type) This a sub-page from the basic modding lua reference in the forum's modwiki: http://modiki.civfanatics.com/index.php/Lua_and_UI_Reference_(Civ5)

So, If I want to create a function that runs from the PlayerDoTurn event, my basic script has to look something like this
Code:
function PlayerTurnProcessing(iPlayer)
	--we want to do something in this function related to player turn processing
	print(iPlayer)
end
GameEvents.PlayerDoTurn(PlayerTurnProcessing)
This would print the player ID# for active players in the game for each player still alive as each player's turn is processed by the game.

'iPlayer' is a variable I created to take the parameter that is passed by the core game to any code subscribed to the PlayerDoTurn 'Game Event'. I could just as easily have called it 'Cheeseburgers' instead of 'iPlayer'

The value that will be passed will be an assigned player ID# made when the current game is set-up. In single-player games, the human player (regardless of the civilization they are playing as) is always PlayerID# 0, and the Barbs are always PlayerID# 63. Major players (including the human player) use Player ID#s 0 - 21, City-States use #s 22 - 62. But if there are only three major civilizations set-up to be used in a game, they will use only Player ID#s 0, 1, and 2. (City-States will still use #22 - #62, Barbs will still use #63).

Once you know what game event you need to use, there are still a boatload of 'methods' that have to be looked-at to determine if they are what you need to get information from the game into your lua script so you can manipulate it. The big ones most-commonly-used are
So, going back to the previous bit of code, if I only want to print the ID#s for players that are not the Barbarians, I do as will follow, but as a 1st action I need to translate the PlayerID# the game sends to the PlayerDoTurn event into a specific player 'object' or 'pointer', so that we are pulling the information for a specific player rather than just the generic 'Player' that is shown in all the Player methods, like so:
Code:
local pPlayer = Players[iPlayer]
  • Players is a meta-table (for lack of better description) that Firaxis provides as part of the Civ5 lua, and which holds all player information for all players in the current game, indexed by the player ID#.
  • The code just shown (local pPlayer = Players[iPlayer]) creates a localized variable that holds all the 'specific player' information we will need to be able to access, so our overall code can thus become
    Code:
    function PlayerTurnProcessing(iPlayer)
    	local pPlayer = Players[iPlayer]
    	--we want to do something in this function related to player turn processing
    	print(iPlayer)
    end
    GameEvents.PlayerDoTurn(PlayerTurnProcessing)
I have not done anything else yet with sorting as to whether or not this player is the Barbarians. There is a player method that can tell us this by returning a true/false boolean as to whether the specific player is the Barbarians: http://modiki.civfanatics.com/index.php?title=Player.IsBarbarian_(Civ5_API)

So I just do a check for whether the player is the Barbarians, and exclude them from the 'print' line:
Code:
function PlayerTurnProcessing(iPlayer)
	local pPlayer = Players[iPlayer]
	--we want to do something in this function related to player turn processing
	if not pPlayer:IsBarbarian() then
		print(iPlayer)
	end
end
GameEvents.PlayerDoTurn(PlayerTurnProcessing)

By itself, even in placed in an lua-file within your mod's project, the code won't do anything because you also need to tell modbuddy how to activate this lua-file. It needs to be set up as an InGameUIAddin entry in the 'Content' tab of modbuddy, and the setting for Import Into VFS should be 'false'. See whoward69's what ModBuddy setting for what file types tutorial for more detailed information on that. Once the file is properly set-up as an InGameUIAddin, every time the human presses the 'NEXT TURN' button, the code will run for every player still alive in numerical sequence, and print their player ID# into the game's lua.log file. But the Barbarians will be excluded, so you should never see '63' being printed in the lua.log.

Hope that at least is a good start.
 
My computer hates most of the links you've provided (I believe my Antivirus software has warned of the Modiki being host to a virus as well, so probably a good thing).

Okay, so I sort of get how all that works, but I think I need some more specific information, namely:
Where in the Civ 5 files can I find the definitions and workings of the PillageGold definition? I know that several of the files have it defined as "Integer" and whatnot near the top, but where can I find the actual workings of that function? I'd like to start there, if possible.
 
You appear to be referring to column 'PillageGold' used in the xml in table 'Improvements'.
<Column name="PillageGold" type="integer" default="0"/>
Depending on what it is you want to do, it may not apply to lua-scripting. If you wish to increase or decrease the amount of gold from pillaging a specific improvement, this is better done as an XML 'code' via an update to the specific improvement. As in if I want to adjust the gold from pillaging a farm, I would create an XML file in ModBuddy for my mod that adjusts farm pillaging gold, and I would do as follows to increase this value to '35' for the farm:
Code:
<GameData>
	<Improvements>
		<Update>
			<Where Type="IMPROVEMENT_FARM" />
			<Set PillageGold="35" />
		</Update>
	</Improvements>
</GameData>
The XML-file where this code appears would also have to be given an "actions" instruction within mod to UpdateDatabase when the mod is 'Activated'. See http://forums.civfanatics.com/showpost.php?p=12203519&postcount=2

All the stuff like this that you see in the game's Firaxis-provided XML-files is in reality just text that is later inserted into the game's SQL database (hence the need to 'UpdateDatabase'). Even then after it is inserted into the SQL form of the game's database it all just text, really. Some parts of that text are then 'read' and implimented by specific-purpose lua files provided as part of the base-game from Firaxis. This type of lua file is usually only meant to control and create the various human-used pop-up menus and UI, and to impliment the actions the human player makes while in these pop-up UI panels. The majority of the stuff that gets inserted into the game's XML/SQL system from the game's base XML files is implimented at the dll level.

Gold Pillaging might be completely or mostly implimented within the game's UnitPanel.lua file, but I haven't looked to see one way or another. UnitPanel.lua and its linked files UnitPanel.xml and UnitPanel_small.xml contain all the instructions for building the unit-panel the human player sees in the lower-left of the game UI, and 'manage' all the instructions for which pushbuttons are valid at any given time. It also 'sends back to the rest of the game' (as it were) the instructions or data related to whichever of the buttons the human player presses.
 
I get how the PillageGold item works, what I wanted to do was to find the actual code that told that how to function and then work in a similar version where instead the player is provided with Faith. So it's result in PillageFaith as it were.

Without being familiar with the actual game's internal workings, I'd assume from past modding experiences that swapping out one term for another and working the feature as would be expected would result in the desired outcome.

I guess I'll check in UnitPanel for the time being then; see if what I'm looking for is in there.
 
The code that handles the "PillageGold" database column is in the game core C++ DLL. Pretty sure it was one of the columns I converted to a yield table (see the Unified Yields thread) so you'd be best off using one of the modded DLLs that implement those if you want to go that route
 
Alright, I've delved into the DLL stuff and I think I'm doing pretty well all considered, thanks to my years of C++ knowledge. However, I wanted to make sure that I'm following the right direction.

So, I'm currently adding a Boolean argument for FaithFromPillaging and an integer argument for PillageFaith. However, as I only want one Civ to be able to receive Faith from pillaging improvements; I don't believe the two are quite as connected as I'd like. I've recreated the Improvements tables from the base game and major expansions and I was wondering if setting them to Update will allow other Civs to pillage for Faith as well. I only want the one I'm currently making to be able to do this.

I'm also adding an argument in the files similar to the IsAbleToAnnexCityStates; is this necessary to accomplish what I'm trying to do?
 
Just my opinion, but... why edit the DLL? Isn't that overkill?

You could hook Events.SerialEventImprovementDestroyed():

http://modiki.civfanatics.com/index.php?title=Events.SerialEventImprovementDestroyed_(Civ5_API)

The example given there isn't great, but you can see the x and y coord coming in. From that you'd have to use Map.GetPlot, Plot:GetOwner() to figure out if it's owned or not, Plot:GetUnit(0) to figure out the unit (uncertain if this works for > 1UPT?), then Unit:GetOwner() to figure out the owner. Be sure to check for nil values along the way.

Once you know the plot is owned by somebody, that somebody isn't your civ, there's a unit on the plot (tile), and the unit owner is your civ, you can credit the civ the faith your trait says they would get through pillaging, because the event hook ensures this code only runs when somebody destroys (pillages) an improvement.

(Note: that's strictly not true. For instance, it fires when Barbarian Encampments are destroyed, too. But those are in unowned territory.)
 
Just tested it, Events.SerialEventImprovementDestroyed doesn't fire on pillaging (only when an improvement is actually destroyed completely, e.g. by being replaced with another improvement). Even then, it only fires once the new improvement is within the human player's view.
 
I decided to scrap the idea of messing with the DLL (ModBuddy didn't want to compile it for me for some reason, said there was a bug with 7zip when compiling). So instead my unique Civ will just focus on pillaging in a different way.

But now I've got a different problem: I added some new promotions to my game and after adding/removing something they no longer work. I've checked the Civilopedia and they're programmed into the game; they just can't be chosen for some reason. I'm certain they're programmed in correctly (they were certainly working before I decided to change the trait to Faith from Kills for a short while). I'm using the method that the Zulu use for their unique promotions, but I can't select any of them, even though the original prerequisite is already present.

Short answer is: I need someone to look through my Mod files and see what might be missing to prevent me from choosing my new promotions.
 

Attachments

Just tested it, Events.SerialEventImprovementDestroyed doesn't fire on pillaging (only when an improvement is actually destroyed completely, e.g. by being replaced with another improvement). Even then, it only fires once the new improvement is within the human player's view.

Weeellll.... that sucks.
 
Never mind, I fixed my problem. It seems I didn't realize I had to define the Prerequisite Promotion under UnitPromotions_UnitCombats, as I figured it already being defined as part of the Civilization's trait was enough. I was wrong (which explains why it worked earlier, since I did have that set up that way at one point).

Thanks for everyone's time and effort at least. :)
 
Back
Top Bottom