Pazyryk
Deity
- Joined
- Jun 13, 2008
- Messages
- 3,584
If you want to do this through DLL rather than Lua, see Adding new XML Tags/Tables to work through the DLL.
Let's say you want a simple policy prerequisite to build a building (there isn't one in the base game).
You can add columns to existing core tables via SQL. Here's the code you need in an SQL file:
SQL is added exactly as you add XML files (as an OnModActivated). Just make sure that the above file runs before your XML where you are using the new "PrereqPolicy" tag.
Now you need to add some Lua code to tell the dll what to do with this new table column:
That's it! You can now use this new tag to make any building construction dependent on having any particular policy, just like you use any other xml tag.
Following the example above, you should be able to add PolicyPrereq tags to Units and Techs in a similar way using these GameEvents:
GameEvents.PlayerCanTrain.Add(function(iPlayer, unitTypeID) ... end)
GameEvents.PlayerCanResearch.Add(function(iPlayer, techTypeID) ... end)
One suggestion: I actually add the above tag as "PrereqPolicy_paz". This is so that there are no problems down the road if the developers add a tag with this exact name (or perhaps a dll modder, once that is possible).
Also note that the above prereq will not be added to the pedia. You'll have to do that yourself. Otherwise, there will be no indication in the game why the building cannot be built.
**************************************************************
Added 12/29
OK, let's say you want to run a conditional test of your own using a tag "LuaReq". Some of you may remember many such tags in FFH (and mods thereof). They are extremely powerful because you can write short Lua functions to create whatever condition you want.
As above, you add another column to Buildings like this:
Now let's make a Monument conditional on something, say turn > 5 (you can do this in XML of course, but I do everything in SQL so it's much easier for me this way):
That works, but it's pretty limited. What you really want to do is this:
If RunUserTestFunction() returns true, then Monument can be built. If it reuturns false, then Monument cannot be built. (Example code below returns true when turn is > 5 and iPlayer == 0.) The code to make this all work for Buildings is below (adds both LuaReq and PrereqPolicy). The only thing you need to do is supply your own Lua condition test functions and call the compile function during game init (the very last line below). If you want to add LuaReq to other tables, hopefully you can use this as skeleton code to do it (be sure to add any Lua callback tags to the two tables on lines 2 and 3).
Edit: One correction, you should (in theory) be able to add an argument to a LuaReq function call. Just be aware that it will be compiled in a global environment. So ReqTurn(5) should work and ReqTurn(g) should work if g is a global, but ReqTurn(x) won't work if x is local. Of course, if the value you want is in a global, you can get it from within the function anyway (that's how I use args above) so I'm not sure if that will be useful. But the ability to supply a hard value would certainly be useful. For example, you could have many buildings call the same ReqTurn function, but each has a different turn requirement. Or you could require different gold levels in treasury. Or different total culture. Or... The possibilities are really endless. (I haven't actually tested this yet.)
**************************************************************
There were a bunch of "Can" game events added in patch 1.0.1.332. Here's the whole list:
**************************************************************
Edit 11/2012: Thanks to DonQuiche, we now have a complete wiki listing all of the GameEvents. (A few errors here and there, but on the whole it is an awesome resource...)
Let's say you want a simple policy prerequisite to build a building (there isn't one in the base game).
You can add columns to existing core tables via SQL. Here's the code you need in an SQL file:
Code:
ALTER TABLE Buildings ADD COLUMN 'PrereqPolicy' TEXT DEFAULT NULL;
SQL is added exactly as you add XML files (as an OnModActivated). Just make sure that the above file runs before your XML where you are using the new "PrereqPolicy" tag.
Now you need to add some Lua code to tell the dll what to do with this new table column:
Code:
GameEvents.PlayerCanConstruct.Add(
function(iPlayer, buildingTypeID)
local prereqPolicy = GameInfo.Buildings[buildingTypeID].PrereqPolicy
if prereqPolicy then
local player = Players[iPlayer]
local policyID = GameInfo.Policies[prereqPolicy].ID
if not player:HasPolicy(policyID) then
return false
end
end
return true
end)
That's it! You can now use this new tag to make any building construction dependent on having any particular policy, just like you use any other xml tag.
Following the example above, you should be able to add PolicyPrereq tags to Units and Techs in a similar way using these GameEvents:
GameEvents.PlayerCanTrain.Add(function(iPlayer, unitTypeID) ... end)
GameEvents.PlayerCanResearch.Add(function(iPlayer, techTypeID) ... end)
One suggestion: I actually add the above tag as "PrereqPolicy_paz". This is so that there are no problems down the road if the developers add a tag with this exact name (or perhaps a dll modder, once that is possible).
Also note that the above prereq will not be added to the pedia. You'll have to do that yourself. Otherwise, there will be no indication in the game why the building cannot be built.
**************************************************************
Added 12/29
OK, let's say you want to run a conditional test of your own using a tag "LuaReq". Some of you may remember many such tags in FFH (and mods thereof). They are extremely powerful because you can write short Lua functions to create whatever condition you want.
As above, you add another column to Buildings like this:
Code:
ALTER TABLE Buildings ADD COLUMN 'LuaReq' TEXT DEFAULT NULL;
Now let's make a Monument conditional on something, say turn > 5 (you can do this in XML of course, but I do everything in SQL so it's much easier for me this way):
Code:
UPDATE Buildings SET LuaReq='Game.GetGameTurn() > 5' WHERE Type='BUILDING_MONUMENT';
That works, but it's pretty limited. What you really want to do is this:
Code:
UPDATE Buildings SET LuaReq='RunUserTestFunction()' WHERE Type='BUILDING_MONUMENT';
If RunUserTestFunction() returns true, then Monument can be built. If it reuturns false, then Monument cannot be built. (Example code below returns true when turn is > 5 and iPlayer == 0.) The code to make this all work for Buildings is below (adds both LuaReq and PrereqPolicy). The only thing you need to do is supply your own Lua condition test functions and call the compile function during game init (the very last line below). If you want to add LuaReq to other tables, hopefully you can use this as skeleton code to do it (be sure to add any Lua callback tags to the two tables on lines 2 and 3).
Code:
--add any tags and their tables to the two tables below in matching pairs (only for tags that "call" a Lua function)
local luaCallbackTable = { "Buildings"}
local luaCallbackTag = { "LuaReq"}
---------------------------------------------------------------
-- Compile Lua callback text (run once at game init)
---------------------------------------------------------------
local bLuaCallbacksCompiled = false
local LuaCallback = {} --will hold all lua functions (as function values) keyed by function text exactly as it appears in table
args = {} --loadstring always compiles in global environment, so use this global to hold arguments for function call
function CompileTableLuaCallbacks()
-- this must run after functions defined!
print("Compiling table Lua callbacks...")
for i, callbackTable in ipairs(luaCallbackTable) do
local callbackTag = luaCallbackTag[i]
for item in GameInfo[callbackTable]() do
local functText = item[callbackTag]
if functText and not LuaCallback[functText] then
print("Table "..callbackTable..", tag "..callbackTag..", function "..functText)
local funct = assert(loadstring("return "..functText))
LuaCallback[functText] = funct
end
end
end
bLuaCallbacksCompiled = true
end
---------------------------------------------------------------
-- Functions for new tags added to tables (including Lua callbacks)
---------------------------------------------------------------
-- Building requirements
GameEvents.PlayerCanConstruct.Add(
function(iPlayer, buildingTypeID)
local player = Players[iPlayer]
local building = GameInfo.Buildings[buildingTypeID]
local prereqPolicy = building.PrereqPolicy
if prereqPolicy then
local policyID = GameInfo.Policies[prereqPolicy].ID
if not player:HasPolicy(policyID) then
return false
end
end
local functText = building.LuaReq
if functText and bLuaCallbacksCompiled then
local funct = LuaCallback[functText]
args.iPlayer = iPlayer
args.buildingTypeID = buildingTypeID
if not funct() then --!!! that was the function call, so make sure args holds whatever you need !!!
return false
end
end
--add any additional restrictions to buildings here
return true
end)
---------------------------------------------------------------
-- An example user defined function (can be in another file but must be same state)
-- Note that any arguments must be passed through a global (I use args here)
---------------------------------------------------------------
function RunUserTestFunction()
print("called RunUserTestFunction")
iPlayer = args.iPlayer
buildingTypeID = args.buildingTypeID
print(iPlayer)
print(buildingTypeID)
if Game.GetGameTurn() > 5 and iPlayer == 0 then
return true
end
return false
end
---------------------------------------------------------------
-- !!! Important !!! Put this somewhere in your program where it runs
-- at game init (after all functions above have been defined)
---------------------------------------------------------------
CompileTableLuaCallbacks()
Edit: One correction, you should (in theory) be able to add an argument to a LuaReq function call. Just be aware that it will be compiled in a global environment. So ReqTurn(5) should work and ReqTurn(g) should work if g is a global, but ReqTurn(x) won't work if x is local. Of course, if the value you want is in a global, you can get it from within the function anyway (that's how I use args above) so I'm not sure if that will be useful. But the ability to supply a hard value would certainly be useful. For example, you could have many buildings call the same ReqTurn function, but each has a different turn requirement. Or you could require different gold levels in treasury. Or different total culture. Or... The possibilities are really endless. (I haven't actually tested this yet.)
**************************************************************
There were a bunch of "Can" game events added in patch 1.0.1.332. Here's the whole list:
Code:
(Lua) Added GameEvents.CityCanBuyAnyPlot(ownerID, cityID) (TestAll)
(Lua) Added GameEvents.CityCanBuyPlot(ownerID, cityID, plotX, plotY) (TestAll)
(Lua) Added GameEvents.CityCanCreate(ownerID, cityID, projectTypeID); (TestAll)
(Lua) Added GameEvents.CityCanMaintain(ownerID, cityID, processTypeID); (TestAll)
(Lua) Added GameEvents.CityCanPrepare(ownerID, cityID, specialistTypeID); (TestAll)
(Lua) Added GameEvents.CityCanTrain(ownerID, cityID, unitTypeID); (TestAll)
(Lua) Added GameEvents.PlayerAdoptPolicy(playerID, policyTypeID); (Hook)
(Lua) Added GameEvents.PlayerAdoptPolicyBranch(playerID, policyBranchTypeID); (Hook)
(Lua) Added GameEvents.PlayerCanAdoptPolicy(playerID, policyTypeID); (TestAll)
(Lua) Added GameEvents.PlayerCanAdoptPolicyBranch(playerID, policyBranchTypeID); (TestAll)
(Lua) Added GameEvents.PlayerCanConstruct(playerID, buildingTypeID); (TestAll)
(Lua) Added GameEvents.PlayerCanCreate(playerID, projectTypeID); (TestAll)
(Lua) Added GameEvents.PlayerCanEverReseearch(playerID, techtypeID); (TestAll)
(Lua) Added GameEvents.PlayerCanMaintain(playerID, processTypeID); (TestAll)
(Lua) Added GameEvents.PlayerCanPrepare(playerID, specialistTypeID); (TestAll)
(Lua) Added GameEvents.PlayerCanResearch(playerID, techTypeID); (TestAll)
(Lua) Added GameEvents.PlayerCanTrain(playerID, unitTypeID); (TestAll)
(Lua) Added GameEvents.TeamSetHasTech(teamID, techID); (Hook)
**************************************************************
Edit 11/2012: Thanks to DonQuiche, we now have a complete wiki listing all of the GameEvents. (A few errors here and there, but on the whole it is an awesome resource...)