[Python or SDK] - Unhealthiness on missile use

jkp1187

Unindicted Co-Conspirator
Joined
Aug 29, 2004
Messages
2,496
Location
Pittsburgh, Pennsylvania
Hello, all. I am looking for a way to create a missile unit that, when used, create unhealthiness in a city (akin to the espionage "poison water" mission). Not sure where to begin on this one. I'm just trying to fix the biowarfare missile from NextWar (whose civilopedia entry claims that it should reduce population if used in cities, but petulantly refuses to do so when I use it.)

Thanks in advance.
 
Looking about in Zebra 9's API, I found this tag which may be useful for you: changeExtraHealth (int iChange)

This is a CyCity class instance so I would presume you'd trigger it once the missile explodes, with the city in question being acted upon. Something like pCity.changeExtraHealth(-5) or whatever you want the negative health to be.

I don't know if this is the best function (looks like it though), and I can't tell you how to make the thing trigger. There is an event check when a nuclear bomb explodes but not one for a guided missile. You may have to mess around in SDK.
 
Thanks, I will check that.

If I have to do an SDK mod, so be it, but my initial experiment will be to do a check in Python -- CvEventManger.py. That file seems to be a repository of a couple of different in-game checks......
 
There might be a workaround using onUnitLost or onUnitKilled (since missiles are destroyed upon use). You could have the game check the unit's ID, and if it is your specific biological missile, then you decrease the health of its target. This is not very efficient and also kind of tricky, since you'd have to have a second check to see if the missile hit a city.

It's doable, but it may not be the most effective way.
 
Hmm. I see what you mean, after looking through CvEventManager. Plus, there might be a danger with onUnitKilled in that if a bio-missile is legitimately destroyed by an enemy in a city, then that city will get the unhealthiness. (Which, of course, might be an interesting game mechanic, too...but not quite what I was looking for at this point.)
 
Hmm, I did not think about that. There is a difference between a Unit Lost and a Unit Killed (I don't know what though--I think it's being killed in combat vs. being "captured," like if a missionary unit is caught alone) so perhaps that distinction would solve this problem. On the other hand, it might not. :crazyeye:
 
onUnitKilled is run when a unit is killed in combat and onUnitLost is run for evey unit that is deleted in the game. So if a unit is killed in combat both onUnitKilled and then onUnitLost will be run.

However, I would use onCombatResult and check if the pWinner.getUnitType() is equal to gc.getInfoTypeForString(<missle tag>).
 
Okay, I'm back to looking at this problem again. Right now, my plan is to attack it like this:

Code:
	def onCombatResult(self, argsList):
		'Combat Result'
		pWinner,pLoser = argsList
		playerX = PyPlayer(pWinner.getOwner())
		unitX = PyInfo.UnitInfo(pWinner.getUnitType())
		playerY = PyPlayer(pLoser.getOwner())
		unitY = PyInfo.UnitInfo(pLoser.getUnitType())
		if (not self.__LOG_COMBAT):
			return
		if playerX and playerX and unitX and playerY:
			CvUtil.pyPrint('Player %d Civilization %s Unit %s has defeated Player %d Civilization %s Unit %s' 
				%(playerX.getID(), playerX.getCivilizationName(), unitX.getDescription(), 
				playerY.getID(), playerY.getCivilizationName(), unitY.getDescription()))
		if pWinner.getUnitClassType() == gc.getInfoTypeForString("UNITCLASS_BIOLOGICAL_WARFARE_MISSILE"):

I was going to use " if plot.isCity():" next, but I'm going to have to define "plot" first, and I don't see anything in the API indicating a way to grab the plot list number.

For instance, could I just use:

int getX ()

and

int getY ()

and have the game automatically know that I'm referring to the plot on which combat took place?
 
Unfortunately, this isn't working:

Code:
	def onCombatResult(self, argsList):
		'Combat Result'
		pWinner,pLoser = argsList
		playerX = PyPlayer(pWinner.getOwner())
		unitX = PyInfo.UnitInfo(pWinner.getUnitType())
		playerY = PyPlayer(pLoser.getOwner())
		unitY = PyInfo.UnitInfo(pLoser.getUnitType())
		if (not self.__LOG_COMBAT):
			return
		if playerX and playerX and unitX and playerY:
			CvUtil.pyPrint('Player %d Civilization %s Unit %s has defeated Player %d Civilization %s Unit %s' 
				%(playerX.getID(), playerX.getCivilizationName(), unitX.getDescription(), 
				playerY.getID(), playerY.getCivilizationName(), unitY.getDescription()))
		if pWinner.getUnitClassType() == gc.getInfoTypeForString("UNITCLASS_BIOLOGICAL_WARFARE_MISSILE"):
			pPlot = pLoser.plot()
			if pPlot.isCity():
				pPlot.changeExtraHealth(-5)

Oddly, I don't get a python exception or anything in the log, nothing happens at all.

I also tried it with what I thought to be the command used for the espionage poison water mission, changeEspionageHealthCounter().
 
Actually, I thought about, perhaps, a better way to do this -- give the Bio Missile an <iBombRate> of some value, and then have the Python script trigger the unhealthiness whenever the bombing mission is used.
 
From an SDK perspective, you can just do this in CvUnit::nuke()


Code:
CvCity* pCity = pPlot->getPlotCity();
if (pCity != NULL)
{
	pCity->changeExtraHealth(-2);
}

Just make sure to place it somewhere below the code that checks to see if the missile is intercepted.

Also, FYI, the health change is going to last the entire game. If you want the health effects to ever be curable, the best way to handle it is to create a whole new variable to store nuke health changes, and new accessor functions. You will also have to update the health parsing function n CvTextManager with an explanation of why the health has changed.
 
That is helpful, although it definitely needs to be curable -- akin to the espionage "Poison Water" mission.

In what file is the missile interception code located?
 
Nuke interception is part of CvUnit::nuke. It's the part that looks like the code below.

Code:
	if (GC.getGameINLINE().getSorenRandNum(100, "Nuke") < iBestInterception)
	{
		for (iI = 0; iI < MAX_PLAYERS; iI++)
		{
			if (GET_PLAYER((PlayerTypes)iI).isAlive())
			{
				szBuffer = gDLL->getText("TXT_KEY_MISC_NUKE_INTERCEPTED", GET_PLAYER(getOwnerINLINE()).getNameKey(), getNameKey(), GET_TEAM(eBestTeam).getName().GetCString());
				gDLL->getInterfaceIFace()->addMessage(((PlayerTypes)iI), (((PlayerTypes)iI) == getOwnerINLINE()), GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_NUKE_INTERCEPTED", MESSAGE_TYPE_MAJOR_EVENT, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
			}
		}

		if (pPlot->isActiveVisible(false))
		{
			// Nuke entity mission
			CvMissionDefinition kDefiniton;
			kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_NUKE).getTime() * gDLL->getSecsPerTurn());
			kDefiniton.setMissionType(MISSION_NUKE);
			kDefiniton.setPlot(pPlot);
			kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
			kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, this);

			// Add the intercepted mission (defender is not NULL)
			gDLL->getEntityIFace()->AddMission(&kDefiniton);
		}

		kill(true);
		return true; // Intercepted!!! (XXX need special event for this...)
	}


If I have some time this afternoon I'll give you an overview of how to make the condition curable.
 
Back
Top Bottom