• Our friends from AlphaCentauri2.info are in need of technical assistance. If you have experience with the LAMP stack and some hours to spare, please help them out and post here.

Help Adding A Unit Mission

LyTning94

Dragonborn
Joined
Nov 10, 2010
Messages
397
Location
Skyrim
I created a mission in CIV4MissionInfos.xml, added it to CvEnums.h, CvSelectionGroup.cpp, CvGameCoreUtils.cpp, and CyEnumsInterface.cpp. I based my code off the other missions I found there.

When I try to load my mod, however, as soon as the game starts initializing, it crashes. I tried to debug it, but it says:

No symbols are loaded for any call stack frame. The source code cannot be displayed.

The top item in the call stack box is Civ4BeyondSword.exe, and I am assuming the symbols aren't loaded because it is crashing in the .exe not the .dll.

What could be causing this crash? I'm assuming I did something which makes the .dll not compatible with the .exe, but I have no idea what this could be.

Here's my code:

Spoiler :
In CvEnums.h:

Code:
enum MissionTypes				// Exposed to Python
{
	NO_MISSION = -1,

	MISSION_MOVE_TO,
        ...
        MISSION_MULTI_DESELECT,

	/********************************************************************************/
	/* LyTning94														1/14/11		*/
	/* Begin Assassins! Code														*/
	/********************************************************************************/

	MISSION_DIPLOMACY,
	
	/********************************************************************************/
	/* End Assassins! Code															*/
	/********************************************************************************/

#ifdef _USRDLL
	NUM_MISSION_TYPES
#endif
};

In CvSelectionGroup.cpp:

Code:
CvPlot* CvSelectionGroup::lastMissionPlot()
{
	CLLNode<MissionData>* pMissionNode;
	CvUnit* pTargetUnit;

	pMissionNode = tailMissionQueueNode();

	while (pMissionNode != NULL)
	{
		switch (pMissionNode->m_data.eMissionType)
		{
		case MISSION_MOVE_TO:
		case MISSION_ROUTE_TO:
			return GC.getMapINLINE().plotINLINE(pMissionNode->m_data.iData1, pMissionNode->m_data.iData2);
			break;
                ...
		case MISSION_DIE_ANIMATION:

		/********************************************************************************/
		/* LyTning94														1/14/11		*/
		/* Begin Assassins! Code														*/
		/********************************************************************************/

		case MISSION_DIPLOMACY:

		/********************************************************************************/
		/* End Assassins! Code															*/
		/********************************************************************************/

			break;

		default:
			FAssert(false);
			break;
		}

		pMissionNode = prevMissionQueueNode(pMissionNode);
	}

	return plot();
}

Code:
bool CvSelectionGroup::canStartMission(int iMission, int iData1, int iData2, CvPlot* pPlot, bool bTestVisible, bool bUseCache)
{
	...
	while (pUnitNode != NULL)
	{
		pLoopUnit = ::getUnit(pUnitNode->m_data);
		pUnitNode = nextUnitNode(pUnitNode);

		switch (iMission)
		{
		case MISSION_MOVE_TO:
			if (!(pPlot->at(iData1, iData2)))
			{
				return true;
			}
			break;

		...
		case MISSION_DIE_ANIMATION:
			return false;
			break;
		
		/********************************************************************************/
		/* LyTning94														1/14/11		*/
		/* Begin Assassins! Code														*/
		/********************************************************************************/

		case MISSION_DIPLOMACY:
			if (pLoopUnit->canBoostDiplomacy(pPlot))
			{
				return true;
			}
			break;

		/********************************************************************************/
		/* End Assassins! Code															*/
		/********************************************************************************/

		case MISSION_BEGIN_COMBAT:
		case MISSION_END_COMBAT:
		case MISSION_AIRSTRIKE:
		case MISSION_SURRENDER:
		case MISSION_IDLE:
		case MISSION_DIE:
		case MISSION_DAMAGE:
		case MISSION_MULTI_SELECT:
		case MISSION_MULTI_DESELECT:
			break;

		default:
			FAssert(false);
			break;
		}
	}

	return false;
}

Code:
void CvSelectionGroup::startMission()
{
	...
		switch (headMissionQueueNode()->m_data.eMissionType)
		{
		case MISSION_MOVE_TO:
		...		
                case MISSION_DIE_ANIMATION:
		
		/********************************************************************************/
		/* LyTning94														1/14/11		*/
		/* Begin Assassins! Code														*/
		/********************************************************************************/

		case MISSION_DIPLOMACY:

		/********************************************************************************/
		/* End Assassins! Code															*/
		/********************************************************************************/

			break;

		default:
			FAssert(false);
			break;
		}

		if ( bNotify )
		{
			NotifyEntity( headMissionQueueNode()->m_data.eMissionType );
		}

		pUnitNode = headUnitNode();

		while (pUnitNode != NULL)
		{
			pLoopUnit = ::getUnit(pUnitNode->m_data);
			pUnitNode = nextUnitNode(pUnitNode);

			if (pLoopUnit->canMove())
			{
				switch (headMissionQueueNode()->m_data.eMissionType)
				{
				case MISSION_MOVE_TO:
				...
				case MISSION_DIE_ANIMATION:
					bAction = true;
					break;

				/********************************************************************************/
				/* LyTning94														1/14/11		*/
				/* Begin Assassins! Code														*/
				/********************************************************************************/

				case MISSION_DIPLOMACY:
					if (pLoopUnit->boostDiplomacy())
					{
						bAction = true;
					}
					break;

				/********************************************************************************/
				/* End Assassins! Code															*/
				/********************************************************************************/					

				...			
                        }
		}
	}
}

Code:
void CvSelectionGroup::continueMission(int iSteps)
{
	...
				case MISSION_DIE_ANIMATION:

				/********************************************************************************/
				/* LyTning94														1/14/11		*/
				/* Begin Assassins! Code														*/
				/********************************************************************************/

				case MISSION_DIPLOMACY:

				/********************************************************************************/
				/* End Assassins! Code															*/
				/********************************************************************************/

					break;

				case MISSION_BUILD:
					if (!groupBuild((BuildTypes)(headMissionQueueNode()->m_data.iData1)))
					{
						bDone = true;
					}
					break;

				default:
					FAssert(false);
					break;
				}
			...
			case MISSION_DIE_ANIMATION:

			/********************************************************************************/
			/* LyTning94														1/14/11		*/
			/* Begin Assassins! Code														*/
			/********************************************************************************/
	
			case MISSION_DIPLOMACY:

			/********************************************************************************/
			/* End Assassins! Code															*/
			/********************************************************************************/

				bDone = true;
				break;

			...
				}
			}
		}
	}
}

And CvGameCoreUtils.cpp:

Code:
        case MISSION_DIE_ANIMATION: szString = L"MISSION_DIE_ANIMATION"; break;

	/********************************************************************************/
	/* LyTning94														1/14/11		*/
	/* Begin Assassins! Code														*/
	/********************************************************************************/

	case MISSION_DIPLOMACY: szString = L"MISSION_DIPLOMACY"; break;

	/********************************************************************************/
	/* End Assassins! Code															*/
	/********************************************************************************/

	case MISSION_BEGIN_COMBAT: szString = L"MISSION_BEGIN_COMBAT"; break;

All new code is in comments. Hope I provided enough. ;)


Thanks in advance!
 
Well, there are a few places you've missed (exposing it to Python, CvGameTextMgr, maybe more - just pick one of the existing missions and do a search in all source files) - but I don't see why it would cause such a mismatch.
Your existing code seems fine (the most important part here is the placement of the mission in the enum - before the NUM_MISSION_TYPES - which you did just fine).

The worst thing it should do is to give you an assert in the DLL code.

Maybe a typo in the mission name in the XML? Though I'm not sure it'll cause it.

Are you sure it's the new mission? maybe try commenting out it's code and make sure it's really the cause?
 
make sure to do a clean rebuild of your dll. That's very important since you changed the value of NUM_MISSION_TYPES
 
Well, there are a few places you've missed (exposing it to Python, CvGameTextMgr, maybe more - just pick one of the existing missions and do a search in all source files) - but I don't see why it would cause such a mismatch.
Your existing code seems fine (the most important part here is the placement of the mission in the enum - before the NUM_MISSION_TYPES - which you did just fine).

I know I've missed places. I'm not finished with it at this point, I was just checking to see if everything worked fine so far when it crashed.

Do I need to manually assign a value to NUM_MISSION_TYPES somewhere? Or does the order of missions need to be specific?

Maybe a typo in the mission name in the XML? Though I'm not sure it'll cause it.

Are you sure it's the new mission? maybe try commenting out it's code and make sure it's really the cause?

No, it's not a typo. I commented out the code and rebuilt, and it loaded fine; so it must be the mission.
 
Do I need to manually assign a value to NUM_MISSION_TYPES somewhere? Or does the order of missions need to be specific?

No, the enum handles it automatically (unless explicitly assigned a value, each item has the previous item's value + 1). You'll also notice that NUM_MISSION_TYPES is defined differently when the header is compiled into the EXE (i.e. when _USRDLL is not defined) - as a call getNumMissionInfos() (see at the bottom of CvGlobals.h).

Regarding the order - even if it matters for the original missions, you've added it after all of them, so you're covered.

No, it's not a typo. I commented out the code and rebuilt, and it loaded fine; so it must be the mission.

Just covering the basics.
And you did a rebuild as Sephi suggested with the new mission code and it still happens? (again, just making sure)

Do you have any Python code refering to this mission?
Which other XML entries refer to this mission?

I might be wrong, but the order of the missions in the files is important, right?

See my answer to LyThing94.
 
Regarding the order - even if it matters for the original missions, you've added it after all of them, so you're covered.

Actually in CvEnums.h I added it after MISSION_MULTI_DESELCT, while in the other files I added it after MISSION_DIE_ANIMATION. I'll try placing them in the same order in all the files and see what happens.

Just covering the basics.
And you did a rebuild as Sephi suggested with the new mission code and it still happens? (again, just making sure)

:yup:

Do you have any Python code refering to this mission?
Which other XML entries refer to this mission?

No, I have no python code currently, and no other XML entries.
 
Actually in CvEnums.h I added it after MISSION_MULTI_DESELCT, while in the other files I added it after MISSION_DIE_ANIMATION. I'll try placing them in the same order in all the files and see what happens.

The order in the enum determines the value of MISSION_XXX. In the other places it's just used as a constant, so I doubt it'll make a difference. But who knows?

Can you attach the files that you've changed? (C++ and XML). I should have time later to try running it myself and see what happens, if you want.

Are you basing your mod on BTS 3.19 or on another mod?
 
I rearranged the order in all the files to match, and then used the same order in Civ4MissionInfos, and it appears to work fine now.

I also noticed that in the other missions not labeled as 'simply a link to entity events' in Civ4MissionInfos, there was no space before the closing ) in CyEnumsInterface.cpp, so I changed:

Code:
.value("MISSION_DIPLOMACY", MISSION_DIPLOMACY )

to:

Code:
.value("MISSION_DIPLOMACY", MISSION_DIPLOMACY)

I'm not sure if this had any effect or not (I'm assuming it was the order change), but it seemed strange to me that some would have the extra space and some wouldn't. :dunno:
 
I'm glad you managed to sort it out.

In retrospective, the order in the XML can have an effect, and should probably have the same order as in the enums. Sorry for not thinking about it :blush:

The space is meaningless.
 
Where is the code which allows the mission to actually show up as a button in the interface?
 
I think you need the CvSelectionGroup::canStartMission() function to return true to this mission. That way canHandleAction() returns true to the python code which displays the buttons.
 
I think you need the CvSelectionGroup::canStartMission() function to return true to this mission. That way canHandleAction() returns true to the python code which displays the buttons.

I've already added that:

Code:
                switch (iMission)
                ...
	        /********************************************************************************/
		/* LyTning94														1/14/11		*/
		/* Begin Assassins! Code														*/
		/********************************************************************************/

		case MISSION_DIPLOMACY:
			if (pLoopUnit->canBoostDiplomacy(pPlot))
			{
				return true;
			}
			break;

		/********************************************************************************/
		/* End Assassins! Code															*/
		/********************************************************************************/

my question is, where does the value for iMission come from (I know it's a parameter in canStartMission(), but how does the function which calls canStartMission() get the value?).
 
you need to define a Command (both XML and dll) and then in doCommand let it start the mission.
 
Are you sure you need to define a command for a mission?
AFAIK a mission covers itself, and is a separate thing than a command.

my question is, where does the value for iMission come from (I know it's a parameter in canStartMission(), but how does the function which calls canStartMission() get the value?).

In CvXMLLoadUtility::SetGlobalActionInfo() the code builds the list of actions.
The actions list includes all the missions, commands, promotions etc.

Then, in Python code (CvMainInterface.py):
Code:
g_pSelectedUnit = pHeadSelectedUnit

iCount = 0

actions = CyInterface().[COLOR="Red"][B]getActionsToShow[/B][/COLOR]()
for i in actions:
	screen.appendMultiListButton( "BottomButtonContainer", gc.getActionInfo(i).getButton(), 0, WidgetTypes.WIDGET_ACTION, i, -1, False )
	screen.show( "BottomButtonContainer" )

	if ( not CyInterface().[COLOR="Red"][B]canHandleAction[/B][/COLOR](i, False) ):
		screen.disableMultiListButton( "BottomButtonContainer", 0, iCount, gc.getActionInfo(i).getButton() )
		
	if ( pHeadSelectedUnit.isActionRecommended(i) ):#or gc.getActionInfo(i).getCommandType() == CommandTypes.COMMAND_PROMOTION ):
		screen.enableMultiListPulse( "BottomButtonContainer", True, 0, iCount )
	else:
		screen.enableMultiListPulse( "BottomButtonContainer", False, 0, iCount )

	iCount = iCount + 1

The highlighted functions are exposed from the exe. getActionsToShow() returns the list of actions previously built.
AFAIK canHandleAction() internally calls CvGame::canHandleAction(), which forwards the query to CvSelectionGroup::canStartMission() in case of a mission.
 
Are you sure you need to define a command for a mission?
AFAIK a mission covers itself, and is a separate thing than a command.

that's true. With a command it would work though. Maybe the bvisibile tag for the mission isn't set?
 
Maybe the bvisibile tag for the mission isn't set?

It appears it wasn't.

I hate it when this happens. I spend two hours searching through code to try and find what the problem is, and it turns out I didn't set the tags correctly in the XML.

I should've known it was something stupid like that.
 
Back
Top Bottom