1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Modders Guide to FfH2

Discussion in 'Civ4 - Fall from Heaven' started by Kael, Aug 14, 2007.

  1. Rando

    Rando Warlord

    Joined:
    Dec 7, 2005
    Messages:
    129
    Hi - I have another question and apologize, I am sure this has been asked and answered at some point - but how do you make hero popups? I have made buttons, flags, etc using Paintshop and DXTBmp - but the hero popups seems to be a bit different somehow. Everytime I try to make my hero popup one of two things happens - the image either rescales to 512 x 128 and doesn't show up in game, or I am able to get it to show up in game at the correct dimensions 384 X 128 but it has lines all through it. It seems there may be some trick for these... any tips?
     
  2. xienwolf

    xienwolf Deity

    Joined:
    Oct 4, 2007
    Messages:
    10,589
    Location:
    Location! Location!
    isFemale() for leaderheads was never exposed to python, so no easy way to tell gender outside of the DLL.

    Hero popups shouldn't be special in any way, just save them like any other DDS (make sure not to create MIP Maps) and it ought to work.
     
  3. Thunder_Gr

    Thunder_Gr Emperor

    Joined:
    Dec 8, 2008
    Messages:
    1,362
    Location:
    Universe
    One could always go to the dll and just add "export" in front of the metod in the .h file.
    This would expose it to python, right?
     
  4. xienwolf

    xienwolf Deity

    Joined:
    Oct 4, 2007
    Messages:
    10,589
    Location:
    Location! Location!
    No, DllExport in the *.h files exposes the function to other DLLs and the exe. To expose to python you have to work in the Cy____.cpp/h files. To expose part of CvInfos.cpp is a single line in the appropriate CyInfoInterface#.cpp file, but it still requires a recompile, which some people dislike doing, and a distribution of a new DLL if you share the mod, which drastically increases the size (unless you already offered new artwork as well)
     
  5. Turinturambar

    Turinturambar Prince

    Joined:
    Dec 19, 2005
    Messages:
    368
    I have two questions. Would be great if anyone could answer them.

    First: How is rounding done in the Python files? Does it do true rounding and how many digits are left after rounding?

    Second: is there an easy way to include a file that overrides definitions in other file. For example I want to create a separate file that overrides def onCityDoTurn(self, argsList): so that I can modify the game and still use Kael's patches. Can I create a separate python file and somehow give it higher priority?
     
  6. Opera

    Opera Deity

    Joined:
    Sep 21, 2008
    Messages:
    4,643
    Gender:
    Female
    I'm having a problem with the FfH editor. The dropdown list for GraphicalOnly in Leaders and Civilizations tabs don't work; it lists promotions. I'm using Excel 2007. Is this normal or buggy?
     
  7. xienwolf

    xienwolf Deity

    Joined:
    Oct 4, 2007
    Messages:
    10,589
    Location:
    Location! Location!
  8. Turinturambar

    Turinturambar Prince

    Joined:
    Dec 19, 2005
    Messages:
    368
    Thanks, reading up a bit on python tells me that python automatically assigns floats/integers as necessary, except when doing divisions. I presume that is true for FFH2 as well? Or is the little i or b in front of some variables explicitly defining them as integers or boolean variables?
     
  9. xienwolf

    xienwolf Deity

    Joined:
    Oct 4, 2007
    Messages:
    10,589
    Location:
    Location! Location!
    the i or b is just there for our sanity and tracking capabilities. And since Python will switch your type at the drop of a hat, it's more so just to say what we THINK it should be and would LIKE to have it be. And yes, anything you treat like a float gets turned into a float, with division being the main sticking point where most people learn that python hates them, and it is personal.
     
  10. arcticnightwolf

    arcticnightwolf Emperor

    Joined:
    Jun 8, 2008
    Messages:
    1,301
    Location:
    Prague, Czech Republic
    Is there any way, how to hide spell notice in messagebox ? (The yellow text in top-center place of screen)
    Because I've added spell, that gives name to unit (MarnokNameGenerator in CustomFunctions.py :D ) - and I don't want it to notice ...

    edit: One more question ... Avatar of Wrath init. action (enraging x% of units) is binded to unit or AC ??
     
  11. Marnok

    Marnok Marnok

    Joined:
    Mar 24, 2008
    Messages:
    506
    Taking control of culture spread in cities - has anyone done anything on this? Just to save me trawling through the dll, if anyone already knows which functions control this, please let me know!
     
  12. Imuratep

    Imuratep Cultist of the Old Ones

    Joined:
    Sep 4, 2008
    Messages:
    722
    Location:
    Germany
    Is it possible to decrease the costs of a tech, unit etc. dependant on your current leader traits?
     
  13. Minor Annoyance

    Minor Annoyance Deity

    Joined:
    Jun 27, 2007
    Messages:
    2,247
    Gender:
    Male
    Location:
    Hamilton, Ontario
    I'm pretty sure this will end up being something simple that I would have known already if I was learning C++ in a way other that reverse engineering, but here we are. It's probably very basic to how functions work... Is that the right term? Function?
    Anyway, I've been looking at CvUnit::combatWon:

    Spoiler :
    Code:
    void CvUnit::combatWon(CvUnit* pLoser, bool bAttacking)
    {
        PromotionTypes ePromotion;
        bool bConvert = false;
        int iUnit = NO_UNIT;
        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;
        CvPlot* pPlot;
        CvUnit* pUnit;
    
        for (int iI = 0; iI < GC.getNumPromotionInfos(); iI++)
        {
            if (isHasPromotion((PromotionTypes)iI))
            {
                if (GC.getPromotionInfo((PromotionTypes)iI).getFreeXPFromCombat() != 0)
                {
                    changeExperience(GC.getPromotionInfo((PromotionTypes)iI).getFreeXPFromCombat(), -1, false, false, false);
                }
                if (GC.getPromotionInfo((PromotionTypes)iI).getModifyGlobalCounterOnCombat() != 0)
                {
                    if (pLoser->isAlive())
                    {
                        GC.getGameINLINE().changeGlobalCounter(GC.getPromotionInfo((PromotionTypes)iI).getModifyGlobalCounterOnCombat());
                    }
    		    }
                if (GC.getPromotionInfo((PromotionTypes)iI).isRemovedByCombat())
                {
                    setHasPromotion(((PromotionTypes)iI), false);
    		    }
                if (GC.getPromotionInfo((PromotionTypes)iI).getPromotionCombatApply() != NO_PROMOTION)
                {
                    ePromotion = (PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getPromotionCombatApply();
                    pPlot = pLoser->plot();
                    pUnitNode = pPlot->headUnitNode();
                    while (pUnitNode != NULL)
                    {
                        pLoopUnit = ::getUnit(pUnitNode->m_data);
                        pUnitNode = pPlot->nextUnitNode(pUnitNode);
                        if (pLoopUnit->isHasPromotion(ePromotion) == false)
                        {
                            if (pLoopUnit->isAlive() || !GC.getPromotionInfo(ePromotion).isPrereqAlive())
                            {
                                if (isEnemy(pLoopUnit->getTeam()))
                                {
                                    if (pLoopUnit->canAcquirePromotion(ePromotion))
                                    {
                                        if (GC.getGameINLINE().getSorenRandNum(100, "Combat Apply") <= GC.getDefineINT("COMBAT_APPLY_CHANCE"))
                                        {
                                            pLoopUnit->setHasPromotion(ePromotion, true);
                                            gDLL->getInterfaceIFace()->addMessage((PlayerTypes)pLoopUnit->getOwner(), true, GC.getEVENT_MESSAGE_TIME(), GC.getPromotionInfo(ePromotion).getDescription(), "", MESSAGE_TYPE_INFO, GC.getPromotionInfo(ePromotion).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
                                            gDLL->getInterfaceIFace()->addMessage((PlayerTypes)getOwner(), true, GC.getEVENT_MESSAGE_TIME(), GC.getPromotionInfo(ePromotion).getDescription(), "", MESSAGE_TYPE_INFO, GC.getPromotionInfo(ePromotion).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                if (GC.getPromotionInfo((PromotionTypes)iI).getCombatCapturePercent() != 0)
                {
                    if (iUnit == NO_UNIT && pLoser->isAlive())
                    {
                        if (GC.getGameINLINE().getSorenRandNum(100, "Combat Capture") <= GC.getPromotionInfo((PromotionTypes)iI).getCombatCapturePercent())
                        {
                            iUnit = pLoser->getUnitType();
                            bConvert = true;
                        }
                    }
                }
                if (GC.getPromotionInfo((PromotionTypes)iI).getCaptureUnitCombat() != NO_UNITCOMBAT)
                {
                    if (iUnit == NO_UNIT && pLoser->getUnitCombatType() == GC.getPromotionInfo((PromotionTypes)iI).getCaptureUnitCombat())
                    {
                        iUnit = pLoser->getUnitType();
                        bConvert = true;
                    }
                }
    		}
            if (pLoser->isHasPromotion((PromotionTypes)iI))
            {
                if (GC.getPromotionInfo((PromotionTypes)iI).getPromotionCombatApply() != NO_PROMOTION)
                {
                    ePromotion = (PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getPromotionCombatApply();
                    if (isHasPromotion(ePromotion) == false)
                    {
                        if (isAlive() || !GC.getPromotionInfo(ePromotion).isPrereqAlive())
                        {
                            if (pLoser->isEnemy(getTeam()))
                            {
                                if (GC.getGameINLINE().getSorenRandNum(100, "Combat Apply") <= GC.getDefineINT("COMBAT_APPLY_CHANCE"))
                                {
                                    setHasPromotion(ePromotion, true);
                                    gDLL->getInterfaceIFace()->addMessage((PlayerTypes)getOwner(), true, GC.getEVENT_MESSAGE_TIME(), GC.getPromotionInfo(ePromotion).getDescription(), "", MESSAGE_TYPE_INFO, GC.getPromotionInfo(ePromotion).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
                                    gDLL->getInterfaceIFace()->addMessage((PlayerTypes)pLoser->getOwner(), true, GC.getEVENT_MESSAGE_TIME(), GC.getPromotionInfo(ePromotion).getDescription(), "", MESSAGE_TYPE_INFO, GC.getPromotionInfo(ePromotion).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
                                }
                            }
                        }
                    }
    		    }
            }
    	}
        if (GET_PLAYER(getOwnerINLINE()).getFreeXPFromCombat() != 0)
        {
            changeExperience(GET_PLAYER(getOwnerINLINE()).getFreeXPFromCombat(), -1, false, false, false);
        }
        if (getCombatHealPercent() != 0)
        {
            if (pLoser->isAlive())
            {
                int i = getCombatHealPercent();
                if (i > getDamage())
                {
                    i = getDamage();
                }
                if (i != 0)
                {
                    changeDamage(-1 * i, NO_PLAYER);
                }
            }
        }
        if (m_pUnitInfo->isExplodeInCombat() && m_pUnitInfo->isSuicide())
        {
            if (bAttacking)
            {
                pPlot = pLoser->plot();
            }
            else
            {
                pPlot = plot();
            }
            if (plot()->isVisibleToWatchingHuman())
            {
                gDLL->getEngineIFace()->TriggerEffect((EffectTypes)GC.getInfoTypeForString("EFFECT_ARTILLERY_SHELL_EXPLODE"), pPlot->getPoint(), (float)(GC.getASyncRand().get(360)));
                gDLL->getInterfaceIFace()->playGeneralSound("AS3D_UN_GRENADE_EXPLODE", pPlot->getPoint());
            }
        }
        if (GC.getUnitInfo(pLoser->getUnitType()).isExplodeInCombat())
        {
            if (plot()->isVisibleToWatchingHuman())
            {
                gDLL->getEngineIFace()->TriggerEffect((EffectTypes)GC.getInfoTypeForString("EFFECT_ARTILLERY_SHELL_EXPLODE"), plot()->getPoint(), (float)(GC.getASyncRand().get(360)));
                gDLL->getInterfaceIFace()->playGeneralSound("AS3D_UN_GRENADE_EXPLODE", plot()->getPoint());
            }
        }
        if ((m_pUnitInfo->getEnslavementChance() + GET_PLAYER(getOwnerINLINE()).getEnslavementChance()) > 0)
        {
            if (getDuration() == 0 && pLoser->isAlive() && !pLoser->isAnimal() && iUnit == NO_UNIT)
            {
                if (GC.getGameINLINE().getSorenRandNum(100, "Enslavement") <= (m_pUnitInfo->getEnslavementChance() + GET_PLAYER(getOwnerINLINE()).getEnslavementChance()))
                {
                    iUnit = GC.getDefineINT("SLAVE_UNIT");
                }
            }
        }
        if (m_pUnitInfo->getPromotionFromCombat() != NO_PROMOTION)
        {
            if (pLoser->isAlive())
            {
                setHasPromotion((PromotionTypes)m_pUnitInfo->getPromotionFromCombat(), true);
            }
        }
        if (getGoldFromCombat() != 0)
        {
            if (!pLoser->isAnimal())
            {
                GET_PLAYER(getOwnerINLINE()).changeGold(getGoldFromCombat());
                CvWString szBuffer = gDLL->getText("TXT_KEY_MESSAGE_GOLD_FROM_COMBAT", getGoldFromCombat()).GetCString();
                gDLL->getInterfaceIFace()->addMessage((PlayerTypes)getOwner(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_GOODY_GOLD", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), true, true);
            }
        }
        if (getDuration() > 0)
        {
            changeDuration(m_pUnitInfo->getDurationFromCombat());
        }
        if (pLoser->getDamageTypeCombat(DAMAGE_POISON) > 0 && GC.getDefineINT("POISONED_PROMOTION") != -1)
        {
            if (isAlive() && getDamage() > 0)
            {
                if (GC.getGameINLINE().getSorenRandNum(100, "Poisoned") >= getDamageTypeResist(DAMAGE_POISON))
                {
                    setHasPromotion((PromotionTypes)GC.getDefineINT("POISONED_PROMOTION"), true);
                }
            }
        }
        if (m_pUnitInfo->getUnitCreateFromCombat() != NO_UNIT)
        {
            if (!pLoser->isImmuneToCapture() && pLoser->isAlive() && GC.getUnitInfo((UnitTypes)pLoser->getUnitType()).getEquipmentPromotion() == NO_PROMOTION)
            {
                if (GC.getGameINLINE().getSorenRandNum(100, "Create Unit from Combat") <= m_pUnitInfo->getUnitCreateFromCombatChance())
                {
                    pUnit = GET_PLAYER(getOwnerINLINE()).initUnit((UnitTypes)m_pUnitInfo->getUnitCreateFromCombat(), plot()->getX_INLINE(), plot()->getY_INLINE());
                    pUnit->setDuration(getDuration());
                    if (isHiddenNationality())
                    {
                        pUnit->setHasPromotion((PromotionTypes)GC.getDefineINT("HIDDEN_NATIONALITY_PROMOTION"), true);
                    }
                    iUnit = NO_UNIT;
                }
            }
        }
        if (iUnit != NO_UNIT)
        {
            if ((!pLoser->isImmuneToCapture() && !isNoCapture() && !pLoser->isImmortal())
              || GC.getUnitInfo((UnitTypes)pLoser->getUnitType()).getEquipmentPromotion() != NO_PROMOTION)
            {
                pUnit = GET_PLAYER(getOwnerINLINE()).initUnit((UnitTypes)iUnit, plot()->getX_INLINE(), plot()->getY_INLINE());
                if (getDuration() != 0)
                {
                    pUnit->setDuration(getDuration());
                }
                if (iUnit == GC.getDefineINT("SLAVE_UNIT"))
                {
                    if (pLoser->getRace() != NO_PROMOTION)
                    {
                        pUnit->setHasPromotion((PromotionTypes)pLoser->getRace(), true);
                    }
                }
                if (bConvert)
                {
                    pLoser->setDamage(75, NO_PLAYER, false);
                    pUnit->convert(pLoser);
                }
            }
        }
        if (!isEmpty(GC.getUnitInfo(getUnitType()).getPyPostCombatWon()))
        {
            CyUnit* pyCaster = new CyUnit(this);
            CyUnit* pyOpponent = new CyUnit(pLoser);
            CyArgsList argsList;
            argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCaster));	// pass in unit class
            argsList.add(gDLL->getPythonIFace()->makePythonObject(pyOpponent));	// pass in unit class
            gDLL->getPythonIFace()->callFunction(PYSpellModule, "postCombatWon", argsList.makeFunctionArgs()); //, &lResult
            delete pyCaster; // python fxn must not hold on to this pointer
            delete pyOpponent; // python fxn must not hold on to this pointer
        }
        if (!isEmpty(GC.getUnitInfo(pLoser->getUnitType()).getPyPostCombatLost()))
        {
            CyUnit* pyCaster = new CyUnit(pLoser);
            CyUnit* pyOpponent = new CyUnit(this);
            CyArgsList argsList;
            argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCaster));	// pass in unit class
            argsList.add(gDLL->getPythonIFace()->makePythonObject(pyOpponent));	// pass in unit class
            gDLL->getPythonIFace()->callFunction(PYSpellModule, "postCombatLost", argsList.makeFunctionArgs()); //, &lResult
            delete pyCaster; // python fxn must not hold on to this pointer
            delete pyOpponent; // python fxn must not hold on to this pointer
        }
        if (m_pUnitInfo->getUnitConvertFromCombat() != NO_UNIT)
        {
            if (GC.getGameINLINE().getSorenRandNum(100, "Convert Unit from Combat") <= m_pUnitInfo->getUnitConvertFromCombatChance())
            {
                pUnit = GET_PLAYER(getOwnerINLINE()).initUnit((UnitTypes)m_pUnitInfo->getUnitConvertFromCombat(), getX_INLINE(), getY_INLINE(), AI_getUnitAIType());
                pUnit->convert(this);
            }
        }
    }


    Although I've cut it down to just the part dealing with the command promotion from unit capturing plus a little extra:

    Spoiler :
    Code:
    void CvUnit::combatWon(CvUnit* pLoser, bool bAttacking)
    {
        bool bConvert = false;
        int iUnit = NO_UNIT;
        CvUnit* pUnit;
    	int idefaultcapturechance = GC.getDefineINT("DEFAULT_CAPTURE_CHANCE");
    
    	if (pLoser->getDomainType() != DOMAIN_IMMOBILE && pLoser->baseMoves() > 0)
    	{
    		if (GC.getGameINLINE().getSorenRandNum(100, "Combat Capture") <= idefaultcapturechance)
    		{
    			iUnit = pLoser->getUnitType();
    			bConvert = true;
    		}
    		else
    		{
    			for (int iI = 0; iI < GC.getNumPromotionInfos(); iI++)
    			{
    				if (isHasPromotion((PromotionTypes)iI))
    				{
    					if (GC.getPromotionInfo((PromotionTypes)iI).getCombatCapturePercent() != 0)
    					{
    						if (iUnit == NO_UNIT)
    						{
    							if (GC.getGameINLINE().getSorenRandNum(100, "Combat Capture") <= GC.getPromotionInfo((PromotionTypes)iI).getCombatCapturePercent())
    							{
    								iUnit = pLoser->getUnitType();
    								bConvert = true;
    							}
    						}
    					}
    				}
    			}
    		}
    	}
        if (iUnit != NO_UNIT)
    	{
    		pUnit = GET_PLAYER(getOwnerINLINE()).initUnit((UnitTypes)iUnit, plot()->getX_INLINE(), plot()->getY_INLINE());
    		if (bConvert)
    		{
    			pLoser->setDamage(75, NO_PLAYER, false);
    			pUnit->convert(pLoser);
    		}
    	}
    }

    Now I don't really understand how the loop that reads the winning units promotions to look for capture promotions works. Usually I see something like "pWhateverUnit->isSomething" when it's checks a unit for something, but it just has "isHasPromotion" which by the way to code works refers to the winning unit, which apparently it does without having to mention that unit. So in what situation do I do that? I plan to erase some promotions (with a specific bool) from pLoser when it's captures and give pLoser the promotions with that bool that the winning unit has. It seems like something not difficult to do, but but I'm not sure how to how the two loops should look. One would be the same as the loop looking for promotions on the winning unit, but I need one to look over the promotions on the pLoser unit. So how does that work differently?
     
  14. xienwolf

    xienwolf Deity

    Joined:
    Oct 4, 2007
    Messages:
    10,589
    Location:
    Location! Location!
    If there is no -> that means it refers to the file you are currently in. So THIS unit is the one who won the combat, and we are checking him for having a promotion, there is essentially an implied pWinner-> slipped in there.
     
  15. OzzyKP

    OzzyKP Emperor

    Joined:
    Dec 16, 2000
    Messages:
    1,729
    Location:
    Washington, DC USA
    In .34 new features/options were added to randomly place towers, dungeons, lairs, etc on the map. Has anything similar been done with goody huts yet?
     
  16. Minor Annoyance

    Minor Annoyance Deity

    Joined:
    Jun 27, 2007
    Messages:
    2,247
    Gender:
    Male
    Location:
    Hamilton, Ontario
    Code:
    BeginMap
    ...
    	Randomize Goody Huts=true
    EndMap
     
  17. OzzyKP

    OzzyKP Emperor

    Joined:
    Dec 16, 2000
    Messages:
    1,729
    Location:
    Washington, DC USA
  18. OzzyKP

    OzzyKP Emperor

    Joined:
    Dec 16, 2000
    Messages:
    1,729
    Location:
    Washington, DC USA
    Hmm, I tried it and got a crash. I removed that line and the mod worked fine...

    Edit: Ok, tried it again and it works fine. I don't know what the issue was. But it works GREAT! Yay! Huts!
     
  19. MagisterCultuum

    MagisterCultuum Great Sage

    Joined:
    Feb 14, 2007
    Messages:
    16,352
    Location:
    Kael's head
    How would I go about removing "You've grown too powerful for us" from the list of reasons that thr AI would refuse to enter a permanent alliance, defensive pact, etc.
     
  20. snarko

    snarko DLLer

    Joined:
    Dec 9, 2003
    Messages:
    1,512
    Location:
    Sweden
    DenialTypes CvTeamAI::AI_permanentAllianceTrade(TeamTypes eTeam) const

    Remove
    Spoiler Code :
    Code:
    	if ((getPower(true) + GET_TEAM(eTeam).getPower(true)) > (GC.getGameINLINE().countTotalCivPower() / 2))
    	{
    		if (getPower(true) > GET_TEAM(eTeam).getPower(true))
    		{
    			return DENIAL_POWER_US;
    		}
    		else
    		{
    			return DENIAL_POWER_YOU;
    		}
    	}



    I don't see any such denial for defensive pacts. Perhaps you meant vassals?

    DenialTypes CvTeamAI::AI_surrenderTrade(TeamTypes eTeam, int iPowerMultiplier) const

    Remove
    Spoiler Code :
    Code:
    			if (GC.getGameINLINE().getAdjustedPopulationPercent((VictoryTypes)i) > 0 || GC.getGameINLINE().getAdjustedLandPercent((VictoryTypes)i) > 0)
    			{
    				if (bLandThreat && bPopulationThreat)
    				{
    					return DENIAL_POWER_YOU;
    				}
    			}
     

Share This Page