Mod-Modders Guide to Fall Further

I really, really like the Aspect idea. Lots of stuff I'm planning on adding to the post FFH .34 FF version of the Legion is going to be terrain/plot based.
 
It kinda makes me wonder if features, improvements, and bonuses might be better if they were changed to just being aspects.
 
I don't know about that.... Seems to me like Aspects would be used for bonus things, like Ring of Stone, maybe some unique terrains only certain civs are allowed to have. Could possibly make it simpler to revert Deep Jungle to Jungle when the Lizards lose the terrain, or could provide the bonuses for the Scion's Haunted Lands without making the empire look completely homogenous..... Actually, think I'm gonna post that particular idea over on the Scion's thread, maybe get Tarq to pressure Xienwolf into implementing it. ;)
 
Features/Improvements/Resources give a graphic. That is why they are limited to 1 per tile. There are some things which each try to accomplish which do not NEED a graphic which can be aspects, but they cannot all be changed to be Aspects
 
Aspects could also help with the events that give extra yields (positive or negative) as well, if I'm thinking right. That way a lot of them could be reversible or temporary.
 
I'm thinking it could be good to add a promotion tag (which would typically be used with bAutoAcquire and bMustMaintain) that requires you be at war. A similar requirement for units and buildings could be good too.
 
I just had a crazy thought: would it be possible to allow promotions to change the unitcombat of units that have it without having to actually change it to a different unit? How about changing domains?


Also, how hard would it be to allow units to have multiple unitcombats? There are a lot of cases where having arcane/disciple, melee/disciple, recon/disciple, mounted/disciple, mounted/archer, and melee/mounted would make sense.
 
Hey guys, i have an issue in the warhammer mod regarding the wetlands terrain and deep jungle... and im hoping that someone here can help us figure out a fix...

i sent this to Cephalo, but just figured id ask here as well.

...

i am having some crazy map bugs in the Warhammer mod which im working on and i havent a clue whats causing them or how to fix them.

i should point out i havent touched the terrain files, though i did fiddle with improvementinfo and featureinfo files a little, but the changes i mad worked fine (added some unique feature improvements and changed feature bonuses). then i moved a couple of techs around and changed the forest chopping tech, and all of a sudden im getting enourmous tracts of the 'Marsh' Terrain, as well as small spots of 'wetland' (including wetland hills) and Deep Jungle (wetland and deep jungle are taken from the Fall Further mod).

so basically im lost for thoughts and im begging you if you would take a look at my files and see if you can fix this mess for me?

thanks mate


so any thoughts?
 
Btw, patch O removed the crashing with modular loading. Well, either that or the full reinstall I did...
 
Icky... I just walked into the proverbial lion's den.


While I was working on something else, I noticed that the CurrentEra is used by the code to decide on a ton of things, most just for how the AI acts, because it assumes that the game has advanced to a certain degree (thus may be over soon, or is beyond the "exploring" phase.


Anyway... some tidbits of where the code goes horribly wrong, and makes the AI do funny things based on what religion they are following (I am curious if the religions are linked to Eras in an order which makes these work halfway decent)

Decides on GPP Actions
Spoiler :
Code:
void CvUnitAI::AI_scientistMove()
	{PROFILE_FUNC();
	if (AI_discover(true, true))
		return;
	if (AI_construct(MAX_INT, 1))
		return;
	if (GET_PLAYER(getOwnerINLINE()).getCurrentEra() < 3)
		if (AI_join(2))
			return;
	if (GET_PLAYER(getOwnerINLINE()).getCurrentEra() <= (GC.getNumEraInfos() / 2))
		if (AI_construct())
			return;
	int iGoldenAgeValue = (GET_PLAYER(getOwnerINLINE()).AI_calculateGoldenAgeValue() / (GET_PLAYER(getOwnerINLINE()).unitsRequiredForGoldenAge()));
	int iDiscoverValue = std::max(1, getDiscoverResearch(NO_TECH));
	if (((iGoldenAgeValue * 100) / iDiscoverValue) > 60)
		{if (AI_goldenAge())
            return;
        if (iDiscoverValue > iGoldenAgeValue)
			{if (AI_discover())
                return;
            if (GET_PLAYER(getOwnerINLINE()).getUnitClassCount(getUnitClassType()) > 1)
                if (AI_join())
                    return;}}
	else
		{if (AI_discover())
			return;
		if (AI_join())
			return;}
	if ((GET_PLAYER(getOwnerINLINE()).AI_getPlotDanger(plot(), 2) > 0) || (getGameTurnCreated() < (GC.getGameINLINE().getGameTurn() - 25)))
		if (AI_discover())
			return;
	if (AI_retreatToCity())
		return;
	if (AI_safety())
		return;
	getGroup()->pushMission(MISSION_SKIP);
	return;}
Unit Artstyles & Unit Sounds (Cosmetic, and worked around by defining all of them to be the same, though if seZ ever gets bored he could redesign every unit to have Good/Neutral/Evil appearances)
Modifies AI Unit Upgrade Costs (based on Handicap settings, so possibly fixed already)
Spoiler :
Code:
int CvUnit::upgradePrice(UnitTypes eUnit) const
	{int iPrice;
	CyArgsList argsList;
	argsList.add(getOwnerINLINE());
	argsList.add(getID());
	argsList.add((int) eUnit);
	long lResult=0;
	gDLL->getPythonIFace()->callFunction(PYGameModule, "getUpgradePriceOverride", argsList.makeFunctionArgs(), &lResult);
	if (lResult >= 0)
		return lResult;
	if (isBarbarian())
		return 0;
	iPrice = GC.getDefineINT("BASE_UNIT_UPGRADE_COST");
	iPrice += (std::max(0, (GET_PLAYER(getOwnerINLINE()).getProductionNeeded(eUnit) - GET_PLAYER(getOwnerINLINE()).getProductionNeeded(getUnitType()))) * GC.getDefineINT("UNIT_UPGRADE_COST_PER_PRODUCTION"));

	if (!isHuman() && !isBarbarian())
		{iPrice *= GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIUnitUpgradePercent();
		iPrice /= 100;
		iPrice *= std::max(0, ((GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIPerEraModifier() * GET_PLAYER(getOwnerINLINE()).getCurrentEra()) + 100));
		iPrice /= 100;}
	iPrice -= (iPrice * getUpgradeDiscount()) / 100;
//FfH Traits: Added by Kael 08/02/2007
    iPrice += (iPrice * GET_PLAYER(getOwnerINLINE()).getUpgradeCostModifier()) / 100;
	CvCity* pCity = getUpgradeCity(eUnit, true);
	if (pCity != NULL)
        iPrice += (iPrice * pCity->getUpgradeCostModifier()) / 100;
//FfH: End Add
	return iPrice;}
Opinion of Enemy Force Strength
Spoiler :
Code:
void CvPlayerAI::AI_doEnemyUnitData()
	{std::vector<int> aiUnitCounts(GC.getNumUnitInfos(), 0);
	std::vector<int> aiDomainSums(NUM_DOMAIN_TYPES, 0);
	CLLNode<IDInfo>* pUnitNode;
	CvUnit* pLoopUnit;
	int iI;
	int iOldTotal = 0;
	int iNewTotal = 0;
	for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
		{CvPlot* pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
		int iAdjacentAttackers = -1;
		if (pLoopPlot->isVisible(getTeam(), false))
			{pUnitNode = pLoopPlot->headUnitNode();
			while (pUnitNode != NULL)
				{pLoopUnit = ::getUnit(pUnitNode->m_data);
				pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
				if (pLoopUnit->canFight())
					{int iUnitValue = 1;
					if (atWar(getTeam(), pLoopUnit->getTeam()))
						{iUnitValue += 10;
						if ((pLoopPlot->getOwnerINLINE() == getID()))
							iUnitValue += 15;
						else if (atWar(getTeam(), pLoopPlot->getTeam()))
							{if (iAdjacentAttackers == -1)
								iAdjacentAttackers = GET_PLAYER(pLoopPlot->getOwnerINLINE()).AI_adjacentPotentialAttackers(pLoopPlot);
							if (iAdjacentAttackers > 0)
								iUnitValue += 15;}}
					else if (pLoopUnit->getOwnerINLINE() != getID())
						{iUnitValue += pLoopUnit->canAttack() ? 4 : 1;
						if (pLoopPlot->getCulture(getID()) > 0)
							iUnitValue += pLoopUnit->canAttack() ? 4 : 1;}
					if (m_aiUnitClassWeights[pLoopUnit->getUnitClassType()] == 0)
						iUnitValue *= 4;
					iUnitValue *= pLoopUnit->baseCombatStr();
					aiUnitCounts[pLoopUnit->getUnitType()] += iUnitValue;
					aiDomainSums[pLoopUnit->getDomainType()] += iUnitValue;
					iNewTotal += iUnitValue;}}}}
	if (iNewTotal == 0)
		return;
	//Decay
	for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
		{m_aiUnitClassWeights[iI] -= 100;
		m_aiUnitClassWeights[iI] *= 3;
		m_aiUnitClassWeights[iI] /= 4;
		m_aiUnitClassWeights[iI] = std::max(0, m_aiUnitClassWeights[iI]);}
	for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
		{if (aiUnitCounts[iI] > 0)
			{UnitTypes eLoopUnit = (UnitTypes)iI;
			TechTypes eTech = (TechTypes)GC.getUnitInfo((UnitTypes)iI).getPrereqAndTech();
			int iEraDiff = (eTech == NO_TECH) ? 4 : std::min(4, getCurrentEra() - GC.getTechInfo(eTech).getEra());
			if (iEraDiff > 1)
				{iEraDiff -= 1;
				aiUnitCounts[iI] *= 3 - iEraDiff;
				aiUnitCounts[iI] /= 3;}
			FAssert(aiDomainSums[GC.getUnitInfo(eLoopUnit).getDomainType()] > 0);
			m_aiUnitClassWeights[GC.getUnitInfo(eLoopUnit).getUnitClassType()] += (5000 * aiUnitCounts[iI]) / std::max(1, aiDomainSums[GC.getUnitInfo(eLoopUnit).getDomainType()]);}}
	for (iI = 0; iI < GC.getNumUnitCombatInfos(); ++iI)
		m_aiUnitCombatWeights[iI] = 0;
	for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
		if (m_aiUnitClassWeights[iI] > 0)
			{UnitTypes eUnit = (UnitTypes)GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex();
			m_aiUnitCombatWeights[GC.getUnitInfo(eUnit).getUnitCombatType()] += m_aiUnitClassWeights[iI];}
	for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
		{if (m_aiUnitCombatWeights[iI] > 25)
			{m_aiUnitCombatWeights[iI] += 2500;}
		else if (m_aiUnitCombatWeights[iI] > 0)
			m_aiUnitCombatWeights[iI] += 1000;}}
Advanced Start Explore Unit preferences, Tech selection, City Size preference (though during Advanced Start your era won't change, so that's fine)

Reaction to Raging Barbarians being active
Spoiler :
Code:
int CvPlayerAI::AI_getTotalFloatingDefendersNeeded(CvArea* pArea)
	{PROFILE_FUNC();
	int iDefenders;
	int iCurrentEra = getCurrentEra();
	int iAreaCities = pArea->getCitiesPerPlayer(getID());
	iCurrentEra = std::max(0, iCurrentEra - GC.getGame().getStartEra() / 2);
	iDefenders = 1 + ((iCurrentEra + ((GC.getGameINLINE().getMaxCityElimination() > 0) ? 3 : 2)) * iAreaCities);
	iDefenders /= 3;
	iDefenders += pArea->getPopulationPerPlayer(getID()) / 7;
	if (pArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE)
		iDefenders *= 2;
	else if ((pArea->getAreaAIType(getTeam()) == AREAAI_OFFENSIVE) || (pArea->getAreaAIType(getTeam()) == AREAAI_MASSING))
		{iDefenders *= 2;
		iDefenders /= 3;}
	if (AI_getTotalAreaCityThreat(pArea) == 0)
		iDefenders /= 2;
	if (!GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
		{iDefenders *= 2;
		iDefenders /= 3;}
	if (AI_isDoStrategy(AI_STRATEGY_GET_BETTER_UNITS) && (pArea->getAreaAIType(getTeam()) != AREAAI_DEFENSIVE))
		iDefenders /= 2;

	if (AI_isDoStrategy(AI_STRATEGY_CULTURE3))
		{iDefenders += 2 * iAreaCities;
		if (pArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE)
			iDefenders *= 2; //go crazy}
	iDefenders *= 60;
	iDefenders /= std::max(30, (GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAITrainPercent() - 20));
	if ((iCurrentEra < 3) && (GC.getGameINLINE().isOption(GAMEOPTION_RAGING_BARBARIANS)))
		iDefenders += 2;
	if (getCapitalCity() != NULL)
		if (getCapitalCity()->area() != pArea)
			iDefenders = std::min(iDefenders, iAreaCities * iAreaCities - 1);    ///Note:  Bad for the Lanun here.
	return iDefenders;}
Notice at the end of the last section that the AI naturally despises bothering to defend any city that is not on the same landmass as the capital....

desire for a Cultural Victory
Spoiler :
Code:
int CvPlayerAI::AI_getCultureVictoryStage()
	{int iValue;
    if (!GC.getGameINLINE().culturalVictoryValid())
        return 0;
    if (getCapitalCity() == NULL)
        return 0;
    if (getCapitalCity()->getGameTurnFounded() > (10 + GC.getGameINLINE().getStartTurn()))
        return 0;
    if (GC.getGame().getStartEra() > 1)
    	return 0;
    iValue = 0;
	if (GC.getGameINLINE().isOption(GAMEOPTION_ALWAYS_PEACE))
    	iValue += 4;
    for (int iI = 0; iI < GC.getNumTraitInfos(); iI++)
        if (hasTrait((TraitTypes)iI))
			{iValue += ((GC.getTraitInfo((TraitTypes)iI).getMaxAnarchy() == 0) ? 4 : 0);
            iValue += (GC.getTraitInfo((TraitTypes)iI).getCommerceChange(COMMERCE_CULTURE) * 2);
            iValue += (GC.getTraitInfo((TraitTypes)iI).getCommerceModifier(COMMERCE_CULTURE) / 6);
            iValue += (GC.getTraitInfo((TraitTypes)iI).getGreatPeopleRateModifier() / 25);
            iValue += (GC.getTraitInfo((TraitTypes)iI).getExtraYieldThreshold(YIELD_COMMERCE)) * 2 * GC.getDefineINT("EXTRA_YIELD");
			iValue += (GC.getTraitInfo((TraitTypes)iI).getTradeYieldModifier(YIELD_COMMERCE)) / 25;
			iValue += ((GC.getTraitInfo((TraitTypes)iI).getMaxGlobalBuildingProductionModifier() / 25));}
    iValue += (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? -3 : 0);
	CivicTypes eCivic = (CivicTypes)GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic();
	if ((eCivic != NO_CIVIC) && (GC.getCivicInfo(eCivic).isNoNonStateReligionSpread()))
		iValue -= 3;
	iValue += countHolyCities();
	if ((GET_TEAM(getTeam()).isAVassal()) && (getNumCities() > 5))
		{int iReligionCount = countTotalHasReligion();
		if (((iReligionCount * 100) / getNumCities()) > 250)
			{iValue += 1;
			iValue += ((2 * iReligionCount) + 1) / getNumCities();}}
    int iNonsense = 0;
    iNonsense += getCapitalCity()->getX();
    iNonsense += getCapitalCity()->getY();
    iValue += (iNonsense % 7);
    if (iValue < 10)
        return 0;
    if (getCurrentEra() >= (GC.getNumEraInfos() - (2 + iNonsense % 2)))
        if (countTotalHasReligion() >= getNumCities() * 3)
            return 3;
		int iHighCultureCount = 0;
		int iLoop;
		CvCity* pLoopCity;
		for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
			{if (pLoopCity->getBaseCommerceRate(COMMERCE_CULTURE) > 100)
				iHighCultureCount++;
			else if (pLoopCity->getCultureLevel() >= (GC.getGameINLINE().culturalVictoryCultureLevel() - 1))
				iHighCultureCount++;}
		if (iHighCultureCount >= GC.getGameINLINE().culturalVictoryNumCultureCities())
			return 3;
    }
	if (getCurrentEra() >= ((GC.getNumEraInfos() / 3) + iNonsense % 2))
	    return 2;
	else
        return 1;
	FAssert(false);}



Anyway... the list goes on a bit longer. This was just the point where I ran out of time to look over everything. And there had been an item or 2 I didn't fully understand yet because it was too short to establish context (something about AI birthmarks and Stacks of Doom...)



So one of these days I have to get in there and clean all of this junk out, that is if xanaqui hasn't already gotten to it by then :) Or Kael, he cleaned up quite a few references to Era when he reworked the Strategy Hash logic.
 
I'd really like the ability to make promotions give combat bonuses based on the plot counter. (you know, the thing that controls hell terrain).

I'd also like the ability for promotions to (have a chance to) increase or decrease the plot counter of the unit's plot. I implemented the effect with a PyPerPurn call for several promotions in my version, but I'm thinking that as often as I used it it would be a lot better if it were handled in the dll.

Having plot counter requirements for entering tiles could be cool too, as it would be easier for me to limit upper level demons to corrupted terrains with such a tag instead of needing to make every non-hell terrain and feature impassible for these units.



Oh, I'm still waiting for my old request of letting promotions give combat bonuses based on the percent of your teams culture in the tile.
 
Ya know, if you released something I would be far more inclined to work on simple tweaks and changes which you have plans for, or if Vehem and I do not think it belongs in FF I would at least help walk you through how to code it so you can get a feel for how to handle some of your crazier ideas on your own.


Right now though, I am working on quite a few things which are quite possibly impossible, and have a 1 kB .txt file of other ideas for future development which need sorted before I go off on wild tangents.

EDIT: Ok, so I misremembered, it is actually 31 KB. I coulda sworn that 1 KB used to be "large" for raw .txt files
 
Ya know, if you released something I would be far more inclined to work on simple tweaks and changes which you have plans for, or if Vehem and I do not think it belongs in FF I would at least help walk you through how to code it so you can get a feel for how to handle some of your crazier ideas on your own.


Right now though, I am working on quite a few things which are quite possibly impossible, and have a 1 kB .txt file of other ideas for future development which need sorted before I go off on wild tangents.

Jesus. Got enough there for a few more major releases lol.
 
I'd like promotions with cheese, pickle, mustard and onion.
 
Ick, don't like tomatoes, myself. :)

That said, if I had any personal wishes for new features, it would definitely be to allow promotions to have a duration. More importantly, for promotions to be able to be GIVEN a duration, like units can. So that you could, for example, ~temporarily~ teach a unit a spell. Or have a spell that grants a few turns of March to every unit in the stack. Currently all of this can be done by adding new, duplicate promotions with a chance of wearing off, but it seems like some way to have limited duration promotions would be a lot cleaner. Plus having promotions that last a predictable amount of time could be useful in other ways, too.
 
That one is buried in the design document somewhere. Simple to code, just haven't thought of anything cool to do with it, so the idea quickly fell into the long list of "sometime soon" land. Same with duration on buildings actually.

EDIT: Though I did just think of a REALLY fun thing you can do with the functionality that is required to have promotions with duration :) You could have Entropy 3 be replaced with "Feeble Minded," a spell which causes all units in range to lose any promotion they have had for more than 50 turns :D Way powerful/fun idea, might even be enough to bump the duration thing up to the top of the docs (at least for a little while)
 
Top Bottom