[BTS][SDK] World size based unit limits (think missionaries)

Seven05

Warmonger
Joined
Dec 5, 2005
Messages
2,056
Location
USA
This has always bugged me so I decided to fix it :)

What this SDK mod does is change the number of allowed world/team/player instances based on the size of the map you're playing on. So, while you get 3 missionaries on a standard map you'll have 6 on a huge map but only 2 on a small map. It doesn't rely on any new XML tags, instead it uses the existing iBuildingPrereqModifier (Note: standard value here is 50, not 100 so the code reflects that in the calculations). It will literally drop right in without any changes to anything outside of the DLL. And, of course the proper number will display in the game. These changes may also be compatible with vanila Civ4 and Warlords as well but I didn't confirm that.

There is one issue because I was too lazy to write my own wrapper nor did I want to mess with the base class definitions. The function to get the limit that is exposed to python simply returns the 'old' limit as defined in the Civ4UnitClassInfos.xml file (so 3 missionaries even on huge maps). This is only important if you actually use python to fetch this value from the xml file, using the canTrain or cannotTrain python functions will work as expected using the new values. However, you can easily duplicate this code in python for the same effect, in fact maybe I'll whip up a python only version later for people who don't want to (or can't) edit their CvGameCoreDLL.

This code can easily be modified to only apply to either world, team or player instances or any combination of them. So it is possible to scale the player instances but not the world instances for one example.

I'll attach the files at the end, if you prefer here are the changes by file:

In CvGame.cpp find the definition for CvGame::isUnitClassMaxedOut(UnitClassTypes eIndex, int iExtra) and replace it with:
Spoiler :
Code:
bool CvGame::isUnitClassMaxedOut(UnitClassTypes eIndex, int iExtra)
{
	FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	FAssertMsg(eIndex < GC.getNumUnitClassInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");

	if (!isWorldUnitClass(eIndex))
	{
		return false;
	}
// ----- World Piece Begin -----
	int iMaxUnits = GC.getUnitClassInfo(eIndex).getMaxGlobalInstances();
	iMaxUnits *= max(0, GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getBuildingClassPrereqModifier());
	iMaxUnits /= 50; // 50 is the modifier used on standard maps so we'll use that, not 100
	iMaxUnits = max(1,iMaxUnits);

	FAssertMsg(getUnitClassCreatedCount(eIndex) <= GC.getUnitClassInfo(eIndex).getMaxGlobalInstances(), "Index is expected to be within maximum bounds (invalid Index)");

	return ((getUnitClassCreatedCount(eIndex) + iExtra) >= iMaxUnits);
// ----- World Piece End -----
}

In CvGameTextMgr.cpp find the definition for CvGameTextMgr::setUnitHelp(CvWStringBuffer &szBuffer, UnitTypes eUnit, bool bCivilopediaText, bool bStrategyText, bool bTechChooserText, CvCity* pCity), look for the following line:
Code:
	if (isWorldUnitClass(eUnitClass))
And replace it and the two following if statements with:
Spoiler :
Code:
	if (isWorldUnitClass(eUnitClass))
	{
// ----- World Piece Begin -----
// Modified max global/team/player units limits based on map size
		int iMaxUnits = GC.getUnitClassInfo(eUnitClass).getMaxGlobalInstances();
		iMaxUnits *= max(0, GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getBuildingClassPrereqModifier());
		iMaxUnits /= 50; // 50 is the modifier used on standard maps so we'll use that, not 100
		iMaxUnits = max(1,iMaxUnits);

		if (pCity == NULL)
		{
			szBuffer.append(NEWLINE);
			szBuffer.append(gDLL->getText("TXT_KEY_UNIT_WORLD_UNIT_ALLOWED", iMaxUnits));
		}
		else
		{
			szBuffer.append(gDLL->getText("TXT_KEY_UNIT_WORLD_UNIT_LEFT", (iMaxUnits - (ePlayer != NO_PLAYER ? GC.getGameINLINE().getUnitClassCreatedCount(eUnitClass) + GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getUnitClassMaking(eUnitClass) : 0))));
		}
	}

	if (isTeamUnitClass(eUnitClass))
	{
		int iMaxUnits = GC.getUnitClassInfo(eUnitClass).getMaxTeamInstances();
		iMaxUnits *= max(0, GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getBuildingClassPrereqModifier());
		iMaxUnits /= 50; // 50 is the modifier used on standard maps so we'll use that, not 100
		iMaxUnits = max(1,iMaxUnits);

		if (pCity == NULL)
		{
			szBuffer.append(NEWLINE);
			szBuffer.append(gDLL->getText("TXT_KEY_UNIT_TEAM_UNIT_ALLOWED", iMaxUnits));
		}
		else
		{
			szBuffer.append(gDLL->getText("TXT_KEY_UNIT_TEAM_UNIT_LEFT", (iMaxUnits - (ePlayer != NO_PLAYER ? GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getUnitClassCountPlusMaking(eUnitClass) : 0))));
		}
	}

	if (isNationalUnitClass(eUnitClass))
	{
		int iMaxUnits = GC.getUnitClassInfo(eUnitClass).getMaxPlayerInstances();
		iMaxUnits *= max(0, GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getBuildingClassPrereqModifier());
		iMaxUnits /= 50; // 50 is the modifier used on standard maps so we'll use that, not 100
		iMaxUnits = max(1,iMaxUnits);

		if (pCity == NULL)
		{
			szBuffer.append(NEWLINE);
			szBuffer.append(gDLL->getText("TXT_KEY_UNIT_NATIONAL_UNIT_ALLOWED", iMaxUnits));
		}
		else
		{
			szBuffer.append(gDLL->getText("TXT_KEY_UNIT_NATIONAL_UNIT_LEFT", (iMaxUnits - (ePlayer != NO_PLAYER ? GET_PLAYER(ePlayer).getUnitClassCountPlusMaking(eUnitClass) : 0))));
		}
	}
// ----- World Piece End -----

In CvPlayer.cpp find the definition for CvPlayer::isUnitClassMaxedOut(UnitClassTypes eIndex, int iExtra) const and replace with:
Spoiler :
Code:
bool CvPlayer::isUnitClassMaxedOut(UnitClassTypes eIndex, int iExtra) const
{
	FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	FAssertMsg(eIndex < GC.getNumUnitClassInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");

	if (!isNationalUnitClass(eIndex))
	{
		return false;
	}
// ----- World Piece Begin -----
	int iMaxUnits = GC.getUnitClassInfo(eIndex).getMaxPlayerInstances();
	iMaxUnits *= max(0, GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getBuildingClassPrereqModifier());
	iMaxUnits /= 50; // 50 is the modifier used on standard maps so we'll use that, not 100
	iMaxUnits = max(1,iMaxUnits);

	FAssertMsg(getUnitClassCount(eIndex) <= iMaxUnits, "getUnitClassCount is expected to be less than maximum bound of MaxPlayerInstances (invalid index)");

	return ((getUnitClassCount(eIndex) + iExtra) >= iMaxUnits);
// ----- World Piece End -----
}

In CvTeam.cpp find the definition for CvTeam::isUnitClassMaxedOut(UnitClassTypes eIndex, int iExtra) const and replace with:
Spoiler :
Code:
bool CvTeam::isUnitClassMaxedOut(UnitClassTypes eIndex, int iExtra) const
{
	FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	FAssertMsg(eIndex < GC.getNumUnitClassInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");

	if (!isTeamUnitClass(eIndex))
	{
		return false;
	}
// ----- World Piece Begin -----
	int iMaxUnits = GC.getUnitClassInfo(eIndex).getMaxPlayerInstances();
	iMaxUnits *= max(0, GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getBuildingClassPrereqModifier());
	iMaxUnits /= 50; // 50 is the modifier used on standard maps so we'll use that, not 100
	iMaxUnits = max(1,iMaxUnits);

	FAssertMsg(getUnitClassCount(eIndex) <= iMaxUnits, "The current unit class count is expected not to exceed the maximum number of instances allowed for this team");

	return ((getUnitClassCount(eIndex) + iExtra) >= iMaxUnits);
// ----- World Piece End -----
}

Alternatively, download the zip file, extract the cpp files and use you favorite software to merge them into your existing project :)

If you're curious, "World Piece" is the name of my top secrect, unreleased Mod, feel free to change those comments to anything you like.:crazyeye:

Note:
I only play on standard & larger maps, in retrospect I should have added some safety catch to the code to make sure at least one unit is always allowed for those of you who do play on small maps. It's a simple fix, just edit every return value using max(1,<old code here>).
 

Attachments

  • Unit Limits.zip
    218 KB · Views: 57
Thanks Demon :)

I fixed the code text in the post (but not the attached files) to make sure at least one unit can be created regardless of the world size so it'll work on smaller than standard sized maps. I'll work on that python only version when I get a chance.
 
Top Bottom