Esempio n. 1
0
/**
 * @brief Prepare things for baseattack battle
 * @param[in] mission Mission to prepare battle for
 */
static void CP_BaseAttackPrepareBattle (mission_t* mission)
{
	if (!mission)
		return;

	base_t* base = mission->data.base;

	GEO_SelectMission(mission);
	mission->active = true;
	cgi->Com_DPrintf(DEBUG_CLIENT, "Base attack: %s at %.0f:%.0f\n", mission->id, mission->pos[0], mission->pos[1]);

	/* Fill the fake aircraft */
	OBJZERO(baseAttackFakeAircraft);
	baseAttackFakeAircraft.homebase = base;
	/* needed for transfer of alien corpses */
	VectorCopy(base->pos, baseAttackFakeAircraft.pos);

	/* needed to spawn soldiers on map */
	baseAttackFakeAircraft.maxTeamSize = std::min(MAX_ACTIVETEAM, E_CountByType(EMPL_SOLDIER) + E_CountByType(EMPL_ROBOT));
	baseAttackFakeAircraft.mission = mission;

	base->aircraftCurrent = &baseAttackFakeAircraft;
	GEO_SetMissionAircraft(&baseAttackFakeAircraft);
	/** @todo remove me - this is not needed because we are using the base->aircraftCurrent
	 * pointer for resolving the aircraft - only Autocombat needs this */
	GEO_SetInterceptorAircraft(&baseAttackFakeAircraft);	/* needed for updating soldier stats sa CHAR_UpdateStats */
	B_SetCurrentSelectedBase(base);						/* needed for equipment menu */

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

	CP_GameTimeStop();
	cgi->UI_PushWindow("popup_baseattack");
}
/**
 * @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);
	CP_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 */
	CAP_UpdateStorageCap(base);
	base->aircraftCurrent = NULL;
	base->baseStatus = BASE_WORKING;
}
Esempio n. 3
0
/**
 * @brief Checks capacity overflows on bases
 * @sa CP_CampaignRun
 */
void CAP_CheckOverflow (void)
{
	base_t* base = nullptr;

	while ((base = B_GetNext(base)) != nullptr) {
		for (int i = CAP_ALIENS; i < MAX_CAP; i++) {
			baseCapacities_t capacityType = (baseCapacities_t)i;
			capacities_t* cap = CAP_Get(base, capacityType);

			if (cap->cur <= cap->max)
				continue;

			switch (capacityType) {
			case CAP_ANTIMATTER:
				CAP_RemoveAntimatterExceedingCapacity(base);
				break;
			case CAP_WORKSPACE:
				PR_UpdateProductionCap(base);
				break;
			case CAP_LABSPACE:
				RS_RemoveScientistsExceedingCapacity(base);
				break;
			case CAP_AIRCRAFT_SMALL:
			case CAP_AIRCRAFT_BIG:
			case CAP_ALIENS:
			case CAP_EMPLOYEES:
			case CAP_ITEMS:
				if (base->baseStatus != BASE_DESTROYED) {
					const buildingType_t bldgType = B_GetBuildingTypeByCapacity((baseCapacities_t)i);
					const building_t* bldg = B_GetBuildingTemplateByType(bldgType);
					CP_GameTimeStop();
					cgi->Cmd_ExecuteString("ui_push popup_cap_overload base %d \"%s\" \"%s\" %d %d",
						base->idx, base->name, _(bldg->name), cap->max - cap->cur, cap->max);
				}
				break;
			default:
				/* nothing to do */
				break;
			}
		}
	}
}
Esempio n. 4
0
/**
 * @brief Adds a new message to message stack
 * @note These are the messages that are displayed at geoscape
 * @param[in] title Already translated message/mail title
 * @param[in] text Already translated message/mail body
 * @param[in] popup Show this as a popup, too?
 * @param[in] type The message type
 * @param[in] pedia Pointer to technology (only if needed)
 * @param[in] playSound Whether the sound associated with the message type should be played
 * @return message_t pointer
 * @sa UP_OpenMail_f
 * @sa CL_EventAddMail_f
 * @note this method forwards to @c MS_AddNewMessageSound with @code playSound = true @endcode
 */
uiMessageListNodeMessage_t* MS_AddNewMessage (const char* title, const char* text, messageType_t type, technology_t* pedia, bool popup, bool playSound)
{
	assert(type < MSG_MAX);

	/* allocate memory for new message - delete this with every new game */
	uiMessageListNodeMessage_t* const mess = Mem_PoolAllocType(uiMessageListNodeMessage_t, cp_campaignPool);

	switch (type) {
	case MSG_DEBUG: /**< only save them in debug mode */
		mess->iconName = "icons/message_debug";
		break;
	case MSG_INFO: /**< don't save these messages */
		mess->iconName = "icons/message_info";
		break;
	case MSG_STANDARD:
		mess->iconName = "icons/message_info";
		break;
	case MSG_RESEARCH_PROPOSAL:
		mess->iconName = "icons/message_research";
		break;
	case MSG_RESEARCH_HALTED:
		mess->iconName = "icons/message_research";
		break;
	case MSG_RESEARCH_FINISHED:
		mess->iconName = "icons/message_research";
		break;
	case MSG_CONSTRUCTION:
		mess->iconName = "icons/message_construction";
		break;
	case MSG_UFOSPOTTED:
		mess->iconName = "icons/message_ufo";
		break;
	case MSG_TERRORSITE:
		mess->iconName = "icons/message_ufo";
		break;
	case MSG_BASEATTACK:
		mess->iconName = "icons/message_ufo";
		break;
	case MSG_TRANSFERFINISHED:
		mess->iconName = "icons/message_transfer";
		break;
	case MSG_PROMOTION:
		mess->iconName = "icons/message_promotion";
		break;
	case MSG_PRODUCTION:
		mess->iconName = "icons/message_production";
		break;
	case MSG_DEATH:
		mess->iconName = "icons/message_death";
		break;
	case MSG_CRASHSITE:
		mess->iconName = "icons/message_ufo";
		break;
	case MSG_EVENT:
		mess->iconName = "icons/message_info";
		break;
	default:
		mess->iconName = "icons/message_info";
		break;
	}

	/* push the new message at the beginning of the stack */
	cgi->UI_MessageAddStack(mess);

	mess->type = type;
	mess->pedia = pedia;		/* pointer to UFOpaedia entry */

	mess->date = ccs.date;

	Q_strncpyz(mess->title, title, sizeof(mess->title));
	mess->text = cgi->PoolStrDup(text, cp_campaignPool, 0);

	/* get formatted date text */
	MS_TimestampedText(mess->timestamp, mess, sizeof(mess->timestamp));

	/* they need to be translated already */
	if (popup) {
		CP_GameTimeStop();
		CP_Popup(mess->title, "%s", mess->text);
	}

	if (playSound) {
		const char* sound = nullptr;
		switch (type) {
		case MSG_DEBUG:
			break;
		case MSG_STANDARD:
			sound = "geoscape/standard";
			break;
		case MSG_INFO:
		case MSG_TRANSFERFINISHED:
		case MSG_DEATH:
		case MSG_CONSTRUCTION:
		case MSG_PRODUCTION:
			sound = "geoscape/info";
			break;
		case MSG_RESEARCH_PROPOSAL:
		case MSG_RESEARCH_FINISHED:
			assert(pedia);
		case MSG_RESEARCH_HALTED:
		case MSG_EVENT:
		case MSG_NEWS:
			/* reread the new mails in UP_GetUnreadMails */
			ccs.numUnreadMails = -1;
			sound = "geoscape/mail";
			break;
		case MSG_UFOLOST:
			sound = "geoscape/ufolost";
			break;
		case MSG_UFOSPOTTED:
			sound = "geoscape/ufospotted";
			break;
		case MSG_BASEATTACK:
			sound = "geoscape/basealert";
			break;
		case MSG_TERRORSITE:
			sound = "geoscape/alien-activity";
			break;
		case MSG_CRASHSITE:
			sound = "geoscape/newmission";
			break;
		case MSG_PROMOTION:
			sound = "geoscape/promotion";
			break;
		case MSG_MAX:
			break;
		}

		cgi->S_StartLocalSample(sound, 1.0f);
	}

	return mess;
}
/**
 * @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);
}