Include unit level in unit name

FredMB

Chieftain
Joined
Oct 28, 2010
Messages
84
Location
Vancouver, Canada
Hello Everyone

I'm experimenting with my first somewhat useful mod that includes LUA. This mod includes the unit's level with the unit name in both UnitPanel.lua and UnitFlagManager.lua. I got the code working just fine for the UnitPanel. It adds the unit's level before the unit's name and even references Language_en_US for text. The code for this is below:

Spoiler :

XML File:
<GameData>
<Language_en_US>
<Row Tag="TXT_KEY_UPANEL_LEVEL_ADDED_TO_UNIT_NAME">
<Text>Lv</Text>
</Row>
</Language_en_US>
</GameData>

LUA File:
-- Start Modification
--------------------------------------------------------------------------------
-- Refresh unit portrait and name
--------------------------------------------------------------------------------
function UpdateUnitPortrait( unit )
local ileveltext = unit:GetLevel(); -- Returns Unit Level (Same as tool tip below).
local name = unit:GetName(); -- Retrieves the name of the unit (Original file).
local Levelinunitname = Locale.ConvertTextKey("TXT_KEY_UPANEL_LEVEL_ADDED_TO_UNIT_NAME"); -- Converts "Lv" from the Language_en_US in level.xml to text.
name = Locale.ToUpper(name); -- Sets all uppercase for unit name (Original file).
--local name = unit:GetNameKey(); -- Disabled in original file.
local ileveltextconvert = Locale.ConvertTextKey(ileveltext); -- Converts unit level to text.
local convertedKey = Locale.ConvertTextKey(name); -- Converts the uppercase unit name to text (Original file).
convertedKey = Locale.ToUpper(convertedKey); -- Sets all uppercase for "convertedKey". Seems redundant. Not sure if needed. (Original file).
Controls.UnitName:SetText(Levelinunitname..ileveltextconvert.." "..convertedKey); -- Not pretty but it works. Space between Lv1 and Unit added here. Now sure how else to do it.
--Controls.UnitName:SetText(convertedKey); -- Prints "convertedKey" in game. (Original file).
Controls.UnitName:SetFontByName("TwCenMT24");
-- End Modification


This code works great in the UnitPanel. It updates live as the unit gains levels. I've tested this with the Live Tuner.

However it doesn't work so great in the UnitFlagManager.lua. The code is very similar but a bit different:
Spoiler :

-- Modified by FredMB
------------------------------------------------------------------
-- constructor
------------------------------------------------------------------
Initialize = function( o, playerID, unitID, fogState, invisible )
o.m_Player = Players[ playerID ];
o.m_PlayerID = playerID;
o.m_UnitID = unitID;

if( g_PrintDebug ) then print( string.format( "Creating UnitFlag for: Player[%i] Unit[%i]", playerID, unitID ) ); end

local pUnit = Players[ playerID ]:GetUnitByID( unitID );
local ileveltext2 = pUnit:GetLevel(); -- Returns Unit Level (Same as UnitPanel.lua).
local ileveltextconvert2 = Locale.ConvertTextKey(ileveltext2); -- Converts unit level to text.
local Levelinunitname2 = Locale.ConvertTextKey("TXT_KEY_UPANEL_LEVEL_ADDED_TO_UNIT_NAME"); -- Converts "Lv" from the Language_en_US in level.xml to text.
if( pUnit == nil )
then
print( string.format( "Unit not found for UnitFlag: Player[%i] Unit[%i]", playerID, unitID ) );
return nil;
end

o.m_IsCivilian = not pUnit:IsCombatUnit();
o.m_IsInvisible = invisible;

-- Technically, we should get a UnitGarrisoned event after the creation event if
-- the unit is garrisoned. So IsGarrisoned should always be false at creation.
-- In the interest of preserving behavior I'm allowing m_IsGarrisoned to be set
-- using IsGarrisoned() on creation. However, in the strategic view this causes
-- a visibility error in some odd cases so there it always starts as false.
if( InStrategicView() )
then
o.m_IsGarrisoned = false;
else
o.m_IsGarrisoned = pUnit:IsGarrisoned();
end

---------------------------------------------------------
-- Hook up the button
local pPlayer = Players[Game.GetActivePlayer()];
local active_team = pPlayer:GetTeam();
local team = o.m_Player:GetTeam();

--local name = pUnit:GetNameKey();
--local localizedName = Locale.ConvertTextKey(name);

local localizedName = pUnit:GetName();
localizedNameWithlevel = (Levelinunitname2..ileveltextconvert2.." "..localizedName); --New line similar to UnitPanel.
local localizedNameWithlevelConvert = Locale.ConvertTextKey(localizedNameWithlevel); --Converts localizedNameWithlevel to text.

if( active_team == team )
then
o.m_Instance.NormalButton:SetVoid1( playerID );
o.m_Instance.NormalButton:SetVoid2( unitID );
o.m_Instance.NormalButton:RegisterCallback( Mouse.eLClick, UnitFlagClicked );
o.m_Instance.NormalButton:RegisterCallback( Mouse.eMouseEnter, UnitFlagEnter );
o.m_Instance.NormalButton:RegisterCallback( Mouse.eMouseExit, UnitFlagExit );
o.m_Instance.NormalButton:SetDisabled( false );
o.m_Instance.NormalButton:SetConsumeMouseOver( true );
if(PreGame.IsMultiplayerGame() and o.m_Player:IsHuman()) then
local unitOwner = Players[playerID];
local string = Locale.ConvertTextKey("TXT_KEY_MULTIPLAYER_UNIT_TT", o.m_Player:GetNickName(), o.m_Player:GetCivilizationAdjectiveKey(), localizedNameWithlevelConvert ); -- localizedNameWithlevel used here.
if( playerID == Game.GetActivePlayer() ) then
string = string .. Locale.ConvertTextKey( "TXT_KEY_UPANEL_CLICK_TO_SELECT" );
end
o.m_Instance.UnitIcon:SetToolTipString( string );
else
o.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_PLOTROLL_UNIT_DESCRIPTION_CIV", o.m_Player:GetCivilizationAdjectiveKey(), localizedNameWithlevelConvert).. Locale.ConvertTextKey( "TXT_KEY_UPANEL_CLICK_TO_SELECT" )); -- localizedNameWithlevel used here.
end
o.m_Instance.HealthBarButton:SetVoid1( playerID );
o.m_Instance.HealthBarButton:SetVoid2( unitID );
o.m_Instance.HealthBarButton:RegisterCallback( Mouse.eLClick, UnitFlagClicked );
o.m_Instance.HealthBarButton:RegisterCallback( Mouse.eMouseEnter, UnitFlagEnter );
o.m_Instance.HealthBarButton:RegisterCallback( Mouse.eMouseExit, UnitFlagExit );
o.m_Instance.HealthBarButton:SetDisabled( false );
o.m_Instance.HealthBarButton:SetConsumeMouseOver( true );

local pPlot = pUnit:GetPlot();
if( pPlot:IsCity() ) then
o.m_CityFlag = UpdateCityCargo( pPlot );
end

else
if(PreGame.IsMultiplayerGame() and o.m_Player:IsHuman()) then
o.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_MULTIPLAYER_UNIT_TT", o.m_Player:GetNickName(), o.m_Player:GetCivilizationAdjectiveKey(), localizedNameWithlevelConvert)); -- localizedNameWithlevel used here.
else
o.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_PLOTROLL_UNIT_DESCRIPTION_CIV", o.m_Player:GetCivilizationAdjectiveKey(), localizedNameWithlevelConvert)); -- localizedNameWithlevel used here.
end
o.m_Instance.NormalButton:SetDisabled( true );
o.m_Instance.NormalButton:SetConsumeMouseOver( false );
o.m_Instance.HealthBarButton:SetDisabled( true );
o.m_Instance.HealthBarButton:SetConsumeMouseOver( false );
end
-- End Modification
---------------------------------------------------------
-- update all the info -- I tried fiddling around with the code below but anything I do crashed the file.
o:SetUnitColor();
o:SetUnitType();
o:UpdateFlagType();
o:UpdateHealth();
o:UpdateSelected();
o:SetFogState( fogState );
o:UpdateFlagOffset();
o:UpdateVisibility();

---------------------------------------------------------
-- Set the world position
local worldPosX, worldPosY, worldPosZ = GridToWorld( pUnit:GetX(), pUnit:GetY() );
worldPosZ = worldPosZ + 35;

o:UnitMove( worldPosX, worldPosY, worldPosZ );

end,


When in game hovering the cursor over a unit gives you the unit's level just like the unit panel. However it's not live. When the unit gains levels the pop up stays the same. The pop up does update with the proper unit level when the unit upgrades though. The pop up also works when hovering the cursor over other civ's units and barbarians, however I haven't done testing to see how it behaves when AI units gain levels or upgrade.

So does anyone have an idea on how to make the pop up in UnitFlagManager.lua update the unit level in real time just like the one UnitPanel.lua?

Any input would be greatly appreciated.

I've included the files with the post for those of you that feel like testing.
 

Attachments

I think you're modifying the wrong function. You're changing the "constructor", which only executes once when the unit is first created. You want to change it whenever the tooltip is updated. To do that, put your changes inside the function:

UpdateTooltip = function

later in the same file.

Other tips:

1)Don't use pUnit until after it's passed the pUnit == nil test.

2) This probably works, but it's unnecessarily obtuse;
local ileveltext2 = pUnit:GetLevel(); -- Returns Unit Level (Same as UnitPanel.lua).
local ileveltextconvert2 = Locale.ConvertTextKey(ileveltext2); -- Converts unit level to text.

GetLevel returns a number. Don't store it an a variable named "text". You don't need to call Locale.ConvertTextKey on a number. There is nothing there to localize. To change a number to a string, simply use something like:
strLevel = tostring(pUnit:GetLevel());

That said, I'm not sure you even need to explicitly change it to a string. From what I've seen, it automatically converts numbers to strings in reporting strings, at least with the print() function. You could probably do simply:

localizedName .. pUnit:GetLevel()

and it'll convert for you.
 
Hmm, sorry, it looks like that UpdateTooltip function I referred you to doesn't actually exist in vanilla Civ5. I was looking at a mod that does something similar and thought he just changed an existing function. It looks like he added it himself. I suggest you download & study the mod "Hover Info", which does something similar to yours. I hope that's still on the mod browser. You'll also need to add some calls to UpdateTooltip() at the right time(s) to actually change anything.

I don't know if there's a simpler way to do this. I didn't think this was going to get so tricky!
 
you'd think it would work as its just appending to a string but consider this:

Code:
local a = "eh"; 
local s = "somestring"; 
local x = "anotherone"; 
local z = "lastone"; 

> print ( Locale.ConvertTextKey( s, x, z ) .. Locale.ConvertTextKey( a ) ); 
> somestringeh

> print ( Locale.ConvertTextKey( s .. x .. z ) .. Locale.ConvertTextKey( a ) ); 
> somestringanotheronelastoneeh

so Locale.ConvertTextKey only takes the first argument in a list to make the string. since i doubt this will make your UI crash and burn, might as well just make the string yourself
 
The additional arguments to Locale.ConvertTextKey() are only used if the text key includes argument handling codes for them. E.g.
"TXT_KEY_PLOTROLL_UNIT_DESCRIPTION_CIV" is {1_CivAdjective:textkey} {2_UnitName}
and
TXT_KEY_MULTIPLAYER_UNIT_TT" = {1_PlayerHandle}: {@2_Adjective} {3_UnitName}

But what I mean is that this *probably* works:

o.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_PLOTROLL_UNIT_DESCRIPTION_CIV" , o.m_Player:GetCivilizationAdjectiveKey(), localizedName) .. pUnit:GetLevel());

putting the GetLevel() outside the ConvertTextKey completely.
 
Thanks to both of you for the feedback. I made it work using Adam Watkins' Hover info mod and including my own convoluted code in it. When a unit levels up it updates the unit panel first and when the unit is moved on screen then it updates that too. However it doesn't give me the unit level for other civ's units. I'll be working on that next.

Thanks again.
 
You're welcome. I don't see any obvious way to change it as soon as the unit levels. For the player's units, you could try acting upon SerialEventUnitInfoDirty, *if* that's triggered by leveling. It might trigger very often though. See how UnitPanel.lua does this:

Events.SerialEventUnitInfoDirty.Add(OnInfoPaneDirty);

then call your unit:UpdateTooltip, where
local unit = UI.GetHeadSelectedUnit();

or something like that. Good luck!

There are events like UnitDataEdited and UnitStateChangeDetected, but I have no idea what triggers those or what the arguments to them would be. Presumably how to access the unit in question...
 
So this is what the final code looks like for the UpdateTooltip in UnitFlagManager.lua:

Spoiler :

-- Original code from Hover Info by Adam Watkins. Modified by FredMB

UpdateTooltip = function ( self )
pUnit = Players[ self.m_PlayerID ]:GetUnitByID( self.m_UnitID );

if( pUnit == nil ) then
return;
end

local unitlevel = pUnit:GetLevel();
local lv = Locale.ConvertTextKey("TXT_KEY_UPANEL_LEVEL_ADDED_TO_UNIT_NAME");

local pPlayer = Players[Game.GetActivePlayer()];
local active_team = pPlayer:GetTeam();
local team = self.m_Player:GetTeam();

if(PreGame.IsMultiplayerGame() or not self.m_Player:IsHuman() or active_team ~= team) then
return;
end

string = Locale.ConvertTextKey("TXT_KEY_PLOTROLL_UNIT_DESCRIPTION_CIV", self.m_Player:GetCivilizationAdjectiveKey(), pUnit:GetName());
string = (lv..unitlevel.." "..string);
string = string .. Locale.ConvertTextKey( "TXT_KEY_UPANEL_CLICK_TO_SELECT" );
self.m_Instance.UnitIcon:SetToolTipString(string);

end,

--- End Modification by FredMB.


I took the code from Hover Info by Adam Watkins, removed what I didn't need from it and cleaned my additions up the best I could. My code is still messy but it works for now. The ToolTip still doesn't update in real time like the InfoPanel does but when a unit levels up it updates as soon as the unit moves, fortifies or if put on hold as soon as the turn ends. I'm very happy with that.

Now I want to add the code that would cover the AI and other player's units. The Constructor (where I originally inserted my code) has the code I think I should base this on:
Spoiler :

if( active_team == team )
then
if(PreGame.IsMultiplayerGame() and o.m_Player:IsHuman()) then
local unitOwner = Players[playerID];
local string = Locale.ConvertTextKey("TXT_KEY_MULTIPLAYER_UNIT_TT", o.m_Player:GetNickName(), o.m_Player:GetCivilizationAdjectiveKey(), localizedName );
if( playerID == Game.GetActivePlayer() ) then
string = string .. Locale.ConvertTextKey( "TXT_KEY_UPANEL_CLICK_TO_SELECT" );
end
o.m_Instance.UnitIcon:SetToolTipString( string );
else
o.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_PLOTROLL_UNIT_DESCRIPTION_CIV", o.m_Player:GetCivilizationAdjectiveKey(), localizedName).. Locale.ConvertTextKey( "TXT_KEY_UPANEL_CLICK_TO_SELECT" ));
end

local pPlot = pUnit:GetPlot();
if( pPlot:IsCity() ) then
o.m_CityFlag = UpdateCityCargo( pPlot );
end

else
if(PreGame.IsMultiplayerGame() and o.m_Player:IsHuman()) then
o.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_MULTIPLAYER_UNIT_TT", o.m_Player:GetNickName(), o.m_Player:GetCivilizationAdjectiveKey(), localizedName));
else
o.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_PLOTROLL_UNIT_DESCRIPTION_CIV", o.m_Player:GetCivilizationAdjectiveKey(), localizedName));
end
end


I removed some of the fluff for clarity but the constructor uses "if" "then" and "else" so that if the unit belongs to the player the TXT_KEY_UPANEL_CLICK_TO_SELECT is added to the ToolTip and if not then it isn't. The UpdateTooltip code ends with an "if" part, if I could figure out the "else" for AI units I think it would work.

I'm gonna need help on that one.

Thanks in advance.
 
Holy molly! I made it work! It WORKS!

Here's the code:
Spoiler :

-- Original code from Hover Info by Adam Watkins. Modified by FredMB

UpdateTooltip = function ( self )
local pUnit = Players[ self.m_PlayerID ]:GetUnitByID( self.m_UnitID );

if( pUnit == nil ) then
return;
end

local pPlayer = Players[Game.GetActivePlayer()];
local active_team = pPlayer:GetTeam();
local team = self.m_Player:GetTeam();

local unitlevel = pUnit:GetLevel();
local localName = pUnit:GetName();
local lv = Locale.ConvertTextKey("TXT_KEY_UPANEL_LEVEL_ADDED_TO_UNIT_NAME");
local lvname = Locale.ConvertTextKey(lv..unitlevel.." "..localName);

if( active_team ~= team ) then
if(PreGame.IsMultiplayerGame() and self.m_Player:IsHuman()) then
local string = Locale.ConvertTextKey("TXT_KEY_MULTIPLAYER_UNIT_TT", self.m_Player:GetNickName(), self.m_Player:GetCivilizationAdjectiveKey(), lvname);
self.m_Instance.UnitIcon:SetToolTipString(string);
else
self.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_PLOTROLL_UNIT_DESCRIPTION_CIV", self.m_Player:GetCivilizationAdjectiveKey(), lvname));
end

else
if(PreGame.IsMultiplayerGame() and self.m_Player:IsHuman()) then
self.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_MULTIPLAYER_UNIT_TT", self.m_Player:GetNickName(), self.m_Player:GetCivilizationAdjectiveKey(), lvname).. Locale.ConvertTextKey( "TXT_KEY_UPANEL_CLICK_TO_SELECT" ));
else
self.m_Instance.UnitIcon:SetToolTipString(Locale.ConvertTextKey("TXT_KEY_PLOTROLL_UNIT_DESCRIPTION_CIV", self.m_Player:GetCivilizationAdjectiveKey(), lvname).. Locale.ConvertTextKey( "TXT_KEY_UPANEL_CLICK_TO_SELECT" ));
end
end

end,

--- End Modification by FredMB.


Merging the code from the Constructor into UpdateTooltip was the way to go. I tested it with FireTuner, it behaves the same way as before (ToolTip updates on move, fortify or after end of turn). It also works for other civ's units and barbarian units.

Well I'm pretty happy about all that. Thank you again Perkus and smellyyummy and thanks to Adam Watkins for his original code too.

Now if anyone wants to try to re-merge Adam Watkins' original code in there to get number of moves and build info in the ToolTip go nuts, I'll leave this one to the pros.

Oh an any suggestions to clean up the code would be great too.

Cheers

P.S. If you want to make this work at home you need to download Hover Info by Adam Watkins and replace his UpdateToolTip with this one. He modified the rest of UnitFlagManager.lua to refer to UpdateToolTip so just cut and pasting the code in the original file won't work.
 
'Grats! That turned out to be a lot to deal with for a first mod. I think my first mod consisted of backing up the Global_defines.xml and then changing one value in it. :)
 
Back
Top Bottom