cp_ufopedia.c

Go to the documentation of this file.
00001 
00008 /*
00009 Copyright (C) 2002-2010 UFO: Alien Invasion.
00010 
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00019 
00020 See the GNU General Public License for more details.
00021 
00022 You should have received a copy of the GNU General Public License
00023 along with this program; if not, write to the Free Software
00024 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00025 
00026 */
00027 
00028 #include "../cl_shared.h"
00029 #include "../cl_inventory.h"
00030 #include "../ui/ui_main.h"
00031 #include "../ui/ui_icon.h"
00032 #include "../../shared/parse.h"
00033 #include "cp_campaign.h"
00034 #include "cp_mapfightequip.h"
00035 #include "cp_time.h"
00036 
00037 static cvar_t *mn_uppretext;
00038 static cvar_t *mn_uppreavailable;
00039 
00040 static pediaChapter_t *upChaptersDisplayList[MAX_PEDIACHAPTERS];
00041 static int numChaptersDisplayList;
00042 
00043 static technology_t *upCurrentTech;
00044 static pediaChapter_t *currentChapter;
00045 
00046 #define MAX_UPTEXT 4096
00047 static char upBuffer[MAX_UPTEXT];
00048 
00049 #define MAIL_LENGTH 256
00050 #define MAIL_BUFFER_SIZE 0x4000
00051 static char mailBuffer[MAIL_BUFFER_SIZE];
00052 #define CHECK_MAIL_EOL if (tempBuf[MAIL_LENGTH-3] != '\n') tempBuf[MAIL_LENGTH-2] = '\n';
00053 #define MAIL_CLIENT_LINES 15
00054 
00059 enum {
00060     UFOPEDIA_CHAPTERS,
00061     UFOPEDIA_INDEX,
00062     UFOPEDIA_ARTICLE,
00063 
00064     UFOPEDIA_DISPLAYEND
00065 };
00066 static int upDisplay = UFOPEDIA_CHAPTERS;
00067 
00075 static qboolean UP_TechGetsDisplayed (const technology_t *tech)
00076 {
00077     const objDef_t *item;
00078 
00079     assert(tech);
00080     /* virtual items are hidden */
00081     item = INVSH_GetItemByIDSilent(tech->provides);
00082     if (item && item->isVirtual)
00083         return qfalse;
00084     /* Is already researched OR has collected items OR (researchable AND have description)
00085      * AND not a logical block AND not redirected */
00086     return (RS_IsResearched_ptr(tech) || RS_Collected_(tech)
00087      || (tech->statusResearchable && tech->preDescription.numDescriptions > 0))
00088      && tech->type != RS_LOGIC && !tech->redirect;
00089 }
00090 
00095 static void UP_ChangeDisplay (int newDisplay)
00096 {
00097     if (newDisplay < UFOPEDIA_DISPLAYEND && newDisplay >= 0)
00098         upDisplay = newDisplay;
00099     else
00100         Com_Printf("Error in UP_ChangeDisplay (%i)\n", newDisplay);
00101 
00102     Cvar_SetValue("mn_uppreavailable", 0);
00103 
00104     /* make sure, that we leave the mail header space */
00105     UI_ResetData(TEXT_UFOPEDIA_MAILHEADER);
00106     UI_ResetData(TEXT_UFOPEDIA_MAIL);
00107     UI_ResetData(TEXT_UFOPEDIA_REQUIREMENT);
00108     UI_ResetData(TEXT_ITEMDESCRIPTION);
00109     UI_ResetData(TEXT_UFOPEDIA);
00110 
00111     switch (upDisplay) {
00112     case UFOPEDIA_CHAPTERS:
00113         currentChapter = NULL;
00114         upCurrentTech = NULL;
00115         Cvar_Set("mn_upmodel_top", "");
00116         Cvar_Set("mn_upmodel_bottom", "");
00117         Cvar_Set("mn_upimage_top", "base/empty");
00118         UI_ExecuteConfunc("mn_up_empty");
00119         Cvar_Set("mn_uptitle", _("UFOpaedia"));
00120         break;
00121     case UFOPEDIA_INDEX:
00122         Cvar_Set("mn_upmodel_top", "");
00123         Cvar_Set("mn_upmodel_bottom", "");
00124         Cvar_Set("mn_upimage_top", "base/empty");
00125         /* no break here */
00126     case UFOPEDIA_ARTICLE:
00127         UI_ExecuteConfunc("mn_up_article");
00128         break;
00129     }
00130     Cvar_SetValue("mn_updisplay", upDisplay);
00131 }
00132 
00138 static const char* CL_AircraftStatToName (int stat)
00139 {
00140     switch (stat) {
00141     case AIR_STATS_SPEED:
00142         return _("Cruising speed");
00143     case AIR_STATS_MAXSPEED:
00144         return _("Maximum speed");
00145     case AIR_STATS_SHIELD:
00146         return _("Armour");
00147     case AIR_STATS_ECM:
00148         return _("ECM");
00149     case AIR_STATS_DAMAGE:
00150         return _("Aircraft damage");
00151     case AIR_STATS_ACCURACY:
00152         return _("Accuracy");
00153     case AIR_STATS_FUELSIZE:
00154         return _("Fuel size");
00155     case AIR_STATS_WRANGE:
00156         return _("Weapon range");
00157     default:
00158         return _("Unknown weapon skill");
00159     }
00160 }
00161 
00166 static const char* CL_AircraftSizeToName (int aircraftSize)
00167 {
00168     switch (aircraftSize) {
00169     case AIRCRAFT_SMALL:
00170         return _("Small");
00171     case AIRCRAFT_LARGE:
00172         return _("Large");
00173     default:
00174         return _("Unknown aircraft size");
00175     }
00176 }
00177 
00184 static void UP_DisplayTechTree (const technology_t* t)
00185 {
00186     linkedList_t *upTechtree;
00187     const requirements_t *required;
00188 
00189     required = &t->requireAND;
00190     upTechtree = NULL;
00191 
00192     if (required->numLinks <= 0)
00193         LIST_AddString(&upTechtree, _("No requirements"));
00194     else {
00195         int i;
00196         for (i = 0; i < required->numLinks; i++) {
00197             const requirement_t *req = &required->links[i];
00198             if (req->type == RS_LINK_TECH) {
00199                 technology_t *techRequired = req->link;
00200                 if (!techRequired)
00201                     Com_Error(ERR_DROP, "Could not find the tech for '%s'", req->id);
00202 
00205                 if (!UP_TechGetsDisplayed(techRequired))
00206                     continue;
00207 
00208                 LIST_AddString(&upTechtree, _(techRequired->name));
00209             }
00210         }
00211     }
00212 
00213     /* and now register the buffer */
00214     Cvar_Set("mn_uprequirement", "1");
00215     UI_RegisterLinkedListText(TEXT_UFOPEDIA_REQUIREMENT, upTechtree);
00216 }
00217 
00222 static void UP_BuildingDescription (const technology_t* t)
00223 {
00224     const building_t* b = B_GetBuildingTemplate(t->provides);
00225 
00226     if (!b) {
00227         Com_sprintf(upBuffer, sizeof(upBuffer), _("Error - could not find building"));
00228     } else {
00229         Com_sprintf(upBuffer, sizeof(upBuffer), _("Needs:\t%s\n"), b->dependsBuilding ? _(b->dependsBuilding->name) : _("None"));
00230         Q_strcat(upBuffer, va(ngettext("Construction time:\t%i day\n", "Construction time:\t%i days\n", b->buildTime), b->buildTime), sizeof(upBuffer));
00231         Q_strcat(upBuffer, va(_("Cost:\t%i c\n"), b->fixCosts), sizeof(upBuffer));
00232         Q_strcat(upBuffer, va(_("Running costs:\t%i c\n"), b->varCosts), sizeof(upBuffer));
00233     }
00234 
00235     Cvar_Set("mn_upmetadata", "1");
00236     UI_RegisterText(TEXT_ITEMDESCRIPTION, upBuffer);
00237     UP_DisplayTechTree(t);
00238 }
00239 
00248 void UP_AircraftItemDescription (const objDef_t *item)
00249 {
00250     static char itemText[1024];
00251     const technology_t *tech;
00252 
00253     /* Set menu text node content to null. */
00254     INV_ItemDescription(NULL);
00255     *itemText = '\0';
00256 
00257     /* no valid item id given */
00258     if (!item) {
00259         Cvar_Set("mn_item", "");
00260         Cvar_Set("mn_itemname", "");
00261         Cvar_Set("mn_upmodel_top", "");
00262         UI_ResetData(TEXT_ITEMDESCRIPTION);
00263         return;
00264     }
00265 
00266     tech = RS_GetTechForItem(item);
00267     /* select item */
00268     assert(item->craftitem.type >= 0);
00269     Cvar_Set("mn_item", item->id);
00270     Cvar_Set("mn_itemname", _(tech->name));
00271     if (tech->mdl)
00272         Cvar_Set("mn_upmodel_top", tech->mdl);
00273     else
00274         Cvar_Set("mn_upmodel_top", "");
00275 
00276     /* set description text */
00277     if (RS_IsResearched_ptr(tech)) {
00278         int i;
00279         if (item->craftitem.type == AC_ITEM_WEAPON)
00280             Q_strcat(itemText, va(_("Weight:\t%s\n"), AII_WeightToName(AII_GetItemWeightBySize(item))), sizeof(itemText));
00281         else if (item->craftitem.type == AC_ITEM_AMMO) {
00282             /* We display the characteristics of this ammo */
00283             Q_strcat(itemText, va(_("Ammo:\t%i\n"), item->ammo), sizeof(itemText));
00284             if (!equal(item->craftitem.weaponDamage, 0))
00285                 Q_strcat(itemText, va(_("Damage:\t%i\n"), (int) item->craftitem.weaponDamage), sizeof(itemText));
00286             Q_strcat(itemText, va(_("Reloading time:\t%i\n"),  (int) item->craftitem.weaponDelay), sizeof(itemText));
00287         } else if (item->craftitem.type < AC_ITEM_WEAPON) {
00288             Q_strcat(itemText, _("Weapon for base defence system\n"), sizeof(itemText));
00289         }
00290         /* We write the range of the weapon */
00291         if (!equal(item->craftitem.stats[AIR_STATS_WRANGE], 0))
00292             Q_strcat(itemText, va("%s:\t%i\n", CL_AircraftStatToName(AIR_STATS_WRANGE),
00293                 CL_AircraftMenuStatsValues(item->craftitem.stats[AIR_STATS_WRANGE], AIR_STATS_WRANGE)), sizeof(itemText));
00294 
00295         /* we scan all stats except weapon range */
00296         for (i = 0; i < AIR_STATS_MAX; i++) {
00297             const char *statsName = CL_AircraftStatToName(i);
00298             if (i == AIR_STATS_WRANGE)
00299                 continue;
00300             if (item->craftitem.stats[i] > 2.0f)
00301                 Q_strcat(itemText, va("%s:\t+%i\n", statsName, CL_AircraftMenuStatsValues(item->craftitem.stats[i], i)), sizeof(itemText));
00302             else if (item->craftitem.stats[i] < -2.0f)
00303                 Q_strcat(itemText, va("%s:\t%i\n", statsName, CL_AircraftMenuStatsValues(item->craftitem.stats[i], i)), sizeof(itemText));
00304             else if (item->craftitem.stats[i] > 1.0f)
00305                 Q_strcat(itemText, va(_("%s:\t+%i %%\n"), statsName, (int)(item->craftitem.stats[i] * 100) - 100), sizeof(itemText));
00306             else if (!equal(item->craftitem.stats[i], 0))
00307                 Q_strcat(itemText, va(_("%s:\t%i %%\n"), statsName, (int)(item->craftitem.stats[i] * 100) - 100), sizeof(itemText));
00308         }
00309     } else {
00310         Q_strcat(itemText, _("Unknown - need to research this"), sizeof(itemText));
00311     }
00312 
00313     Cvar_Set("mn_upmetadata", "1");
00314     UI_RegisterText(TEXT_ITEMDESCRIPTION, itemText);
00315 }
00316 
00323 void UP_AircraftDescription (const technology_t* tech)
00324 {
00325     INV_ItemDescription(NULL);
00326 
00327     /* ensure that the buffer is emptied in every case */
00328     upBuffer[0] = '\0';
00329 
00330     if (RS_IsResearched_ptr(tech)) {
00331         const aircraft_t* aircraft = AIR_GetAircraft(tech->provides);
00332         int i;
00333         for (i = 0; i < AIR_STATS_MAX; i++) {
00334             switch (i) {
00335             case AIR_STATS_SPEED:
00336                 /* speed may be converted to km/h : multiply by pi / 180 * earth_radius */
00337                 Q_strcat(upBuffer, va(_("%s:\t%i km/h\n"), CL_AircraftStatToName(i),
00338                     CL_AircraftMenuStatsValues(aircraft->stats[i], i)), sizeof(upBuffer));
00339                 break;
00340             case AIR_STATS_MAXSPEED:
00341                 /* speed may be converted to km/h : multiply by pi / 180 * earth_radius */
00342                 Q_strcat(upBuffer, va(_("%s:\t%i km/h\n"), CL_AircraftStatToName(i),
00343                     CL_AircraftMenuStatsValues(aircraft->stats[i], i)), sizeof(upBuffer));
00344                 break;
00345             case AIR_STATS_FUELSIZE:
00346                 Q_strcat(upBuffer, va(_("Operational range:\t%i km\n"),
00347                     AIR_GetOperationRange(aircraft)), sizeof(upBuffer));
00348             case AIR_STATS_ACCURACY:
00349                 Q_strcat(upBuffer, va(_("%s:\t%i\n"), CL_AircraftStatToName(i),
00350                     CL_AircraftMenuStatsValues(aircraft->stats[i], i)), sizeof(upBuffer));
00351                 break;
00352             default:
00353                 break;
00354             }
00355         }
00356         Q_strcat(upBuffer, va(_("Aircraft size:\t%s\n"), CL_AircraftSizeToName(aircraft->size)), sizeof(upBuffer));
00357         /* @note: while MAX_ACTIVETEAM limits the number of soldiers on a craft
00358          * there is no use to show this in case of an UFO (would be misleading): */
00359         if (!AIR_IsUFO(aircraft))
00360             Q_strcat(upBuffer, va(_("Max. soldiers:\t%i\n"), aircraft->maxTeamSize), sizeof(upBuffer));
00361     } else if (RS_Collected_(tech)) {
00363         Com_sprintf(upBuffer, sizeof(upBuffer), _("Unknown - need to research this"));
00364     } else {
00365         Com_sprintf(upBuffer, sizeof(upBuffer), _("Unknown - need to research this"));
00366     }
00367 
00368     Cvar_Set("mn_upmetadata", "1");
00369     UI_RegisterText(TEXT_ITEMDESCRIPTION, upBuffer);
00370     UP_DisplayTechTree(tech);
00371 }
00372 
00379 void UP_UGVDescription (const ugv_t *ugvType)
00380 {
00381     static char itemText[512];
00382     const technology_t *tech;
00383 
00384     assert(ugvType);
00385 
00386     tech = RS_GetTechByProvided(ugvType->id);
00387     assert(tech);
00388 
00389     INV_ItemDescription(NULL);
00390 
00391     /* Set name of ugv/robot */
00392     Cvar_Set("mn_itemname", _(tech->name));
00393     Cvar_Set("mn_item", tech->provides);
00394 
00395     Cvar_Set("mn_upmetadata", "1");
00396     if (RS_IsResearched_ptr(tech)) {
00398         Com_sprintf(itemText, sizeof(itemText), _("%s\n%s"), _(tech->name), ugvType->weapon);
00399     } else if (RS_Collected_(tech)) {
00401         Com_sprintf(itemText, sizeof(itemText), _("Unknown - need to research this"));
00402     } else {
00403         Com_sprintf(itemText, sizeof(itemText), _("Unknown - need to research this"));
00404     }
00405     UI_RegisterText(TEXT_ITEMDESCRIPTION, itemText);
00406 }
00407 
00414 int UP_GetUnreadMails (void)
00415 {
00416     const message_t *m = cp_messageStack;
00417 
00418     if (ccs.numUnreadMails != -1)
00419         return ccs.numUnreadMails;
00420 
00421     ccs.numUnreadMails = 0;
00422 
00423     while (m) {
00424         switch (m->type) {
00425         case MSG_RESEARCH_PROPOSAL:
00426             assert(m->pedia);
00427             if (m->pedia->mail[TECHMAIL_PRE].from && !m->pedia->mail[TECHMAIL_PRE].read)
00428                 ccs.numUnreadMails++;
00429             break;
00430         case MSG_RESEARCH_FINISHED:
00431             assert(m->pedia);
00432             if (m->pedia->mail[TECHMAIL_RESEARCHED].from && RS_IsResearched_ptr(m->pedia) && !m->pedia->mail[TECHMAIL_RESEARCHED].read)
00433                 ccs.numUnreadMails++;
00434             break;
00435         case MSG_NEWS:
00436             assert(m->pedia);
00437             if (m->pedia->mail[TECHMAIL_PRE].from && !m->pedia->mail[TECHMAIL_PRE].read)
00438                 ccs.numUnreadMails++;
00439             if (m->pedia->mail[TECHMAIL_RESEARCHED].from && !m->pedia->mail[TECHMAIL_RESEARCHED].read)
00440                 ccs.numUnreadMails++;
00441             break;
00442         case MSG_EVENT:
00443             assert(m->eventMail);
00444             if (!m->eventMail->read)
00445                 ccs.numUnreadMails++;
00446             break;
00447         default:
00448             break;
00449         }
00450         m = m->next;
00451     }
00452 
00453     /* use strings here */
00454     Cvar_Set("mn_upunreadmail", va("%i", ccs.numUnreadMails));
00455     return ccs.numUnreadMails;
00456 }
00457 
00468 static void UP_SetMailHeader (technology_t* tech, techMailType_t type, eventMail_t* mail)
00469 {
00470     static char mailHeader[8 * MAX_VAR] = ""; /* bigger as techMail_t (utf8) */
00471     char dateBuf[MAX_VAR] = "";
00472     const char *subjectType = "";
00473     const char *from, *to, *subject, *model;
00474     dateLong_t date;
00475 
00476     if (mail) {
00477         from = mail->from;
00478         to = mail->to;
00479         model = mail->model;
00480         subject = mail->subject;
00481         Q_strncpyz(dateBuf, _(mail->date), sizeof(dateBuf));
00482         mail->read = qtrue;
00483         /* reread the unread mails in UP_GetUnreadMails */
00484         ccs.numUnreadMails = -1;
00485     } else {
00486         assert(tech);
00487         assert(type < TECHMAIL_MAX);
00488 
00489         from = tech->mail[type].from;
00490         to = tech->mail[type].to;
00491         subject = tech->mail[type].subject;
00492         model = tech->mail[type].model;
00493 
00494         if (tech->mail[type].date) {
00495             Q_strncpyz(dateBuf, _(tech->mail[type].date), sizeof(dateBuf));
00496         } else {
00497             switch (type) {
00498             case TECHMAIL_PRE:
00499                 CL_DateConvertLong(&tech->preResearchedDate, &date);
00500                 Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"),
00501                     date.year, Date_GetMonthName(date.month - 1), date.day);
00502                 break;
00503             case TECHMAIL_RESEARCHED:
00504                 CL_DateConvertLong(&tech->researchedDate, &date);
00505                 Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"),
00506                     date.year, Date_GetMonthName(date.month - 1), date.day);
00507                 break;
00508             default:
00509                 Com_Error(ERR_DROP, "UP_SetMailHeader: unhandled techMailType_t %i for date.", type);
00510             }
00511         }
00512         if (from != NULL) {
00513             if (!tech->mail[type].read) {
00514                 tech->mail[type].read = qtrue;
00515                 /* reread the unread mails in UP_GetUnreadMails */
00516                 ccs.numUnreadMails = -1;
00517             }
00518             /* only if mail and mail_pre are available */
00519             if (tech->numTechMails == TECHMAIL_MAX) {
00520                 switch (type) {
00521                 case TECHMAIL_PRE:
00522                     subjectType = _("Proposal: ");
00523                     break;
00524                 case TECHMAIL_RESEARCHED:
00525                     subjectType = _("Re: ");
00526                     break;
00527                 default:
00528                     Com_Error(ERR_DROP, "UP_SetMailHeader: unhandled techMailType_t %i for subject.", type);
00529                 }
00530             }
00531         } else {
00532             UI_ResetData(TEXT_UFOPEDIA_MAILHEADER);
00533             return;
00534         }
00535     }
00536     Com_sprintf(mailHeader, sizeof(mailHeader), _("FROM: %s\nTO: %s\nDATE: %s"),
00537         _(from), _(to), dateBuf);
00538     Cvar_Set("mn_mail_sender_head", model ? model : "");
00539     Cvar_Set("mn_mail_from", _(from));
00540     Cvar_Set("mn_mail_subject", va("%s%s", subjectType, _(subject)));
00541     Cvar_Set("mn_mail_to", _(to));
00542     Cvar_Set("mn_mail_date", dateBuf);
00543     UI_RegisterText(TEXT_UFOPEDIA_MAILHEADER, mailHeader);
00544 }
00545 
00551 static void UP_DrawAssociatedAmmo (const technology_t* tech)
00552 {
00553     const objDef_t *od = INVSH_GetItemByID(tech->provides);
00554     /* If this is a weapon, we display the model of the associated ammunition in the lower right */
00555     if (od->numAmmos > 0) {
00556         const technology_t *associated = RS_GetTechForItem(od->ammos[0]);
00557         Cvar_Set("mn_upmodel_bottom", associated->mdl);
00558     }
00559 }
00560 
00567 static void UP_Article (technology_t* tech, eventMail_t *mail)
00568 {
00569     UP_ChangeDisplay(UFOPEDIA_ARTICLE);
00570 
00571     if (tech) {
00572         if (tech->mdl)
00573             Cvar_Set("mn_upmodel_top", tech->mdl);
00574         else
00575             Cvar_Set("mn_upmodel_top", "");
00576 
00577         if (tech->image)
00578             Cvar_Set("mn_upimage_top", tech->image);
00579         else
00580             Cvar_Set("mn_upimage_top", "");
00581 
00582         Cvar_Set("mn_upmodel_bottom", "");
00583 
00584         if (tech->type == RS_WEAPON)
00585             UP_DrawAssociatedAmmo(tech);
00586         Cvar_Set("mn_uprequirement", "");
00587         Cvar_Set("mn_upmetadata", "");
00588     }
00589 
00590     UI_ResetData(TEXT_UFOPEDIA);
00591     UI_ResetData(TEXT_UFOPEDIA_REQUIREMENT);
00592 
00593     if (mail) {
00594         /* event mail */
00595         Cvar_SetValue("mn_uppreavailable", 0);
00596         Cvar_SetValue("mn_updisplay", UFOPEDIA_CHAPTERS);
00597         UP_SetMailHeader(NULL, 0, mail);
00598         UI_RegisterText(TEXT_UFOPEDIA, _(mail->body));
00599         /* This allows us to use the index button in the UFOpaedia,
00600          * eventMails don't have any chapter to go back to. */
00601         upDisplay = UFOPEDIA_INDEX;
00602     } else if (tech) {
00603         currentChapter = tech->upChapter;
00604         upCurrentTech = tech;
00605 
00606         /* Reset itemdescription */
00607         UI_ExecuteConfunc("itemdesc_view 0 0;");
00608         if (RS_IsResearched_ptr(tech)) {
00609             int i;
00610             Cvar_Set("mn_uptitle", va("%s: %s %s", _("UFOpaedia"), _(tech->name), _("(complete)")));
00611             /* If researched -> display research text */
00612             UI_RegisterText(TEXT_UFOPEDIA, _(RS_GetDescription(&tech->description)));
00613             if (tech->preDescription.numDescriptions > 0) {
00614                 /* Display pre-research text and the buttons if a pre-research text is available. */
00615                 if (mn_uppretext->integer) {
00616                     UI_RegisterText(TEXT_UFOPEDIA, _(RS_GetDescription(&tech->preDescription)));
00617                     UP_SetMailHeader(tech, TECHMAIL_PRE, NULL);
00618                 } else {
00619                     UP_SetMailHeader(tech, TECHMAIL_RESEARCHED, NULL);
00620                 }
00621                 Cvar_SetValue("mn_uppreavailable", 1);
00622             } else {
00623                 /* Do not display the pre-research-text button if none is available (no need to even bother clicking there). */
00624                 Cvar_SetValue("mn_uppreavailable", 0);
00625                 Cvar_SetValue("mn_updisplay", UFOPEDIA_CHAPTERS);
00626                 UP_SetMailHeader(tech, TECHMAIL_RESEARCHED, NULL);
00627             }
00628 
00629             switch (tech->type) {
00630             case RS_ARMOUR:
00631             case RS_WEAPON:
00632                 for (i = 0; i < csi.numODs; i++) {
00633                     const objDef_t *od = INVSH_GetItemByIDX(i);
00634                     if (!strcmp(tech->provides, od->id)) {
00635                         INV_ItemDescription(od);
00636                         UP_DisplayTechTree(tech);
00637                         Cvar_Set("mn_upmetadata", "1");
00638                         break;
00639                     }
00640                 }
00641                 break;
00642             case RS_TECH:
00643                 UP_DisplayTechTree(tech);
00644                 break;
00645             case RS_CRAFT:
00646                 UP_AircraftDescription(tech);
00647                 break;
00648             case RS_CRAFTITEM:
00649                 UP_AircraftItemDescription(INVSH_GetItemByID(tech->provides));
00650                 break;
00651             case RS_BUILDING:
00652                 UP_BuildingDescription(tech);
00653                 break;
00654             case RS_UGV:
00655                 UP_UGVDescription(Com_GetUGVByIDSilent(tech->provides));
00656                 break;
00657             default:
00658                 break;
00659             }
00660         /* see also UP_TechGetsDisplayed */
00661         } else if (RS_Collected_(tech) || (tech->statusResearchable && tech->preDescription.numDescriptions > 0)) {
00662             /* This tech has something collected or has a research proposal. (i.e. pre-research text) */
00663             Cvar_Set("mn_uptitle", va("%s: %s", _("UFOpaedia"), _(tech->name)));
00664             /* Not researched but some items collected -> display pre-research text if available. */
00665             if (tech->preDescription.numDescriptions > 0) {
00666                 UI_RegisterText(TEXT_UFOPEDIA, _(RS_GetDescription(&tech->preDescription)));
00667                 UP_SetMailHeader(tech, TECHMAIL_PRE, NULL);
00668             } else {
00669                 UI_RegisterText(TEXT_UFOPEDIA, _("No pre-research description available."));
00670             }
00671         } else {
00672             Cvar_Set("mn_uptitle", va("%s: %s", _("UFOpaedia"), _(tech->name)));
00673             UI_ResetData(TEXT_UFOPEDIA);
00674         }
00675     } else {
00676         Com_Error(ERR_DROP, "UP_Article: No mail or tech given");
00677     }
00678 }
00679 
00683 void UP_OpenEventMail (const char *eventMailID)
00684 {
00685     eventMail_t* mail;
00686     mail = CL_GetEventMail(eventMailID, qfalse);
00687     if (!mail)
00688         return;
00689 
00690     UI_PushWindow("mail", NULL);
00691     UP_Article(NULL, mail);
00692 }
00693 
00699 static void UP_OpenMailWith (const char *techID)
00700 {
00701     if (!techID)
00702         return;
00703 
00704     UI_PushWindow("mail", NULL);
00705     Cbuf_AddText(va("ufopedia %s\n", techID));
00706 }
00707 
00713 void UP_OpenWith (const char *techID)
00714 {
00715     if (!techID)
00716         return;
00717 
00718     UI_PushWindow("ufopedia", NULL);
00719     Cbuf_AddText(va("ufopedia %s; update_ufopedia_layout;\n", techID));
00720 }
00721 
00727 void UP_OpenCopyWith (const char *techID)
00728 {
00729     Cmd_ExecuteString("mn_push ufopedia");
00730     Cbuf_AddText(va("ufopedia %s\n", techID));
00731 }
00732 
00733 
00737 static void UP_FindEntry_f (void)
00738 {
00739     const char *id;
00740     technology_t *tech;
00741 
00742     if (Cmd_Argc() < 2) {
00743         Com_Printf("Usage: %s <id>\n", Cmd_Argv(0));
00744         return;
00745     }
00746 
00747     /* what are we searching for? */
00748     id = Cmd_Argv(1);
00749 
00750     /* maybe we get a call like 'ufopedia ""' */
00751     if (id[0] == '\0') {
00752         Com_Printf("UP_FindEntry_f: No UFOpaedia entry given as parameter\n");
00753         return;
00754     }
00755 
00756     tech = RS_GetTechByID(id);
00757     if (!tech) {
00758         Com_DPrintf(DEBUG_CLIENT, "UP_FindEntry_f: No UFOpaedia entry found for %s\n", id);
00759         return;
00760     }
00761 
00762     if (tech->redirect)
00763         tech = tech->redirect;
00764 
00765     UP_Article(tech, NULL);
00766 }
00767 
00773 static uiNode_t* UP_GenerateArticlesSummary (pediaChapter_t *parentChapter)
00774 {
00775     technology_t *tech = parentChapter->first;
00776     uiNode_t* first = NULL;
00777 
00778     while (tech) {
00779         if (UP_TechGetsDisplayed(tech)) {
00780             const char* id = va("@%i", tech->idx);
00781             UI_AddOption(&first, id, va("_%s", tech->name), id);
00782         }
00783         tech = tech->upNext;
00784     }
00785 
00786     UI_SortOptions(&first);
00787 
00788     return first;
00789 }
00790 
00795 static void UP_GenerateSummary (void)
00796 {
00797     int i;
00798     uiNode_t *chapters = NULL;
00799     int num = 0;
00800 
00801     numChaptersDisplayList = 0;
00802 
00803     for (i = 0; i < ccs.numChapters; i++) {
00804         /* Check if there are any researched or collected items in this chapter ... */
00805         uiNode_t *chapter;
00806         qboolean researchedEntries = qfalse;
00807         upCurrentTech = ccs.upChapters[i].first;
00808         do {
00809             if (UP_TechGetsDisplayed(upCurrentTech)) {
00810                 researchedEntries = qtrue;
00811                 break;
00812             }
00813             upCurrentTech = upCurrentTech->upNext;
00814         } while (upCurrentTech);
00815 
00816         /* .. and if so add them to the displaylist of chapters. */
00817         if (researchedEntries) {
00818             if (numChaptersDisplayList >= MAX_PEDIACHAPTERS)
00819                 Com_Error(ERR_DROP, "MAX_PEDIACHAPTERS hit");
00820             upChaptersDisplayList[numChaptersDisplayList++] = &ccs.upChapters[i];
00821 
00822             /* chapter section*/
00823             chapter = UI_AddOption(&chapters, ccs.upChapters[i].id, va("_%s", ccs.upChapters[i].name), va("%i", num));
00824             OPTIONEXTRADATA(chapter).icon = UI_GetIconByName(va("ufopedia_%s", ccs.upChapters[i].id));
00825             chapter->firstChild = UP_GenerateArticlesSummary(&ccs.upChapters[i]);
00826 
00827             num++;
00828         }
00829     }
00830 
00831     UI_RegisterOption(OPTION_UFOPEDIA, chapters);
00832     Cvar_Set("mn_uptitle", _("UFOpaedia"));
00833 }
00834 
00838 static void UP_Content_f (void)
00839 {
00840     UP_GenerateSummary();
00841     UP_ChangeDisplay(UFOPEDIA_CHAPTERS);
00842 }
00843 
00849 static void UP_Click_f (void)
00850 {
00851     if (Cmd_Argc() < 2)
00852         return;
00853 
00854     /* article index starts with a @ */
00855     if (Cmd_Argv(1)[0] == '@') {
00856         const int techId = atoi(Cmd_Argv(1) + 1);
00857         technology_t* tech;
00858         assert(techId >= 0);
00859         assert(techId < ccs.numTechnologies);
00860         tech = &ccs.technologies[techId];
00861         if (tech)
00862             UP_Article(tech, NULL);
00863         return;
00864     } else {
00865         /* Reset itemdescription */
00866         UI_ExecuteConfunc("itemdesc_view 0 0;");
00867     }
00868 
00869     /* it clean up the display */
00870     UP_ChangeDisplay(UFOPEDIA_CHAPTERS);
00871 }
00872 
00876 static void UP_TechTreeClick_f (void)
00877 {
00878     int num;
00879     int i;
00880     requirements_t *required_AND;
00881     technology_t *techRequired;
00882 
00883     if (Cmd_Argc() < 2)
00884         return;
00885     num = atoi(Cmd_Argv(1));
00886 
00887     if (!upCurrentTech)
00888         return;
00889 
00890     required_AND = &upCurrentTech->requireAND;
00891     if (num < 0 || num >= required_AND->numLinks)
00892         return;
00893 
00894     /* skip every tech which have not been displayed in techtree */
00895     for (i = 0; i <= num; i++) {
00896         if (required_AND->links[i].type != RS_LINK_TECH
00897          && required_AND->links[i].type != RS_LINK_TECH_NOT)
00898             num++;
00899     }
00900 
00901     techRequired = required_AND->links[num].link;
00902     if (!techRequired)
00903         Com_Error(ERR_DROP, "Could not find the tech for '%s'", required_AND->links[num].id);
00904 
00905     /* maybe there is no UFOpaedia chapter assigned - this tech should not be opened at all */
00906     if (!techRequired->upChapter)
00907         return;
00908 
00909     UP_OpenWith(techRequired->id);
00910 }
00911 
00915 static void UP_Update_f (void)
00916 {
00917     if (upCurrentTech)
00918         UP_Article(upCurrentTech, NULL);
00919 }
00920 
00925 static void UP_MailClientClick_f (void)
00926 {
00927     message_t *m = cp_messageStack;
00928     int num;
00929     int cnt = -1;
00930 
00931     if (Cmd_Argc() < 2)
00932         return;
00933 
00934     num = atoi(Cmd_Argv(1));
00935 
00936     while (m) {
00937         switch (m->type) {
00938         case MSG_RESEARCH_PROPOSAL:
00939             if (!m->pedia->mail[TECHMAIL_PRE].from)
00940                 break;
00941             cnt++;
00942             if (cnt == num) {
00943                 Cvar_SetValue("mn_uppretext", 1);
00944                 UP_OpenMailWith(m->pedia->id);
00945                 return;
00946             }
00947             break;
00948         case MSG_RESEARCH_FINISHED:
00949             if (!m->pedia->mail[TECHMAIL_RESEARCHED].from)
00950                 break;
00951             cnt++;
00952             if (cnt == num) {
00953                 Cvar_SetValue("mn_uppretext", 0);
00954                 UP_OpenMailWith(m->pedia->id);
00955                 return;
00956             }
00957             break;
00958         case MSG_NEWS:
00959             if (m->pedia->mail[TECHMAIL_PRE].from || m->pedia->mail[TECHMAIL_RESEARCHED].from) {
00960                 cnt++;
00961                 if (cnt >= num) {
00962                     UP_OpenMailWith(m->pedia->id);
00963                     return;
00964                 }
00965             }
00966             break;
00967         case MSG_EVENT:
00968             cnt++;
00969             if (cnt >= num) {
00970                 UP_OpenEventMail(m->eventMail->id);
00971                 return;
00972             }
00973             break;
00974         default:
00975             break;
00976         }
00977         m = m->next;
00978     }
00979 }
00980 
00984 static void UP_ResearchedLinkClick_f (void)
00985 {
00986     objDef_t *od;
00987 
00988     if (!upCurrentTech) /* if called from console */
00989         return;
00990 
00991     od = INVSH_GetItemByID(upCurrentTech->provides);
00992     assert(od);
00993 
00994     if (INV_IsAmmo(od)) {
00995         const technology_t *t = RS_GetTechForItem(od->weapons[0]);
00996         if (UP_TechGetsDisplayed(t))
00997             UP_OpenWith(t->id);
00998     } else if (od->weapon && od->reload) {
00999         const technology_t *t = RS_GetTechForItem(od->ammos[0]);
01000         if (UP_TechGetsDisplayed(t))
01001             UP_OpenWith(t->id);
01002     }
01003 }
01004 
01011 static void UP_SetMailButtons_f (void)
01012 {
01013     int i = 0, num;
01014     const message_t *m = cp_messageStack;
01015 
01016     if (Cmd_Argc() != 2) {
01017         Com_Printf("Usage: %s <pos>\n", Cmd_Argv(0));
01018         return;
01019     }
01020 
01021     num = atoi(Cmd_Argv(1));
01022 
01023     while (m && (i < MAIL_CLIENT_LINES)) {
01024         switch (m->type) {
01025         case MSG_RESEARCH_PROPOSAL:
01026             if (!m->pedia->mail[TECHMAIL_PRE].from)
01027                 break;
01028             if (num) {
01029                 num--;
01030             } else {
01031                 Cvar_Set(va("mn_mail_read%i", i), m->pedia->mail[TECHMAIL_PRE].read ? "1": "0");
01032                 Cvar_Set(va("mn_mail_icon%i", i++), m->pedia->mail[TECHMAIL_PRE].icon);
01033             }
01034             break;
01035         case MSG_RESEARCH_FINISHED:
01036             if (!m->pedia->mail[TECHMAIL_RESEARCHED].from)
01037                 break;
01038             if (num) {
01039                 num--;
01040             } else {
01041                 Cvar_Set(va("mn_mail_read%i", i), m->pedia->mail[TECHMAIL_RESEARCHED].read ? "1": "0");
01042                 Cvar_Set(va("mn_mail_icon%i", i++), m->pedia->mail[TECHMAIL_RESEARCHED].icon);
01043             }
01044             break;
01045         case MSG_NEWS:
01046             if (m->pedia->mail[TECHMAIL_PRE].from) {
01047                 if (num) {
01048                     num--;
01049                 } else {
01050                     Cvar_Set(va("mn_mail_read%i", i), m->pedia->mail[TECHMAIL_PRE].read ? "1": "0");
01051                     Cvar_Set(va("mn_mail_icon%i", i++), m->pedia->mail[TECHMAIL_PRE].icon);
01052                 }
01053             } else if (m->pedia->mail[TECHMAIL_RESEARCHED].from) {
01054                 if (num) {
01055                     num--;
01056                 } else {
01057                     Cvar_Set(va("mn_mail_read%i", i), m->pedia->mail[TECHMAIL_RESEARCHED].read ? "1": "0");
01058                     Cvar_Set(va("mn_mail_icon%i", i++), m->pedia->mail[TECHMAIL_RESEARCHED].icon);
01059                 }
01060             }
01061             break;
01062         case MSG_EVENT:
01063             if (m->eventMail->from) {
01064                 if (num) {
01065                     num--;
01066                 } else {
01067                     Cvar_Set(va("mn_mail_read%i", i), m->eventMail->read ? "1": "0");
01068                     Cvar_Set(va("mn_mail_icon%i", i++), m->eventMail->icon ? m->eventMail->icon : "");
01069                 }
01070             }
01071             break;
01072         default:
01073             break;
01074         }
01075         m = m->next;
01076     }
01077     while (i < MAIL_CLIENT_LINES) {
01078         Cvar_Set(va("mn_mail_read%i", i), "-1");
01079         Cvar_Set(va("mn_mail_icon%i", i++), "");
01080     }
01081 }
01082 
01090 static void UP_OpenMail_f (void)
01091 {
01092     char tempBuf[MAIL_LENGTH] = "";
01093     const message_t *m = cp_messageStack;
01094     dateLong_t date;
01095 
01096     mailBuffer[0] = '\0';
01097 
01098     while (m) {
01099         switch (m->type) {
01100         case MSG_RESEARCH_PROPOSAL:
01101             if (!m->pedia->mail[TECHMAIL_PRE].from)
01102                 break;
01103             CL_DateConvertLong(&m->pedia->preResearchedDate, &date);
01104             if (!m->pedia->mail[TECHMAIL_PRE].read)
01105                 Com_sprintf(tempBuf, sizeof(tempBuf), _("^BProposal: %s\t%i %s %02i\n"),
01106                     _(m->pedia->mail[TECHMAIL_PRE].subject),
01107                     date.year, Date_GetMonthName(date.month - 1), date.day);
01108             else
01109                 Com_sprintf(tempBuf, sizeof(tempBuf), _("Proposal: %s\t%i %s %02i\n"),
01110                     _(m->pedia->mail[TECHMAIL_PRE].subject),
01111                     date.year, Date_GetMonthName(date.month - 1), date.day);
01112             CHECK_MAIL_EOL
01113             Q_strcat(mailBuffer, tempBuf, sizeof(mailBuffer));
01114             break;
01115         case MSG_RESEARCH_FINISHED:
01116             if (!m->pedia->mail[TECHMAIL_RESEARCHED].from)
01117                 break;
01118             CL_DateConvertLong(&m->pedia->researchedDate, &date);
01119             if (!m->pedia->mail[TECHMAIL_RESEARCHED].read)
01120                 Com_sprintf(tempBuf, sizeof(tempBuf), _("^BRe: %s\t%i %s %02i\n"),
01121                     _(m->pedia->mail[TECHMAIL_RESEARCHED].subject),
01122                     date.year, Date_GetMonthName(date.month - 1), date.day);
01123             else
01124                 Com_sprintf(tempBuf, sizeof(tempBuf), _("Re: %s\t%i %s %02i\n"),
01125                     _(m->pedia->mail[TECHMAIL_RESEARCHED].subject),
01126                     date.year, Date_GetMonthName(date.month - 1), date.day);
01127             CHECK_MAIL_EOL
01128             Q_strcat(mailBuffer, tempBuf, sizeof(mailBuffer));
01129             break;
01130         case MSG_NEWS:
01131             if (m->pedia->mail[TECHMAIL_PRE].from) {
01132                 CL_DateConvertLong(&m->pedia->preResearchedDate, &date);
01133                 if (!m->pedia->mail[TECHMAIL_PRE].read)
01134                     Com_sprintf(tempBuf, sizeof(tempBuf), _("^B%s\t%i %s %02i\n"),
01135                         _(m->pedia->mail[TECHMAIL_PRE].subject),
01136                         date.year, Date_GetMonthName(date.month - 1), date.day);
01137                 else
01138                     Com_sprintf(tempBuf, sizeof(tempBuf), _("%s\t%i %s %02i\n"),
01139                         _(m->pedia->mail[TECHMAIL_PRE].subject),
01140                         date.year, Date_GetMonthName(date.month - 1), date.day);
01141                 CHECK_MAIL_EOL
01142                 Q_strcat(mailBuffer, tempBuf, sizeof(mailBuffer));
01143             } else if (m->pedia->mail[TECHMAIL_RESEARCHED].from) {
01144                 CL_DateConvertLong(&m->pedia->researchedDate, &date);
01145                 if (!m->pedia->mail[TECHMAIL_RESEARCHED].read)
01146                     Com_sprintf(tempBuf, sizeof(tempBuf), _("^B%s\t%i %s %02i\n"),
01147                         _(m->pedia->mail[TECHMAIL_RESEARCHED].subject),
01148                         date.year, Date_GetMonthName(date.month - 1), date.day);
01149                 else
01150                     Com_sprintf(tempBuf, sizeof(tempBuf), _("%s\t%i %s %02i\n"),
01151                         _(m->pedia->mail[TECHMAIL_RESEARCHED].subject),
01152                         date.year, Date_GetMonthName(date.month - 1), date.day);
01153                 CHECK_MAIL_EOL
01154                 Q_strcat(mailBuffer, tempBuf, sizeof(mailBuffer));
01155             }
01156             break;
01157         case MSG_EVENT:
01158             assert(m->eventMail);
01159             if (!m->eventMail->from)
01160                 break;
01161             if (!m->eventMail->read)
01162                 Com_sprintf(tempBuf, sizeof(tempBuf), _("^B%s\t%s\n"),
01163                     _(m->eventMail->subject), _(m->eventMail->date));
01164             else
01165                 Com_sprintf(tempBuf, sizeof(tempBuf), _("%s\t%s\n"),
01166                     _(m->eventMail->subject), _(m->eventMail->date));
01167             CHECK_MAIL_EOL
01168             Q_strcat(mailBuffer, tempBuf, sizeof(mailBuffer));
01169             break;
01170         default:
01171             break;
01172         }
01173         m = m->next;
01174     }
01175     UI_RegisterText(TEXT_UFOPEDIA_MAIL, mailBuffer);
01176 
01177     UP_SetMailButtons_f();
01178 }
01179 
01183 static void UP_SetAllMailsRead_f (void)
01184 {
01185     const message_t *m = cp_messageStack;
01186 
01187     while (m) {
01188         switch (m->type) {
01189         case MSG_RESEARCH_PROPOSAL:
01190             assert(m->pedia);
01191             m->pedia->mail[TECHMAIL_PRE].read = qtrue;
01192             break;
01193         case MSG_RESEARCH_FINISHED:
01194             assert(m->pedia);
01195             m->pedia->mail[TECHMAIL_RESEARCHED].read = qtrue;
01196             break;
01197         case MSG_NEWS:
01198             assert(m->pedia);
01199             m->pedia->mail[TECHMAIL_PRE].read = qtrue;
01200             m->pedia->mail[TECHMAIL_RESEARCHED].read = qtrue;
01201             break;
01202         case MSG_EVENT:
01203             assert(m->eventMail);
01204             m->eventMail->read = qtrue;
01205             break;
01206         default:
01207             break;
01208         }
01209         m = m->next;
01210     }
01211 
01212     ccs.numUnreadMails = 0;
01213     Cvar_Set("mn_upunreadmail", va("%i", ccs.numUnreadMails));
01214     UP_OpenMail_f();
01215 }
01216 
01220 void UP_InitStartup (void)
01221 {
01222     /* add commands and cvars */
01223     Cmd_AddCommand("mn_upcontent", UP_Content_f, "Shows the UFOpaedia chapters");
01224     Cmd_AddCommand("mn_upupdate", UP_Update_f, "Redraw the current UFOpaedia article");
01225     Cmd_AddCommand("ufopedia", UP_FindEntry_f, "Open the UFOpaedia with the given article");
01226     Cmd_AddCommand("ufopedia_click", UP_Click_f, NULL);
01227     Cmd_AddCommand("mailclient_click", UP_MailClientClick_f, NULL);
01228     Cmd_AddCommand("mn_mail_readall", UP_SetAllMailsRead_f, "Mark all mails read");
01229     Cmd_AddCommand("ufopedia_openmail", UP_OpenMail_f, "Start the mailclient");
01230     Cmd_AddCommand("ufopedia_scrollmail", UP_SetMailButtons_f, NULL);
01231     Cmd_AddCommand("techtree_click", UP_TechTreeClick_f, NULL);
01232     Cmd_AddCommand("mn_upgotoresearchedlink", UP_ResearchedLinkClick_f, NULL);
01233 
01234     mn_uppretext = Cvar_Get("mn_uppretext", "0", 0, "Show the pre-research text in the UFOpaedia");
01235     mn_uppreavailable = Cvar_Get("mn_uppreavailable", "0", 0, "True if there is a pre-research text available");
01236     Cvar_Set("mn_uprequirement", "");
01237     Cvar_Set("mn_upmetadata", "");
01238 }
01239 
01243 void UP_Shutdown (void)
01244 {
01245     /* add commands and cvars */
01246     Cmd_RemoveCommand("mn_upcontent");
01247     Cmd_RemoveCommand("mn_upupdate");
01248     Cmd_RemoveCommand("ufopedia");
01249     Cmd_RemoveCommand("ufopedia_click");
01250     Cmd_RemoveCommand("mailclient_click");
01251     Cmd_RemoveCommand("mn_mail_readall");
01252     Cmd_RemoveCommand("ufopedia_openmail");
01253     Cmd_RemoveCommand("ufopedia_scrollmail");
01254     Cmd_RemoveCommand("techtree_click");
01255     Cmd_RemoveCommand("mn_upgotoresearchedlink");
01256 
01257     Cvar_Delete("mn_uppretext");
01258     Cvar_Delete("mn_uppreavailable");
01259     Cvar_Delete("mn_uprequirement");
01260     Cvar_Delete("mn_upmetadata");
01261 }
01262 
01269 void UP_ParseChapters (const char *name, const char **text)
01270 {
01271     const char *errhead = "UP_ParseChapters: unexpected end of file (names ";
01272     const char *token;
01273 
01274     /* get name list body body */
01275     token = Com_Parse(text);
01276 
01277     if (!*text || *token !='{') {
01278         Com_Printf("UP_ParseChapters: chapter def \"%s\" without body ignored\n", name);
01279         return;
01280     }
01281 
01282     do {
01283         /* get the id */
01284         token = Com_EParse(text, errhead, name);
01285         if (!*text)
01286             break;
01287         if (*token == '}')
01288             break;
01289 
01290         /* add chapter */
01291         if (ccs.numChapters >= MAX_PEDIACHAPTERS) {
01292             Com_Printf("UP_ParseChapters: too many chapter defs\n");
01293             return;
01294         }
01295         memset(&ccs.upChapters[ccs.numChapters], 0, sizeof(ccs.upChapters[ccs.numChapters]));
01296         ccs.upChapters[ccs.numChapters].id = Mem_PoolStrDup(token, cp_campaignPool, 0);
01297         ccs.upChapters[ccs.numChapters].idx = ccs.numChapters;  /* set self-link */
01298 
01299         /* get the name */
01300         token = Com_EParse(text, errhead, name);
01301         if (!*text)
01302             break;
01303         if (*token == '}')
01304             break;
01305         if (*token == '_')
01306             token++;
01307         if (!*token)
01308             continue;
01309         ccs.upChapters[ccs.numChapters].name = Mem_PoolStrDup(token, cp_campaignPool, 0);
01310 
01311         ccs.numChapters++;
01312     } while (*text);
01313 }

Generated by  doxygen 1.6.2