pUnit.convert() loses move mission?

davidlallen

Deity
Joined
Apr 28, 2008
Messages
4,743
Location
California
I want to have a unit which looks different, when it moves in certain terrain. I have this partly working; I use another unit with its own artdef, and I hook onUnitMove to call pUnit.convert(). In the following complete standalone example, I convert Knight to its related unit Cataphract, whenever the knight moves off grass. And the Cataphract converts back to knight when it moves onto grass.
Code:
	def Convert(self, pUnit, iNewType):
		ix = pUnit.getX() ; iy = pUnit.getY()
		iOwner = pUnit.getOwner()
		pOwner = gc.getPlayer(iOwner)
		dir = DirectionTypes(pUnit.getFacingDirection())
	        pNew = pOwner.initUnit(iNewType, ix, iy, \
	        	UnitAITypes.NO_UNITAI, dir)
		pNew.convert(pUnit)
		pUnit.kill(true, iOwner)

	def onUnitMove(self, argsList):
		pPlot,pUnit,pOldPlot = argsList
		iUnit = pUnit.getUnitType()
		iTerr = pPlot.getTerrainType()
		iUnit1 = gc.getInfoTypeForString("UNIT_BYZANTINE_CATAPHRACT")
		iUnit2 = gc.getInfoTypeForString("UNIT_KNIGHT")
		iTerr1 = gc.getInfoTypeForString("TERRAIN_GRASS")
		if iUnit == iUnit1 and iTerr == iTerr1: 
			self.Convert(pUnit, iUnit2)
		elif iUnit == iUnit2 and iTerr != iTerr1:
			self.Convert(pUnit, iUnit1)

This works, as long as I move the unit only one plot. If I move the unit two plots, and the first plot causes a change, the unit forgets its mission and stops. How do I keep the mission for the unit?

In case you are curious, the actual application is in Dune Wars. When a Fremen unit moves into the desert, it automatically attracts a sandworm to ride. We have the graphics for a sandworm rider; I just need to make it appear and disappear on demand.
 
I have modified the example slightly to show two bigger problems. I have attached a tiny mod which has onUnitMove in CvEventManager.py, and I have made a copy of the Scout unit called "Scout Grass". The idea is the same; scout converts to a "scout grass" on grass and back to scout when it leaves grass.

1. As above, it loses the mission information. This turns out to be the least important problem.
2. When the scout is on plains, and I move it across grass to another plains, I get a free unit! A copy of the original scout is left behind on the grass square it moved through. Cool, but not what I wanted. It seems the kill may not be taking effect, but the new unit is created.
3. I used the scout as the basis because it is a common early game unit. When I autoplay with this mod (using CyGame().setAIAutoPlay, since it is a tiny vanilla mod) the game hangs. This is reproducible; start any game (I use large pangaea for maximum scouting) and autoplay 100 turns. It will hang somewhere in there. Change the python so that the Convert function just returns, and it will not hang.

Is there any good way to get unit art swapping to work with onUnitMove?
 
cvunit::convert already kills the old unit, try to remove the punit.kill, might help.I guess you could save the last mission of a punit and push the mission to the pNew after converting.
 
I am not sure that pUnit.convert() also does kill. I have always added the kill afterwards. In this particular example, there are "free" units generated anyway, so it is hard to tell if removing the kill makes any difference.

Is there any predefined way to copy the mission queue? I can call pSelgroup.getMission() and then find out the type, data1, data2 parameters; but to call pushMission(), I also need a bunch of other arguments like MissionAIType eMissionAI, CyPlot pMissionAIPlot, CyUnit pMissionAIUnit. I do not know where to get these. Also, there may be many missions on the queue, and I guess it is necessary to loop all of them and copy. Of course I will have to be sure to traverse backwards so that when I push each one, they end up in the right place.
 
Since the mission queue is attached to the CvSelectionGroup, can't you do the following?

  1. Create new unit
  2. Convert it
  3. Add it to the old unit's group
  4. Kill the old unit, removing it from the gorup
That way you don't need to copy the mission queue. As long as converting the unit doesn't kill the old one it should work.
 
well, this is the last line from cvunit::convert

pUnit->kill(true);

but if you always use it that way it's probably not the cause of your problems.

In some cases the AI will force seperate groups if a group is joined (cvunit::joingroup) , this might also cause issues here.
 
I suppose the following sequence might work:
1. Get the old unit's selection group
2. init the new unit
3. Add the new unit to the selection group
4. convert the new unit (which kills)

I assume that regardless of whether the killed unit is the head of the selection group or not, the mission queue will still be kept for other units in the group.

The only problem is I cannot find a python function for #3.
 
Is it possible to alter the code that makes CIV4UnitArtStyleTypeInfos.xml work. Example XML from that file:

Code:
        <StyleUnit>
            <UnitType>UNIT_ARCHER</UnitType>
            <UnitMeshGroup>
                <EarlyArtDefineTag>ART_DEF_UNIT_ARCHER_EURASIAN</EarlyArtDefineTag>
                <LateArtDefineTag>ART_DEF_UNIT_ARCHER_EUROPEAN</LateArtDefineTag>
                <MiddleArtDefineTag> ART_DEF_UNIT_ARCHER_EUROPEAN</MiddleArtDefineTag>
            </UnitMeshGroup>
        </StyleUnit>

This allows the same unit to have different art dependent on Era. Presumably, there must be some code that checks what era it is and uses different unit art dependent on that. Perhaps, for Dune Wars, since we do not care about have unit art change with this eras we could make this check depend on promotions. I gather that the Fall Further code changes the art style with promotion - perhaps this works in a similar way. Perhaps this won't help with changing the unit art on the fly, it's just a thought I had.
 
Perhaps, for Dune Wars, since we do not care about have unit art change with this eras we could make this check depend on promotions.

That is a good suggestion, which simplifies a lot of things. I had previously experimented with hijacking the Siege Tower, like Maniac does in planetfall, but that art is *added* to the original art instead of replacing it. However, Maniac has done this by modding CvUnit::setXY, which is an interesting choice because it happens at a very low level in the engine so it is very efficient. It may be that I can swap there, or in related CvUnit::move(), and then use that to set the era for the unit. The swapping would happen for all units which have the existing DW promotion SandRider; we do not have to add a temporary promo just to track whether the unit is on ocean.
 
Back
Top Bottom