00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "../cl_shared.h"
00027 #include "../cl_renderer.h"
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
00052
00053
00054
00060 void CP_SetMissionVars (const mission_t *mission)
00061 {
00062 int i;
00063
00064 assert(mission->mapDef);
00065
00066
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
00073
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
00101 UI_InitStack(NULL, "singleplayermission", qtrue, qfalse);
00102
00103 assert(mission->mapDef->map);
00104
00109 refdef.mapZone = ccs.battleParameters.zoneType;
00110
00111
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
00118 if (!base)
00119 Com_Error(ERR_DROP, "Baseattack map without base!\n");
00120
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
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
00175 for (i = 0; i < ccs.numAlienCategories; i++) {
00176 alienTeamCategory_t *cat = &ccs.alienCategories[i];
00177
00178
00179 if (!CP_IsAlienTeamForCategory(cat, mission->category))
00180 continue;
00181
00182
00183
00184
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
00197 i = rand() % numAvailableGroup;
00198
00199
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
00237
00238
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
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
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;
00341
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
00350 if (mission->mapDef->map[0] == '+') {
00351
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
00363 if (mission->mapDef->map[0] == '+') {
00364
00365 Cvar_Set("rm_ufo", Com_GetRandomMapAssemblyNameForCraft(shortUFOType));
00366 }
00367 }
00368
00369
00370 if (mission->mapDef->map[0] == '+') {
00371 Cvar_Set("rm_drop", Com_GetRandomMapAssemblyNameForCraft(ccs.missionAircraft->id));
00372 }
00373 }
00374
00375
00376
00377
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
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
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
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
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
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
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
00639
00640
00641
00647 const char* MAP_GetMissionModel (const mission_t *mission)
00648 {
00649
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
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
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
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
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
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
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
00820
00821
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
00830 if (status != MISDET_MAY_BE_DETECTED || mission->onGeoscape)
00831 continue;
00832
00833
00834 assert(!mission->ufo || !mission->ufo->detected);
00835
00836 if (frand() <= missionDetectionProbability) {
00837
00838 CP_MissionAddToGeoscape(mission, qtrue);
00839
00840
00841 if (!MAP_IsRadarOverlayActivated())
00842 MAP_SetOverlay("radar");
00843
00844
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
00867 CP_MissionRemoveFromGeoscape(mission);
00868 } else if (!mission->onGeoscape && CP_CheckMissionVisibleOnGeoscape(mission) == MISDET_ALWAYS_DETECTED) {
00869
00870
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
00888 mission->ufo->landed = qtrue;
00889
00890
00891 AIR_AircraftsNotifyUFORemoved(mission->ufo, destroyed);
00892 MAP_NotifyUFORemoved(mission->ufo, destroyed);
00893
00894 if (destroyed) {
00895 linkedList_t *list;
00896
00897
00898 RADAR_NotifyUFORemoved(mission->ufo, destroyed);
00899
00900
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
00912
00913 RADAR_NotifyUFORemoved(mission->ufo, destroyed);
00914 }
00915 }
00916
00917
00918
00919
00920
00921
00922
00923
00928 void CP_MissionRemove (mission_t *mission)
00929 {
00930 linkedList_t *list;
00931
00932
00933 if (mission->ufo)
00934 CP_UFORemoveFromGeoscape(mission, qtrue);
00935
00936
00937 if (mission == ccs.battleParameters.mission)
00938 ccs.battleParameters.mission = NULL;
00939
00940
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
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
01004 if (mission->category == INTERESTCATEGORY_BASE_ATTACK && mission->data) {
01005 base_t *missionBase = (base_t *) mission->data;
01006 if (base == missionBase) {
01007
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
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
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
01180 if (mission->stage == STAGE_BASE_ATTACK) {
01181 if (won) {
01182
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
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;
01218
01219 if (mission->stage == STAGE_BASE_ATTACK) {
01220 base = (base_t *)mission->data;
01221 assert(base);
01222
01223 aircraft = base->aircraftCurrent;
01224 } else {
01225 aircraft = ccs.missionAircraft;
01226 base = aircraft->homebase;
01227 }
01228
01229
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
01241 CP_ParseCharacterData(NULL);
01242
01243
01244 CL_UpdateCharacterStats(base, aircraft);
01245
01246
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
01259 if (chr->HP <= 0) {
01260
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
01271 if (CP_TransferOfAliensToOtherBaseNeeded(base, &ccs.missionResults)) {
01272
01273
01274 Cmd_ExecuteString(va("trans_aliens %i", aircraft->idx));
01275 } else {
01276
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
01305 return qfalse;
01306 }
01307 }
01308
01313 void CP_UFOProceedMission (aircraft_t *ufo)
01314 {
01315
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
01321
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
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
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
01372
01373 CP_UFORemoveFromGeoscape(mission, qfalse);
01374
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
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
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
01426
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
01438
01439 if (aircraft->homebase->aircraftCurrent == aircraft)
01440 aircraft->homebase->aircraftCurrent = AIR_GetNextFromBase(aircraft->homebase, NULL);
01441
01442
01443 AIR_UpdateHangarCapForAll(aircraft->homebase);
01444
01445 mission->finalDate = Date_Add(ccs.date, Date_Random(minCrashDelay, crashDelay));
01446
01447 CP_MissionAddToGeoscape(mission, qfalse);
01448 }
01449
01450
01451
01452
01453
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
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
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
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
01547 randNumber = frand();
01548
01549
01550 if (canBeSpawnedFromGround) {
01551 const int XVI_PARAM = 10;
01552
01553 groundProbability = max(0.1f, 1.0f - exp(-CP_GetAverageXVIRate() / XVI_PARAM));
01554
01555
01556 if (randNumber < groundProbability)
01557 return UFO_MAX;
01558 }
01559
01560
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
01585 if (!ccs.missions)
01586 return;
01587
01588
01589
01590
01591 if (!CP_GetMissionByIDSilent(mission->id))
01592 break;
01593
01594 num++;
01595 } while (num);
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};
01610 linkedList_t *list;
01611
01612
01613 if (category <= INTERESTCATEGORY_NONE || category >= INTERESTCATEGORY_MAX)
01614 return NULL;
01615
01616 memset(&mission, 0, sizeof(mission));
01617
01618
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
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
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
01680
01681
01682
01683
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
01690
01691 CL_ChangeIndividualInterest(AB_GetAlienBaseNumber() * 0.1f, INTERESTCATEGORY_SUPPLY);
01692
01693
01694
01695
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
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
01764 vec2_t pos;
01765 alienBase_t *base;
01766 AB_SetAlienBasePosition(pos);
01767 base = AB_BuildBase(pos);
01768 if (!base) {
01769 Com_Printf("CP_BuildBaseSetUpBase: could not create base\n");
01770 return;
01771 }
01772 CP_SpawnAlienBaseMission(base);
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
01801 if (!CP_MissionBegin(mission))
01802 return;
01803 if (type == 1)
01804
01805 CP_ReconMissionAerial(mission);
01806 else
01807
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
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
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
01988 const alienBase_t *base = (alienBase_t*)mission->data;
01989
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
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
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 }