00001
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "../cl_shared.h"
00033 #include "../ui/ui_main.h"
00034 #include "../ui/ui_popup.h"
00035 #include "../../shared/parse.h"
00036 #include "cp_campaign.h"
00037 #include "cp_research.h"
00038 #include "save/save_research.h"
00039
00040 #define TECH_HASH_SIZE 64
00041 static technology_t *techHash[TECH_HASH_SIZE];
00042 static technology_t *techHashProvided[TECH_HASH_SIZE];
00043
00049 static inline void RS_PushNewsWhenResearchedFinished (const technology_t* tech)
00050 {
00051
00052
00053 }
00054
00059 void RS_ResearchFinish (technology_t* tech)
00060 {
00061
00062 while (tech->scientists > 0)
00063 RS_RemoveScientist(tech, NULL);
00064
00069 RS_GetDescription(&tech->description);
00070 if (tech->preDescription.usedDescription < 0) {
00071
00072 RS_GetDescription(&tech->preDescription);
00073 }
00074
00075
00076 if (tech->finishedResearchEvent && tech->statusResearch != RS_FINISH)
00077 Cmd_ExecuteString(tech->finishedResearchEvent);
00078
00079 tech->statusResearch = RS_FINISH;
00080 tech->researchedDate = ccs.date;
00081 if (!tech->statusResearchable) {
00082 tech->statusResearchable = qtrue;
00083 tech->preResearchedDate = ccs.date;
00084 }
00085 RS_PushNewsWhenResearchedFinished(tech);
00086
00087
00088 if ((tech->mailSent < MAILSENT_FINISHED) && (tech->type != RS_LOGIC)) {
00089 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A research project has been completed: %s\n"), _(tech->name));
00090 MSO_CheckAddNewMessage( NT_RESEARCH_COMPLETED, _("Research finished"), cp_messageBuffer, qfalse, MSG_RESEARCH_FINISHED, tech);
00091 tech->mailSent = MAILSENT_FINISHED;
00092 }
00093 }
00094
00099 void RS_StopResearch (technology_t* tech)
00100 {
00101 assert(tech);
00102 while (tech->scientists > 0)
00103 RS_RemoveScientist(tech, NULL);
00104 assert(tech->statusResearch == RS_PAUSED);
00105 }
00106
00113 void RS_MarkOneResearchable (technology_t* tech)
00114 {
00115 if (!tech)
00116 return;
00117
00118 Com_DPrintf(DEBUG_CLIENT, "RS_MarkOneResearchable: \"%s\" marked as researchable.\n", tech->id);
00119
00120
00121 if (tech->time == -1)
00122 return;
00123
00124
00125 if (tech->time == 0)
00126 tech->mailSent = MAILSENT_FINISHED;
00127
00132 RS_GetDescription(&tech->preDescription);
00133
00134
00135 if (tech->mailSent < MAILSENT_PROPOSAL) {
00136 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("New research proposal: %s\n"), _(tech->name));
00137 MSO_CheckAddNewMessage(NT_RESEARCH_PROPOSED, _("Unknown Technology researchable"), cp_messageBuffer, qfalse, MSG_RESEARCH_PROPOSAL, tech);
00138 tech->mailSent = MAILSENT_PROPOSAL;
00139 }
00140
00141 tech->statusResearchable = qtrue;
00142
00143
00144 if (tech->preResearchedDate.day == 0) {
00145 tech->preResearchedDate = ccs.date;
00146 }
00147 }
00148
00158 qboolean RS_RequirementsMet (const requirements_t *requiredAND, const requirements_t *requiredOR, const base_t *base)
00159 {
00160 int i;
00161 qboolean metAND = qfalse;
00162 qboolean metOR = qfalse;
00163
00164 if (!requiredAND && !requiredOR) {
00165 Com_Printf("RS_RequirementsMet: No requirement list(s) given as parameter.\n");
00166 return qfalse;
00167 }
00168
00169
00170 if (requiredAND->numLinks == 0 && requiredOR->numLinks == 0) {
00171 Com_DPrintf(DEBUG_CLIENT, "RS_RequirementsMet: No requirements set for this tech. They are 'met'.\n");
00172 return qtrue;
00173 }
00174
00175 if (requiredAND->numLinks) {
00176 metAND = qtrue;
00177 for (i = 0; i < requiredAND->numLinks; i++) {
00178 const requirement_t *req = &requiredAND->links[i];
00179 switch (req->type) {
00180 case RS_LINK_TECH:
00181 assert(req->link);
00182 Com_DPrintf(DEBUG_CLIENT, "RS_RequirementsMet: ANDtech: %s / %i\n", req->id, ((technology_t*)req->link)->idx);
00183 if (!RS_IsResearched_ptr(req->link)) {
00184 Com_DPrintf(DEBUG_CLIENT, "RS_RequirementsMet: this tech not researched ----> %s \n", req->id);
00185 metAND = qfalse;
00186 }
00187 break;
00188 case RS_LINK_TECH_NOT:
00189 assert(req->link);
00190 Com_DPrintf(DEBUG_CLIENT, "RS_RequirementsMet: ANDtech NOT: %s / %i\n", req->id, ((technology_t*)req->link)->idx);
00191 if (RS_IsResearched_ptr(req->link)) {
00192 Com_DPrintf(DEBUG_CLIENT, "RS_RequirementsMet: this tech researched but it must not be ----> %s \n", req->id);
00193 metAND = qfalse;
00194 }
00195 break;
00196 case RS_LINK_ITEM:
00197 assert(req->link);
00198 assert(base);
00199
00200 Com_DPrintf(DEBUG_CLIENT, "RS_RequirementsMet: ANDitem: %s / %i\n", req->id, ((objDef_t*)req->link)->idx);
00201 if (B_ItemInBase(req->link, base) < req->amount)
00202 metAND = qfalse;
00203 break;
00204 case RS_LINK_ALIEN_DEAD:
00205 case RS_LINK_ALIEN:
00206 assert(req->link);
00207 assert(base);
00208 if (AL_GetAlienAmount(req->link, req->type, base) < req->amount)
00209 metAND = qfalse;
00210 break;
00211 case RS_LINK_ALIEN_GLOBAL:
00212 if (AL_CountAll() < req->amount)
00213 metAND = qfalse;
00214 break;
00215 case RS_LINK_UFO:
00216 assert(req->link);
00217 if (US_UFOsInStorage(req->link, NULL) < req->amount)
00218 metAND = qfalse;
00219 break;
00220 case RS_LINK_ANTIMATTER:
00221 if (B_AntimatterInBase(base) < req->amount)
00222 metAND = qfalse;
00223 break;
00224 default:
00225 break;
00226 }
00227
00228 if (!metAND)
00229 break;
00230 }
00231 }
00232
00233 if (requiredOR->numLinks)
00234 for (i = 0; i < requiredOR->numLinks; i++) {
00235 const requirement_t *req = &requiredOR->links[i];
00236 switch (req->type) {
00237 case RS_LINK_TECH:
00238 assert(req->link);
00239 Com_DPrintf(DEBUG_CLIENT, "RS_RequirementsMet: ORtech: %s / %i\n", req->id, ((technology_t*)req->link)->idx);
00240 if (RS_IsResearched_ptr(req->link))
00241 metOR = qtrue;
00242 break;
00243 case RS_LINK_TECH_NOT:
00244 assert(req->link);
00245 Com_DPrintf(DEBUG_CLIENT, "RS_RequirementsMet: ORtech: NOT %s / %i\n", req->id, ((technology_t*)req->link)->idx);
00246 if (!RS_IsResearched_ptr(req->link))
00247 metOR = qtrue;
00248 break;
00249 case RS_LINK_ITEM:
00250 assert(req->link);
00251 assert(base);
00252
00253 Com_DPrintf(DEBUG_CLIENT, "RS_RequirementsMet: ORitem: %s / %i\n", req->id, ((objDef_t*)req->link)->idx);
00254 if (B_ItemInBase(req->link, base) >= req->amount)
00255 metOR = qtrue;
00256 break;
00257 case RS_LINK_ALIEN:
00258 case RS_LINK_ALIEN_DEAD:
00259 assert(req->link);
00260 assert(base);
00261 if (AL_GetAlienAmount(req->link, req->type, base) >= req->amount)
00262 metOR = qtrue;
00263 break;
00264 case RS_LINK_ALIEN_GLOBAL:
00265 if (AL_CountAll() >= req->amount)
00266 metOR = qtrue;
00267 break;
00268 case RS_LINK_UFO:
00269 assert(req->link);
00270 if (US_UFOsInStorage(req->link, NULL) >= req->amount)
00271 metOR = qtrue;
00272 break;
00273 case RS_LINK_ANTIMATTER:
00274 if (B_AntimatterInBase(base) >= req->amount)
00275 metOR = qtrue;
00276 default:
00277 break;
00278 }
00279
00280 if (metOR)
00281 break;
00282 }
00283 Com_DPrintf(DEBUG_CLIENT, "met_AND is %i, met_OR is %i\n", metAND, metOR);
00284
00285 return (metAND || metOR);
00286 }
00287
00292 const char *RS_GetDescription (descriptions_t *desc)
00293 {
00294 int i;
00295
00296
00297
00298 if (desc->numDescriptions == 0)
00299 return desc->text[0];
00300
00301
00302 if (desc->usedDescription >= 0)
00303 return desc->text[desc->usedDescription];
00304
00305
00306
00307 for (i = 1; i < desc->numDescriptions; i++) {
00308 const technology_t *tech = RS_GetTechByID(desc->tech[i]);
00309 if (!tech)
00310 continue;
00311
00312 if (RS_IsResearched_ptr(tech)) {
00313 desc->usedDescription = i;
00314 return desc->text[i];
00315 }
00316 }
00317
00318 return desc->text[0];
00319 }
00320
00328 void RS_MarkCollected (technology_t* tech)
00329 {
00330 assert(tech);
00331
00332 if (tech->time == 0)
00333 tech->mailSent = MAILSENT_FINISHED;
00334
00335 if (tech->mailSent < MAILSENT_PROPOSAL) {
00336 if (tech->statusResearch < RS_FINISH) {
00337 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("New research proposal: %s\n"), _(tech->name));
00338 MSO_CheckAddNewMessage(NT_RESEARCH_PROPOSED, _("Unknown Technology found"), cp_messageBuffer, qfalse, MSG_RESEARCH_PROPOSAL, tech);
00339 }
00340 tech->mailSent = MAILSENT_PROPOSAL;
00341 }
00342
00343
00344 if (tech->preResearchedDate.day == 0) {
00345 tech->preResearchedDate = ccs.date;
00346 }
00347
00348 tech->statusCollected = qtrue;
00349 }
00350
00358 void RS_MarkResearchable (qboolean init, const base_t* base)
00359 {
00360 int i;
00361 const base_t *thisBase = base;
00362
00363
00364 for (i = 0; i < ccs.numTechnologies; i++) {
00365 technology_t *tech = RS_GetTechByIDX(i);
00366 tech->statusResearchable = qfalse;
00367 }
00368
00369 for (i = 0; i < ccs.numTechnologies; i++) {
00370 technology_t *tech = RS_GetTechByIDX(i);
00371 if (!tech->statusResearchable) {
00372
00373 if (tech->statusResearch != RS_FINISH) {
00374 Com_DPrintf(DEBUG_CLIENT, "RS_MarkResearchable: handling \"%s\".\n", tech->id);
00375
00376
00377 if (tech->base)
00378 base = tech->base;
00379 else
00380 base = thisBase;
00381
00382 assert(base);
00383
00384
00385 if (RS_RequirementsMet(&tech->requireAND, &tech->requireOR, base)) {
00386 Com_DPrintf(DEBUG_CLIENT, "RS_MarkResearchable: \"%s\" marked researchable. reason:requirements.\n", tech->id);
00387 if (init && tech->time == 0)
00388 tech->mailSent = MAILSENT_PROPOSAL;
00389 RS_MarkOneResearchable(tech);
00390 }
00391
00392
00393
00394
00395 if (tech->statusResearchable && tech->time == 0) {
00396 if (init)
00397 tech->mailSent = MAILSENT_FINISHED;
00398 RS_ResearchFinish(tech);
00399 Com_DPrintf(DEBUG_CLIENT, "RS_MarkResearchable: automatically researched \"%s\"\n", tech->id);
00400
00401 i = -1;
00402 }
00403 }
00404 }
00405 }
00406 Com_DPrintf(DEBUG_CLIENT, "RS_MarkResearchable: Done.\n");
00407 }
00408
00413 static void RS_AssignTechLinks (requirements_t *reqs)
00414 {
00415 int i;
00416 requirement_t *req;
00417
00418 for (i = 0; i < reqs->numLinks; i++) {
00419 req = &reqs->links[i];
00420 switch (req->type) {
00421 case RS_LINK_TECH:
00422 case RS_LINK_TECH_NOT:
00423
00424 req->link = RS_GetTechByID(req->id);
00425 if (!req->link)
00426 Com_Error(ERR_DROP, "RS_AssignTechLinks: Could not get tech definition for '%s'", req->id);
00427 break;
00428 case RS_LINK_ITEM:
00429
00430 req->link = INVSH_GetItemByID(req->id);
00431 if (!req->link)
00432 Com_Error(ERR_DROP, "RS_AssignTechLinks: Could not get item definition for '%s'", req->id);
00433 break;
00434 case RS_LINK_ALIEN:
00435 case RS_LINK_ALIEN_DEAD:
00436 req->link = Com_GetTeamDefinitionByID(req->id);
00437 if (!req->link)
00438 Com_Error(ERR_DROP, "RS_AssignTechLinks: Could not get alien type (alien or alien_dead) definition for '%s'", req->id);
00439 break;
00440 case RS_LINK_UFO:
00441 req->link = AIR_GetAircraft(req->id);
00442 break;
00443 default:
00444 break;
00445 }
00446 }
00447 }
00448
00449 static linkedList_t *redirectedTechs;
00450
00455 void RS_RequiredLinksAssign (void)
00456 {
00457 linkedList_t* ll = redirectedTechs;
00458 technology_t *redirectedTech;
00459 int i;
00460
00461 for (i = 0; i < ccs.numTechnologies; i++) {
00462 technology_t *tech = RS_GetTechByIDX(i);
00463 if (tech->requireAND.numLinks)
00464 RS_AssignTechLinks(&tech->requireAND);
00465 if (tech->requireOR.numLinks)
00466 RS_AssignTechLinks(&tech->requireOR);
00467 if (tech->requireForProduction.numLinks)
00468 RS_AssignTechLinks(&tech->requireForProduction);
00469 }
00470
00471
00472 while (ll) {
00473
00474 assert(ll);
00475 redirectedTech = (technology_t *) ll->data;
00476 ll = ll->next;
00477
00478 assert(redirectedTech);
00479
00480 assert(ll);
00481 redirectedTech->redirect = RS_GetTechByID((char*)ll->data);
00482 ll = ll->next;
00483 }
00484
00485
00486 LIST_Delete(&redirectedTechs);
00487 }
00488
00489 technology_t* RS_GetTechForItem (const objDef_t *item)
00490 {
00491 if (item == NULL)
00492 Com_Error(ERR_DROP, "RS_GetTechForItem: No item given");
00493 if (item->idx < 0 || item->idx > lengthof(ccs.objDefTechs))
00494 Com_Error(ERR_DROP, "RS_GetTechForItem: Buffer overflow");
00495 if (ccs.objDefTechs[item->idx] == NULL)
00496 Com_Error(ERR_DROP, "RS_GetTechForItem: No technology for item %s", item->id);
00497 return ccs.objDefTechs[item->idx];
00498 }
00499
00509 void RS_InitTree (qboolean load)
00510 {
00511 int i, j;
00512 technology_t *tech;
00513 byte found;
00514 objDef_t *od;
00515
00516
00517 for (i = 0, od = csi.ods; i < csi.numODs; i++, od++) {
00518 ccs.objDefTechs[od->idx] = RS_GetTechByProvided(od->id);
00519 if (!ccs.objDefTechs[od->idx])
00520 Com_Error(ERR_DROP, "RS_InitTree: Could not find a valid tech for item %s", od->id);
00521 }
00522
00523 for (i = 0, tech = ccs.technologies; i < ccs.numTechnologies; i++, tech++) {
00524 for (j = 0; j < tech->markResearched.numDefinitions; j++) {
00525 if (tech->markResearched.markOnly[j] && !strcmp(tech->markResearched.campaign[j], ccs.curCampaign->researched)) {
00526 Com_DPrintf(DEBUG_CLIENT, "...mark %s as researched\n", tech->id);
00527 RS_ResearchFinish(tech);
00528 break;
00529 }
00530 }
00531
00532
00533
00534 RS_AssignTechLinks(&tech->requireAND);
00535 RS_AssignTechLinks(&tech->requireOR);
00536
00537
00538 switch (tech->type) {
00539 case RS_CRAFTITEM:
00540 if (!tech->name)
00541 Com_DPrintf(DEBUG_CLIENT, "RS_InitTree: \"%s\" A type craftitem needs to have a 'name\txxx' defined.", tech->id);
00542 break;
00543 case RS_NEWS:
00544 if (!tech->name)
00545 Com_DPrintf(DEBUG_CLIENT, "RS_InitTree: \"%s\" A 'type news' item needs to have a 'name\txxx' defined.", tech->id);
00546 break;
00547 case RS_TECH:
00548 if (!tech->name)
00549 Com_DPrintf(DEBUG_CLIENT, "RS_InitTree: \"%s\" A 'type tech' item needs to have a 'name\txxx' defined.", tech->id);
00550 break;
00551 case RS_WEAPON:
00552 case RS_ARMOUR:
00553 found = qfalse;
00554 for (j = 0; j < csi.numODs; j++) {
00555 objDef_t *item = INVSH_GetItemByIDX(j);
00556
00557
00558 if (!strcmp(tech->provides, item->id)) {
00559 found = qtrue;
00560 if (!tech->name)
00561 tech->name = Mem_PoolStrDup(item->name, cp_campaignPool, 0);
00562 if (!tech->mdl)
00563 tech->mdl = Mem_PoolStrDup(item->model, cp_campaignPool, 0);
00564 if (!tech->image)
00565 tech->image = Mem_PoolStrDup(item->image, cp_campaignPool, 0);
00566 break;
00567 }
00568 }
00569
00570 if (!found) {
00571 tech->name = Mem_PoolStrDup(tech->id, cp_campaignPool, 0);
00572 Com_Printf("RS_InitTree: \"%s\" - Linked weapon or armour (provided=\"%s\") not found. Tech-id used as name.\n",
00573 tech->id, tech->provides);
00574 }
00575 break;
00576 case RS_BUILDING:
00577 found = qfalse;
00578 for (j = 0; j < ccs.numBuildingTemplates; j++) {
00579 building_t *building = &ccs.buildingTemplates[j];
00580
00581 if (!strcmp(tech->provides, building->id)) {
00582 found = qtrue;
00583 if (!tech->name)
00584 tech->name = Mem_PoolStrDup(building->name, cp_campaignPool, 0);
00585 if (!tech->image)
00586 tech->image = Mem_PoolStrDup(building->image, cp_campaignPool, 0);
00587 break;
00588 }
00589 }
00590 if (!found) {
00591 tech->name = Mem_PoolStrDup(tech->id, cp_campaignPool, 0);
00592 Com_DPrintf(DEBUG_CLIENT, "RS_InitTree: \"%s\" - Linked building (provided=\"%s\") not found. Tech-id used as name.\n",
00593 tech->id, tech->provides);
00594 }
00595 break;
00596 case RS_CRAFT:
00597 found = qfalse;
00598 for (j = 0; j < ccs.numAircraftTemplates; j++) {
00599 aircraft_t *aircraftTemplate = &ccs.aircraftTemplates[j];
00600
00601 if (!tech->provides)
00602 Com_Error(ERR_FATAL, "RS_InitTree: \"%s\" - No linked aircraft or craft-upgrade.\n", tech->id);
00603 if (!strcmp(tech->provides, aircraftTemplate->id)) {
00604 found = qtrue;
00605 if (!tech->name)
00606 tech->name = Mem_PoolStrDup(aircraftTemplate->name, cp_campaignPool, 0);
00607 if (!tech->mdl) {
00608 tech->mdl = Mem_PoolStrDup(aircraftTemplate->model, cp_campaignPool, 0);
00609 Com_DPrintf(DEBUG_CLIENT, "RS_InitTree: aircraft model \"%s\" \n", aircraftTemplate->model);
00610 }
00611 aircraftTemplate->tech = tech;
00612 break;
00613 }
00614 }
00615 if (!found)
00616 Com_Printf("RS_InitTree: \"%s\" - Linked aircraft or craft-upgrade (provided=\"%s\") not found.\n", tech->id, tech->provides);
00617 break;
00618 case RS_ALIEN:
00619
00620 break;
00621 case RS_UGV:
00623 break;
00624 case RS_LOGIC:
00625
00626 break;
00627 }
00628
00629
00630 if (!tech->name) {
00631 if (tech->type != RS_LOGIC)
00632 Com_Error(ERR_DROP, "RS_InitTree: \"%s\" - no name found!", tech->id);
00633 } else {
00634
00635
00636 for (j = 0; j < TECHMAIL_MAX; j++) {
00637
00638 if (!tech->mail[j].subject && tech->mail[j].to) {
00639 tech->mail[j].subject = tech->name;
00640 }
00641 }
00642 }
00643
00644 if (!tech->image && !tech->mdl)
00645 Com_DPrintf(DEBUG_CLIENT, "Tech %s of type %i has no image (%p) and no model (%p) assigned.\n",
00646 tech->id, tech->type, tech->image, tech->mdl);
00647 }
00648
00649 if (load) {
00650
00651
00652 for (j = 0; j < ccs.numBases; j++) {
00653 base_t *b = B_GetFoundedBaseByIDX(j);
00654 aircraft_t *aircraft;
00655 if (!b)
00656 continue;
00657
00658 aircraft = NULL;
00659 while ((aircraft = AIR_GetNextFromBase(b, aircraft)) != NULL) {
00660
00661 if (!aircraft->tech)
00662 aircraft->tech = RS_GetTechByProvided(aircraft->id);
00663 }
00664 }
00665 }
00666
00667 Com_DPrintf(DEBUG_CLIENT, "RS_InitTree: Technology tree initialised. %i entries found.\n", i);
00668 }
00669
00680 void RS_AssignScientist (technology_t* tech, base_t *base, employee_t *employee)
00681 {
00682
00683 assert(tech);
00684 Com_DPrintf(DEBUG_CLIENT, "RS_AssignScientist: %i | %s \n", tech->idx, tech->name);
00685
00686
00687 if (tech->base)
00688 base = tech->base;
00689
00690 assert(base);
00691
00692 if (!employee)
00693 employee = E_GetUnassignedEmployee(base, EMPL_SCIENTIST);
00694 if (!employee) {
00695
00696 Com_DPrintf(DEBUG_CLIENT, "No free scientists in this base (%s) to assign to tech '%s'\n", base->name, tech->id);
00697 return;
00698 }
00699 if (employee->type != EMPL_SCIENTIST) {
00700 Com_Error(ERR_DROP, "Trying to assign a non-scientist to research tech: %s, at base %s\n", tech->id, base->name);
00701 }
00702 if (employee->building) {
00704 Com_Printf("RS_AssignScientist: Scientist %i is already assigned\n", employee->idx);
00705 return;
00706 }
00707
00708 if (tech->statusResearchable) {
00709
00710 building_t *building = B_GetBuildingInBaseByType(base, B_LAB, qtrue);
00711 if (building) {
00712
00713 if (base->capacities[CAP_LABSPACE].max > base->capacities[CAP_LABSPACE].cur) {
00714 tech->scientists++;
00715 tech->base = base;
00716 base->capacities[CAP_LABSPACE].cur++;
00717
00718
00719 employee->building = building;
00720 } else {
00721 UI_Popup(_("Not enough laboratories"), _("No free space in laboratories left.\nBuild more laboratories.\n"));
00722 return;
00723 }
00724 } else {
00725 UI_Popup(_("Not enough laboratories"), _("No free space in laboratories left.\nBuild more laboratories.\n"));
00726 return;
00727 }
00728
00729 tech->statusResearch = RS_RUNNING;
00730 }
00731 }
00732
00741 void RS_RemoveScientist (technology_t* tech, employee_t *employee)
00742 {
00743 assert(tech);
00744
00745
00746 if (tech->scientists == 0) {
00747 assert(tech->base == NULL);
00748 assert(tech->statusResearch == RS_PAUSED);
00749 return;
00750 }
00751
00752 if (!employee)
00753 employee = E_GetAssignedEmployee(tech->base, EMPL_SCIENTIST);
00754 if (employee) {
00755
00756 tech->scientists--;
00757
00758 tech->base->capacities[CAP_LABSPACE].cur--;
00759
00760 employee->building = NULL;
00761 } else {
00762 Com_Error(ERR_DROP, "No assigned scientists found - serious inconsistency.");
00763 }
00764
00765 assert(tech->scientists >= 0);
00766
00767 if (tech->scientists == 0) {
00768
00769 tech->base = NULL;
00770 tech->statusResearch = RS_PAUSED;
00771 }
00772 }
00773
00782 void RS_RemoveFiredScientist (base_t *base, employee_t *employee)
00783 {
00784 technology_t *tech;
00785 employee_t *freeScientist = E_GetUnassignedEmployee(base, EMPL_SCIENTIST);
00786
00787 assert(base);
00788 assert(employee);
00789
00790
00791 tech = RS_GetTechWithMostScientists(base);
00792
00793
00794 assert(tech);
00795 RS_RemoveScientist(tech, employee);
00796
00797
00798 if (freeScientist)
00799 RS_AssignScientist(tech, base, freeScientist);
00800 }
00801
00809 static void RS_MarkResearched (technology_t *tech, const base_t *base)
00810 {
00811 RS_ResearchFinish(tech);
00812 Com_DPrintf(DEBUG_CLIENT, "Research of \"%s\" finished.\n", tech->id);
00813 RS_MarkResearchable(qfalse, base);
00814 }
00815
00821 qboolean RS_MarkStoryLineEventResearched (const char *techID)
00822 {
00823 technology_t* tech = RS_GetTechByID(techID);
00824 if (!RS_IsResearched_ptr(tech)) {
00825 int j;
00826 for (j = 0; j < ccs.numBases; j++) {
00827 const base_t *base = B_GetFoundedBaseByIDX(j);
00828 if (base) {
00829 RS_MarkResearched(tech, base);
00830 return qtrue;
00831 }
00832 }
00833 }
00834 return qfalse;
00835 }
00836
00837
00843 void RS_ResearchRun (void)
00844 {
00845 int i, newResearch = 0;
00846 base_t* checkBases[MAX_BASES];
00847
00848 memset(checkBases, 0, sizeof(checkBases));
00849
00850 for (i = 0; i < ccs.numTechnologies; i++) {
00851 technology_t *tech = RS_GetTechByIDX(i);
00852
00853 if (tech->statusResearch != RS_RUNNING)
00854 continue;
00855
00856 if (!RS_RequirementsMet(&tech->requireAND, &tech->requireOR, tech->base)) {
00857 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Research prerequisites of %s do not met at %s. Research halted!"), _(tech->name), tech->base->name);
00858 MSO_CheckAddNewMessage(NT_RESEARCH_HALTED, _("Research halted"), cp_messageBuffer, qfalse, MSG_RESEARCH_HALTED, NULL);
00859
00860 while (tech->scientists > 0)
00861 RS_RemoveScientist(tech, NULL);
00862 assert(tech->statusResearch == RS_PAUSED);
00863
00864 continue;
00865 }
00866
00867
00868 if (tech->time > 0 && tech->scientists > 0) {
00869 base_t *base = tech->base;
00870 assert(tech->base);
00871 if (RS_ResearchAllowed(base)) {
00873 tech->time -= tech->scientists * 0.8;
00874
00875 if (tech->time <= 0) {
00876 RS_MarkResearched(tech, base);
00877
00878 newResearch++;
00879
00880
00881 checkBases[base->idx] = base;
00882 tech->time = 0;
00883 }
00884 }
00885 }
00886 }
00887
00888
00891 for (i = 0; i < MAX_BASES; i++) {
00892 if (checkBases[i])
00893 RS_MarkResearchable(qfalse, checkBases[i]);
00894 }
00895 }
00896
00897 #ifdef DEBUG
00898
00899 static const char *RS_TechTypeToName (researchType_t type)
00900 {
00901 switch(type) {
00902 case RS_TECH:
00903 return "tech";
00904 case RS_WEAPON:
00905 return "weapon";
00906 case RS_ARMOUR:
00907 return "armour";
00908 case RS_CRAFT:
00909 return "craft";
00910 case RS_CRAFTITEM:
00911 return "craftitem";
00912 case RS_BUILDING:
00913 return "building";
00914 case RS_ALIEN:
00915 return "alien";
00916 case RS_UGV:
00917 return "ugv";
00918 case RS_NEWS:
00919 return "news";
00920 case RS_LOGIC:
00921 return "logic";
00922 default:
00923 return "unknown";
00924 }
00925 }
00926
00927 static const char *RS_TechReqToName (requirement_t *req)
00928 {
00929 assert(req->link);
00930 switch(req->type) {
00931 case RS_LINK_TECH:
00932 return ((technology_t*)req->link)->id;
00933 case RS_LINK_TECH_NOT:
00934 return va("not %s", ((technology_t*)req->link)->id);
00935 case RS_LINK_ITEM:
00936 return ((objDef_t*)req->link)->id;
00937 case RS_LINK_ALIEN:
00938 return ((teamDef_t*)req->link)->id;
00939 case RS_LINK_ALIEN_DEAD:
00940 return ((teamDef_t*)req->link)->id;
00941 case RS_LINK_ALIEN_GLOBAL:
00942 return "global alien count";
00943 case RS_LINK_UFO:
00944 return ((aircraft_t*)req->link)->id;
00945 case RS_LINK_ANTIMATTER:
00946 return "antimatter";
00947 default:
00948 return "unknown";
00949 }
00950 }
00951
00953 static const char *RS_TechLinkTypeToName (requirementType_t type)
00954 {
00955 switch(type) {
00956 case RS_LINK_TECH:
00957 return "tech";
00958 case RS_LINK_TECH_NOT:
00959 return "tech (not)";
00960 case RS_LINK_ITEM:
00961 return "item";
00962 case RS_LINK_ALIEN:
00963 return "alien";
00964 case RS_LINK_ALIEN_DEAD:
00965 return "alien_dead";
00966 case RS_LINK_ALIEN_GLOBAL:
00967 return "alienglobal";
00968 case RS_LINK_UFO:
00969 return "ufo";
00970 case RS_LINK_ANTIMATTER:
00971 return "antimatter";
00972 default:
00973 return "unknown";
00974 }
00975 }
00976
00981 static void RS_TechnologyList_f (void)
00982 {
00983 int i, j;
00984 technology_t *tech;
00985 requirements_t *reqs;
00986 dateLong_t date;
00987
00988 Com_Printf("#techs: %i\n", ccs.numTechnologies);
00989 for (i = 0; i < ccs.numTechnologies; i++) {
00990 tech = RS_GetTechByIDX(i);
00991 Com_Printf("Tech: %s\n", tech->id);
00992 Com_Printf("... time -> %.2f\n", tech->time);
00993 Com_Printf("... name -> %s\n", tech->name);
00994 reqs = &tech->requireAND;
00995 Com_Printf("... requires ALL ->");
00996 for (j = 0; j < reqs->numLinks; j++)
00997 Com_Printf(" %s (%s) %s", reqs->links[j].id, RS_TechLinkTypeToName(reqs->links[j].type), RS_TechReqToName(&reqs->links[j]));
00998 reqs = &tech->requireOR;
00999 Com_Printf("\n");
01000 Com_Printf("... requires ANY ->");
01001 for (j = 0; j < reqs->numLinks; j++)
01002 Com_Printf(" %s (%s) %s", reqs->links[j].id, RS_TechLinkTypeToName(reqs->links[j].type), RS_TechReqToName(&reqs->links[j]));
01003 Com_Printf("\n");
01004 Com_Printf("... provides -> %s", tech->provides);
01005 Com_Printf("\n");
01006
01007 Com_Printf("... type -> ");
01008 Com_Printf("%s\n", RS_TechTypeToName(tech->type));
01009
01010 Com_Printf("... researchable -> %i\n", tech->statusResearchable);
01011
01012 if (tech->statusResearchable) {
01013 CL_DateConvertLong(&tech->preResearchedDate, &date);
01014 Com_Printf("... researchable date: %02i %02i %i\n", date.day, date.month, date.year);
01015 }
01016
01017 Com_Printf("... research -> ");
01018 switch (tech->statusResearch) {
01019 case RS_NONE:
01020 Com_Printf("nothing\n");
01021 break;
01022 case RS_RUNNING:
01023 Com_Printf("running\n");
01024 break;
01025 case RS_PAUSED:
01026 Com_Printf("paused\n");
01027 break;
01028 case RS_FINISH:
01029 Com_Printf("done\n");
01030 CL_DateConvertLong(&tech->researchedDate, &date);
01031 Com_Printf("... research date: %02i %02i %i\n", date.day, date.month, date.year);
01032 break;
01033 default:
01034 Com_Printf("unknown\n");
01035 break;
01036 }
01037 }
01038 }
01039
01045 static void RS_DebugMarkResearchedAll (void)
01046 {
01047 int i;
01048 technology_t *tech;
01049
01050 for (i = 0; i < ccs.numTechnologies; i++) {
01051 tech = RS_GetTechByIDX(i);
01052 Com_DPrintf(DEBUG_CLIENT, "...mark %s as researched\n", tech->id);
01053 RS_MarkOneResearchable(tech);
01054 RS_ResearchFinish(tech);
01056 }
01057 }
01058
01063 static void RS_DebugResearchAll_f (void)
01064 {
01065 if (Cmd_Argc() != 2) {
01066 RS_DebugMarkResearchedAll();
01067 } else {
01068 technology_t *tech = RS_GetTechByID(Cmd_Argv(1));
01069 if (!tech)
01070 return;
01071 Com_DPrintf(DEBUG_CLIENT, "...mark %s as researched\n", tech->id);
01072 RS_MarkOneResearchable(tech);
01073 RS_ResearchFinish(tech);
01074 }
01075 }
01076
01081 static void RS_DebugResearchableAll_f (void)
01082 {
01083 int i;
01084
01085 if (Cmd_Argc() != 2) {
01086 for (i = 0; i < ccs.numTechnologies; i++) {
01087 technology_t *tech = RS_GetTechByIDX(i);
01088 Com_Printf("...mark %s as researchable\n", tech->id);
01089 RS_MarkOneResearchable(tech);
01090 RS_MarkCollected(tech);
01091 }
01092 } else {
01093 technology_t *tech = RS_GetTechByID(Cmd_Argv(1));
01094 if (tech) {
01095 Com_Printf("...mark %s as researchable\n", tech->id);
01096 RS_MarkOneResearchable(tech);
01097 RS_MarkCollected(tech);
01098 }
01099 }
01100 }
01101
01102 static void RS_DebugFinishResearches_f (void)
01103 {
01104 int i;
01105
01106 for (i = 0; i < ccs.numTechnologies; i++) {
01107 technology_t *tech = RS_GetTechByIDX(i);
01108 if (tech->statusResearch == RS_RUNNING) {
01109 assert(tech->base);
01110 Com_DPrintf(DEBUG_CLIENT, "...mark %s as researched\n", tech->id);
01111 RS_MarkResearched(tech, tech->base);
01112 }
01113 }
01114 }
01115 #endif
01116
01117
01123 void RS_InitStartup (void)
01124 {
01125
01126 #ifdef DEBUG
01127 Cmd_AddCommand("debug_listtech", RS_TechnologyList_f, "Print the current parsed technologies to the game console");
01128 Cmd_AddCommand("debug_researchall", RS_DebugResearchAll_f, "Mark all techs as researched");
01129 Cmd_AddCommand("debug_researchableall", RS_DebugResearchableAll_f, "Mark all techs as researchable");
01130 Cmd_AddCommand("debug_finishresearches", RS_DebugFinishResearches_f, "Mark all running researches as finished");
01131 #endif
01132 }
01133
01137 void RS_ResetTechs (void)
01138 {
01139
01140 memset(techHash, 0, sizeof(techHash));
01141 memset(techHashProvided, 0, sizeof(techHashProvided));
01142
01143
01144 LIST_Delete(&redirectedTechs);
01145 }
01146
01152 static const value_t valid_tech_vars[] = {
01153 {"name", V_TRANSLATION_STRING, offsetof(technology_t, name), 0},
01154 {"provides", V_CLIENT_HUNK_STRING, offsetof(technology_t, provides), 0},
01155 {"event", V_CLIENT_HUNK_STRING, offsetof(technology_t, finishedResearchEvent), 0},
01156 {"delay", V_INT, offsetof(technology_t, delay), MEMBER_SIZEOF(technology_t, delay)},
01157 {"producetime", V_INT, offsetof(technology_t, produceTime), MEMBER_SIZEOF(technology_t, produceTime)},
01158 {"time", V_FLOAT, offsetof(technology_t, time), MEMBER_SIZEOF(technology_t, time)},
01159 {"image", V_CLIENT_HUNK_STRING, offsetof(technology_t, image), 0},
01160 {"mdl", V_CLIENT_HUNK_STRING, offsetof(technology_t, mdl), 0},
01161
01162 {NULL, 0, 0, 0}
01163 };
01164
01168 static const value_t valid_techmail_vars[] = {
01169 {"from", V_TRANSLATION_STRING, offsetof(techMail_t, from), 0},
01170 {"to", V_TRANSLATION_STRING, offsetof(techMail_t, to), 0},
01171 {"subject", V_TRANSLATION_STRING, offsetof(techMail_t, subject), 0},
01172 {"date", V_TRANSLATION_STRING, offsetof(techMail_t, date), 0},
01173 {"icon", V_CLIENT_HUNK_STRING, offsetof(techMail_t, icon), 0},
01174 {"model", V_CLIENT_HUNK_STRING, offsetof(techMail_t, model), 0},
01175
01176 {NULL, 0, 0, 0}
01177 };
01178
01187 void RS_ParseTechnologies (const char *name, const char **text)
01188 {
01189 const value_t *vp;
01190 technology_t *tech;
01191 unsigned hash;
01192 const char *errhead = "RS_ParseTechnologies: unexpected end of file.";
01193 const char *token;
01194 requirements_t *requiredTemp;
01195 descriptions_t *descTemp;
01196 int i;
01197
01198 for (i = 0; i < ccs.numTechnologies; i++) {
01199 if (!strcmp(ccs.technologies[i].id, name)) {
01200 Com_Printf("RS_ParseTechnologies: Second tech with same name found (%s) - second ignored\n", name);
01201 return;
01202 }
01203 }
01204
01205 if (ccs.numTechnologies >= MAX_TECHNOLOGIES) {
01206 Com_Printf("RS_ParseTechnologies: too many technology entries. limit is %i.\n", MAX_TECHNOLOGIES);
01207 return;
01208 }
01209
01210
01211 token = Com_Parse(text);
01212 if (!*text || *token != '{') {
01213 Com_Printf("RS_ParseTechnologies: \"%s\" technology def without body ignored.\n", name);
01214 return;
01215 }
01216
01217
01218 tech = &ccs.technologies[ccs.numTechnologies];
01219 ccs.numTechnologies++;
01220
01221 memset(tech, 0, sizeof(*tech));
01222
01223
01224
01225
01226 tech->idx = ccs.numTechnologies - 1;
01227 tech->id = Mem_PoolStrDup(name, cp_campaignPool, 0);
01228 hash = Com_HashKey(tech->id, TECH_HASH_SIZE);
01229
01230
01231 tech->description.text[0] = _("No description available.");
01232 tech->preDescription.text[0] = _("No research proposal available.");
01233
01234 tech->description.usedDescription = -1;
01235 tech->preDescription.usedDescription = -1;
01236
01237
01238
01239 tech->hashNext = techHash[hash];
01240
01241
01242
01243
01244
01245 techHash[hash] = tech;
01246
01247 tech->type = RS_TECH;
01248 tech->statusResearch = RS_NONE;
01249 tech->statusResearchable = qfalse;
01250
01251 do {
01252
01253 token = Com_EParse(text, errhead, name);
01254 if (!*text)
01255 break;
01256 if (*token == '}')
01257 break;
01258
01259 if (!strcmp(token, "type")) {
01260
01261 token = Com_EParse(text, errhead, name);
01262 if (!*text)
01263 return;
01265
01266 if (!strcmp(token, "tech"))
01267 tech->type = RS_TECH;
01268 else if (!strcmp(token, "weapon"))
01269 tech->type = RS_WEAPON;
01270 else if (!strcmp(token, "news"))
01271 tech->type = RS_NEWS;
01272 else if (!strcmp(token, "armour"))
01273 tech->type = RS_ARMOUR;
01274 else if (!strcmp(token, "craft"))
01275 tech->type = RS_CRAFT;
01276 else if (!strcmp(token, "craftitem"))
01277 tech->type = RS_CRAFTITEM;
01278 else if (!strcmp(token, "building"))
01279 tech->type = RS_BUILDING;
01280 else if (!strcmp(token, "alien"))
01281 tech->type = RS_ALIEN;
01282 else if (!strcmp(token, "ugv"))
01283 tech->type = RS_UGV;
01284 else if (!strcmp(token, "logic"))
01285 tech->type = RS_LOGIC;
01286 else
01287 Com_Printf("RS_ParseTechnologies: \"%s\" unknown techtype: \"%s\" - ignored.\n", name, token);
01288 } else {
01289 if (!strcmp(token, "description") || !strcmp(token, "pre_description")) {
01290
01291
01292
01293 if (!strcmp(token, "pre_description")) {
01294 descTemp = &tech->preDescription;
01295 } else {
01296 descTemp = &tech->description;
01297 }
01298
01299 token = Com_EParse(text, errhead, name);
01300 if (!*text)
01301 break;
01302 if (*token != '{')
01303 break;
01304 if (*token == '}')
01305 break;
01306
01307 do {
01308 token = Com_EParse(text, errhead, name);
01309 if (!*text)
01310 return;
01311 if (*token == '}')
01312 break;
01313
01314 if (descTemp->numDescriptions < MAX_DESCRIPTIONS) {
01315
01316 descTemp->tech[descTemp->numDescriptions] = Mem_PoolStrDup(token, cp_campaignPool, 0);
01317
01318
01319 token = Com_EParse(text, errhead, name);
01320
01321 if (*token == '_')
01322 token++;
01323 else
01324 Com_Error(ERR_DROP, "RS_ParseTechnologies: '%s' No gettext string for description '%s'. Abort.\n", name, descTemp->tech[descTemp->numDescriptions]);
01325
01326 descTemp->text[descTemp->numDescriptions] = Mem_PoolStrDup(token, cp_campaignPool, 0);
01327 descTemp->numDescriptions++;
01328 }
01329 } while (*text);
01330
01331 } else if (!strcmp(token, "redirect")) {
01332 token = Com_EParse(text, errhead, name);
01333
01334 LIST_AddPointer(&redirectedTechs, tech);
01335 LIST_AddString(&redirectedTechs, token);
01336 } else if (!strcmp(token, "require_AND") || !strcmp(token, "require_OR") || !strcmp(token, "require_for_production")) {
01337
01338 if (!strcmp(token, "require_AND")) {
01339 requiredTemp = &tech->requireAND;
01340 } else if (!strcmp(token, "require_OR")) {
01341 requiredTemp = &tech->requireOR;
01342 } else {
01343 requiredTemp = &tech->requireForProduction;
01344 }
01345
01346 token = Com_EParse(text, errhead, name);
01347 if (!*text)
01348 break;
01349 if (*token != '{')
01350 break;
01351 if (*token == '}')
01352 break;
01353
01354 do {
01355 token = Com_EParse(text, errhead, name);
01356 if (!*text)
01357 return;
01358 if (*token == '}')
01359 break;
01360
01361 if (!strcmp(token, "tech") || !strcmp(token, "tech_not")) {
01362 if (requiredTemp->numLinks < MAX_TECHLINKS) {
01363
01364 if (!strcmp(token, "tech_not"))
01365 requiredTemp->links[requiredTemp->numLinks].type = RS_LINK_TECH_NOT;
01366 else
01367 requiredTemp->links[requiredTemp->numLinks].type = RS_LINK_TECH;
01368
01369
01370 token = Com_Parse(text);
01371 requiredTemp->links[requiredTemp->numLinks].id = Mem_PoolStrDup(token, cp_campaignPool, 0);
01372
01373 Com_DPrintf(DEBUG_CLIENT, "RS_ParseTechnologies: require-tech ('tech' or 'tech_not')- %s\n", requiredTemp->links[requiredTemp->numLinks].id);
01374
01375 requiredTemp->numLinks++;
01376 } else {
01377 Com_Printf("RS_ParseTechnologies: \"%s\" Too many 'required' defined. Limit is %i - ignored.\n", name, MAX_TECHLINKS);
01378 }
01379 } else if (!strcmp(token, "item")) {
01380
01381 if (requiredTemp->numLinks < MAX_TECHLINKS) {
01382
01383 requiredTemp->links[requiredTemp->numLinks].type = RS_LINK_ITEM;
01384
01385 token = Com_Parse(text);
01386 requiredTemp->links[requiredTemp->numLinks].id = Mem_PoolStrDup(token, cp_campaignPool, 0);
01387
01388 token = Com_Parse(text);
01389 requiredTemp->links[requiredTemp->numLinks].amount = atoi(token);
01390 Com_DPrintf(DEBUG_CLIENT, "RS_ParseTechnologies: require-item - %s - %i\n", requiredTemp->links[requiredTemp->numLinks].id, requiredTemp->links[requiredTemp->numLinks].amount);
01391 requiredTemp->numLinks++;
01392 } else {
01393 Com_Printf("RS_ParseTechnologies: \"%s\" Too many 'required' defined. Limit is %i - ignored.\n", name, MAX_TECHLINKS);
01394 }
01395 } else if (!strcmp(token, "alienglobal")) {
01396 if (requiredTemp->numLinks < MAX_TECHLINKS) {
01397
01398 requiredTemp->links[requiredTemp->numLinks].type = RS_LINK_ALIEN_GLOBAL;
01399 Com_DPrintf(DEBUG_CLIENT, "RS_ParseTechnologies: require-alienglobal - %i\n", requiredTemp->links[requiredTemp->numLinks].amount);
01400
01401
01402 token = Com_Parse(text);
01403 requiredTemp->links[requiredTemp->numLinks].amount = atoi(token);
01404 requiredTemp->numLinks++;
01405 } else {
01406 Com_Printf("RS_ParseTechnologies: \"%s\" Too many 'required' defined. Limit is %i - ignored.\n", name, MAX_TECHLINKS);
01407 }
01408 } else if (!strcmp(token, "alien_dead") || !strcmp(token, "alien")) {
01409
01410 if (requiredTemp->numLinks < MAX_TECHLINKS) {
01411
01412 if (!strcmp(token, "alien_dead")) {
01413 requiredTemp->links[requiredTemp->numLinks].type = RS_LINK_ALIEN_DEAD;
01414 Com_DPrintf(DEBUG_CLIENT, "RS_ParseTechnologies: require-alien dead - %s - %i\n", requiredTemp->links[requiredTemp->numLinks].id, requiredTemp->links[requiredTemp->numLinks].amount);
01415 } else {
01416 requiredTemp->links[requiredTemp->numLinks].type = RS_LINK_ALIEN;
01417 Com_DPrintf(DEBUG_CLIENT, "RS_ParseTechnologies: require-alien alive - %s - %i\n", requiredTemp->links[requiredTemp->numLinks].id, requiredTemp->links[requiredTemp->numLinks].amount);
01418 }
01419
01420 token = Com_Parse(text);
01421 requiredTemp->links[requiredTemp->numLinks].id = Mem_PoolStrDup(token, cp_campaignPool, 0);
01422
01423 token = Com_Parse(text);
01424 requiredTemp->links[requiredTemp->numLinks].amount = atoi(token);
01425 requiredTemp->numLinks++;
01426 } else {
01427 Com_Printf("RS_ParseTechnologies: \"%s\" Too many 'required' defined. Limit is %i - ignored.\n", name, MAX_TECHLINKS);
01428 }
01429 } else if (!strcmp(token, "ufo")) {
01430
01431 if (requiredTemp->numLinks < MAX_TECHLINKS) {
01432
01433 requiredTemp->links[requiredTemp->numLinks].type = RS_LINK_UFO;
01434
01435 token = Com_Parse(text);
01436 requiredTemp->links[requiredTemp->numLinks].id = Mem_PoolStrDup(token, cp_campaignPool, 0);
01437
01438 token = Com_Parse(text);
01439 requiredTemp->links[requiredTemp->numLinks].amount = atoi(token);
01440 Com_DPrintf(DEBUG_CLIENT, "RS_ParseTechnologies: require-ufo - %s - %i\n", requiredTemp->links[requiredTemp->numLinks].id, requiredTemp->links[requiredTemp->numLinks].amount);
01441 requiredTemp->numLinks++;
01442 }
01443 } else if (!strcmp(token, "antimatter")) {
01444
01445 if (requiredTemp->numLinks < MAX_TECHLINKS) {
01446
01447 requiredTemp->links[requiredTemp->numLinks].type = RS_LINK_ANTIMATTER;
01448
01449 token = Com_Parse(text);
01450 requiredTemp->links[requiredTemp->numLinks].amount = atoi(token);
01451 Com_DPrintf(DEBUG_CLIENT, "RS_ParseTechnologies: require-antimatter - %i\n", requiredTemp->links[requiredTemp->numLinks].amount);
01452 requiredTemp->numLinks++;
01453 }
01454 } else {
01455 Com_Printf("RS_ParseTechnologies: \"%s\" unknown requirement-type: \"%s\" - ignored.\n", name, token);
01456 }
01457 } while (*text);
01458 } else if (!strcmp(token, "up_chapter")) {
01459
01460 token = Com_EParse(text, errhead, name);
01461 if (!*text)
01462 return;
01463
01464 if (*token) {
01465
01466 for (i = 0; i < ccs.numChapters; i++) {
01467 if (!strcmp(token, ccs.upChapters[i].id)) {
01468
01469 tech->upChapter = &ccs.upChapters[i];
01470 if (!ccs.upChapters[i].first) {
01471 ccs.upChapters[i].first = tech;
01472 ccs.upChapters[i].last = tech;
01473 tech->upPrev = NULL;
01474 tech->upNext = NULL;
01475 } else {
01476
01477 technology_t *techOld = ccs.upChapters[i].last;
01478 ccs.upChapters[i].last = tech;
01479 techOld->upNext = tech;
01480 ccs.upChapters[i].last->upPrev = techOld;
01481 ccs.upChapters[i].last->upNext = NULL;
01482 }
01483 break;
01484 }
01485 if (i == ccs.numChapters)
01486 Com_Printf("RS_ParseTechnologies: \"%s\" - chapter \"%s\" not found.\n", name, token);
01487 }
01488 }
01489 } else if (!strcmp(token, "mail") || !strcmp(token, "mail_pre")) {
01490 techMail_t* mail;
01491
01492
01493
01494 tech->numTechMails++;
01495
01496 if (tech->numTechMails > TECHMAIL_MAX)
01497 Com_Printf("RS_ParseTechnologies: more techmail-entries found than supported. \"%s\"\n", name);
01498
01499 if (!strcmp(token, "mail_pre")) {
01500 mail = &tech->mail[TECHMAIL_PRE];
01501 } else {
01502 mail = &tech->mail[TECHMAIL_RESEARCHED];
01503 }
01504 token = Com_EParse(text, errhead, name);
01505 if (!*text || *token != '{')
01506 return;
01507
01508
01509 token = Com_EParse(text, errhead, name);
01510 if (!*text || *token == '}')
01511 return;
01512 do {
01513 for (vp = valid_techmail_vars; vp->string; vp++)
01514 if (!strcmp(token, vp->string)) {
01515
01516 token = Com_EParse(text, errhead, name);
01517 if (!*text)
01518 return;
01519
01520 switch (vp->type) {
01521 case V_TRANSLATION_STRING:
01522 token++;
01523 case V_CLIENT_HUNK_STRING:
01524 Mem_PoolStrDupTo(token, (char**) ((char*)mail + (int)vp->ofs), cp_campaignPool, 0);
01525 break;
01526 case V_NULL:
01527 Com_Printf("RS_ParseTechnologies Error: - no buffer for technologies - V_NULL not allowed (token: '%s') entry: '%s'\n", token, name);
01528 break;
01529 default:
01530 Com_EParseValue(mail, token, vp->type, vp->ofs, vp->size);
01531 }
01532 break;
01533 }
01534
01535 token = Com_EParse(text, errhead, name);
01536 if (!*text)
01537 return;
01538 } while (*text && *token != '}');
01539
01540 if (mail->model == NULL)
01541 mail->model = "characters/navarre";
01542 } else {
01543 for (vp = valid_tech_vars; vp->string; vp++)
01544 if (!strcmp(token, vp->string)) {
01545
01546 token = Com_EParse(text, errhead, name);
01547 if (!*text)
01548 return;
01549
01550 if (!vp->ofs)
01551 break;
01552 switch (vp->type) {
01553 case V_TRANSLATION_STRING:
01554 if (*token == '_')
01555 token++;
01556 case V_CLIENT_HUNK_STRING:
01557 Mem_PoolStrDupTo(token, (char**) ((char*)tech + (int)vp->ofs), cp_campaignPool, 0);
01558 break;
01559 case V_NULL:
01560 Com_Printf("RS_ParseTechnologies Error: - no buffer for technologies - V_NULL not allowed (token: '%s') entry: '%s'\n", token, name);
01561 break;
01562 default:
01563 Com_EParseValue(tech, token, vp->type, vp->ofs, vp->size);
01564 }
01565 break;
01566 }
01568 if (!vp->string)
01569 Com_Printf("RS_ParseTechnologies: unknown token \"%s\" ignored (entry %s)\n", token, name);
01570 }
01571 }
01572 } while (*text);
01573
01574 if (tech->provides) {
01575 hash = Com_HashKey(tech->provides, TECH_HASH_SIZE);
01576
01577
01578 tech->hashProvidedNext = techHashProvided[hash];
01579
01580
01581
01582
01583
01584 techHashProvided[hash] = tech;
01585 } else {
01586 Com_DPrintf(DEBUG_CLIENT, "tech '%s' doesn't have a provides string\n", tech->id);
01587 }
01588
01589
01590 tech->overallTime = tech->time;
01591 }
01592
01593 static inline qboolean RS_IsValidTechIndex (int techIdx)
01594 {
01595 if (techIdx == TECH_INVALID)
01596 return qfalse;
01597 if (techIdx < 0 || techIdx >= ccs.numTechnologies)
01598 return qfalse;
01599 if (techIdx >= MAX_TECHNOLOGIES)
01600 return qfalse;
01601
01602 return qtrue;
01603 }
01604
01611 qboolean RS_IsResearched_idx (int techIdx)
01612 {
01613 if (!RS_IsValidTechIndex(techIdx))
01614 return qfalse;
01615
01616 if (ccs.technologies[techIdx].statusResearch == RS_FINISH)
01617 return qtrue;
01618
01619 return qfalse;
01620 }
01621
01627 qboolean RS_IsResearched_ptr (const technology_t * tech)
01628 {
01629 if (tech && tech->statusResearch == RS_FINISH)
01630 return qtrue;
01631 return qfalse;
01632 }
01633
01638 int RS_Collected_ (const technology_t * tech)
01639 {
01640 if (tech)
01641 return tech->statusCollected;
01642
01643 Com_DPrintf(DEBUG_CLIENT, "RS_Collected_: NULL technology given.\n");
01644 return -1;
01645 }
01646
01653 technology_t* RS_GetTechByIDX (int techIdx)
01654 {
01655 if (!RS_IsValidTechIndex(techIdx))
01656 return NULL;
01657 else
01658 return &ccs.technologies[techIdx];
01659 }
01660
01661
01667 technology_t *RS_GetTechByID (const char *id)
01668 {
01669 unsigned hash;
01670 technology_t *tech;
01671
01672 if (!id || id[0] == '\0')
01673 return NULL;
01674
01675 hash = Com_HashKey(id, TECH_HASH_SIZE);
01676 for (tech = techHash[hash]; tech; tech = tech->hashNext)
01677 if (!Q_strcasecmp(id, tech->id))
01678 return tech;
01679
01680 Com_Printf("RS_GetTechByID: Could not find a technology with id \"%s\"\n", id);
01681 return NULL;
01682 }
01683
01689 technology_t *RS_GetTechByProvided (const char *idProvided)
01690 {
01691 unsigned hash;
01692 technology_t *tech;
01693
01694 if (!idProvided)
01695 return NULL;
01696
01697 if (idProvided[0] == '\0')
01698 return NULL;
01699
01700 hash = Com_HashKey(idProvided, TECH_HASH_SIZE);
01701 for (tech = techHashProvided[hash]; tech; tech = tech->hashProvidedNext)
01702 if (!Q_strcasecmp(idProvided, tech->provides))
01703 return tech;
01704
01705 Com_DPrintf(DEBUG_CLIENT, "RS_GetTechByProvided: %s\n", idProvided);
01706
01707
01708 return NULL;
01709 }
01710
01716 technology_t *RS_GetTechWithMostScientists (const struct base_s *base)
01717 {
01718 technology_t *tech;
01719 int i, max;
01720
01721 if (!base)
01722 return NULL;
01723
01724 tech = NULL;
01725 max = 0;
01726 for (i = 0; i < ccs.numTechnologies; i++) {
01727 technology_t *tech_temp = RS_GetTechByIDX(i);
01728 if (tech_temp->statusResearch == RS_RUNNING && tech_temp->base == base) {
01729 if (tech_temp->scientists > max) {
01730 tech = tech_temp;
01731 max = tech->scientists;
01732 }
01733 }
01734 }
01735
01736
01737 return tech;
01738 }
01739
01744 int RS_GetTechIdxByName (const char *name)
01745 {
01746 technology_t *tech;
01747 const unsigned hash = Com_HashKey(name, TECH_HASH_SIZE);
01748
01749 for (tech = techHash[hash]; tech; tech = tech->hashNext)
01750 if (!Q_strcasecmp(name, tech->id))
01751 return tech->idx;
01752
01753 Com_Printf("RS_GetTechIdxByName: Could not find tech '%s'\n", name);
01754 return TECH_INVALID;
01755 }
01756
01763 int RS_CountScientistsInBase (const base_t *base)
01764 {
01765 int i, counter = 0;
01766
01767 for (i = 0; i < ccs.numTechnologies; i++) {
01768 const technology_t *tech = &ccs.technologies[i];
01769 if (tech->base == base) {
01770
01771 counter += tech->scientists;
01772 }
01773 }
01774
01775 return counter;
01776 }
01777
01782 void RS_RemoveScientistsExceedingCapacity (base_t *base)
01783 {
01784 assert(base);
01785
01786
01787 base->capacities[CAP_LABSPACE].cur = RS_CountScientistsInBase(base);
01788
01789 while (base->capacities[CAP_LABSPACE].cur > base->capacities[CAP_LABSPACE].max) {
01790 technology_t *tech = RS_GetTechWithMostScientists(base);
01791 RS_RemoveScientist(tech, NULL);
01792 }
01793 }
01794
01800 qboolean RS_SaveXML (mxml_node_t *parent)
01801 {
01802 int i;
01803 mxml_node_t *node;
01804
01805 Com_RegisterConstList(saveResearchConstants);
01806 node = mxml_AddNode(parent, SAVE_RESEARCH_RESEARCH);
01807 for (i = 0; i < ccs.numTechnologies; i++) {
01808 int j;
01809 const technology_t *t = RS_GetTechByIDX(i);
01810
01811 mxml_node_t * snode = mxml_AddNode(node, SAVE_RESEARCH_TECH);
01812 mxml_AddString(snode, SAVE_RESEARCH_ID, t->id);
01813 mxml_AddBoolValue(snode, SAVE_RESEARCH_STATUSCOLLECTED, t->statusCollected);
01814 mxml_AddFloatValue(snode, SAVE_RESEARCH_TIME, t->time);
01815 mxml_AddString(snode, SAVE_RESEARCH_STATUSRESEARCH, Com_GetConstVariable(SAVE_RESEARCHSTATUS_NAMESPACE, t->statusResearch));
01816 if (t->base)
01817 mxml_AddInt(snode, SAVE_RESEARCH_BASE, t->base->idx);
01818 mxml_AddIntValue(snode, SAVE_RESEARCH_SCIENTISTS, t->scientists);
01819 mxml_AddInt(snode, SAVE_RESEARCH_STATUSRESEARCHABLE, t->statusResearchable);
01820 mxml_AddDate(snode, SAVE_RESEARCH_PREDATE, t->preResearchedDate.day, t->preResearchedDate.sec);
01821 mxml_AddDate(snode, SAVE_RESEARCH_DATE, t->researchedDate.day, t->researchedDate.sec);
01822 mxml_AddInt(snode, SAVE_RESEARCH_MAILSENT, t->mailSent);
01823
01824
01826 for (j = 0; j < TECHMAIL_MAX; j++) {
01827 if (t->mail[j].read) {
01828 mxml_node_t * ssnode = mxml_AddNode(snode, SAVE_RESEARCH_MAIL);
01829 mxml_AddInt(ssnode, SAVE_RESEARCH_MAIL_ID, j);
01830 }
01831 }
01832 }
01833 Com_UnregisterConstList(saveResearchConstants);
01834
01835 return qtrue;
01836 }
01837
01843 qboolean RS_LoadXML (mxml_node_t *parent)
01844 {
01845 mxml_node_t *topnode;
01846 mxml_node_t *snode;
01847 qboolean success = qtrue;
01848
01849 topnode = mxml_GetNode(parent, SAVE_RESEARCH_RESEARCH);
01850 if (!topnode)
01851 return qfalse;
01852
01853 Com_RegisterConstList(saveResearchConstants);
01854 for (snode = mxml_GetNode(topnode, SAVE_RESEARCH_TECH); snode; snode = mxml_GetNextNode(snode, topnode, "tech")) {
01855 const char *techString = mxml_GetString(snode, SAVE_RESEARCH_ID);
01856 mxml_node_t * ssnode;
01857 int baseIdx;
01858 technology_t *t = RS_GetTechByID(techString);
01859 const char *type = mxml_GetString(snode, SAVE_RESEARCH_STATUSRESEARCH);
01860
01861 if (!t) {
01862 Com_Printf("......your game doesn't know anything about tech '%s'\n", techString);
01863 continue;
01864 }
01865
01866 if (!Com_GetConstIntFromNamespace(SAVE_RESEARCHSTATUS_NAMESPACE, type, (int*) &t->statusResearch)) {
01867 Com_Printf("Invaild research status '%s'\n", type);
01868 success = qfalse;
01869 break;
01870 }
01871
01872 t->statusCollected = mxml_GetBool(snode, SAVE_RESEARCH_STATUSCOLLECTED, qfalse);
01873 t->time = mxml_GetFloat(snode, SAVE_RESEARCH_TIME, 0.0);
01874
01875 baseIdx = mxml_GetInt(snode, SAVE_RESEARCH_BASE, -1);
01876 if (baseIdx >= 0)
01877
01878 t->base = B_GetBaseByIDX(baseIdx);
01879 t->scientists = mxml_GetInt(snode, SAVE_RESEARCH_SCIENTISTS, 0);
01880 t->statusResearchable = mxml_GetInt(snode, SAVE_RESEARCH_STATUSRESEARCHABLE, 0);
01881 mxml_GetDate(snode, SAVE_RESEARCH_PREDATE, &t->preResearchedDate.day, &t->preResearchedDate.sec);
01882 mxml_GetDate(snode, SAVE_RESEARCH_DATE, &t->researchedDate.day, &t->researchedDate.sec);
01883 t->mailSent = mxml_GetInt(snode, SAVE_RESEARCH_MAILSENT, 0);
01884
01885
01887 for (ssnode = mxml_GetNode(snode, SAVE_RESEARCH_MAIL); ssnode; ssnode = mxml_GetNextNode(ssnode, snode, SAVE_RESEARCH_MAIL)) {
01888 const int j= mxml_GetInt(ssnode, SAVE_RESEARCH_MAIL_ID, TECHMAIL_MAX);
01889 if (j < TECHMAIL_MAX) {
01890 const techMailType_t mailType = j;
01891 t->mail[mailType].read = qtrue;
01892 } else
01893 Com_Printf("......your save game contains unknown techmail ids... \n");
01894 }
01895
01896 #ifdef DEBUG
01897 if (t->statusResearch == RS_RUNNING && t->scientists > 0) {
01898 if (!t->base) {
01899 Com_Printf("No base but research is running and scientists are assigned");
01900 success = qfalse;
01901 break;
01902 }
01903 }
01904 #endif
01905 }
01906 Com_UnregisterConstList(saveResearchConstants);
01907
01908 return success;
01909 }
01910
01916 qboolean RS_ResearchAllowed (const base_t* base)
01917 {
01918 assert(base);
01919 if (base->baseStatus != BASE_UNDER_ATTACK && B_GetBuildingStatus(base, B_LAB)
01920 && E_CountHired(base, EMPL_SCIENTIST) > 0) {
01921 return qtrue;
01922 } else {
01923 return qfalse;
01924 }
01925 }
01926
01931 qboolean RS_ScriptSanityCheck (void)
01932 {
01933 int i, error = 0;
01934 technology_t *t;
01935
01936 for (i = 0, t = ccs.technologies; i < ccs.numTechnologies; i++, t++) {
01937 if (!t->name) {
01938 error++;
01939 Com_Printf("...... technology '%s' has no name\n", t->id);
01940 }
01941 if (!t->provides) {
01942 switch (t->type) {
01943 case RS_TECH:
01944 case RS_NEWS:
01945 case RS_LOGIC:
01946 case RS_ALIEN:
01947 break;
01948 default:
01949 error++;
01950 Com_Printf("...... technology '%s' doesn't provide anything\n", t->id);
01951 }
01952 }
01953
01954 if (t->produceTime == 0) {
01955 switch (t->type) {
01956 case RS_TECH:
01957 case RS_NEWS:
01958 case RS_LOGIC:
01959 case RS_BUILDING:
01960 case RS_ALIEN:
01961 break;
01962 default:
01964 Com_Printf("...... technology '%s' has zero (0) produceTime, is this on purpose?\n", t->id);
01965 }
01966 }
01967
01968 if (t->type != RS_LOGIC && (!t->description.text[0] || t->description.text[0][0] == '_')) {
01969 if (!t->description.text[0])
01970 Com_Printf("...... technology '%s' has a strange 'description' value '%s'.\n", t->id, t->description.text[0]);
01971 else
01972 Com_Printf("...... technology '%s' has no 'description' value.\n", t->id);
01973 }
01974 }
01975
01976 if (!error)
01977 return qtrue;
01978 else
01979 return qfalse;
01980 }