• Our friends from AlphaCentauri2.info are in need of technical assistance. If you have experience with the LAMP stack and some hours to spare, please help them out and post here.

Lua Objects

Indeed, I just saw that. Thanks for the answer :)

Is there a way to make feedback appear ingame ?

Civ 5 had a method used to print messages to the screen; is there a Civ 6 version?
 
Civ5 Method = Events.GameplayAlertMessage(sMessage)

There was also a more complicated method you could use to create floating text over a tile when X happened at that tile.

So far I have not found an equivalency for civ6.
 
Civ5 Method = Events.GameplayAlertMessage(sMessage)

There was also a more complicated method you could use to create floating text over a tile when X happened at that tile.

So far I have not found an equivalency for civ6.
For floating text in script:
Code:
Game.AddWorldViewText(0, "your text", x, y, 0)
or in UI:
Code:
UI.AddWorldViewText(0, "your text", x, y, 0)

the first 0 can be replaced by an EventSubTypes (like EventSubTypes.DAMAGE), no idea for the second.

To replace GameplayAlertMessage I re-use code from the Firaxis automation scripts:

UI context lua
Code:
function StatusMessage( str:string, fDisplayTime:number, type:number )

    if (type == ReportingStatusTypes.DEFAULT or
        type == ReportingStatusTypes.GOSSIP) then    -- A type we handle?

        local kTypeEntry :table = m_kMessages[type];
        if (kTypeEntry == nil) then
            -- New type
            m_kMessages[type] = {
                InstanceManager = nil,
                MessageInstances= {}
            };
            kTypeEntry = m_kMessages[type];

            -- Link to the instance manager and the stack the UI displays in
            if (type == ReportingStatusTypes.GOSSIP) then
                kTypeEntry.InstanceManager    = m_gossipIM;
            else
                kTypeEntry.InstanceManager    = m_statusIM;
            end
        end

        local pInstance:table = kTypeEntry.InstanceManager:GetInstance();
        table.insert( kTypeEntry.MessageInstances, pInstance );

        local timeToDisplay:number = (fDisplayTime > 0) and fDisplayTime or DEFAULT_TIME_TO_DISPLAY;
        pInstance.StatusLabel:SetText( str );    
        pInstance.Anim:SetEndPauseTime( timeToDisplay );
        pInstance.Anim:RegisterEndCallback( function() OnEndAnim(kTypeEntry,pInstance) end );
        pInstance.Anim:SetToBeginning();
        pInstance.Anim:Play();

        Controls.StackOfMessages:CalculateSize();
        Controls.StackOfMessages:ReprocessAnchoring();
    end
end

and the corresponding XML:
Code:
<?xml version="1.0" encoding="utf-8" ?>
<Context Name="AutoPlay_InGame" >

  <Container Size="full,full">
     <Stack ID="StackOfMessages" Anchor="C,T" StackGrowth="Bottom" Offset="0,120"/>
  </Container>
 
    <Instance                            Name="StatusMessageInstance">
    <Container                    ID="Root"                    Anchor="C,T" Size="650,40" AutoSize="V">
      <AlphaAnim                ID="Anim"                    Anchor="C,T" AlphaBegin="0" AlphaEnd="1" Speed="3"  Size="650,parent" Cycle="OneBounce" EndPause="10" AutoSize="V">
        <Grid                                                            Anchor="C,T" Style="EnhancedToolTip" MinSize="54,54" Size="parent+10,parent-10" Color="255,255,255,255" AutoSize="V" InnerPadding="10,30">
          <Label                ID="StatusLabel"    Anchor="C,C" Style="BodyText18" Offset="0,-2" WrapWidth="parent-20" />
        </Grid>
      </AlphaAnim>
    </Container>
    </Instance>
  
  <Instance                            Name="GossipMessageInstance">
      <Container                ID="Root"                    Anchor="C,T"                                                                                    AutoSize="1"  >
        <AlphaAnim            ID="Anim"                    Anchor="C,T"                                    Size="650,82"                        AutoSize="V"    AlphaBegin="0" AlphaEnd="1" Speed="3"  Cycle="OneBounce" EndPause="10" >
          <Image                                                    Anchor="C,T" Offset="2,0"            Size="650,82"                        AutoSize="V"  AutoSizePadding="0,-38"                                                Texture="Parchment_Pattern" StretchMode="Tile" >
            <Grid                                                                             Offset="-25,-25" Size="parent,parent"        AutoSize="1"    AutoSizePadding="25,0" InnerPadding="10,0"        Texture="Controls_GoldBox"                    SliceTextureSize="132,132"    SliceCorner="66,66" MinSize="132,132" >
              <Grid                                                Anchor="C,T" Offset="0,28"        Size="parent-9,parent"    AutoSize="V"                                                     InnerPadding="0,0"        Texture="Controls_GossipContainer"    SliceTextureSize="70,70"        SliceCorner="35,35" MinSize="70,70" Color="74,67,60,150" >
                <Label    ID="StatusLabel"    Anchor="C,C" Style="BodyTextParchment18" WrapWidth="parent-20"/>
              </Grid>
            </Grid>
          </Image>
        </AlphaAnim>
      </Container>
  </Instance>

</Context>

then for example : StatusMessage( "My message", 3, ReportingStatusTypes.DEFAULT )
 
The generic "print" common shows up in the log, found for PC users at C:\Users\[yourname]\Documents\My Games\Sid Meier's Civilization VI\Logs\Lua.log.

But a better way to view it is in Firetuner, on the LuaConsole tab. There you can change the "context" to your script, which eliminate all other Lua feedback and only display print messages from your script. I was disconnected from the game when I took this screenshot, but you can see the context drop down on the top left. Here it is set to my custom gameplay script which is why you only see print statements related to my Combined Tweaks mod.

Spoiler :
upload_2017-3-25_11-8-7.png
 
Last edited:
For floating text in script:
Code:
Game.AddWorldViewText(0, "your text", x, y, 0)
or in UI:
Code:
UI.AddWorldViewText(0, "your text", x, y, 0)

the first 0 can be replaced by an EventSubTypes (like EventSubTypes.DAMAGE), no idea for the second.

To replace GameplayAlertMessage I re-use code from the Firaxis automation scripts:

UI context lua
Code:
function StatusMessage( str:string, fDisplayTime:number, type:number )

    if (type == ReportingStatusTypes.DEFAULT or
        type == ReportingStatusTypes.GOSSIP) then    -- A type we handle?

        local kTypeEntry :table = m_kMessages[type];
        if (kTypeEntry == nil) then
            -- New type
            m_kMessages[type] = {
                InstanceManager = nil,
                MessageInstances= {}
            };
            kTypeEntry = m_kMessages[type];

            -- Link to the instance manager and the stack the UI displays in
            if (type == ReportingStatusTypes.GOSSIP) then
                kTypeEntry.InstanceManager    = m_gossipIM;
            else
                kTypeEntry.InstanceManager    = m_statusIM;
            end
        end

        local pInstance:table = kTypeEntry.InstanceManager:GetInstance();
        table.insert( kTypeEntry.MessageInstances, pInstance );

        local timeToDisplay:number = (fDisplayTime > 0) and fDisplayTime or DEFAULT_TIME_TO_DISPLAY;
        pInstance.StatusLabel:SetText( str );   
        pInstance.Anim:SetEndPauseTime( timeToDisplay );
        pInstance.Anim:RegisterEndCallback( function() OnEndAnim(kTypeEntry,pInstance) end );
        pInstance.Anim:SetToBeginning();
        pInstance.Anim:Play();

        Controls.StackOfMessages:CalculateSize();
        Controls.StackOfMessages:ReprocessAnchoring();
    end
end

and the corresponding XML:
Code:
<?xml version="1.0" encoding="utf-8" ?>
<Context Name="AutoPlay_InGame" >

  <Container Size="full,full">
     <Stack ID="StackOfMessages" Anchor="C,T" StackGrowth="Bottom" Offset="0,120"/>
  </Container>
 
    <Instance                            Name="StatusMessageInstance">
    <Container                    ID="Root"                    Anchor="C,T" Size="650,40" AutoSize="V">
      <AlphaAnim                ID="Anim"                    Anchor="C,T" AlphaBegin="0" AlphaEnd="1" Speed="3"  Size="650,parent" Cycle="OneBounce" EndPause="10" AutoSize="V">
        <Grid                                                            Anchor="C,T" Style="EnhancedToolTip" MinSize="54,54" Size="parent+10,parent-10" Color="255,255,255,255" AutoSize="V" InnerPadding="10,30">
          <Label                ID="StatusLabel"    Anchor="C,C" Style="BodyText18" Offset="0,-2" WrapWidth="parent-20" />
        </Grid>
      </AlphaAnim>
    </Container>
    </Instance>
 
  <Instance                            Name="GossipMessageInstance">
      <Container                ID="Root"                    Anchor="C,T"                                                                                    AutoSize="1"  >
        <AlphaAnim            ID="Anim"                    Anchor="C,T"                                    Size="650,82"                        AutoSize="V"    AlphaBegin="0" AlphaEnd="1" Speed="3"  Cycle="OneBounce" EndPause="10" >
          <Image                                                    Anchor="C,T" Offset="2,0"            Size="650,82"                        AutoSize="V"  AutoSizePadding="0,-38"                                                Texture="Parchment_Pattern" StretchMode="Tile" >
            <Grid                                                                             Offset="-25,-25" Size="parent,parent"        AutoSize="1"    AutoSizePadding="25,0" InnerPadding="10,0"        Texture="Controls_GoldBox"                    SliceTextureSize="132,132"    SliceCorner="66,66" MinSize="132,132" >
              <Grid                                                Anchor="C,T" Offset="0,28"        Size="parent-9,parent"    AutoSize="V"                                                     InnerPadding="0,0"        Texture="Controls_GossipContainer"    SliceTextureSize="70,70"        SliceCorner="35,35" MinSize="70,70" Color="74,67,60,150" >
                <Label    ID="StatusLabel"    Anchor="C,C" Style="BodyTextParchment18" WrapWidth="parent-20"/>
              </Grid>
            </Grid>
          </Image>
        </AlphaAnim>
      </Container>
  </Instance>

</Context>

then for example : StatusMessage( "My message", 3, ReportingStatusTypes.DEFAULT )


Whoah, thanks for this. I needed code for sending messages to the player's screen desperately.
 
Amusing part is they made it much easier to add a floating text over a tile, which everybody wanted for Civ5 (easier coding method, I mean), but also made the method for GamePlayAlertMessage text harder.

What Ed Giveth with the Left Hand, Dennis Taketh Away with the Right Hand. :lol:
 
I would like to have the name of the units of all the players appear in firetuner at the end of the turn.

Code:
function printBlah()
     local localPlayer        = Players[Game.GetLocalPlayer()];
     local pUnits = localPlayer:GetUnits()
     for k,v in pUnits:Members() do
        local name = GameInfo.Unit.GetName(v)
        print(name)
     end
     print("bloh blah")
end

Events.TurnEnd.Add(printBlah)

"of course" this doesn't work :-)
Any help please ?
What If I want only the player units to appear (in single player) ?
 
> for _,u in Players[0]:GetUnits():Members() do print(u:GetName()) end
HelloWorldPopup: LOC_UNIT_SETTLER_NAME
HelloWorldPopup: LOC_UNIT_WARRIOR_NAME
> for _,u in Players[0]:GetUnits():Members() do print(Locale.Lookup(u:GetName())) end
HelloWorldPopup: Settler
HelloWorldPopup: Warrior
> for _,u in Players[0]:GetUnits():Members() do print(u:GetUnitType()) end
HelloWorldPopup: 0
HelloWorldPopup: 17
> for _,u in Players[0]:GetUnits():Members() do print(GameInfo.Units[u:GetUnitType()].UnitType) end
HelloWorldPopup: UNIT_SETTLER
HelloWorldPopup: UNIT_WARRIOR
> for _,u in Players[0]:GetUnits():Members() do print(u:GetCombat()) end
HelloWorldPopup: 0
HelloWorldPopup: 20
 
Some more info on lua hook events and the data passed to them in the arguments from the game core:
Spoiler :

  1. Events.UnitAddedToMap(iPlayer, iUnitID, iPlotX, iPlotY)
    • iPlotX, iPlotY are not reliable for original game settler and warrior because both show the player's starting location.
  2. Events.UnitMoved(iPlayer, iUnitID, iPlotX, iPlotY, bMoveIsVisibleToHuman, iUnknownInteger)
    • fires for each unit's individual moves
  3. Events.FeatureRemovedFromMap(iPlotX, iPlotY)
  4. Events.ImprovementChanged(iPlotX, iPlotY, iImprovementIndex, iPlayer, iInteger2, iInteger3, iInteger4)
    • data was OnImprovementChanged(17, 57, 1, 0, -1, 0, 0) for builder of player #0 making a Farm @ 17, 57.
    • the event seems terribly unreliable: it ia not clear what fires it and why, and at times seems not to fire when it ought, and then fires multiple times for the same hex and same action with identical argument data when this makes no sense either
  5. Events.ImprovementActivated(iPlotX, iPlotY, iPlayer, iUnitID, iImprovementIndex, iInteger1, iInteger2, iInteger3)
    • data was OnImprovementActivated(31, 81, 0, 131073, 9, -1, 0, 0) for the starting warrior of player #0 stepping on a GoodyHut
    • data was OnImprovementActivated(44, 33, 0, 1572869, 9, -1, 0, 0) for a scout of player #0 stepping on a GoodyHut
    • have not so far seen the event fire for anything except a unit stepping on a Goody Hut
    • other CFC members note that it fires on clearing a Barb Camp
  6. Events.MapYieldsChanged()
    • Pretty much pointless, provides no arguments. So can only really be used as a signal to check any and all conditions you are interested in
  7. Events.PlayerEraChanged(iPlayer, iEra)
    • fires on game start / game load so needs to be delayed to LoadScreenClose activation
  8. Events.PlotYieldChanged(iPlotX, iPlotY)
    • it also fires for completion of Districts and Buildings when this changes the plot's yields, so turn processing will cause the event to fire for each plot affected by completed items.
    • harvesting a forest will cause the event to fire for the plot where the harvest occured as well as for any other plots (such as a district completed as a result of the harvest, or a district adjaceny bonus) that are affected by the plot harvesting. So any in-game action that causes the yield of a tile to change may also cause this event to fire for multiple other tiles because their yields have also been affected by whatever the action was (adjacency issues are going to abound using this hook event).
  9. Events.ResourceAddedToMap(iPlotX, iPlotY, iResource)
    • seems the game event really ought to be called ResourceAddedToVisibleMap or ResourceRevealedToPlayer
    • fires on game start / game load so needs to be delayed to LoadScreenClose activation
  10. Events.TurnBegin(iInteger)
    • fires at the beginning of the turn for the 1st player to enter the turn
    • iInteger appears to just be the turn number so is really not very useful as a game event
    • fires on game start / game load so needs to be delayed to LoadScreenClose activation
    • seems pretty useless, really
  11. Events.TurnEnd(iInteger)
    • fires at the end of the last player player to process for the turn
    • iInteger appears to just be the turn number so is really not very useful as a game event
    • fires on game start / game load so needs to be delayed to LoadScreenClose activation
    • seems pretty useless, really
  12. Events.UnitChargesChanged(iPlayer, iUnitID, iNumberChargesRemaining, iPreviousNumberCharges)
  13. Events.UnitMoveComplete(iPlayer, iunitID, iPlotX, iPlotY)
    • fires for the plot the unit's movements terminated in, and any plots you as the human player stopped the unit in along the way during that turn
    • fires when you tell the unit to skip rest of moves Erroneous, what was actually happening was the previous note.
    • appears to fire when a builder does a build, but does not always do so. Do not rely on this event for a "Builder Built" method.
  14. Events.BuildingAddedToMap(iPlotX, iPlotY, iBuildingID, iPlayerID, iUnknown1, iUnknown2)
    • The event will not re-fire for the same building for the same city if the buildng is taken out of the qeue and re-added later.
    • In this case it will only re-fire for that city for that Buildng if the building is purchased or completed, and verification is needed for whether it fires for the completion via normal production methods
    • the event fires for any building 'added' via an lua script
    • iBuildingID = Building's Index # both when a Bulding is added to a city qeue and when the building is actually purchased or completed
  15. Events.DistrictAddedToMap(iPlayer, iDistrict, iCity, iPlotX, iPlotY, iDistrictIndex, iUnknownEventSubType1, iUnknownEventSubType2, iPercentComplete, iPlotAppeal, iUnknown3)
    • iDistrict == iCity for founding a city and plopping the City Center District
      • for adding Holy Site to the build qeue in human player's capital city: 0, 131073, 65536, 66, 30, 1, -1851407529, -1155553852, 0, 2, 0
    • the event only appears to fire when the district is placed for construciton, not when it is completed.
    • for adding Hanging Gardens to the map on save reload
      • argument data was: 0, 196610, 65536, 21, 59, 13, -1851407529, -1806906687, 100, -4, 0
      • "13" matches to the index # within table <Districts> for "DISTRICT_WONDER"
    • Event fires as part of saved-game reloading for all districts and wonders placed on the map
    • iUnknownEventSubType1 and iUnknownEventSubType2 seem to be just some sort of integer hash value undetermined as yet, but for the same sort of "event" vary depending on player. For founding a capital city (ie, city center district):
      • for human player (Cleopatra, Egypt)
        iUnknownEventSubType1 == -1851407529
        iUnknownEventSubType2 == -1155553852​
      • for Montezuma, Aztecs:
        iUnknownEventSubType1 == -1851407529
        iUnknownEventSubType2 == 1347583174​
  16. Events.CityProductionChanged(iPlayer, CityID, ItemTypeReference, ItemID, UnknownInteger)
    • the event is for when the Item in the qeue is changed to something else, not for when more 'cogs' are added to the Item's progress
    • UnknownInteger = 0 except when item is purchased, in which case it appears to be equal to (CityID - 1)
    • ItemID = -1 when ItemID was purchased
    • ItemTypeReference
      • 0 : -> UNIT
      • 1 : -> BUILDING
      • 2 : -> DISTRICT
      • 3 : -> PROJECT
  17. Events.CityProductionCompleted(iPlayer, iCityID, iCompletedItemType, iItemId, iUnknownBoolean, iUnknownInteger3)
    • for capital city founding for player 0 (human), the values were: (0, 65536, 1, 1, false, 65535)
    • for placement of dummy building #81 in city center of capital for player 0 (human), the values were: (0, 65536, 1, 81, false, 65535)
    • for placement of building #89 in city center of capital for player 0 (human), the values were: (0, 65536, 1, 89, false, 65535)
    • iCompletedItemType
      • 0 : -> UNIT
      • 1 : -> BUILDING
      • 2 : -> DISTRICT
      • 3 : -> PROJECT
    • for Completion of district #1 (Holy Site) in city center of capital for player 0 (human), the values were: 0, 65536, 2, 1, false, 65535)
      • the completion was done via chopping
      • the event fired twice in this instance with identical data
    • for Completion of Campus Research Grants in city center of capital for player 0 (human), the values were: 0, 65536, 3, 16, false, -1)
      • the completion was done via chopping rainforest(jungle)
      • in this case iItemId matches to PROJECT_ENHANCE_DISTRICT_CAMPUS and iCompletedItemType (3) is for <Projects>
  18. Events.CityProductionUpdated(iPlayer, iCityID, iProductionItemIndex, iUnknownInteger1, iUnknownInteger2)
    • for completion (as a result of chopping) of a Holy Site in City ID #65536: 0, 65536, 1, 0, 65535
    • the event is not very useful given it only tells you the production was updated, not by how much
  19. Events.DistrictBuildProgressChanged(iPlayer, iDistrictID, oCityID, iDistrictX, iDistrictY, iDistrictTypeIndex, iUnknownEventSubType1, iUnknownEventSubType2, iPercentComplete, iPlotAppeal, iUnknown3)
    • for a Holy Site in the human player's capital city as turn-processing production progress: 0, 131073, 65536, 66, 30, 1, -1851407529, -1155553852, 7, 2, 0
    • the event appears to fire up to twice (2x) each turn as part of turn processing
    • for a Holy Site in the human player's capital city as completion via chopping: 0, 131073, 65536, 66, 30, 1, -1851407529, -1155553852, 100, 2, 0
  20. Events.BuildingChanged(iPlotX, iPlotY, iBuildingIndex, iPlayer, iPercentCompleted, iUnknown)
    • no difference seen between a purchase of a building within a city and a placement direct by an lua method.
  21. Events.CityPopulationChanged(iPlayer, iCityId, iPopulation)
    • iPopulation appears to be the new total population of the city
    • Event fires as part of saved-game reloading for all cities placed on the map
    • Event fires as part of city-creation
 
Last edited:
have not so far seen the event fire for anything except a unit stepping on a Goody Hut
I think it might also fire when Barb Camp is cleared.

Events.TurnBegin(iInteger)
Oh, TurnBegin and TurnEnd are not so useless if you want to add some code that affects all players.

the event fired twice in this instance with identical data
Is seems that it's bugged with Districts. It fires only once for other build-types.


BTW, the firing sequence each turn is:

-- initialize events - starting events
Events.LocalPlayerChanged.Add( OnLocalPlayerChanged ); -- fires in-between TurnEnd and TurnBegin
Events.PreTurnBegin.Add( OnPreTurnBegin ); -- fires ONCE at start of turn, before actual Turn start
Events.TurnBegin.Add( OnTurnBegin ); -- fires ONCE at the start of Turn
Events.PhaseBegin.Add( OnPhaseBegin ); -- engine?
Events.LocalPlayerTurnBegin.Add( OnLocalPlayerTurnBegin ); -- event for LOCAL player only (i.e. HUMANS), fires BEFORE PlayerTurnActivated
Events.PlayerTurnActivated.Add( OnPlayerTurnActivated ); -- main event for any player start (AIs, including minors), goes for playerID = 0,1,2,...
-- these events fire AFTER custom PlayerTurnActivated()
Events.CivicBoostTriggered.Add( OnCivicBoostTriggered );
Events.TechBoostTriggered.Add( OnTechBoostTriggered );
Events.CityProductionCompleted.Add( OnCityProductionCompleted );
Events.CityProjectCompleted.Add( OnCityProjectComplete );
-- HERE YOU PLAY GAME AS HUMAN
-- initialize events - finishing events
Events.LocalPlayerTurnEnd.Add( OnLocalPlayerTurnEnd ); -- fires only for HUMANS
Events.PlayerTurnDeactivated.Add( OnPlayerTurnDeactivated ); -- main event for any player end (including minors)
Events.PhaseEnd.Add( OnPhaseEnd ); -- engine?
Events.TurnEnd.Add( OnTurnEnd ); -- fires ONCE at end of turn
Events.EndTurnDirty.Add( OnEndTurnDirty ); -- engine event, triggers very often
 
  • Like
Reactions: cvb
Actually, I am using TurnEnd in my script. Whatever. I do have an "issue", is that healing of a unit is applied before the TurnEnd sequence (resulting in that units are healed, then they suffer from TurnEnd attrition. Even TurnBegin does it, which is weird. However, thanks to you guys, I will test more options so that attrition is dealt BEFORE Healing.
 
Back
Top Bottom