Example #1
0
/* Give a group of droids an order */
void orderGroupObj(DROID_GROUP *psGroup, DROID_ORDER order, BASE_OBJECT *psObj)
{
	DROID	*psCurr;

	ASSERT((PTRVALID(psGroup, sizeof(DROID_GROUP)),
		"orderGroupObj: invalid droid group"));

	if(bMultiPlayer)
	{
		SendGroupOrderGroup(psGroup,order,0,0,psObj);
		bMultiPlayer = FALSE;

		for(psCurr = psGroup->psList; psCurr; psCurr = psCurr->psGrpNext)
		{
			orderDroidObj(psCurr, order, psObj);
		}
	
		bMultiPlayer = TRUE;
	}
	else
	{
		for(psCurr = psGroup->psList; psCurr; psCurr = psCurr->psGrpNext)
		{
			orderDroidObj(psCurr, order, psObj);
		}
	}
}
Example #2
0
/* Make droid/structure look for a better target */
static BOOL updateAttackTarget(BASE_OBJECT * psAttacker, SDWORD weapon_slot)
{
	BASE_OBJECT		*psBetterTarget=NULL;
	UWORD			tmpOrigin = ORIGIN_UNKNOWN;

	if(aiChooseTarget(psAttacker, &psBetterTarget, weapon_slot, true, &tmpOrigin))	//update target
	{
		if(psAttacker->type == OBJ_DROID)
		{
			DROID *psDroid = (DROID *)psAttacker;

			if( (orderState(psDroid, DORDER_NONE) ||
				orderState(psDroid, DORDER_GUARD) ||
				orderState(psDroid, DORDER_ATTACKTARGET)) &&
				weapon_slot == 0)	//Watermelon:only primary slot(0) updates affect order
			{
				orderDroidObj((DROID *)psAttacker, DORDER_ATTACKTARGET, psBetterTarget);
			}
			else	//can't override current order
			{
				setDroidActionTarget(psDroid, psBetterTarget, weapon_slot);
			}
		}
		else if (psAttacker->type == OBJ_STRUCTURE)
		{
			STRUCTURE *psBuilding = (STRUCTURE *)psAttacker;

			setStructureTarget(psBuilding, psBetterTarget, weapon_slot, tmpOrigin);
		}

		return true;
	}

	return false;
}
Example #3
0
// add a droid to a command group
void cmdDroidAddDroid(DROID *psCommander, DROID *psDroid)
{
	DROID_GROUP	*psGroup;

	if (psCommander->psGroup == NULL)
	{
		if (!grpCreate(&psGroup))
		{
			return;
		}
		grpJoin(psGroup, psCommander);
		psDroid->group = UBYTE_MAX;
	}

	if (grpNumMembers(psCommander->psGroup) < cmdDroidMaxGroup(psCommander))
	{
		grpJoin(psCommander->psGroup, psDroid);
		psDroid->group = UBYTE_MAX;

		// set the secondary states for the unit
		secondarySetState(psDroid, DSO_ATTACK_RANGE, (SECONDARY_STATE)(psCommander->secondaryOrder & DSS_ARANGE_MASK));
		secondarySetState(psDroid, DSO_REPAIR_LEVEL, (SECONDARY_STATE)(psCommander->secondaryOrder & DSS_REPLEV_MASK));
		secondarySetState(psDroid, DSO_ATTACK_LEVEL, (SECONDARY_STATE)(psCommander->secondaryOrder & DSS_ALEV_MASK));
		secondarySetState(psDroid, DSO_HALTTYPE, (SECONDARY_STATE)(psCommander->secondaryOrder & DSS_HALT_MASK));

		orderDroidObj(psDroid, DORDER_GUARD, (BASE_OBJECT *)psCommander, ModeQueue);
	}
}
Example #4
0
/** This function adds the droid to the command group commanded by psCommander.
 * It creates a group if it doesn't exist.
 * If the group is not full, it adds the droid to it and sets all the droid's states and orders to the group's.
 */
void cmdDroidAddDroid(DROID *psCommander, DROID *psDroid)
{
	DROID_GROUP	*psGroup;

	if (psCommander->psGroup == NULL)
	{
		psGroup = grpCreate();
		psGroup->add(psCommander);
		psDroid->group = UBYTE_MAX;
	}

	if (psCommander->psGroup->getNumMembers() < cmdDroidMaxGroup(psCommander))
	{
		psCommander->psGroup->add(psDroid);
		psDroid->group = UBYTE_MAX;

		// set the secondary states for the unit
		secondarySetState(psDroid, DSO_REPAIR_LEVEL, (SECONDARY_STATE)(psCommander->secondaryOrder & DSS_REPLEV_MASK), ModeImmediate);
		secondarySetState(psDroid, DSO_ATTACK_LEVEL, (SECONDARY_STATE)(psCommander->secondaryOrder & DSS_ALEV_MASK), ModeImmediate);

		orderDroidObj(psDroid, DORDER_GUARD, (BASE_OBJECT *)psCommander, ModeImmediate);
	}
	else
	{
		audio_PlayTrack( ID_SOUND_BUILD_FAIL );
		addConsoleMessage(_("Commander needs a higher level to command more units"), DEFAULT_JUSTIFY,  SYSTEM_MESSAGE);
	}
}
Example #5
0
/*checks through the target players list of structures and droids to see
if any support a counter battery sensor*/
void counterBatteryFire(BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget)
{
	BASE_OBJECT		*psViewer;

	/*if a null target is passed in ignore - this will be the case when a 'miss'
	projectile is sent - we may have to cater for these at some point*/
	// also ignore cases where you attack your own player
	// Also ignore cases where there are already 1000 missiles heading towards the attacker.
	if (psTarget == NULL
	    || (psAttacker != NULL && psAttacker->player == psTarget->player)
	    || aiObjectIsProbablyDoomed(psAttacker))
	{
		return;
	}

	CHECK_OBJECT(psTarget);

	for (psViewer = apsSensorList[0]; psViewer; psViewer = psViewer->psNextFunc)
	{
		if (aiCheckAlliances(psTarget->player, psViewer->player))
		{
			if ((psViewer->type == OBJ_STRUCTURE && !structCBSensor((STRUCTURE *)psViewer))
			    || (psViewer->type == OBJ_DROID && !cbSensorDroid((DROID *)psViewer)))
			{
				continue;
			}
			const int sensorRange = objSensorRange(psViewer);

			// Check sensor distance from target
			const int xDiff = psViewer->pos.x - psTarget->pos.x;
			const int yDiff = psViewer->pos.y - psTarget->pos.y;

			if (xDiff * xDiff + yDiff * yDiff < sensorRange * sensorRange)
			{
				// Inform viewer of target
				if (psViewer->type == OBJ_DROID)
				{
					orderDroidObj((DROID *)psViewer, DORDER_OBSERVE, psAttacker, ModeImmediate);
				}
				else if (psViewer->type == OBJ_STRUCTURE)
				{
					((STRUCTURE *)psViewer)->psTarget[0] = psAttacker;
				}
			}
		}
	}
}
Example #6
0
// Give a Droid an order to an object
bool scrOrderDroidObj(void)
{
	DROID			*psDroid;
	DROID_ORDER		order;
	BASE_OBJECT		*psObj;

	if (!stackPopParams(3, ST_DROID, &psDroid, VAL_INT, &order, ST_BASEOBJECT, &psObj))
	{
		return false;
	}

	ASSERT(psDroid != NULL,
	       "scrOrderUnitObj: Invalid unit pointer");
	ASSERT(psObj != NULL,
	       "scrOrderUnitObj: Invalid object pointer");
	if (psDroid == NULL || psObj == NULL)
	{
		return false;
	}

	if (order != DORDER_ATTACK &&
	    order != DORDER_HELPBUILD &&
	    order != DORDER_DEMOLISH &&
	    order != DORDER_REPAIR &&
	    order != DORDER_OBSERVE &&
	    order != DORDER_EMBARK &&
	    order != DORDER_FIRESUPPORT &&
	    order != DORDER_DROIDREPAIR)
	{
		ASSERT(false,
		       "scrOrderUnitObj: Invalid order");
		return false;
	}

	orderDroidObj(psDroid, order, psObj, ModeQueue);

	return true;
}
Example #7
0
// Give a Droid an order to an object
BOOL scrOrderDroidObj(void)
{
	DROID			*psDroid;
	SDWORD			order;
	BASE_OBJECT		*psObj;

	if (!stackPopParams(3, ST_DROID, &psDroid, VAL_INT, &order, ST_BASEOBJECT, &psObj))
	{
		return FALSE;
	}

	ASSERT((PTRVALID(psDroid, sizeof(DROID)),
		"scrOrderUnitObj: Invalid unit pointer"));
	ASSERT((PTRVALID(psObj, sizeof(BASE_OBJECT)),
		"scrOrderUnitObj: Invalid object pointer"));
	if (psDroid == NULL || psObj == NULL)
	{
		return FALSE;
	}

	if (order != DORDER_ATTACK &&
		order != DORDER_HELPBUILD &&
		order != DORDER_DEMOLISH &&
		order != DORDER_REPAIR &&
		order != DORDER_OBSERVE &&
		order != DORDER_EMBARK &&
		order != DORDER_FIRESUPPORT)
	{
		ASSERT((FALSE,
			"scrOrderUnitObj: Invalid order"));
		return FALSE;
	}

	orderDroidObj(psDroid, order, psObj);

	return TRUE;
}
Example #8
0
/*checks through the target players list of structures and droids to see
if any support a counter battery sensor*/
void counterBatteryFire(BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget)
{
	BASE_OBJECT		*psViewer;

	/*if a null target is passed in ignore - this will be the case when a 'miss'
	projectile is sent - we may have to cater for these at some point*/
	// also ignore cases where you attack your own player
	// Also ignore cases where there are already 1000 missiles heading towards the attacker.
	if ((psTarget == NULL) ||
		((psAttacker != NULL) && (psAttacker->player == psTarget->player)) ||
		aiObjectIsProbablyDoomed(psAttacker))
	{
		return;
	}

	CHECK_OBJECT(psTarget);

	gridStartIterate(psTarget->pos.x, psTarget->pos.y, PREVIOUS_DEFAULT_GRID_SEARCH_RADIUS);
	for (psViewer = gridIterate(); psViewer != NULL; psViewer = gridIterate())
	{
		STRUCTURE	*psStruct;
		DROID		*psDroid;
		SDWORD		sensorRange = 0;

		if (psViewer->player != psTarget->player)
		{
			//ignore non target players' objects
			continue;
		}
		if (psViewer->type == OBJ_STRUCTURE)
		{
			psStruct = (STRUCTURE *)psViewer;
			//check if have a sensor of correct type
			if (structCBSensor(psStruct) || structVTOLCBSensor(psStruct))
			{
				sensorRange = psStruct->pStructureType->pSensor->range;
			}
		}
		else if (psViewer->type == OBJ_DROID)
		{
			psDroid = (DROID *)psViewer;
			//must be a CB sensor
			if (cbSensorDroid(psDroid))
			{
				sensorRange = asSensorStats[psDroid->asBits[COMP_SENSOR].
					nStat].range;
			}
		}
		//check sensor distance from target
		if (sensorRange)
		{
			SDWORD	xDiff = (SDWORD)psViewer->pos.x - (SDWORD)psTarget->pos.x;
			SDWORD	yDiff = (SDWORD)psViewer->pos.y - (SDWORD)psTarget->pos.y;

			if (xDiff*xDiff + yDiff*yDiff < sensorRange * sensorRange)
			{
				//inform viewer of target
				if (psViewer->type == OBJ_DROID)
				{
					orderDroidObj((DROID *)psViewer, DORDER_OBSERVE, psAttacker, ModeImmediate);
				}
				else if (psViewer->type == OBJ_STRUCTURE)
				{
					((STRUCTURE *)psViewer)->psTarget[0] = psAttacker;
				}
			}
		}
	}
}
Example #9
0
/* Do the AI for a droid */
void aiUpdateDroid(DROID *psDroid)
{
	BASE_OBJECT	*psTarget;
	BOOL		lookForTarget,updateTarget;

	ASSERT(psDroid != NULL, "Invalid droid pointer");
	if (!psDroid || isDead((BASE_OBJECT *)psDroid))
	{
		return;
	}

	// HACK: we always want to update orders when NOT running a MP game,
	// and we don't want to update when the droid belongs to another human player
	if (!myResponsibility(psDroid->player) && bMultiPlayer
		  && isHumanPlayer(psDroid->player))
	{
		return;		// we should not order this droid around
	}
	
	lookForTarget = false;
	updateTarget = false;
	
	// look for a target if doing nothing
	if (orderState(psDroid, DORDER_NONE) ||
		orderState(psDroid, DORDER_GUARD) ||
		orderState(psDroid, DORDER_TEMP_HOLD))
	{
		lookForTarget = true;
	}
	// but do not choose another target if doing anything while guarding
	if (orderState(psDroid, DORDER_GUARD) &&
		(psDroid->action != DACTION_NONE))
	{
		lookForTarget = false;
	}
	// except when self-repairing
	if (psDroid->action == DACTION_DROIDREPAIR &&
	    psDroid->psActionTarget[0] == (BASE_OBJECT *)psDroid)
	{
		lookForTarget = true;
	}
	// don't look for a target if sulking
	if (psDroid->action == DACTION_SULK)
	{
		lookForTarget = false;
	}

	/* Only try to update target if already have some target */
	if (psDroid->action == DACTION_ATTACK ||
		psDroid->action == DACTION_MOVEFIRE ||
		psDroid->action == DACTION_MOVETOATTACK ||
		psDroid->action == DACTION_ROTATETOATTACK)
	{
		updateTarget = true;
	}
	if ((orderState(psDroid, DORDER_OBSERVE) || orderState(psDroid, DORDER_ATTACKTARGET)) &&
	    psDroid->psTarget && aiObjectIsProbablyDoomed(psDroid->psTarget))
	{
		lookForTarget = true;
		updateTarget = false;
	}

	/* Don't update target if we are sent to attack and reached
		attack destination (attacking our target) */
	if (orderState(psDroid, DORDER_ATTACK) && psDroid->psActionTarget[0] == psDroid->psTarget)
	{
		updateTarget = false;
	}

	// don't look for a target if there are any queued orders
	if (psDroid->listSize > 0)
	{
		lookForTarget = false;
		updateTarget = false;
	}

	// horrible check to stop droids looking for a target if
	// they would switch to the guard order in the order update loop
	if ((psDroid->order == DORDER_NONE) &&
		(psDroid->player == selectedPlayer) &&
		!isVtolDroid(psDroid) &&
		secondaryGetState(psDroid, DSO_HALTTYPE) == DSS_HALT_GUARD)
	{
		lookForTarget = false;
		updateTarget = false;
	}

	// don't allow units to start attacking if they will switch to guarding the commander
	if(hasCommander(psDroid))
	{
		lookForTarget = false;
		updateTarget = false;
	}

	if(bMultiPlayer && isVtolDroid(psDroid) && isHumanPlayer(psDroid->player))
	{
		lookForTarget = false;
		updateTarget = false;
	}

	// do not look for a target if droid is currently under direct control.
	if(driveModeActive() && (psDroid == driveGetDriven())) {
		lookForTarget = false;
		updateTarget = false;
	}

	// CB and VTOL CB droids can't autotarget.
	if (psDroid->droidType == DROID_SENSOR && !standardSensorDroid(psDroid))
	{
		lookForTarget = false;
		updateTarget = false;
	}

	// do not attack if the attack level is wrong
	if (secondaryGetState(psDroid, DSO_ATTACK_LEVEL) != DSS_ALEV_ALWAYS)
	{
		lookForTarget = false;
	}

	/* For commanders and non-assigned non-commanders:
	 look for a better target once in a while */
	if(!lookForTarget && updateTarget)
	{
		if((psDroid->numWeaps > 0) && !hasCommander(psDroid))	//not assigned to commander
		{
			if((psDroid->id + gameTime)/TARGET_UPD_SKIP_FRAMES != (psDroid->id + gameTime - deltaGameTime)/TARGET_UPD_SKIP_FRAMES)
			{
				unsigned int i;

				(void)updateAttackTarget((BASE_OBJECT*)psDroid, 0); // this function always has to be called on weapon-slot 0 (even if ->numWeaps == 0)

				//updates all targets
				for (i = 1; i < psDroid->numWeaps; ++i)
				{
					(void)updateAttackTarget((BASE_OBJECT*)psDroid, i);
				}
			}
		}
	}

	/* Null target - see if there is an enemy to attack */

	if (lookForTarget && !updateTarget)
	{
		if (psDroid->droidType == DROID_SENSOR)
		{
			if (aiChooseSensorTarget((BASE_OBJECT *)psDroid, &psTarget))
			{
				orderDroidObj(psDroid, DORDER_OBSERVE, psTarget);
			}
		}
		else
		{
			if (aiChooseTarget((BASE_OBJECT *)psDroid, &psTarget, 0, true, NULL))
			{
				orderDroidObj(psDroid, DORDER_ATTACKTARGET, psTarget);
			}
		}
	}
}
Example #10
0
// ////////////////////////////////////////////////////////////////////////////
// receive a check and update the local world state accordingly
BOOL recvDroidCheck(NETQUEUE queue)
{
	uint8_t		count;
	int		i;
	uint32_t        synchTime;

	NETbeginDecode(queue, GAME_CHECK_DROID);

		// Get the number of droids to expect
		NETuint8_t(&count);
		NETuint32_t(&synchTime);  // Get game time.

		for (i = 0; i < count; i++)
		{
			DROID *         pD;
			PACKAGED_CHECK  pc, pc2;
			Position        precPos;

			NETPACKAGED_CHECK(&pc);

			// Find the droid in question
			if (!IdToDroid(pc.droidID, pc.player, &pD))
			{
				NETlogEntry("Recvd Unknown droid info. val=player", SYNC_FLAG, pc.player);
				debug(LOG_SYNC, "Received checking info for an unknown (as yet) droid. player:%d ref:%d", pc.player, pc.droidID);
				continue;
			}

			syncDebugDroid(pD, '<');

			if (pD->gameCheckDroid == NULL)
			{
				debug(LOG_SYNC, "We got a droid %u synch, but we couldn't find the droid!", pc.droidID);
				continue;  // Can't synch, since we didn't save data to be able to calculate a delta.
			}

			pc2 = *(PACKAGED_CHECK *)pD->gameCheckDroid;  // pc2 should be declared here, as const.

			if (pc2.gameTime != synchTime + MIN_DELAY_BETWEEN_DROID_SYNCHS)
			{
				debug(LOG_SYNC, "We got a droid %u synch, but we didn't choose the same droid to synch.", pc.droidID);
				((PACKAGED_CHECK *)pD->gameCheckDroid)->gameTime = synchTime + MIN_DELAY_BETWEEN_DROID_SYNCHS;  // Get droid synch time back in synch.
				continue;  // Can't synch, since we didn't save data to be able to calculate a delta.
			}

#define MERGECOPY(x, y, z)  if (pc.y != pc2.y) { debug(LOG_SYNC, "Droid %u out of synch, changing "#x" from %"z" to %"z".", pc.droidID, x, pc.y);             x = pc.y; }
#define MERGEDELTA(x, y, z) if (pc.y != pc2.y) { debug(LOG_SYNC, "Droid %u out of synch, changing "#x" from %"z" to %"z".", pc.droidID, x, x + pc.y - pc2.y); x += pc.y - pc2.y; }
			// player not synched here...
			precPos = droidGetPrecisePosition(pD);
			MERGEDELTA(precPos.x, pos.x, "d");
			MERGEDELTA(precPos.y, pos.y, "d");
			MERGEDELTA(precPos.z, pos.z, "d");
			droidSetPrecisePosition(pD, precPos);
			MERGEDELTA(pD->rot.direction, rot.direction, "d");
			MERGEDELTA(pD->rot.pitch, rot.pitch, "d");
			MERGEDELTA(pD->rot.roll, rot.roll, "d");
			MERGEDELTA(pD->body, body, "u");
			if (pD->body > pD->originalBody)
			{
				pD->body = pD->originalBody;
				debug(LOG_SYNC, "Droid %u body was too high after synch, reducing to %u.", pc.droidID, pD->body);
			}
			MERGEDELTA(pD->experience, experience, "u");

			if (pc.pos.x != pc2.pos.x || pc.pos.y != pc2.pos.y)
			{
				// snap droid(if on ground) to terrain level at x,y.
				if ((asPropulsionStats + pD->asBits[COMP_PROPULSION].nStat)->propulsionType != PROPULSION_TYPE_LIFT)  // if not airborne.
				{
					pD->pos.z = map_Height(pD->pos.x, pD->pos.y);
				}
			}

			// Doesn't cover all cases, but at least doesn't actively break stuff randomly.
			switch (pc.order)
			{
				case DORDER_MOVE:
					if (pc.order != pc2.order || pc.orderX != pc2.orderX || pc.orderY != pc2.orderY)
					{
						debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s(%d, %d).", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order), pc.orderX, pc.orderY);
						// reroute the droid.
						orderDroidLoc(pD, pc.order, pc.orderX, pc.orderY, ModeImmediate);
					}
					break;
				case DORDER_ATTACK:
					if (pc.order != pc2.order || pc.targetID != pc2.targetID)
					{
						BASE_OBJECT *obj = IdToPointer(pc.targetID, ANYPLAYER);
						if (obj != NULL)
						{
							debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s(%u).", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order), pc.targetID);
							// remote droid is attacking, not here tho!
							orderDroidObj(pD, pc.order, IdToPointer(pc.targetID, ANYPLAYER), ModeImmediate);
						}
						else
						{
							debug(LOG_SYNC, "Droid %u out of synch, would change order from %s to %s(%u), but can't find target.", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order), pc.targetID);
						}
					}
					break;
				case DORDER_NONE:
				case DORDER_GUARD:
					if (pc.order != pc2.order)
					{
						DROID_ORDER_DATA sOrder;
						memset(&sOrder, 0, sizeof(DROID_ORDER_DATA));
						sOrder.order = pc.order;

						debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s.", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order));
						turnOffMultiMsg(true);
						moveStopDroid(pD);
						orderDroidBase(pD, &sOrder);
						turnOffMultiMsg(false);
					}
					break;
				default:
					break;  // Don't know what to do, but at least won't be actively breaking anything.
			}

			MERGECOPY(pD->secondaryOrder, secondaryOrder, "u");  // The old code set this after changing orders, so doing that in case.
#undef MERGECOPY
#undef MERGEDELTA

			syncDebugDroid(pD, '>');

			// ...and repeat!
		}

	NETend();

	return true;
}