What would cause...

TheLadiesOgre

Aspiring Codesmith
Joined
Jan 16, 2009
Messages
505
...a notification to fire endlessly? I'd put up a screenshot but for some reason "screenshot failed" pops up everytime I try to take one (which is a first, for me at least). Anyway, I was trying to take some screenshots of the new text notifications and the Survivor Promotions notification will not stop firing. The top center "notification box" fills up with about 6 or 8 of them and then empties and then fills up again (I let it sit for a little and it kept doing it over and over). Any ideas as to what would cause this (other than my inexperience)? :sad:
 
...a notification to fire endlessly? I'd put up a screenshot but for some reason "screenshot failed" pops up everytime I try to take one (which is a first, for me at least). Anyway, I was trying to take some screenshots of the new text notifications and the Survivor Promotions notification will not stop firing. The top center "notification box" fills up with about 6 or 8 of them and then empties and then fills up again (I let it sit for a little and it kept doing it over and over). Any ideas as to what would cause this (other than my inexperience)? :sad:

An endless loop? I doubt anyone can help you without posting your code first.
 
Well, here is the section from ::updateCombat


Spoiler :
Code:
			bool bSurvivor = false;
			if (getSurvivorChance() > 0)
			{
				if (GC.getGameINLINE().getSorenRandNum(100, "Too Badass Check") <= getSurvivorChance())
				{
					bSurvivor = true;
					setSurvivor(true);
					jumpToNearestValidPlot();
				}
			}


and here is the what it leads to in ::kill

Spoiler :
Code:
	if (isSurvivor())
	{
		setDamage(0);
		setDamage(-(GC.getMAX_HIT_POINTS() -(getSurvivorChance() / 1000)));
		CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_UNIT_IS_HARDCORE", getNameKey());
		gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_POSITIVE_DINK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
		return;
	}
 
Hmm. It seems that your second snippet is being looped all over again. Not sure why, though...
Hmm... Maybe show us where you put the second snippet exactly?
 
The modded CvUnit::kill in it's entirity

Spoiler :
Code:
void CvUnit::kill(bool bDelay, PlayerTypes ePlayer)
{
	PROFILE_FUNC();
	
	CLLNode<IDInfo>* pUnitNode;
	CvUnit* pTransportUnit;
	CvUnit* pLoopUnit;
	CvPlot* pPlot;
	CvWString szBuffer;
	PlayerTypes eOwner;
	PlayerTypes eCapturingPlayer;
	UnitTypes eCaptureUnitType;

	pPlot = plot();
	FAssertMsg(pPlot != NULL, "Plot is not assigned a valid value");

	static std::vector<IDInfo> oldUnits;
	oldUnits.clear();
	pUnitNode = pPlot->headUnitNode();

	while (pUnitNode != NULL)
	{
		oldUnits.push_back(pUnitNode->m_data);
		pUnitNode = pPlot->nextUnitNode(pUnitNode);
	}

	for (uint i = 0; i < oldUnits.size(); i++)
	{
		pLoopUnit = ::getUnit(oldUnits[i]);
		
		if (pLoopUnit != NULL)
		{
			if (pLoopUnit->getTransportUnit() == this)
			{
				//save old units because kill will clear the static list
				std::vector<IDInfo> tempUnits = oldUnits;

				if (pPlot->isValidDomainForLocation(*pLoopUnit))
				{
					pLoopUnit->setCapturingPlayer(NO_PLAYER);
				}
				
				pLoopUnit->kill(false, ePlayer);

				oldUnits = tempUnits;
			}
		}
	}

	if (ePlayer != NO_PLAYER)
	{
		CvEventReporter::getInstance().unitKilled(this, ePlayer);

		if (NO_UNIT != getLeaderUnitType())
		{
			for (int iI = 0; iI < MAX_PLAYERS; iI++)
			{
				if (GET_PLAYER((PlayerTypes)iI).isAlive())
				{
					szBuffer = gDLL->getText("TXT_KEY_MISC_GENERAL_KILLED", getNameKey());
					gDLL->getInterfaceIFace()->addMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_MAJOR_EVENT);
				}
			}
		}
	}

	if (bDelay)
	{
		startDelayedDeath();
		return;
	}

	if (isMadeAttack() && nukeRange() != -1)
	{
		CvPlot* pTarget = getAttackPlot();
		if (pTarget)
		{
			pTarget->nukeExplosion(nukeRange(), this);
			setAttackPlot(NULL, false);
		}
	}

/*****************************************************************************************************/
/**  Author: TheLadiesOgre                                                                          **/
/**  Date: 21.09.2009                                                                               **/
/**  ModComp: TLOTags                                                                               **/
/**  Reason Added: New Bool Flags                                                                   **/
/**  Notes:                                                                                         **/
/*****************************************************************************************************/
	if (isCanRespawn())
	{
		CvCity* pCapitalCity = GET_PLAYER(getOwnerINLINE()).getCapitalCity();
		setXY(pCapitalCity->getX_INLINE(), pCapitalCity->getY_INLINE(), false, false, false);
		setDamage(0);
		changeOneUpCount(-1);
		CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_BATTLEFIELD_EVAC", getNameKey());
		gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_POSITIVE_DINK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
		return;
	}

	if (isSurvivor())
	{
		setDamage(-(GC.getMAX_HIT_POINTS() -(getSurvivorChance() / 1000)));
		CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_UNIT_IS_HARDCORE", getNameKey());
		gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_POSITIVE_DINK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
		return;
	}
/*****************************************************************************************************/
/**  TheLadiesOgre; 21.09.2009; TLOTags                                                             **/
/*****************************************************************************************************/

	finishMoves();

	if (IsSelected())
	{
		if (gDLL->getInterfaceIFace()->getLengthSelectionList() == 1)
		{
			if (!(gDLL->getInterfaceIFace()->isFocused()) && !(gDLL->getInterfaceIFace()->isCitySelection()) && !(gDLL->getInterfaceIFace()->isDiploOrPopupWaiting()))
			{
				GC.getGameINLINE().updateSelectionList();
			}

			if (IsSelected())
			{
				gDLL->getInterfaceIFace()->setCycleSelectionCounter(1);
			}
			else
			{
				gDLL->getInterfaceIFace()->setDirty(SelectionCamera_DIRTY_BIT, true);
			}
		}
	}

	gDLL->getInterfaceIFace()->removeFromSelectionList(this);

	// XXX this is NOT a hack, without it, the game crashes.
	gDLL->getEntityIFace()->RemoveUnitFromBattle(this);

	FAssertMsg(!isCombat(), "isCombat did not return false as expected");

	pTransportUnit = getTransportUnit();

	if (pTransportUnit != NULL)
	{
		setTransportUnit(NULL);
	}

	setReconPlot(NULL);
	setBlockading(false);

	FAssertMsg(getAttackPlot() == NULL, "The current unit instance's attack plot is expected to be NULL");
	FAssertMsg(getCombatUnit() == NULL, "The current unit instance's combat unit is expected to be NULL");

	GET_TEAM(getTeam()).changeUnitClassCount((UnitClassTypes)m_pUnitInfo->getUnitClassType(), -1);
	GET_PLAYER(getOwnerINLINE()).changeUnitClassCount((UnitClassTypes)m_pUnitInfo->getUnitClassType(), -1);

	GET_PLAYER(getOwnerINLINE()).changeExtraUnitCost(-(m_pUnitInfo->getExtraCost()));

	if (m_pUnitInfo->getNukeRange() != -1)
	{
		GET_PLAYER(getOwnerINLINE()).changeNumNukeUnits(-1);
	}

	if (m_pUnitInfo->isMilitarySupport())
	{
		GET_PLAYER(getOwnerINLINE()).changeNumMilitaryUnits(-1);
	}

	GET_PLAYER(getOwnerINLINE()).changeAssets(-(m_pUnitInfo->getAssetValue()));

	GET_PLAYER(getOwnerINLINE()).changePower(-(m_pUnitInfo->getPowerValue()));

	GET_PLAYER(getOwnerINLINE()).AI_changeNumAIUnits(AI_getUnitAIType(), -1);

	eOwner = getOwnerINLINE();
	eCapturingPlayer = getCapturingPlayer();
	eCaptureUnitType = ((eCapturingPlayer != NO_PLAYER) ? getCaptureUnitType(GET_PLAYER(eCapturingPlayer).getCivilizationType()) : NO_UNIT);

	setXY(INVALID_PLOT_COORD, INVALID_PLOT_COORD, true);

	joinGroup(NULL, false, false);

	CvEventReporter::getInstance().unitLost(this);

	GET_PLAYER(getOwnerINLINE()).deleteUnit(getID());

	if ((eCapturingPlayer != NO_PLAYER) && (eCaptureUnitType != NO_UNIT) && !(GET_PLAYER(eCapturingPlayer).isBarbarian()))
	{
		if (GET_PLAYER(eCapturingPlayer).isHuman() || GET_PLAYER(eCapturingPlayer).AI_captureUnit(eCaptureUnitType, pPlot) || 0 == GC.getDefineINT("AI_CAN_DISBAND_UNITS"))
		{
			CvUnit* pkCapturedUnit = GET_PLAYER(eCapturingPlayer).initUnit(eCaptureUnitType, pPlot->getX_INLINE(), pPlot->getY_INLINE());

			if (pkCapturedUnit != NULL)
			{
				szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_CAPTURED_UNIT", GC.getUnitInfo(eCaptureUnitType).getTextKeyWide());
				gDLL->getInterfaceIFace()->addMessage(eCapturingPlayer, true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_UNITCAPTURE", MESSAGE_TYPE_INFO, pkCapturedUnit->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());

				// Add a captured mission
				CvMissionDefinition kMission;
				kMission.setMissionTime(GC.getMissionInfo(MISSION_CAPTURED).getTime() * gDLL->getSecsPerTurn());
				kMission.setUnit(BATTLE_UNIT_ATTACKER, pkCapturedUnit);
				kMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
				kMission.setPlot(pPlot);
				kMission.setMissionType(MISSION_CAPTURED);
				gDLL->getEntityIFace()->AddMission(&kMission);

				pkCapturedUnit->finishMoves();

				if (!GET_PLAYER(eCapturingPlayer).isHuman())
				{
					CvPlot* pPlot = pkCapturedUnit->plot();
					if (pPlot && !pPlot->isCity(false))
					{
						if (GET_PLAYER(eCapturingPlayer).AI_getPlotDanger(pPlot) && GC.getDefineINT("AI_CAN_DISBAND_UNITS"))
						{
							pkCapturedUnit->kill(false);
						}
					}
				}
			}
		}
	}
}
 
Still no idea... The isCanRespawn thing is working correctly or is it bugging too?
If it is bugging, try removing return... just a wild guess. Would most likely kill the unit though...
 
Your problem is the delayedDeath routine. This disables the full death check when first called (during combat, which is still using the unit) and has it run later in a cleanup function. Since you stop the ::kill routine from running, you need to set m_bDeathDelay = false; somewhere or it will try to run ::kill forever.

Easy rule of thumb: If you write return; anywhere within ::kill, precede it with m_bDeathDelay = false;
 
What exactly is this code supposed to do?

Code:
setDamage(-(GC.getMAX_HIT_POINTS() -(getSurvivorChance() / 1000)));

I assume getSurvivorChange() returns a value in [0, 100] to be used as a percent, at least how it looks in your random check above. It's an int, right? If so, dividing it by 1000 will always produce 0 because that's integer division. It will then set the damage to -MAX_HIT_POINTS. Does that get capped to 0 or do you end up with a unit twice normal strength?

If you're trying to make it have the same % hit points left as its survivor chance, try this

Code:
setDamage(GC.getMAX_HIT_POINTS() * (100 - getSurvivorChance()) / 100));

So if the survivor chance is 25% and MAX_HIT_POINTS is 20 (it's not, but for example), you get 20 * 75 / 100 = 15 HP damage or 5 HP remaining.
 
Thank you Xienwolf, Opera and EmperorFool. Yes, isCanRespawn is bugging out too (but not for long now that I know how to fix it). Again thank you all.

@EmperorFool
The intention that I had in dividing by 1000 was to leave the unit with 1/10 of their survivor chance as hit points, it is supposed to simulate "I'm right on death's door, but I'm still alive" like you see in the movies where the good guy is "dead" and then gets up and shuffles away despite the fact that he is only being held together by two tendons and a stiff breeze.
 
Okay, to make it like that modify my formula like so:

Code:
setDamage(GC.getMAX_HIT_POINTS() - GC.getMAX_HIT_POINTS() * getSurvivorChance() / 1000));
 
Alrighty, new problem now. I added the m_bDelayDeath = false; and the notifications are firing perfectly, though now at the beginning of the following turn, the unit disappears completely for both Respawn and Survivor.

Here is what it has developed into in my attempts to fix it.

This is what I have in ::updateCombat (this is in there twice once aimed at the attacker and once aimed at the defender)
Spoiler :
Code:
			bool bSurvivor = false;
			if (getSurvivorChance() > 0)
			{
				if (GC.getGameINLINE().getSorenRandNum(100, "Too Badass Check") <= getSurvivorChance())
				{
					bSurvivor = true;
					setSurvivor(true);
					jumpToNearestValidPlot();
					CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_UNIT_IS_HARDCORE", getNameKey());
					gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_POSITIVE_DINK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
					finishMoves();
				}
			}

			if ((!bSurvivor) && (getOneUpCount() > 0))
			{
				setCanRespawn(true);
				changeOneUpCount(-1);
				CvCity* pCapitalCity = GET_PLAYER(getOwnerINLINE()).getCapitalCity();
				setXY(pCapitalCity->getX_INLINE(), pCapitalCity->getY_INLINE(), false, false, false);
				CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_BATTLEFIELD_EVAC", getNameKey());
				gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_POSITIVE_DINK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
				finishMoves();
			}

This is in ::kill
Spoiler :
Code:
	bool m_bDelayDeath = true;
	if (isCanRespawn())
	{
		m_bDelayDeath = false;
		setDamage(0);
		return;
	}

	if (isSurvivor())
	{
		m_bDelayDeath = false;
		setDamage(GC.getMAX_HIT_POINTS() -(GC.getMAX_HIT_POINTS() * 90 / 100));
		return;
	}

and I added this in doTurn to see if that helped

Spoiler :
Code:
	if (isCanRespawn())
	{
		if (getOneUpCount() > 0)
		{
			setCanRespawn(false);
		}
	}

	if (isSurvivor())
	{
		setSurvivor(false);
	}

I am at a loss
 
Are you healing them when you respawn them? The unit has 0, or negative, hitpoints, so during cleanup in ::doTurn it probably gets killed again. Run a setDamage function during your respawning to ensure they have SOME health (even if just 1 Hp)
 
wouldn't the setdamage(0) that I put in ::kill cover that? also, while I'm thinking about it, should I do a setdamage(0) before the survivor setdamage(GC.getMAX_HIT_POINTS() -(GC.getMAX_HIT_POINTS() * 90 / 100)); (note: I changed it thinking that maybe the unit having so little hitpoints was what was causing it for survivor)
 
setDamage() overwrites whatever the current damage is with the new value, so doing two setDamage() calls one after the other has no effect that I know about. It's always good to look at the function body when you want to know what it does, and it will speed your learning of the SDK.

I haven't looked at this function myself, but I have seen Firaxis's pattern (which is common) of using changeFoo(iChange) to add iChange to Foo and setFoo(iValue) to overwrite Foo with iValue. changeFoo() typically does setFoo(getFoo() + value).
 
Alright, so should I replace the setCanRespawn(false) and setSurvivor(false) with changeCanRespawn and changeSurvivor?
 
Alright, so I added setDamage(-getDamage()) (which is adapted from code in doHeal()) and now I'm getting CTD's after it fires.
 
setDamage() constrains the value to be within [0, maxHitPoints()]:

Code:
m_iDamage = range(iNewValue, 0, maxHitPoints());

Since getDamage() must return a non-negative value, you will pass in a negative or zero value which will get pinned to zero. This should end up setting the unit to full strength. I have no idea how this could cause a CTD, but I suspect it is caused by some other code you've added.
 
Top Bottom