예제 #1
0
/**
 * @brief Puts alien cargo into Alien Containment.
 * @param[in] aircraft Aircraft transporting cargo to homebase.
 * @sa B_AircraftReturnedToHomeBase
 * @sa AL_FillInContainment
 * @note an event mail about missing breathing tech will be triggered if necessary.
 */
void AL_AddAliens (aircraft_t* aircraft)
{
	if (!aircraft)
		return;
	if (!aircraft->alienCargo)
		return;
	if (!aircraft->homebase) {
		Com_Printf("AL_AddAliens: Aircraft %s (idx: %d) has no base, alienCargo destroyed\n", aircraft->name, aircraft->idx);
		delete aircraft->alienCargo;
		aircraft->alienCargo = nullptr;
		return;
	}

	if (aircraft->homebase->alienContainment == nullptr)
		aircraft->homebase->alienContainment = new AlienContainment(CAP_Get(aircraft->homebase, CAP_ALIENS), nullptr);

	AlienContainment* cont = aircraft->homebase->alienContainment;
	if (!cont)
		return;

	bool messageSent = false;
	linkedList_t* cargo = aircraft->alienCargo->list();
	LIST_Foreach(cargo, alienCargo_t, item) {
		const bool lifeSupported = AlienContainment::isLifeSupported(item->teamDef);

		if (!lifeSupported) {
			cont->add(item->teamDef, 0, item->alive + item->dead);
			aircraft->alienCargo->add(item->teamDef, -item->alive, -item->dead);

			ccs.campaignStats.killedAliens += item->dead + item->alive;
			if (item->alive > 0) {
				CP_TriggerEvent(CAPTURED_ALIENS_DIED, nullptr);
				/* only once */
				if (!messageSent) {
					MS_AddNewMessage(_("Notice"), _("You can't hold live aliens yet. Aliens died."), MSG_DEATH);
					messageSent = true;
				}
			}
		} else {
			cont->add(item->teamDef, item->alive, item->dead);
			aircraft->alienCargo->add(item->teamDef, -item->alive, -item->dead);

			ccs.campaignStats.killedAliens += item->dead;
			ccs.campaignStats.capturedAliens += item->alive;
			if (item->alive > 0) {
				CP_TriggerEvent(CAPTURED_ALIENS, nullptr);
				if (!messageSent) {
					MS_AddNewMessage(_("Notice"), _("You've captured new aliens."));
					messageSent = true;
				}
			}
		}
	}
	cgi->LIST_Delete(&cargo);
}
예제 #2
0
/**
 * @brief Actions to execute when a fight is done.
 * @param[in] campaign The campaign data structure
 * @param[in] shooter Pointer to the aircraft that fired the projectile.
 * @param[in] aircraft Pointer to the aircraft which was destroyed (alien or phalanx).
 * @param[in] phalanxWon qtrue if PHALANX won, qfalse if UFO won.
 * @note Some of these mission values are redone (and not reloaded) in CP_Load
 * @note shooter may be NULL
 * @sa UFO_DestroyAllUFOsOnGeoscape_f
 * @sa CP_Load
 * @sa CP_SpawnCrashSiteMission
 */
void AIRFIGHT_ActionsAfterAirfight (const campaign_t* campaign, aircraft_t *shooter, aircraft_t* aircraft, qboolean phalanxWon)
{
	if (phalanxWon) {
		const byte *color;

		assert(aircraft);

		/* change destination of other projectiles aiming aircraft */
		AIRFIGHT_RemoveProjectileAimingAircraft(aircraft);
		/* now update the projectile for the destroyed aircraft, too */
		AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft);

		/* don't remove ufo from global array: the mission is not over yet
		 * UFO are removed from game only at the end of the mission
		 * (in case we need to know what item to collect e.g.) */

		/* get the color value of the map at the crash position */
		color = MAP_GetColor(aircraft->pos, MAPTYPE_TERRAIN, NULL);
		/* if this color value is not the value for water ...
		 * and we hit the probability to spawn a crashsite mission */
		if (!MapIsWater(color)) {
			CP_SpawnCrashSiteMission(aircraft);
		} else {
			Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ActionsAfterAirfight: zone: %s (%i:%i:%i)\n", MAP_GetTerrainType(color), color[0], color[1], color[2]);
			MS_AddNewMessage(_("Interception"), _("UFO interception successful -- UFO lost to sea."), qfalse, MSG_STANDARD, NULL);
			CP_MissionIsOverByUFO(aircraft);
		}
	} else {
		/* change destination of other projectiles aiming aircraft */
		AIRFIGHT_RemoveProjectileAimingAircraft(aircraft);

		/* and now update the projectile pointers (there still might be some in the air
		 * of the current destroyed aircraft) - this is needed not send the aircraft
		 * back to base as soon as the projectiles will hit their target */
		AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft);

		/* notify UFOs that a phalanx aircraft has been destroyed */
		UFO_NotifyPhalanxAircraftRemoved(aircraft);

		if (!MapIsWater(MAP_GetColor(aircraft->pos, MAPTYPE_TERRAIN, NULL)))
			CP_SpawnRescueMission(aircraft, shooter);
		else {
			/* Destroy the aircraft and everything onboard - the aircraft pointer
			 * is no longer valid after this point */
			AIR_DestroyAircraft(aircraft);
		}

		/* Make UFO proceed with its mission, if it has not been already destroyed */
		if (shooter)
			CP_UFOProceedMission(campaign, shooter);

		MS_AddNewMessage(_("Interception"), _("You've lost the battle"), qfalse, MSG_DEATH, NULL);
	}
}
예제 #3
0
/**
 * @brief Sets the title of the base to a cvar to prepare the rename menu.
 */
static void B_SetBaseTitle_f (void)
{
	int baseCount = B_GetCount();

	if (baseCount < MAX_BASES) {
		char baseName[MAX_VAR];

		if (baseCount > 0) {
			int j;
			int i = 2;
			do {
				j = 0;
				Com_sprintf(baseName, lengthof(baseName), _("Base #%i"), i);
				while (j <= baseCount && !Q_streq(baseName, ccs.bases[j].name)) {
					j++;
				}
			} while (i++ <= baseCount && j <= baseCount);
		} else {
			Q_strncpyz(baseName, _("Home"), lengthof(baseName));
		}

		cgi->Cvar_Set("mn_base_title", baseName);
	} else {
		MS_AddNewMessage(_("Notice"), _("You've reached the base limit."));
		cgi->UI_PopWindow(false);		/* remove the new base popup */
	}
}
예제 #4
0
/**
 * @brief onDestroy Callback for Antimatter Storage
 */
static void B_Destroy_AntimaterStorage_f (void)
{
	base_t *base;
	const float prob = frand();

	if (cgi->Cmd_Argc() < 4) {	/** note: third parameter not used but we must be sure we have probability parameter */
		Com_Printf("Usage: %s <probability> <baseID> <buildingType>\n", cgi->Cmd_Argv(0));
		return;
	}

	base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(2)));
	if (!base)
		return;
	if (CAP_GetCurrent(base, CAP_ANTIMATTER) <= 0)
		return;

	CAP_RemoveAntimatterExceedingCapacity(base);

	if (base->baseStatus != BASE_WORKING)
		return;

	if (prob < atof(cgi->Cmd_Argv(1))) {
		MS_AddNewMessage(_("Notice"), va(_("%s has been destroyed by an antimatter storage breach."), base->name));
		cgi->UI_PopWindow(false);
		B_Destroy(base);
	}
}
예제 #5
0
파일: cp_airfight.cpp 프로젝트: yason/ufoai
/**
 * @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 Saves to the quick save slot
 * @sa SAV_GameQuickLoad_f
 */
static void SAV_GameQuickSave_f (void)
{
	if (!CP_IsRunning())
		return;

	if (!SAV_QuickSave())
		Com_Printf("Could not save the campaign\n");
	else
		MS_AddNewMessage(_("Quicksave"), _("Campaign was successfully saved."), MSG_INFO);
}
예제 #7
0
파일: cp_save.cpp 프로젝트: AresAndy/ufoai
/**
 * @brief Saves to the quick save slot
 */
static void SAV_GameQuickSave_f (void)
{
	if (!CP_IsRunning())
		return;
	if (cgi->CL_OnBattlescape())
		return;

	char* error = nullptr;
	bool result = SAV_GameSave("slotquick", _("QuickSave"), &error);
	if (!result)
		Com_Printf("Error saving the xml game: %s\n", error ? error : "");
	else
		MS_AddNewMessage(_("Quicksave"), _("Campaign was successfully saved."), MSG_INFO);
}
/**
 * @brief Function to start UFO selling process.
 * @note Command to call this: cp_uforecovery_sell_start.
 */
static void UR_DialogStartSell_f (void)
{
	int price = -1;
	const nation_t *nation;
	int i;

	if (!ufoRecovery.nation)
		return;

	nation = ufoRecovery.nation;

	i = UR_DialogGetCurrentNationIndex();
	price = ufoRecovery.ufoNations[i].price;

	assert(price >= 0);
#if 0
	if (ufoRecovery.selectedStorage) {
		Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Sold previously recovered %s from %s to nation %s, gained %i credits."), UFO_TypeToName(
				ufoRecovery.selectedStorage->ufoTemplate->ufotype), ufoRecovery.selectedStorage->base->name, _(nation->name), price);
	} else
#endif
	{
		Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Recovered %s from the battlefield. UFO sold to nation %s, gained %i credits."), UFO_GetName(ufoRecovery.ufoTemplate), _(nation->name), price);
	}
	MS_AddNewMessage(_("UFO Recovery"), cp_messageBuffer);
	CP_UpdateCredits(ccs.credits + price);

	/* update nation happiness */
	for (i = 0; i < ccs.numNations; i++) {
		nation_t *nat = NAT_GetNationByIDX(i);
		float ufoHappiness;

		assert(nat);
		if (nat == nation)
			/* nation is happy because it got the UFO */
			ufoHappiness = HAPPINESS_UFO_SALE_GAIN;
		else
			/* nation is unhappy because it wanted the UFO */
			ufoHappiness = HAPPINESS_UFO_SALE_LOSS;

		NAT_SetHappiness(ccs.curCampaign->minhappiness, nat, nat->stats[0].happiness + ufoHappiness);
	}

	/* UFO recovery process is done, disable buttons. */
	UR_DialogRecoveryDone();
}
예제 #9
0
/**
 * @brief Loads the quick save slot
 * @sa SAV_GameQuickSave_f
 */
static void SAV_GameQuickLoad_f (void)
{
	const char *error = NULL;

	if (cgi->CL_OnBattlescape()) {
		Com_Printf("Could not load the campaign while you are on the battlefield\n");
		return;
	}

	if (!SAV_GameLoad("slotquick", &error)) {
		Cbuf_Execute(); /* wipe outstanding campaign commands */
		CP_Popup(_("Error"), "%s\n%s", _("Error loading game."), error ? error : "");
	} else {
		MS_AddNewMessage(_("Campaign loaded"), _("Quicksave campaign was successfully loaded."), MSG_INFO);
		CP_CheckBaseAttacks();
	}
}
예제 #10
0
/**
 * @brief Base attack mission ends: UFO leave earth.
 * @note Base attack mission -- Stage 3
 * @note UFO attacking this base will be redirected when notify function will be called, don't set new destination here.
 */
void CP_BaseAttackMissionDestroyBase (mission_t *mission)
{
	base_t *base = mission->data.base;
	assert(base);
	/* Base attack is over, alien won */
	Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Your base: %s has been destroyed! All employees killed and all equipment destroyed."), base->name);
	MS_AddNewMessage(_("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
	B_Destroy(base);
	CL_GameTimeStop();

	/* we really don't want to use the fake aircraft anywhere */
	MAP_SetMissionAircraft(NULL);

	/* HACK This hack is only needed until base will be really destroyed
	 * we must recalculate items in storage because of the items we collected on battlefield */
	B_UpdateStorageCap(base);
	base->aircraftCurrent = NULL;
	base->baseStatus = BASE_WORKING;
}
예제 #11
0
/**
 * @brief Constructs a new base.
 * @sa B_NewBase
 */
static void B_BuildBase_f (void)
{
	const campaign_t* campaign = ccs.curCampaign;

	if (ccs.mapAction == MA_NEWBASE)
		ccs.mapAction = MA_NONE;

	if (ccs.credits - campaign->basecost > 0) {
		const nation_t* nation;
		const char* baseName = mn_base_title->string;
		base_t* base;
		/* there may be no " in the base name */
		if (!Com_IsValidName(baseName))
			baseName = _("Base");

		base = B_Build(campaign, ccs.newBasePos, baseName);
		if (!base)
			cgi->Com_Error(ERR_DROP, "Cannot build base");

		CP_UpdateCredits(ccs.credits - campaign->basecost);
		nation = GEO_GetNation(base->pos);
		if (nation)
			Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new base has been built: %s (nation: %s)"), mn_base_title->string, _(nation->name));
		else
			Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new base has been built: %s"), mn_base_title->string);
		MS_AddNewMessage(_("Base built"), cp_messageBuffer, MSG_CONSTRUCTION);

		/* First base */
		if (ccs.campaignStats.basesBuilt == 1)
			B_SetUpFirstBase(campaign, base);

		cgi->Cvar_SetValue("mn_base_count", B_GetCount());
		B_SelectBase(base);
	} else {
		/** @todo Why is this needed? Also see bug #5401 */
		if (GEO_IsRadarOverlayActivated())
			GEO_SetOverlay("radar", 0);

		CP_PopupList(_("Notice"), _("Not enough credits to set up a new base."));
	}
}
예제 #12
0
/**
 * @brief Adds the event mail to the message stack. This message is going to be added to the savegame.
 */
void CL_EventAddMail (const char *eventMailId)
{
	eventMail_t* eventMail = CL_GetEventMail(eventMailId);
	if (!eventMail) {
		Com_Printf("CL_EventAddMail: Could not find eventmail with id '%s'\n", eventMailId);
		return;
	}

	if (eventMail->sent) {
		return;
	}

	if (!eventMail->from || !eventMail->to || !eventMail->subject || !eventMail->body) {
		Com_Printf("CL_EventAddMail: mail with id '%s' has incomplete data\n", eventMailId);
		return;
	}

	if (!eventMail->date) {
		dateLong_t date;
		char dateBuf[MAX_VAR] = "";

		CP_DateConvertLong(&ccs.date, &date);
		Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"),
			date.year, Date_GetMonthName(date.month - 1), date.day);
		eventMail->date = Mem_PoolStrDup(dateBuf, cp_campaignPool, 0);
	}

	eventMail->sent = true;

	if (!eventMail->skipMessage) {
		uiMessageListNodeMessage_t *m = MS_AddNewMessage("", va(_("You've got a new mail: %s"), _(eventMail->subject)), MSG_EVENT);
		if (m)
			m->eventMail = eventMail;
		else
			Com_Printf("CL_EventAddMail: Could not add message with id: %s\n", eventMailId);
	}

	UP_OpenEventMail(eventMailId);
}
/**
 * @brief Starts an aircraft or stops the current mission and let the aircraft idle around.
 */
static void AIM_AircraftStart_f (void)
{
	aircraft_t *aircraft;
	base_t *base = B_GetCurrentSelectedBase();

	if (!base)
		return;

	if (!base->aircraftCurrent) {
		Com_DPrintf(DEBUG_CLIENT, "Error - there is no current aircraft in this base\n");
		return;
	}

	/* Aircraft cannot start without Command Centre operational. */
	if (!B_GetBuildingStatus(base, B_COMMAND)) {
		CP_Popup(_("Notice"), _("No operational Command Centre in this base.\n\nAircraft can not start.\n"));
		return;
	}

	aircraft = base->aircraftCurrent;

	/* Aircraft cannot start without a pilot. */
	if (!AIR_GetPilot(aircraft)) {
		CP_Popup(_("Notice"), _("There is no pilot assigned to this aircraft.\n\nAircraft can not start.\n"));
		return;
	}

	if (AIR_IsAircraftInBase(aircraft)) {
		/* reload its ammunition */
		AII_ReloadAircraftWeapons(aircraft);
	}
	MS_AddNewMessage(_("Notice"), _("Aircraft started"), qfalse, MSG_STANDARD, NULL);
	aircraft->status = AIR_IDLE;

	MAP_SelectAircraft(aircraft);
	/* Return to geoscape. */
	UI_PopWindow(qfalse);
	UI_PopWindow(qfalse);
}
/**
 * @brief Function to start UFO recovery process.
 * @note Command to call this: cp_uforecovery_store_start.
 */
static void UR_DialogStartStore_f (void)
{
	installation_t *installation = nullptr;
	int idx;
	int count = 0;
	date_t date;

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

	idx = atoi(cgi->Cmd_Argv(1));

	INS_Foreach(i) {
		if (i->ufoCapacity.max <= 0 || i->ufoCapacity.max <= i->ufoCapacity.cur)
			continue;

		if (count == idx) {
			installation = i;
			break;
		}
		count++;
	}

	if (!installation)
		return;

	Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("Recovered %s from the battlefield. UFO is being transported to %s."),
			UFO_GetName(ufoRecovery.ufoTemplate), installation->name);
	MS_AddNewMessage(_("UFO Recovery"), cp_messageBuffer);
	date = ccs.date;
	date.day += (int) RECOVERY_DELAY;

	US_StoreUFO(ufoRecovery.ufoTemplate, installation, date, ufoRecovery.condition);
	UR_DialogRecoveryDone();
}
예제 #15
0
파일: cp_messages.cpp 프로젝트: ufoai/ufoai
/**
 * @brief Load callback for messages
 * @param[in] p XML Node structure, where we get the information from
 * @sa MS_SaveXML
 * @sa UI_AddNewMessageSound
 */
bool MS_LoadXML (xmlNode_t* p)
{
	int i;
	xmlNode_t* n, *sn;
	n = cgi->XML_GetNode(p, SAVE_MESSAGES_MESSAGES);

	if (!n)
		return false;

	/* we have to set this a little bit higher here, otherwise the samples that are played when adding
	 * a message to the stack would all played a few milliseconds after each other - that doesn't sound
	 * nice */
	cgi->S_SetSampleRepeatRate(500);

	cgi->Com_RegisterConstList(saveMessageConstants);
	for (sn = cgi->XML_GetNode(n, SAVE_MESSAGES_MESSAGE), i = 0; sn; sn = cgi->XML_GetNextNode(sn, n, SAVE_MESSAGES_MESSAGE), i++) {
		eventMail_t* mail;
		const char* type = cgi->XML_GetString(sn, SAVE_MESSAGES_TYPE);
		int mtype;
		char title[MAX_VAR];
		char text[MAX_MESSAGE_TEXT];
		char id[MAX_VAR];
		technology_t* tech = nullptr;
		uiMessageListNodeMessage_t* mess;

		if (!cgi->Com_GetConstIntFromNamespace(SAVE_MESSAGETYPE_NAMESPACE, type, (int*) &mtype)) {
			cgi->Com_Printf("Invalid message type '%s'\n", type);
			continue;
		}

		/* can contain high bits due to utf8 */
		Q_strncpyz(title, cgi->XML_GetString(sn, SAVE_MESSAGES_TITLE), sizeof(title));
		Q_strncpyz(text,  cgi->XML_GetString(sn, SAVE_MESSAGES_TEXT),  sizeof(text));

		if (mtype == MSG_EVENT) {
			mail = CL_GetEventMail(cgi->XML_GetString(sn, SAVE_MESSAGES_EVENTMAILID));
			if (mail)
				mail->read = cgi->XML_GetBool(sn, SAVE_MESSAGES_EVENTMAILREAD, false);
		} else
			mail = nullptr;

		/* event and not mail means, dynamic mail - we don't save or load them */
		if (mtype == MSG_EVENT && !mail)
			continue;
		if (mtype == MSG_DEBUG && cgi->Cvar_GetInteger("developer") == 0)
			continue;

		Q_strncpyz(id, cgi->XML_GetString(sn, SAVE_MESSAGES_PEDIAID), sizeof(id));
		if (id[0] != '\0')
			tech = RS_GetTechByID(id);
		if (!tech && (mtype == MSG_RESEARCH_PROPOSAL || mtype == MSG_RESEARCH_FINISHED)) {
			/** No tech found drop message. */
			continue;
		}
		mess = MS_AddNewMessage(title, text, (messageType_t)mtype, tech, false, false);
		mess->eventMail = mail;
		cgi->XML_GetDate(sn, SAVE_MESSAGES_DATE, &mess->date.day, &mess->date.sec);
		/* redo timestamp text after setting date */
		MS_TimestampedText(mess->timestamp, mess, sizeof(mess->timestamp));

		if (mail) {
			dateLong_t date;
			char dateBuf[MAX_VAR] = "";

			CP_DateConvertLong(&mess->date, &date);
			Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"),
				date.year, Date_GetMonthName(date.month - 1), date.day);
			mail->date = cgi->PoolStrDup(dateBuf, cp_campaignPool, 0);
		}
	}
	cgi->Com_UnregisterConstList(saveMessageConstants);

	/* reset the sample repeat rate */
	cgi->S_SetSampleRepeatRate(0);

	return true;
}
예제 #16
0
/**
 * @brief Puts alien cargo into Alien Containment.
 * @param[in] aircraft Aircraft transporting cargo to homebase.
 * @sa B_AircraftReturnedToHomeBase
 * @sa AL_FillInContainment
 * @note an event mail about missing breathing tech will be triggered if necessary.
 */
void AL_AddAliens (aircraft_t *aircraft)
{
	base_t *toBase;
	const aliensTmp_t *cargo;
	int alienCargoTypes;
	int i;
	int j;
	qboolean limit = qfalse;
	qboolean messageAlreadySet = qfalse;
	technology_t *breathingTech;
	qboolean alienBreathing = qfalse;
	const objDef_t *alienBreathingObjDef;

	assert(aircraft);
	toBase = aircraft->homebase;
	assert(toBase);

	cargo = AL_GetAircraftAlienCargo(aircraft);
	alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft);

	if (alienCargoTypes == 0)
		return;

	if (!B_GetBuildingStatus(toBase, B_ALIEN_CONTAINMENT)) {
		MS_AddNewMessage(_("Notice"), _("You cannot process aliens yet. Alien Containment not ready in this base."), qfalse, MSG_STANDARD, NULL);
		return;
	}

	breathingTech = RS_GetTechByID(BREATHINGAPPARATUS_TECH);
	if (!breathingTech)
		Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus tech definition");
	alienBreathing = RS_IsResearched_ptr(breathingTech);
	alienBreathingObjDef = INVSH_GetItemByID(breathingTech->provides);
	if (!alienBreathingObjDef)
		Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus item definition");

	for (i = 0; i < alienCargoTypes; i++) {
		for (j = 0; j < ccs.numAliensTD; j++) {
			assert(toBase->alienscont[j].teamDef);
			assert(cargo[i].teamDef);

			if (toBase->alienscont[j].teamDef == cargo[i].teamDef) {
				toBase->alienscont[j].amountDead += cargo[i].amountDead;
				/* Add breathing apparatuses to aircraft cargo so that they are processed with other collected items */
				AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountDead);
				if (cargo[i].amountAlive <= 0)
					continue;
				if (!alienBreathing && !CHRSH_IsTeamDefRobot(cargo[i].teamDef)) {
					/* We can not store living (i.e. no robots or dead bodies) aliens without rs_alien_breathing tech */
					toBase->alienscont[j].amountDead += cargo[i].amountAlive;
					/* Add breathing apparatuses as well */
					AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountAlive);
					/* only once */
					if (!messageAlreadySet) {
						MS_AddNewMessage(_("Notice"), _("You can't hold live aliens yet. Aliens died."), qfalse, MSG_DEATH, NULL);
						messageAlreadySet = qtrue;
					}
					if (!ccs.breathingMailSent) {
						Cmd_ExecuteString("addeventmail alienbreathing");
						ccs.breathingMailSent = qtrue;
					}
				} else {
					int k;

					for (k = 0; k < cargo[i].amountAlive; k++) {
						/* Check base capacity. */
						if (AL_CheckAliveFreeSpace(toBase, NULL, 1)) {
							AL_ChangeAliveAlienNumber(toBase, &(toBase->alienscont[j]), 1);
						} else {
							/* Every exceeding alien is killed
							 * Display a message only when first one is killed */
							if (!limit) {
								toBase->capacities[CAP_ALIENS].cur = toBase->capacities[CAP_ALIENS].max;
								MS_AddNewMessage(_("Notice"), _("You don't have enough space in Alien Containment. Some aliens got killed."), qfalse, MSG_STANDARD, NULL);
								limit = qtrue;
							}
							/* Just kill aliens which don't fit the limit. */
							toBase->alienscont[j].amountDead++;
							AII_CollectItem(aircraft, alienBreathingObjDef, 1);
						}
					}
					/* only once */
					if (!messageAlreadySet) {
						MS_AddNewMessage(_("Notice"), _("You've captured new aliens."), qfalse, MSG_STANDARD, NULL);
						messageAlreadySet = qtrue;
					}
				}
				break;
			}
		}
	}

	for (i = 0; i < ccs.numAliensTD; i++) {
		aliensCont_t *ac = &toBase->alienscont[i];
		technology_t *tech = ac->tech;
#ifdef DEBUG
		if (!tech)
			Sys_Error("AL_AddAliens: Failed to initialize the tech for '%s'\n", ac->teamDef->name);
#endif
		/* we need this to let RS_Collected_ return true */
		if (ac->amountAlive + ac->amountDead > 0)
			RS_MarkCollected(tech);
#ifdef DEBUG
		/* print all of them */
		if (ac->amountAlive > 0)
			Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens alive: %s amount: %i\n", ac->teamDef->name, ac->amountAlive);
		if (ac->amountDead > 0)
			Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens bodies: %s amount: %i\n", ac->teamDef->name, ac->amountDead);
#endif
	}

	/* we shouldn't have any more aliens on the aircraft after this */
	AL_SetAircraftAlienCargoTypes(aircraft, 0);
}
예제 #17
0
파일: cp_airfight.cpp 프로젝트: yason/ufoai
/**
 * @brief Actions to execute when a fight is done.
 * @param[in] campaign The campaign data structure
 * @param[in] shooter Pointer to the aircraft that fired the projectile.
 * @param[in] aircraft Pointer to the aircraft which was destroyed (alien or phalanx).
 * @param[in] phalanxWon true if PHALANX won, false if UFO won.
 * @note Some of these mission values are redone (and not reloaded) in CP_Load
 * @note shooter may be nullptr
 * @sa UFO_DestroyAllUFOsOnGeoscape_f
 * @sa CP_Load
 * @sa CP_SpawnCrashSiteMission
 */
void AIRFIGHT_ActionsAfterAirfight (const campaign_t* campaign, aircraft_t* shooter, aircraft_t* aircraft, bool phalanxWon)
{
	if (phalanxWon) {
		const byte* color;

		assert(aircraft);

		/* change destination of other projectiles aiming aircraft */
		AIRFIGHT_RemoveProjectileAimingAircraft(aircraft);
		/* now update the projectile for the destroyed aircraft, too */
		AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft);

		/* don't remove ufo from global array: the mission is not over yet
		 * UFO are removed from game only at the end of the mission
		 * (in case we need to know what item to collect e.g.) */

		/* get the color value of the map at the crash position */
		color = GEO_GetColor(aircraft->pos, MAPTYPE_TERRAIN, nullptr);
		/* if this color value is not the value for water ...
		 * and we hit the probability to spawn a crashsite mission */
		if (!MapIsWater(color)) {
			CP_SpawnCrashSiteMission(aircraft);
		} else {
			Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ActionsAfterAirfight: zone: %s (%i:%i:%i)\n", cgi->csi->terrainDefs.getTerrainName(color), color[0], color[1], color[2]);
			MS_AddNewMessage(_("Interception"), _("UFO interception successful -- UFO lost to sea."));
			CP_MissionIsOverByUFO(aircraft);
		}

		/* skill increase (for aircraft only, base defences skip) */
		if (shooter) {
			/* Increase targeting skill of pilot who destroyed UFO. Never more than 70, see AIRFIGHT_ProbabilityToHit() */
			shooter->pilot->chr.score.skills[SKILL_TARGETING] += 1;
			shooter->pilot->chr.score.skills[SKILL_TARGETING] = std::min(shooter->pilot->chr.score.skills[SKILL_TARGETING], 70);

			/* Increase evasion skill of pilot who destroyed UFO if the aircraft it attacked can carry weapons.
			 * Never more than 70, see AIRFIGHT_ProbabilityToHit() */
			if (aircraft->maxWeapons > 0) {
				shooter->pilot->chr.score.skills[SKILL_EVADING] += 1;
				shooter->pilot->chr.score.skills[SKILL_EVADING] = std::min(shooter->pilot->chr.score.skills[SKILL_EVADING], 70);
			}
		}
	} else {
		/* change destination of other projectiles aiming aircraft */
		AIRFIGHT_RemoveProjectileAimingAircraft(aircraft);

		/* and now update the projectile pointers (there still might be some in the air
		 * of the current destroyed aircraft) - this is needed not send the aircraft
		 * back to base as soon as the projectiles will hit their target */
		AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft);

		/* notify UFOs that a phalanx aircraft has been destroyed */
		UFO_NotifyPhalanxAircraftRemoved(aircraft);

		if (!MapIsWater(GEO_GetColor(aircraft->pos, MAPTYPE_TERRAIN, nullptr))) {
			CP_SpawnRescueMission(aircraft, shooter);
		} else {
			/* Destroy the aircraft and everything onboard - the aircraft pointer
			 * is no longer valid after this point */
			bool pilotSurvived = false;
			if (AIR_PilotSurvivedCrash(aircraft))
				pilotSurvived = true;

			AIR_DestroyAircraft(aircraft, pilotSurvived);

			if (pilotSurvived)
				MS_AddNewMessage(_("Interception"), _("Pilot ejected from craft"), MSG_STANDARD);
			else
				MS_AddNewMessage(_("Interception"), _("Pilot killed in action"), MSG_STANDARD);
		}

		/* Make UFO proceed with its mission, if it has not been already destroyed */
		if (shooter)
			CP_UFOProceedMission(campaign, shooter);

		MS_AddNewMessage(_("Interception"), _("A PHALANX craft has been destroyed"), MSG_DEATH);
	}
}