[MODCOMP] Multiple Professions per Building

What was the bug ?

The bug was that the game would crash if you put a citizen on a building slot if all possible professions for that building already had other citizens working in them, and those citizens' production was being unproduced due to lack of inputs. So, for instance, if your carpenter's shop already had a carpenter whose production was being unproduced due to lack of lumber, if you put another citizen in the carpenter's shop the game would crash.

Could you post the code of the fix ?
(Maybe highlighted in red.)

Yes, here it is (the change is highlighted in red):

Code:
//Androrc Multiple Professions per Building
void CvCity::alterUnitWorkingBuilding(BuildingTypes eBuilding, int iUnitId, bool bAskProfession)
{
	FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
	FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");

	//shouldn't be used for the AI at least for now; it can already handle multiple professions in a building
	if (!isHuman())
	{
		return;
	}

	if (!isHasBuilding(eBuilding))
	{
		return;
	}

	CvUnit* pUnit = NULL;
	if (iUnitId != -1)
	{
		pUnit = getPopulationUnitById(iUnitId);
		if (pUnit == NULL)
		{
			pUnit = GET_PLAYER(getOwnerINLINE()).getUnit(iUnitId);
			if (pUnit != NULL)
			{
				if (!pUnit->canJoinCity(plot()))
				{
					return;
				}
				addPopulationUnit(pUnit, NO_PROFESSION);
			}
		}
	}

	if (isAvailableBuildingSlot(eBuilding, pUnit))
	{
		if (pUnit != NULL)
		{
			pUnit->setColonistLocked(true);
		}

		//code based on the one in setUnitWorkingPlot()
		FAssert(pUnit->isColonistLocked());
		if (pUnit->isColonistLocked())
		{
			//assign profession that produces yields
			ProfessionTypes eBestProfession = NO_PROFESSION;
			int iBestYieldAmount = 0;
			for(int i=0;i<GC.getNumProfessionInfos();i++)
			{
				ProfessionTypes eLoopProfession = (ProfessionTypes) i;
				if (GET_PLAYER(getOwnerINLINE()).isProfessionValid(eLoopProfession, pUnit->getUnitType()))
				{
					if(GC.getProfessionInfo(eLoopProfession).getSpecialBuilding() == GC.getBuildingInfo(eBuilding).getSpecialBuildingType())
					{
						int iLoopYieldAmount = getProfessionOutput(eLoopProfession, pUnit);
						if (GC.getProfessionInfo(eLoopProfession).getYieldConsumed() != NO_YIELD)
						{
							int iUnproducedAmount = (getYieldStored((YieldTypes) GC.getProfessionInfo(eLoopProfession).getYieldConsumed()) + getYieldRate((YieldTypes) GC.getProfessionInfo(eLoopProfession).getYieldConsumed())) - getProfessionInput(eLoopProfession, pUnit);
							if (iUnproducedAmount < 0)
							{
								iLoopYieldAmount += (iUnproducedAmount / (getProfessionInput(eLoopProfession, pUnit) / getProfessionOutput(eLoopProfession, pUnit)));
[COLOR="Red"]								//Androrc Multiple Professions per Building 1.3 Start
								if (iLoopYieldAmount < 0)
								{
									iLoopYieldAmount = 0;
								}
								//Androrc Multiple Professions per Building 1.3 End[/COLOR]
							}
						}
						if(iLoopYieldAmount >= iBestYieldAmount)
						{
							eBestProfession = eLoopProfession;
							iBestYieldAmount = iLoopYieldAmount;
						}
					}
				}
			}
			FAssert(eBestProfession != NO_PROFESSION);
			if(eBestProfession != NO_PROFESSION)
			{
				pUnit->setProfession(eBestProfession);
			}
		}

		if (isHuman() && bAskProfession)
		{
			if (pUnit != NULL)
			{
				CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_CHOOSE_PROFESSION, getID(), pUnit->getID(), 2);
				gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE(), true, true);
			}
		}
	}
}
//Androrc End
 
I found another bug (the game crashes if you drop a unit that can't have any of the building's professions on a building; for instance, if you drop a specialist on the Schoolhouse).

Thus, this modcomp is now being updated to version 1.4; it contains the following changes (in red) to CvCity::alterUnitWorkingBuilding(():

Code:
//Androrc Multiple Professions per Building
void CvCity::alterUnitWorkingBuilding(BuildingTypes eBuilding, int iUnitId, bool bAskProfession)
{
	FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
	FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");

	//shouldn't be used for the AI at least for now; it can already handle multiple professions in a building
	if (!isHuman())
	{
		return;
	}

	if (!isHasBuilding(eBuilding))
	{
		return;
	}

	CvUnit* pUnit = NULL;
	if (iUnitId != -1)
	{
		pUnit = getPopulationUnitById(iUnitId);
		if (pUnit == NULL)
		{
			pUnit = GET_PLAYER(getOwnerINLINE()).getUnit(iUnitId);
			if (pUnit != NULL)
			{
				if (!pUnit->canJoinCity(plot()))
				{
					return;
				}
				addPopulationUnit(pUnit, NO_PROFESSION);
			}
		}
	}

	if (isAvailableBuildingSlot(eBuilding, pUnit))
	{
		if (pUnit != NULL)
		{
			pUnit->setColonistLocked(true);
		}

		//code based on the one in setUnitWorkingPlot()
		FAssert(pUnit->isColonistLocked());
[COLOR="Red"]		ProfessionTypes eBestProfession = NO_PROFESSION; //transferred from below[/COLOR]
		if (pUnit->isColonistLocked())
		{
			//assign profession that produces yields
			int iBestYieldAmount = 0;
			for(int i=0;i<GC.getNumProfessionInfos();i++)
			{
				ProfessionTypes eLoopProfession = (ProfessionTypes) i;
[COLOR="Red"]				if (GET_PLAYER(getOwnerINLINE()).isProfessionValid(eLoopProfession, pUnit->getUnitType()) && pUnit->canHaveProfession(eLoopProfession, true))
[/COLOR]				{
					if(GC.getProfessionInfo(eLoopProfession).getSpecialBuilding() == GC.getBuildingInfo(eBuilding).getSpecialBuildingType())
					{
						int iLoopYieldAmount = getProfessionOutput(eLoopProfession, pUnit);
						if (GC.getProfessionInfo(eLoopProfession).getYieldConsumed() != NO_YIELD)
						{
							int iUnproducedAmount = (getYieldStored((YieldTypes) GC.getProfessionInfo(eLoopProfession).getYieldConsumed()) + getYieldRate((YieldTypes) GC.getProfessionInfo(eLoopProfession).getYieldConsumed())) - getProfessionInput(eLoopProfession, pUnit);
							if (iUnproducedAmount < 0)
							{
								iLoopYieldAmount += (iUnproducedAmount / (getProfessionInput(eLoopProfession, pUnit) / getProfessionOutput(eLoopProfession, pUnit)));
								//Androrc Multiple Professions per Building 1.3 Start
								if (iLoopYieldAmount < 0)
								{
									iLoopYieldAmount = 0;
								}
								//Androrc Multiple Professions per Building 1.3 End
							}
						}
						if(iLoopYieldAmount >= iBestYieldAmount)
						{
							eBestProfession = eLoopProfession;
							iBestYieldAmount = iLoopYieldAmount;
						}
					}
				}
			}
[COLOR="Red"]                        //deleted assert here that no longer served a purpose[/COLOR]
			if(eBestProfession != NO_PROFESSION)
			{
				pUnit->setProfession(eBestProfession);
			}
[COLOR="Red"]			else
			{
				pUnit->setColonistLocked(false);
			}[/COLOR]
		}

[COLOR="Red"]		if (isHuman() && bAskProfession && eBestProfession != NO_PROFESSION)[/COLOR]
		{
			if (pUnit != NULL)
			{
				CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_CHOOSE_PROFESSION, getID(), pUnit->getID(), 2);
				gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE(), true, true);
			}
		}
	}
}
//Androrc End
 
I will integrate that fix in the Religion and Revolution Sources. :thumbsup:
(I will upload new DLL and Sources to SVN, too.)
 
Top Bottom