Modders Guide to FfH2

I'm having issues with a new artstyle for my new civilization.

I added a summonable civ (like Infernals, Mercurians) to the game. I want their art to be a mix of Infernal and Illian art + Frostling art (since my artistic talents are pretty limited. I can recolor DDS files but that's about it.)


Here is their artstyle. Note that I tried to replace the Adept art with the art for Mokka. However, when I start a game as the Frozen (well, after summoning them) my starting Adept unit appears just like a regular Adept. Everything else looks like an Infernal unit. And yes, I did make my civilization use this artstyle:

Code:
        <CivilizationInfo>
            <Type>CIVILIZATION_FROZEN</Type>
            <Description>TXT_KEY_CIV_FROZEN_DESC</Description>
            <ShortDescription>TXT_KEY_CIV_FROZEN_SHORT_DESC</ShortDescription>
            <Adjective>TXT_KEY_CIV_FROZEN_ADJECTIVE</Adjective>
            <Civilopedia>TXT_KEY_CIV_FROZEN_PEDIA</Civilopedia>
            <DefaultPlayerColor>PLAYERCOLOR_SVARTALFAR</DefaultPlayerColor>
            <ArtDefineTag>ART_DEF_CIVILIZATION_ILLIANS</ArtDefineTag>
            <ArtStyleType>ARTSTYLE_ILLIANS</ArtStyleType>
            <UnitArtStyleType>UNIT_ARTSTYLE_FROZEN</UnitArtStyleType>

Spoiler :
Code:
		<UnitArtStyleTypeInfo>
			<Type>UNIT_ARTSTYLE_FROZEN</Type>
			<StyleUnits>
				<StyleUnit>
					<UnitType>UNIT_ADEPT</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_MOKKA</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_MOKKA</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_MOKKA</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_ARCHER</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_FROSTLING_ARCHER</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_FROSTLING_ARCHER</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_FROSTLING_ARCHER</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_FROSTLING_ARCHER</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_FROSTLING_ARCHER</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_FROSTLING_ARCHER</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_FROSTLING_ARCHER</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_FROSTLING_ARCHER</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_FROSTLING_ARCHER</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_ARCHMAGE</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_ARCHMAGE_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_ARCHMAGE_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_ARCHMAGE_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_ASSASSIN</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_ASSASSIN_ILLIANS</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_ASSASSIN_ILLIANS</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_ASSASSIN_ILLIANS</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_AXEMAN</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_AXEMAN_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_AXEMAN_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_AXEMAN_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_AXEMAN_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_AXEMAN_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_AXEMAN_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_AXEMAN_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_AXEMAN_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_AXEMAN_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_CHAMPION</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_CHAMPION_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_CHAMPION_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_CHAMPION_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_CHARIOT</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_CHARIOT_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_CHARIOT_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_CHARIOT_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_CROSSBOWMAN</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_CROSSBOWMAN_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_CROSSBOWMAN_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_CROSSBOWMAN_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_HORSE_ARCHER</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_WOLF_RIDER</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_WOLF_RIDER</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_WOLF_RIDER</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_WOLF_RIDER</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_WOLF_RIDER</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_WOLF_RIDER</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_HORSEMAN</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_WOLF_RIDER</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_WOLF_RIDER</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_WOLF_RIDER</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_IMMORTAL</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_IMMORTAL_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_IMMORTAL_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_IMMORTAL_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_MAGE</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_MAGE_ILLIANS</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_MAGE_ILLIANS</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_MAGE_ILLIANS</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_MARKSMAN</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_MARKSMAN_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_MARKSMAN_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_MARKSMAN_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_PHALANX</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_PHALANX_ILLIANS</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_PHALANX_ILLIANS</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_PHALANX_ILLIANS</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_BEASTMASTER</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_BEASTMASTER_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_BEASTMASTER_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_BEASTMASTER_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_BEAR</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_BEAR</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_BEAR</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_BEAR</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_BEAR</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_PIT_BEAR</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_PIT_BEAR</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_PIT_BEAR</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_PIT_BEAR</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_RANGER</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_RANGER_ILLIANS</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_RANGER_ILLIANS</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_RANGER_ILLIANS</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_WOLF</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_WOLF</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_WOLF</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_SCOUT</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_SCOUT_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_SCOUT_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_SCOUT_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_SETTLER</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_SETTLER_MALE_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_SETTLER_MALE_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_SETTLER_MALE_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_SETTLER_FEMALE_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_SETTLER_FEMALE_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_SETTLER_FEMALE_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_SETTLER_CHILD_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_SETTLER_CHILD_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_SETTLER_CHILD_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_WARRIOR</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_FROSTLING</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_FROSTLING</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_FROSTLING</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_FROSTLING</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_FROSTLING</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_FROSTLING</MiddleArtDefineTag>
					</UnitMeshGroup>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_FROSTLING</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_FROSTLING</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_FROSTLING</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
				<StyleUnit>
					<UnitType>UNIT_WORKER</UnitType>
					<UnitMeshGroup>
						<EarlyArtDefineTag>ART_DEF_UNIT_WORKER_INFERNAL</EarlyArtDefineTag>
						<LateArtDefineTag>ART_DEF_UNIT_WORKER_INFERNAL</LateArtDefineTag>
						<MiddleArtDefineTag>ART_DEF_UNIT_WORKER_INFERNAL</MiddleArtDefineTag>
					</UnitMeshGroup>
				</StyleUnit>
			</StyleUnits>
		</UnitArtStyleTypeInfo>

What have I done wrong?
 
Are you applying Demon as a racial trait? Promotion ArtStyle overrides Civilization Artstyle.

Yeah, I am. Okay, I can fix this then by making a new racial promotion.

Also, I have another weird issue. I tried to add a spell that destroyed grain or livestock resources on a plot.

Code:
def reqCounterthaw(caster):
	pPlot = caster.plot()
	if pPlot.getBonusType() != -1:
		iBonus = pPlot.getBonusType()
		if (gc.getBonusInfo(iBonus).getBonusClassType() != gc.getInfoTypeForString('BONUSCLASS_LIVESTOCK') and gc.getBonusInfo(iBonus).getBonusClassType() != gc.getInfoTypeForString('BONUSCLASS_GRAIN')):
			return False
	if pPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_BLIZZARD') or pPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_WINTER'):
		return False
	if gc.getImprovementInfo(pPlot.getImprovementType()).isPermanent() == True:
		return False
	if pPlot.getTerrainType() == gc.getInfoTypeForString('TERRAIN_SNOW'):
		return False
	return True
	
def spellCounterthaw(caster):
	pPlot = caster.plot()
	iBonus = pPlot.getBonusType()
	if (gc.getBonusInfo(iBonus).getBonusClassType() == gc.getInfoTypeForString('BONUSCLASS_LIVESTOCK') or gc.getBonusInfo(iBonus).getBonusClassType() == gc.getInfoTypeForString('BONUSCLASS_GRAIN')):
		pPlot.setBonusType(-1)
		pPlot.setImprovementType(-1)
		pPlot.setTerrainType(gc.getInfoTypeForString('TERRAIN_SNOW'), true,true)

This seems right. But then whenever the reqCounterthaw is called, I get this error:

Code:
Traceback (most recent call last):

  File "CvSpellInterface", line 27, in canCast

  File "<string>", line 0, in ?

  File "CvSpellInterface", line 3710, in reqCounterthaw

ArgumentError: Python argument types in
    CyPlot.getBonusType(CyPlot)
did not match C++ signature:
    getBonusType(class CyPlot {lvalue}, int)
ERR: Python function canCast failed, module CvSpellInterface

Line 3710 is " if pPlot.getBonusType() != -1:".
 
You have to pass in a player number for getBonusType() to know WHO is checking for a bonus type. Use negative 1 to just find out what bonus is really there (for example, if using 0 and player 0 isn't Lanun, you would not see any bonus on a tile which actually has Pearls)
 
I'm new to modding and in the process of creating "religious affinity" style tags (for FFPlus), which would work somewhat like the normal BonusAffinities, except you get a (possibly typed) bonus for the number of Civs that follow your state religion.

I have some coding experience (informatics student), but I'm new at Civ modding. Since the tags would work rather similar to the BonusAffinities and DamageTypes, I want to check how they work to get the hang of it. I'm kind of lost, however. I found all the variables and getters and setters in CvInfos, but that's it. Main two questions I have:

- The affinities and damage types are defined in integer arrays. I am not clear on what exactly the arrays represent. I presume the indices correspond to a certain damage type/bonus, and the values represent the damage/affinity amount? If so, where do I find which numbers correspond to which types/bonuses

- When and how are the damage bonuses calculated and applied? Yep, no clue here. Sorry. But no need to go into much detail, pointing me in the right direction should suffice unless its complicated. EDIT: seem to have found where bonus affinities are applied and updated. Now to find where they are used.

Maybe I should've started modding with something easier, however I am of the opinion that these tags will be Totally Rad(tm) and thus want to implement them :)
 
For Civ, you have to look at both the XML and the DLL to get the whole picture. Arrays are defined by the loaded XML, so the integer corresponds to the order of listing in the XML. Ideally, my Idiot's Guide should prove somewhat helpful for you to find your way around inside the DLL, but it is written to assume that the modder has already played with XML and/or python modding, so doesn't cover that particular tidbit.

So in DamageTypes, we have loaded the CIV4DamageTypeInfos.xml file, which sets the size and precise order. In BonusInfos we have loaded CIV4BonusInfos.xml file, which contains a reference to a DamageType for anyone with an affinity to that bonus to set the type of extra damage they gain. In UnitInfos, we have loaded CIV4UnitInfos.xml, which can state a BonusType for an Affinity, and a value to multiply the source by to get the final net bonus.

To see precisely which files line up with wich Info type (usually the name makes it pretty obvious though), you can look in CvXMLUtilitySet.cpp, and to see the affinity actually applied to the unit, look in CvUnit.cpp
 
Thanks. Forgot to check the XML files. I hate new codebases :( Already found how the affinities are applied though, should be able to continue break... I mean modding my game.

Your Idiot's Guide is sweet btw, been skimming through it to get the hang of how things work in Civ and it's very clear and thorough. Also charming how you try to get new coders to use huge commentblocks and lots of debug commands. Futile, but charming ;)
 
Alright, stuck again. Long post to show what I already did (not much :().

The tags have a lot in common with the DamageTypeCombat tags, so I'm basically trying to follow them as much as possible (e.g. up untill the functional part). Goal is to make the tag a promotion effect as well as a base unit parameter, I've started with the latter.

In the UnitInfos XML, it looks like this:

Code:
			<DivineCombats>
				<DivineCombat>
					<DamageType>DAMAGE_HOLY</DamageType>
					<iCombatDivine>1</iCombatDivine>
				</DivineCombat>
			</DivineCombats>

Which, when all is said and done, would mean that the unit gets a +1 to its combat (both defense and offense, seperate tags hopefully coming in the future :p), for each Civ that follows the unit's owner's state religion (including the owner himself).

For completeness sake, the Civ4UnitSchema.XML:
Code:
	...
        <ElementType name="iCombatDivine" content="textOnly" dt:type="int"/>
	<ElementType name="DivineCombat" content="eltOnly">
		<element type="DamageType" />
		<element type="iCombatDivine" />
	</ElementType>
	<ElementType name="DivineCombats" content="eltOnly">
		<element type="DivineCombat" minOccurs="0"/>
	</ElementType>
	...
	<ElementType name="UnitInfo" content="eltOnly">
	...
		<element type="DamageTypeCombats" minOccurs="0"/>
		<element type="BonusAffinities" minOccurs="0"/>
		<element type="DivineCombats" minOccurs="0"/>
		<element type="AllowPromotions" minOccurs="0"/>
	...
	</ElementType>

All of this looks ok to me, and everything loads without errors (added the tag to the Monk to check that).

Now to the DLL. I was going step by step here. First, CvInfos.h (I normally use Xienwolf's Huge Commentblocks (tm), but left them out for clarity's sake).
Code:
class CvUnitInfo : public CvHotkeyInfo
{
//---------------------------------------PUBLIC INTERFACE---------------------------------
public:
	...
	int getDivineCombat(int i) const;
	...
protected: 
	...
	int* m_piDivineCombat;
	...

And in CvInfos.cpp:

Code:
CvUnitInfo::CvUnitInfo() :
...
m_piDivineCombat(NULL), 
...
CvUnitInfo::~CvUnitInfo()
{
	...
	SAFE_DELETE_ARRAY(m_piDivineCombat);
	...
}

int CvUnitInfo::getDivineCombat(int i) const
{
	return m_piDivineCombat ? m_piDivineCombat[i] : -1;
}

void CvUnitInfo::read(FDataStreamBase* stream)
{
	...
	stream->ReadString(m_iNumImages, m_paszImages);
	SAFE_DELETE_ARRAY(m_piDivineCombat);
	m_piDivineCombat = new int[GC.getNumDamageTypeInfos()];
	stream->Read(GC.getNumDamageTypeInfos(), m_piDivineCombat);
//FfH Units: Added by Kael 08/04/2007
	stream->Read(&m_bAbandon);
	...
}

void CvUnitInfo::write(FDataStreamBase* stream)
{
	...
	stream->WriteString(m_iNumImages, m_paszImages);
	stream->Write(GC.getNumDamageTypeInfos(), m_piDivineCombat);
//FfH Units: Added by Kael 08/04/2007
	stream->Write(m_bAbandon);
	...
}

bool CvUnitInfo::read(CvXMLLoadUtility* pXML)
{
	...
	pXML->SetVariableListTagPair(&m_piDivineCombat, "DivineCombats", sizeof(GC.getDamageTypeInfo((DamageTypes)0)), GC.getNumDamageTypeInfos());
	...
}

All of this compiled and loaded without any errors. I could start a new game, worldbuilder myself a monk (with tag) and quicksave and quickload.

--- Problems start here ---

Now onto CvUnit.h/cpp. In the header-file I put:
Code:
protected:
	...
	int* m_paiDivineCombat;

(As an aside, what does the "a" stand for. I presumed p was for arrays (or pointers to arrays, anyway the arrays in CvInfos all start with m_p), the i is for integer, but I'm lost on the a).

And in CvUnit.cpp:
Code:
CvUnit::CvUnit()
{
	...
	m_paiDivineCombat = NULL;
	...
}

void CvUnit::uninit()
{
	SAFE_DELETE_ARRAY(m_paiDivineCombat);
	...
}

void CvUnit::read(FDataStreamBase* pStream)
{
	...
	pStream->Read(&m_bSuppressImage);
	pStream->Read(GC.getNumDamageTypeInfos(), m_paiDivineCombat);
//FfH Spell System: Added by Kael 07/23/2007
	pStream->Read(&m_bFleeWithdrawl);
	...
}

void CvUnit::write(FDataStreamBase* pStream)
{
	...
	pStream->Write(m_bSuppressImage);
	pStream->Write(GC.getNumDamageTypeInfos(), m_paiDivineCombat);
//FfH Spell System: Added by Kael 07/23/2007
	pStream->Write(m_bFleeWithdrawl);
	...
}

That's it. So nothing functional yet, just (un)initializing and reading and writing of the array. Build, run, try to start a new game => CtD.

And that's before I started trying to figure out where in the code I should apply the combat bonus (I piggyback on DamageTypeCombat, so I guess it's in reset()) and how to update the bonus any time a civ changes its state religion...

Any and all help is greatly appreciated :)
 
Maybe you should try writing the reset() code? I'm totally not sure it could cause a CtD though. Compared with m_paiDamageTypeResist and all seem to fit it but the reset() part, so...

As for "a", I believe it's "array"; m_pai -> member_pointerArrayInteger? At least, it's what I think :)
 
As for "a", I believe it's "array"; m_pai -> member_pointerArrayInteger? At least, it's what I think :)

That's what I thought, but the fact that all the arrays in CvInfos are m_pi, when they seem to be used as arrays everywhere, confused me. But it doesn't really matter all that much.

Actually already tried writing something in reset(), but it was wonky. Going to try something easy and non-functional to see if it fixes the crash.

EDIT:

In reset(), changed this:
Code:
        m_paiDamageTypeCombat = new int[GC.getNumDamageTypeInfos()];
        m_paiDamageTypeResist = new int[GC.getNumDamageTypeInfos()];
        for (iI = 0; iI < GC.getNumDamageTypeInfos(); iI++)
        {
            int iChange = (NO_UNIT != m_eUnitType) ? m_pUnitInfo->getDamageTypeCombat(iI) : 0;
            m_paiDamageTypeCombat[iI] = iChange;
            m_paiDamageTypeResist[iI] = 0;
            m_iTotalDamageTypeCombat += iChange;
        }
to this:
Code:
	m_paiDivineCombat = new int[GC.getNumDamageTypeInfos()];
	m_paiDamageTypeCombat = new int[GC.getNumDamageTypeInfos()];
	m_paiDamageTypeResist = new int[GC.getNumDamageTypeInfos()];
	for (iI = 0; iI < GC.getNumDamageTypeInfos(); iI++)
	{
		int iChangeDT = (NO_UNIT != m_eUnitType) ? m_pUnitInfo->getDamageTypeCombat(iI) : 0;
		int iChangeDC = (NO_UNIT != m_eUnitType) ? m_pUnitInfo->getDivineCombat(iI) : 0;
		m_paiDamageTypeCombat[iI] = iChangeDT;
		m_paiDivineCombat[iI] = iChangeDC;
		m_paiDamageTypeResist[iI] = 0;
		m_iTotalDamageTypeCombat += iChangeDT;
	}

to no avail. Still CtD when starting a new game.
 
Reset() creates the array, reserving it memory space and whatnot. you MUST do something in there for the code to work. For the time being, I would say set it to 0 would be best, rather than trying to set it to an actual value.

I am curious why you return a negative 1 as your default in CvInfos when the array doesn't exist. Seems that 0 would be the value which is lowest impact on the code.

Your important line was m_paiDivineCombat = new int[GC.getNumDamageTypeInfos()];, setting initial values of 0 or unitinfos is a kind of "bonus" in addition to that memory allocation.

Typically, I advise that you write CvGameTextMgr.cpp pieces before you write CvUnit pieces, then you know for certain that your CvInfos stuff is actually working properly. I think I have to advise that here as well, as I don't see anything in your setup with ::reset() written as well which ought to crash.
 
Reset() creates the array, reserving it memory space and whatnot. you MUST do something in there for the code to work. For the time being, I would say set it to 0 would be best, rather than trying to set it to an actual value.

I am curious why you return a negative 1 as your default in CvInfos when the array doesn't exist. Seems that 0 would be the value which is lowest impact on the code.

I didn't know anything about how CvUnit works when I was busy in CvInfos, so I basically copied what getDamageTypeCombat(int i) did. -1 makes little sense indeed, and now of course I am curious why getDamageTypeCombat and getBonusAffinity default to -1 :)

Your important line was m_paiDivineCombat = new int[GC.getNumDamageTypeInfos()];, setting initial values of 0 or unitinfos is a kind of "bonus" in addition to that memory allocation.

Typically, I advise that you write CvGameTextMgr.cpp pieces before you write CvUnit pieces, then you know for certain that your CvInfos stuff is actually working properly. I think I have to advise that here as well, as I don't see anything in your setup with ::reset() written as well which ought to crash.

Sound logical. Tried it (only setBasicUnitHelp as I obviously can't refer to CvUnit parameters yet), no errors, and I fine looking pedia entry in my custom test-Monk:

pedia.jpg


Aside from a surprisingly big amount of "wheee" at seeing I finally got something in the game (even if it does nothing), this of course means I still haven't found out what's wrong. Going to try some more things.
 
I'm stumped, don't know what to try anymore. CvInfos seems to work fine, so I'm going over what I did in CvUnit again (short version this time).

1) Add array parameter int* m_paiDivineCombat to CvUnit.h
2) In the CvUnit constructor, add m_paiDivineCombat = NULL;
3) In :uninit(), add SAFE_DELETE_ARRAY(m_paiDivineCombat);
4) Initialize the array in the :reset() function, just put a 0 at each index (changed this, to no avail). The initializing happens in the if (!bConstructorCall){} block, same as for damageTypeCombat.
5) Add pStream->Read(GC.getNumDamageTypeInfos(), m_paiDivineCombat); to the :read() function and corresponding pStream->Write() command to the :write() function, making sure they have the same order.

What am I missing? :(
 
For my new civ, I wanted to make it so that both they and the Illians could "ascend", so they each gained a unit on completing Ascension. This was the code I added to that ritual in the Event Manager, instead of just spawning Auric.

Code:
		if iProjectType == gc.getInfoTypeForString('PROJECT_ASCENSION'):
			if pPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIATION_ILLIANS'):
				pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AURIC_ASCENDED'), pCity.getX(), pCity.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
			if pPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIATION_FROZEN'):
				pPlayer.initUnit(gc.getInfoTypeForString('UNIT_TARANIS_ASCENDED'), pCity.getX(), pCity.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
				for iPlot in range(CyMap().numPlots()):
					pTaranisPlot = CyMap().plotByIndex(iPlot)
					for iUnit in range(pTaranisPlot.getNumUnits()):
						pTaranis = pTaranisPlot.getUnit(iUnit)
						if pTaranis.getUnitType() == gc.getInfoTypeForString('UNIT_TARANIS'):
							pTaranis.kill(false, -1)

It should check if you are the Illians or Frozen, give you the correct unit based on that, then (if you are the Frozen) find where Taranis is on the map and kill him. (since he is spawned as Taranis Ascended).

But it doesn't work, no units are spawned for either Illians or Frozen. I am apparently using the wrong function to identify a player's civilization, it seems. How should I be doing this?
 
I'm stumped, don't know what to try anymore. CvInfos seems to work fine, so I'm going over what I did in CvUnit again (short version this time).

1) Add array parameter int* m_paiDivineCombat to CvUnit.h
2) In the CvUnit constructor, add m_paiDivineCombat = NULL;
3) In :uninit(), add SAFE_DELETE_ARRAY(m_paiDivineCombat);
4) Initialize the array in the :reset() function, just put a 0 at each index (changed this, to no avail). The initializing happens in the if (!bConstructorCall){} block, same as for damageTypeCombat.
5) Add pStream->Read(GC.getNumDamageTypeInfos(), m_paiDivineCombat); to the :read() function and corresponding pStream->Write() command to the :write() function, making sure they have the same order.

What am I missing? :(

You are missing everything that makes the function work now :) The steps you have taken are the basic requirements for an Array to exist, but now you have to link your array to the important areas of the code which actually adjust unit strength. So look for DamageTypes or whatever you were copying from and find a CvUnit::get____(int i) const type of function which uses a line like return m_piDamageTypes; and make yourself something similar. But in yours you want to use something like return m_piHolyDamageTypes + gc.getUnitInfo(getUnitType()).getHolyDamageType(i); to include the unit damage. Or go back to ::reset() and make your array look like the other one again with loading of the UnitInfo values initially.

Once you have done that, search for all places that use the ::get____(int i) function which you copied and insert your own function along with them. Or just modify that particular function to call yours and add it to the return value.
 
Thing is, I currently am trying just to add that array parameter to the CvUnit.h/cpp files without crashing on starting a new game. I'm not yet at the functional parts, unfortunately :(

Going to take unchanged CvUnit files and try adding everything yet again.

EDIT: woopsie, pushed quote instead of edit.
Oh and btw I can't say enough how much I appreciate the help :) Thanks!
 
Here's what I did, in order (modded = with my changes, unmodded = original FFPlus files)

1) Clean rebuild, modded. Surprisingly no CtD when starting new game, but hung at the "The Age of Ice has ended..." screen (stuck with the spinning globe busy-cursor, had to Ctrl-Alt-Del).
2) Comment out my CvUnit code. Build. CtD when starting new game.
3) Replace CvUnit.h/.cpp with unmodded versions. Build. CtD when starting new game.
4) Clean and build (with unmodded CvUnit). Works perfectly.
5) Mod CvUnit. Build. CtD when starting new game.
6) Clean and build, with modded CvUnit. Game starts, no problem
7) Comment out changes in CvUnit. Build. CtD when starting new game
8) Uncomment changes (so identical file as step 6). Build. Game starts, no problems.

Strange. Behavior is really wonky, but the wonkyness seems consistent after each clean and build... Going to bed now (2 hours of (clean and) building and launching and crashing the game, yay), will see tomorrow if adding anything at all to CvUnit will break stuff again. I fear so though, build process seems rather borked. My knowledge of makefiles and VS2008 project configuration is virtually non-existent, unfortunately. Any suggestion as to the cause are therefore greatly appreciated ;)
 
Back
Top Bottom