C++/Lua Request Thread

Alright, someone who knows lua better than me, why does this:
Code:
promotionCategories[xyz] = row.PediaType;
print("check: ",promotionCategories[xyz],"VS: ",row.PediaType);

produce this:


And how would I get to it work?

(It's a bit out of context, but the general gist of my issue is reached.)

Using the lua demo (http://www.lua.org/cgi-bin/demo), the only way I can replicate your output is to code as this
Code:
local promotionCategories = {}
local row = {}
row.PediaType = "PEDIA_LAND"

promotionCategories[1] = {}
print("check: ",promotionCategories[1],"VS: ",row.PediaType)
Which gives me this in the output of the lua demo page:
Code:
check: 	table: 0x18aa690	VS: 	PEDIA_LAND
This has to mean that for whatever reason promotionCategories[xyz] is not being interpretted by the game as a "key" within a table's "key, value" pair assignments that has integer or text data in its "value", but is being interpretted as a "key" that has another table in its "value".
 
Hi

I've recently added a Wonder Unit (<MaxGlobalInstances>1</MaxGlobalInstances>) to my mod, and I was wanting a notification to pop up, like what you get for building a wonder. Having looked at Firaxis' code, I decided to have a go, but I've got no real idea what I'm doing. Can anyone help?

What I came up with:
Spoiler :

Code:
print("300 Notification is working!");
local is300Ready = false;
function Is300Ready(iPlayer)
	local pPlayer = Players[iPlayer];
	if PlayerCanTrain (pPlayer, GameInfoTypes["UNIT_300"]) == true then
		is300Ready = true;
		GameEvents.PlayerDoTurn.Remove(Is300Ready);
	end
end
GameEvents.PlayerDoTurn.Add(Is300Ready);

function Is300Built(iPlayer)
	local pPlayer = Players[iPlayer]
	if (PlayerCanTrain(pPlayer, GameInfoTypes["UNIT_300"]) == false) and (is300Ready == true) then
		local portraitIndex = GameInfo.Units["UNIT_300"].PortraitIndex;
		IconHookup( portraitIndex, 80, GameInfo.Units["UNIT_300"].IconAtlas, instance.WonderConstructedAlphaAnim );
		GameEvents.PlayerDoTurn(iPlayer).Remove(Is300Built);
	end
	if iExtraGameData ~= -1 then
		CivIconHookup (iExtraGameData, 45, instance.CivIcon, instance.CivIconBH, instance.CivIconShadow, false, true );
		instance.WonderSmallCivFrame:SetHide(false);
		GameEvents.PlayerDoTurn.Remove(Is300Built);
	end
end
GameEvents.PlayerDoTurn.Add(Is300Built);
If I were you I would ask this as a new help request thread here in the SDK/LUA subforum because I think any meaningul answer would run on to too much intense code-talk for the original purposes of this thread.

Moderators: sorry about the back-to-back posts. I was certain I was using "multi". *sigh*
 
EDIT:

Whoops I had

Code:
promotionCategories[iJFCM] = row.PediaType;
		promotionCategories[iJFCM] = {};
		promotionCategories[iJFCM][1] = 1;
which should be reversed.

Thanks. Works.
 
I'll probably be on this thread a lot due to lack of knowledge/skill/contacts. Basically what I need at the moment is:
  • A trait/promotion that gives units a combat bonus of 15% when on a Trade Route path.
  • A trait/dummy building that gives scalable Food and City Strength for each outgoing trade route in any given city.
  • A promotion that, at the start or end of turn, whichever is easier, heals adjacent friendly units for 15% of their total HP and damages enemy units for the same amount.
  • Something (I'm not used to improvements) that, upon discovering Chivalry, gives IMPROVEMENT_KINTREE X Food, where X is the number of desert tiles adjacent to IMPROVEMENT_KINTREE.
  • Something that prevents combat units from entering a tile which has IMROVEMENT_KINTREE on it.

If any of this is impossible, please let me know.

Also, thanks in advance, hopefully?
 
The code for bullet 2 is below. It's adapted from code Leugi used in his Aymarra civ. I've assumed you wanted the trade route bonus to apply to external trade routes, not internal ones, so the code filters for that. Let me know if that's not the case. Bullet number 1 should be pretty easy but I don't have time now to do it. Bullet 3 seems doable, bullets 4 & 5 I'm not so sure about. You may want to open a new thread since you've got several requests; that might make it easier to manage.

Spoiler :
Code:
	<BuildingClasses>
		<Row>
			<Type>BUILDINGCLASS_SENSHI_TRADE_ROUTE_DUMMY</Type>
			<DefaultBuilding>BUILDING_SENSHI_TRADE_ROUTE_DUMMY</DefaultBuilding>
			<NoLimit>true</NoLimit>
			<Description>TXT_KEY_BUILDING_SENSHI_TRADE_ROUTE_DUMMY</Description>
		</Row>
	</BuildingClasses>

	<Buildings>
		<Row>
			<Type>BUILDING_SENSHI_TRADE_ROUTE_DUMMY</Type>
			<BuildingClass>BUILDINGCLASS_SENSHI_TRADE_ROUTE_DUMMY</BuildingClass>
			<Description>TXT_KEY_BUILDING_SENSHI_TRADE_ROUTE_DUMMY</Description>
			<MutuallyExclusiveGroup>-1</MutuallyExclusiveGroup>
			<Cost>-1</Cost>
			<FaithCost>-1</FaithCost>
			<GreatWorkCount>-1</GreatWorkCount>
			<NeverCapture>true</NeverCapture>
			<Defense>200</Defense>
			<DisplayPosition>8</DisplayPosition>
			<PortraitIndex>1</PortraitIndex>
			<IconAtlas>HALL_COLOR_ATLAS</IconAtlas>
		</Row>
	</Buildings>

	<Building_YieldChanges>
		<Row>
			<BuildingType>BUILDING_SENSHI_TRADE_ROUTE_DUMMY</BuildingType>
			<YieldType>YIELD_FOOD</YieldType>
			<Yield>2</Yield>
		</Row>
	</Building_YieldChanges>

You'll need to add the TXT_KEY, btw


Spoiler :
Code:
-- TestSenshi
-- Author: calcul8or
-- DateCreated: 9/24/2015 6:35:40 PM
--------------------------------------------------------------

function TradeOutStrength(PlayerID)
	local pPlayer = Players[PlayerID]
	local BldgID = GameInfoTypes["BUILDING_SENSHI_TRADE_ROUTE_DUMMY"]
	if (pPlayer:IsAlive() and pPlayer:GetCivilizationType() == GameInfoTypes["CIVILIZATION_GREAT_PUMPKIN"]) then
		print("Trade out!");
		local Routes = pPlayer:GetTradeRoutes();
		local NumRoutes = pPlayer:GetNumInternationalTradeRoutesUsed()
		if (NumRoutes > 0) then
			for city in pPlayer:Cities() do
				local iCounter = 0;
				local cityID = city:GetID();
				for tradeRouteID, tradeRoute in ipairs(Routes) do
					local OrigCity = tradeRoute.FromCity
					local OrigCityID = OrigCity:GetID();
					local DestCity = tradeRoute.ToCity
					local DestPlayer = DestCity:GetOwner()
					local dPlayer = Players[DestPlayer]
					
					if (dPlayer ~= pPlayer) then
						if (cityID == OrigCityID) then
							iCounter = iCounter + 1
						end
					end
				end
				city:SetNumRealBuilding(BldgID, iCounter)
			end
		end
	end
end

GameEvents.PlayerDoTurn.Add(TradeOutStrength)
Note: There might be more efficient ways to accomplish this, I sometime overthink things. So, if anyone has simplifications, let me know.
 
Thanks a lot! I'll be sure to add and adjust it (right after the BfZ prerelease).
 
Is it possible to work with such functions like Events.SerialEventHexHighlight in firetuner? I wonder where those Event functions are defined. I only know that GameEvents are defined in the DLL code.
 
I'm trying to make it so that units affected by the leadership of my great general replacement receive a custom promotion. However, instead, they're getting the Heal Instantly promotion. How might I fix that? (The lua I'm using is below)

Spoiler :
Code:
local civilisationID = GameInfoTypes["CIVILIZATION_SAILORMOON"]
local isSailorMoonCivActive = SailorMoon_IsCivilisationActive(civilisationID)
local mathCeil = math.ceil
local unitPromotionSailorSenshiID = GameInfoTypes["PROMOTION_GREAT_GENERAL"]
local unitPromotionSailorSenshiHealID = GameInfoTypes["TXT_KEY_PROMOTION_SAILORSENSHI"]
local unitSailorSenshiID = GameInfoTypes["UNIT_SAILORSENSHI"]

if isSailorMoonCivActive then
	print("Usagi Tsukino is in this game")
end
----------------------------------------------------------------------------------------------------------------------------
function UnitsNearSailorSenshi(playerID, unitID, unitX, unitY)
	local player = Players[playerID]
	local SailorSenshiNearby = false
	local unit = player:GetUnitByID(unitID)
	if (unit and (unit:IsCombatUnit() or unit:IsHasPromotion(unitPromotionSailorSenshiID))) then
		for SailorSenshi in player:Units() do
			if SailorSenshi:IsHasPromotion(unitPromotionSailorSenshiID) then
				if Map.PlotDistance(unitX, unitY, SailorSenshi:GetX(), SailorSenshi:GetY()) < 2 then
					SailorSenshiNearby = true
				end
			end
		end
		
		if (SailorSenshiNearby and unit:IsCombatUnit()) then
			if not unit:IsHasPromotion(unitPromotionSailorSenshiHealID) then
				unit:SetHasPromotion(unitPromotionSailorSenshiHealID, true)
			end
		else
			if unit:IsHasPromotion(unitPromotionSailorSenshiHealID) then
				unit:SetHasPromotion(unitPromotionSailorSenshiHealID, false)
			end
		end
		
	end
end
GameEvents.UnitSetXY.Add(UnitsNearSailorSenshi)
 
Spot the difference

Code:
local unitPromotionSailorSenshiID = GameInfoTypes["PROMOTION_GREAT_GENERAL"]
local unitPromotionSailorSenshiHealID = GameInfoTypes["TXT_KEY_PROMOTION_SAILORSENSHI"]
local unitSailorSenshiID = GameInfoTypes["UNIT_SAILORSENSHI"]

Hint: The middle one is wrong
 
Spot the difference

Code:
local unitPromotionSailorSenshiID = GameInfoTypes["PROMOTION_GREAT_GENERAL"]
local unitPromotionSailorSenshiHealID = GameInfoTypes["TXT_KEY_PROMOTION_SAILORSENSHI"]
local unitSailorSenshiID = GameInfoTypes["UNIT_SAILORSENSHI"]

Hint: The middle one is wrong

Oh wow. It's really that dumb of a mistake on my part.

Thanks for the help!
 
I'm at a slightly complicated situation right now:

I'm making the E&D for a mod, and I have absolutely no idea how to implement one of the Decisions in that mod which is that you have a City-State get into your empire, much like Austria's UA (but without the changed User Interface from Austria).

I'm able to implement just about everything in the Events and Decisions but the "get a city-state free of charge part".
 
Okay, so I have a function (That use to work)

Code:
-- Lua for assiging a random promotion.
---------------------------------------------------------------------------------------
LuaEvents.AssignArchetype.Add(
--------------
-- function to select a random promotion, uses the UnitPromotions_RandomArchetypes table
function(pUnit, pPromotion, bRandom, pCombatType)
	local tRandomArchetypes = {};
	local iNumInTable = -1; --Adjusted for for loop.
	local ePromotionToBeGained;
	local ePromotion = "PromotionType = '" .. GameInfo.UnitPromotions[pPromotion].Type .. "'";
	local eCombatType = false;
	if pCombatType then
		eCombatType = "UnitCombatType = '" .. GameInfo.UnitCombatTypes[pCombatType].Type .. "'";
	end

	-- Fill the Array
	for row in GameInfo.UnitPromotions_Archetypes[ePromotion] do
		iNumInTable = iNumInTable+1;
		tRandomArchetypes[iNumInTable] = {};
		tRandomArchetypes[iNumInTable][0] = row.NewPromotion;
		tRandomArchetypes[iNumInTable][1] = row.UnitCombatType;
		tRandomArchetypes[iNumInTable][2] = row.Weight;
	end

	local arrayMax = #tRandomArchetypes;

	-- For Adapatation.
	if eCombatType then
		for row = 0, arrayMax do
			if eCombatType == tRandomArchetypes[row][1] then
				ePromotionToBeGained = tRandomArchetypes[row][0];
			end
		end
	end

	-- No Weights.
	if not bRandom and not eCombatType then
		local randomPromotion = math.random(0, iNumInTable);
		ePromotionToBeGained = tRandomArchetypes[randomPromotion][0];
	end

	-- Use the Weights
	while bRandom and not eCombatType do
		local randomPromotion = math.random(0, iNumInTable);
		local weight = tRandomArchetypes[randomPromotion][2];
		local randomValue = (math.random(0, 100) + math.random(0, 100))/2;
		local randomTest = math.random(60, 140);
		if(weight+randomValue) >= randomTest then
			bRandom = false;
			ePromotionToBeGained = tRandomArchetypes[randomPromotion][0];
		end
	end

	local pPromotionToBeGained = GameInfoTypes[ePromotionToBeGained];
	pUnit:SetHasPromotion(pPromotion, false);
	pUnit:SetHasPromotion(pPromotionToBeGained, true);
	print("JFCMFunctions -|- AssignArchetype -|- UnitType: ",pUnit:GetUnitType()," Promotion: ",ePromotionToBeGained);
end)
---------------------------------------------------------------------------------------

However, now I am getting this error:

Runtime Error: Indexing by type is not available for this table.

I kinda recently merged two tables into this
Code:
<Table name="UnitPromotions_Archetypes">
		<Column name="PromotionType" type="text" reference="UnitPromotions(Type)"/>
		<Column name="NewPromotion" type="text" reference="UnitPromotions(Type)"/>
		<Column name="UnitCombatType" type="text" reference="UnitCombatInfos(Type)"/>
		<Column name="Weight" type="integer" default="0"/>
	</Table> <!-- Lua support -->

and changed the call function to be calling using a table rather than hardcoded values

the table reference prints the same output as the previous GameInfoTypes["PROMOTION_WHATEVER"] (the ID value of 148)

here is the calling function:
Code:
---------------------------------------------------------------------------------------
function RandomArchetype(playerID, unitID, eUnit, iHexX, iHexY)
	local pPlayer = Players[playerID];
	local pUnit = pPlayer:GetUnitByID(unitID);
	-- Sterilize
	if(pPlayer == nil or pUnit == nil or pUnit:IsDead()) then
		-- Abort!
		return;
	end
	
	arrayMax = #tRandomArchetypes;

	for array = 0, arrayMax do
		if pUnit:IsHasPromotion(GameInfoTypes[tRandomArchetypes[array]]) then

			-- Call JFCMFunctions.lua
			print("JFCMLua -|- RandomArchetype -|- Civ: ",pPlayer:GetCivilizationType()," Unit: ",pUnit:GetUnitType(),"Promotions: ",GameInfoTypes[tRandomArchetypes[array]],"Same as?",GameInfoTypes["PROMOTION_DAWN_BRIGADE"]);
			LuaEvents.AssignArchetype(pUnit, GameInfoTypes[tRandomArchetypes[array]], true, false);
		end
	end

	-- City States might build one of these units!
end
---------------------------------------------------------------------------------------
Events.SerialEventUnitCreated.Add(RandomArchetype);

(The array is filled upon Lua activation, so no issues there.)
So Idunno. It use to let me roll through the database via Type, but isn't anymore. :/

Using a db viewer, I see the table (and it's data) just fine. No errors there either.
I'm sure I'm missing something silly here.

EDIT: The error is for line 95 (which is the:
for row in GameInfo.UnitPromotions_Archetypes[ePromotion] do
line)
 
Runtime Error: Indexing by type is not available for this table.

EDIT: The error is for line 95 (which is the:
for row in GameInfo.UnitPromotions_Archetypes[ePromotion] do
line)
Change the square brackets around ePromotion to parentheses
Code:
	for row in GameInfo.UnitPromotions_Archetypes(ePromotion) do

...

How can I get the plots, which can be reached by a unit in the current turn?
See these plot iterators in conjunction with plot:MovementCost.
 

Thank you very much!
I don't understand how to use the MovementCost function. The game always crashes, when I use it in Firetuner.
I selected a unit and used the following code in an action control:
Spoiler :
local x, y = 12, 17
local pUnit = UI.GetHeadSelectedUnit()

local pFromPlot = Map.GetPlot(x, y)
local pPlot = Map.GetPlot(x+1, y+1)
cost = pPlot:MovementCost(pUnit, pFromPlot)


Then Civ5 just crashes. :(
 
The Lua API is bugged in that it uses BasicLuaMethod to invoke CvPlot::movementCost()

The movementCost() method expects a CvUnit* and a CvPlot* parameter and has an optional third parameter

Whoever wrote the plot API code (and admittedly it looks like it was originally auto-generated from the C++ code) failed to read the comments before the declaration of the BasicLuaMethod methods

Code:
//These are helper templates that will allow for quick and easy member function wrapping when implementing a Lua method.
//They currently do not support non-native types or optional values so the scope is quite limited.

so
Code:
return BasicLuaMethod(L, &CvPlot::movementCost);
violates both "non-native types" and "optional values"

Regardless of what you pass from Lua, the C++ movementCost method is being called with garbage in the parameters - hence the crash

pPlot:MovementCost() is never used by the game core Lua code, so presumably Firaxis never needed to fix it.

All the used/working API methods that take Lua objects (at least the ones I know and checked) all explicitly pull the parameters from the Lua stack (via CvLuaXyz::GetInstance()) and call the associated C++ method directly, eg
Code:
//bool isValidDomainForLocation(CyUnit* pUnit);
int CvLuaPlot::lIsValidDomainForLocation(lua_State* L)
{
	CvPlot* pkPlot = GetInstance(L);
	[B][COLOR="Red"]CvUnit* pkUnit = CvLuaUnit::GetInstance(L, 2);[/COLOR][/B]

	const bool bResult = pkPlot->isValidDomainForLocation(*pkUnit);
	lua_pushboolean(L, bResult);
	return 1;
}
 
So there is no easy solution for my problem? :(:(
I originally wanted to write a Lua code for the AI. Thus I needed to check, how far a unit could go and still have movements left to attack in the current turn.
 
So there is no easy solution for my problem?
The easy solution is to fix the DLL, not particularly hard, just 1001 compatibility issues!

However, even if fixed, pPlot:MovementCost() won't answer your question directly, as it assumes pFromPlot and pToPlot are adjacent. For your example above, of adding 1 to both the x and y coordinates, that depends on whether y is even or odd.

To make the Lua easy, you'd need to expose the C++ route finding code (the A-Star algorithms in all their convoluted glory) API - and that is definitely non-trivial.
 
Back
Top Bottom