Modders Guide to FfH2

But lets say I make my shapeshifter a recon and make a promotion three for him.

How can I make it such that normal promotions available to recon troops is not available for my shapeshifter? Ie normal promotions which are lost when shapeing form (using permanent summonging and caster killed methode)

It's pretty simple to do it that way. You would create a new UnitCombat for just the shapeshifter, then you would create new promotions which are for him alone, and each Promotion would be available only to the Shapeshifter Unitcombat.


I think ideally you would create new units for each of his animal forms as well which share this unitcombat. Thus he can still gain his unique promotions while in Animal Form.


If you are just going ot have him summon an Animal, with the Promotions passing on to the summon, and the spell killing the caster, then you will be setting yourself back to 0 XP each time. Which could be used to rack up a TON of promotions really quickly since you would get a new one at 2XP each time.
 
iTier was intended to be a function so that we can block on the Tier of a unti (warrior being t1, axeman t2, champion t3 and immortal t4). That way we could set a tier requirement on promotions, spells, ability to travel through certain areas, etc. To this point it hasn't been used and it may be taken out.

I updated the first post with all the attributes in 0.32 thus far, and added definitions for a lot of them so it should be more up to date. But there is still work left to do.
 
Contemplating going through the PromotionInfos & UnitInfos files and making all the fields in UnitInfos available through PromotionInfos. As well as altering the Schema for each to make cut down on overall filesize by making only the absolutely required fields actually required.


Any tidbits of advice are more than welcome :) I am sure I shall overlook many things in the process of doing this. Especially if anyone has pointers on how to integrate the more complicated fields into the DLL, or which parts of the Schema are really and truly "required" fields. (main motivation is just familiarity with the areas that all of these lie in the DLL so that I can possibly sort out the bugs with Modular Loading).
 
Contemplating going through the PromotionInfos & UnitInfos files and making all the fields in UnitInfos available through PromotionInfos. As well as altering the Schema for each to make cut down on overall filesize by making only the absolutely required fields actually required.

Good. This includes making build orders available from promotions, right? And the python calls, and bAlways hostile, bcanMoveImpassible, PromotionSummonPerk, TerrainImpassables, FeatureImpassables, etc? A few things from promotions could be good in the units file too, like PyPerTurn. While you're at it, why not add unitReligion prereqs and minimum level prereqs to promotions? (And maybe a <iSpellExtraRange>, or just merging with broader alignments?)

Smaller files would be good too. In addition to making many tags be unneeded for most defines, you can switch from spaces to tabs to reduce the size (since a space and tab are of equal size, but you use at least twice as many spaces)
 
Contemplating going through the PromotionInfos & UnitInfos files and making all the fields in UnitInfos available through PromotionInfos.

That'd be great.
It would have made my questions about Alazkan's Mirror unnecessary, for one thing. :)
 
What do these tags do and what do you put in them?
Code:
            <UnitClassTargets>
            </UnitClassTargets>
            <UnitCombatTargets>
            </UnitCombatTargets>
            <UnitClassDefenders>
            </UnitClassDefenders>
            <UnitCombatDefenders>
            </UnitCombatDefenders>
 
They make the unit target or defend against certain other unitcombats without checking the normal "best defender" criteria. Even in Vanilla very few units use them.

For instance:
Code:
			<UnitCombatTargets>
				<UnitCombatTarget>
					<UnitCombatTargetType>UNITCOMBAT_MOUNTED</UnitCombatTargetType>
					<bUnitCombatTarget>1</bUnitCombatTarget>
				</UnitCombatTarget>
			</UnitCombatTargets>

Means that it will target Mounted Units before any other Units in the stack.


I had used it on one of my summons once for a UnitCombat_Recon defender as the perfect anti-Assassin (since all assassin types are recon) that was still unable to guard againstMarksmen (the archers).


Another fun thing you can do is for a Force 2 spell you can allow 10 different spells, one per unitcombat, which summon an unkillable permanent unit which will always defend against that unit class. Add a Python check to ensure that only 1 of each of the 10 types can exist on a tile at a time. Then you can make your city/stack immune to Melee, but Archers, recon, mounted, etc can walk right up and smack you... have to choose the right one to block :)

Alternative to 10 spells to summon different units, you move this ability to Promotions and summon a single mob which can "crew change" to block any 1 of the 10 Unitcombats
 
Thanks :D I was hoping that it would let the unit only target/defend against that unit combat.

I have another question regarding nif files. In nifskope all I did was translate the queen of the line 550 up and then save it as airship.nif and made a new artdefine using the new nif instead of the queen of the line and the unit using it shows up as a red blob. What am I missing that I should have done? :confused:
 
As I recall, the easy rule of thumb was: If there are multiple files in the same folder for Artwork (NIF & KF/KFM), you keep all those files together, no matter how few of them you change.

So make a copy of the entire folder for the Queen, change your one file, and leave the rest there in the new folder anyway.

Also, I have had NO luck with making a unit change the z-axis In Game by modification of the NIF. I haven't a clue how it is done (if you look at the NIF of the Hawk, he is on the axis, same as any land unit's feet). I generally cheat and place something on the axis, then lift everything else way up into the air.
 
I did that. Hmmm, I think I should actually read a nifskope tutorial instead of diving in head first. I like your way of making things fly I'll do that if I get the nif to work. Thanks!
 
So, since we have the PostPythonCombat thingie working for the Pyre Zombies to Explode after Combat... is <bExplodeinCombat> even used now?

EDIT: Nevermind, I checked in the SDK and it looks like it is a purely Graphical effect. I thought it was supposed to do the damage as well.
 
The War Machine is the only unit to use the incorrectly named <bAutoRaze> function right now, and I remember someone complained that it doesn't give :gold:. Well, it also doesn't Pillage (degrade 1 level), but rather destroys (removes entirely). I changed it to work just like Pillaging without costing :move:, code is below:

Spoiler :
Code:
    if (m_pUnitInfo->isAutoRaze())
    {
        if (pPlot->isOwned())
        {
            if (pPlot->getImprovementType() != NO_IMPROVEMENT)
            {
                if (!GC.getImprovementInfo((ImprovementTypes)pPlot->getImprovementType()).isPermanent())
                {
                    if (atWar(getTeam(), GET_PLAYER(pPlot->getOwner()).getTeam()))
                    {
//AutoPillage Add Begin                        pPlot->setImprovementType(NO_IMPROVEMENT);
                        CvWString szBuffer;
                        int iPillageGold;
                        long lPillageGold;
                        lPillageGold = 0;

                        CyPlot* pyPlot = new CyPlot(pPlot);
                        CyUnit* pyUnit = new CyUnit(this);

                        CyArgsList argsList;
                        argsList.add(gDLL->getPythonIFace()->makePythonObject(pyPlot));	// pass in plot class
                        argsList.add(gDLL->getPythonIFace()->makePythonObject(pyUnit));	// pass in unit class

                        gDLL->getPythonIFace()->callFunction(PYGameModule, "doPillageGold", argsList.makeFunctionArgs(),&lPillageGold);

                        delete pyPlot;	// python fxn must not hold on to this pointer
                        delete pyUnit;	// python fxn must not hold on to this pointer

                        iPillageGold = (int)lPillageGold;

                        if (iPillageGold > 0)
                        {
                            iPillageGold += (iPillageGold * GET_PLAYER(getOwnerINLINE()).getPillagingGold()) / 100;

                            GET_PLAYER(getOwnerINLINE()).changeGold(iPillageGold);

                            szBuffer = gDLL->getText("TXT_KEY_MISC_PLUNDERED_GOLD_FROM_IMP", iPillageGold, GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide());
                            gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGE", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());

                            if (pPlot->isOwned())
                            {
                                szBuffer = gDLL->getText("TXT_KEY_MISC_IMP_DESTROYED", GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide(), getNameKey(), getVisualCivAdjective(pPlot->getTeam()));
                                gDLL->getInterfaceIFace()->addMessage(pPlot->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
                            }
                            gDLL->getEventReporterIFace()->unitPillage(this, pPlot->getImprovementType(), NO_ROUTE, getOwnerINLINE());
                            pPlot->setImprovementType((ImprovementTypes)(GC.getImprovementInfo(pPlot->getImprovementType()).getImprovementPillage()));
                        }//AutoPillage Add End

If you don't recognize it I just heisted most of the code from the Pillage function, so no great feat of accomplishment on my own here, just saves some legwork. I left it so that Roads are not touched though since that would actually be a BAD thing for the player if they have Commando on any troops.

Written this way I think it would be the perfect promotion to grant with Raiders Trait instead of Commando :)


Also, Kael you can feel free to snag any of the stuff I do with WorkerMod should you decide you want to move a field into PromotionInfos as well. Some of them are proving quite fun so far :)
 
The War Machine is the only unit to use the incorrectly named <bAutoRaze> function right now, and I remember someone complained that it doesn't give :gold:. Well, it also doesn't Pillage (degrade 1 level), but rather destroys (removes entirely). I changed it to work just like Pillaging without costing :move:, code is below:

Spoiler :
Code:
    if (m_pUnitInfo->isAutoRaze())
    {
        if (pPlot->isOwned())
        {
            if (pPlot->getImprovementType() != NO_IMPROVEMENT)
            {
                if (!GC.getImprovementInfo((ImprovementTypes)pPlot->getImprovementType()).isPermanent())
                {
                    if (atWar(getTeam(), GET_PLAYER(pPlot->getOwner()).getTeam()))
                    {
//AutoPillage Add Begin                        pPlot->setImprovementType(NO_IMPROVEMENT);
                        CvWString szBuffer;
                        int iPillageGold;
                        long lPillageGold;
                        lPillageGold = 0;

                        CyPlot* pyPlot = new CyPlot(pPlot);
                        CyUnit* pyUnit = new CyUnit(this);

                        CyArgsList argsList;
                        argsList.add(gDLL->getPythonIFace()->makePythonObject(pyPlot));	// pass in plot class
                        argsList.add(gDLL->getPythonIFace()->makePythonObject(pyUnit));	// pass in unit class

                        gDLL->getPythonIFace()->callFunction(PYGameModule, "doPillageGold", argsList.makeFunctionArgs(),&lPillageGold);

                        delete pyPlot;	// python fxn must not hold on to this pointer
                        delete pyUnit;	// python fxn must not hold on to this pointer

                        iPillageGold = (int)lPillageGold;

                        if (iPillageGold > 0)
                        {
                            iPillageGold += (iPillageGold * GET_PLAYER(getOwnerINLINE()).getPillagingGold()) / 100;

                            GET_PLAYER(getOwnerINLINE()).changeGold(iPillageGold);

                            szBuffer = gDLL->getText("TXT_KEY_MISC_PLUNDERED_GOLD_FROM_IMP", iPillageGold, GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide());
                            gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGE", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());

                            if (pPlot->isOwned())
                            {
                                szBuffer = gDLL->getText("TXT_KEY_MISC_IMP_DESTROYED", GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide(), getNameKey(), getVisualCivAdjective(pPlot->getTeam()));
                                gDLL->getInterfaceIFace()->addMessage(pPlot->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
                            }
                            gDLL->getEventReporterIFace()->unitPillage(this, pPlot->getImprovementType(), NO_ROUTE, getOwnerINLINE());
                            pPlot->setImprovementType((ImprovementTypes)(GC.getImprovementInfo(pPlot->getImprovementType()).getImprovementPillage()));
                        }//AutoPillage Add End

If you don't recognize it I just heisted most of the code from the Pillage function, so no great feat of accomplishment on my own here, just saves some legwork. I left it so that Roads are not touched though since that would actually be a BAD thing for the player if they have Commando on any troops.

Written this way I think it would be the perfect promotion to grant with Raiders Trait instead of Commando :)


Also, Kael you can feel free to snag any of the stuff I do with WorkerMod should you decide you want to move a field into PromotionInfos as well. Some of them are proving quite fun so far :)

You know Im not shy about stealing. ;)
 
Vehem pointed out that shortly after my edited code above the old RazeOnMove code was set to remove Features as well. Doesn't quite fit the flavor of the Pillage on Move and is potentially overpowered against the elves. So could be worth removing that line of the code.
 
I wanted to have the Workrate of Workers & Slaves display on Mouseover of the unit, so I added to CvGameTextMgr.cpp:

Code:
			if (pUnit->workRate(1) != 0)
			{
				szString.append(NEWLINE);
				szString.append(gDLL->getText("TXT_KEY_PROMOTION_WORK_RATE", pUnit->workRate(1)));
			}

But this winds up displaying "+100 Work Rate" with the Gametext set as "[ICON BULLET]%D1 Work Rate"

How can I get rid of that Plus Sign?

Also, this only shows up when you do a Mouseover of the unit's picture in the lower left corner. I'd like it to show up when you do a mouseover of the unit itself on the map... I am guessing I have to do that in another file somewhere? (Warrior's +25% City Attack shows up like that, but not many other things that I have noticed do...)

EDIT: Came across this post quite some time later. I figured out the answers a long time ago. the + sign is added because I used %D1 instead of %d1. And to place the Workrate in the mouseover information I had to move it outside of the if (!bShort) statement in the SDK.
 
The whole "Immortal Units cause City to Evacuate" thing does confuse me quite a bit, since it ought not to happen from what I have seen so far in the code. But I had a question about the Immortal Defender bit in the code:

Code:
            if (pDefender->isFleeImmortal())
            {
                pDefender->joinGroup(NULL);
                pDefender->setFleeImmortal(false);
                pDefender->doImmortalRebirth();
                changeMoves(std::max(GC.getMOVE_DENOMINATOR(), pPlot->movementCost(this, plot())));
                getGroup()->groupMove(pPlot, true, ((canAdvance(pPlot, 0)) ? this : NULL));
                getGroup()->clearMissionQueue();
            }

The last 2 lines confuse me a bit. At first I read it as still analyzing the defender, so it made no sense for it to be calling the Group, but then I realized that it must be calling the Attackers group in actuality, since it doesn't mention the defender in that line and this section of the code is all about the Attacker.

Does this bit of code, specifically the canAdvance check, have any chance of forcing the defender units out of the stack? It is also called in updating Combat if the Defender == NULL, or if there are no Visible Units in a Tile. And looking at it now it is called on standard Defensive Withdrawal as well...


It seems to me like this command is informing the entire attacking group that it is allowed to move into that square, rather than doing a check to see if they are allowed to move there. It should possibly be nested inside of an IF statement which checks the target plot for Visible Defenders, after having moved the Withdrawing unit out (which it already is after).


Another question to pile in with this: How does the Promotion affect Global Counter? I can't find if it is done in the DLL or in Python. I am trying to affect Military Support Cost by Promotion right now actually, but so far I can only see that it is increased on Building a Unit, and Decreased on Destroying the unit. I can't figure out how to increase/Decrease it on the gain/loss of a promotion.

EDIT: Found where Promotions are gained in the DLL. Not where you do the Global Counter at, but since the only way I can think of to get that promotion is through buildings which grant it on Unit Creation maybe it isn't an issue. :dunno:
 
if this has been asked and answered, then I apologize, I scaned through the 16 pages and didn't see it, but admittedly I mighta missed it...

but what does

UnitArtStyleType-

do in promotions?...I had been thinking it was for diseased units but it didn't list anything for diseased so I got curious.
 
Back
Top Bottom