1 /**
2 * @file
3 * @brief Menu related callback functions used for production.
4 */
5
6 /*
7 Copyright (C) 2002-2013 UFO: Alien Invasion.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18 See the GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 */
25
26 #include "../../cl_shared.h"
27 #include "../../cl_inventory.h"
28 #include "../../ui/ui_dataids.h"
29 #include "cp_campaign.h"
30 #include "cp_market.h"
31 #include "cp_ufo.h"
32 #include "cp_popup.h"
33 #include "cp_produce.h"
34 #include "cp_produce_callbacks.h"
35
36 /**
37 * Holds the current active production category/filter type.
38 * @sa itemFilterTypes_t
39 */
40 static itemFilterTypes_t produceCategory = FILTER_S_PRIMARY;
41
42 /** Holds the current active selected queue entry. */
43 static production_t* selectedProduction = nullptr;
44
45 /** A list if all producable items. */
46 static linkedList_t* productionItemList;
47
48 /** Currently selected entry in the productionItemList (depends on content) */
49 static productionData_t selectedData;
50
51 /** @brief Number of blank lines between queued items and tech list. */
52 static const int QUEUE_SPACERS = 2;
53
54 /**
55 * @brief Resets the selected item data structure. Does not reset the selected production
56 * @sa PR_ClearSelected
57 */
PR_ClearSelectedItems(void)58 static void PR_ClearSelectedItems (void)
59 {
60 OBJZERO(selectedData);
61 selectedData.type = PRODUCTION_TYPE_MAX;
62 }
63
64 /**
65 * @brief Resets all "selected" pointers to nullptr.
66 * @sa PR_ClearSelectedItems
67 */
PR_ClearSelected(void)68 static void PR_ClearSelected (void)
69 {
70 PR_ClearSelectedItems();
71 selectedProduction = nullptr;
72 }
73
74 /**
75 * @brief update the list of queued and available items
76 * @param[in] base Pointer to the base.
77 */
PR_UpdateProductionList(const base_t * base)78 static void PR_UpdateProductionList (const base_t* base)
79 {
80 int i;
81 linkedList_t* productionList = nullptr;
82 linkedList_t* productionQueued = nullptr;
83 linkedList_t* productionAmount = nullptr;
84 const production_queue_t* queue;
85
86 assert(base);
87
88 queue = PR_GetProductionForBase(base);
89
90 /* First add all the queue items ... */
91 for (i = 0; i < queue->numItems; i++) {
92 const production_t* prod = &queue->items[i];
93 if (PR_IsItem(prod)) {
94 const objDef_t* od = prod->data.data.item;
95 cgi->LIST_AddString(&productionList, va("%s", _(od->name)));
96 cgi->LIST_AddString(&productionAmount, va("%i", B_ItemInBase(prod->data.data.item, base)));
97 cgi->LIST_AddString(&productionQueued, va("%i", prod->amount));
98 } else if (PR_IsAircraft(prod)) {
99 const aircraft_t* aircraftTemplate = prod->data.data.aircraft;
100
101 cgi->LIST_AddString(&productionList, va("%s", _(aircraftTemplate->name)));
102 cgi->LIST_AddString(&productionAmount, va("%i", AIR_CountInBaseByTemplate(base, aircraftTemplate)));
103 cgi->LIST_AddString(&productionQueued, va("%i", prod->amount));
104 } else if (PR_IsDisassembly(prod)) {
105 const storedUFO_t* ufo = prod->data.data.ufo;
106
107 cgi->LIST_AddString(&productionList, va("%s (%.0f%%)", UFO_TypeToName(ufo->ufoTemplate->ufotype), ufo->condition * 100));
108 cgi->LIST_AddString(&productionAmount, va("%i", US_UFOsInStorage(ufo->ufoTemplate, ufo->installation)));
109 cgi->LIST_AddString(&productionQueued, "1");
110 }
111 }
112
113 /* Then spacers ... */
114 for (i = 0; i < QUEUE_SPACERS; i++) {
115 cgi->LIST_AddString(&productionList, "");
116 cgi->LIST_AddString(&productionAmount, "");
117 cgi->LIST_AddString(&productionQueued, "");
118 }
119
120 cgi->LIST_Delete(&productionItemList);
121
122 /* Then go through all object definitions ... */
123 if (produceCategory == FILTER_DISASSEMBLY) {
124 /** UFOs at UFO stores */
125 US_Foreach(ufo) {
126 /* UFO is being transported */
127 if (ufo->status != SUFO_STORED)
128 continue;
129 /* UFO not researched */
130 if (!RS_IsResearched_ptr(ufo->ufoTemplate->tech))
131 continue;
132 /* The UFO is being disassembled already */
133 if (ufo->disassembly)
134 continue;
135
136 cgi->LIST_AddPointer(&productionItemList, ufo);
137 cgi->LIST_AddString(&productionList, va("%s (%.0f%%)", UFO_TypeToName(ufo->ufoTemplate->ufotype), ufo->condition * 100));
138 cgi->LIST_AddString(&productionAmount, va("%i", US_UFOsInStorage(ufo->ufoTemplate, ufo->installation)));
139 cgi->LIST_AddString(&productionQueued, "");
140 }
141 } else if (produceCategory == FILTER_AIRCRAFT) {
142 for (i = 0; i < ccs.numAircraftTemplates; i++) {
143 aircraft_t* aircraftTemplate = &ccs.aircraftTemplates[i];
144 /* don't allow producing ufos */
145 if (AIR_IsUFO(aircraftTemplate))
146 continue;
147 if (!aircraftTemplate->tech) {
148 Com_Printf("PR_UpdateProductionList: no technology for craft %s!\n", aircraftTemplate->id);
149 continue;
150 }
151
152 Com_DPrintf(DEBUG_CLIENT, "air: %s ufotype: %i tech: %s time: %i\n", aircraftTemplate->id,
153 aircraftTemplate->ufotype, aircraftTemplate->tech->id, aircraftTemplate->tech->produceTime);
154
155 if (aircraftTemplate->tech->produceTime > 0 && RS_IsResearched_ptr(aircraftTemplate->tech)) {
156 cgi->LIST_AddPointer(&productionItemList, aircraftTemplate);
157 cgi->LIST_AddString(&productionList, va("%s", _(aircraftTemplate->name)));
158 cgi->LIST_AddString(&productionAmount, va("%i", AIR_CountInBaseByTemplate(base, aircraftTemplate)));
159 cgi->LIST_AddString(&productionQueued, "");
160 }
161 }
162 } else {
163 objDef_t* od;
164 for (i = 0, od = cgi->csi->ods; i < cgi->csi->numODs; i++, od++) {
165 const technology_t* tech;
166 if (od->isVirtual)
167 continue;
168 tech = RS_GetTechForItem(od);
169 /* We will not show items that are not producible.
170 * We can only produce what was researched before. */
171 if (RS_IsResearched_ptr(tech) && PR_ItemIsProduceable(od) && INV_ItemMatchesFilter(od, produceCategory)) {
172 cgi->LIST_AddPointer(&productionItemList, od);
173
174 cgi->LIST_AddString(&productionList, va("%s", _(od->name)));
175 cgi->LIST_AddString(&productionAmount, va("%i", B_ItemInBase(od, base)));
176 cgi->LIST_AddString(&productionQueued, "");
177 }
178 }
179 }
180
181 /* bind the menu text to our static char array */
182 cgi->UI_RegisterLinkedListText(TEXT_PRODUCTION_LIST, productionList);
183 /* bind the amount of available items */
184 cgi->UI_RegisterLinkedListText(TEXT_PRODUCTION_AMOUNT, productionAmount);
185 /* bind the amount of queued items */
186 cgi->UI_RegisterLinkedListText(TEXT_PRODUCTION_QUEUED, productionQueued);
187 }
188
PR_RequirementsInfo(const base_t * base,const requirements_t * reqs)189 static void PR_RequirementsInfo (const base_t* base, const requirements_t* reqs) {
190 const vec4_t green = {0.0f, 1.0f, 0.0f, 0.8f};
191 const vec4_t yellow = {1.0f, 0.874f, 0.294f, 1.0f};
192 int i;
193 uiNode_t* req_root = nullptr;
194 uiNode_t* req_items = nullptr;
195 #if 0
196 uiNode_t* req_techs = nullptr;
197 uiNode_t* req_technots = nullptr;
198 #endif
199
200 cgi->UI_ResetData(OPTION_PRODUCTION_REQUIREMENTS);
201
202 if (!reqs->numLinks)
203 return;
204
205 for (i = 0; i < reqs->numLinks; i++) {
206 const requirement_t* req = &reqs->links[i];
207
208 switch (req->type) {
209 case RS_LINK_ITEM: {
210 uiNode_t* node = cgi->UI_AddOption(&req_items, req->link.od->id, va("%i %s", req->amount, _(req->link.od->name)), va("%i", i));
211 if (B_ItemInBase(req->link.od, base) >= req->amount)
212 Vector4Copy(green, node->color);
213 else
214 Vector4Copy(yellow, node->color);
215 break;
216 }
217 case RS_LINK_ANTIMATTER: {
218 technology_t* tech = RS_GetTechForItem(INVSH_GetItemByID(ANTIMATTER_TECH_ID));
219 uiNode_t* node;
220
221 assert(tech);
222 node = cgi->UI_AddOption(&req_items, ANTIMATTER_TECH_ID, va("%i %s", req->amount, _(tech->name)), va("%i", i));
223 if (B_AntimatterInBase(base) >= req->amount)
224 Vector4Copy(green, node->color);
225 else
226 Vector4Copy(yellow, node->color);
227 break;
228 }
229 #if 0
230 case RS_LINK_TECH:
231 /* if (node && RS_IsResearched_ptr(req->link)) ... */
232 break;
233 case RS_LINK_TECH_NOT:
234 /* if (node && RS_IsResearched_ptr(req->link)) ... */
235 break;
236 #endif
237 default:
238 break;
239 }
240 }
241 if (req_items) {
242 uiNode_t* node = cgi->UI_AddOption(&req_root, "items", "_Items", "item");
243 node->firstChild = req_items;
244 }
245 cgi->UI_RegisterOption(OPTION_PRODUCTION_REQUIREMENTS, req_root);
246 }
247
248 /**
249 * @brief Prints information about the selected item (no aircraft) in production.
250 * @param[in] base Pointer to the base where informations should be printed.
251 * @param[in] od The attributes of the item being produced.
252 * @param[in] remainingHours The remaining hours until this production is finished
253 * @sa PR_ProductionInfo
254 */
PR_ItemProductionInfo(const base_t * base,const objDef_t * od,int remainingHours)255 static void PR_ItemProductionInfo (const base_t* base, const objDef_t* od, int remainingHours)
256 {
257 static char productionInfo[512];
258
259 assert(base);
260 assert(od);
261
262 /* Don't try to display an item which is not producible. */
263 if (!PR_ItemIsProduceable(od)) {
264 Com_sprintf(productionInfo, sizeof(productionInfo), _("No item selected"));
265 cgi->Cvar_Set("mn_item", "");
266 } else {
267 const technology_t* tech = RS_GetTechForItem(od);
268
269 Com_sprintf(productionInfo, sizeof(productionInfo), "%s\n", _(od->name));
270 Q_strcat(productionInfo, sizeof(productionInfo), _("Cost per item\t%i c\n"), PR_GetPrice(od));
271 Q_strcat(productionInfo, sizeof(productionInfo), _("Production time\t%ih\n"), remainingHours);
272 Q_strcat(productionInfo, sizeof(productionInfo), _("Item size\t%i\n"), od->size);
273 cgi->Cvar_Set("mn_item", "%s", od->id);
274
275 PR_RequirementsInfo(base, &tech->requireForProduction);
276 cgi->Cmd_ExecuteString("show_requirements %i", tech->requireForProduction.numLinks);
277 }
278 cgi->UI_RegisterText(TEXT_PRODUCTION_INFO, productionInfo);
279 }
280
281 /**
282 * @brief Prints information about the selected disassembly task
283 * @param[in] ufo The UFO being disassembled.
284 * @param[in] remainingHours The remaining hours until this production is finished
285 * @sa PR_ProductionInfo
286 */
PR_DisassemblyInfo(const storedUFO_t * ufo,int remainingHours)287 static void PR_DisassemblyInfo (const storedUFO_t* ufo, int remainingHours)
288 {
289 static char productionInfo[512];
290 int i;
291
292 assert(ufo);
293 assert(ufo->ufoTemplate);
294
295 Com_sprintf(productionInfo, sizeof(productionInfo), "%s (%.0f%%) - %s\n", _(UFO_TypeToName(ufo->ufoTemplate->ufotype)), ufo->condition * 100, _("Disassembly"));
296 Q_strcat(productionInfo, sizeof(productionInfo), _("Stored at: %s\n"), ufo->installation->name);
297 Q_strcat(productionInfo, sizeof(productionInfo), _("Disassembly time: %ih\n"), remainingHours);
298 Q_strcat(productionInfo, sizeof(productionInfo), _("Components:\n"));
299 /* Print components. */
300 for (i = 0; i < ufo->comp->numItemtypes; i++) {
301 const objDef_t* compOd = ufo->comp->items[i];
302 const int amount = (ufo->condition < 1 && ufo->comp->itemAmount2[i] != COMP_ITEMCOUNT_SCALED) ? ufo->comp->itemAmount2[i] : round(ufo->comp->itemAmount[i] * ufo->condition);
303
304 if (amount == 0)
305 continue;
306
307 assert(compOd);
308 Q_strcat(productionInfo, sizeof(productionInfo), " %s (%i)\n", _(compOd->name), amount);
309 }
310 cgi->UI_RegisterText(TEXT_PRODUCTION_INFO, productionInfo);
311 cgi->Cvar_Set("mn_item", "%s", ufo->id);
312 cgi->Cmd_ExecuteString("show_requirements 0");
313 }
314
315 /**
316 * @brief Prints information about the selected aircraft in production.
317 * @param[in] base Pointer to the base where informations should be printed.
318 * @param[in] aircraftTemplate The aircraft to print the information for
319 * @param[in] remainingHours The remaining hours until this production is finished
320 * @sa PR_ProductionInfo
321 */
PR_AircraftInfo(const base_t * base,const aircraft_t * aircraftTemplate,int remainingHours)322 static void PR_AircraftInfo (const base_t* base, const aircraft_t* aircraftTemplate, int remainingHours)
323 {
324 static char productionInfo[512];
325
326 Com_sprintf(productionInfo, sizeof(productionInfo), "%s\n", _(aircraftTemplate->name));
327 Q_strcat(productionInfo, sizeof(productionInfo), _("Production costs\t%i c\n"), PR_GetPrice(aircraftTemplate));
328 Q_strcat(productionInfo, sizeof(productionInfo), _("Production time\t%ih\n"), remainingHours);
329 cgi->UI_RegisterText(TEXT_PRODUCTION_INFO, productionInfo);
330 cgi->Cvar_Set("mn_item", "%s", aircraftTemplate->id);
331 PR_RequirementsInfo(base, &aircraftTemplate->tech->requireForProduction);
332 cgi->Cmd_ExecuteString("show_requirements %i", aircraftTemplate->tech->requireForProduction.numLinks);
333 }
334
335 /**
336 * @brief Prints information about the selected item in production.
337 * @param[in] base Pointer to the base where informations should be printed.
338 * @sa PR_AircraftInfo
339 * @sa PR_ItemProductionInfo
340 * @sa PR_DisassemblyInfo
341 */
PR_ProductionInfo(const base_t * base)342 static void PR_ProductionInfo (const base_t* base)
343 {
344 if (selectedProduction) {
345 const production_t* prod = selectedProduction;
346 const int time = PR_GetRemainingHours(prod);
347 cgi->UI_ExecuteConfunc("prod_taskselected");
348
349 if (PR_IsAircraft(prod)) {
350 PR_AircraftInfo(base, prod->data.data.aircraft, time);
351 cgi->UI_ExecuteConfunc("amountsetter enable");
352 } else if (PR_IsItem(prod)) {
353 PR_ItemProductionInfo(base, prod->data.data.item, time);
354 cgi->UI_ExecuteConfunc("amountsetter enable");
355 } else if (PR_IsDisassembly(prod)) {
356 PR_DisassemblyInfo(prod->data.data.ufo, time);
357 cgi->UI_ExecuteConfunc("amountsetter disable");
358 } else {
359 cgi->Com_Error(ERR_DROP, "PR_ProductionInfo: Selected production is not item nor aircraft nor ufo.\n");
360 }
361 cgi->Cvar_SetValue("mn_production_amount", selectedProduction->amount);
362 } else {
363 if (!PR_IsDataValid(&selectedData)) {
364 cgi->UI_ExecuteConfunc("prod_nothingselected");
365 if (produceCategory == FILTER_AIRCRAFT)
366 cgi->UI_RegisterText(TEXT_PRODUCTION_INFO, _("No aircraft selected."));
367 else
368 cgi->UI_RegisterText(TEXT_PRODUCTION_INFO, _("No item selected"));
369 cgi->Cvar_Set("mn_item", "");
370 } else {
371 cgi->UI_ExecuteConfunc("prod_availableselected");
372 if (PR_IsAircraftData(&selectedData)) {
373 PR_AircraftInfo(base, selectedData.data.aircraft, PR_GetProductionHours(base, &selectedData));
374 } else if (PR_IsItemData(&selectedData)) {
375 PR_ItemProductionInfo(base, selectedData.data.item, PR_GetProductionHours(base, &selectedData));
376 } else if (PR_IsDisassemblyData(&selectedData)) {
377 PR_DisassemblyInfo(selectedData.data.ufo, PR_GetProductionHours(base, &selectedData));
378 }
379 }
380 }
381 /* update capacity counters */
382 cgi->UI_ExecuteConfunc("ui_prod_update_caps %d %d %d %d %d %d %d %d", CAP_GetFreeCapacity(base, CAP_ITEMS), CAP_GetMax(base, CAP_ITEMS),
383 CAP_GetFreeCapacity(base, CAP_AIRCRAFT_SMALL), CAP_GetMax(base, CAP_AIRCRAFT_SMALL),
384 CAP_GetFreeCapacity(base, CAP_AIRCRAFT_BIG), CAP_GetMax(base, CAP_AIRCRAFT_BIG),
385 CAP_GetMax(base, CAP_WORKSPACE) - E_CountHired(base, EMPL_WORKER), CAP_GetMax(base, CAP_WORKSPACE)
386 );
387 }
388
389 /**
390 * @brief Click function for production list
391 * @note Opens the UFOpaedia - by right clicking an item
392 */
PR_ProductionListRightClick_f(void)393 static void PR_ProductionListRightClick_f (void)
394 {
395 int num;
396 production_queue_t* queue;
397 base_t* base = B_GetCurrentSelectedBase();
398
399 /* can be called from everywhere without a base set */
400 if (!base)
401 return;
402
403 queue = PR_GetProductionForBase(base);
404
405 /* not enough parameters */
406 if (cgi->Cmd_Argc() < 2) {
407 Com_Printf("Usage: %s <arg>\n", cgi->Cmd_Argv(0));
408 return;
409 }
410
411 /* clicked which item? */
412 num = atoi(cgi->Cmd_Argv(1));
413
414 /* Clicked the production queue or the item list? */
415 if (num < queue->numItems && num >= 0) {
416 production_t* prod = &queue->items[num];
417 const technology_t* tech = PR_GetTech(&prod->data);
418 selectedProduction = prod;
419 UP_OpenWith(tech->id);
420 } else if (num >= queue->numItems + QUEUE_SPACERS) {
421 /* Clicked in the item list. */
422 const int idx = num - queue->numItems - QUEUE_SPACERS;
423
424 if (produceCategory == FILTER_AIRCRAFT) {
425 const aircraft_t* aircraftTemplate = (const aircraft_t*)cgi->LIST_GetByIdx(productionItemList, idx);
426 /* aircraftTemplate may be empty if rclicked below real entry.
427 * UFO research definition must not have a tech assigned,
428 * only RS_CRAFT types have */
429 if (aircraftTemplate && aircraftTemplate->tech)
430 UP_OpenWith(aircraftTemplate->tech->id);
431 } else if (produceCategory == FILTER_DISASSEMBLY) {
432 const storedUFO_t* ufo = (const storedUFO_t*)cgi->LIST_GetByIdx(productionItemList, idx);
433 if (ufo && ufo->ufoTemplate && ufo->ufoTemplate->tech) {
434 UP_OpenWith(ufo->ufoTemplate->tech->id);
435 }
436 } else {
437 objDef_t* od = (objDef_t*)cgi->LIST_GetByIdx(productionItemList, idx);
438 const technology_t* tech = RS_GetTechForItem(od);
439
440 /* Open up UFOpaedia for this entry. */
441 if (RS_IsResearched_ptr(tech) && INV_ItemMatchesFilter(od, produceCategory)) {
442 PR_ClearSelected();
443 PR_SetData(&selectedData, PRODUCTION_TYPE_ITEM, od);
444 UP_OpenWith(tech->id);
445 return;
446 }
447 }
448 }
449 #ifdef DEBUG
450 else
451 Com_DPrintf(DEBUG_CLIENT, "PR_ProductionListRightClick_f: Click on spacer %i\n", num);
452 #endif
453 }
454
455 /**
456 * @brief Click function for production list.
457 * @note "num" is the entry in the visible production list (includes queued entries and spaces).
458 * @todo left click on spacer should either delete current selection or do nothing, not update visible selection but show old info
459 */
PR_ProductionListClick_f(void)460 static void PR_ProductionListClick_f (void)
461 {
462 int num;
463 production_queue_t* queue;
464 base_t* base = B_GetCurrentSelectedBase();
465
466 /* can be called from everywhere without a base set */
467 if (!base)
468 return;
469
470 queue = PR_GetProductionForBase(base);
471
472 /* Break if there are not enough parameters. */
473 if (cgi->Cmd_Argc() < 2) {
474 Com_Printf("Usage: %s <arg>\n", cgi->Cmd_Argv(0));
475 return;
476 }
477
478 /* Clicked which item? */
479 num = atoi(cgi->Cmd_Argv(1));
480
481 /* Clicked the production queue or the item list? */
482 if (num < queue->numItems && num >= 0) {
483 selectedProduction = &queue->items[num];
484 PR_ProductionInfo(base);
485 } else if (num >= queue->numItems + QUEUE_SPACERS) {
486 /* Clicked in the item list. */
487 const int idx = num - queue->numItems - QUEUE_SPACERS;
488
489 if (produceCategory == FILTER_DISASSEMBLY) {
490 storedUFO_t* ufo = (storedUFO_t*)cgi->LIST_GetByIdx(productionItemList, idx);
491
492 PR_ClearSelected();
493 PR_SetData(&selectedData, PRODUCTION_TYPE_DISASSEMBLY, ufo);
494
495 PR_ProductionInfo(base);
496 } else if (produceCategory == FILTER_AIRCRAFT) {
497 aircraft_t* aircraftTemplate = (aircraft_t*)cgi->LIST_GetByIdx(productionItemList, idx);
498 if (!aircraftTemplate) {
499 Com_DPrintf(DEBUG_CLIENT, "PR_ProductionListClick_f: No item found at the list-position %i!\n", idx);
500 return;
501 }
502 /* ufo research definition must not have a tech assigned
503 * only RS_CRAFT types have
504 * @sa RS_InitTree */
505 if (aircraftTemplate->tech && aircraftTemplate->tech->produceTime >= 0
506 && RS_IsResearched_ptr(aircraftTemplate->tech)) {
507 PR_ClearSelected();
508 PR_SetData(&selectedData, PRODUCTION_TYPE_AIRCRAFT, aircraftTemplate);
509 PR_ProductionInfo(base);
510 }
511 } else {
512 objDef_t* od = (objDef_t*)cgi->LIST_GetByIdx(productionItemList, idx);
513 const technology_t* tech = RS_GetTechForItem(od);
514
515 /* We can only produce items that fulfill the following conditions... */
516 if (RS_IsResearched_ptr(tech) && PR_ItemIsProduceable(od) && INV_ItemMatchesFilter(od, produceCategory)) {
517 PR_ClearSelected();
518 PR_SetData(&selectedData, PRODUCTION_TYPE_ITEM, od);
519 PR_ProductionInfo(base);
520 }
521 }
522 }
523 }
524
525 /**
526 * @brief Will select a new tab on the production list.
527 */
PR_ProductionType_f(void)528 static void PR_ProductionType_f (void)
529 {
530 itemFilterTypes_t cat;
531 base_t* base = B_GetCurrentSelectedBase();
532
533 if (cgi->Cmd_Argc() < 2) {
534 Com_Printf("Usage: %s <category>\n", cgi->Cmd_Argv(0));
535 return;
536 }
537
538 cat = INV_GetFilterTypeID(cgi->Cmd_Argv(1));
539
540 /* Check if the given category index is valid. */
541 if (cat == MAX_FILTERTYPES)
542 cat = FILTER_S_PRIMARY;
543
544 /* Can be called from everywhere without a base set */
545 if (!base)
546 return;
547
548 produceCategory = cat;
549 cgi->Cvar_Set("mn_itemtype", "%s", INV_GetFilterType(produceCategory));
550
551 /* Update list of entries for current production tab. */
552 PR_UpdateProductionList(base);
553
554 /* Reset selected entry, if it was not from the queue */
555 PR_ClearSelectedItems();
556
557 /* Select first entry in the list (if any). */
558 if (cgi->LIST_Count(productionItemList) > 0) {
559 if (produceCategory == FILTER_AIRCRAFT) {
560 const aircraft_t* aircraft = (const aircraft_t*)cgi->LIST_GetByIdx(productionItemList, 0);
561 PR_SetData(&selectedData, PRODUCTION_TYPE_AIRCRAFT, aircraft);
562 } else if (produceCategory == FILTER_DISASSEMBLY) {
563 const storedUFO_t* storedUFO = (const storedUFO_t*)cgi->LIST_GetByIdx(productionItemList, 0);
564 PR_SetData(&selectedData, PRODUCTION_TYPE_DISASSEMBLY, storedUFO);
565 } else {
566 const objDef_t* item = (const objDef_t*)cgi->LIST_GetByIdx(productionItemList, 0);
567 PR_SetData(&selectedData, PRODUCTION_TYPE_ITEM, item);
568 }
569 }
570 /* update selection index if first entry of actual list was chosen */
571 if (!selectedProduction) {
572 const production_queue_t* prod = PR_GetProductionForBase(base);
573 cgi->UI_ExecuteConfunc("prod_selectline %i", prod->numItems + QUEUE_SPACERS);
574 }
575
576 /* Update displayed info about selected entry (if any). */
577 PR_ProductionInfo(base);
578 }
579
580 /**
581 * @brief Will fill the list of producible items.
582 * @note Some of Production Menu related cvars are being set here.
583 */
PR_ProductionList_f(void)584 static void PR_ProductionList_f (void)
585 {
586 base_t* base = B_GetCurrentSelectedBase();
587 /* can be called from everywhere without a started game */
588 if (!base)
589 return;
590
591 cgi->Cvar_SetValue("mn_production_basecap", CAP_GetMax(base, CAP_WORKSPACE));
592
593 /* Set amount of workers - all/ready to work (determined by base capacity. */
594 PR_UpdateProductionCap(base);
595
596 cgi->Cvar_Set("mn_production_workers", "%i/%i",
597 CAP_GetCurrent(base, CAP_WORKSPACE), E_CountHired(base, EMPL_WORKER));
598
599 cgi->Cvar_Set("mn_production_storage", "%i/%i",
600 CAP_GetCurrent(base, CAP_ITEMS), CAP_GetMax(base, CAP_ITEMS));
601
602 PR_ClearSelected();
603 }
604
605 /**
606 * @brief Increases the production amount by given parameter.
607 */
PR_ProductionIncrease_f(void)608 static void PR_ProductionIncrease_f (void)
609 {
610 production_t* prod;
611 base_t* base = B_GetCurrentSelectedBase();
612 technology_t* tech = nullptr;
613 int amount = 1;
614 int producibleAmount;
615
616 if (!base)
617 return;
618
619 if (!PR_IsDataValid(&selectedData))
620 return;
621
622 if (cgi->Cmd_Argc() == 2)
623 amount = atoi(cgi->Cmd_Argv(1));
624
625 if (selectedProduction) {
626 prod = selectedProduction;
627
628 /* We can disassembly UFOs only one-by-one. */
629 if (PR_IsDisassembly(prod))
630 return;
631
632 if (PR_IsAircraft(prod)) {
633 /* Don't allow to queue more aircraft if there is no free space. */
634 if (CAP_GetFreeCapacity(base, AIR_GetCapacityByAircraftWeight(prod->data.data.aircraft)) <= 0) {
635 CP_Popup(_("Hangars not ready"), _("You cannot queue aircraft.\nNo free space in hangars.\n"));
636 cgi->Cvar_SetValue("mn_production_amount", prod->amount);
637 return;
638 }
639 }
640
641 /* amount limit per one production */
642 if (prod->amount + amount > MAX_PRODUCTION_AMOUNT) {
643 amount = std::max(0, MAX_PRODUCTION_AMOUNT - prod->amount);
644 }
645 if (amount == 0) {
646 cgi->Cvar_SetValue("mn_production_amount", prod->amount);
647 return;
648 }
649
650 tech = PR_GetTech(&prod->data);
651 assert(tech);
652
653 producibleAmount = PR_RequirementsMet(amount, &tech->requireForProduction, base);
654 if (producibleAmount == 0) {
655 CP_Popup(_("Not enough materials"), _("You don't have the materials needed for producing more of this item.\n"));
656 cgi->Cvar_SetValue("mn_production_amount", prod->amount);
657 return;
658 } else if (amount != producibleAmount) {
659 CP_Popup(_("Not enough material!"), _("You don't have enough material to produce all (%i) additional items. Only %i could be added."), amount, producibleAmount);
660 }
661
662 PR_IncreaseProduction(prod, producibleAmount);
663 cgi->Cvar_SetValue("mn_production_amount", prod->amount);
664 } else {
665 const char* name = nullptr;
666
667 tech = PR_GetTech(&selectedData);
668 name = PR_GetName(&selectedData);
669
670 producibleAmount = PR_RequirementsMet(amount, &tech->requireForProduction, base);
671 if (producibleAmount == 0) {
672 CP_Popup(_("Not enough materials"), _("You don't have the materials needed for producing this item.\n"));
673 return;
674 } else if (amount != producibleAmount) {
675 CP_Popup(_("Not enough material!"), _("You don't have enough material to produce all (%i) items. Production will continue with a reduced (%i) number."), amount, producibleAmount);
676 }
677 /** @todo
678 * -) need to popup something like: "You need the following items in order to produce more of ITEM: x of ITEM, x of ITEM, etc..."
679 * This info should also be displayed in the item-info.
680 * -) can can (if possible) change the 'amount' to a vlalue that _can_ be produced (i.e. the maximum amount possible).*/
681
682 if (PR_IsAircraftData(&selectedData) && CAP_GetFreeCapacity(base, AIR_GetCapacityByAircraftWeight(selectedData.data.aircraft)) <= 0) {
683 CP_Popup(_("Hangars not ready"), _("You cannot queue aircraft.\nNo free space in hangars.\n"));
684 return;
685 }
686
687 /* add production */
688 prod = PR_QueueNew(base, &selectedData, producibleAmount);
689
690 /** @todo this popup hides any previous popup, like popup created in PR_QueueNew */
691 if (!prod)
692 return;
693
694 /* Now we select the item we just created. */
695 selectedProduction = prod;
696 cgi->UI_ExecuteConfunc("prod_selectline %i", selectedProduction->idx);
697
698 /* messages */
699 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Work begun on %s"), _(name));
700 MSO_CheckAddNewMessage(NT_PRODUCTION_STARTED, _("Production started"), cp_messageBuffer, MSG_PRODUCTION, tech);
701 }
702
703 PR_ProductionInfo(base);
704 PR_UpdateProductionList(base);
705 }
706
707 /**
708 * @brief Stops the current running production
709 */
PR_ProductionStop_f(void)710 static void PR_ProductionStop_f (void)
711 {
712 production_queue_t* queue;
713 base_t* base = B_GetCurrentSelectedBase();
714 int prodIDX;
715
716 if (!base || !selectedProduction)
717 return;
718
719 prodIDX = selectedProduction->idx;
720 queue = PR_GetProductionForBase(base);
721
722 PR_QueueDelete(base, queue, prodIDX);
723
724 if (queue->numItems == 0) {
725 selectedProduction = nullptr;
726 cgi->UI_ExecuteConfunc("prod_selectline -1");
727 } else if (prodIDX >= queue->numItems) {
728 selectedProduction = &queue->items[queue->numItems - 1];
729 cgi->UI_ExecuteConfunc("prod_selectline %i", prodIDX);
730 }
731
732 PR_ProductionInfo(base);
733 PR_UpdateProductionList(base);
734 }
735
736 /**
737 * @brief Decrease the production amount by given parameter
738 */
PR_ProductionDecrease_f(void)739 static void PR_ProductionDecrease_f (void)
740 {
741 int amount = 1;
742 const base_t* base = B_GetCurrentSelectedBase();
743 production_t* prod = selectedProduction;
744
745 if (cgi->Cmd_Argc() == 2)
746 amount = atoi(cgi->Cmd_Argv(1));
747
748 if (!prod)
749 return;
750
751 if (prod->amount <= amount) {
752 PR_ProductionStop_f();
753 return;
754 }
755
756 /** @todo add (confirmaton) popup in case storage cannot take all the items we add back to it */
757 PR_DecreaseProduction(prod, amount);
758
759 if (base) {
760 PR_ProductionInfo(base);
761 PR_UpdateProductionList(base);
762 }
763 }
764
765 /**
766 * @brief Change the production amount by given diff.
767 */
PR_ProductionChange_f(void)768 static void PR_ProductionChange_f (void)
769 {
770 int amount;
771
772 if (!selectedProduction)
773 return;
774
775 if (!PR_IsDataValid(&selectedData))
776 return;
777
778 if (cgi->Cmd_Argc() != 2) {
779 Com_Printf("Usage: %s <diff> : change the production amount\n", cgi->Cmd_Argv(0));
780 return;
781 }
782
783 amount = atoi(cgi->Cmd_Argv(1));
784 if (amount > 0) {
785 cgi->Cbuf_AddText("prod_inc %i\n", amount);
786 } else {
787 cgi->Cbuf_AddText("prod_dec %i\n", -amount);
788 }
789 }
790
791 /**
792 * @brief shift the current production up the list
793 */
PR_ProductionUp_f(void)794 static void PR_ProductionUp_f (void)
795 {
796 production_queue_t* queue;
797 base_t* base = B_GetCurrentSelectedBase();
798
799 if (!base || !selectedProduction)
800 return;
801
802 /* first position already */
803 if (selectedProduction->idx == 0)
804 return;
805
806 queue = PR_GetProductionForBase(base);
807 PR_QueueMove(queue, selectedProduction->idx, -1);
808
809 selectedProduction = &queue->items[selectedProduction->idx - 1];
810 cgi->UI_ExecuteConfunc("prod_selectline %i", selectedProduction->idx);
811 PR_UpdateProductionList(base);
812 }
813
814 /**
815 * @brief shift the current production down the list
816 */
PR_ProductionDown_f(void)817 static void PR_ProductionDown_f (void)
818 {
819 production_queue_t* queue;
820 base_t* base = B_GetCurrentSelectedBase();
821
822 if (!base || !selectedProduction)
823 return;
824
825 queue = PR_GetProductionForBase(base);
826
827 if (selectedProduction->idx >= queue->numItems - 1)
828 return;
829
830 PR_QueueMove(queue, selectedProduction->idx, 1);
831
832 selectedProduction = &queue->items[selectedProduction->idx + 1];
833 cgi->UI_ExecuteConfunc("prod_selectline %i", selectedProduction->idx);
834 PR_UpdateProductionList(base);
835 }
836
PR_InitCallbacks(void)837 void PR_InitCallbacks (void)
838 {
839 cgi->Cmd_AddCommand("prod_init", PR_ProductionList_f, nullptr);
840 cgi->Cmd_AddCommand("prod_type", PR_ProductionType_f, nullptr);
841 cgi->Cmd_AddCommand("prod_up", PR_ProductionUp_f, "Move production item up in the queue");
842 cgi->Cmd_AddCommand("prod_down", PR_ProductionDown_f, "Move production item down in the queue");
843 cgi->Cmd_AddCommand("prod_change", PR_ProductionChange_f, "Change production amount");
844 cgi->Cmd_AddCommand("prod_inc", PR_ProductionIncrease_f, "Increase production amount");
845 cgi->Cmd_AddCommand("prod_dec", PR_ProductionDecrease_f, "Decrease production amount");
846 cgi->Cmd_AddCommand("prod_stop", PR_ProductionStop_f, "Stop production");
847 cgi->Cmd_AddCommand("prodlist_rclick", PR_ProductionListRightClick_f, nullptr);
848 cgi->Cmd_AddCommand("prodlist_click", PR_ProductionListClick_f, nullptr);
849 }
850
PR_ShutdownCallbacks(void)851 void PR_ShutdownCallbacks (void)
852 {
853 cgi->Cmd_RemoveCommand("prod_init");
854 cgi->Cmd_RemoveCommand("prod_type");
855 cgi->Cmd_RemoveCommand("prod_up");
856 cgi->Cmd_RemoveCommand("prod_down");
857 cgi->Cmd_RemoveCommand("prod_change");
858 cgi->Cmd_RemoveCommand("prod_inc");
859 cgi->Cmd_RemoveCommand("prod_dec");
860 cgi->Cmd_RemoveCommand("prod_stop");
861 cgi->Cmd_RemoveCommand("prodlist_rclick");
862 cgi->Cmd_RemoveCommand("prodlist_click");
863 }
864