示例#1
0
/**
 * @brief Unloads transfer cargo when finishing the transfer or destroys it when no buildings/base.
 * @param[in,out] destination The destination base - might be NULL in case the base
 * is already destroyed
 * @param[in] transfer Pointer to transfer in ccs.transfers.
 * @param[in] success True if the transfer reaches dest base, false if the base got destroyed.
 * @sa TR_TransferEnd
 */
static void TR_EmptyTransferCargo (base_t *destination, transfer_t *transfer, qboolean success)
{
	assert(transfer);

	if (transfer->hasItems && success) {	/* Items. */
		const objDef_t *od = INVSH_GetItemByID(ANTIMATTER_TECH_ID);
		int i;

		/* antimatter */
		if (transfer->itemAmount[od->idx] > 0) {
			if (B_GetBuildingStatus(destination, B_ANTIMATTER)) {
				B_ManageAntimatter(destination, transfer->itemAmount[od->idx], qtrue);
			} else {
				Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Antimatter Storage, antimatter are removed!"), destination->name);
				MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, qfalse, MSG_TRANSFERFINISHED, NULL);
			}
		}
		/* items */
		for (i = 0; i < csi.numODs; i++) {
			od = INVSH_GetItemByIDX(i);

			if (transfer->itemAmount[od->idx] <= 0)
				continue;
			if (!B_ItemIsStoredInBaseStorage(od))
				continue;
			if (!B_GetBuildingStatus(destination, B_STORAGE)) {
				Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Storage, items are removed!"), destination->name);
				MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, qfalse, MSG_TRANSFERFINISHED, NULL);
				break;
			}
			B_UpdateStorageAndCapacity(destination, od, transfer->itemAmount[od->idx], qfalse, qtrue);
		}
	}

	if (transfer->hasEmployees && transfer->srcBase) {	/* Employees. (cannot come from a mission) */
		if (!success || !B_GetBuildingStatus(destination, B_QUARTERS)) {	/* Employees will be unhired. */
			employeeType_t i;
			if (success) {
				Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Living Quarters, employees got unhired!"), destination->name);
				MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, qfalse, MSG_TRANSFERFINISHED, NULL);
			}
			for (i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
				employee_t *employee;
				TR_ForeachEmployee(employee, transfer, i) {
					employee->baseHired = transfer->srcBase;	/* Restore back the original baseid. */
					employee->transfer = qfalse;
					E_UnhireEmployee(employee);
				}
			}
		} else {
示例#2
0
/**
 * @brief Run actions on finishing disassembling of a ufo
 * @param base The base to produce in
 * @param prod The production that is running
 */
static void PR_FinishDisassembly (base_t* base, production_t* prod)
{
	storedUFO_t* ufo = prod->data.data.ufo;

	assert(ufo);
	for (int i = 0; i < ufo->comp->numItemtypes; i++) {
		const objDef_t* compOd = ufo->comp->items[i];
		const int amount = (ufo->condition < 1 && ufo->comp->itemAmount2[i] != COMP_ITEMCOUNT_SCALED) ?
			ufo->comp->itemAmount2[i] : round(ufo->comp->itemAmount[i] * ufo->condition);

		assert(compOd);

		if (amount <= 0)
			continue;

		if (Q_streq(compOd->id, ANTIMATTER_ITEM_ID)) {
			B_AddAntimatter(base, amount);
		} else {
			technology_t* tech = RS_GetTechForItem(compOd);
			B_AddToStorage(base, compOd, amount);
			RS_MarkCollected(tech);
		}
	}

	Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("The disassembling of %s at %s has finished."),
			UFO_TypeToName(ufo->ufoTemplate->getUfoType()), base->name);
	MSO_CheckAddNewMessage(NT_PRODUCTION_FINISHED, _("Production finished"), cp_messageBuffer, MSG_PRODUCTION, ufo->ufoTemplate->tech);

	/* Removing UFO will remove the production too */
	US_RemoveStoredUFO(ufo);
}
示例#3
0
/**
 * @brief Run actions on finishing production of one item/aircraft/UGV..
 * @param base The base to produce in
 * @param prod The production that is running
 */
static void PR_FinishProduction (base_t* base, production_t* prod)
{
	const char* name = PR_GetName(&prod->data);
	technology_t* tech = PR_GetTech(&prod->data);

	prod->frame = 0;
	prod->amount--;

	if (PR_IsItem(prod)) {
		CP_UpdateCredits(ccs.credits - PR_GetPrice(prod->data.data.item));
		/* Now add it to equipment and update capacity. */
		B_AddToStorage(base, prod->data.data.item, 1);
	} else if (PR_IsAircraft(prod)) {
		CP_UpdateCredits(ccs.credits - PR_GetPrice(prod->data.data.aircraft));
		/* Now add new aircraft. */
		AIR_NewAircraft(base, prod->data.data.aircraft);
	}

	if (prod->amount > 0)
		return;

	Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Work on %s at %s has finished."), name, base->name);
	MSO_CheckAddNewMessage(NT_PRODUCTION_FINISHED, _("Production finished"), cp_messageBuffer, MSG_PRODUCTION, tech);
	/* queue the next production */
	PR_QueueNext(base);
}
示例#4
0
/**
 * @brief Checks whether production can continue
 * @param base The base the production is running in
 * @param prod The production
 * @return @c false if production is stopped, @c true if production can be continued.
 */
static bool PR_CheckFrame (base_t* base, production_t* prod)
{
	int price = 0;

	if (PR_IsItem(prod)) {
		const objDef_t* od = prod->data.data.item;
		price = PR_GetPrice(od);
	} else if (PR_IsAircraft(prod)) {
		const aircraft_t* aircraft = prod->data.data.aircraft;
		price = PR_GetPrice(aircraft);
	}

	/* Not enough money */
	if (price > 0 && price > ccs.credits) {
		if (!prod->creditMessage) {
			Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Not enough credits to finish production in %s."), base->name);
			MSO_CheckAddNewMessage(NT_PRODUCTION_FAILED, _("Notice"), cp_messageBuffer);
			prod->creditMessage = true;
		}

		PR_ProductionRollBottom(base);
		return false;
	}

	return true;
}
示例#5
0
/**
 * @brief Decide what an attacking aircraft can do.
 * @param[in] campaign The campaign data structure
 * @param[in] shooter The aircraft we attack with.
 * @param[in] target The ufo we are going to attack.
 * @todo Implement me and display an attack popup.
 */
void AIRFIGHT_ExecuteActions (const campaign_t* campaign, aircraft_t* shooter, aircraft_t* target)
{
	/* some asserts */
	assert(shooter);
	assert(target);

	/* Check if the attacking aircraft can shoot */
	const int slotIdx = AIRFIGHT_ChooseWeapon(shooter->weapons, shooter->maxWeapons, shooter->pos, target->pos);

	/* if weapon found that can shoot */
	if (slotIdx >= AIRFIGHT_WEAPON_CAN_SHOOT) {
		aircraftSlot_t* weaponSlot = &shooter->weapons[slotIdx];
		const objDef_t* ammo = weaponSlot->ammo;

		/* shoot */
		if (AIRFIGHT_AddProjectile(nullptr, nullptr, shooter, target, weaponSlot)) {
			/* will we miss the target ? */
			const float probability = frand();
			Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ExecuteActions: %s - Random probability to hit: %f\n", shooter->name, probability);
			weaponSlot->delayNextShot = ammo->craftitem.weaponDelay;

			const float calculatedProbability = AIRFIGHT_ProbabilityToHit(shooter, target, shooter->weapons + slotIdx);
			Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ExecuteActions: %s - Calculated probability to hit: %f\n", shooter->name, calculatedProbability);

			if (probability > calculatedProbability)
				AIRFIGHT_MissTarget(&ccs.projectiles[ccs.numProjectiles - 1]);

			if (shooter->type != AIRCRAFT_UFO) {
				/* Maybe UFO is going to shoot back ? */
				UFO_CheckShootBack(campaign, target, shooter);
			} else {
				/* an undetected UFO within radar range and firing should become detected */
				if (!shooter->detected && RADAR_CheckRadarSensored(shooter->pos)) {
					/* stop time and notify */
					MSO_CheckAddNewMessage(NT_UFO_ATTACKING, _("Notice"), va(_("A UFO is shooting at %s"), target->name));
					RADAR_AddDetectedUFOToEveryRadar(shooter);
					UFO_DetectNewUFO(shooter);
				}
			}
		}
	} else if (slotIdx == AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT) {
		/* no ammo to fire atm (too far or reloading), pursue target */
		if (shooter->type == AIRCRAFT_UFO) {
			/** @todo This should be calculated only when target destination changes, or when aircraft speed changes.
			 *  @sa AIR_GetDestination */
			UFO_SendPursuingAircraft(shooter, target);
		} else
			AIR_SendAircraftPursuingUFO(shooter, target);
	} else {
		/* no ammo left, or no weapon, proceed with mission */
		if (shooter->type == AIRCRAFT_UFO) {
			shooter->aircraftTarget = nullptr;		/* reset target */
			CP_UFOProceedMission(campaign, shooter);
		} else {
			MS_AddNewMessage(_("Notice"), _("Our aircraft has no more ammo left - returning to home base now."));
			AIR_AircraftReturnToBase(shooter);
		}
	}
}
示例#6
0
/**
 * @brief Queues the next production in the queue.
 * @param[in] base Pointer to the base.
 */
void PR_QueueNext (base_t* base)
{
	production_queue_t* queue = PR_GetProductionForBase(base);

	PR_QueueDelete(base, queue, 0);

	if (queue->numItems == 0) {
		Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Production queue for %s is empty"), base->name);
		MSO_CheckAddNewMessage(NT_PRODUCTION_QUEUE_EMPTY, _("Production queue empty"), cp_messageBuffer, MSG_PRODUCTION);
	}
}
示例#7
0
/**
 * @brief Unloads transfer cargo when finishing the transfer or destroys it when no buildings/base.
 * @param[in,out] destination The destination base - might be nullptr in case the base
 * is already destroyed
 * @param[in] transfer Pointer to transfer in ccs.transfers.
 * @param[in] success True if the transfer reaches dest base, false if the base got destroyed.
 * @sa TR_TransferEnd
 */
static void TR_EmptyTransferCargo (base_t* destination, transfer_t* transfer, bool success)
{
	assert(transfer);

	/* antimatter */
	if (transfer->antimatter > 0 && success) {
		if (B_GetBuildingStatus(destination, B_ANTIMATTER)) {
			B_AddAntimatter(destination, transfer->antimatter);
		} else {
			Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Antimatter Storage, antimatter are removed!"), destination->name);
			MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, MSG_TRANSFERFINISHED);
		}
	}

	/* items */
	if (transfer->itemCargo != nullptr) {
		if (success) {
			linkedList_t* cargo = transfer->itemCargo->list();
			LIST_Foreach(cargo, itemCargo_t, item) {
				if (item->amount <= 0)
					continue;
				if (!B_ItemIsStoredInBaseStorage(item->objDef))
					continue;
				B_AddToStorage(destination, item->objDef, item->amount);
			}
			cgi->LIST_Delete(&cargo);
		}
		delete transfer->alienCargo;
		transfer->alienCargo = nullptr;
	}

	/* Employee */
	if (transfer->hasEmployees && transfer->srcBase) {	/* Employees. (cannot come from a mission) */
		for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
			const employeeType_t type = (employeeType_t)i;
			TR_ForeachEmployee(employee, transfer, type) {
				employee->transfer = false;
				if (!success) {
					E_DeleteEmployee(employee);
					continue;
				}
				switch (type) {
				case EMPL_WORKER:
					PR_UpdateProductionCap(destination, 0);
					break;
				case EMPL_PILOT:
					AIR_AutoAddPilotToAircraft(destination, employee);
					break;
				default:
					break;
				}
			}
		}
	}
示例#8
0
/**
 * @brief Check if some installation are build.
 * @note Daily called.
 */
void INS_UpdateInstallationData (void)
{
	installation_t *installation;

	INS_Foreach(installation) {
		if (installation->installationStatus == INSTALLATION_UNDER_CONSTRUCTION
		 && installation->buildStart
		 && installation->buildStart + installation->installationTemplate->buildTime <= ccs.date.day) {

			INS_FinishInstallation(installation);

			Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("Construction of installation %s finished."), installation->name);
			MSO_CheckAddNewMessage(NT_INSTALLATION_BUILDFINISH, _("Installation finished"), cp_messageBuffer, qfalse, MSG_CONSTRUCTION, NULL);
		}
	}
}
/**
 * @brief Constructs a new installation.
 */
static void INS_BuildInstallation_f (void)
{
	const installationTemplate_t* installationTemplate;

	if (cgi->Cmd_Argc() < 1) {
		Com_Printf("Usage: %s <installationType>\n", cgi->Cmd_Argv(0));
		return;
	}

	/* We shouldn't build more installations than the actual limit */
	if (B_GetInstallationLimit() <= INS_GetCount())
		return;

	installationTemplate = INS_GetInstallationTemplateByID(cgi->Cmd_Argv(1));
	if (!installationTemplate) {
		Com_Printf("The installation type %s passed for %s is not valid.\n", cgi->Cmd_Argv(1), cgi->Cmd_Argv(0));
		return;
	}

	assert(installationTemplate->cost >= 0);

	if (ccs.credits - installationTemplate->cost > 0) {
		/* set up the installation */
		installation_t* installation = INS_Build(installationTemplate, ccs.newBasePos, cgi->Cvar_GetString("mn_installation_title"));

		CP_UpdateCredits(ccs.credits - installationTemplate->cost);
		/* this cvar is used for disabling the installation build button on geoscape if MAX_INSTALLATIONS was reached */
		cgi->Cvar_SetValue("mn_installation_count", INS_GetCount());

		const nation_t* nation = GEO_GetNation(installation->pos);
		if (nation)
			Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new installation has been built: %s (nation: %s)"), installation->name, _(nation->name));
		else
			Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new installation has been built: %s"), installation->name);
		MSO_CheckAddNewMessage(NT_INSTALLATION_BUILDSTART, _("Installation building"), cp_messageBuffer, MSG_CONSTRUCTION);
	} else {
		if (installationTemplate->type == INSTALLATION_RADAR) {
			if (GEO_IsRadarOverlayActivated())
					GEO_SetOverlay("radar");
		}
		if (ccs.mapAction == MA_NEWINSTALLATION)
			ccs.mapAction = MA_NONE;

		CP_Popup(_("Notice"), _("Not enough credits to set up a new installation."));
	}
	ccs.mapAction = MA_NONE;
}
示例#10
0
/**
 * @brief Start Base Attack.
 * @param[in] mission Pointer to the baseattack mission
 */
void CP_BaseAttackStartMission (mission_t* mission)
{
	base_t* base = mission->data.base;
	int soldiers;

	assert(base);
	mission->stage = STAGE_BASE_ATTACK;
	CP_MissionDisableTimeLimit(mission);
	if (mission->ufo) {
		/* ufo becomes invisible on geoscape, but don't remove it from ufo global array (may reappear)*/
		CP_UFORemoveFromGeoscape(mission, false);
	}

	/* we always need at least one command centre in the base - because the
	 * phalanx soldiers have their starting positions here.
	 * There should also always be an entrance - the aliens start there
	 * but we don't need to check that as entrance can't be destroyed */
	if (!B_GetNumberOfBuildingsInBaseByBuildingType(base, B_COMMAND)) {
		/** @todo handle command centre properly */
		cgi->Com_DPrintf(DEBUG_CLIENT, "CP_BaseAttackStartMission: Base '%s' has no Command Center: it can't defend itself. Destroy base.\n", base->name);
		CP_BaseAttackMissionDestroyBase(mission);
		return;
	}

	MSO_CheckAddNewMessage(NT_BASE_ATTACK, _("Base attack"), va(_("Base '%s' is under attack!"), base->name), MSG_BASEATTACK);

	base->baseStatus = BASE_UNDER_ATTACK;
	ccs.campaignStats.basesAttacked++;

	soldiers = 0;
	E_Foreach(EMPL_SOLDIER, employee) {
		if (!employee->isHiredInBase(base))
			continue;
		if (employee->isAwayFromBase())
			continue;
		soldiers++;
	}
	if (soldiers == 0) {
		cgi->Com_DPrintf(DEBUG_CLIENT, "CP_BaseAttackStartMission: Base '%s' has no soldiers at home: it can't defend itself. Destroy base.\n", base->name);
		CP_BaseAttackMissionDestroyBase(mission);
		return;
	}
	CP_BaseAttackPrepareBattle(mission);
}
示例#11
0
/**
 * @brief Check if the ufo can shoot at a PHALANX aircraft
 */
static void UFO_SearchAircraftTarget (const campaign_t* campaign, aircraft_t *ufo)
{
	float distance = 999999.;

	/* UFO never try to attack a PHALANX aircraft except if they came on earth in that aim */
	if (ufo->mission->stage != STAGE_INTERCEPT) {
		/* Check if UFO is defending itself */
		if (ufo->aircraftTarget)
			UFO_CheckShootBack(campaign, ufo, ufo->aircraftTarget);
		return;
	}

	/* check if the ufo is already attacking an aircraft */
	if (ufo->aircraftTarget) {
		/* check if the target disappeared from geoscape (fled in a base) */
		if (AIR_IsAircraftOnGeoscape(ufo->aircraftTarget))
			AIRFIGHT_ExecuteActions(campaign, ufo, ufo->aircraftTarget);
		else
			ufo->aircraftTarget = NULL;
		return;
	}

	ufo->status = AIR_TRANSIT;
	AIR_Foreach(phalanxAircraft) {
		/* check that aircraft is flying */
		if (AIR_IsAircraftOnGeoscape(phalanxAircraft)) {
			/* get the distance from ufo to aircraft */
			const float dist = GetDistanceOnGlobe(ufo->pos, phalanxAircraft->pos);
			/* check out of reach */
			if (dist > MAX_DETECTING_RANGE)
				continue;
			/* choose the nearest target */
			if (dist < distance) {
				distance = dist;
				if (UFO_SendPursuingAircraft(ufo, phalanxAircraft) && UFO_IsUFOSeenOnGeoscape(ufo)) {
					/* stop time and notify */
					MSO_CheckAddNewMessage(NT_UFO_ATTACKING, _("Notice"), va(_("A UFO is flying toward %s"), phalanxAircraft->name), qfalse,
						MSG_STANDARD, NULL);
					/** @todo present a popup with possible orders like: return to base, attack the ufo, try to flee the rockets */
				}
			}
		}
	}
}
示例#12
0
/**
 * @brief Destroys an installation
 * @param[in,out] installation Pointer to the installation to be destroyed
 */
void INS_DestroyInstallation (installation_t *installation)
{
	if (!installation)
		return;
	/* Disable radar */
	RADAR_UpdateInstallationRadarCoverage(installation, 0, 0);
	/* Destroy stored UFOs */
	if (installation->ufoCapacity.max > 0) {
		installation->ufoCapacity.max = 0;
		US_RemoveUFOsExceedingCapacity(installation);
	}
	CP_MissionNotifyInstallationDestroyed(installation);

	Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Installation %s was destroyed."), installation->name);
	MSO_CheckAddNewMessage(NT_INSTALLATION_DESTROY, _("Installation destroyed"), cp_messageBuffer, qfalse, MSG_CONSTRUCTION, NULL);

	LIST_Remove(&ccs.installations, installation);
	Cvar_Set("mn_installation_count", va("%i", INS_GetCount()));
}
示例#13
0
/**
 * @brief Unloads transfer cargo when finishing the transfer or destroys it when no buildings/base.
 * @param[in,out] destination The destination base - might be nullptr in case the base
 * is already destroyed
 * @param[in] transfer Pointer to transfer in ccs.transfers.
 * @param[in] success True if the transfer reaches dest base, false if the base got destroyed.
 * @sa TR_TransferEnd
 */
static void TR_EmptyTransferCargo (base_t* destination, transfer_t* transfer, bool success)
{
	assert(transfer);

	if (transfer->hasItems && success) {	/* Items. */
		const objDef_t* od = INVSH_GetItemByID(ANTIMATTER_ITEM_ID);
		int i;

		/* antimatter */
		if (transfer->itemAmount[od->idx] > 0) {
			if (B_GetBuildingStatus(destination, B_ANTIMATTER)) {
				B_ManageAntimatter(destination, transfer->itemAmount[od->idx], true);
			} else {
				Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Antimatter Storage, antimatter are removed!"), destination->name);
				MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, MSG_TRANSFERFINISHED);
			}
		}
		/* items */
		for (i = 0; i < cgi->csi->numODs; i++) {
			od = INVSH_GetItemByIDX(i);

			if (transfer->itemAmount[od->idx] <= 0)
				continue;
			if (!B_ItemIsStoredInBaseStorage(od))
				continue;
			B_AddToStorage(destination, od, transfer->itemAmount[od->idx]);
		}
	}

	if (transfer->hasEmployees && transfer->srcBase) {	/* Employees. (cannot come from a mission) */
		if (!success) {	/* Employees will be unhired. */
			for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
				const employeeType_t type = (employeeType_t)i;
				TR_ForeachEmployee(employee, transfer, type) {
					employee->baseHired = transfer->srcBase;	/* Restore back the original baseid. */
					employee->transfer = false;
					employee->unhire();
				}
			}
		} else {
			for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
示例#14
0
/**
 * @brief Check events for UFOs: Appears or disappears on radars
 * @return qtrue if any new ufo was detected during this iteration, qfalse otherwise
 */
qboolean UFO_CampaignCheckEvents (void)
{
	qboolean newDetection;
	aircraft_t *ufo;

	newDetection = qfalse;

	/* For each ufo in geoscape */
	ufo = NULL;
	while ((ufo = UFO_GetNext(ufo)) != NULL) {
		char detectedBy[MAX_VAR] = "";
		float minDistance = -1;
		/* detected tells us whether or not a UFO is detected NOW, whereas ufo->detected tells
		 * us whether or not the UFO was detected PREVIOUSLY. */
		qboolean detected = qfalse;
		base_t *base;

		/* don't update UFO status id UFO is landed or crashed */
		if (ufo->landed)
			continue;

		/* note: We can't exit these loops as soon as we found the UFO detected
		 * RADAR_CheckUFOSensored registers the UFO in every radars' detection list
		 * which detect it */

		/* Check if UFO is detected by an aircraft */
		AIR_Foreach(aircraft) {
			if (!AIR_IsAircraftOnGeoscape(aircraft))
				continue;
			/* maybe the ufo is already detected, don't reset it */
			if (RADAR_CheckUFOSensored(&aircraft->radar, aircraft->pos, ufo, detected | ufo->detected)) {
				const int distance = GetDistanceOnGlobe(aircraft->pos, ufo->pos);
				detected = qtrue;
				if (minDistance < 0 || minDistance > distance) {
					minDistance = distance;
					Q_strncpyz(detectedBy, aircraft->name, sizeof(detectedBy));
				}
			}
		}

		/* Check if UFO is detected by a base */
		base = NULL;
		while ((base = B_GetNext(base)) != NULL) {
			if (!B_GetBuildingStatus(base, B_POWER))
				continue;

			/* maybe the ufo is already detected, don't reset it */
			if (RADAR_CheckUFOSensored(&base->radar, base->pos, ufo, detected | ufo->detected)) {
				const int distance = GetDistanceOnGlobe(base->pos, ufo->pos);
				detected = qtrue;
				if (minDistance < 0 || minDistance > distance) {
					minDistance = distance;
					Q_strncpyz(detectedBy, base->name, sizeof(detectedBy));
				}
			}

		}

		/* Check if UFO is detected by a radartower */
		INS_Foreach(installation) {
			/* maybe the ufo is already detected, don't reset it */
			if (RADAR_CheckUFOSensored(&installation->radar, installation->pos, ufo, detected | ufo->detected)) {
				const int distance = GetDistanceOnGlobe(installation->pos, ufo->pos);
				detected = qtrue;
				if (minDistance < 0 || minDistance > distance) {
					minDistance = distance;
					Q_strncpyz(detectedBy, installation->name, sizeof(detectedBy));
				}
			}
		}

		/* Check if ufo appears or disappears on radar */
		if (detected != ufo->detected) {
			if (detected) {
				/* if UFO is aiming a PHALANX aircraft, warn player */
				if (ufo->aircraftTarget) {
					/* stop time and notify */
					MSO_CheckAddNewMessage(NT_UFO_ATTACKING, _("Notice"), va(_("A UFO is flying toward %s"), ufo->aircraftTarget->name), qfalse,
							MSG_STANDARD, NULL);
					/** @todo present a popup with possible orders like: return to base, attack the ufo, try to flee the rockets
					 * @sa UFO_SearchAircraftTarget */
				} else {
					MSO_CheckAddNewMessage(NT_UFO_SPOTTED, _("Notice"), va(_("Our radar detected a new UFO near %s"), detectedBy), qfalse,
							MSG_UFOSPOTTED, NULL);
				}
				newDetection = qtrue;
				UFO_DetectNewUFO(ufo);
			} else if (!detected) {
				MSO_CheckAddNewMessage(NT_UFO_SIGNAL_LOST, _("Notice"), _("Our radar has lost the tracking on a UFO"), qfalse, MSG_UFOLOST, NULL);
				/* Make this UFO undetected */
				ufo->detected = qfalse;
				/* Notify that ufo disappeared */
				AIR_AircraftsUFODisappear(ufo);
				MAP_NotifyUFODisappear(ufo);

				/* Deactivate Radar overlay */
				RADAR_DeactivateRadarOverlay();
			}
		}
	}
	return newDetection;
}
示例#15
0
/**
 * @brief Check if the ufo can shoot at a PHALANX aircraft and whether it should follow another ufo
 */
static void UFO_SearchAircraftTarget (const campaign_t* campaign, aircraft_t* ufo, float maxDetectionRange = MAX_DETECTING_RANGE)
{
	float distance = 999999.;

	/* UFO never try to attack a PHALANX aircraft except if they came on earth in that aim */
	if (ufo->mission->stage != STAGE_INTERCEPT) {
		/* Check if UFO is defending itself */
		if (ufo->aircraftTarget)
			UFO_CheckShootBack(campaign, ufo, ufo->aircraftTarget);
		return;
	}

	/* check if the ufo is already attacking an aircraft */
	if (ufo->aircraftTarget) {
		/* check if the target disappeared from geoscape (fled in a base) */
		if (AIR_IsAircraftOnGeoscape(ufo->aircraftTarget))
			AIRFIGHT_ExecuteActions(campaign, ufo, ufo->aircraftTarget);
		else
			ufo->aircraftTarget = nullptr;
		return;
	}

	ufo->status = AIR_TRANSIT;
	AIR_Foreach(phalanxAircraft) {
		/* check that aircraft is flying */
		if (AIR_IsAircraftOnGeoscape(phalanxAircraft)) {
			/* get the distance from ufo to aircraft */
			const float dist = GetDistanceOnGlobe(ufo->pos, phalanxAircraft->pos);
			/* check out of reach */
			if (dist > maxDetectionRange)
				continue;
			/* choose the nearest target */
			if (dist < distance) {
				distance = dist;
				if (UFO_SendPursuingAircraft(ufo, phalanxAircraft) && UFO_IsUFOSeenOnGeoscape(ufo)) {
					/* stop time and notify */
					MSO_CheckAddNewMessage(NT_UFO_ATTACKING, _("Notice"), va(_("A UFO is flying toward %s"), phalanxAircraft->name));
					/** @todo present a popup with possible orders like: return to base, attack the ufo, try to flee the rockets */
					return;
				}
			}
		}
	}

	/* if this ufo is a leader, it does not try to search another one */
	if (ufo->leader)
		return;
	aircraft_t* otherUFO = nullptr;
	const float polarCoordinatesOffset = 1.0f;
	while ((otherUFO = UFO_GetNextOnGeoscape(otherUFO)) != nullptr) {
		if (otherUFO == ufo)
			continue;
		if (otherUFO->leader) {
			vec2_t dest;
			AIR_GetDestinationWhilePursuing(ufo, otherUFO, dest);
			dest[0] += polarCoordinatesOffset;
			dest[1] += polarCoordinatesOffset;
			GEO_CalcLine(ufo->pos, dest, &ufo->route);
			ufo->time = 0;
			ufo->point = 0;
			break;
		}
	}
}
/**
 * @brief Start Base Attack.
 * @note Base attack mission -- Stage 2
 */
void CP_BaseAttackStartMission (mission_t *mission)
{
	base_t *base = mission->data.base;
	linkedList_t *hiredSoldiersInBase = NULL;
	int soldiers;

	assert(base);

	mission->stage = STAGE_BASE_ATTACK;

	CP_MissionDisableTimeLimit(mission);

	if (mission->ufo) {
		/* ufo becomes invisible on geoscape, but don't remove it from ufo global array (may reappear)*/
		CP_UFORemoveFromGeoscape(mission, qfalse);
	}

	/* we always need at least one command centre in the base - because the
	 * phalanx soldiers have their starting positions here.
	 * There should also always be an entrance - the aliens start there
	 * but we don't need to check that as entrance can't be destroyed */
	if (!B_GetNumberOfBuildingsInBaseByBuildingType(base, B_COMMAND)) {
		/** @todo handle command centre properly */
		Com_DPrintf(DEBUG_CLIENT, "CP_BaseAttackStartMission: Base '%s' has no Command Center: it can't defend itself. Destroy base.\n", base->name);
		CP_BaseAttackMissionDestroyBase(mission);
		return;
	}

	MSO_CheckAddNewMessage(NT_BASE_ATTACK, _("Base attack"), va(_("Base '%s' is under attack!"), base->name), qfalse, MSG_BASEATTACK, NULL);

	base->baseStatus = BASE_UNDER_ATTACK;
	ccs.campaignStats.basesAttacked++;

	MAP_SelectMission(mission);
	mission->active = qtrue;
	ccs.mapAction = MA_BASEATTACK;
	Com_DPrintf(DEBUG_CLIENT, "Base attack: %s at %.0f:%.0f\n", mission->id, mission->pos[0], mission->pos[1]);

	/** @todo EMPL_ROBOT */
	E_GetHiredEmployees(base, EMPL_SOLDIER, &hiredSoldiersInBase);

	/* Fill the fake aircraft */
	LIST_Delete(&baseAttackFakeAircraft.acTeam);
	OBJZERO(baseAttackFakeAircraft);
	baseAttackFakeAircraft.homebase = base;
	/* needed for transfer of alien corpses */
	VectorCopy(base->pos, baseAttackFakeAircraft.pos);
#if 0
	/** @todo active this once more than 8 soldiers are working */
	/* needed to spawn soldiers on map */
	baseAttackFakeAircraft.maxTeamSize = LIST_Count(hiredSoldiersInBase);
#else
	baseAttackFakeAircraft.maxTeamSize = MAX_ACTIVETEAM;
#endif

	if (!hiredSoldiersInBase) {
		Com_DPrintf(DEBUG_CLIENT, "CP_BaseAttackStartMission: Base '%s' has no soldiers: it can't defend itself. Destroy base.\n", base->name);
		CP_BaseAttackMissionDestroyBase(mission);
		return;
	}

	UI_ExecuteConfunc("soldierlist_clear");
	soldiers = 0;
	LIST_Foreach(hiredSoldiersInBase, employee_t, employee) {
		const rank_t *rank = CL_GetRankByIdx(employee->chr.score.rank);

		if (E_IsAwayFromBase(employee))
			continue;
		UI_ExecuteConfunc("soldierlist_add %d \"%s %s\"", employee->chr.ucn, (rank) ? _(rank->shortname) : "", employee->chr.name);
		if (soldiers < 1)
			UI_ExecuteConfunc("team_select_ucn %d", employee->chr.ucn);
		soldiers++;
	}
	if (soldiers == 0) {
		Com_DPrintf(DEBUG_CLIENT, "CP_BaseAttackStartMission: Base '%s' has no soldiers at home: it can't defend itself. Destroy base.\n", base->name);
		CP_BaseAttackMissionDestroyBase(mission);
		return;
	}
#if 0
	/** @todo active this once more than 8 soldiers are working */
	/* all soldiers in the base should get used */
	baseAttackFakeAircraft.maxTeamSize = AIR_GetTeamSize(&baseAttackFakeAircraft);
#endif

	LIST_Delete(&hiredSoldiersInBase);
	base->aircraftCurrent = &baseAttackFakeAircraft;
	MAP_SetMissionAircraft(&baseAttackFakeAircraft);
	/** @todo remove me - this is not needed because we are using the base->aircraftCurrent
	 * pointer for resolving the aircraft - only CP_GameAutoGo needs this */
	MAP_SetInterceptorAircraft(&baseAttackFakeAircraft);	/* needed for updating soldier stats sa CP_UpdateCharacterStats*/
	B_SetCurrentSelectedBase(base);						/* needed for equipment menu */

	Com_sprintf(popupText, sizeof(popupText), _("Base '%s' is under attack! What to do ?"), base->name);
	UI_RegisterText(TEXT_POPUP, popupText);

	CP_GameTimeStop();
	UI_PushWindow("popup_baseattack", NULL, NULL);
}