Auto Repeat Unit Recon Mission

OrionVeteran

Deity
Joined
Dec 25, 2003
Messages
2,443
Location
Newport News VA
I'd like to automate the recon mission for an air unit, without having to push the recon button on every turn. Why? Because I'm lazy and want to speed up my turns.

Goal: Auto repeat air recon mission to specified plot, until mission is cancelled.

Problem: How can I make an air unit auto repeat a recon mission to the same specified plot on every turn?

Where should this function be executed from?

unfinished code:
Code:
def doReconMission(pUnit, destinationPlot):
	if pUnit.canRecon(pUnit.plot()):
		pUnit.getGroup().pushMission(MissionTypes.MISSION_RECON, destinationPlot.getX(), destination.getY(), 0, False, True, MissionAITypes.NO_MISSIONAI, pUnit.plot(), pUnit)

Any ideas would be greatly appreciated.

Sincerely,
 
Those recon missions are indeed annoying to deal with.

I guess that you would need to have a button pushed on/off in the MainInterface. Which implies passing by onModNetMessage in the CvEventManager if you want to avoid OOS errors in MP games. Edit: or maybe not as it has no other implication in the game than revealing plots for yourself.

I remember this tutorial on how to make Python Action Buttons. Well, you know how to do it but it might give you additional ideas.

Edit: the tutorial uses the function 'canPickPlot' in GameUtils and 'onPlotPicked' in CvEventManager.

Perhaps the mission itself could better be coded in the SDK as it is a repetition of an existing mission.
 
I think the mission would have to be repeated at the beginning of each turn; end of turn isn't helpful. Not sure if that complicates matters. The new mission could replace the fairly useless Explore mission.
 
Beginning of turn code:

Sure, I could create a button that loops through all existing aircraft in a city and sends them out in different directions to their max range. The number of aircraft could be limited by a max integer. But this process won't identify one specific aircraft to repeatedly perform a single plot recon mission automatically every turn.

Yes. I want my cake and eat it too. :D
 
Getting closer to a solution.

The function AI_exploreAir() automates the plot selection, as it determines the best plot.

Code:
getGroup()->pushMission(MISSION_RECON, pBestPlot->getX(), pBestPlot->getY());

I want to be able to manually select the plot once and then have the air unit perform the automated recon mission to only that selected plot on each turn.

It is like an automated plot patrol. I might be able to do it by giving the air unit a directional promotion and remove the promotion when I want to cancel the mission.

Is there a way to store a rally point plot or something similar, against a single unit? Selecting the plot is far superior to a directional mission.

Note: There was an old game called Empire for Windows that could identify a plot to send an air unit and the air unit would continuously go to the same plot on every turn until the orders were changed.
 
If you are willing to modify the dll code you could update CvUnit and duplicate the getReconPlot and setReconPlot functions and associated variables and expose these to Python. You could set the new variables (m_iLastReconX / m_iLastReconY) in the existing setReconPlot function. Your Python code would then reference the new getLastReconPlot() function when setting the mission target.

One issue with this is that it would break any save game as you would need to read/write the m_iLastReconPlotX/Y variables to the save in a similar way to the existing m_iReconPlotX/Y vars.

==============
EDIT
==============
Forget that, looking at the dll, there is a lastMissionPlot() method that is exposed to Python via the selection group

PHP:
CyPlot* CySelectionGroup::lastMissionPlot()

That may do what you are looking for without any changes
 
Would this work? ...or would patrolPlot blow up on me?

Code:
bool CvUnit::airReconPlot()
{
	CvPlot* patrolPlot;
	
	patrolPlot = getGroup()->lastMissionPlot();
	
	int iX = patrolPlot->getX_INLINE()
	int iY = patrolPlot->getX_INLINE()
		
	if (!canReconAt(plot(), iX, iY))
	{
		return false;
	}

	setReconPlot(patrolPlot);

	finishMoves();

	...

Also. How can we determine if the lastMissionPlot was a recon mission plot?
 
Looking in a bit more detail at the lastMissionPlot() function I am not convinced that it will actually work for a MISSION_RECON, in which case you are back to storing the info yourself.

If you look at CvUnit::doTurn() that is where the recon plot is cleared so we would need an alternative way to store it that persists across turns.... which leads me back to my first suggestion of creating an additional pair of variables m_iLastReconX & m_iLastReconY and setting them in the CvUnit::setReconPlot(CvPlot* pNewValue) method. You would then duplicate the getReconPlot() function to get the last recon plot.

I'm also not convinced that you need a new airReconPlot() method, why can't you call the existing recon() method with the stored plot X/Y values? Somewhere would need to call your new method so that place could also call the existing recon method.
 
Looking in a bit more detail at the lastMissionPlot() function I am not convinced that it will actually work for a MISSION_RECON, in which case you are back to storing the info yourself.

If you look at CvUnit::doTurn() that is where the recon plot is cleared so we would need an alternative way to store it that persists across turns.... which leads me back to my first suggestion of creating an additional pair of variables m_iLastReconX & m_iLastReconY and setting them in the CvUnit::setReconPlot(CvPlot* pNewValue) method. You would then duplicate the getReconPlot() function to get the last recon plot.

I'm also not convinced that you need a new airReconPlot() method, why can't you call the existing recon() method with the stored plot X/Y values? Somewhere would need to call your new method so that place could also call the existing recon method.


I want to take this one step at a time. I have created the new variables in the following:

Spoiler :


Code:
pStream->Read(&m_iLastReconX);
pStream->Read(&m_iLastReconY);

Code:
pStream->Write(m_iLastReconX);
pStream->Write(m_iLastReconY);

Code:
m_iLastReconX = INVALID_PLOT_COORD;
m_iLastReconY = INVALID_PLOT_COORD;

Code:
CvPlot* CvUnit::getLastReconPlot() const;

Code:
int CvUnit::getLastReconPlot() const
{
	return GC.getMapINLINE().plotSorenINLINE(m_iLastReconX, m_iLastReconY);
}

Code:
CyPlot* getLastReconPlot();

Code:
CyPlot* CyUnit::getLastReconPlot()
{
	return m_pUnit ? new CyPlot(m_pUnit->getLastReconPlot()) : NULL;
}

Code:
.def("getLastReconPlot", &CyUnit::getLastReconPlot, python::return_value_policy<python::manage_new_object>(), "CyPlot* ()")



If this is OK, then let's move on. What's next?

Note: I want to be able to call the auto recon mission to execute continuous visits to the same plot every turn. Then I need a way to turn off the auto recon mission for that unit. I have already created an icon to be used for turning on auto recon for an air unit. ...no code behind it yet.
 
What's next?
You need to set the variables so I would create a setter function and add a call to it from the existing setReconPlot function:
Spoiler :
PHP:
void CvUnit::setLastReconPlot(CvPlot* pNewValue)
{
	CvPlot* pOldPlot = getLastReconPlot();
	if (pOldPlot != pNewValue)
	{
		if (pNewValue == NULL)
		{
			m_iLastReconX = INVALID_PLOT_COORD;
			m_iLastReconY = INVALID_PLOT_COORD;
		}
		else
		{
			m_iLastReconX = pNewValue->getX_INLINE();
			m_iLastReconY = pNewValue->getY_INLINE();
		}
	}
}

PHP:
void CvUnit::setReconPlot(CvPlot* pNewValue)
{
	CvPlot* pOldPlot = getReconPlot();
	if (pOldPlot != pNewValue)
	{
		if (pOldPlot != NULL)
		{
			pOldPlot->changeAdjacentSight(getTeam(), GC.getDefineINT("RECON_VISIBILITY_RANGE"), false, this, true);
			pOldPlot->changeReconCount(-1); // changeAdjacentSight() tests for getReconCount()
		}

		if (pNewValue == NULL)
		{
			m_iReconX = INVALID_PLOT_COORD;
			m_iReconY = INVALID_PLOT_COORD;
		}
		else
		{
			m_iReconX = pNewValue->getX_INLINE();
			m_iReconY = pNewValue->getY_INLINE();

			setLastReconPlot(pOldPlot ); // <===== New line here
			
			pNewValue->changeReconCount(1); // changeAdjacentSight() tests for getReconCount()
			pNewValue->changeAdjacentSight(getTeam(), GC.getDefineINT("RECON_VISIBILITY_RANGE"), true, this, true);
		}
	}
}

I want to be able to call the auto recon mission to execute continuous visits to the same plot every turn.
I don't do Python coding so for me this would involve a new mission type which is a non-trivial exercise. Below are the notes I have for creating a new one, you would be able to reuse the majority of the code from the MISSION_RECON mission so it wouldn't be as big a job as creating a brand new mission from scratch:
Spoiler :
Adding a new mission

==================
MISSION
==================

XML/Units/CIV4MissionInfos.xml
+++++++++++++++++++++++
Add entry

XML/Text/Archid_Mission_CIV4GameText.xml
+++++++++++++++++++++++
Add text entries for the description & help

Art/Interface/Buttons/Actions/<mission>.dds
+++++++++++++++++++++++
Create the button for the action


CvEnums.h
++++++++++++++
Add entry to the following enum, ensuring the same order as the XML above
enum MissionTypes { ... }


CvDLLWidgetData.cpp
+++++++++++++++

parseActionHelp()
-----------------
Add an entry here to add the text for the action


CyEnumsInterface.cpp
+++++++++++++++++++++

CyEnumsPythonInterface()
------------------------
Add mission to python::enum_<MissionTypes>("MissionTypes")


CvGameCoreUtils.cpp
+++++++++++++++++++++

getMissionTypeString()
-----------------------
Add mission to list


CvUnit (cpp & h)
++++++++++++++++++++++

Create a method to test whether the mission can be performed (return bool)
Create a method to perform the mission (return true if mission was completed, false otherwise)


CvSelectionGroup.cpp
++++++++++++++++++++++

lastMissionPlot()
----------------------
Add mission to the switch statement

canStartMission()
----------------------
Add mission to the switch statement indicating whether the mission can be started.

startMission()
----------------------
Add mission to the initial switch statement to do any setup actions
Add mission to the second switch statement to perform the mission

continueMission()
----------------------
Add mission to the initial switch statement
Add mission to the second switch statement


==================
MISSIONAI
==================

CvEnums.h
++++++++++++++++++
Add mission ai to MissionAITypes enum


CvGameCoreUtils.cpp
+++++++++++++++++++

getMissionAIString()
-----------------------
Add mission AI to switch statement


CyEnumsInterface.cpp
++++++++++++++++++++

CyEnumsPythyonInterface()
-------------------------
Add value to MissionAITypes enum

xxxAI.cpp
------------------------
Used when pushing MISSION_MOVE_TO to get to real mission plot. CvUnitAI::AI_greatWork() is a simple example where it is used in 2 places.


Then I need a way to turn off the auto recon mission for that unit.
Give it another mission, or cancel the action the way you would if the unit were in the middle of a Goto Plot mission

Alternatively the dll mission may be better based around something as simple as setting a boolean variable that can be used to prevent the call to setReconPlot(NULL) in CvUnit::doTurn() rather than copying all the MISSION_RECON stuff, but I don't have time at present to investigate it in that much detail. I am just throwing out the quick response stuff.

From your posts earlier in the thread I though the piece you were missing from the puzzle was being able to store and retrieve the last mission plot and not the complete implementation details. It may be simpler, if slower, to do a lot of this stuff in Python but that's not my area so there are better people than I to comment on that front.
 
I had partial results:

1. I created buttons to turn on/off Auto recon missions by giving/removing a promotion (AirReconPlot).
2. I set the last recon plot as we discussed previously in the SDK.
3. I set up a scenario with 3 aircraft in a city.
4. I wrote a Python function to loop through all the units to find all aircraft with the AirReconPlot promotion and send them out.
5. Results: Only one aircraft auto deployed. The other two did not move.

It looks like I'm going to need a table with data fields to store:

1. UnitID (integer)
2. iCityPlotX (integer)
3. iCityPlotY (integer)
4. iReconPlotX (integer)
5. iReconPlotY (integer)
6. bAutoRecon (True/False)
7. iPlayerID (integer)

Then I can have the SDK refer to the table data to execute an auto recon for each city.

I'm not sure how I'm going to do this. Do I use an Access database file (*.mdb) file?

This is new territory for me.
 
Why would you need the city data when you have the unit id?
Furthermore, not all planes have to be stationed in cities.
 
Why would you need the city data when you have the unit id?
Furthermore, not all planes have to be stationed in cities.


Sure. I can get the plot where the unit resides, usually a city.

My problem is creating a table to hold multiple lines of data in C++ or an external table.

Have you got an example of how this can be done?
 
Edit: Oh, Archid had already figured all this out. Sorry.

Looks like CvUnit already stores the latest recon target -- there are data members m_iReconX and m_iReconY with (Python-exposed) setter and getter:
Code:
[COLOR="Gray"]CvPlot* CvUnit::getReconPlot() const
void setReconPlot(CvPlot* pNewValue)[/color]
m_iReconX and m_iReconY are stored in savegames too (CvUnit::read, CvUnit::write). I think this is so that the explored plots remain visible when the map is updated (CvMap::updateSight), in particular during the turns of other civs. CvUnit::doTurn() resets the recon plot each turn (to INVALID_PLOT_COORD). If the reset is skipped for units on auto-recon, it should be OK to store the recon plot using setReconPlot (and access it with getReconPlot).

Then, the loop that sends units on recon could start like this:
Code:
for all units u
  if u does not have the auto-recon promotion, continue [do nothing]
  unitPlot = u->plot() [items 2 and 3 in the proposed table]
  reconPlot = u->getReconPlot() [items 4 and 5]
  playerId = u->getOwner() [item 7]
So, I think it's all covered without storing additional data. Well, the dummy promotion is stored, but that's implicit. On that note, one could add a boolean bAutoRecon to CvUnit instead of using a promotion.[/COLOR]
 
Looks like CvUnit already stores the latest recon target -- there are data members m_iReconX and m_iReconY with (Python-exposed) setter and getter:
Code:
CvPlot* CvUnit::getReconPlot() const
void setReconPlot(CvPlot* pNewValue)
m_iReconX and m_iReconY are stored in savegames too (CvUnit::read, CvUnit::write). I think this is so that the explored plots remain visible when the map is updated (CvMap::updateSight), in particular during the turns of other civs. CvUnit::doTurn() resets the recon plot each turn (to INVALID_PLOT_COORD). If the reset is skipped for units on auto-recon, it should be OK to store the recon plot using setReconPlot (and access it with getReconPlot).

Then, the loop that sends units on recon could start like this:
Code:
for all units u
  if u does not have the auto-recon promotion, continue [do nothing]
  unitPlot = u->plot() [items 2 and 3 in the proposed table]
  reconPlot = u->getReconPlot() [items 4 and 5]
  playerId = u->getOwner() [item 7]
So, I think it's all covered without storing additional data. Well, the dummy promotion is stored, but that's implicit. On that note, one could add a boolean bAutoRecon to CvUnit instead of using a promotion.

I created new holders called:

m_iLastReconX
m_iLastReconY

The Recon function updates the unit with the current recon mission plot x/y coordinates.
But it seems these fields hold only one set of coordinates and not a set of coordinates for each air unit. A test moved only the first air unit, when I looped through all the air units in Python and executed a recon mission for each unit based upon the last Recon plot coordinates.

So now I am looking to create a persistent table that I can add/delete data and records. Then I will reference that table to get the exact unit data to execute auto recon missions from multiple cities deploying multiple air units to different specified plot coordinates around each city. Ever wonder why I spend so much time writing code? This project is a perfect example. :crazyeye:
 
But it seems these fields hold only one set of coordinates and not a set of coordinates for each air unit.

Those variables are stored in the unit structure so each air unit has its own set ... or you should be storing them with the unit in the CvUnit.h/.cpp files.

If it looks like only one unit is performing the auto-recon then I would look to the code that loops through the units to make sure that it is being called for each unit and not just the first one multiple times.
 
Sorry to necro this thread but was there ever a firm solution crafted for this problem?
 
Top Bottom