DLL - Various Mod Components

hello @whoward69 thank you for pointing out cause for the CTD.
just started learning lua to do modding. Do you think it possible to solve this problem provisionally by some lua scripts? I found one "team:IsDefensivePact" instance method but not sure about whether it works. do you have experience with the function before? I wrote the following lines (edited after some investigation of the function), do you have any suggestions or better ideas? thanks.


Code:
function CSDefPact(iPlayer)             
    local pPlayer = Players[iPlayer]
    local pTeam = Teams[pPlayer:GetTeam()]

    if pPlayer:IsAlive() then

        if pPlayer:IsMinorCiv() then
            for iMajorPlayer = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
                local iMajorTeam = Teams[Players[iMajorPlayer]:GetTeam()]   
                if pTeam:IsDefensivePact(iMajorTeam) then
                    for pCity in pPlayer:Cities() do               
                        pCity:SetNeverLost(true)
                    end
                    return
                else
                    for pCity in pPlayer:Cities() do               
                        pCity:SetNeverLost(false)
                    end
                 end
            end
    end
end
GameEvents.PlayerDoTurn.Add(CSDefPact)
 
Last edited:
hello @whoward69 thank you for pointing out cause for the CTD.
just started learning lua to do modding. Do you think it possible to solve this problem provisionally by some lua scripts? I found one "team:IsDefensivePact" instance method but not sure about whether it works. do you have experience with the function before? I wrote the following lines (edited after some investigation of the function), do you have any suggestions or better ideas? thanks.


Code:
function CSDefPact(iPlayer)            
    local pPlayer = Players[iPlayer]
    local pTeam = Teams[pPlayer:GetTeam()]

    if pPlayer:IsAlive() then

        if pPlayer:IsMinorCiv() then
            for iMajorPlayer = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
                local iMajorTeam = Teams[Players[iMajorPlayer]:GetTeam()]  
                if pTeam:IsDefensivePact(iMajorTeam) then
                    for pCity in pPlayer:Cities() do              
                        pCity:SetNeverLost(true)
                    end
                    return
                else
                    for pCity in pPlayer:Cities() do              
                        pCity:SetNeverLost(false)
                    end
                 end
            end
    end
end
GameEvents.PlayerDoTurn.Add(CSDefPact)
update: built a mod based on this code but failed in a save with the CTD error. 💔
 
Lua's not going to fix it, as the problem seems to be some corruption in that game, so it'll need a DLL patch to detect and ignore the corruption
 
Lua's not going to fix it, as the problem seems to be some corruption in that game, so it'll need a DLL patch to detect and ignore the corruption
yes you're right. temporarily bypassed the corruption by SetDamage(0) to city states at war, lose the joy and sense of accomplishment of liberating CS, though. XD
 
Recently discovered that the table Policy_BuildingClassYieldModifiers doesn't work with negative values, but was more surprised to find that VMC seems to have retained the issue. Is there a technical reason why Unified Yields also performs a > 0 check on the value retrieved by GetBuildingClassYieldModifiers, or is it a restriction that could be lifted?
 
Not sure if someone has pointed this out but Improvement - Tunnels (v. 6) isn't working with the 34 civ DLL. I'm getting this message in the logs: "table Improvements has no column named NegatesTerrainDamage". I only have the DLL and that one mod loaded.

Edit: version 5 is compatible.
 
Last edited:
Very weird. Easy way to patch this is to overwrite the PlotBasedDamage.sql file (or something like that) with the one from the standard DLL-VMC
 
Very weird. Easy way to patch this is to overwrite the PlotBasedDamage.sql file (or something like that) with the one from the standard DLL-VMC
I noticed the 34 civ DLL hasn’t been updated since 2019 unlike the regular DLL. I figured this is this cause of the problem.
 
Hi @whoward69
is it possible to share the pdb file after dll compilation? I want to check the dump file after CTD so tried to git-clone your project to local directory but failed to build the dll from your source code in VS. ..
 
@mayuyu from what I can see in that CvMiniDump file, the City State is in a defensive pact with a major civ - and that should never happen!

I'll add a note to the code to defend against the possibility if/when I next do an update
it seems I've figured out the cause for the CTD upon capturing CS. I'm also using your AI - Warmonger Adjustments mod and After I deleted all contents in the xml, game continues without CTD. Confirmed this in 2 saves.

Update: after some testing I found that if set "WARMONGER_THREAT_COOP_WAR_PERCENT" to 100 then the problem is fixed.
 
Last edited:
@whoward69 I have a number of questions:
  1. I struggled to find a detailed description of what the "No Science Overflow Exploit" mod does.
    1. Does it disable Firaxis' "fix" for the Science overflow exploit (which had unintuitive side effects of capping Science overflow from all sources, even legitimately-earned Science from Great Scientists)?
    2. How does your mod address the Science overflow exploit? Does it completely disable the plagiarism (researching a Tech that other Civs already have) multiplier? Or does it just set a cap on Science earned via the plagiarism multiplier?
    3. If your mod sets a cap on the Science earned via the plagiarism modifier, then what is the cap? Is it a Turn's worth of Science? Is it the cost of the Tech? Is it something else?
  2. Is it possible to add support for intercepting Nuclear Weapons? According to the Civilopedia, it should be possible to intercept Nuclear Weapons - but this doesn't currently work in practice, apparently because Firaxis never got around to implementing it (see https://steamcommunity.com/app/8930/discussions/0/154642447913171735/ and https://github.com/yairm210/Unciv/issues/4697#issuecomment-1001174733).
 
IIRC it replaces the Firaxis fix and correctly calculates the "refund" due to base tech (and not what was left over from researching a tech after all the multipliers were applied). As this was years ago I've forgotten the exact details. There was a very detailed thread on the subject on these forums but it appears to have been lost to the mists of time. If you want the precise details, the code is on GitHub, search for MOD_BUGFIX_RESEARCH_OVERFLOW
 
Hey whoward, I have a couple questions/possible bugfixes I wanted to bring to you. If this is out of nowhere for my first post, see the TLDR below! I promise these are not about compatibility with other mods.

First, I used your technique for an invisible promotion in combination with LeeS' Civ-Linked Great Generals to make the internal promotion invisible, and it worked perfectly. But since my main goal in modding is to condense mods and teach myself for fun, I am combining a whole bunch. The next mod that modded the UnitPanel.lua was your UI - Condensed Promotions. That one works too, but suddenly the Created promotion is visible again. I nearly pulled my hair out trying to track it down, and I think your Condensed Promotions mod had a line deleted by accident at some point. (redownloaded and verified that v15 is still missing it).

Between lines 794 and 795 of UnitPanel.lua in Condensed Promotions, there is a local missing
Code:
    local unitPromotionID = unitPromotion.ID;

You also left out the condition in the if clause...
Code:
if (unitPromotion.ShowInUnitPanel == 1 and
...even though you added it to the original function in the very same file (and which is now ignored!) Condensed Promotions work, but trying to use it with the invisible promotion functionality doesn't! (alternatively, you didn't want that functionality in this mod and left it in the original function either by mistake or because you knew it was ignored?)

Second, there is a column in the Beliefs table called FaithFromDyingUnits. I've read lots of talk here and elsewhere about it not working and being deprecated, so after testing it extensively I downloaded your dll source and started digging into it. All the hookups seem to be there. From what I can tell, the only thing missing is the actual function.

Spoiler FaithFromKills in CvBeliefClasses.cpp has this :

Code:
/// Faith from kills
int CvReligionBeliefs::GetFaithFromKills(int iDistance) const
{
    CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs();
    int rtnValue = 0;
    int iRequiredDistance;

    for(int i = 0; i < pBeliefs->GetNumBeliefs(); i++)
    {
        if(HasBelief((BeliefTypes)i))
        {
            iRequiredDistance = pBeliefs->GetEntry(i)->GetMaxDistance();
            if(iRequiredDistance == 0 || iDistance <= iRequiredDistance)
            {
                rtnValue += pBeliefs->GetEntry(i)->GetFaithFromKills();
            }
        }
    }

    return rtnValue;
}

Is it as simple as duplicating that, but substituting FaithFromDyingUnits? If yes, are you interested in implementing it? I know your goal is more about bugfixes and QoL stuff, but if that's all that's needed I can add it to my own copy simply enough.. I'm currently in the process of writing lua to make this tag function but running into the issues with UnitPrekill that have also been documented on this forum. I'd rather a clean, complete fix.

Thanks for your time!

Spoiler TLDR - all my gushing and thanks :
Been lurking since the first month of the pandemic when I first got into Civ5 modding, and you and everyone on this forum here have taught me so much. I'm only an amateur programmer but haven't done much in years until Civ, and I've since learned SQL and lua thanks to you guys. I have a few mods I want to post sometime in the future if I get up the nerve (and perhaps have them in a slightly more presentable form...and cover all my bases to make sure I've credited everyone...)

I've become quite proud of how good I've gotten at lua, so I'm relatively confident these won't just be stupid questions for you. I've even done extensive modding of ASP.lua (forgive my language) over these last three years. Pretty sure I can say I understand that file better than most by now! C++ is a language I haven't used much though, as I was always more focused on web stuff (html, java, css, etc.), so your dll has been a godsend. I especially appreciate your thorough documenting of your code and try to emulate it in my own mods. It really is good practice that has saved me a lot of headaches. :thumbsup:[/ISPOILER]
 
Last edited:
Is it as simple as duplicating that, but substituting FaithFromDyingUnits?
Nope, you would also need to find all the code that calls CvReligionBeliefs::GetFaithFromKills(int iDistance) and add the equivalent for your new method
If yes, are you interested in implementing it?
Nope
 
Nope, you would also need to find all the code that calls CvReligionBeliefs::GetFaithFromKills(int iDistance) and add the equivalent for your new method
Ah, of course. Thank you for the direction!
 
2. Is it possible to add support for intercepting Nuclear Weapons? According to the Civilopedia, it should be possible to intercept Nuclear Weapons - but this doesn't currently work in practice, apparently because Firaxis never got around to implementing it (see https://steamcommunity.com/app/8930/discussions/0/154642447913171735/ and https://github.com/yairm210/Unciv/issues/4697#issuecomment-1001174733).​
Sort of... my idea would be to abort the attack if an interception occurs, ideally you could set on a unit-type-basis how high the damage should be before it aborts but I haven't successfully implemented that yet. Until then will occur a very humorous outcome of nuclear missiles being stopped by fighters...
Currently my code for CvUnitCombat.cpp just ends up having the nuke explosion as normal without any combat effects (need a mid-attack animation change), and it doesn't seem to do interception damage properly (1 HP lost???). Also has a chance of extending the intercepting team's turn indefinitely if they're AI (should definitely test what's from the human perspective is if I want to fix it).

Code:
void CvUnitCombat::GenerateNuclearCombatInfo(CvUnit& kAttacker, CvPlot& plot, CvCombatInfo* pkCombatInfo)
{
    int iExperience = 0;

    pkCombatInfo->setUnit(BATTLE_UNIT_ATTACKER, &kAttacker);
    pkCombatInfo->setUnit(BATTLE_UNIT_DEFENDER, NULL);
    pkCombatInfo->setPlot(&plot);

    // Any interception to be done?
    CvUnit* pInterceptor = kAttacker.GetBestInterceptor(plot);
    int iInterceptionDamage = 0;

    if(pInterceptor != NULL)
    {
        pkCombatInfo->setUnit(BATTLE_UNIT_INTERCEPTOR, pInterceptor);
        // Does the attacker evade?
        if(GC.getGame().getJonRandNum(100, "Evasion Rand") >= kAttacker.evasionProbability())
        {
            // Is the interception successful?
            if(GC.getGame().getJonRandNum(100, "Intercept Rand (Air)") < pInterceptor->currInterceptionProbability())
            {
                iInterceptionDamage = pInterceptor->GetInterceptionDamage(&kAttacker);
            }
        }
        pkCombatInfo->setDamageInflicted(BATTLE_UNIT_INTERCEPTOR, iInterceptionDamage);        // Damage inflicted this round
    }

    CvString strBuffer;
    bool abTeamsAffected[MAX_TEAMS];
    int iI;
    for(iI = 0; iI < MAX_TEAMS; iI++)
    {
        abTeamsAffected[iI] = kAttacker.isNukeVictim(&plot, ((TeamTypes)iI));
    }

    ...

    pkCombatInfo->setFinalDamage(BATTLE_UNIT_ATTACKER, iInterceptionDamage);        // Total damage to the unit
    pkCombatInfo->setDamageInflicted(BATTLE_UNIT_ATTACKER, 0);                        // Damage inflicted this round
    pkCombatInfo->setFinalDamage(BATTLE_UNIT_DEFENDER, 0);                            // Total damage to the unit
    pkCombatInfo->setDamageInflicted(BATTLE_UNIT_DEFENDER, 0);                        // Damage inflicted this round

    pkCombatInfo->setFearDamageInflicted(BATTLE_UNIT_ATTACKER, 0);

    pkCombatInfo->setExperience(BATTLE_UNIT_ATTACKER, 0);
    pkCombatInfo->setMaxExperienceAllowed(BATTLE_UNIT_ATTACKER, 0);
    pkCombatInfo->setInBorders(BATTLE_UNIT_ATTACKER, plot.getOwner() != kAttacker.getOwner());    // Not really correct
    pkCombatInfo->setUpdateGlobal(BATTLE_UNIT_ATTACKER, !kAttacker.isBarbarian());

    pkCombatInfo->setExperience(BATTLE_UNIT_DEFENDER, 0);
    pkCombatInfo->setMaxExperienceAllowed(BATTLE_UNIT_DEFENDER, 0);
    pkCombatInfo->setInBorders(BATTLE_UNIT_DEFENDER, plot.getOwner() == kAttacker.getOwner());
    pkCombatInfo->setUpdateGlobal(BATTLE_UNIT_DEFENDER, false);

    if (iInterceptionDamage > 0)
    {
        iExperience = /*2*/ GC.getEXPERIENCE_DEFENDING_AIR_SWEEP_GROUND();
        pkCombatInfo->setExperience( BATTLE_UNIT_INTERCEPTOR, iExperience );
        pkCombatInfo->setMaxExperienceAllowed( BATTLE_UNIT_INTERCEPTOR, MAX_INT );
        pkCombatInfo->setInBorders( BATTLE_UNIT_INTERCEPTOR, plot.getOwner() == kAttacker.getOwner() );
        pkCombatInfo->setUpdateGlobal( BATTLE_UNIT_INTERCEPTOR, false );
    }

    pkCombatInfo->setAttackIsBombingMission(true);
    pkCombatInfo->setDefenderRetaliates(true);
    pkCombatInfo->setAttackNuclearLevel(kAttacker.GetNukeDamageLevel() + 1);

    if (!iInterceptionDamage)
    {
        // Set all of the units in the blast radius to defenders and calculate their damage
        int iDamageMembers = 0;
        GenerateNuclearExplosionDamage(&plot, kAttacker.GetNukeDamageLevel(), &kAttacker, pkCombatInfo->getDamageMembers(), &iDamageMembers, pkCombatInfo->getMaxDamageMemberCount());
        pkCombatInfo->setDamageMemberCount(iDamageMembers);
    }

    GC.GetEngineUserInterface()->setDirty(UnitInfo_DIRTY_BIT, true);
}

...

void CvUnitCombat::ResolveNuclearCombat(const CvCombatInfo& kCombatInfo, uint uiParentEventID)
{
    UNREFERENCED_PARAMETER(uiParentEventID);

    bool bTargetDied = false;
    int iAttackerDamageInflicted = kCombatInfo.getDamageInflicted(BATTLE_UNIT_ATTACKER);
    int iDefenderDamageInflicted = kCombatInfo.getDamageInflicted(BATTLE_UNIT_DEFENDER);

    CvUnit* pkAttacker = kCombatInfo.getUnit(BATTLE_UNIT_ATTACKER);
    CvAssert_Debug(pkAttacker);

    ICvUserInterface2* pkDLLInterface = GC.GetEngineUserInterface();
                             

    CvString strBuffer;

    int iActivePlayerID = GC.getGame().getActivePlayer();

    // Interception?
    int iInterceptionDamage = kCombatInfo.getDamageInflicted(BATTLE_UNIT_INTERCEPTOR);
    if (iInterceptionDamage > 0)
        iDefenderDamageInflicted += iInterceptionDamage;

    CvUnit* pInterceptor = kCombatInfo.getUnit(BATTLE_UNIT_INTERCEPTOR);
    CvAssert_Debug(pInterceptor);
    if (pInterceptor)
    {
        pInterceptor->setMadeInterception(true);
        pInterceptor->setCombatUnit(NULL);
        pInterceptor->changeExperience(
            kCombatInfo.getExperience(BATTLE_UNIT_INTERCEPTOR),
            kCombatInfo.getMaxExperienceAllowed(BATTLE_UNIT_INTERCEPTOR),
            true,
            kCombatInfo.getInBorders(BATTLE_UNIT_INTERCEPTOR),
            kCombatInfo.getUpdateGlobal(BATTLE_UNIT_INTERCEPTOR));
        if(!CvUnitMission::IsHeadMission(pInterceptor, CvTypes::getMISSION_WAIT_FOR()))        // If the top mission was not a 'wait for', then clear it.
                pInterceptor->ClearMissionQueue();
        if (pkAttacker)
        {
            pkAttacker->changeDamage(iDefenderDamageInflicted, pInterceptor->getOwner());

            // Attacker died
            if(pkAttacker->IsDead())
            {
                auto_ptr<ICvUnit1> pAttacker = GC.WrapUnitPointer(pkAttacker);
                gDLL->GameplayUnitDestroyedInCombat(pAttacker.get());

                if(iActivePlayerID == pkAttacker->getOwner())
                {
                    strBuffer = GetLocalizedText("TXT_KEY_MISC_YOU_UNIT_SHOT_DOWN", pkAttacker->getNameKey(), pInterceptor->getNameKey());
                    pkDLLInterface->AddMessage(uiParentEventID, pkAttacker->getOwner(), true, GC.getEVENT_MESSAGE_TIME(), strBuffer/*, GC.getEraInfo(GC.getGame().getCurrentEra())->getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pkTargetPlot->getX(), pkTargetPlot->getY()*/);
                }
                if(iActivePlayerID == pInterceptor->getOwner())
                {
                    strBuffer = GetLocalizedText("TXT_KEY_MISC_ENEMY_AIR_UNIT_DESTROYED", pInterceptor->getNameKey(), pkAttacker->getVisualCivAdjective(pInterceptor->getTeam()), pkAttacker->getNameKey(), pInterceptor->getNameKey());
                    pkDLLInterface->AddMessage(uiParentEventID, pInterceptor->getOwner(), true, GC.getEVENT_MESSAGE_TIME(), strBuffer/*, GC.getEraInfo(GC.getGame().getCurrentEra())->getAudioUnitVictoryScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pkTargetPlot->getX(), pkTargetPlot->getY()*/);
                }

                ApplyPostCombatTraitEffects(pkAttacker, pInterceptor);     
            }

            // Clean up some stuff
            pkAttacker->setCombatUnit(NULL);
            pkAttacker->setAttackPlot(NULL, false);
            pkAttacker->ClearMissionQueue(GetPostCombatDelay());
            pkAttacker->SetAutomateType(NO_AUTOMATE); // kick unit out of automation

            // Spend a move for this attack
            pkAttacker->changeMoves(-GC.getMOVE_DENOMINATOR());

            // Can't move or attack again
            if (!pkAttacker->canMoveAfterAttacking())
            {
                pkAttacker->finishMoves();
            }
        }

        // Report that combat is over in case we want to queue another attack
        GET_PLAYER(pkAttacker->getOwner()).GetTacticalAI()->CombatResolved(pkAttacker, true);
    }
    else
    {
        CvPlot* pkTargetPlot = kCombatInfo.getPlot();
        CvAssert_Debug(pkTargetPlot);

        CvString strBuffer;

        GC.getGame().changeNukesExploded(1);

...

CvUnitCombat::ATTACK_RESULT CvUnitCombat::AttackNuclear(CvUnit& kAttacker, int iX, int iY, ATTACK_OPTION /* eOption */)
{

...

        // Set a combat unit/city.  Not really needed for the combat since we are killing everyone, but it is currently the only way a unit is marked that it is 'in-combat'
        if(pPlot->getPlotCity())
            kAttacker.setCombatCity(pPlot->getPlotCity());
        else
        {
            if(pPlot->getNumUnits())
                kAttacker.setCombatUnit(pPlot->getUnitByIndex(0), true);
            else
                kAttacker.setAttackPlot(pPlot, false);
        }

        auto_ptr<ICvCombatInfo1> pDllCombatInfo(new CvDllCombatInfo(&kCombatInfo));
        uiParentEventID = gDLL->GameplayUnitCombat(pDllCombatInfo.get());

        CvUnit* pDefenderSupport = kCombatInfo.getUnit(BATTLE_UNIT_INTERCEPTOR);
        if (pDefenderSupport)
            pDefenderSupport->setCombatUnit(&kAttacker, false);

        eResult = ATTACK_QUEUED;
    }
    else
    {
        eResult = ATTACK_COMPLETED;
        // Set the plot, just so the unit is marked as 'in-combat'
        kAttacker.setAttackPlot(pPlot, false);
    }

    ResolveCombat(kCombatInfo,  uiParentEventID);

    return eResult;
}
 

Attachments

  • InterceptableNukes.zip
    2.6 MB · Views: 15
I guess I'm not really sure what to do with this Lua.

So if I used this function with the code, it always works.
Code:
GameEvents.GreatPersonExpended.Add(function(iPlayer, iUnit, iUnitType, iX, iY)
    local pUnit = Players[iPlayer]:GetUnitByID(iUnit)
    if pUnit:GetUnitClassType() == GameInfoTypes.UNITCLASS_GREAT_ADMIRAL then
        local pPlot = Map.GetPlot(pUnit:GetX(), pUnit:GetY())
        for a = 0, pPlot:GetNumUnits() - 1, 1 do
            local pStackedUnit = pPlot:GetUnit(a)
            if pStackedUnit:GetOwner() == iPlayer and pStackedUnit:IsCombatUnit() and (pStackedUnit:GetDomainType() == DomainTypes.DOMAIN_SEA or pStackedUnit:IsEmbarked()) then
                pStackedUnit:ChangeExperience(15)
                print("wholesome2")
            end
        end   
        for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
            local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
            for a = 0, pAdjacentPlot:GetNumUnits() - 1, 1 do
                local pStackedUnit = pAdjacentPlot:GetUnit(a)
                if pStackedUnit:GetOwner() == iPlayer and pStackedUnit:IsCombatUnit() and (pStackedUnit:GetDomainType() == DomainTypes.DOMAIN_SEA or pStackedUnit:IsEmbarked()) then
                    pStackedUnit:ChangeExperience(15)
                    print("wholesome")
                end
            end
        end
    end
end)
However... I only want it to detect Repair Fleet. So I thought to use CustomMissionCompleted to see if this would work.
Code:
function AdmiralComplete(iPlayer, iUnit, iMission, iData1, iData2, iFlags, iTurn)
    print(iPlayer)
    print(iUnit)
    print(iMission)
    if iMission == GameInfoTypes.MISSION_REPAIR_FLEET then
        local pUnit = Players[iPlayer]:GetUnitByID(iUnit)
        if pUnit:GetUnitClassType() == GameInfoTypes.UNITCLASS_GREAT_ADMIRAL then
            local pPlot = Map.GetPlot(pUnit:GetX(), pUnit:GetY())
            for a = 0, pPlot:GetNumUnits() - 1, 1 do
                local pStackedUnit = pPlot:GetUnit(a)
                if pStackedUnit:GetOwner() == iPlayer and pStackedUnit:IsCombatUnit() and (pStackedUnit:GetDomainType() == DomainTypes.DOMAIN_SEA or pStackedUnit:IsEmbarked()) then
                    pStackedUnit:ChangeExperience(15)
                    print("wholesome2")
                end
            end   
            for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
                local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
                for a = 0, pAdjacentPlot:GetNumUnits() - 1, 1 do
                    local pStackedUnit = pAdjacentPlot:GetUnit(a)
                    if pStackedUnit:GetOwner() == iPlayer and pStackedUnit:IsCombatUnit() and (pStackedUnit:GetDomainType() == DomainTypes.DOMAIN_SEA or pStackedUnit:IsEmbarked()) then
                        pStackedUnit:ChangeExperience(15)
                        print("wholesome")
                    end
                end
            end
            print("finally")
            return true
        end
        print("itbrokeagain?")
        return true
    end
    print("it broke")
    return false
end
GameEvents.CustomMissionCompleted.Add(AdmiralComplete)

However running through a few print statements, I guess CustomMissionCompleted does not pick up on these missions since it seems to be printing out (move to) missions instead of any of the GP actions like building an improvement. Just a question whether CustomMissionCompleted even works with base missions then or am I doing something completely wrong.
 
Top Bottom