cl_game_campaign.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 "client.h"
00027 #include "cl_game.h"
00028 #include "cl_team.h"
00029 #include "campaign/cp_campaign.h"
00030 #include "campaign/cp_missions.h"
00031 #include "campaign/cp_mission_triggers.h"
00032 #include "campaign/cp_team.h"
00033 #include "ui/ui_main.h"
00034 #include "ui/ui_nodes.h"
00035 #include "ui/node/ui_node_model.h"
00036 #include "ui/node/ui_node_text.h"
00037 
00042 static void GAME_CP_MissionAutoGo_f (void)
00043 {
00044     if (!ccs.selectedMission) {
00045         Com_DPrintf(DEBUG_CLIENT, "GAME_CP_MissionAutoGo_f: No update after automission\n");
00046         return;
00047     }
00048 
00049     if (!ccs.missionAircraft) {
00050         Com_Printf("GAME_CP_MissionAutoGo_f: No aircraft at target pos\n");
00051         return;
00052     }
00053 
00054     /* start the map */
00055     CL_GameAutoGo(ccs.selectedMission);
00056 }
00057 
00065 static void GAME_CP_MissionAutoCheck_f (void)
00066 {
00067     if (!ccs.selectedMission || !ccs.interceptAircraft) {
00068         Com_DPrintf(DEBUG_CLIENT, "GAME_CP_MissionAutoCheck_f: No update after automission\n");
00069         return;
00070     }
00071 
00072     if (ccs.selectedMission->mapDef->storyRelated) {
00073         Com_DPrintf(DEBUG_CLIENT, "GAME_CP_MissionAutoCheck_f: story related - auto mission is disabled\n");
00074         Cvar_Set("cp_mission_autogo_available", "0");
00075     } else {
00076         Com_DPrintf(DEBUG_CLIENT, "GAME_CP_MissionAutoCheck_f: auto mission is enabled\n");
00077         Cvar_Set("cp_mission_autogo_available", "1");
00078     }
00079 }
00080 
00086 static void GAME_CP_Results_f (void)
00087 {
00088     /* multiplayer? */
00089     if (!ccs.selectedMission)
00090         return;
00091 
00092     /* check for replay */
00093     if (Cvar_GetInteger("cp_mission_tryagain")) {
00094         /* don't collect things and update stats --- we retry the mission */
00095         CP_StartSelectedMission();
00096         return;
00097     }
00098     /* check for win */
00099     if (Cmd_Argc() < 2) {
00100         Com_Printf("Usage: %s <won:true|false>\n", Cmd_Argv(0));
00101         return;
00102     }
00103 
00104     CP_MissionEnd(ccs.selectedMission, Com_ParseBoolean(Cmd_Argv(1)));
00105 }
00106 
00111 static inline const char* CP_ToDifficultyName (int difficulty)
00112 {
00113     switch (difficulty) {
00114     case -4:
00115         return _("Chicken-hearted");
00116     case -3:
00117         return _("Very Easy");
00118     case -2:
00119     case -1:
00120         return _("Easy");
00121     case 0:
00122         return _("Normal");
00123     case 1:
00124     case 2:
00125         return _("Hard");
00126     case 3:
00127         return _("Very Hard");
00128     case 4:
00129         return _("Insane");
00130     default:
00131         Com_Error(ERR_DROP, "Unknown difficulty id %i", difficulty);
00132     }
00133 }
00134 
00135 #define MAXCAMPAIGNTEXT 4096
00136 static char campaignDesc[MAXCAMPAIGNTEXT];
00140 static void GAME_CP_GetCampaigns_f (void)
00141 {
00142     int i;
00143     linkedList_t *campaignList = NULL;
00144 
00145     *campaignDesc = '\0';
00146 
00147     for (i = 0; i < ccs.numCampaigns; i++) {
00148         const campaign_t *c = &ccs.campaigns[i];
00149         if (c->visible)
00150             LIST_AddString(&campaignList, va("%s", _(c->name)));
00151     }
00152     /* default campaign */
00153     UI_RegisterText(TEXT_STANDARD, campaignDesc);
00154     UI_RegisterLinkedListText(TEXT_CAMPAIGN_LIST, campaignList);
00155 
00156     /* select main as default */
00157     for (i = 0; i < ccs.numCampaigns; i++) {
00158         const campaign_t *c = &ccs.campaigns[i];
00159         if (!strcmp(c->id, "main")) {
00160             Cmd_ExecuteString(va("campaignlist_click %i", i));
00161             return;
00162         }
00163     }
00164     Cmd_ExecuteString("campaignlist_click 0");
00165 }
00166 
00170 static void GAME_CP_CampaignListClick_f (void)
00171 {
00172     int num;
00173     const char *racetype;
00174     uiNode_t *campaignlist;
00175 
00176     if (Cmd_Argc() < 2) {
00177         Com_Printf("Usage: %s <campaign list index>\n", Cmd_Argv(0));
00178         return;
00179     }
00180 
00181     /* Which campaign in the list? */
00182     num = atoi(Cmd_Argv(1));
00183     if (num < 0 || num >= ccs.numCampaigns)
00184         return;
00185 
00186     /* jump over all invisible campaigns */
00187     while (!ccs.campaigns[num].visible) {
00188         num++;
00189         if (num >= ccs.numCampaigns)
00190             return;
00191     }
00192 
00193     Cvar_Set("cp_campaign", ccs.campaigns[num].id);
00194     if (ccs.campaigns[num].team == TEAM_PHALANX)
00195         racetype = _("Human");
00196     else
00197         racetype = _("Aliens");
00198 
00199     Com_sprintf(campaignDesc, sizeof(campaignDesc), _("%s\n\nRace: %s\nRecruits: %i %s, %i %s, %i %s\n"
00200         "Credits: %ic\nDifficulty: %s\n"
00201         "Min. happiness of nations: %i %%\n"
00202         "Max. allowed debts: %ic\n"
00203         "%s\n"), _(ccs.campaigns[num].name), racetype,
00204             ccs.campaigns[num].soldiers, ngettext("soldier", "soldiers", ccs.campaigns[num].soldiers),
00205             ccs.campaigns[num].scientists, ngettext("scientist", "scientists", ccs.campaigns[num].scientists),
00206             ccs.campaigns[num].workers, ngettext("worker", "workers", ccs.campaigns[num].workers),
00207             ccs.campaigns[num].credits, CP_ToDifficultyName(ccs.campaigns[num].difficulty),
00208             (int)(round(ccs.campaigns[num].minhappiness * 100.0f)), ccs.campaigns[num].negativeCreditsUntilLost,
00209             _(ccs.campaigns[num].text));
00210     UI_RegisterText(TEXT_STANDARD, campaignDesc);
00211 
00212     /* Highlight currently selected entry */
00213     campaignlist = UI_GetNodeByPath("campaigns.campaignlist");
00214     UI_TextNodeSelectLine(campaignlist, num);
00215 }
00216 
00222 static void GAME_CP_Start_f (void)
00223 {
00224     /* get campaign - they are already parsed here */
00225     campaign_t *campaign = CL_GetCampaign(cp_campaign->string);
00226     if (!campaign)
00227         return;
00228 
00229     CP_CampaignInit(campaign, qfalse);
00230 
00231     /* Intro sentences */
00232     Cbuf_AddText("seq_start intro\n");
00233 }
00234 
00246 void GAME_CP_Results (struct dbuffer *msg, int winner, int *numSpawned, int *numAlive, int numKilled[][MAX_TEAMS], int numStunned[][MAX_TEAMS])
00247 {
00248     int i, j;
00249     int ownSurvived, ownKilled, ownStunned;
00250     int aliensSurvived, aliensKilled, aliensStunned;
00251     int civiliansSurvived, civiliansKilled, civiliansStunned;
00252 
00253     CP_ParseCharacterData(msg);
00254 
00255     ownSurvived = ownKilled = ownStunned = 0;
00256     aliensSurvived = aliensKilled = aliensStunned = 0;
00257     civiliansSurvived = civiliansKilled = civiliansStunned = 0;
00258 
00259     for (i = 0; i < MAX_TEAMS; i++) {
00260         if (i == cls.team)
00261             ownSurvived = numAlive[i];
00262         else if (i == TEAM_CIVILIAN)
00263             civiliansSurvived = numAlive[i];
00264         else
00265             aliensSurvived += numAlive[i];
00266         for (j = 0; j < MAX_TEAMS; j++)
00267             if (j == cls.team) {
00268                 ownKilled += numKilled[i][j];
00269                 ownStunned += numStunned[i][j]++;
00270             } else if (j == TEAM_CIVILIAN) {
00271                 civiliansKilled += numKilled[i][j];
00272                 civiliansStunned += numStunned[i][j]++;
00273             } else {
00274                 aliensKilled += numKilled[i][j];
00275                 aliensStunned += numStunned[i][j]++;
00276             }
00277     }
00278     /* if we won, our stunned are alive */
00279     if (winner == cls.team) {
00280         ownSurvived += ownStunned;
00281         ownStunned = 0;
00282     } else
00283         /* if we lost, they revive stunned */
00284         aliensStunned = 0;
00285 
00286     /* we won, and we're not the dirty aliens */
00287     if (winner == cls.team)
00288         civiliansSurvived += civiliansStunned;
00289     else
00290         civiliansKilled += civiliansStunned;
00291 
00292     /* Collect items from the battlefield. */
00293     AII_CollectingItems(ccs.missionAircraft, winner == cls.team);
00294     if (winner == cls.team)
00295         /* Collect aliens from the battlefield. */
00296         AL_CollectingAliens(ccs.missionAircraft);
00297 
00298     ccs.aliensKilled += aliensKilled;
00299 
00300     ccs.missionResults.aliensKilled = aliensKilled;
00301     ccs.missionResults.aliensStunned = aliensStunned;
00302     ccs.missionResults.aliensSurvived = aliensSurvived;
00303     ccs.missionResults.ownKilled = ownKilled - numKilled[cls.team][cls.team] - numKilled[TEAM_CIVILIAN][cls.team];
00304     ccs.missionResults.ownStunned = ownStunned;
00305     ccs.missionResults.ownKilledFriendlyFire = numKilled[cls.team][cls.team] + numKilled[TEAM_CIVILIAN][cls.team];
00306     ccs.missionResults.ownSurvived = ownSurvived;
00307     ccs.missionResults.civiliansKilled = civiliansKilled;
00308     ccs.missionResults.civiliansKilledFriendlyFire = numKilled[cls.team][TEAM_CIVILIAN] + numKilled[TEAM_CIVILIAN][TEAM_CIVILIAN];
00309     ccs.missionResults.civiliansSurvived = civiliansSurvived;
00310 
00311     CP_InitMissionResults(winner == cls.team);
00312 
00313     UI_InitStack("geoscape", "campaign_main", qtrue, qtrue);
00314 
00315     CP_ExecuteMissionTrigger(ccs.selectedMission, winner == cls.team);
00316 
00317     if (winner == cls.team) {
00318         UI_PushWindow("won", NULL);
00319     } else
00320         UI_PushWindow("lost", NULL);
00321 
00322     CL_Disconnect();
00323     SV_Shutdown("Mission end", qfalse);
00324 }
00325 
00326 qboolean GAME_CP_Spawn (void)
00327 {
00328     aircraft_t *aircraft = ccs.missionAircraft;
00329     base_t *base;
00330     linkedList_t* l;
00331 
00332     if (!aircraft)
00333         return qfalse;
00334 
00335     /* convert aircraft team to character list */
00336     for (l = aircraft->acTeam; l != NULL; l = l->next) {
00337         employee_t *employee = (employee_t *)l->data;
00338         cl.chrList.chr[cl.chrList.num] = &employee->chr;
00339         cl.chrList.num++;
00340     }
00341 
00342     base = aircraft->homebase;
00343 
00344     /* clean temp inventory */
00345     CL_CleanTempInventory(base);
00346 
00347     /* activate hud */
00348     UI_InitStack(mn_hud->string, NULL, qfalse, qtrue);
00349 
00350     return qtrue;
00351 }
00352 
00353 const mapDef_t* GAME_CP_MapInfo (int step)
00354 {
00355     return Com_GetMapDefByIDX(cls.currentSelectedMap);
00356 }
00357 
00358 qboolean GAME_CP_ItemIsUseable (const objDef_t *od)
00359 {
00360     const technology_t *tech = RS_GetTechForItem(od);
00361     return RS_IsResearched_ptr(tech);
00362 }
00363 
00364 int GAME_CP_GetTeam (void)
00365 {
00366     assert(ccs.curCampaign);
00367     return ccs.curCampaign->team;
00368 }
00369 
00375 qboolean GAME_CP_TeamIsKnown (const teamDef_t *teamDef)
00376 {
00377     if (!CHRSH_IsTeamDefAlien(teamDef))
00378         return qtrue;
00379 
00380     if (!ccs.teamDefTechs[teamDef->idx])
00381         Com_Error(ERR_DROP, "Could not find tech for teamdef '%s'", teamDef->id);
00382 
00383     return RS_IsResearched_ptr(ccs.teamDefTechs[teamDef->idx]);
00384 }
00385 
00386 void GAME_CP_Drop (void)
00387 {
00389     UI_InitStack("geoscape", "campaign_main", qtrue, qtrue);
00390 
00391     SV_Shutdown("Mission end", qfalse);
00392     CL_Disconnect();
00393 }
00394 
00395 void GAME_CP_Frame (void)
00396 {
00397     /* don't run the campaign in console mode */
00398     if (cls.keyDest == key_console)
00399         return;
00400 
00401     /* advance time */
00402     CL_CampaignRun();
00403 }
00404 
00413 const char* GAME_CP_GetModelForItem (const objDef_t *od)
00414 {
00415     const technology_t *tech = RS_GetTechForItem(od);
00416     if (tech->mdl) {
00417         /* the model from the tech structure has higher priority, because the item model itself
00418          * is mainly for the battlescape or the geoscape - only use that as a fallback */
00419         return tech->mdl;
00420     }
00421     return NULL;
00422 }
00423 
00428 void GAME_CP_InitializeBattlescape (const chrList_t *team)
00429 {
00430     int i;
00431     struct dbuffer *msg = new_dbuffer();
00432 
00433     NET_WriteByte(msg, clc_initactorstates);
00434     NET_WriteByte(msg, team->num);
00435 
00436     for (i = 0; i < team->num; i++) {
00437         const character_t *chr = team->chr[i];
00438         NET_WriteShort(msg, chr->ucn);
00439         NET_WriteShort(msg, chr->state);
00440     }
00441 
00442     NET_WriteMsg(cls.netStream, msg);
00443 }
00444 
00445 equipDef_t *GAME_CP_GetEquipmentDefinition (void)
00446 {
00447     return &ccs.eMission;
00448 }
00449 
00450 void GAME_CP_CharacterCvars (const character_t *chr)
00451 {
00452     /* Display rank if the character has one. */
00453     if (chr->score.rank >= 0) {
00454         char buf[MAX_VAR];
00455         const rank_t *rank = CL_GetRankByIdx(chr->score.rank);
00456         Com_sprintf(buf, sizeof(buf), _("Rank: %s"), _(rank->name));
00457         Cvar_Set("mn_chrrank", buf);
00458         Cvar_Set("mn_chrrank_img", rank->image);
00459     } else {
00460         Cvar_Set("mn_chrrank_img", "");
00461         Cvar_Set("mn_chrrank", "");
00462     }
00463 
00464     Cvar_Set("mn_chrmis", va("%i", chr->score.assignedMissions));
00465     Cvar_Set("mn_chrkillalien", va("%i", chr->score.kills[KILLED_ENEMIES]));
00466     Cvar_Set("mn_chrkillcivilian", va("%i", chr->score.kills[KILLED_CIVILIANS]));
00467     Cvar_Set("mn_chrkillteam", va("%i", chr->score.kills[KILLED_TEAM]));
00468 }
00469 
00470 void GAME_CP_DisplayItemInfo (uiNode_t *node, const char *string)
00471 {
00472     const aircraft_t *aircraft = AIR_GetAircraftSilent(string);
00473     if (aircraft) {
00474         assert(aircraft->tech);
00475         UI_DrawModelNode(node, aircraft->tech->mdl);
00476     } else {
00477         const technology_t *tech = RS_GetTechByProvided(string);
00478         if (tech)
00479             UI_DrawModelNode(node, tech->mdl);
00480         else
00481             Com_Printf("UI_ItemNodeDraw: Unknown item: '%s'\n", string);
00482     }
00483 }
00484 
00485 void GAME_CP_InitStartup (void)
00486 {
00487     Cmd_AddCommand("cp_results", GAME_CP_Results_f, "Parses and shows the game results");
00488     Cmd_AddCommand("cp_missionauto_check", GAME_CP_MissionAutoCheck_f, "Checks whether this mission can be done automatically");
00489     Cmd_AddCommand("cp_mission_autogo", GAME_CP_MissionAutoGo_f, "Let the current selection mission be done automatically");
00490     Cmd_AddCommand("campaignlist_click", GAME_CP_CampaignListClick_f, NULL);
00491     Cmd_AddCommand("cp_getcampaigns", GAME_CP_GetCampaigns_f, "Fill the campaign list with available campaigns");
00492     Cmd_AddCommand("cp_start", GAME_CP_Start_f, "Start the new campaign");
00493 
00494     CP_InitStartup();
00495 
00496     cp_campaign = Cvar_Get("cp_campaign", "main", 0, "Which is the current selected campaign id");
00497     cp_start_employees = Cvar_Get("cp_start_employees", "1", CVAR_ARCHIVE, "Start with hired employees");
00498     /* cvars might have been changed by other gametypes */
00499     Cvar_ForceSet("sv_maxclients", "1");
00500     Cvar_ForceSet("sv_ai", "1");
00501 
00502     /* reset campaign data */
00503     CL_ResetSinglePlayerData();
00504     CL_ReadSinglePlayerData();
00505 }
00506 
00507 void GAME_CP_Shutdown (void)
00508 {
00509     Cmd_RemoveCommand("cp_results");
00510     Cmd_RemoveCommand("cp_missionauto_check");
00511     Cmd_RemoveCommand("cp_mission_autogo");
00512     Cmd_RemoveCommand("campaignlist_click");
00513     Cmd_RemoveCommand("cp_getcampaigns");
00514     Cmd_RemoveCommand("cp_start");
00515 
00516     CP_CampaignExit();
00517 
00518     CL_ResetSinglePlayerData();
00519 
00520     SV_Shutdown("Quitting server.", qfalse);
00521 }

Generated by  doxygen 1.6.2