[UNIT] PPQ unit art and questions thread

Maybe a stupid question, but can't it be just some settings? :confused:
Every other animation works, so that is unlikely.

This said, I am going to try using KMOD as my base for future unit testing. If that works for me than that solves the issue.
 
The intercept works with the vanilla game, and warlords.
Vanilla_Intercept.jpg

Warlords_Intercept.jpg

BTS_Intercept.jpg


I remember it worked with BTS without any patches since thats what I played until relatively recently. I only started playing with a patched game and mods in the last two years.
The mobile sam was a poor choice as an example since it doesn't have anit-air animations. It just uses a ranged strike for everything.
 
The mobile sam was a poor choice as an example since it doesn't have anit-air animations. It just uses a ranged strike for everything.
Aha, that's good to know. I get the same results as you guys then; K-Mod doesn't make a difference.

Warlords and BtS 3.03 trigger the animation in CvUnit::interceptTest. v3.13 does it in updateAirCombat (not in CvUnit::updateAirStrike as I had first thought). The code fragment that sets up the CvAirMissionDefinition seems to be the same and v3.13 does play an animation, just not the right one. Since the DLL doesn't choose between a proper anti-air animation and a range-strike fallback, it seems likelier to me that the handling of missions in the EXE was changed around the same time and that something went wrong there. Still, restoring the 3.03 logic in the DLL might be worth a try ...
 
...
Nope, plugging in the WL/3.03 code
Spoiler :
Code:
void CvUnit::fightInterceptor(const CvPlot *pPlot, bool bQuick)
{
   FAssert(getCombatTimer() == 0);
   /*setAttackPlot(pPlot, true);
   updateAirCombat(bQuick);*/ // BtS 3.19
   // From CvUnit::interceptTest in WL ...
   CvUnit* pInterceptor = bestInterceptor(pPlot);
   gDLL->getEntityIFace()->RemoveUnitFromBattle(pInterceptor);
   CvAirMissionDefinition kAirMission;
   kAirMission.setMissionType(MISSION_AIRSTRIKE);
   kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
   kAirMission.setUnit(BATTLE_UNIT_DEFENDER, pInterceptor);
   // (Skip damage calculation)
   kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 80);
   kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
   kAirMission.setPlot(&kPlot);
   kAirMission.setMissionTime(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime() * gDLL->getSecsPerTurn());
   gDLL->getEntityIFace()->AddMission(&kAirMission);
   changeDamage(80, pInterceptor->getOwner());
   pInterceptor->changeDamage(0, getOwner());
   changeMoves(GC.getMOVE_DENOMINATOR());
   pInterceptor->setMadeInterception(true);
}
still shows the wrong animation.

I guess, in order to swap the animations (so that the EXE will pick the right one), one would have to change the .kfm file at runtime, and then get the game to reload it. That doesn't sound like a workable approach.

There's CvDLLEntity:: PlayAnimation(AnimationTypes, ...), but a comment in CvEnums.h says that
"AnimationTypes is depreciated [sic], and will be eventually removed.
BONUSANIMATION_* and IMPROVEMENTANIMATION_* are still used, and will be left.
"
Not reassuring; I've no clue how one would obtain the proper animation id.
 
Another observation is the mobile sam, and the Sam infantry both play the hurta animation (1023). Which is a melee animation and shouldn't be played at all.
So, changing the KFM to ranged attack (1032) rather than antiair(1042) wouldn't work. Since that's how the mobile Sam works anyway.
Probably not helpful but eh. :dunno:
 
Print them all out and see
...
Nope, plugging in the WL/3.03 code
Spoiler :
Code:
void CvUnit::fightInterceptor(const CvPlot *pPlot, bool bQuick)
{
   FAssert(getCombatTimer() == 0);
   /*setAttackPlot(pPlot, true);
   updateAirCombat(bQuick);*/ // BtS 3.19
   // From CvUnit::interceptTest in WL ...
   CvUnit* pInterceptor = bestInterceptor(pPlot);
   gDLL->getEntityIFace()->RemoveUnitFromBattle(pInterceptor);
   CvAirMissionDefinition kAirMission;
   kAirMission.setMissionType(MISSION_AIRSTRIKE);
   kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
   kAirMission.setUnit(BATTLE_UNIT_DEFENDER, pInterceptor);
   // (Skip damage calculation)
   kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 80);
   kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
   kAirMission.setPlot(&kPlot);
   kAirMission.setMissionTime(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime() * gDLL->getSecsPerTurn());
   gDLL->getEntityIFace()->AddMission(&kAirMission);
   changeDamage(80, pInterceptor->getOwner());
   pInterceptor->changeDamage(0, getOwner());
   changeMoves(GC.getMOVE_DENOMINATOR());
   pInterceptor->setMadeInterception(true);
}
still shows the wrong animation.

I guess, in order to swap the animations (so that the EXE will pick the right one), one would have to change the .kfm file at runtime, and then get the game to reload it. That doesn't sound like a workable approach.

There's CvDLLEntity:: PlayAnimation(AnimationTypes, ...), but a comment in CvEnums.h says that
"AnimationTypes is depreciated [sic], and will be eventually removed.
BONUSANIMATION_* and IMPROVEMENTANIMATION_* are still used, and will be left.
"
Not reassuring; I've no clue how one would obtain the proper animation id.
Does the DLL support some form of manual log printing?

Also from my professional experience I can tell you that "will be eventually removed." means that it usually still ain't. So it's worth a try. I'd do it my self but I just can't spare the time these days to set up for DLL compilation.
 
Last edited:
Does the DLL support some form of manual log printing?
Well, yes ...
I'd do it my self but I just can't spare the time these days to set up for DLL compilation.
So, to whom it may concern, logging from the DLL:
Spoiler :
In CvDLLUtilityIFaceBase.h (global instance: gDLL) there are
Code:
void logMsg(const TCHAR* pLogFileName, const TCHAR* pBuf, bool bWriteToConsole=false, bool bTimeStamp=true);
void messageControlLog(char* s);
The former requires LoggingEnabled=1 in CivilizationIV.ini, the latter writes to MPLog.txt and requires MessageLog=1.
Print them all out and see
But what to iterate over? There are animation paths in XML, but those don't reference a particular animation. I really don't understand how this stuff works once the EXE has determined the animation path and category.
Spoiler XML for animations :
Code:
<AnimationPath>
   <Type>ANIMATIONPATH_AIRSTRIKE</Type>
   <bMissionPath>1</bMissionPath>
   <PathEntry>
       <Category>ANIMCAT_AIRSTRIKE</Category>
       <Operator/>
       <Parameter/>
   </PathEntry>
</AnimationPath>
<AnimationCategory>
   <Type>ANIMCAT_AIRSTRIKE</Type>
   <DefaultTo>ANIMCAT_RANGED_STRIKE</DefaultTo>
   <BaseID>42</BaseID>
</AnimationCategory>
<AnimationCategory>
   <Type>ANIMCAT_RANGED_STRIKE</Type>
   <DefaultTo>ANIMCAT_MELEE_STRIKE</DefaultTo>
   <BaseID>32</BaseID>
</AnimationCategory>
<AnimationCategory>
   <Type>ANIMCAT_MELEE_STRIKE</Type>
   <DefaultTo>ANIMCAT_RANGED_STRIKE</DefaultTo>
   <BaseID>22</BaseID>
</AnimationCategory>
Maybe the AnimationTypes aren't even related to units (anymore, if ever); at any rate, the enumerators are no help:
Spoiler :
Code:
// AnimationTypes is depreciated, and will be eventually removed.
// BONUSANIMATION_* and IMPROVEMENTANIMATION_* are still used, and will be left.
enum AnimationTypes
{
   NONE_ANIMATION = -1,   // NO_ANIMATION is used by FirePlace

   BONUSANIMATION_UNIMPROVED = 1,
   BONUSANIMATION_NOT_WORKED,
   BONUSANIMATION_WORKED,

   IMPROVEMENTANIMATION_OFF = 2,
   IMPROVEMENTANIMATION_ON,
   IMPROVEMENTANIMATION_OFF_EXTRA,
   IMPROVEMENTANIMATION_ON_EXTRA_1,
   IMPROVEMENTANIMATION_ON_EXTRA_2,
   IMPROVEMENTANIMATION_ON_EXTRA_3,
   IMPROVEMENTANIMATION_ON_EXTRA_4,
};
:think: What one could try is call PlayAnimation for all ids from 10 (the first 9 being assigned to resources and improvements) up to ... some number and see what the unit model does. For the ANIMCAT_AIRSTRIKE BaseID of 42, the SAM Infantry just freezes for a few seconds. If I try to play them in a loop
Code:
for (int i = 10; i < 50; i++)
    gDLL->getEntityIFace()->PlayAnimation(pInterceptor->getEntity(), (AnimationTypes)i);
I get the same result. So it seems like each call cancels the previous call ... meaning that testing the IDs this way is pretty tedious.
 
What does "deprecated" mean in code speak?

From what I can translate into dummy talk; they were planning to merge air animations with ranged attack for some reason? Then broke it. Then left it like that with no documentation on what they did or how to undo it?
 
What does "deprecated" mean in code speak?

From what I can translate into dummy talk; they were planning to merge air animations with ranged attack for some reason? Then broke it. Then left it like that with no documentation on what they did or how to undo it?
Deprecated is programmer talk for:
We don't like this bit for what ever reason so we want to remove it. But because there is stuff that still depends on it and we need time to trim all that out we can't just delete it without breaking a lot of code. So instead of doing that we are going to slowly start changing every single piece of code that uses that stuff to do things differently. And until that is done (if ever) we are going to mark that piece as [deprecated] so that people don't go around adding new references.

To use a real world example. When the first commercially viable mass production automobile was invented the horse became deprecated. That was in 1908. But people didn't just shoot all the horses and burn down all the stables. Instead the road from deprecated to obsolete took 50 or so years. That's how long it took for the new code to worm its way into every aspect of the simulation and slowly replace all the old. And if you visit places too remote to have gotten a patch yet you can still run into them.

That is why I suspect that the method still does something and is used somewhere.

--------------------------
As for testing, can't you just add a time delay and loop through the animations that way?
 
Last edited:
As for testing, can't you just add a time delay and loop through the animations that way?
I've tried playing a new animation at every 16th game update – that should be every 2 seconds. Still, up to id 100, there is no effect on the unit. I've tried 1042, which is the number shown on the Ctrl+U screen (which appears to be part of the EXE); also no effect. I'm not going to investigate the PlayAnimation function further in this manner. I don't think it has the right parameters to be intended for unit animations:
Code:
AnimationTypes eAnim, float fSpeed = 1.0f, bool bQueue = false,
int iLayer = 0, float fStartPct = 0.0f, float fEndPct = 1.0f
It gets called on a CvUnitEntity instance. Each unit has only one of those, but an animation should affect only one of the three 3D models in a unit. I don't think iLayer is for selecting the model. Oh, but bQueue=true probably would've made my life easier when trying to call the function in a loop.
From what I can translate into dummy talk; they were planning to merge air animations with ranged attack for some reason? Then broke it. Then left it like that with no documentation on what they did or how to undo it?
The 3.13 release notes do contain a few bullets about air units:
• iAirCombatLimit works with ranged combat as well
• Fixed stack air combat
• Trying to airstrike one of your own cities does not result in a rebase
Doesn't really sound like it would require the code to be restructured; well, the stack combat thing perhaps. I would assume that Firaxis hadn't noticed that they broke the SAM Infantry. That really seems to be the only Vanilla/BtS unit with special anti-air animations.
Another observation is the mobile sam, and the Sam infantry both play the hurta animation (1023). Which is a melee animation and shouldn't be played at all.
Mobile SAM only has that one hurt animation; so I don't suppose that's an error?
Spoiler MobileSam directory listing :
Code:
MobileSam.kfm
MobileSam.nif
MobileSAM_128_damage_all.dds
MobileSAM_128_Gloss.dds
MobileSAM_256.dds
MobileSAM_freeze0000.nif
MobileSAM_freeze0001.nif
MobileSam_freeze1000.nif
MobileSam_freeze1031.nif
MobileSam_FX.nif
MobileSam_MD_Damage_BodyGeometry_DamBody1.kf
MobileSam_MD_Damage_BodyGeometry_DamBody2.kf
MobileSam_MD_Damage_BodyGeometry_DamBody3.kf
MobileSam_MD_Damage_BodyGeometry_DamBody4.kf
MobileSam_MD_Damage_TankTexture_DamTexture1.kf
MobileSam_MD_Damage_TankTexture_DamTexture2.kf
MobileSam_MD_Damage_TankTexture_DamTexture3.kf
MobileSam_MD_Damage_TankTexture_DamTexture4.kf
MobileSam_MD_Heal.kf
MobileSam_MD_HurtA.kf
MobileSam_MD_Idle.kf
MobileSam_MD_RangedDie.kf
MobileSam_MD_RangedDie_Fade.kf
MobileSam_MD_RangedFortify.kf
MobileSam_MD_RangedStrike.kf
MobileSam_MD_Ranged_Idle.kf
MobileSam_MD_Run.kf
MobileSAM_MD_XFadeIn.kf
MobileSAM_MD_XFadeOut.kf
 
Mobile SAM only has that one hurt animation; so I don't suppose that's an error?
This is more anecdotal observation. Code stuff is more like magic spells for me. But ranged units don't use the hurt animation during combat (rifleman). This is because they would have to switch to a melee/idle pose from a ranged pose. Bombardment does trigger it. Anyway, that was my line of thought. It has since occurred to me that is wrong. Mech units do use the hurta in combat. However, in this case the mobile sam should use the ranged attack animation for interception.

For what it is worth I did a test yesterday. I switched all the air combat event numbers for ranged attack for the jetfighter. There is no equivalent for the patrol. All it does is play a sound. I tested it against the mobile sam. Every thing worked identically as before. No effect.
 
If intercept is ranged attack shouldn't patrol be ranged fortify?
 
This is more anecdotal observation. Code stuff is more like magic spells for me. But ranged units don't use the hurt animation during combat (rifleman). This is because they would have to switch to a melee/idle pose from a ranged pose. Bombardment does trigger it. Anyway, that was my line of thought. It has since occurred to me that is wrong. Mech units do use the hurta in combat. However, in this case the mobile sam should use the ranged attack animation for interception.

For what it is worth I did a test yesterday. I switched all the air combat event numbers for ranged attack for the jetfighter. There is no equivalent for the patrol. All it does is play a sound. I tested it against the mobile sam. Every thing worked identically as before. No effect.
From what I can see, range units have a hurt animation and use them, but only in melee combat. For pure range units, like the rifleman and the mech units, the hurt animation is made for their ranged stance. The ancient and medieval range units change into a melee combat stance and have their hurt animation for that stance. If you have a combat where both units are pure range, no hurt animation will be triggered.
 
https://i.imgur.com/qkGi3C1.mp4

I recorded an extremely low quality mp4 of the jetfighter attacking the mobile sam. You see on the last attack it finally is intercepted. The jet plays the hurta animation like it should. The mobile sam also plays its hurta animation. And not its ranged attack like it should.

Here is the xml for the KFM I tested:
Code:
<KFM>
    <Header>;Gamebryo KFM File Version 2.0.0.0b
</Header>
    <NIF>JetFighter.nif</NIF>
    <Master>MD</Master>
    <Unknown></Unknown>
    <Animations>
        <Animation>
            <Event>1000</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Idle.kf</File>
        </Animation>
        <Animation>
            <Event>1020</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_FadeIn_Intercept.kf</File>
        </Animation>
        <Animation>
            <Event>1010</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Run.kf</File>
        </Animation>
        <Animation>
            <Event>1500</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Damage_Plane_Texture_DamTexture1.kf</File>
        </Animation>
        <Animation>
            <Event>2500</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Damage_Plane_Texture_DamTexture2.kf</File>
        </Animation>
        <Animation>
            <Event>3500</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Damage_Plane_Texture_DamTexture3.kf</File>
        </Animation>
        <Animation>
            <Event>4500</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Damage_Plane_Texture_DamTexture4.kf</File>
        </Animation>
        <Animation>
            <Event>1022</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Intercept.kf</File>
        </Animation>
        <Animation>
            <Event>1023</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Hurt.kf</File>
        </Animation>
        <Animation>
            <Event>1024</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_RunDie.kf</File>
        </Animation>
        <Animation>
            <Event>1510</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Damage_Smoke_DamSmoke1.kf</File>
        </Animation>
        <Animation>
            <Event>2510</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Damage_Smoke_DamSmoke2.kf</File>
        </Animation>
        <Animation>
            <Event>3510</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Damage_Smoke_DamSmoke3.kf</File>
        </Animation>
        <Animation>
            <Event>4510</Event>
            <Variation>0</Variation>
            <File>JetFighter_MD_Damage_Smoke_DamSmoke4.kf</File>
        </Animation>
    </Animations>
</KFM>
If intercept is ranged attack shouldn't patrol be ranged fortify?
That would follow... Except you see the intercept uses the melee attack. It is the bomb that is the ranged attack. It seems the "Fadein" intercept is the "fortify" (or the equivalent for planes).

All I can conclude form all this is the problems aren't caused by the event numbers. :badcomp:
 
At this point I have a suggestion. If you guys can make a diagram of the animations, what should be played and what is actually being played we can go and make new KFM files that address that and fix things that way.

Like for example, if hurta = intercept aircraft we could create a KFM where the intercept animation is tied to hurta instead.
 
The typical units in CIV use teamcolor.bmp to paint teamcolor onto a unit. Is there a way to paint both teamcolor1 and teamcolor2 onto a unit?
 
I've tried to figure something out. One interesting thing I discovered is the game doesn't actually use, or care about "TeamColor.bmp".
Teamcolor.jpg
I replaced the "TeamColor.bmp" entry with nothing and it still works. I initially tried "playercolor01.tga" since that's what is used for the secondary color.

I did manage to get the entire unit to be the secondary color with there settings:
teamcolor2.jpg
Not really useable. Unless you make a totally seperate part of the mesh with a different material that has this color.
 
I can work with that. I think.
 
These trucks are driving me insane. Like this is the best I've managed to do so far.
Civ4ScreenShot0016.JPG Civ4ScreenShot0000.JPG

What do you guys think? Any ideas or comments? I am half way made up to just give up and publish this.

And yes, there is a long version as well. This one is just easier to screenshot with multiple civs in one frame.
 
Back
Top Bottom