Adding new Ability to Great People

InkAxis

King
Joined
Feb 24, 2020
Messages
779
So I was wondering if there was a way to add a new ability to a GP?
I've seen mods that add a new improvement with the Builds table, but I was wondering if it was possible to add an ability, like how Great Prophets can found a Religion, Great Writers writing a Political Treatise, etc. Basically I want to add some new ability to great People that will run some lua code.
I'm using the VP DLL so if they added anything that makes this easier then that would be helpful.
Also, is it possible to "hide" these abilities? I only want them to show up when the player has a certain dummy building, but it's fine if it's not possible.
 
I am working on something similar for my mod. I will let you know if I figure it out.
 
This is possible, but only for the human player.
GameEvents.CustomMissionPossible.Add
GameEvents.CustomMissionStart.Add
GameEvents.CustomMissionCompleted.Add

or

GameEvents.GreatPersonExpended.Add
 
Hello, if you are still working on this I can share my solution. It involves editing UnitPanel.lua and it can only be used by human players (you would have to write something special for the AI to use the ability).

Here are the results:
Spoiler :


If you are an experienced modder you probably don't need to read this, otherwise here is an explanation for what everything does:

Line 213 within UpdateUnitActions. This adds a button for our special ability for Great Generals:
Code:
    -- KEWLIPO: Let's add a special ability to our great generals.
    if unit:GetUnitClassType() == GameInfo.UnitClasses.UNITCLASS_GREAT_GENERAL.ID then
        print("We have a general selected!")
        instance = g_PrimaryIM:GetInstance();
        numPrimaryActions = numPrimaryActions + 1;
        -- We need to add our own version of lines 305-319 with the unique checks that we want for our ability
        if not unit:AtPlot(pActivePlayer:GetCapitalCity():Plot()) then -- We want our general to be in the capital to use the ability
            bDisabled = true;
            instance.UnitActionButton:SetAlpha( 0.4 );           
            instance.UnitActionButton:SetDisabled( true );           
        else
            instance.UnitActionButton:SetAlpha( 1.0 );
            instance.UnitActionButton:SetDisabled( false );           
        end

        IconHookup(3, actionIconSize, "EXPANSION2_PROMOTION_ATLAS", instance.UnitActionIcon); -- We can add an icon later
        instance.UnitActionButton:RegisterCallback( Mouse.eLClick, GreatGeneralAbilitySelected );
        instance.UnitActionButton:SetVoid1(30);
        instance.UnitActionButton:SetToolTipCallback( TipHandler )
    end
Note "instance.UnitActionButton:SetVoid1(30)" While the rest of the code uses iAction for SetVoid1, I just chose 30 because no action related to Great Generals has that iAction ID. It is kind of ugly in implementation but it serves our purposes. Also note where we check whether a unit can use their ability. If you want the units to only use the ability when they are in a city with a certain dummy building, you would replace
Code:
if not unit:AtPlot(pActivePlayer:GetCapitalCity():Plot()) then
with code that checks that.

Line 917. This is what happens when the user clicks the button:
Code:
-- KEWLIPO: Let's add our ability...
function GreatGeneralAbilitySelected()
    local unit = UI.GetHeadSelectedUnit()
    if unit:GetUnitClassType() == GameInfo.UnitClasses.UNITCLASS_GREAT_GENERAL.ID then -- Just to make sure we're talking about the right unit here
        -- Add effects of the ability. I will just delete the unit to test if things are working.
        unit:Kill()
    end
end

Line 967. We have to change the tooltips. Unit buttons use a special tooltip handler, so we must change things within TipHandler if our button is being hovered:
Code:
    -- KEWLIPO: Let's add a special case if we're hovering our button.
    if unit:GetUnitClassType() == GameInfo.UnitClasses.UNITCLASS_GREAT_GENERAL.ID and iAction == 30 then
        print("Hovering...")
        --tipControlTable.UnitActionHelp:SetText( strToolTip );
        tipControlTable.UnitActionHelp:SetText("test help"); -- localize later
 
        --local strTitleString = "[COLOR_POSITIVE_TEXT]" .. Locale.ConvertTextKey( text ) .. "[ENDCOLOR]".. strBuildTurnsString;
        --tipControlTable.UnitActionText:SetText( strTitleString );
        tipControlTable.UnitActionText:SetText("test title"); -- localize later
 
        -- HotKey
        --[[
        if action.SubType == ActionSubTypes.ACTIONSUBTYPE_PROMOTION then
            tipControlTable.UnitActionHotKey:SetText( "" );
        elseif action.HotKey and action.HotKey ~= "" then
            tipControlTable.UnitActionHotKey:SetText( "("..tostring(action.HotKey)..")" );
        else
            tipControlTable.UnitActionHotKey:SetText( "" );
        end]] -- We don't need this
 
        -- Autosize tooltip
        tipControlTable.UnitActionMouseover:DoAutoSize();
        local mouseoverSize = tipControlTable.UnitActionMouseover:GetSize();
        if mouseoverSize.x < 350 then
            tipControlTable.UnitActionMouseover:SetSizeVal( 350, mouseoverSize.y );
        end
        return
    end

Here is UnitPanel.lua with the edits I made. You can use it as a template:
https://drive.google.com/file/d/1nBRKDX0MGddxDIM7iQRRlPIeLQNjbYvq/view?usp=sharing

I hope that helps.
 
So I made a dummy improvement but the problem is that I only want it to be available in certain circumstances. However, the only way I found that you could restrict them was making them civ-specific or have a tech requirement. Obviously being civ specific doesn't help me, but the tech prerequisite does help. I made a dummy technology, but I still have a couple issues with that, mainly that when you give a technology to the player it gives a big popup, which I obviously don't want.

So does anyone know:

1. How to restrict building improvements, other than tech/civ requirements?
2. How to make a dummy technology not show a popup?

I am using the VP DLL, and I know they implemented the "IsDummy" column for buildings but I was wondering if they did anything like that for Technologies.

If not, I'll probably use @Kewlipo 's Ability thing he showed. It's a really good solution, but the fact the AI can't use it means I'll probably just have to fake it, as if the AI did it.
 
As you're using VP (based on my DLL)

Code:
    <!-- Events sent by plots (v30) -->
    <!--   GameEvents.PlayerCanBuild.Add(function(iPlayer, iUnit, iX, iY, iBuild) return true end) -->
    <!--   GameEvents.PlotCanImprove.Add(function(iX, iY, iImprovement) return true end) -->
    <!--   GameEvents.PlayerBuilding.Add(function(iPlayer, iUnit, iX, iY, iBuild, bStarting) end) (v46) -->
    <!--   GameEvents.PlayerBuilt.Add(function(iPlayer, iUnit, iX, iY, iBuild) end) (v46) -->
    <!-- See also: "Improvement - Pontoon Bridge" -->
    <Row Class="3" Name="EVENTS_PLOT" Value="0"/>

And pTeam:SetHasTech(iTech, true) generates the popup, whereas pTeam:GetTeamTechs():SetHasTech(iTech, true) doesn't

And if you want to use the custom mission event, the tutorial is here - https://forums.civfanatics.com/threads/dll-various-mod-components.479600/page-56#post-13643698
 
Last edited:
As you're using VP (based on my DLL)

Code:
    <!-- Events sent by plots (v30) -->
    <!--   GameEvents.PlayerCanBuild.Add(function(iPlayer, iUnit, iX, iY, iBuild) return true end) -->
    <!--   GameEvents.PlotCanImprove.Add(function(iX, iY, iImprovement) return true end) -->
    <!--   GameEvents.PlayerBuilding.Add(function(iPlayer, iUnit, iX, iY, iBuild, bStarting) end) (v46) -->
    <!--   GameEvents.PlayerBuilt.Add(function(iPlayer, iUnit, iX, iY, iBuild) end) (v46) -->
    <!-- See also: "Improvement - Pontoon Bridge" -->
    <Row Class="3" Name="EVENTS_PLOT" Value="0"/>

And pTeam:SetHasTech(iTech, true) generates the popup, whereas pTeam:GetTeamTechs():SetHasTech(iTech, true) doesn't

And if you want to use the custom mission event, the tutorial is here - https://forums.civfanatics.com/threads/dll-various-mod-components.479600/page-56#post-13643698

This is exactly what I needed, thank you so much!

But now I've tried the Custom Mission Event and I can't seem to get it to work. I have a function for CustomMissionPossible and I added something that should print to the lua logs, but it doesn't. But when I just do a PlayerDoTurn function it prints to the logs.

I also get an error like:
Runtime Error: C:\Users\[My Name]\Documents\My Games\Sid Meier's Civilization 5\MODS\(1) Community Patch\Core Files\CoreLua\InGame.lua:941: bad argument #1 to 'ipairs' (table expected, got nil)

Here's my code:

.sql file, OnModActivated -> UpdateDatabase
Code:
INSERT INTO Missions
        (Type,                    Description,                    Help,                                DisabledHelp,                                   IconAtlas,                IconIndex, OrderPriority, Visible, EntityEventType,              Time)
VALUES  ('MISSION_IA_EXAMPLE',  'TXT_KEY_MISSION_IA_EXAMPLE',   'TXT_KEY_MISSION_IA_EXAMPLE_HELP',    'TXT_KEY_MISSION_IA_EXAMPLE_HELP_DISABLED',   'ATLAS_IA_MONOPOLY',        1,         201,           1,       'ENTITY_EVENT_GREAT_EVENT',   25);     


INSERT INTO Language_en_US (Tag, Text)
VALUES
('TXT_KEY_MISSION_IA_EXAMPLE', 'Example Mission'),
('TXT_KEY_MISSION_IA_EXAMPLE_HELP', 'This order will consume the Unit and do something.'),
('TXT_KEY_MISSION_IA_EXAMPLE_HELP_DISABLED', 'Must be in owned territory to do this action.');

.lua file, InGameUIAddin
Code:
local eBuildingDummyForPlantationHouse = GameInfo.Buildings.BUILDING_IA_PLANTATION_D.ID
local iMissionExample = GameInfoTypes.MISSION_IA_EXAMPLE

local CUSTOM_MISSION_NO_ACTION       = 0
local CUSTOM_MISSION_ACTION          = 1
local CUSTOM_MISSION_DONE            = 2
local CUSTOM_MISSION_ACTION_AND_DONE = 3

function OnCustomMissionPossible(iPlayer, iUnit, iMission, iData1, iData2, _, _, iPlotX, iPlotY, bTestVisible)

    print("MISSION POSSIBLE")
    local pPlayer = Players[iPlayer]
    local iNumPlantationDummies = pPlayer:CountNumBuildings(eBuildingDummyForPlantationHouse)

    local pUnit = pPlayer:GetUnitByID(iUnit)

    if (iMission == iMissionExample) then

        if (iNumPlantationDummies > 0) then

            if (pUnit:GetPlot():GetOwner() ~= iPlayer) then

                return bTestVisible
            else

                return true
            end
        end
    else

        return false
    end
end

function OnCustomMissionStart(iPlayer, iUnit, iMission, iData1, iData2, iFlags, iTurn)

    if (iMission == iMissionExample) then

        print("MISSION START")

        return CUSTOM_MISSION_NO_ACTION
    end

    return CUSTOM_MISSION_NO_ACTION
end



function OnCustomMissionCompleted(iPlayer, iUnit, iMission, iData1, iData2, iFlags, iTurn)
    if (iMission == iMissionExample) then
        return true
    end
    return false
end

function test(iPlayer)
    local pPlayer = Players[iPlayer]
    print("TEST")
    local iNumPlantationDummies = pPlayer:CountNumBuildings(eBuildingDummyForPlantationHouse)

    if (iNumPlantationDummies > 0) then
        print(">0 DUMMIES")
    end
end

GameEvents.PlayerDoTurn.Add(test)
GameEvents.CustomMissionPossible.Add(OnCustomMissionPossible)
GameEvents.CustomMissionStart.Add(OnCustomMissionStart)
GameEvents.CustomMissionCompleted.Add(OnCustomMissionCompleted)

Basically, it prints TEST or >0 DUMMIES if the dummy building is present, but nothing in the other functions works.
 
You don't seem to have enabled the events by setting the appropriate entry from custommodoptions to 1 (admittedly not shown in the linked example, by required for any events added by my DLL)
 
Everything is working perfectly now, thanks for your help. I even made code so that the AI "uses" it.

This isn't really necessary, but does anyone know how to make the animation play for the unit, or to make a "sparkle" or whatever show up on the tile? My ability is for Great People, and normally when you use them a little animation plays as well as a sparkle/light on the tile. I'm just using Unit:Kill() so the unit just disappears. It'd be cool if there was better indication.
 
Top Bottom