getting a user selected plot?

davidlallen

Deity
Joined
Apr 28, 2008
Messages
4,743
Location
California
There are action buttons such as the recon mission. When you click the button, a radius is displayed as an overlay, and you can click to select any highlighted square. Then an action runs based on the square you clicked.

I want to add an action which is similar, except the user can click *anywhere* on the map. I can add the action button fine. But, what python can I write to let the user select a plot anywhere, and then return me the x,y location?

The idea is to allow a satellite recon mission, which can happen anywhere on the map. I'd rather not use an overlay with an absurdly high range (50?) but if the only way is with an overlay, that is better than nothing.

This is for Dune Wars, which is based on RevDCM and WOC lite, in case those packages provide some related code I can use.
 
You can set the interface mode (InterfaceModeTypes). I use this in the DotMapping tool in BUG. However, I can't immediately see how to do it modally as you want. In BUG, I set the mode which just changes the cursor and highlights the plot with a green square just like sign mode. However, I use a ScreenUtils class to track the mouse movement and receive mouse clicks.

I would look at setting it up like a nuke strike as TC01 suggested. It's handled entirely in the engine (no Python) AFAIK. Of course, that means the plot will be reported in the engine/SDK as well.
 
Several different civs in Dune Wars are likely to use the selection effect for different purposes, so I probably cannot simply mod onNukeExplosion. Thanks for the reminder about GOO; I have traced through its messy changes to CvMainInterface before to learn other special effects like python action buttons. I will trace through for the GOO meteor strike.
 
I was trying to figure out this as well, and I think I have. So below is how it works, using the Priest of Ki (Earthquake) effect as an example. First, it has code for each button that is pretty similar in the Main Interface file.

Code:
# Priest of Ki button pressed
if (inputClass.getNotifyCode() == 11 and inputClass.getData1() == 666 and inputClass.getData2() == 666):
	GodsOfOld.iPushButton = 1
	GodsOfOld.iPushedButtonUnit = g_pSelectedUnit
	CyInterface().setInterfaceMode(InterfaceModeTypes.INTERFACEMODE_PYTHON_PICK_PLOT)

Note: The "GodsOfOld.py" file consists of only the code below:

Code:
iPushButton = 0
iPushedButtonUnit = 0

Using this code causes the "onPlotPicked" function in the Event Manager (code nearly identical for each function) to execute:

Code:
# Prophet of Ki
if GodsOfOld.iPushButton == 1:
	pUnit = GodsOfOld.iPushedButtonUnit
			
	iMessageID = self.m_iNetMessage_ProphetKi
	iPlotX = pPlot.getX()
	iPlotY = pPlot.getY()
	iOwner = pUnit.getOwner()
	iUnitID = pUnit.getID()
			
	pUnitOwner = gc.getPlayer(iOwner)
	if pUnitOwner.isTurnActive( ):

		CyMessageControl( ).sendModNetMessage( iMessageID, iPlotX, iPlotY, iOwner, iUnitID )

And then, this causes the game to run the code below in onModNetMessage, which is called whenever the CyMessageControl().sentModNetMessage() code is ran:

Code:
#Prophet of Ki Effect
if ( iMessageID == self.m_iNetMessage_ProphetKi ):
			
	iPlotX = iData2
	iPlotY = iData3
	iOwner = iData4
	iUnitID = iData5
		
	pPlot = CyMap( ).plot( iPlotX, iPlotY )					
	pPlayer = gc.getPlayer( iOwner )
	pUnit = pPlayer.getUnit( iUnitID )

	self.doEventEarthquake(pPlot)
	pUnit.kill( 0, -1 )
	if pPlot.getOwner( ) >= 0:		
		if not ( gc.getPlayer( pPlot.getOwner( ) ).isHuman( ) ):
			gc.getPlayer( pPlot.getOwner()).AI_changeAttitudeExtra( iOwner, -5 )
			szAttackedCiv = gc.getPlayer( pPlot.getOwner()).getCivilizationDescriptionKey()
			szAttackingCiv = pPlayer.getCivilizationDescriptionKey()
			szTitle = localText.getText("TXT_KEY_GODS_DISASTER_ANGER", (szAttackedCiv , szAttackingCiv, ))
			CyInterface().addImmediateMessage( szTitle , None)
		else:
			szAttackedCiv = gc.getPlayer( pPlot.getOwner()).getCivilizationDescriptionKey()
			szAttackingCiv = pPlayer.getCivilizationDescriptionKey()
			szTitle = localText.getText("TXT_KEY_GODS_DISASTER_HUMAN", (szAttackedCiv , szAttackingCiv, ))

(note: code non-indented for readability.)
 
Thanks, that is working. There are two things I cannot figure out; one may be easy, one hard.

Easy (maybe): one effect I am trying to get is basically a teleport, where a unit instantly arrives at the selected location. Is there a way I can disallow selecting a plot, where the unit cannot move legally? For example, I would like to prevent teleporting a worker onto an ocean tile. It immediately drowns.

Hard: I can only select units where I have a spotter, ie, I cannot select plots in fog of war. This may be OK for the design that I want. But, is there an alternate way (not using nukeExplosion) where I could select plots in fog of war? If I select plots I have never even explored, I suppose I accept the risk of teleporting the unit into the ocean and drowning.
 
Easy (maybe): one effect I am trying to get is basically a teleport, where a unit instantly arrives at the selected location. Is there a way I can disallow selecting a plot, where the unit cannot move legally? For example, I would like to prevent teleporting a worker onto an ocean tile. It immediately drowns.

Afterworld uses code like this in onPlotPicked to control the range a plot can be picked. So you could write a "if not pPlot.isMountain" or "if not pPlot.isWater()" check here, and add in any other plot types you can't move onto.

Sorry, but I have no idea about the other question.
 
BUG's DotMapping tool uses a CvScreenUtils class to track the mouse movement and clicks. Doing so, it is able to detect clicks on unrevealed tiles. You could do something similar. Note that you don't have to draw anything on-screen to use screen utils--it's more about intercepting all mouse actions.
 
Afterworld uses code like this in onPlotPicked to control the range a plot can be picked. So you could write a "if not pPlot.isMountain()" or "if not pPlot.isWater()" check here, and add in any other plot types you can't move onto.

I tried that. What happens is that if I click on an invalid target, nothing happens. So I can keep clicking until I click on a workable tile, but there is no feedback about why nothing happened on the click.

Can anybody suggest a way to grey out the highlight when the target is invalid? This is how the vanilla paradrop works.

EmperorFool said:
BUG's DotMapping tool uses a CvScreenUtils class to track the mouse movement and clicks.

That sounds complicated, but it may be workable. I will investigate.
 
I tried that. What happens is that if I click on an invalid target, nothing happens. So I can keep clicking until I click on a workable tile, but there is no feedback about why nothing happened on the click.

Can anybody suggest a way to grey out the highlight when the target is invalid? This is how the vanilla paradrop works.

That's also how it works in Afterworld, though... I think. You'd have to hunt through the AW Python to figure out how.


Actually, I believe there's a GameUtils function called "canPickPlot" or something. But whether it means you can actually pick a plot or whether a picked plot is valid I don't know.
 
The SDK calls canPickPlot() from CvGameInterface::getPlotHighlightColor() only if the plot is revealed to the active team. If you want to avoid requiring a custom DLL, you'll have to go the screen utils route.
 
canPickPlot does exactly what I wanted for the "Easy" part. If canPickPlot returns false, the plot is not highlighted and clicking does nothing. There is no way for the user to find out why they cannot click there, but the lack of highlighting makes it clear where something will happen and where it will not. I think that is good enough.

I will try to find some leads on the "Hard" part, getting a callback for any plot even if never explored.
 
Has anybody dug around onNukeExplosion? I can see the event, but I cannot see any related hooks in python or the sdk code. It is handy that the event gets passed the causing unit, so I can make the behavior depend on that. I cannot see offhand how to change the button for different units, or how to change the actual effect. One example would be the teleporter I mentioned earlier. My original request was for a satellite mission for recon. It turns out one good way to do that is to use the plot picked to put down an invisible unit with a duration, that will naturally uncover fog of war.

But I need to change the button and the effect. Any pointers?

EDIT: Duh, start out by spelling the event correctly. It's nukeExplosion, called in CvPlot.cpp and other places. But I still do not see how the button or effect hooks in.
 
I suspect that you want to look at the various interface modes. Python Pick Plot is just one such mode, but there's one for airdrop, rebase, nuke, recon, etc. Each one is associated with an action. To add a new one you need to at least add it to XML but I think also to the SDK.

For an example, check out Pep's Extra Sentry Actions mod. It is an SDK mod that adds a few additional sentry-related actions, and one is "Go To and Sentry" which makes the unit act like a sentry while moving to a new plot. It has a different button, action, and interface mode plus SDK code to handle the action.
 
Thanks for the suggestion. I have looked through the interface modes. It is too bad that the canPick callback results in turning an invalid plot to "no color" instead of dark grey like all the other interface modes do; it would be nicer if the plot selection interface worked exactly the same in a python routine as it does in a vanilla routine such as paradrop.

But, I have been picking through the interface for nukes, and I am stuck. I want to see if it is possible to use a different action button for some units with a nukerange, and a different effect. I can't see how the nuke button or effect gets associated with units having a nukerange. The nuke button is interface/buttons/actions/nuke.dds, or in the specialists atlas at 7,2 and I cannot find either one referenced anywhere in sdk, python or xml. The nuke effect is called EFFECT_ICBM_NUCLEAR_EXPLOSION but I cannot find that one used anywhere either.

Does anybody know how the nuke button and effect are bound in?
 
Thanks for the suggestion. I have looked through the interface modes. It is too bad that the canPick callback results in turning an invalid plot to "no color" instead of dark grey like all the other interface modes do; it would be nicer if the plot selection interface worked exactly the same in a python routine as it does in a vanilla routine such as paradrop.

But, I have been picking through the interface for nukes, and I am stuck. I want to see if it is possible to use a different action button for some units with a nukerange, and a different effect. I can't see how the nuke button or effect gets associated with units having a nukerange. The nuke button is interface/buttons/actions/nuke.dds, or in the specialists atlas at 7,2 and I cannot find either one referenced anywhere in sdk, python or xml. The nuke effect is called EFFECT_ICBM_NUCLEAR_EXPLOSION but I cannot find that one used anywhere either.

Does anybody know how the nuke button and effect are bound in?

You can add a different nuke effect (though it will use the same button) by adding a "if pNukeUnit.getUnitType() ==" statement in onNukeExplosion, which will do your effect. That can presumably be tied to a button.

For instance, if you want to do something but don't want Fallout, you can add code like this:

Code:
if pNukeUnit.getUnitType() == gc.getInfoTypeForString('UNIT_NO_FALLOUT'):
	iX = pPlot.getX()
	iY = pPlot.getY()
	for iiX in range(iX-1, iX+2, 1):
		for iiY in range(iY-1, iY+2, 1):
			pNukedPlot = CyMap().plot(iiX,iiY)
			if pNukedPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_FALLOUT'):
				pNukedPlot.setFeatureType(-1, -1)

I know the "nuke feature" is listed in GlobalDefines.xml:

Code:
<Define>
	<DefineName>NUKE_FEATURE</DefineName>
	<DefineTextVal>FEATURE_FALLOUT</DefineTextVal>
</Define>

And "Nuke Range" is defined in the XML too, as seen in UNIT_ICBM. However, I have no idea what a "1" there means, it clearly doesn't just mean one plot radius.

Code:
<iAirRange>4</iAirRange>
<iAirUnitCap>0</iAirUnitCap>
<iDropRange>0</iDropRange>
<iNukeRange>1</iNukeRange>
 
You can add a different nuke effect (though it will use the same button) by adding a "if pNukeUnit.getUnitType() ==" statement in onNukeExplosion, which will do your effect.

I meant a different graphical effect, as opposed to the brightly glowing nuclear mushroom cloud. I agree that your method allows a different game impact such as maybe adding mutant cows, but that occurs after the graphical effect happens. I was hoping to skip/modify the graphical effect itself.
 
Back
Top Bottom