Grey Fox said:Uhm, I dont understand really what you did there. Why not just add the +40% or whatever to the promotion rather then in the battle code? Im confused.
Kael said:If you added +40% to the promotion it would be +40% vs everything. Applying the modifier in this function lets a modmaker be more granular about when the bonus is gained. Want a unit to have a bonus when it is attacking into forests or when the defending unit is French/fatigued/republican/hindu/etc etc? Then you may want to modify the maxCombatStr code.
Grey Fox said:Ok, im getting what you are saying now.
But cant you just make some units be classed Elf. And then the promotion give +40% vs Elf like the ones in the game does vs melee etc. Or do you want your elfs to still be melee and also elf? Isnt it easier to make a super class then that you give all units that defines their race? And a promotion can be against Unit Superclass, class and type (like the Grenadiers +vs Riflemen.).
Or am I still confused?
One reason I can think of is it makes a good example for how to use the SDK in the absence of documentation from Firaxis. Good work KaelLord Olleus said:But why do this in C++ Kael?
Lord Olleus said:But why do this in C++ Kael?
Why not just create an event which is called at the same time as combat and do that in python? It would be a much neater way of doing it.
What about CvGameUtils.py? Those functions return values to the c++ part don't they?Kael said:I dont think I have ever seen a python event that passes data back to c++ as we would need to do if you wanted a python event that allow you to modify the odds.
long lResult=0;
gDLL->getPythonIFace()->callFunction(PYGameModule, "canTrain", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return true;
}
snarko said:What about CvGameUtils.py? Those functions return values to the c++ part don't they?
edit: from canTrain in CvCity.cpp
Code:long lResult=0; gDLL->getPythonIFace()->callFunction(PYGameModule, "canTrain", argsList.makeFunctionArgs(), &lResult); delete pyCity; // python fxn must not hold on to this pointer if (lResult == 1) { return true; }
Kael said:...Want a unit to have a bonus when it is attacking into forests ...
Vehem said:This is the similar to the bonus I'm trying to implement - "Attackers with Woodsman2 (soon to be Woodsman3) gain a bonus when attacking into Forests equal to the current iDefense of the Forest feature. Also, the Defender does not receive the bonus."
The only part of this I've managed to get working is that I can reverse the terrain bonuses indiscriminantly, though the way I've done that seems messy from a coding point of view.
The problems I have are
[*]identifying forests specifically (as they're Enumerated into FeatureTypes after loading the XML - the best I've got so far is along the lines of [ GC.getFeatureInfo(pPlot->getFeatureType()).getTextKey() == "FEATURE_FOREST" ]
if (pPlot->getFeatureType() == (FeatureTypes)GC.getInfoTypeForString("FEATURE_FOREST"))
//FfH: Added by Kael 05/02/2006
CyUnit* pyUnit = new CyUnit(this);
CyUnit* pyUnit2 = new CyUnit(pDefender);
CyArgsList argsList; // XXX
long lResult=0;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyUnit)); // pass in unit class
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyUnit2)); // pass in unit class
gDLL->getPythonIFace()->callFunction(PYGameModule, "cannotAttack", argsList.makeFunctionArgs(), &lResult);
delete pyUnit; // python fxn must not hold on to this pointer
delete pyUnit2; // python fxn must not hold on to this pointer
if (lResult == 1)
{
bFinish = true;
}
//FfH: End Add
def cannotAttack(argsList):
pAttacker = argsList[0]
pDefender = argsList[1]
if pAttacker.isHasPromotion(gc.getInfoTypeForString('PROMOTION_CHARMED')):
return True
if pDefender.isHasPromotion(gc.getInfoTypeForString('PROMOTION_FEAR')):
if pDefender.maxCombatStr(pDefender.plot(), pAttacker) > pAttacker.maxCombatStr(pDefender.plot(), pDefender):
return True
return False
gDLL->getPythonIFace()->callFunction(PYGameModule, "cannotAttack", argsList.makeFunctionArgs(), &lResult);
gDLL->getEventReporterIFace()->genericEvent("yourevent", argsList.makeFunctionArgs());
void CvCity::doReligion()
{
CvCity* pLoopCity;
int iRandThreshold;
int iSpread;
int iLoop;
int iI, iJ;
CyCity* pyCity = new CyCity(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
long lResult=0;
gDLL->getPythonIFace()->callFunction(PYGameModule, "doReligion", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return;
}
if (getReligionCount() == 0)
{
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (!isHasReligion((ReligionTypes)iI))
{
if ((iI == GET_PLAYER(getOwnerINLINE()).getStateReligion()) || !(GET_PLAYER(getOwnerINLINE()).isNoNonStateReligionSpread()))
{
iRandThreshold = 0;
for (iJ = 0; iJ < MAX_PLAYERS; iJ++)
{
if (GET_PLAYER((PlayerTypes)iJ).isAlive())
{
for (pLoopCity = GET_PLAYER((PlayerTypes)iJ).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iJ).nextCity(&iLoop))
{
if (pLoopCity->isConnectedTo(this))
{
iSpread = pLoopCity->getReligionInfluence((ReligionTypes)iI);
iSpread *= GC.getReligionInfo((ReligionTypes)iI).getSpreadFactor();
if (iSpread > 0)
{
iSpread /= max(1, (((GC.getDefineINT("RELIGION_SPREAD_DISTANCE_DIVISOR") * plotDistance(getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE())) / GC.getMapINLINE().maxPlotDistance()) - 5));
//iSpread /= (getReligionCount() + 1);
iRandThreshold = max(iRandThreshold, iSpread);
}
}
}
}
}
if (GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("RELIGION_SPREAD_RAND"), "Religion Spread") < iRandThreshold)
{
setHasReligion(((ReligionTypes)iI), true, true);
break;
}
}
}
}
}
}
void CvCity::doReligion()
{
CvCity* pLoopCity;
int iRandThreshold;
int iSpread;
int iLoop;
int iI, iJ;
CyCity* pyCity = new CyCity(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
long lResult=0;
gDLL->getPythonIFace()->callFunction(PYGameModule, "doReligion", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return;
}
// if (getReligionCount() == 0)
// {
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (!isHasReligion((ReligionTypes)iI))
{
if ((iI == GET_PLAYER(getOwnerINLINE()).getStateReligion()) || !(GET_PLAYER(getOwnerINLINE()).isNoNonStateReligionSpread()))
{
iRandThreshold = 0;
for (iJ = 0; iJ < MAX_PLAYERS; iJ++)
{
if (GET_PLAYER((PlayerTypes)iJ).isAlive())
{
for (pLoopCity = GET_PLAYER((PlayerTypes)iJ).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iJ).nextCity(&iLoop))
{
if (pLoopCity->isConnectedTo(this))
{
iSpread = pLoopCity->getReligionInfluence((ReligionTypes)iI);
iSpread *= GC.getReligionInfo((ReligionTypes)iI).getSpreadFactor();
if (iSpread > 0)
{
iSpread /= max(1, (((GC.getDefineINT("RELIGION_SPREAD_DISTANCE_DIVISOR") * plotDistance(getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE())) / GC.getMapINLINE().maxPlotDistance()) - 5));
iSpread /= (getReligionCount() + 1);
iRandThreshold = max(iRandThreshold, iSpread);
}
}
}
}
}
if (GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("RELIGION_SPREAD_RAND"), "Religion Spread") < iRandThreshold)
{
setHasReligion(((ReligionTypes)iI), true, true);
break;
}
}
}
}
// }
}
How difficult is it to add a new tag in CIV4PromotionInfos.xml that is coded with a more generic code so as to allow modders to add their own anti-promotion promotions without setting the specifics for it all in the SDK?Kael said:Just a simple example of a change that can be made in CvUnit::maxCombatStr(). This applies a combat modifier based on the existence of the "Elf" promotion on the defender and the "Elf Slaying" promotion on the attacker. maxCombatStr is called from the perspective of the defender and I have the 2 checks in so that the bonus is applied on attack and defense.
Changes are in bold.
CvUnit::maxCombatStr()
Code:if (getUnitCombatType() != NO_UNITCOMBAT) { iModifier -= pAttacker->unitCombatModifier(getUnitCombatType()); if (pCombatDetails != NULL) { pCombatDetails->iCombatModifierT = -(pAttacker->unitCombatModifier(getUnitCombatType())); } } [b] if (isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_ELF"))) { if (pAttacker->isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_ELF_SLAYING"))) { iModifier = iModifier - 40; } } if (isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_ELF_SLAYING"))) { if (pAttacker->isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_ELF"))) { iModifier = iModifier + 40; } }[/b] iModifier += domainModifier(pAttacker->getDomainType()); if (pCombatDetails != NULL) { pCombatDetails->iDomainModifierA = domainModifier(pAttacker->getDomainType()); }
Why use this? It provides the ability to specify anti-unit combat bonuses without having to make new unit combats or declare specific unitclasses. Also since its easy to add and remove promotions as you play it can allow you to create pretty dynamic bonuses.
Outside of that just being familiar with the effect of changing maxCombatStr can be very helpful to anyone who wants to effect the combat odds. Changes made here are reflect both in the actual combat odds as well as the display.
Shqype said:Kael, you might want to update post 35 to let people know that modification is done in CvCity.cpp
How difficult is it to add a new tag in CIV4PromotionInfos.xml that is coded with a more generic code so as to allow modders to add their own anti-promotion promotions without setting the specifics for it all in the SDK?
Kael said:Just a simple example of a change that can be made in CvUnit::maxCombatStr(). This applies a combat modifier based on the existence of the "Elf" promotion on the defender and the "Elf Slaying" promotion on the attacker. maxCombatStr is called from the perspective of the defender and I have the 2 checks in so that the bonus is applied on attack and defense.
Changes are in bold.
CvUnit::maxCombatStr()
Code:if (getUnitCombatType() != NO_UNITCOMBAT) { iModifier -= pAttacker->unitCombatModifier(getUnitCombatType()); if (pCombatDetails != NULL) { pCombatDetails->iCombatModifierT = -(pAttacker->unitCombatModifier(getUnitCombatType())); } } [b] if (isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_ELF"))) { if (pAttacker->isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_ELF_SLAYING"))) { iModifier = iModifier - 40; } } if (isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_ELF_SLAYING"))) { if (pAttacker->isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_ELF"))) { iModifier = iModifier + 40; } }[/b] iModifier += domainModifier(pAttacker->getDomainType()); if (pCombatDetails != NULL) { pCombatDetails->iDomainModifierA = domainModifier(pAttacker->getDomainType()); }
Why use this? It provides the ability to specify anti-unit combat bonuses without having to make new unit combats or declare specific unitclasses. Also since its easy to add and remove promotions as you play it can allow you to create pretty dynamic bonuses.
Outside of that just being familiar with the effect of changing maxCombatStr can be very helpful to anyone who wants to effect the combat odds. Changes made here are reflect both in the actual combat odds as well as the display.