cp_missions.c

Go to the documentation of this file.
00001 
00006 /*
00007 Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 This program is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU General Public License
00011 as published by the Free Software Foundation; either version 2
00012 of the License, or (at your option) any later version.
00013 
00014 This program is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018 See the GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023 
00024 */
00025 
00026 #include "../cl_shared.h"
00027 #include "../cl_renderer.h" /* refdef */
00028 #include "../cl_team.h"
00029 #include "../cl_game.h"
00030 #include "../ui/ui_main.h"
00031 #include "cp_campaign.h"
00032 #include "cp_map.h"
00033 #include "cp_ufo.h"
00034 #include "cp_alienbase.h"
00035 #include "cp_alien_interest.h"
00036 #include "cp_missions.h"
00037 #include "cp_time.h"
00038 #include "cp_xvi.h"
00039 #include "save/save_missions.h"
00040 #include "save/save_interest.h"
00041 
00043 const int MAX_POS_LOOP = 10;
00044 
00046 static const float MIN_CRASHEDUFO_CONDITION = 0.2f;
00047 static const float MAX_CRASHEDUFO_CONDITION = 0.81f;
00048 
00049 /*====================================
00050 *
00051 * Prepare battlescape
00052 *
00053 ====================================*/
00054 
00060 void CP_SetMissionVars (const mission_t *mission)
00061 {
00062     int i;
00063 
00064     assert(mission->mapDef);
00065 
00066     /* start the map */
00067     Cvar_SetValue("ai_numaliens", (float) ccs.battleParameters.aliens);
00068     Cvar_SetValue("ai_numcivilians", (float) ccs.battleParameters.civilians);
00069     Cvar_Set("ai_civilian", ccs.battleParameters.civTeam);
00070     Cvar_Set("ai_equipment", ccs.battleParameters.alienEquipment);
00071 
00072     /* now store the alien teams in the shared csi struct to let the game dll
00073      * have access to this data, too */
00074     csi.numAlienTeams = 0;
00075     for (i = 0; i < ccs.battleParameters.alienTeamGroup->numAlienTeams; i++) {
00076         csi.alienTeams[i] = ccs.battleParameters.alienTeamGroup->alienTeams[i];
00077         csi.numAlienTeams++;
00078     }
00079 
00080     Com_DPrintf(DEBUG_CLIENT, "..numAliens: %i\n..numCivilians: %i\n..numalienTeams: %i\n..civTeam: '%s'\n..alienEquip: '%s'\n",
00081         ccs.battleParameters.aliens,
00082         ccs.battleParameters.civilians,
00083         ccs.battleParameters.alienTeamGroup->numAlienTeams,
00084         ccs.battleParameters.civTeam,
00085         ccs.battleParameters.alienEquipment);
00086 }
00087 
00096 void CP_StartMissionMap (mission_t* mission)
00097 {
00098     const char *param = NULL;
00099 
00100     /* prepare */
00101     UI_InitStack(NULL, "singleplayermission", qtrue, qfalse);
00102 
00103     assert(mission->mapDef->map);
00104 
00109     refdef.mapZone = ccs.battleParameters.zoneType;
00110 
00111     /* base attack maps starts with a dot */
00112     if (mission->mapDef->map[0] == '.') {
00113         base_t *base = (base_t*)mission->data;
00114 
00115         if (mission->category != INTERESTCATEGORY_BASE_ATTACK)
00116             Com_Printf("Baseattack map on non-baseattack mission! (id=%s, category=%d)\n", mission->id, mission->category);
00117         /* assemble a random base */
00118         if (!base)
00119             Com_Error(ERR_DROP, "Baseattack map without base!\n");
00120         /* base must be under attack and might not have been destroyed in the meantime. */
00121         B_AssembleMap(base);
00122 
00123         return;
00124     }
00125 
00126     SAV_QuickSave();
00127 
00128     if (ccs.battleParameters.param)
00129         param = ccs.battleParameters.param;
00130     else
00131         param = mission->mapDef->param;
00132 
00133     if (mission->mapDef->hurtAliens)
00134         Cvar_Set("sv_hurtaliens", "1");
00135     else
00136         Cvar_Set("sv_hurtaliens", "0");
00137 
00138     Cbuf_AddText(va("map %s %s %s\n", (MAP_IsNight(mission->pos) ? "night" : "day"),
00139         mission->mapDef->map, param ? param : ""));
00140 
00141     /* let the (local) server know which map we are running right now */
00142     cls.currentMD = mission->mapDef;
00143 }
00144 
00151 static qboolean CP_IsAlienTeamForCategory (const alienTeamCategory_t const *cat, const interestCategory_t missionCat)
00152 {
00153     int i;
00154 
00155     for (i = 0; i < cat->numMissionCategories; i++) {
00156         if (missionCat == cat->missionCategories[i])
00157             return qtrue;
00158     }
00159 
00160     return qfalse;
00161 }
00162 
00167 static void CP_SetAlienTeamByInterest (const mission_t *mission)
00168 {
00169     int i, j;
00170     const int MAX_AVAILABLE_GROUPS = 4;
00171     alienTeamGroup_t *availableGroups[MAX_AVAILABLE_GROUPS];
00172     int numAvailableGroup = 0;
00173 
00174     /* Find all available alien team groups */
00175     for (i = 0; i < ccs.numAlienCategories; i++) {
00176         alienTeamCategory_t *cat = &ccs.alienCategories[i];
00177 
00178         /* Check if this alien team category may be used */
00179         if (!CP_IsAlienTeamForCategory(cat, mission->category))
00180             continue;
00181 
00182         /* Find all available team groups for current alien interest
00183          * use mission->initialOverallInterest and not ccs.overallInterest:
00184          * the alien team should not change depending on when you encounter it */
00185         for (j = 0; j < cat->numAlienTeamGroups; j++) {
00186             if (cat->alienTeamGroups[j].minInterest <= mission->initialOverallInterest
00187              && cat->alienTeamGroups[j].maxInterest > mission->initialOverallInterest)
00188                 availableGroups[numAvailableGroup++] = &cat->alienTeamGroups[j];
00189         }
00190     }
00191 
00192     if (!numAvailableGroup)
00193         Com_Error(ERR_DROP, "CP_SetAlienTeamByInterest: no available alien team for mission '%s': interest = %i -- category = %i",
00194             mission->id, mission->initialOverallInterest, mission->category);
00195 
00196     /* Pick up one group randomly */
00197     i = rand() % numAvailableGroup;
00198 
00199     /* store this group for latter use */
00200     ccs.battleParameters.alienTeamGroup = availableGroups[i];
00201 }
00202 
00209 static qboolean CP_IsAlienEquipmentSelectable (const mission_t *mission, const equipDef_t *equip)
00210 {
00211     const alienTeamGroup_t const *group = ccs.battleParameters.alienTeamGroup;
00212     const linkedList_t const *equipPack = ccs.alienCategories[group->categoryIdx].equipment;
00213 
00214     if (mission->initialOverallInterest > equip->maxInterest || mission->initialOverallInterest <= equip->minInterest)
00215         return qfalse;
00216 
00217     while (equip->name != NULL && equipPack != NULL) {
00218         if (!strncmp((const char*)equipPack->data, equip->name, strlen((const char*)equipPack->data)))
00219             return qtrue;
00220         equipPack = equipPack->next;
00221     }
00222 
00223     return qfalse;
00224 }
00225 
00232 static void CP_SetAlienEquipmentByInterest (const mission_t *mission)
00233 {
00234     int i, randomNum, availableEquipDef = 0;
00235 
00236     /* look for all available fitting alien equipement definitions
00237      * use mission->initialOverallInterest and not ccs.overallInterest: the alien equipment should not change depending on
00238      * when you encounter it */
00239     for (i = 0; i < csi.numEDs; i++) {
00240         const equipDef_t *ed = &csi.eds[i];
00241         if (CP_IsAlienEquipmentSelectable(mission, ed))
00242             availableEquipDef++;
00243     }
00244 
00245     Com_DPrintf(DEBUG_CLIENT, "CP_SetAlienEquipmentByInterest: %i available equipment packs for mission %s\n", availableEquipDef, mission->id);
00246 
00247     if (!availableEquipDef)
00248         Com_Error(ERR_DROP, "CP_SetAlienEquipmentByInterest: no available alien equipment for mission '%s'", mission->id);
00249 
00250     /* Choose an alien equipment definition -- between 0 and availableStage - 1 */
00251     randomNum = rand() % availableEquipDef;
00252 
00253     availableEquipDef = 0;
00254     for (i = 0; i < csi.numEDs; i++) {
00255         const equipDef_t *ed = &csi.eds[i];
00256         if (CP_IsAlienEquipmentSelectable(mission, ed)) {
00257             if (availableEquipDef == randomNum) {
00258                 Com_sprintf(ccs.battleParameters.alienEquipment, sizeof(ccs.battleParameters.alienEquipment), "%s", ed->name);
00259                 break;
00260             } else
00261                 availableEquipDef++;
00262         }
00263     }
00264 
00265     Com_DPrintf(DEBUG_CLIENT, "CP_SetAlienEquipmentByInterest: selected available equipment pack '%s'\n", ccs.battleParameters.alienEquipment);
00266 }
00267 
00273 static void CP_CreateAlienTeam (mission_t *mission)
00274 {
00275     int numAliens;
00276 
00277     assert(mission->posAssigned);
00278 
00279     numAliens = max(4, 4 + (int) ccs.overallInterest / 50 + (rand() % 3) - 1);
00280     if (mission->ufo && mission->ufo->maxTeamSize && numAliens > mission->ufo->maxTeamSize)
00281         numAliens = mission->ufo->maxTeamSize;
00282     if (numAliens > mission->mapDef->maxAliens)
00283         numAliens = mission->mapDef->maxAliens;
00284     ccs.battleParameters.aliens = numAliens;
00285 
00286     CP_SetAlienTeamByInterest(mission);
00287 
00288     CP_SetAlienEquipmentByInterest(mission);
00289 }
00290 
00295 static void CP_CreateCivilianTeam (const mission_t *mission)
00296 {
00297     nation_t *nation;
00298 
00299     assert(mission->posAssigned);
00300 
00301     ccs.battleParameters.civilians = MAP_GetCivilianNumberByPosition(mission->pos);
00302 
00303     nation = MAP_GetNation(mission->pos);
00304     ccs.battleParameters.nation = nation;
00305     if (nation) {
00307         Q_strncpyz(ccs.battleParameters.civTeam, nation->id, sizeof(ccs.battleParameters.civTeam));
00308     } else {
00309         Q_strncpyz(ccs.battleParameters.civTeam, "europe", sizeof(ccs.battleParameters.civTeam));
00310     }
00311 }
00312 
00321 void CP_CreateBattleParameters (mission_t *mission, battleParam_t *param)
00322 {
00323     const char *zoneType;
00324     const byte *color;
00325 
00326     assert(mission->posAssigned);
00327 
00328     CP_CreateAlienTeam(mission);
00329     CP_CreateCivilianTeam(mission);
00330 
00331     /* Reset parameters */
00332     if (param->param) {
00333         Mem_Free(param->param);
00334         param->param = NULL;
00335     }
00336 
00337     param->mission = mission;
00338     color = MAP_GetColor(mission->pos, MAPTYPE_TERRAIN);
00339     zoneType = MAP_GetTerrainType(color);
00340     param->zoneType = zoneType; /* store to terrain type for texture replacement */
00341     /* Is there a UFO to recover ? */
00342     if (mission->ufo) {
00343         const aircraft_t *ufo = mission->ufo;
00344         const char *shortUFOType;
00345         float ufoCondition;
00346 
00347         if (mission->crashed) {
00348             shortUFOType = Com_UFOCrashedTypeToShortName(ufo->ufotype);
00349             /* Set random map UFO if this is a random map */
00350             if (mission->mapDef->map[0] == '+') {
00351                 /* set battleParameters.param to the ufo type: used for ufocrash random map */
00352                 if (!strcmp(mission->mapDef->id, "ufocrash"))
00353                     param->param = Mem_PoolStrDup(shortUFOType, cp_campaignPool, 0);
00354             }
00355             ufoCondition = frand() * (MAX_CRASHEDUFO_CONDITION - MIN_CRASHEDUFO_CONDITION) + MIN_CRASHEDUFO_CONDITION;
00356         } else {
00357             shortUFOType = Com_UFOTypeToShortName(ufo->ufotype);
00358             ufoCondition = 1.0f;
00359         }
00360 
00361         Com_sprintf(mission->onwin, sizeof(mission->onwin), "cp_uforecovery_init %s %f", ufo->id, ufoCondition);
00362         /* Set random map UFO if this is a random map */
00363         if (mission->mapDef->map[0] == '+') {
00364             /* set rm_ufo to the ufo type used */
00365             Cvar_Set("rm_ufo", Com_GetRandomMapAssemblyNameForCraft(shortUFOType));
00366         }
00367     }
00368 
00369     /* Set random map aircraft if this is a random map */
00370     if (mission->mapDef->map[0] == '+') {
00371         Cvar_Set("rm_drop", Com_GetRandomMapAssemblyNameForCraft(ccs.missionAircraft->id));
00372     }
00373 }
00374 
00375 /*====================================
00376 *
00377 * Get informations from mission list
00378 *
00379 ====================================*/
00380 
00386 mission_t* CP_GetMissionByIDSilent (const char *missionId)
00387 {
00388     const linkedList_t *list = ccs.missions;
00389 
00390     if (!missionId)
00391         return NULL;
00392 
00393     for (;list; list = list->next) {
00394         mission_t *mission = (mission_t *)list->data;
00395         if (!strcmp(mission->id, missionId))
00396             return mission;
00397     }
00398 
00399     return NULL;
00400 }
00401 
00407 mission_t* CP_GetMissionByID (const char *missionId)
00408 {
00409     mission_t *mission = CP_GetMissionByIDSilent(missionId);
00410 
00411     if (!missionId)
00412         Com_Printf("CP_GetMissionByID: missionId was NULL!\n");
00413     else if (!mission)
00414         Com_Printf("CP_GetMissionByID: Could not find mission %s\n", missionId);
00415 
00416     return mission;
00417 }
00418 
00422 mission_t* MAP_GetMissionByIDX (int id)
00423 {
00424     const linkedList_t *list;
00425 
00426     /* first element start to 1 */
00427     if (id == 0)
00428         return NULL;
00429 
00430     for (list = ccs.missions; list; list = list->next) {
00431         mission_t *mission = (mission_t *)list->data;
00432         if (mission->idx == id)
00433             return mission;
00434     }
00435 
00436     Com_Printf("MAP_GetMissionByIDX: No mission of id %i\n", id);
00437     return NULL;
00438 }
00439 
00443 int MAP_GetIDXByMission (const mission_t *mis)
00444 {
00445     return mis->idx;
00446 }
00447 
00451 const char* CP_MissionToTypeString (const mission_t *mission)
00452 {
00453     if (mission->category == INTERESTCATEGORY_RESCUE)
00454         return _("Crashed aircraft");
00455 
00456     switch (mission->stage) {
00457     case STAGE_RECON_GROUND:
00458     case STAGE_SPREAD_XVI:
00459         return _("Landed UFO");
00460     case STAGE_TERROR_MISSION:
00461         return _("Terror mission");
00462     case STAGE_BASE_ATTACK:
00463         return _("Base attack");
00464     case STAGE_BASE_DISCOVERED:
00465         return _("Alien base");
00466     case STAGE_HARVEST:
00467         return _("Harvesting mission");
00468     default:
00469         return _("Crashed UFO");
00470     }
00471 }
00472 
00473 #ifdef DEBUG
00474 
00477 static const char* CP_MissionCategoryToName (interestCategory_t category)
00478 {
00479     switch (category) {
00480     case INTERESTCATEGORY_NONE:
00481         return "None";
00482     case INTERESTCATEGORY_RECON:
00483         return "Recon Mission";
00484     case INTERESTCATEGORY_TERROR_ATTACK:
00485         return "Terror mission";
00486     case INTERESTCATEGORY_BASE_ATTACK:
00487         return "Base attack";
00488     case INTERESTCATEGORY_BUILDING:
00489         return "Building Base or Subverting Government";
00490     case INTERESTCATEGORY_SUPPLY:
00491         return "Supply base";
00492     case INTERESTCATEGORY_XVI:
00493         return "XVI propagation";
00494     case INTERESTCATEGORY_INTERCEPT:
00495         return "Interception";
00496     case INTERESTCATEGORY_HARVEST:
00497         return "Harvest";
00498     case INTERESTCATEGORY_ALIENBASE:
00499         return "Alien base discovered";
00500     case INTERESTCATEGORY_RESCUE:
00501         return "Rescue mission";
00502     case INTERESTCATEGORY_MAX:
00503         return "Unknown mission category";
00504     }
00505 
00506     /* Can't reach this point */
00507     return "";
00508 }
00509 
00514 static const char* CP_MissionStageToName (const missionStage_t stage)
00515 {
00516     switch (stage) {
00517     case STAGE_NOT_ACTIVE:
00518         return "Not active yet";
00519     case STAGE_COME_FROM_ORBIT:
00520         return "UFO coming from orbit";
00521     case STAGE_RECON_AIR:
00522         return "Aerial recon underway";
00523     case STAGE_MISSION_GOTO:
00524         return "Going to mission position";
00525     case STAGE_RECON_GROUND:
00526         return "Ground recon mission underway";
00527     case STAGE_TERROR_MISSION:
00528         return "Terror mission underway";
00529     case STAGE_BUILD_BASE:
00530         return "Building base";
00531     case STAGE_BASE_ATTACK:
00532         return "Attacking a base";
00533     case STAGE_SUBVERT_GOV:
00534         return "Subverting a government";
00535     case STAGE_SUPPLY:
00536         return "Supplying";
00537     case STAGE_SPREAD_XVI:
00538         return "Spreading XVI";
00539     case STAGE_INTERCEPT:
00540         return "Intercepting or attacking installation";
00541     case STAGE_RETURN_TO_ORBIT:
00542         return "Leaving earth";
00543     case STAGE_BASE_DISCOVERED:
00544         return "Base visible";
00545     case STAGE_HARVEST:
00546         return "Harvesting";
00547     case STAGE_OVER:
00548         return "Mission over";
00549     }
00550 
00551     /* Can't reach this point */
00552     return "";
00553 }
00554 #endif
00555 
00562 int CP_CountMission (void)
00563 {
00564     int counterMission = 0;
00565     const linkedList_t *list = ccs.missions;
00566 #ifdef DEBUG
00567     int counterInvalidMission = 0;
00568 #endif
00569 
00570     for (; list; list = list->next, counterMission++) {
00571 #ifdef DEBUG
00572         const mission_t *mission = (mission_t *)list->data;
00573         /* check whether current selected gametype is a valid one */
00574         if (mission->stage == STAGE_OVER) {
00575             counterInvalidMission++;
00576         }
00577 #endif
00578     }
00579 
00580 #ifdef DEBUG
00581     if (counterInvalidMission)
00582         Com_Printf("CP_CountMission: Warning, there are %i mission that should have been removed from global mission array\n", counterInvalidMission);
00583 #endif
00584     return counterMission;
00585 }
00586 
00593 int CP_CountMissionActive (void)
00594 {
00595     int counterMission = 0;
00596     int counterActiveMission = 0;
00597 
00598     const linkedList_t *list = ccs.missions;
00599 
00600     for (; list; list = list->next, counterMission++) {
00601         const mission_t *mission = (mission_t *)list->data;
00602         /* check whether current selected gametype is a valid one */
00603         if (mission->stage != STAGE_NOT_ACTIVE && mission->stage != STAGE_OVER) {
00604             counterActiveMission++;
00605         }
00606     }
00607 
00608     Com_DPrintf(DEBUG_CLIENT, "Total number of mission: %i -- Number of active mission: %i\n", counterMission, counterActiveMission);
00609     return counterActiveMission;
00610 }
00611 
00618 int CP_CountMissionOnGeoscape (void)
00619 {
00620     int counterVisibleMission = 0;
00621 
00622     const linkedList_t *list = ccs.missions;
00623 
00624     for (; list; list = list->next) {
00625         const mission_t *mission = (mission_t *)list->data;
00626         /* check whether current selected gametype is a valid one */
00627         if (mission->stage != STAGE_NOT_ACTIVE && mission->stage != STAGE_OVER && mission->onGeoscape) {
00628             counterVisibleMission++;
00629         }
00630     }
00631 
00632     return counterVisibleMission;
00633 }
00634 
00635 
00636 /*====================================
00637 *
00638 * Functions relative to geoscape
00639 *
00640 ====================================*/
00641 
00647 const char* MAP_GetMissionModel (const mission_t *mission)
00648 {
00649     /* Mission shouldn't be drawn on geoscape if mapDef is not defined */
00650     assert(mission->mapDef);
00651 
00652     if (mission->crashed)
00653         return "geoscape/ufocrash";
00654 
00655     if (mission->mapDef->storyRelated && mission->category != INTERESTCATEGORY_ALIENBASE)
00657         return "geoscape/mission";
00658 
00659     Com_DPrintf(DEBUG_CLIENT, "Mission is %s, %d\n", mission->id, mission->category);
00660     switch (mission->category) {
00662     case INTERESTCATEGORY_RESCUE:
00663     case INTERESTCATEGORY_RECON:
00664     case INTERESTCATEGORY_XVI:
00665     case INTERESTCATEGORY_HARVEST:
00666     case INTERESTCATEGORY_TERROR_ATTACK:
00667     case INTERESTCATEGORY_BUILDING:
00668         return "geoscape/mission";
00669     case INTERESTCATEGORY_ALIENBASE:
00671         return "geoscape/alienbase";
00672     /* Should not be reached, these mission categories are not drawn on geoscape */
00673     case INTERESTCATEGORY_BASE_ATTACK:
00674         return "geoscape/base2";
00675     case INTERESTCATEGORY_SUPPLY:
00676     case INTERESTCATEGORY_INTERCEPT:
00677     case INTERESTCATEGORY_NONE:
00678     case INTERESTCATEGORY_MAX:
00679         break;
00680     }
00681     Com_Error(ERR_DROP, "Unknown mission interest category");
00682 }
00683 
00689 static missionDetectionStatus_t CP_CheckMissionVisibleOnGeoscape (const mission_t const *mission)
00690 {
00691     /* This function could be called before position of the mission is defined */
00692     if (!mission->posAssigned)
00693         return qfalse;
00694 
00695     if (mission->crashed)
00696         return MISDET_ALWAYS_DETECTED;
00697 
00698     if (mission->ufo && mission->ufo->detected && mission->ufo->landed)
00699         return MISDET_ALWAYS_DETECTED;
00700 
00701     if (mission->category == INTERESTCATEGORY_RESCUE)
00702         return MISDET_ALWAYS_DETECTED;
00703 
00704     switch (mission->stage) {
00705     case STAGE_TERROR_MISSION:
00706     case STAGE_BASE_DISCOVERED:
00707         return MISDET_ALWAYS_DETECTED;
00708     case STAGE_RECON_GROUND:
00709     case STAGE_SUBVERT_GOV:
00710     case STAGE_SPREAD_XVI:
00711     case STAGE_HARVEST:
00712         if (RADAR_CheckRadarSensored(mission->pos))
00713             return MISDET_MAY_BE_DETECTED;
00714         break;
00715     case STAGE_COME_FROM_ORBIT:
00716     case STAGE_MISSION_GOTO:
00717     case STAGE_INTERCEPT:
00718     case STAGE_RECON_AIR:
00719     case STAGE_RETURN_TO_ORBIT:
00720     case STAGE_NOT_ACTIVE:
00721     case STAGE_BUILD_BASE:
00722     case STAGE_BASE_ATTACK:
00723     case STAGE_OVER:
00724     case STAGE_SUPPLY:
00725         break;
00726     }
00727     return MISDET_CANT_BE_DETECTED;
00728 }
00729 
00733 void CP_MissionRemoveFromGeoscape (mission_t *mission)
00734 {
00735     if (!mission->onGeoscape && mission->category != INTERESTCATEGORY_BASE_ATTACK)
00736         return;
00737 
00738     mission->onGeoscape = qfalse;
00739 
00740     /* Notifications */
00741     MAP_NotifyMissionRemoved(mission);
00742     AIR_AircraftsNotifyMissionRemoved(mission);
00743 }
00744 
00750 static inline messageType_t CP_MissionGetMessageLevel (const mission_t *mission)
00751 {
00752     switch (mission->stage) {
00753     case STAGE_BASE_ATTACK:
00754         return MSG_BASEATTACK;
00755     case STAGE_TERROR_MISSION:
00756         return MSG_TERRORSITE;
00757     default:
00758         break;
00759     }
00760 
00761     if (mission->crashed)
00762         return MSG_CRASHSITE;
00763     return MSG_STANDARD;
00764 }
00765 
00771 static inline const char *CP_MissionGetMessage (const mission_t *mission)
00772 {
00773     if (mission->category == INTERESTCATEGORY_RESCUE)
00774         Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Go on a rescue mission at %s. You might some of your soldiers alive."), mission->location);
00775     else
00776         Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Alien activity has been detected in %s."), mission->location);
00777 
00778     return cp_messageBuffer;
00779 }
00780 
00787 void CP_MissionAddToGeoscape (mission_t *mission, qboolean force)
00788 {
00789     const missionDetectionStatus_t status = CP_CheckMissionVisibleOnGeoscape(mission);
00790 
00791     /* don't show a mission spawned by a undetected ufo, unless forced : this may be done at next detection stage */
00792     if (status == MISDET_CANT_BE_DETECTED || (!force && status == MISDET_MAY_BE_DETECTED && mission->ufo && !mission->ufo->detected))
00793         return;
00794 
00795 #ifdef DEBUG
00796     /* UFO that spawned this mission should be close of mission */
00797     if (mission->ufo && ((fabs(mission->ufo->pos[0] - mission->pos[0]) > 1.0f) || (fabs(mission->ufo->pos[1] - mission->pos[1]) > 1.0f))) {
00798         Com_Printf("Error: mission (stage: %s) spawned is not at the same location as UFO\n", CP_MissionStageToName(mission->stage));
00799     }
00800 #endif
00801 
00802     /* Notify the player */
00803     MS_AddNewMessage(_("Notice"), CP_MissionGetMessage(mission), qfalse, CP_MissionGetMessageLevel(mission), NULL);
00804 
00805     mission->onGeoscape = qtrue;
00806     CL_GameTimeStop();
00807     MAP_UpdateGeoscapeDock();
00808 }
00809 
00816 qboolean CP_CheckNewMissionDetectedOnGeoscape (void)
00817 {
00818     const linkedList_t *list = ccs.missions;
00819     /* Probability to detect UFO each DETECTION_INTERVAL
00820      * This correspond to 40 percents each 30 minutes (coded this way to be able to
00821      * change DETECTION_INTERVAL without changing the way radar works) */
00822     const float missionDetectionProbability = 0.000125f * DETECTION_INTERVAL;
00823     qboolean newDetection = qfalse;
00824 
00825     for (; list; list = list->next) {
00826         mission_t *mission = (mission_t *)list->data;
00827         const missionDetectionStatus_t status = CP_CheckMissionVisibleOnGeoscape(mission);
00828 
00829         /* only check mission that can be detected, and that are not already detected */
00830         if (status != MISDET_MAY_BE_DETECTED || mission->onGeoscape)
00831             continue;
00832 
00833         /* if there is a ufo assigned, it must not be detected */
00834         assert(!mission->ufo || !mission->ufo->detected);
00835 
00836         if (frand() <= missionDetectionProbability) {
00837             /* mission is detected */
00838             CP_MissionAddToGeoscape(mission, qtrue);
00839 
00840             /* maybe radar is not activated yet (as ufo wasn't detected before) */
00841             if (!MAP_IsRadarOverlayActivated())
00842                 MAP_SetOverlay("radar");
00843 
00844             /* if mission has a UFO, detect the UFO when it takes off */
00845             if (mission->ufo)
00846                 mission->ufo->detected = qtrue;
00847 
00848             newDetection = qtrue;
00849         }
00850     }
00851     return newDetection;
00852 }
00853 
00859 void CP_UpdateMissionVisibleOnGeoscape (void)
00860 {
00861     const linkedList_t *list = ccs.missions;
00862 
00863     for (; list; list = list->next) {
00864         mission_t *mission = (mission_t *)list->data;
00865         if (mission->onGeoscape && CP_CheckMissionVisibleOnGeoscape(mission) == MISDET_CANT_BE_DETECTED) {
00866             /* remove a mission when radar is destroyed */
00867             CP_MissionRemoveFromGeoscape(mission);
00868         } else if (!mission->onGeoscape && CP_CheckMissionVisibleOnGeoscape(mission) == MISDET_ALWAYS_DETECTED) {
00869             /* only show mission that are always detected: mission that have a probability to be detected will be
00870              * tested at next half hour */
00871             CP_MissionAddToGeoscape(mission, qfalse);
00872         }
00873     }
00874 }
00875 
00883 void CP_UFORemoveFromGeoscape (mission_t *mission, qboolean destroyed)
00884 {
00885     assert(mission->ufo);
00886 
00887     /* UFO is landed even if it's destroyed: crash site mission is spawned */
00888     mission->ufo->landed = qtrue;
00889 
00890     /* Notications */
00891     AIR_AircraftsNotifyUFORemoved(mission->ufo, destroyed);
00892     MAP_NotifyUFORemoved(mission->ufo, destroyed);
00893 
00894     if (destroyed) {
00895         linkedList_t *list;
00896 
00897         /* remove UFO from radar and update idx of ufo in radar array */
00898         RADAR_NotifyUFORemoved(mission->ufo, destroyed);
00899 
00900         /* Update UFO idx */
00901         for (list = ccs.missions; list; list = list->next) {
00902             mission_t *removedMission = (mission_t *)list->data;
00903 
00904             if (removedMission->ufo && (removedMission->ufo > mission->ufo))
00905                 removedMission->ufo--;
00906         }
00907 
00908         UFO_RemoveFromGeoscape(mission->ufo);
00909         mission->ufo = NULL;
00910     } else if (mission->ufo->detected && !RADAR_CheckRadarSensored(mission->ufo->pos)) {
00911         /* maybe we use a high speed time: UFO was detected, but flied out of radar
00912          * range since last calculation, and mission is spawned outside radar range */
00913         RADAR_NotifyUFORemoved(mission->ufo, destroyed);
00914     }
00915 }
00916 
00917 
00918 /*====================================
00919 *
00920 * Handling missions
00921 *
00922 *====================================*/
00923 
00928 void CP_MissionRemove (mission_t *mission)
00929 {
00930     linkedList_t *list;
00931 
00932     /* Destroy UFO */
00933     if (mission->ufo)
00934         CP_UFORemoveFromGeoscape(mission, qtrue);       /* for the notifications */
00935 
00936     /* Remove from ccs.battleParameters */
00937     if (mission == ccs.battleParameters.mission)
00938         ccs.battleParameters.mission = NULL;
00939 
00940     /* Notifications */
00941     CP_MissionRemoveFromGeoscape(mission);
00942 
00943     list = ccs.missions;
00944     for (; list; list = list->next) {
00945         mission_t *removedMission = (mission_t *)list->data;
00946         if (removedMission == mission) {
00947             LIST_RemoveEntry(&ccs.missions, list);
00948             Com_DPrintf(DEBUG_CLIENT, "Mission removed from global array: %i missions left\n", CP_CountMission());
00949             return;
00950         }
00951     }
00952 
00953     Com_Printf("CP_MissionRemove: Could not find mission '%s' to remove.\n", mission->id);
00954 #ifdef DEBUG
00955     Com_Printf("   missions in list are: ");
00956     list = ccs.missions;
00957     for (; list; list = list->next) {
00958         const mission_t *removedMission = (mission_t *)list->data;
00959         Com_Printf("'%s', ", removedMission->id);
00960     }
00961     Com_Printf("\n");
00962 #endif
00963 }
00964 
00971 void CP_MissionDisableTimeLimit (mission_t *mission)
00972 {
00973     mission->finalDate.day = 0;
00974 }
00975 
00982 qboolean CP_CheckMissionLimitedInTime (const mission_t *mission)
00983 {
00984     return mission->finalDate.day != 0;
00985 }
00986 
00987 
00988 /*====================================
00989 *
00990 * Notifications
00991 *
00992 ====================================*/
00993 
00997 void CP_MissionNotifyBaseDestroyed (const base_t *base)
00998 {
00999     linkedList_t *list = ccs.missions;
01000 
01001     for (; list; list = list->next) {
01002         mission_t *mission = (mission_t *)list->data;
01003         /* Check if this is a base attack mission attacking this base */
01004         if (mission->category == INTERESTCATEGORY_BASE_ATTACK && mission->data) {
01005             base_t *missionBase = (base_t *) mission->data;
01006             if (base == missionBase) {
01007                 /* Aimed base has been destroyed, abort mission */
01008                 CP_BaseAttackMissionLeave(mission);
01009             }
01010         }
01011     }
01012 }
01013 
01018 void CP_MissionNotifyInstallationDestroyed (const installation_t const *installation)
01019 {
01020     linkedList_t *missionlist = ccs.missions;
01021 
01022     while (missionlist) {
01023         mission_t *mission = (mission_t*) missionlist->data;
01024 
01025         if (mission->category == INTERESTCATEGORY_INTERCEPT && mission->data) {
01026             if ((installation_t*) mission->data == installation)
01027                 CP_InterceptMissionLeave(mission, qfalse);
01028         }
01029         missionlist = missionlist->next;
01030     }
01031 }
01032 
01033 /*====================================
01034 *
01035 * Functions common to several mission type
01036 *
01037 ====================================*/
01038 
01043 void CP_MissionStageEnd (mission_t *mission)
01044 {
01045     Com_DPrintf(DEBUG_CLIENT, "Ending mission category %i, stage %i (time: %i day, %i sec)\n",
01046         mission->category, mission->stage, ccs.date.day, ccs.date.sec);
01047 
01048     /* Crash mission is on the map for too long: aliens die or go away. End mission */
01049     if (mission->crashed) {
01051         CP_MissionIsOver(mission);
01052         return;
01053     }
01054 
01055     switch (mission->category) {
01056     case INTERESTCATEGORY_RECON:
01057         CP_ReconMissionNextStage(mission);
01058         break;
01059     case INTERESTCATEGORY_TERROR_ATTACK:
01060         CP_TerrorMissionNextStage(mission);
01061         break;
01062     case INTERESTCATEGORY_BASE_ATTACK:
01063         CP_BaseAttackMissionNextStage(mission);
01064         break;
01065     case INTERESTCATEGORY_BUILDING:
01066         CP_BuildBaseMissionNextStage(mission);
01067         break;
01068     case INTERESTCATEGORY_SUPPLY:
01069         CP_SupplyMissionNextStage(mission);
01070         break;
01071     case INTERESTCATEGORY_XVI:
01072         CP_XVIMissionNextStage(mission);
01073         break;
01074     case INTERESTCATEGORY_INTERCEPT:
01075         CP_InterceptNextStage(mission);
01076         break;
01077     case INTERESTCATEGORY_HARVEST:
01078         CP_HarvestMissionNextStage(mission);
01079         break;
01080     case INTERESTCATEGORY_ALIENBASE:
01081     case INTERESTCATEGORY_RESCUE:
01082     case INTERESTCATEGORY_NONE:
01083     case INTERESTCATEGORY_MAX:
01084         Com_Printf("CP_MissionStageEnd: Invalid type of mission (%i), remove mission '%s'\n", mission->category, mission->id);
01085         CP_MissionRemove(mission);
01086     }
01087 }
01088 
01093 void CP_MissionIsOver (mission_t *mission)
01094 {
01095     switch (mission->category) {
01096     case INTERESTCATEGORY_RECON:
01097         CP_ReconMissionIsFailure(mission);
01098         break;
01099     case INTERESTCATEGORY_TERROR_ATTACK:
01100         CP_TerrorMissionIsFailure(mission);
01101         break;
01102     case INTERESTCATEGORY_BASE_ATTACK:
01103         if (mission->stage <= STAGE_BASE_ATTACK)
01104             CP_BaseAttackMissionIsFailure(mission);
01105         else
01106             CP_BaseAttackMissionIsSuccess(mission);
01107         break;
01108     case INTERESTCATEGORY_BUILDING:
01109         if (mission->stage <= STAGE_BUILD_BASE)
01110             CP_BuildBaseMissionIsFailure(mission);
01111         else
01112             CP_BuildBaseMissionIsSuccess(mission);
01113         break;
01114     case INTERESTCATEGORY_SUPPLY:
01115         if (mission->stage <= STAGE_SUPPLY)
01116             CP_SupplyMissionIsFailure(mission);
01117         else
01118             CP_SupplyMissionIsSuccess(mission);
01119         break;
01120     case INTERESTCATEGORY_XVI:
01121         if (mission->stage <= STAGE_SPREAD_XVI)
01122             CP_XVIMissionIsFailure(mission);
01123         else
01124             CP_XVIMissionIsSuccess(mission);
01125         break;
01126     case INTERESTCATEGORY_INTERCEPT:
01127         if (mission->stage <= STAGE_INTERCEPT)
01128             CP_InterceptMissionIsFailure(mission);
01129         else
01130             CP_InterceptMissionIsSuccess(mission);
01131         break;
01132     case INTERESTCATEGORY_HARVEST:
01133         CP_HarvestMissionIsFailure(mission);
01134         break;
01135     case INTERESTCATEGORY_ALIENBASE:
01136         CP_BuildBaseMissionBaseDestroyed(mission);
01137         break;
01138     case INTERESTCATEGORY_RESCUE:
01139         CP_MissionRemove(mission);
01140         break;
01141     case INTERESTCATEGORY_NONE:
01142     case INTERESTCATEGORY_MAX:
01143         Com_Printf("CP_MissionIsOver: Invalid type of mission (%i), remove mission\n", mission->category);
01144         CP_MissionRemove(mission);
01145         break;
01146     }
01147 }
01148 
01153 void CP_MissionIsOverByUFO (aircraft_t *ufocraft)
01154 {
01155     assert(ufocraft->mission);
01156     CP_MissionIsOver(ufocraft->mission);
01157 }
01158 
01164 static inline qboolean CP_TransferOfAliensToOtherBaseNeeded (const base_t *base, const missionResults_t *missionResults)
01165 {
01166     assert(missionResults);
01167     return (missionResults->aliensStunned > 0 && !AL_CheckAliveFreeSpace(base, NULL, missionResults->aliensStunned))
01168         || (missionResults->aliensKilled > 0 && !AC_ContainmentAllowed(base));
01169 }
01170 
01177 void CP_MissionEndActions (mission_t *mission, aircraft_t *aircraft, qboolean won)
01178 {
01179     /* handle base attack mission */
01180     if (mission->stage == STAGE_BASE_ATTACK) {
01181         if (won) {
01182             /* fake an aircraft return to collect goods and aliens */
01183             B_DumpAircraftToHomeBase(aircraft);
01184 
01185             Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Defence of base: %s successful!"),
01186                     aircraft->homebase->name);
01187             MS_AddNewMessage(_("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
01188             CP_BaseAttackMissionIsFailure(mission);
01189         } else
01190             CP_BaseAttackMissionDestroyBase(mission);
01191 
01192         return;
01193     }
01194 
01195     if (mission->category == INTERESTCATEGORY_RESCUE) {
01196         if (won)
01197             /* return to collect goods and aliens from the crashed aircraft */
01198             B_DumpAircraftToHomeBase((aircraft_t *)mission->data);
01199         AIR_DestroyAircraft((aircraft_t *)mission->data);
01200     }
01201 
01202     AIR_AircraftReturnToBase(aircraft);
01203     if (won)
01204         CP_MissionIsOver(mission);
01205 }
01206 
01210 void CP_MissionEnd (mission_t* mission, qboolean won)
01211 {
01212     int civiliansKilled;
01213     int aliensKilled;
01214     int i;
01215     base_t *base;
01216     aircraft_t *aircraft;
01217     int numberOfSoldiers = 0; /* DEBUG */
01218 
01219     if (mission->stage == STAGE_BASE_ATTACK) {
01220         base = (base_t *)mission->data;
01221         assert(base);
01222         /* HACK */
01223         aircraft = base->aircraftCurrent;
01224     } else {
01225         aircraft = ccs.missionAircraft;
01226         base = aircraft->homebase;
01227     }
01228 
01229     /* add the looted goods to base storage and market */
01230     base->storage = ccs.eMission;
01231 
01232     civiliansKilled = ccs.civiliansKilled;
01233     aliensKilled = ccs.aliensKilled;
01234     Com_DPrintf(DEBUG_CLIENT, "Won: %d   Civilians: %d/%d   Aliens: %d/%d\n",
01235         won, ccs.battleParameters.civilians - civiliansKilled, civiliansKilled,
01236         ccs.battleParameters.aliens - aliensKilled, aliensKilled);
01237     CL_HandleNationData(won, mission);
01238     CP_CheckLostCondition();
01239 
01240     /* update the character stats */
01241     CP_ParseCharacterData(NULL);
01242 
01243     /* update stats */
01244     CL_UpdateCharacterStats(base, aircraft);
01245 
01246     /* Backward loop because ccs.numEmployees[EMPL_SOLDIER] is decremented by E_DeleteEmployee */
01247     for (i = ccs.numEmployees[EMPL_SOLDIER] - 1; i >= 0; i--) {
01248         employee_t *employee = &ccs.employees[EMPL_SOLDIER][i];
01249 
01250         if (AIR_IsEmployeeInAircraft(employee, aircraft))
01251             numberOfSoldiers++;
01252 
01253         Com_DPrintf(DEBUG_CLIENT, "CP_MissionEnd - try to get player %i \n", i);
01254 
01255         if (E_IsInBase(employee, base)) {
01256             const character_t *chr = &(employee->chr);
01257             Com_DPrintf(DEBUG_CLIENT, "CP_MissionEnd - ucn %d hp %d\n", chr->ucn, chr->HP);
01258             /* if employee is marked as dead */
01259             if (chr->HP <= 0) { 
01260                 /* sideeffect: ccs.numEmployees[EMPL_SOLDIER] and teamNum[] are decremented by one here. */
01261                 Com_DPrintf(DEBUG_CLIENT, "CP_MissionEnd: Delete this dead employee: %i (%s)\n", i, chr->name);
01262                 E_DeleteEmployee(employee, EMPL_SOLDIER);
01263             }
01264         }
01265     }
01266     Com_DPrintf(DEBUG_CLIENT, "CP_MissionEnd - num %i\n", numberOfSoldiers);
01267 
01268     /* Check for alien containment in aircraft homebase. */
01271     if (CP_TransferOfAliensToOtherBaseNeeded(base, &ccs.missionResults)) {
01272         /* We have captured/killed aliens, but the homebase of this aircraft does not have free alien containment space.
01273          * Popup aircraft transfer dialog to choose a better base. */
01274         Cmd_ExecuteString(va("trans_aliens %i", aircraft->idx));
01275     } else {
01276         /* The aircraft can be safely sent to its homebase without losing aliens */
01277     }
01278 
01279     CP_MissionEndActions(mission, aircraft, won);
01280 }
01281 
01288 qboolean CP_CheckNextStageDestination (aircraft_t *ufocraft)
01289 {
01290     mission_t *mission;
01291 
01292     mission = ufocraft->mission;
01293     assert(mission);
01294 
01295     switch (mission->stage) {
01296     case STAGE_COME_FROM_ORBIT:
01297     case STAGE_MISSION_GOTO:
01298         CP_MissionStageEnd(mission);
01299         return qfalse;
01300     case STAGE_RETURN_TO_ORBIT:
01301         CP_MissionStageEnd(mission);
01302         return qtrue;
01303     default:
01304         /* Do nothing */
01305         return qfalse;
01306     }
01307 }
01308 
01313 void CP_UFOProceedMission (aircraft_t *ufo)
01314 {
01315     /* Every UFO on geoscape should have a mission assigned */
01316     assert(ufo->mission);
01317 
01318     if (ufo->mission->category == INTERESTCATEGORY_INTERCEPT && !ufo->mission->data) {
01319         const int slotIndex = AIRFIGHT_ChooseWeapon(ufo->weapons, ufo->maxWeapons, ufo->pos, ufo->pos);
01320         /* This is an Intercept mission where UFO attacks aircraft (not installations) */
01321         /* Keep on looking targets until mission is over, unless no more ammo */
01322         ufo->status = AIR_TRANSIT;
01323         if (slotIndex != AIRFIGHT_WEAPON_CAN_NEVER_SHOOT)
01324             UFO_SetRandomDest(ufo);
01325         else
01326             CP_MissionStageEnd(ufo->mission);
01327     } else if (ufo->mission->stage < STAGE_MISSION_GOTO ||
01328         ufo->mission->stage >= STAGE_RETURN_TO_ORBIT) {
01329         UFO_SetRandomDest(ufo);
01330     } else {
01331         UFO_SendToDestination(ufo, ufo->mission->pos);
01332     }
01333 }
01334 
01338 void CP_SpawnCrashSiteMission (aircraft_t *ufo)
01339 {
01340     const date_t minCrashDelay = {7, 0};
01341     /* How long the crash mission will stay before aliens leave / die */
01342     const date_t crashDelay = {14, 0};
01343     const nation_t *nation;
01344     mission_t *mission;
01345 
01346     mission = ufo->mission;
01347     if (!mission)
01348         Com_Error(ERR_DROP, "CP_SpawnCrashSiteMission: No mission correspond to ufo '%s'", ufo->id);
01349 
01350     mission->crashed = qtrue;
01351 
01352     /* Reset mapDef. CP_ChooseMap don't overwrite if set */
01353     mission->mapDef = NULL;
01354     if (!CP_ChooseMap(mission, ufo->pos)) {
01355         Com_Printf("CP_SpawnCrashSiteMission: No map found, remove mission.\n");
01356         CP_MissionRemove(mission);
01357         return;
01358     }
01359 
01360     Vector2Copy(ufo->pos, mission->pos);
01361     mission->posAssigned = qtrue;
01362 
01363     nation = MAP_GetNation(mission->pos);
01364     if (nation) {
01365         Com_sprintf(mission->location, sizeof(mission->location), "%s", _(nation->name));
01366     } else {
01367         Com_sprintf(mission->location, sizeof(mission->location), "%s", _("No nation"));
01368     }
01369 
01370     mission->finalDate = Date_Add(ccs.date, Date_Random(minCrashDelay, crashDelay));
01371     /* ufo becomes invisible on geoscape, but don't remove it from ufo global array
01372      * (may be used to know what items are collected from battlefield)*/
01373     CP_UFORemoveFromGeoscape(mission, qfalse);
01374     /* mission appear on geoscape, player can go there */
01375     CP_MissionAddToGeoscape(mission, qfalse);
01376 }
01377 
01378 
01385 void CP_SpawnRescueMission (aircraft_t *aircraft, aircraft_t *ufo)
01386 {
01387     const date_t minCrashDelay = {7, 0};
01388     /* How long the crash mission will stay before aliens leave / die */
01389     const date_t crashDelay = {14, 0};
01390     const nation_t *nation;
01391     mission_t *mission;
01392     linkedList_t* l;
01393     employee_t *pilot;
01394 
01395     mission = CP_CreateNewMission(INTERESTCATEGORY_RESCUE, qtrue);
01396     if (!mission)
01397         Com_Error(ERR_DROP, "CP_SpawnRescueMission: mission could not be created");
01398 
01399     /* Reset mapDef. CP_ChooseMap don't overwrite if set */
01400     mission->mapDef = NULL;
01401     if (!CP_ChooseMap(mission, aircraft->pos)) {
01402         CP_MissionRemove(mission);
01403         return;
01404     }
01405 
01406     if (ccs.selectedAircraft == aircraft)
01407         ccs.selectedAircraft = NULL;
01408 
01409     Vector2Copy(aircraft->pos, mission->pos);
01410     mission->posAssigned = qtrue;
01411 
01412     nation = MAP_GetNation(mission->pos);
01413     if (nation) {
01414         Com_sprintf(mission->location, sizeof(mission->location), "%s", _(nation->name));
01415     } else {
01416         Com_sprintf(mission->location, sizeof(mission->location), "%s", _("No nation"));
01417     }
01418 
01419     mission->data = aircraft;
01420     mission->ufo = ufo;
01421     mission->stage = STAGE_TERROR_MISSION;
01422 
01423     for (l = aircraft->acTeam; l != NULL; l = l->next) {
01424         employee_t *employee = (employee_t *)l->data;
01425         /*const character_t *chr = &employee->chr;
01426         const chrScoreGlobal_t *score = &chr->score;*/
01428         AIR_RemoveEmployee(employee, aircraft);
01429     }
01430 
01431     pilot = aircraft->pilot;
01432     AIR_RemovePilotFromAssignedAircraft(aircraft->homebase, pilot);
01433     E_DeleteEmployee(pilot, EMPL_PILOT);
01434 
01435     aircraft->status = AIR_CRASHED;
01436 
01437     /* after we set this to AIR_CRASHED we can get the next 'valid'
01438      * aircraft to correct the pointer in the homebase */
01439     if (aircraft->homebase->aircraftCurrent == aircraft)
01440         aircraft->homebase->aircraftCurrent = AIR_GetNextFromBase(aircraft->homebase, NULL);
01441 
01442     /* a crashed aircraft is no longer using capacity of the hangars */
01443     AIR_UpdateHangarCapForAll(aircraft->homebase);
01444 
01445     mission->finalDate = Date_Add(ccs.date, Date_Random(minCrashDelay, crashDelay));
01446     /* mission appear on geoscape, player can go there */
01447     CP_MissionAddToGeoscape(mission, qfalse);
01448 }
01449 
01450 
01451 /*====================================
01452 *
01453 * Spawn new missions
01454 *
01455 ====================================*/
01456 
01463 qboolean CP_MissionBegin (mission_t *mission)
01464 {
01465     int ufoType;
01466 
01467     mission->stage = STAGE_COME_FROM_ORBIT;
01468 
01469     ufoType = CP_MissionChooseUFO(mission);
01470     if (ufoType == UFO_MAX) {
01471         mission->ufo = NULL;
01472         /* Mission starts from ground: no UFO. Go to next stage on next frame (skip UFO arrives on earth) */
01473         mission->finalDate = ccs.date;
01474     } else {
01475         mission->ufo = UFO_AddToGeoscape(ufoType, NULL, mission);
01476         if (!mission->ufo) {
01477             Com_Printf("CP_MissionBegin: Could not add UFO '%s', remove mission\n",
01478                     Com_UFOTypeToShortName(ufoType));
01479             CP_MissionRemove(mission);
01480             return qfalse;
01481         }
01482         /* Stage will finish when UFO arrives at destination */
01483         CP_MissionDisableTimeLimit(mission);
01484     }
01485 
01486     mission->idx = ++ccs.campaignStats.missions;
01487     return qtrue;
01488 }
01489 
01497 ufoType_t CP_MissionChooseUFO (const mission_t *mission)
01498 {
01499     ufoType_t ufoTypes[UFO_MAX];
01500     int numTypes = 0;
01501     int idx;
01502     qboolean canBeSpawnedFromGround = qfalse;
01503     float groundProbability = 0.0f; 
01504     float randNumber;
01505 
01506     switch (mission->category) {
01507     case INTERESTCATEGORY_RECON:
01508         numTypes = CP_ReconMissionAvailableUFOs(mission, ufoTypes);
01509         canBeSpawnedFromGround = qtrue;
01510         break;
01511     case INTERESTCATEGORY_TERROR_ATTACK:
01512         numTypes = CP_TerrorMissionAvailableUFOs(mission, ufoTypes);
01513         canBeSpawnedFromGround = qtrue;
01514         break;
01515     case INTERESTCATEGORY_BASE_ATTACK:
01516         numTypes = CP_BaseAttackMissionAvailableUFOs(mission, ufoTypes);
01517         canBeSpawnedFromGround = qtrue;
01518         break;
01519     case INTERESTCATEGORY_BUILDING:
01520         numTypes = CP_BuildBaseMissionAvailableUFOs(mission, ufoTypes);
01521         break;
01522     case INTERESTCATEGORY_SUPPLY:
01523         numTypes = CP_SupplyMissionAvailableUFOs(mission, ufoTypes);
01524         break;
01525     case INTERESTCATEGORY_XVI:
01526         numTypes = CP_XVIMissionAvailableUFOs(mission, ufoTypes);
01527         canBeSpawnedFromGround = qtrue;
01528         break;
01529     case INTERESTCATEGORY_INTERCEPT:
01530         numTypes = CP_InterceptMissionAvailableUFOs(mission, ufoTypes);
01531         break;
01532     case INTERESTCATEGORY_HARVEST:
01533         numTypes = CP_HarvestMissionAvailableUFOs(mission, ufoTypes);
01534         break;
01535     case INTERESTCATEGORY_ALIENBASE:
01536         /* can't be spawned: alien base is the result of base building mission */
01537     case INTERESTCATEGORY_RESCUE:
01538     case INTERESTCATEGORY_NONE:
01539     case INTERESTCATEGORY_MAX:
01540         Com_Error(ERR_DROP, "CP_MissionChooseUFO: Wrong mission category %i", mission->category);
01541     }
01542 
01543     if (numTypes > UFO_MAX)
01544         Com_Error(ERR_DROP, "CP_MissionChooseUFO: Too many values UFOs (%i/%i)", numTypes, UFO_MAX);
01545 
01546     /* Roll the random number */
01547     randNumber = frand();
01548 
01549     /* Check if the mission is spawned without UFO */
01550     if (canBeSpawnedFromGround) {
01551         const int XVI_PARAM = 10;       
01552         /* The higher the XVI rate, the higher the probability to have a mission spawned from ground */
01553         groundProbability = max(0.1f, 1.0f - exp(-CP_GetAverageXVIRate() / XVI_PARAM));
01554 
01555         /* Mission spawned from ground */
01556         if (randNumber < groundProbability)
01557             return UFO_MAX;
01558     }
01559 
01560     /* If we reached this point, then mission will be spawned from space: choose UFO */
01561     assert(numTypes);
01562     idx = (int) ((numTypes - 1) * randNumber);
01563     if (idx >= numTypes)
01564         Sys_Error("CP_MissionChooseUFO: idx exceeded: %i (randNumber: %f, groundProbability: %f, numTypes: %i)",
01565                 idx, randNumber, groundProbability, numTypes);
01566 
01567     return ufoTypes[idx];
01568 }
01569 
01576 static inline void CP_SetMissionName (mission_t *mission)
01577 {
01578     int num = 0;
01579 
01580     do {
01581         Com_sprintf(mission->id, sizeof(mission->id), "cat%i_interest%i_%i",
01582             mission->category, mission->initialOverallInterest, num);
01583 
01584         /* if mission list is empty, the id is unique for sure */
01585         if (!ccs.missions)
01586             return;
01587 
01588         /* check whether a mission with the same id already exists in the list
01589          * if so, generate a new name by using an increased num values - otherwise
01590          * just use the mission->id we just stored - it should be unique now */
01591         if (!CP_GetMissionByIDSilent(mission->id))
01592             break;
01593 
01594         num++;
01595     } while (num); /* fake condition */
01596 }
01597 
01605 mission_t *CP_CreateNewMission (interestCategory_t category, qboolean beginNow)
01606 {
01607     mission_t mission;
01608     const date_t minNextSpawningDate = {0, 0};
01609     const date_t nextSpawningDate = {3, 0}; /* Delay between 2 mission spawning */
01610     linkedList_t *list;
01611 
01612     /* Some event are non-occurrence */
01613     if (category <= INTERESTCATEGORY_NONE || category >= INTERESTCATEGORY_MAX)
01614         return NULL;
01615 
01616     memset(&mission, 0, sizeof(mission));
01617 
01618     /* First fill common datas between all type of missions */
01619     mission.category = category;
01620     mission.initialOverallInterest = ccs.overallInterest;
01621     mission.initialIndividualInterest = ccs.interest[category];
01622     mission.stage = STAGE_NOT_ACTIVE;
01623     mission.ufo = NULL;
01624     if (beginNow) {
01625         mission.startDate.day = ccs.date.day;
01626         mission.startDate.sec = ccs.date.sec;
01627     } else
01628         mission.startDate = Date_Add(ccs.date, Date_Random(minNextSpawningDate, nextSpawningDate));
01629     mission.finalDate = mission.startDate;
01630     mission.idx = ++ccs.campaignStats.missions;
01631 
01632     CP_SetMissionName(&mission);
01633 
01634     /* Handle mission specific, spawn-time routines */
01635     if (mission.category == INTERESTCATEGORY_BUILDING)
01636         CP_BuildBaseMissionOnSpawn();
01637     else if (mission.category == INTERESTCATEGORY_BASE_ATTACK)
01638         CP_BaseAttackMissionOnSpawn();
01639     else if (mission.category == INTERESTCATEGORY_TERROR_ATTACK)
01640         CP_TerrorMissionOnSpawn();
01641 
01642     /* Add mission to global array */
01643     list = LIST_Add(&ccs.missions, (byte*) &mission, sizeof(mission));
01644     return (mission_t *)list->data;
01645 }
01646 
01651 static int CP_SelectNewMissionType (void)
01652 {
01653     int sum = 0;
01654     int i, randomNumber;
01655 
01656     for (i = 0; i < INTERESTCATEGORY_MAX; i++)
01657         sum += ccs.interest[i];
01658 
01659     randomNumber = (int) (frand() * (float) sum);
01660 
01661     for (i = 0; randomNumber >= 0; i++)
01662         randomNumber -= ccs.interest[i];
01663 
01664     return i - 1;
01665 }
01666 
01672 void CP_SpawnNewMissions (void)
01673 {
01674     ccs.lastMissionSpawnedDelay++;
01675 
01676     if (ccs.lastMissionSpawnedDelay > DELAY_BETWEEN_MISSION_SPAWNING) {
01677         float nonOccurrence;
01678         int newMissionNum, i;
01679         /* Select the amount of missions that will be spawned in the next cycle. */
01680 
01681         /* Each mission has a certain probability to not occur. This provides some randomness to the mission density.
01682          * However, once the campaign passes a certain point, this effect rapidly diminishes. This means that by the
01683          * end of the game, ALL missions will spawn, quickly overrunning the player. */
01684         if (ccs.overallInterest > FINAL_OVERALL_INTEREST)
01685             nonOccurrence = NON_OCCURRENCE_PROBABILITY / pow(((ccs.overallInterest - FINAL_OVERALL_INTEREST / 30) + 1), 2);
01686         else
01687             nonOccurrence = NON_OCCURRENCE_PROBABILITY;
01688 
01689         /* Increase the alien's interest in supplying their bases for this cycle.
01690          * The more bases, the greater their interest in supplying them. */
01691         CL_ChangeIndividualInterest(AB_GetAlienBaseNumber() * 0.1f, INTERESTCATEGORY_SUPPLY);
01692 
01693         /* Calculate the amount of missions to be spawned this cycle.
01694          * Note: This is a function over css.overallInterest. It looks like this:
01695          * http://www.wolframalpha.com/input/?i=Plot%5B40%2B%285-40%29%2A%28%28x-1000%29%2F%2820-1000%29%29%5E2%2C+%7Bx%2C+0%2C+1100%7D%5D
01696          */
01697         newMissionNum = (int) (MAXIMUM_MISSIONS_PER_CYCLE + (MINIMUM_MISSIONS_PER_CYCLE - MAXIMUM_MISSIONS_PER_CYCLE) * pow(((ccs.overallInterest - FINAL_OVERALL_INTEREST) / (INITIAL_OVERALL_INTEREST - FINAL_OVERALL_INTEREST)), 2));
01698         Com_DPrintf(DEBUG_CLIENT, "interest = %d, new missions = %d\n", ccs.overallInterest, newMissionNum);
01699         for (i = 0; i < newMissionNum; i++) {
01700             if (frand() > nonOccurrence) {
01701                 const int type = CP_SelectNewMissionType();
01702                 CP_CreateNewMission(type, qfalse);
01703             }
01704         }
01705 
01706         ccs.lastMissionSpawnedDelay -= DELAY_BETWEEN_MISSION_SPAWNING;
01707     }
01708 }
01709 
01715 void CP_InitializeSpawningDelay (void)
01716 {
01717     ccs.lastMissionSpawnedDelay = DELAY_BETWEEN_MISSION_SPAWNING;
01718 
01719     CP_SpawnNewMissions();
01720 }
01721 
01722 
01723 /*====================================
01724 *
01725 * Debug functions
01726 *
01727 ====================================*/
01728 
01729 #ifdef DEBUG
01730 
01734 static void CP_SpawnNewMissions_f (void)
01735 {
01736     int category, type = 0;
01737     mission_t *mission;
01738 
01739     if (Cmd_Argc() < 2) {
01740         Com_Printf("Usage: %s <category> [<type>]\n", Cmd_Argv(0));
01741         for (category = INTERESTCATEGORY_RECON; category < INTERESTCATEGORY_MAX; category++) {
01742             Com_Printf("...%i: %s", category, CP_MissionCategoryToName(category));
01743             if (category == INTERESTCATEGORY_RECON)
01744                 Com_Printf(" <0:Random, 1:Aerial, 2:Ground>");
01745             else if (category == INTERESTCATEGORY_BUILDING)
01746                 Com_Printf(" <0:Subverse Government, 1:Build Base>");
01747             else if (category == INTERESTCATEGORY_INTERCEPT)
01748                 Com_Printf(" <0:Intercept aircraft, 1:Attack installation>");
01749             Com_Printf("\n");
01750         }
01751         return;
01752     }
01753 
01754     if (Cmd_Argc() >= 3)
01755         type = atoi(Cmd_Argv(2));
01756 
01757     category = atoi(Cmd_Argv(1));
01758 
01759     if (category < INTERESTCATEGORY_NONE || category >= INTERESTCATEGORY_MAX)
01760         return;
01761 
01762     if (category == INTERESTCATEGORY_ALIENBASE) {
01763         /* spawning an alien base is special */
01764         vec2_t pos;
01765         alienBase_t *base;
01766         AB_SetAlienBasePosition(pos);               /* get base position */
01767         base = AB_BuildBase(pos);                   /* build base */
01768         if (!base) {
01769             Com_Printf("CP_BuildBaseSetUpBase: could not create base\n");
01770             return;
01771         }
01772         CP_SpawnAlienBaseMission(base);             /* make base visible */
01773         return;
01774     } else if (category == INTERESTCATEGORY_RESCUE) {
01775         const base_t *base = B_GetFoundedBaseByIDX(0);
01776         aircraft_t *aircraft;
01777         if (!base) {
01778             Com_Printf("No base yet\n");
01779             return;
01780         }
01781 
01782         aircraft = AIR_GetNextFromBase(base, NULL);
01783         if (!aircraft) {
01784             Com_Printf("No aircraft in base\n");
01785             return;
01786         }
01787         CP_SpawnRescueMission(aircraft, NULL);
01788         return;
01789     }
01790 
01791     mission = CP_CreateNewMission(category, qtrue);
01792     if (!mission) {
01793         Com_Printf("CP_SpawnNewMissions_f: Could not add mission, abort\n");
01794         return;
01795     }
01796 
01797     if (type) {
01798         switch (category) {
01799         case INTERESTCATEGORY_RECON:
01800             /* Start mission */
01801             if (!CP_MissionBegin(mission))
01802                 return;
01803             if (type == 1)
01804                 /* Aerial mission */
01805                 CP_ReconMissionAerial(mission);
01806             else
01807                 /* This is a ground mission */
01808                 CP_ReconMissionGroundGo(mission);
01809             break;
01810         case INTERESTCATEGORY_BUILDING:
01811             if (type == 1)
01812                 mission->initialOverallInterest = STARTING_BASEBUILD_INTEREST + 1;
01813             break;
01814         case INTERESTCATEGORY_INTERCEPT:
01815             /* Start mission */
01816             if (!CP_MissionBegin(mission))
01817                 return;
01818             if (type == 1) {
01819                 mission->ufo->ufotype = UFO_HARVESTER;
01820                 CP_InterceptGoToInstallation(mission);
01821             } else {
01822                 CP_InterceptAircraftMissionSet(mission);
01823             }
01824             break;
01825         default:
01826             Com_Printf("Type is not implemented for this category.\n");
01827         }
01828     }
01829     Com_Printf("Spawned mission with id '%s'\n", mission->id);
01830 }
01831 
01835 static void CP_MissionSetMap_f (void)
01836 {
01837     mapDef_t *mapDef;
01838     mission_t *mission;
01839     if (Cmd_Argc() < 3) {
01840         Com_Printf("Usage: %s <missionid> <mapdef>\n", Cmd_Argv(0));
01841         return;
01842     }
01843     mission = CP_GetMissionByID(Cmd_Argv(1));
01844     mapDef = Com_GetMapDefinitionByID(Cmd_Argv(2));
01845     if (mapDef == NULL) {
01846         Com_Printf("Could not find mapdef for %s\n", Cmd_Argv(2));
01847         return;
01848     }
01849     mission->mapDef = mapDef;
01850 }
01851 
01856 static void CP_MissionList_f (void)
01857 {
01858     const linkedList_t *list = ccs.missions;
01859     qboolean noMission = qtrue;
01860 
01861     for (; list; list = list->next) {
01862         const mission_t *mission = (mission_t *)list->data;
01863         Com_Printf("mission: '%s'\n", mission->id);
01864         Com_Printf("...category %i. '%s' -- stage %i. '%s'\n", mission->category,
01865             CP_MissionCategoryToName(mission->category), mission->stage, CP_MissionStageToName(mission->stage));
01866         Com_Printf("...mapDef: '%s'\n", mission->mapDef ? mission->mapDef->id : "No mapDef defined");
01867         Com_Printf("...location: '%s'\n", mission->location);
01868         Com_Printf("...start (day = %i, sec = %i), ends (day = %i, sec = %i)\n",
01869             mission->startDate.day, mission->startDate.sec, mission->finalDate.day, mission->finalDate.sec);
01870         Com_Printf("...pos (%.02f, %.02f)%s -- mission %son Geoscape\n", mission->pos[0], mission->pos[1], mission->posAssigned ? "(assigned Pos)" : "", mission->onGeoscape ? "" : "not ");
01871         if (mission->ufo)
01872             Com_Printf("...UFO: %s (%i/%i)\n", mission->ufo->id, (int) (mission->ufo - ccs.ufos), ccs.numUFOs - 1);
01873         else
01874             Com_Printf("...UFO: no UFO\n");
01875         noMission = qfalse;
01876     }
01877     if (noMission)
01878         Com_Printf("No mission currently in game.\n");
01879 }
01880 
01885 static void CP_DeleteMissions_f (void)
01886 {
01887     int n = CP_CountMission();
01888     const linkedList_t *list = ccs.missions;
01889 
01890     for (;list; list = list->next) {
01891         mission_t *mission = (mission_t *)list->data;
01892         CP_MissionRemove(mission);
01893     }
01894     Com_Printf("Removed %i mission(s) from global array\n", n);
01895 
01896     if (ccs.numUFOs != 0) {
01897         Com_Printf("CP_DeleteMissions_f: Error, there are still %i UFO in game afer removing all missions. Force removal.\n", ccs.numUFOs);
01898         while (ccs.numUFOs)
01899             UFO_RemoveFromGeoscape(ccs.ufos);
01900     }
01901 }
01902 
01907 static void CP_AlienInterestList_f (void)
01908 {
01909     int i;
01910 
01911     Com_Printf("Overall interest: %i\n", ccs.overallInterest);
01912     Com_Printf("Individual interest:\n");
01913 
01914     for (i = 0; i < INTERESTCATEGORY_MAX; i++)
01915         Com_Printf("...%i. %s -- %i\n", i, CP_MissionCategoryToName(i), ccs.interest[i]);
01916 }
01917 
01922 static void CP_SetAlienInterest_f (void)
01923 {
01924     if (Cmd_Argc() < 2) {
01925         Com_Printf("Usage: %s <interestlevel>\n", Cmd_Argv(0));
01926         return;
01927     }
01928 
01929     ccs.overallInterest = max(0, atoi(Cmd_Argv(1)));
01930 }
01931 #endif
01932 
01937 qboolean CP_SaveMissionsXML (mxml_node_t *parent)
01938 {
01939     const linkedList_t *list = ccs.missions;
01940     mxml_node_t *missionsNode = mxml_AddNode(parent, SAVE_MISSIONS);
01941 
01942     Com_RegisterConstList(saveInterestConstants);
01943     Com_RegisterConstList(saveMissionConstants);
01944     for (; list; list = list->next) {
01945         const mission_t *mission = (mission_t *)list->data;
01946         mxml_node_t *missionNode = mxml_AddNode(missionsNode, SAVE_MISSIONS_MISSION);
01947 
01948         mxml_AddInt(missionNode, SAVE_MISSIONS_MISSION_IDX, mission->idx);
01949         mxml_AddString(missionNode, SAVE_MISSIONS_ID, mission->id);
01950         if (mission->mapDef) {
01951             mxml_AddString(missionNode, SAVE_MISSIONS_MAPDEF_ID, mission->mapDef->id);
01952             mxml_AddInt(missionNode, SAVE_MISSIONS_MAPDEFTIMES, mission->mapDef->timesAlreadyUsed);
01953         }
01954         mxml_AddBool(missionNode, SAVE_MISSIONS_ACTIVE, mission->active);
01955         mxml_AddBool(missionNode, SAVE_MISSIONS_POSASSIGNED, mission->posAssigned);
01956         mxml_AddBool(missionNode, SAVE_MISSIONS_CRASHED, mission->crashed);
01957         mxml_AddString(missionNode, SAVE_MISSIONS_ONWIN, mission->onwin);
01958         mxml_AddString(missionNode, SAVE_MISSIONS_ONLOSE, mission->onlose);
01959         mxml_AddString(missionNode, SAVE_MISSIONS_CATEGORY, Com_GetConstVariable(SAVE_INTERESTCAT_NAMESPACE, mission->category));
01960         mxml_AddString(missionNode, SAVE_MISSIONS_STAGE, Com_GetConstVariable(SAVE_MISSIONSTAGE_NAMESPACE, mission->stage));
01961         switch (mission->category) {
01962         case INTERESTCATEGORY_BASE_ATTACK:
01963             if (mission->stage == STAGE_MISSION_GOTO || mission->stage == STAGE_BASE_ATTACK) {
01964                 const base_t *base;
01965                 /* save IDX of base under attack if required */
01966                 base = (base_t*)mission->data;
01967                 assert(base);
01968                 mxml_AddShort(missionNode, SAVE_MISSIONS_BASEINDEX, base->idx);
01969             }
01970             break;
01971         case INTERESTCATEGORY_INTERCEPT:
01972             if (mission->stage == STAGE_MISSION_GOTO || mission->stage == STAGE_INTERCEPT) {
01973                 const installation_t *installation = (installation_t*)mission->data;
01974                 if (installation)
01975                     mxml_AddShort(missionNode, SAVE_MISSIONS_INSTALLATIONINDEX, installation->idx);
01976             }
01977             break;
01978         case INTERESTCATEGORY_RESCUE:
01979             {
01980                 const aircraft_t *aircraft = (const aircraft_t *)mission->data;
01981                 mxml_AddShort(missionNode, SAVE_MISSIONS_CRASHED_AIRCRAFT, aircraft->idx);
01982             }
01983             break;
01984         case INTERESTCATEGORY_BUILDING:
01985         case INTERESTCATEGORY_SUPPLY:
01986             {
01987                 /* save IDX of alien base if required */
01988                 const alienBase_t *base = (alienBase_t*)mission->data;
01989                 /* there may be no base is the mission is a subverting government */
01990                 if (base)
01991                     mxml_AddShort(missionNode, SAVE_MISSIONS_ALIENBASEINDEX, base->idx);
01992             }
01993             break;
01994         default:
01995             break;
01996         }
01997         mxml_AddString(missionNode, SAVE_MISSIONS_LOCATION, mission->location);
01998         mxml_AddShort(missionNode, SAVE_MISSIONS_INITIALOVERALLINTEREST, mission->initialOverallInterest);
01999         mxml_AddShort(missionNode, SAVE_MISSIONS_INITIALINDIVIDUALINTEREST, mission->initialIndividualInterest);
02000         mxml_AddDate(missionNode, SAVE_MISSIONS_STARTDATE, mission->startDate.day, mission->startDate.sec);
02001         mxml_AddDate(missionNode, SAVE_MISSIONS_FINALDATE, mission->finalDate.day, mission->finalDate.sec);
02002         mxml_AddPos2(missionNode, SAVE_MISSIONS_POS, mission->pos);
02003         if (mission->ufo)
02004             mxml_AddShort(missionNode, SAVE_MISSIONS_UFO, mission->ufo - ccs.ufos);
02005         mxml_AddBoolValue(missionNode, SAVE_MISSIONS_ONGEOSCAPE, mission->onGeoscape);
02006     }
02007     Com_UnregisterConstList(saveInterestConstants);
02008     Com_UnregisterConstList(saveMissionConstants);
02009 
02010     return qtrue;
02011 }
02012 
02017 qboolean CP_LoadMissionsXML (mxml_node_t *parent)
02018 {
02019     mxml_node_t *missionNode;
02020     mxml_node_t *node;
02021     qboolean success = qtrue;
02022 
02023     Com_RegisterConstList(saveInterestConstants);
02024     Com_RegisterConstList(saveMissionConstants);
02025     missionNode = mxml_GetNode(parent, SAVE_MISSIONS);
02026     for (node = mxml_GetNode(missionNode, SAVE_MISSIONS_MISSION); node;
02027             node = mxml_GetNextNode(node, missionNode, SAVE_MISSIONS_MISSION)) {
02028         const char *name;
02029         mission_t mission;
02030         int ufoIdx;
02031         qboolean defaultAssigned = qfalse;
02032         const char *categoryId = mxml_GetString(node, SAVE_MISSIONS_CATEGORY);
02033         const char *stageId = mxml_GetString(node, SAVE_MISSIONS_STAGE);
02034 
02035         memset(&mission, 0, sizeof(mission));
02036         Q_strncpyz(mission.id, mxml_GetString(node, SAVE_MISSIONS_ID), sizeof(mission.id));
02037         mission.idx = mxml_GetInt(node, SAVE_MISSIONS_MISSION_IDX, 0);
02038         if (mission.idx <= 0) {
02039             Com_Printf("mission has invalid or no index\n");
02040             success = qfalse;
02041             break;
02042         }
02043         name = mxml_GetString(node, SAVE_MISSIONS_MAPDEF_ID);
02044         if (name && name[0] != '\0') {
02045             mission.mapDef = Com_GetMapDefinitionByID(name);
02046             if (!mission.mapDef) {
02047                 Com_Printf("......mapdef \"%s\" doesn't exist.\n", name);
02048                 success = qfalse;
02049                 break;
02050             }
02051             mission.mapDef->timesAlreadyUsed = mxml_GetInt(node, SAVE_MISSIONS_MAPDEFTIMES, 0);
02052         } else
02053             mission.mapDef = NULL;
02054 
02055         if (!Com_GetConstIntFromNamespace(SAVE_INTERESTCAT_NAMESPACE, categoryId, (int*) &mission.category)) {
02056             Com_Printf("Invalid mission category '%s'\n", categoryId);
02057             success = qfalse;
02058             break;
02059         }
02060 
02061         if (!Com_GetConstIntFromNamespace(SAVE_MISSIONSTAGE_NAMESPACE, stageId, (int*) &mission.stage)) {
02062             Com_Printf("Invalid mission stage '%s'\n", stageId);
02063             success = qfalse;
02064             break;
02065         }
02066 
02067         mission.active = mxml_GetBool(node, SAVE_MISSIONS_ACTIVE, qfalse);
02068         Q_strncpyz(mission.onwin, mxml_GetString(node, SAVE_MISSIONS_ONWIN), sizeof(mission.onwin));
02069         Q_strncpyz(mission.onlose, mxml_GetString(node, SAVE_MISSIONS_ONLOSE), sizeof(mission.onlose));
02070 
02071         mission.initialOverallInterest = mxml_GetInt(node, SAVE_MISSIONS_INITIALOVERALLINTEREST, 0);
02072         mission.initialIndividualInterest = mxml_GetInt(node, SAVE_MISSIONS_INITIALINDIVIDUALINTEREST, 0);
02073 
02074         switch (mission.category) {
02075         case INTERESTCATEGORY_BASE_ATTACK:
02076             if (mission.stage == STAGE_MISSION_GOTO || mission.stage == STAGE_BASE_ATTACK) {
02077                 /* Load IDX of base under attack */
02078                 const int baseidx = mxml_GetInt(node, SAVE_MISSIONS_BASEINDEX, BYTES_NONE);
02079                 if (baseidx != BYTES_NONE) {
02080                     base_t *base = B_GetBaseByIDX(baseidx);
02081                     assert(base);
02082                     if (mission.stage == STAGE_BASE_ATTACK && base->baseStatus != BASE_UNDER_ATTACK)
02083                         Com_Printf("......warning: base %i (%s) is supposedly under attack but base status doesn't match!\n", baseidx, base->name);
02084                     mission.data = (void *) base;
02085                 } else
02086                     Com_Printf("......warning: Missing BaseIndex\n");
02087             }
02088             break;
02089         case INTERESTCATEGORY_INTERCEPT:
02090             if (mission.stage == STAGE_MISSION_GOTO || mission.stage == STAGE_INTERCEPT) {
02091                 const int installationIdx = mxml_GetInt(node, SAVE_MISSIONS_INSTALLATIONINDEX, BYTES_NONE);
02092                 if (installationIdx != BYTES_NONE) {
02093                     installation_t *installation = INS_GetInstallationByIDX(installationIdx);
02094                     if (installation)
02095                         mission.data = (void *) installation;
02096                     else {
02097                         Com_Printf("Mission on non-existent installation\n");
02098                         success = qfalse;
02099                     }
02100                 }
02101             }
02102             break;
02103         case INTERESTCATEGORY_RESCUE:
02104             {
02105                 const int aircraftIdx = mxml_GetInt(node, SAVE_MISSIONS_CRASHED_AIRCRAFT, -1);
02106                 mission.data = (void *) AIR_AircraftGetFromIDX(aircraftIdx);
02107                 if (mission.data == NULL) {
02108                     Com_Printf("Error while loading rescue mission (missionidx %i, aircraftidx: %i, category: %i, stage: %i)\n",
02109                             mission.idx, aircraftIdx, mission.category, mission.stage);
02110                     success = qfalse;
02111                 }
02112             }
02113             break;
02114         case INTERESTCATEGORY_BUILDING:
02115         case INTERESTCATEGORY_SUPPLY:
02116             {
02117                 int baseIDX = mxml_GetInt(node, SAVE_MISSIONS_ALIENBASEINDEX, BYTES_NONE);
02118                 if (baseIDX != BYTES_NONE) {
02119                     alienBase_t *alienBase = AB_GetByIDX(baseIDX);
02120                     mission.data = (void *) alienBase;
02121                 }
02122                 if (!mission.data && !CP_BasemissionIsSubvertingGovernmentMission(&mission) && (mission.stage >= STAGE_BUILD_BASE)) {
02123                     Com_Printf("Error while loading Alien Base mission (missionidx %i, baseidx: %i, category: %i, stage: %i)\n",
02124                             mission.idx, baseIDX, mission.category, mission.stage);
02125                     success = qfalse;
02126                 }
02127             }
02128             break;
02129         default:
02130             break;
02131         }
02132         if (!success)
02133             break;
02134 
02135         Q_strncpyz(mission.location, mxml_GetString(node, SAVE_MISSIONS_LOCATION), sizeof(mission.location));
02136 
02137         mxml_GetDate(node, SAVE_MISSIONS_STARTDATE, &mission.startDate.day, &mission.startDate.sec);
02138         mxml_GetDate(node, SAVE_MISSIONS_FINALDATE, &mission.finalDate.day, &mission.finalDate.sec);
02139         mxml_GetPos2(node, SAVE_MISSIONS_POS, mission.pos);
02140 
02141         ufoIdx = mxml_GetInt(node, SAVE_MISSIONS_UFO, -1);
02142         if (ufoIdx <= -1 || ufoIdx >= lengthof(ccs.ufos))
02143             mission.ufo = NULL;
02144         else
02145             mission.ufo = &ccs.ufos[ufoIdx];
02146 
02147         mission.crashed = mxml_GetBool(node, SAVE_MISSIONS_CRASHED, qfalse);
02148         mission.onGeoscape = mxml_GetBool(node, SAVE_MISSIONS_ONGEOSCAPE, qfalse);
02149 
02150         if (mission.pos[0] > 0.001 || mission.pos[1] > 0.001)
02151             defaultAssigned = qtrue;
02152         mission.posAssigned = mxml_GetBool(node, SAVE_MISSIONS_POSASSIGNED, defaultAssigned);
02153         /* Add mission to global array */
02154         LIST_Add(&ccs.missions, (byte*) &mission, sizeof(mission));
02155     }
02156     Com_UnregisterConstList(saveInterestConstants);
02157     Com_UnregisterConstList(saveMissionConstants);
02158 
02159     return success;
02160 }
02161 
02162 void CP_MissionsInit (void)
02163 {
02164 #ifdef DEBUG
02165     Cmd_AddCommand("debug_missionsetmap", CP_MissionSetMap_f, "Changes the map for a spawned mission");
02166     Cmd_AddCommand("debug_missionadd", CP_SpawnNewMissions_f, "Add a new mission");
02167     Cmd_AddCommand("debug_missiondeleteall", CP_DeleteMissions_f, "Remove all missions from global array");
02168     Cmd_AddCommand("debug_missionlist", CP_MissionList_f, "Debug function to show all missions");
02169     Cmd_AddCommand("debug_interestlist", CP_AlienInterestList_f, "Debug function to show alien interest values");
02170     Cmd_AddCommand("debug_interestset", CP_SetAlienInterest_f, "Set overall interest level.");
02171 #endif
02172 }

Generated by  doxygen 1.6.2