• We are currently performing site maintenance, parts of civfanatics are currently offline, but will come back online in the coming days. For more updates please see here.

Add new instances to a core lua-file

martijnaikema

Warlord
Joined
Oct 30, 2010
Messages
111
In the EnemyUnitPanel.xml the unit-bonusses that are displayed in the panel (like Rough Terrain Bonus) are created as instances. In the xml it is displayed as:

Spoiler :

<Instance Name="UsCombatInfo" >

<Label Anchor="R,C" Offset="52.0" String=" " Font="TwCenMT14" ID="Text" WrapWidth="230" LeadingOffset="-6" >

<Label Anchor="R,C" AnchorSide="O.I" Offset="4.0" Font="TwCenMT14" ID="Value" />
</Label>
</Instance>


If I edit the core file (EnemyUnitPanel.lua) and add my bonus to it, it is displayed in the screen:

Spoiler :

local iType = pMyUnit:GetNameKey();
if (pToPlot:IsRiver() and iType == "TXT_KEY_UNIT_WATERGEUS") then

-- RiverModifier
iModifier = 50;
if (iModifier ~= 0) then
controlTable = g_MyCombatDataIM:GetInstance();
local RiverBonus = "River";

controlTable.Text:LocalizeAndSetText( "TXT_KEY_EUPANEL_ATTACK_INTO_BONUS", RiverBonus );

controlTable.Value:SetText( GetFormattedText(strText, iModifier, true, true) );
end
end
-- pMyUnit = UI.GetHeadSelectedUnit();
-- pToPlot = the mouseover of a certain plot



When I add this to the core lua-file the text and bonus is displayed in the panel:
BonusRiver.jpg


But I do not want to edit the core file but create the new instance with my own lua. But I can not get it to work. Here is some more info from the instance.

* The instance in the core-lua is created through the command: g_MyCombatDataIM:GetInstance = InstanceManager:new( "UsCombatInfo", "Text", Controls.MyCombatResultsStack );
* The instance is created by including the InstanceManager.lua
* To add a new instance I guess I have to read the instances allready created by EnemyUnitPanel.lua and add my instance to it
* I found this thread really interesting cause it shows the power of ContextPtr-commands. I tried different options, but I can't get it to work.
I hope someone knows how I should add a new Instance to the panel.

Edit:When I try to create a new instance I get an error "Instance Manager built with a bad Root Control [USCombatInfo] [Text]"
 
I checked the InstanceManager.lua file and I saw that it uses ContextPtr:BuildInstance and ContextPtr:BuildInstanceForControl.

But I can't figure out how they work I tried the following with the correpsonding errors:

1
ContextPtr:BuildInstanceForControl("/InGame/WorldView/EnemyUnitPanel/UsCombatInfo"):SetText( "Attack into River", 50);

Error:
LuaContext::BuildInstanceForControl could not be called: 2 values on stack. Expected 1 parent table and 3 arguments

2
ContextPtr:BuildInstance("/InGame/WorldView/EnemyUnitPanel/UsCombatInfo"):SetText( "Attack into River", 50, false, true);

Error:
LuaContext::BuildInstance could not be called: 2 values on stack. Expected 1 parent table and 2 arguments

3
ContextPtr:BuildInstance("/InGame/WorldView/EnemyUnitPanel/UsCombatInfo").Text:SetText( "Attack into River")
ContextPtr:BuildInstance("/InGame/WorldView/EnemyUnitPanel/UsCombatInfo").Value:SetText( "50")

Error:
LuaContext::BuildInstance could not be called: 2 values on stack. Expected 1 parent table and 2 arguments
 
I still can not get it to work. Here is what I did:

1) Created an lua-file. Named it watergeusbonus.lua.

It contains the code to make a new instance and refers to the "MyCombatResultStack" of the core EnemyUnitPanel.lua file:

Spoiler :

local g_MyCombatDataRiverIM = InstanceManager:new( "UsCombatInfoRiver", "Text", Controls.MyCombatResultsStack );
local controlTable = g_MyCombatDataRiverIM:GetInstance();

ContextPtr:BuildInstanceForControl ( "UsCombatInfoRiver", controlTable , Controls.MyCombatResultsStack );

controlTable.Text:SetText ( "Bonus attacking into River" );
controlTable.Value:SetText ( "50" )



2) Created 2 xml-files. Named it watergeusbonus.xml and watergeusbonus_small.xml.

It contains the new instance "UsCombatInfoRiver"

Spoiler :

<?xml version="1.0" encoding="utf-8"?>
<Context ColorSet="Beige_Black" Font="TwCenMT20" FontStyle="Shadow" >

<!-- Combat Info Label Instances for River -->

<Instance Name="UsCombatInfoRiver" >
<Label Anchor="R,C" Offset="52.0" String=" " Font="TwCenMT14" ID="Text" WrapWidth="230" LeadingOffset="-6" >
<Label Anchor="R,C" AnchorSide="O.I" Offset="4.0" Font="TwCenMT14" ID="Value" />
</Label>
</Instance>

</Context>



3) Test ingame. I get an error:

Incorrect type for pointer argument. Using NULL instead.

4) The text itself is not displayed in the panel, but on the right side of the screen

attachment.php



I really hope someone can help me with this. I have uploaded the current version of the MOD so you can test it out yourself.
 

Attachments

I don't think you need to call ContextPtr:BuildInstanceForControl. What's going on here is:

local controlTable = g_MyCombatDataRiverIM:GetInstance();
You ask for a new instance of the UsCombatInfoRiver label. This label is already tied to the Controls.MyCombatResultsStack so it will be added to this stack automatically. Then you call:

ContextPtr:BuildInstanceForControl ( "UsCombatInfoRiver", controlTable , Controls.MyCombatResultsStack );
What this call actually does is build a sort of "blank" instance. If you look through the various lua files you'll see that it's only ever used on an empty instance like:

local textControls = {};
ContextPtr:BuildInstanceForControl("LTextEntry", textControls, controlTable.PoliciesStack);

If this call is causing your pointer error then it could well break the label's association with the MyCombatResultsStack, which would make it appear at the right center of the screen like it does now.

Also, depending on when your .lua is executed you may have to tell the game to re-size the MyCombatResultsStack when you're finished:

Code:
Controls.MyCombatResultsStack:CalculateSize();
Controls.DetailsGrid:DoAutoSize();
Controls.DetailsGrid:SetSizeX( Controls.DetailsGrid:GetSizeX() );
Controls.DetailsSeperator:SetSizeY( Controls.DetailsGrid:GetSizeY() );
Controls.DetailsGrid:ReprocessAnchoring();

That's my guess anyway :)
 
Thank you for your suggestion. I tried what you said and changed the code to:

local g_MyCombatDataRiverIM = InstanceManager:new( "UsCombatInfoRiver", "Text", Controls.MyCombatResultsStack );
local controlTable = g_MyCombatDataRiverIM:GetInstance();

--ContextPtr:BuildInstanceForControl ( "UsCombatInfoRiver", controlTable , Controls.MyCombatResultsStack );

controlTable.Text:SetText ( "Bonus attacking into River" );
controlTable.Value:SetText ( "50" )

But the text is still displayed on the right of the screen and is not added at the existing Controls.MyCombatResultsStack.

I also tried to change to instance name from UsCombatInfoRiver to UsCombatInfo to match the original label and change the xml-files accordingly. But still no luck :(

Do I need to add something extra to the xml-files? I now only have the instance in the xml-files. Or re-anchor the new bonus to the panel?
 
Maybe you should try to go about this a bit differently. Instead of creating your own Instance class in your xml file try to make it a plain label, since you won't need to make more than one copy of it anyway.

Spoiler :

<?xml version="1.0" encoding="utf-8"?>
<Context ColorSet="Beige_Black" Font="TwCenMT20" FontStyle="Shadow" >

<!-- Combat Info Label Instances for River -->

<Label Anchor="R,C" Offset="52.0" String=" " Font="TwCenMT14" ID="RiverText" WrapWidth="230" LeadingOffset="-6" >
<Label Anchor="R,C" AnchorSide="O.I" Offset="4.0" Font="TwCenMT14" ID="Value" />
</Label>


</Context>


Then in your lua file you force the game to associate it with the MyCombatResultsStack in the core files:

Spoiler :

Controls.RiverText:SetText("Bonus attacking into River" );
Controls.Value:SetText( "50" );

Controls.RiverText:ChangeParent(ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/MyCombatResultsStack"));
ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/MyCombatResultsStack"):ReprocessAnchoring();
 
You were right. That indeed did the trick. Thank you.

To get the anchoring right I added this code:

Spoiler :

local iModifier = 50;
local iValue = " : [COLOR_POSITIVE_TEXT]+" .. Locale.ToNumber(iModifier, "#.##") .. "%[ENDCOLOR][]";
Controls.RiverText:SetText( "Bonus fighting near River");
Controls.Value:SetText( iValue );
Controls.RiverText:SetHide(false);

Controls.RiverText:ChangeParent(ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/MyCombatResultsStack"));
ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/MyCombatResultsStack"):CalculateSize();
ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/TheirCombatResultsStack"):CalculateSize();

local sizeX = ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/DetailsGrid"):GetSizeX();
local sizeY = ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/DetailsGrid"):GetSizeY()
ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/DetailsGrid"):DoAutoSize();
ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/DetailsGrid"):SetSizeX( sizeX );
ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/DetailsSeperator"):SetSizeY( sizeY );
ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel/DetailsGrid"):ReprocessAnchoring();
 
Glad to help :)

If you want to streamline it a bit you could store the EnemyUnitPanel in a local variable, then you only have to do the LookUpControl once:

Code:
local eupanel = ContextPtr:LookUpControl("/InGame/WorldView/EnemyUnitPanel");

Controls.RiverText:ChangeParent(eupanel.MyCombatResultsStack);
...
eupanel.DetailsGrid:DoAutoSize();
...

At least I assume this would work.
 
Back
Top Bottom