v1.61: Two Bugs involving Unit Mission Queues

Gerikes

User of Run-on Sentences.
Joined
Jul 26, 2005
Messages
1,753
Location
Massachusetts
Hi there. Two bugs here:

Action Buttons not Updated Correctly when Shift-Right-Clicking a plot:

When shift-right-clicking a plot (in order to push the MISSION_MOVE_TO mission onto the unit's queue) the selection list on the bottom of the screen should update so that actions can be checked for if they can be done on the end plot. For example, if you're on a plot with no improvement, then the Pillage icon should not show up. If you shift-right-click on a plot with an improvement, you should now have the Pillage item show up on the bottom, because you are telling the unit what to do when it reaches this final plot. Most of the times this works, but sometimes it doesn't. The problem seems to be with not enough time passing between when you press Shift and when you press the right mouse button.

Steps to Reproduce:

I use pillage as an example, but the problem isn't with specific actions but the fact that the entire list is made for the wrong plot.

1.) Select a unit that has the ability to pillage but is in a plot with no improvement. Notice that it has no Pillage icon, since it can not do this.
2.) Hold down the shift button on the keyboard. Wait a few seconds.
3.) Right-click a plot that has an improvement. You'll see that the Pillage icon shows up. This is the desired affect.
4.) Hit backspace to clear the unit's queue. Then, release the shift key. You should now be back to where you were at Step 1 with no change.
5.) Hold down the right shift key, but then press the right-mouse button to select the plot with the improvement with as little pause between the press of the shift key and the press of the right-mouse button.

If done fast enough, the action list buttons will still be showing the list of actions that are applicable on the plot you're standing on, not the plot you would be standing on after the move that was first pushed is complete. This is INCORRECT.

----------------------------------------------------------


Mission Queue clears if specific Missions are "next in line" while a unit ends it's turn finishing the last Mission

A unit, after completing a mission that causes them to reach their max amount of moves, will delete from the queue that final mission as it should. Unfortunately, it also will attempt the next mission, and sometimes delete it thinking that it's completed when it really never did.

Here's a way to recreate this problem:

1.) Take a settler.
2.) Hold down shift.
3.) Right-click a plot that is two movement points away (so when the settler reaches this plot, they will be forced to end their turn).
4.) While Shift is still being held down, click "Found".
5.) Release Shift.
6.) The settler will move to the plot, and end it's turn as it should. If you are required to, press "Enter" to end your turn.
7.) At the beginning of your next turn, the unit will not found a city, but instead have it's control given back to you.

After looking through the SDK, I think I've found the problem. When the selection group containing the unit finishes a mission, it deletes that queue from the node using the deleteMissionQueueNode function:

Code:
// Part of CvSelectionGroup::continueMission:
if (bDone)
	{
	if (!isBusy())
	{
		if (getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())
		{
			if (IsSelected())
			{
				if ((headMissionQueueNode()->m_data.eMissionType == MISSION_MOVE_TO) ||
					  (headMissionQueueNode()->m_data.eMissionType == MISSION_ROUTE_TO) ||
					  (headMissionQueueNode()->m_data.eMissionType == MISSION_MOVE_TO_UNIT))
				{
					gDLL->getInterfaceIFace()->changeCycleSelectionCounter((GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_QUICK_MOVES)) ? 1 : 2);
				}
			}
		}
		[b]deleteMissionQueueNode(headMissionQueueNode());[/b]
	}
}

Code:
CLLNode<MissionData>* CvSelectionGroup::deleteMissionQueueNode(CLLNode<MissionData>* pNode)
{
	CLLNode<MissionData>* pNextMissionNode;

	FAssertMsg(pNode != NULL, "Node is not assigned a valid value");
	FAssert(getOwnerINLINE() != NO_PLAYER);

	if (pNode == headMissionQueueNode())
	{
		deactivateHeadMission();
	}

	pNextMissionNode = m_missionQueue.deleteNode(pNode);

	[b]if (pNextMissionNode == headMissionQueueNode())
	{
		activateHeadMission();
	}[/b]

	if ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && IsSelected())
	{
		gDLL->getInterfaceIFace()->setDirty(Waypoints_DIRTY_BIT, true);
		gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
		gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
	}

	return pNextMissionNode;
}

activateHeadMission eventually calls startMission, which means that the Mission will try to be started even though the unit in the selectionGroup has no movement points. The main part of startMission is where it loops through all the units in the selection group and does the action for the mission specified. However, prior to doing the action, each units are checked to make sure that they can move. The unit can't move in this case, so the CvUnit::found function is never called. The startMission function continues on.

The problem, however, is that eventually startMission calls continueMission. In continue mission, there's this:

Code:
if (!bDone)
{
	switch (headMissionQueueNode()->m_data.eMissionType)
	{
// [b]Some cases snipped[/b]
	case MISSION_AIRLIFT:
	case MISSION_NUKE:
	case MISSION_RECON:
	case MISSION_AIRBOMB:
	case MISSION_BOMBARD:
	case MISSION_PILLAGE:
	case MISSION_SABOTAGE:
	case MISSION_DESTROY:
	case MISSION_STEAL_PLANS:
	case MISSION_FOUND:
	case MISSION_SPREAD:
	case MISSION_JOIN:
	case MISSION_CONSTRUCT:
	case MISSION_DISCOVER:
	case MISSION_HURRY:
	case MISSION_TRADE:
	case MISSION_GREAT_WORK:
	case MISSION_GOLDEN_AGE:
		bDone = true;
		break;

I'm guessing that all of these simply assume that the startMission function worked and that all of the units actually did their missions, so they make bDone true, which will go through the process of deleting the mission from the queue, thus skipping over the mission.

Even if we were to somehow change the code so continueMission doesn't start after startMission for this case, continueMission is called anyway when the selection group is updated (via CvPlayer::updateTimers()->CvSelectionGroup()::updateTimers()->CvSelectionGroup::updateMission() )

Code:
void CvSelectionGroup::updateMission()
{
	FAssert(getOwnerINLINE() != NO_PLAYER);

	if (getMissionTimer() > 0)
	{
		changeMissionTimer(-1);

		if (getMissionTimer() == 0)
		{
			if (getActivityType() == ACTIVITY_MISSION)
			{
				[b]continueMission();[/b]
			}
			else
			{
				if (getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())
				{
					if (gDLL->getInterfaceIFace()->getHeadSelectedUnit() == NULL)
					{
						gDLL->getInterfaceIFace()->changeCycleSelectionCounter(1);
					}
				}
			}
		}
	}
}

Interestingly, both of these cases where continueMission is called is only done when getActivityType() == ACTIVITY_MISSION, so I'm going to take a look at that and see if we can solve any problems by not making the group's activityType ACTIVITY_MISSION directly at the top of startMission, but rather either check if all the units can move first, or build this into the main part of the startMission function that actually checks if the units can move.
 
Thank you!

I believe you are correct that replacing
Code:
setActivityType(ACTIVITY_MISSION);
by
Code:
if (canAllMove())
{
   setActivityType(ACTIVITY_MISSION);
}
else
{
   setActivityType(ACTIVITY_HOLD);
}
in CvSelectionGroup::startMission() should fix the second issue.

I did not manage to reproduce the first issue in Warlords. Please let us know if it still happens.
 
Back
Top Bottom