/** * @brief Intercept mission ends: UFO leave earth. * @param[in] mission Pointer to the mission * @param[in] destroyed true if the UFO actually destroyed the installation, false else * @note Intercept mission -- Stage 3 */ void CP_InterceptMissionLeave (mission_t* mission, bool destroyed) { installation_t* installation; assert(mission->ufo); mission->stage = STAGE_RETURN_TO_ORBIT; /* if the mission was an attack of an installation, destroy it */ installation = mission->data.installation; if (installation) { vec3_t missionPos; Vector2Copy(mission->pos, missionPos); missionPos[2] = installation->pos[2]; if (destroyed && VectorCompareEps(missionPos, installation->pos, UFO_EPSILON)) INS_DestroyInstallation(installation); } CP_MissionDisableTimeLimit(mission); UFO_SetRandomDest(mission->ufo); CP_MissionRemoveFromGeoscape(mission); /* Display UFO on geoscape if it is detected */ mission->ufo->landed = false; }
/** * @brief Set base attack mission, and go to base position. * @note Base attack mission -- Stage 1 */ static void CP_BaseAttackGoToBase (mission_t *mission) { base_t *base; mission->stage = STAGE_MISSION_GOTO; base = CP_BaseAttackChooseBase(); if (!base) { Com_Printf("CP_BaseAttackGoToBase: no base found\n"); CP_MissionRemove(mission); return; } mission->data.base = base; mission->mapDef = Com_GetMapDefinitionByID("baseattack"); if (!mission->mapDef) { CP_MissionRemove(mission); Com_Error(ERR_DROP, "Could not find mapdef baseattack"); return; } Vector2Copy(base->pos, mission->pos); mission->posAssigned = qtrue; Com_sprintf(mission->location, sizeof(mission->location), "%s", base->name); if (mission->ufo) { CP_MissionDisableTimeLimit(mission); UFO_SendToDestination(mission->ufo, mission->pos); } else { /* Go to next stage on next frame */ mission->finalDate = ccs.date; } }
/** * @brief Supply mission begins: UFO arrive on earth. * @note Supply mission -- Stage 0 */ static void CP_SupplyMissionCreate (mission_t *mission) { ufoType_t ufoType; mission->stage = STAGE_COME_FROM_ORBIT; /* Maybe base has been destroyed since mission creation ? */ if (!AB_CheckSupplyMissionPossible()) { Com_DPrintf(DEBUG_CLIENT, "No base in game: removing supply mission.\n"); CP_MissionRemove(mission); return; } ufoType = CP_MissionChooseUFO(mission); if (ufoType == UFO_MAX) { Com_DPrintf(DEBUG_CLIENT, "Supply mission can't be spawned without UFO: removing supply mission.\n"); CP_MissionRemove(mission); } else { CP_MissionDisableTimeLimit(mission); mission->ufo = UFO_AddToGeoscape(ufoType, NULL, mission); if (!mission->ufo) { Com_Printf("CP_SupplyMissionCreate: Could not add UFO '%s', remove mission\n", Com_UFOTypeToShortName(ufoType)); CP_MissionRemove(mission); } } }
/** * @brief Build Base mission ends: UFO leave earth. * @note Build Base mission -- Stage 3 */ static void CP_BuildBaseMissionLeave (mission_t* mission) { assert(mission->ufo); /* there must be an alien base set */ assert(mission->data.alienBase); mission->stage = STAGE_RETURN_TO_ORBIT; CP_MissionDisableTimeLimit(mission); UFO_SetRandomDest(mission->ufo); /* Display UFO on geoscape if it is detected */ mission->ufo->landed = false; }
/** * @brief Base attack mission ends: UFO leave earth. * @note Base attack mission -- Stage 3 */ void CP_BaseAttackMissionLeave (mission_t* mission) { mission->stage = STAGE_RETURN_TO_ORBIT; if (mission->ufo) { CP_MissionDisableTimeLimit(mission); UFO_SetRandomDest(mission->ufo); /* Display UFO on geoscape if it is detected */ mission->ufo->landed = false; } else { /* Go to next stage on next frame */ mission->finalDate = ccs.date; } }
/** * @brief Set ground mission, and go to ground mission pos. * @note Recon mission -- Stage 1 * @note ground mission can be spawned without UFO * @sa CP_ReconMissionSelect */ void CP_ReconMissionGroundGo (mission_t *mission) { const nation_t *nation; mission->stage = STAGE_MISSION_GOTO; /* maybe the UFO just finished a ground mission and starts a new one? */ if (mission->ufo) { CP_MissionRemoveFromGeoscape(mission); mission->ufo->landed = false; } /* Choose a map */ if (CP_ChooseMap(mission, NULL)) { int counter; for (counter = 0; counter < MAX_POS_LOOP; counter++) { if (!CP_GetRandomPosOnGeoscapeWithParameters(mission->pos, mission->mapDef->terrains, mission->mapDef->cultures, mission->mapDef->populations, NULL)) continue; if (MAP_PositionCloseToBase(mission->pos)) continue; mission->posAssigned = true; break; } if (counter >= MAX_POS_LOOP) { Com_Printf("CP_ReconMissionGroundGo: Error, could not set position.\n"); CP_MissionRemove(mission); return; } } else { Com_Printf("CP_ReconMissionGroundGo: No map found, remove mission.\n"); CP_MissionRemove(mission); return; } nation = MAP_GetNation(mission->pos); if (nation) { Com_sprintf(mission->location, sizeof(mission->location), "%s", _(nation->name)); } else { Com_sprintf(mission->location, sizeof(mission->location), "%s", _("No nation")); } if (mission->ufo) { CP_MissionDisableTimeLimit(mission); UFO_SendToDestination(mission->ufo, mission->pos); } else { /* Go to next stage on next frame */ mission->finalDate = ccs.date; } }
/** * @brief Set Harvest mission, and go to mission pos. * @note Harvesting attack mission -- Stage 1 * @todo Remove me when CP_XVIMissionGo will be implemented * This function should take a location close to an XVI infection point * see gameplay proposal on wiki */ void CP_HarvestMissionGo (mission_t *mission) { const nation_t *nation; mission->stage = STAGE_MISSION_GOTO; /* Choose a map */ if (CP_ChooseMap(mission, NULL)) { int counter; linkedList_t *nationList = NULL; const qboolean nationTest = CP_ChooseNation(mission, &nationList); for (counter = 0; counter < MAX_POS_LOOP; counter++) { if (!CP_GetRandomPosOnGeoscapeWithParameters(mission->pos, mission->mapDef->terrains, mission->mapDef->cultures, mission->mapDef->populations, nationTest ? nationList : NULL)) continue; if (MAP_PositionCloseToBase(mission->pos)) continue; mission->posAssigned = qtrue; break; } if (counter >= MAX_POS_LOOP) { Com_Printf("CP_HarvestMissionGo: Error, could not set position.\n"); CP_MissionRemove(mission); return; } LIST_Delete(&nationList); } else { Com_Printf("CP_HarvestMissionGo: No map found, remove mission.\n"); CP_MissionRemove(mission); return; } nation = MAP_GetNation(mission->pos); if (nation) { Com_sprintf(mission->location, sizeof(mission->location), "%s", _(nation->name)); } else { Com_sprintf(mission->location, sizeof(mission->location), "%s", _("No nation")); } if (mission->ufo) { CP_MissionDisableTimeLimit(mission); UFO_SendToDestination(mission->ufo, mission->pos); } else { /* Go to next stage on next frame */ mission->finalDate = ccs.date; } }
/** * @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); }
/** * @brief Set Terror attack mission, and go to Terror attack mission pos. * @note Terror attack mission -- Stage 1 * @note Terror missions can only take place in city: pick one in ccs.cities. */ static void CP_TerrorMissionGo (mission_t *mission) { int counter; mission->stage = STAGE_MISSION_GOTO; /* Choose a map */ for (counter = 0; counter < MAX_POS_LOOP; counter++) { city_t *city = CP_ChooseCity(); if (!city) continue; if (GEO_PositionCloseToBase(city->pos)) continue; if (!CP_ChooseMap(mission, city->pos)) continue; if (CP_TerrorInCity(city)) continue; Vector2Copy(city->pos, mission->pos); mission->data.city = city; mission->posAssigned = true; break; } if (counter >= MAX_POS_LOOP) { Com_DPrintf(DEBUG_CLIENT, "CP_TerrorMissionGo: Could not set position.\n"); CP_MissionRemove(mission); return; } if (mission->ufo) { CP_MissionDisableTimeLimit(mission); UFO_SendToDestination(mission->ufo, mission->pos); } else { /* Go to next stage on next frame */ mission->finalDate = ccs.date; } }
/** * @brief Set Intercept mission: UFO chooses an installation an flies to it. * @note Intercept mission -- Stage 1 */ void CP_InterceptGoToInstallation (mission_t* mission) { installation_t* installation; assert(mission->ufo); mission->stage = STAGE_MISSION_GOTO; installation = CP_InterceptChooseInstallation(mission); if (!installation) { Com_Printf("CP_InterceptGoToInstallation: no installation found\n"); CP_MissionRemove(mission); return; } mission->data.installation = installation; Vector2Copy(installation->pos, mission->pos); mission->posAssigned = true; CP_MissionDisableTimeLimit(mission); UFO_SendToDestination(mission->ufo, mission->pos); }
/** * @brief Subverting Mission ends: UFO leave earth. * @note Build Base mission -- Stage 3 */ static void CP_BuildBaseGovernmentLeave (const campaign_t* campaign, mission_t* mission) { nation_t* nation; assert(mission); assert(mission->ufo); mission->stage = STAGE_RETURN_TO_ORBIT; /* Mission is a success: government is subverted => lower happiness */ nation = GEO_GetNation(mission->pos); /** @todo when the mission is created, we should select a position where nation exists, * otherwise subverting a government is meaningless */ if (nation) { const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation); NAT_SetHappiness(campaign->minhappiness, nation, stats->happiness + HAPPINESS_SUBVERSION_LOSS); } CP_MissionDisableTimeLimit(mission); UFO_SetRandomDest(mission->ufo); /* Display UFO on geoscape if it is detected */ mission->ufo->landed = false; }
/** * @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; employee_t *employee; 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; } base->baseStatus = BASE_UNDER_ATTACK; ccs.campaignStats.basesAttacked++; #if 0 /** @todo implement onattack: add it to basemanagement.ufo and implement functions */ if (base->onAttack[0] != '\0') /* execute next frame */ Cbuf_AddText(va("%s %i", base->onAttack, base->id)); #endif 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 */ 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; } LIST_Foreach(hiredSoldiersInBase, employee_t, employee) { if (E_IsAwayFromBase(employee)) continue; AIR_AddToAircraftTeam(&baseAttackFakeAircraft, employee); } if (AIR_GetTeamSize(&baseAttackFakeAircraft) == 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 CL_GameAutoGo needs this */ MAP_SetInterceptorAircraft(&baseAttackFakeAircraft); /* needed for updating soldier stats sa CL_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); CL_GameTimeStop(); UI_PushWindow("popup_baseattack", NULL, NULL); }