Bug Summary

File:client/cgame/campaign/cp_produce.cpp
Location:line 63, column 17
Description:Access to field 'produceTime' results in a dereference of a null pointer (loaded from variable 'tech')

Annotated Source Code

1/**
2 * @file
3 * @brief Single player production stuff
4 * @note Production stuff functions prefix: PR_
5 */
6
7/*
8Copyright (C) 2002-2012 UFO: Alien Invasion.
9
10This program is free software; you can redistribute it and/or
11modify it under the terms of the GNU General Public License
12as published by the Free Software Foundation; either version 2
13of the License, or (at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18
19See the GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License
22along with this program; if not, write to the Free Software
23Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24*/
25
26#include "../../cl_shared.h"
27#include "cp_campaign.h"
28#include "cp_capacity.h"
29#include "cp_ufo.h"
30#include "cp_popup.h"
31#include "cp_produce.h"
32#include "cp_produce_callbacks.h"
33#include "save/save_produce.h"
34
35/** @brief Default amount of workers, the produceTime for technologies is defined. */
36/** @note producetime for technology entries is the time for PRODUCE_WORKERS amount of workers. */
37static const int PRODUCE_WORKERS = 10;
38
39static cvar_t* mn_production_limit; /**< Maximum items in queue. */
40static cvar_t* mn_production_workers; /**< Amount of hired workers in base. */
41static cvar_t* mn_production_amount; /**< Amount of the current production; if no production, an invalid value */
42
43/**
44 * @brief Calculates the fraction (percentage) of production of an item in 1 minute.
45 * @param[in] base Pointer to the base with given production.
46 * @param[in] prodData Pointer to the productionData structure.
47 * @sa PR_ProductionRun
48 * @sa PR_ItemProductionInfo
49 * @sa PR_DisassemblyInfo
50 * @return 0 if the production does not make any progress, 1 if the whole item is built in 1 minute
51 */
52static double PR_CalculateProductionTimeInMinutes (const base_t *base, const productionData_t *prodData)
53{
54 /* Check how many workers hired in this base. */
55 const signed int allWorkers = E_CountHired(base, EMPL_WORKER);
56 /* We will not use more workers than workspace capacity in this base. */
57 const signed int maxWorkers = std::min(allWorkers, CAP_GetMax(base, CAP_WORKSPACE)(base)->capacities[(CAP_WORKSPACE)].max);
58
59 double timeDefault;
60 if (PR_IsProductionData(prodData)(!((prodData)->type == PRODUCTION_TYPE_DISASSEMBLY))) {
1
Taking true branch
61 const technology_t *tech = PR_GetTech(prodData);
62 /* This is the default production time for PRODUCE_WORKERS workers. */
63 timeDefault = tech->produceTime;
2
Access to field 'produceTime' results in a dereference of a null pointer (loaded from variable 'tech')
64 } else {
65 const storedUFO_t *storedUFO = prodData->data.ufo;
66 /* This is the default disassemble time for PRODUCE_WORKERS workers. */
67 timeDefault = storedUFO->comp->time;
68 /* Production is 4 times longer when installation is on Antipodes
69 * Penalty starts when distance is greater than 45 degrees */
70 timeDefault *= std::max(1.0, GetDistanceOnGlobe(storedUFO->installation->pos, base->pos) / 45.0);
71 }
72 /* Calculate the time needed for production of the item for our amount of workers. */
73 double const timeScaled = timeDefault * (MINUTES_PER_HOUR60 * PRODUCE_WORKERS) / maxWorkers;
74 /* Don't allow to return a time of less than 1 (you still need at least 1 minute to produce an item). */
75 return std::max(1.0, timeScaled);
76}
77
78/**
79 * @brief Calculates the total frame count needed for producing an item
80 * @param[in] base Pointer to the base the production happen
81 * @param[in] prodData Pointer to the productionData structure
82 */
83static int PR_CalculateTotalFrames (const base_t *base, const productionData_t *prodData)
84{
85 return PR_CalculateProductionTimeInMinutes(base, prodData) + 1;
86}
87
88/**
89 * @brief Calculates the remaining time for a technology in minutes
90 * @param[in] prod Pointer to the production structure
91 */
92int PR_GetRemainingMinutes (const production_t *prod)
93{
94 assert(prod)(__builtin_expect(!(prod), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 94, "prod") : (void)0)
;
95 return prod->totalFrames - prod->frame;
96}
97
98/**
99 * @brief Calculates the remaining hours for a technology
100 * @param[in] prod Pointer to the production structure
101 */
102int PR_GetRemainingHours (const production_t *prod)
103{
104 return round(PR_GetRemainingMinutes(prod) / (double)MINUTES_PER_HOUR60);
105}
106
107/**
108 * @brief Calculates the production time (in hours) for a technology
109 * @param[in] base Pointer to the base to calculate production time at
110 * @param[in] prodData Pointer to the production data structure
111 */
112int PR_GetProductionHours (const base_t *base, const productionData_t *prodData)
113{
114 return round(PR_CalculateTotalFrames(base, prodData) / (double)MINUTES_PER_HOUR60);
115}
116
117/**
118 * @brief Remove or add the required items from/to the a base.
119 * @param[in] base Pointer to base.
120 * @param[in] amount How many items are planned to be added (positive number) or removed (negative number).
121 * @param[in] reqs The production requirements of the item that is to be produced. These included numbers are multiplied with 'amount')
122 */
123void PR_UpdateRequiredItemsInBasestorage (base_t *base, int amount, const requirements_t *reqs)
124{
125 int i;
126
127 if (!base)
128 return;
129
130 if (amount == 0)
131 return;
132
133 for (i = 0; i < reqs->numLinks; i++) {
134 const requirement_t *req = &reqs->links[i];
135 switch (req->type) {
136 case RS_LINK_ITEM: {
137 const objDef_t *item = req->link.od;
138 assert(item)(__builtin_expect(!(item), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 138, "item") : (void)0)
;
139 B_AddToStorage(base, item, req->amount * amount);
140 break;
141 }
142 case RS_LINK_ANTIMATTER:
143 if (req->amount > 0)
144 B_ManageAntimatter(base, req->amount * amount, true);
145 else if (req->amount < 0)
146 B_ManageAntimatter(base, req->amount * -amount, false);
147 break;
148 case RS_LINK_TECH:
149 case RS_LINK_TECH_NOT:
150 break;
151 default:
152 Com_Error(ERR_DROP1, "Invalid requirement for production!\n");
153 }
154 }
155}
156
157/**
158 * @brief Checks if the production requirements are met for a defined amount.
159 * @param[in] amount How many items are planned to be produced.
160 * @param[in] reqs The production requirements of the item that is to be produced.
161 * @param[in] base Pointer to base.
162 * @return how much item/aircraft/etc can be produced
163 */
164int PR_RequirementsMet (int amount, const requirements_t *reqs, base_t *base)
165{
166 int i;
167 int producibleAmount = amount;
168
169 for (i = 0; i < reqs->numLinks; i++) {
170 const requirement_t *req = &reqs->links[i];
171
172 switch (req->type) {
173 case RS_LINK_ITEM: {
174 const int items = std::min(amount, B_ItemInBase(req->link.od, base) / ((req->amount) ? req->amount : 1));
175 producibleAmount = std::min(producibleAmount, items);
176 break;
177 }
178 case RS_LINK_ANTIMATTER: {
179 const int am = std::min(amount, B_AntimatterInBase(base) / ((req->amount) ? req->amount : 1));
180 producibleAmount = std::min(producibleAmount, am);
181 break;
182 }
183 case RS_LINK_TECH:
184 producibleAmount = (RS_IsResearched_ptr(req->link.tech)) ? producibleAmount : 0;
185 break;
186 case RS_LINK_TECH_NOT:
187 producibleAmount = (RS_IsResearched_ptr(req->link.tech)) ? 0 : producibleAmount;
188 break;
189 default:
190 break;
191 }
192 }
193
194 return producibleAmount;
195}
196
197/**
198 * @brief returns the number of free production slots of a queue
199 * @param[in] base Pointer to the base to check
200 */
201int PR_QueueFreeSpace (const base_t* base)
202{
203 const production_queue_t *queue = PR_GetProductionForBase(base)(&((base)->productions));
204
205 int numWorkshops;
206
207 assert(queue)(__builtin_expect(!(queue), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 207, "queue") : (void)0)
;
208 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 208, "base") : (void)0)
;
209
210 numWorkshops = std::max(B_GetNumberOfBuildingsInBaseByBuildingType(base, B_WORKSHOP), 0);
211
212 return std::min(MAX_PRODUCTIONS256, numWorkshops * MAX_PRODUCTIONS_PER_WORKSHOP5 - queue->numItems);
213}
214
215/**
216 * @return The translated name of the item in production
217 */
218const char* PR_GetName (const productionData_t *data)
219{
220 switch (data->type) {
221 case PRODUCTION_TYPE_ITEM:
222 return _(data->data.item->name)gettext(data->data.item->name);
223 case PRODUCTION_TYPE_AIRCRAFT:
224 return _(data->data.aircraft->tpl->name)gettext(data->data.aircraft->tpl->name);
225 case PRODUCTION_TYPE_DISASSEMBLY:
226 return UFO_TypeToName(data->data.ufo->ufoTemplate->ufotype);
227 default:
228 Com_Error(ERR_DROP1, "Invalid production type given: %i", data->type);
229 }
230}
231
232/**
233 * @return The technology for the item that is assigned to the given production
234 */
235technology_t* PR_GetTech (const productionData_t *data)
236{
237 switch (data->type) {
238 case PRODUCTION_TYPE_ITEM:
239 return RS_GetTechForItem(data->data.item);
240 case PRODUCTION_TYPE_AIRCRAFT:
241 return data->data.aircraft->tech;
242 case PRODUCTION_TYPE_DISASSEMBLY:
243 return data->data.ufo->ufoTemplate->tech;
244 default:
245 return NULL__null;
246 }
247}
248
249/**
250 * @brief Add a new item to the bottom of the production queue.
251 * @param[in] base Pointer to base, where the queue is.
252 * @param[in] data The production data
253 * @param[in] amount Desired amount to produce.
254 */
255production_t *PR_QueueNew (base_t *base, const productionData_t *data, signed int amount)
256{
257 production_t *prod;
258 const technology_t *tech;
259 production_queue_t *queue = PR_GetProductionForBase(base)(&((base)->productions));
260
261 if (PR_QueueFreeSpace(base) <= 0)
262 return NULL__null;
263 if (E_CountHired(base, EMPL_WORKER) <= 0)
264 return NULL__null;
265
266 /* Initialize */
267 prod = &queue->items[queue->numItems];
268 OBJZERO(*prod)(memset(&((*prod)), (0), sizeof((*prod))));
269 /* self-reference. */
270 prod->idx = queue->numItems;
271 prod->data = *data;
272 prod->amount = amount;
273
274 tech = PR_GetTech(&prod->data);
275 if (!tech)
276 return NULL__null;
277
278 prod->totalFrames = PR_CalculateTotalFrames(base, data);
279
280 if (PR_IsDisassemblyData(data)((data)->type == PRODUCTION_TYPE_DISASSEMBLY)) {
281 /* only one item for disassemblies */
282 amount = 1;
283 } else if (tech->produceTime < 0) {
284 /* Don't try to add an item to the queue which is not producible. */
285 return NULL__null;
286 }
287
288 amount = PR_RequirementsMet(amount, &tech->requireForProduction, base);
289 if (amount == 0)
290 return NULL__null;
291
292 PR_UpdateRequiredItemsInBasestorage(base, -amount, &tech->requireForProduction);
293
294 /* We cannot queue new aircraft if no free hangar space. */
295 /** @todo move this check out into a new function */
296 if (data->type == PRODUCTION_TYPE_AIRCRAFT) {
297 if (!B_GetBuildingStatus(base, B_COMMAND)) {
298 /** @todo move popup into menucode */
299 CP_Popup(_("Hangars not ready")gettext("Hangars not ready"), _("You cannot queue aircraft.\nNo command centre in this base.\n")gettext("You cannot queue aircraft.\nNo command centre in this base.\n"
)
);
300 return NULL__null;
301 } else if (!B_GetBuildingStatus(base, B_HANGAR) && !B_GetBuildingStatus(base, B_SMALL_HANGAR)) {
302 /** @todo move popup into menucode */
303 CP_Popup(_("Hangars not ready")gettext("Hangars not ready"), _("You cannot queue aircraft.\nNo hangars in this base.\n")gettext("You cannot queue aircraft.\nNo hangars in this base.\n"
)
);
304 return NULL__null;
305 }
306 /** @todo we should also count aircraft that are already in the queue list */
307 if (AIR_CalculateHangarStorage(data->data.aircraft, base, 0) <= 0) {
308 CP_Popup(_("Hangars not ready")gettext("Hangars not ready"), _("You cannot queue aircraft.\nNo free space in hangars.\n")gettext("You cannot queue aircraft.\nNo free space in hangars.\n"
)
);
309 return NULL__null;
310 }
311 }
312
313 /** @todo remove this and make the ufo const */
314 if (PR_IsDisassembly(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
)
315 prod->data.data.ufo->disassembly = prod;
316
317 queue->numItems++;
318 return prod;
319}
320
321/**
322 * @brief Delete the selected entry from the queue.
323 * @param[in] base Pointer to base, where the queue is.
324 * @param[in] queue Pointer to the queue.
325 * @param[in] index Selected index in queue.
326 */
327void PR_QueueDelete (base_t *base, production_queue_t *queue, int index)
328{
329 int i;
330 production_t *prod = &queue->items[index];
331 const technology_t *tech;
332
333 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 333, "base") : (void)0)
;
334
335 tech = PR_GetTech(&prod->data);
336 if (tech == NULL__null)
337 Com_Error(ERR_DROP1, "No tech pointer for production");
338
339 if (PR_IsDisassembly(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
)
340 prod->data.data.ufo->disassembly = NULL__null;
341
342 PR_UpdateRequiredItemsInBasestorage(base, prod->amount, &tech->requireForProduction);
343
344 REMOVE_ELEM_ADJUST_IDX(queue->items, index, queue->numItems)do { size_t idx__ = (index); size_t n__; size_t i__; do { size_t
idx__ = (index); size_t n__ = --(queue->numItems); (__builtin_expect
(!(idx__ <= n__), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 344, "idx__ <= n__") : (void)0); memmove((queue->items
) + idx__, (queue->items) + idx__ + 1, (n__ - idx__) * sizeof
(*(queue->items))); (memset(&(((queue->items)[n__])
), (0), sizeof(((queue->items)[n__])))); } while (0); n__ =
(queue->numItems); for (i__ = idx__; i__ < n__; ++i__)
--(queue->items)[i__].idx; } while (0)
;
345
346 /* Adjust ufos' disassembly pointer */
347 for (i = index; i < queue->numItems; i++) {
348 production_t *disassembly = &queue->items[i];
349
350 if (PR_IsDisassembly(disassembly)(((&(disassembly)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
)
351 disassembly->data.data.ufo->disassembly = disassembly;
352 }
353}
354
355/**
356 * @brief Moves the given queue item in the given direction.
357 * @param[in] queue Pointer to the queue.
358 * @param[in] index The production item index in the queue
359 * @param[in] offset The offset relative to the given index where the item should be moved, too
360 */
361void PR_QueueMove (production_queue_t *queue, int index, int offset)
362{
363 const int newIndex = std::max(0, std::min(index + offset, queue->numItems - 1));
364 int i;
365 production_t saved;
366
367 if (newIndex == index)
368 return;
369
370 saved = queue->items[index];
371
372 /* copy up */
373 for (i = index; i < newIndex; i++) {
374 production_t *prod;
375 queue->items[i] = queue->items[i + 1];
376 prod = &queue->items[i];
377 prod->idx = i;
378 if (PR_IsDisassembly(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
)
379 prod->data.data.ufo->disassembly = prod;
380 }
381
382 /* copy down */
383 for (i = index; i > newIndex; i--) {
384 production_t *prod;
385 queue->items[i] = queue->items[i - 1];
386 prod = &queue->items[i];
387 prod->idx = i;
388 if (PR_IsDisassembly(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
)
389 prod->data.data.ufo->disassembly = prod;
390 }
391
392 /* insert item */
393 queue->items[newIndex] = saved;
394 queue->items[newIndex].idx = newIndex;
395 if (PR_IsDisassembly(&queue->items[newIndex])(((&(&queue->items[newIndex])->data)->type ==
PRODUCTION_TYPE_DISASSEMBLY))
)
396 queue->items[newIndex].data.data.ufo->disassembly = &(queue->items[newIndex]);
397}
398
399/**
400 * @brief Queues the next production in the queue.
401 * @param[in] base Pointer to the base.
402 */
403void PR_QueueNext (base_t *base)
404{
405 production_queue_t *queue = PR_GetProductionForBase(base)(&((base)->productions));
406
407 PR_QueueDelete(base, queue, 0);
408
409 if (queue->numItems == 0) {
410 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Production queue for %s is empty")gettext("Production queue for %s is empty"), base->name);
411 MSO_CheckAddNewMessage(NT_PRODUCTION_QUEUE_EMPTY, _("Production queue empty")gettext("Production queue empty"), cp_messageBuffer, MSG_PRODUCTION);
412 }
413}
414
415/**
416 * @brief clears the production queue on a base
417 */
418static void PR_EmptyQueue (base_t *base)
419{
420 production_queue_t *queue;
421
422 if (!base)
423 return;
424
425 queue = PR_GetProductionForBase(base)(&((base)->productions));
426 while (queue->numItems)
427 PR_QueueDelete(base, queue, 0);
428}
429
430/**
431 * @brief moves the first production to the bottom of the list
432 */
433static void PR_ProductionRollBottom (base_t *base)
434{
435 production_queue_t *queue = PR_GetProductionForBase(base)(&((base)->productions));
436 if (queue->numItems < 2)
437 return;
438
439 PR_QueueMove(queue, 0, queue->numItems - 1);
440}
441
442/**
443 * @brief Disassembles item, adds components to base storage and calculates all components size.
444 * @param[in] base Pointer to base where the disassembling is being made.
445 * @param[in] comp Pointer to components definition.
446 * @param[in] condition condition of the item/UFO being disassembled, objects gathered from disassembly decreased to that factor
447 * @param[in] calculate True if this is only calculation of item size, false if this is real disassembling.
448 * @return Size of all components in this disassembling.
449 */
450static int PR_DisassembleItem (base_t *base, const components_t *comp, float condition, bool calculate)
451{
452 int i;
453 int size = 0;
454
455 for (i = 0; i < comp->numItemtypes; i++) {
456 const objDef_t *compOd = comp->items[i];
457 const int amount = (condition < 1 && comp->itemAmount2[i] != COMP_ITEMCOUNT_SCALED-32768) ? comp->itemAmount2[i] : round(comp->itemAmount[i] * condition);
458
459 if (amount <= 0)
460 continue;
461
462 assert(compOd)(__builtin_expect(!(compOd), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 462, "compOd") : (void)0)
;
463 size += compOd->size * amount;
464 /* Add to base storage only if this is real disassembling, not calculation of size. */
465 if (!calculate) {
466 if (Q_streq(compOd->id, ANTIMATTER_TECH_ID)(strcmp(compOd->id, "antimatter") == 0)) {
467 B_ManageAntimatter(base, amount, true);
468 } else {
469 technology_t *tech = RS_GetTechForItem(compOd);
470 B_UpdateStorageAndCapacity(base, compOd, amount, false);
471 RS_MarkCollected(tech);
472 }
473 Com_DPrintf(DEBUG_CLIENT0x20, "PR_DisassembleItem: added %i amounts of %s\n", amount, compOd->id);
474 }
475 }
476 return size;
477}
478
479/**
480 * @brief Checks whether production can continue
481 * @param base The base the production is running in
482 * @param prod The production
483 * @return @c false if production is stopped, @c true if production can be continued.
484 */
485static bool PR_CheckFrame (base_t *base, production_t *prod)
486{
487 bool state = true;
488
489 if (PR_IsItem(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_ITEM))) {
490 const objDef_t *od = prod->data.data.item;
491 /* Not enough money to produce more items in this base. */
492 if (PR_HasInsufficientCredits(od)(((od)->productionCost * 1 / 1) > ccs.credits)) {
493 if (!prod->creditMessage) {
494 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Not enough credits to finish production in %s.")gettext("Not enough credits to finish production in %s."), base->name);
495 MSO_CheckAddNewMessage(NT_PRODUCTION_FAILED, _("Notice")gettext("Notice"), cp_messageBuffer);
496 prod->creditMessage = true;
497 }
498 state = false;
499 }
500 /* Not enough free space in base storage for this item. */
501 else if (CAP_GetFreeCapacity(base, CAP_ITEMS) < od->size) {
502 if (!prod->spaceMessage) {
503 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Not enough free storage space in %s. Production postponed.")gettext("Not enough free storage space in %s. Production postponed."
)
, base->name);
504 MSO_CheckAddNewMessage(NT_PRODUCTION_FAILED, _("Notice")gettext("Notice"), cp_messageBuffer);
505 prod->spaceMessage = true;
506 }
507 state = false;
508 }
509 } else if (PR_IsAircraft(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_AIRCRAFT
))
) {
510 const aircraft_t *aircraft = prod->data.data.aircraft;
511 /* Not enough money to produce more items in this base. */
512 if (PR_HasInsufficientCredits(aircraft)(((aircraft)->productionCost * 1 / 1) > ccs.credits)) {
513 if (!prod->creditMessage) {
514 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Not enough credits to finish production in %s.")gettext("Not enough credits to finish production in %s."), base->name);
515 MSO_CheckAddNewMessage(NT_PRODUCTION_FAILED, _("Notice")gettext("Notice"), cp_messageBuffer);
516 prod->creditMessage = true;
517 }
518 state = false;
519 }
520 /* Not enough free space in hangars for this aircraft. */
521 else if (AIR_CalculateHangarStorage(aircraft, base, 0) <= 0) {
522 if (!prod->spaceMessage) {
523 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Not enough free hangar space in %s. Production postponed.")gettext("Not enough free hangar space in %s. Production postponed."
)
, base->name);
524 MSO_CheckAddNewMessage(NT_PRODUCTION_FAILED, _("Notice")gettext("Notice"), cp_messageBuffer);
525 prod->spaceMessage = true;
526 }
527 state = false;
528 }
529 } else if (PR_IsDisassembly(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
) {
530 const storedUFO_t *ufo = prod->data.data.ufo;
531
532 if (CAP_GetFreeCapacity(base, CAP_ITEMS) < PR_DisassembleItem(base, ufo->comp, ufo->condition, true)) {
533 if (!prod->spaceMessage) {
534 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Not enough free storage space in %s. Disassembling postponed.")gettext("Not enough free storage space in %s. Disassembling postponed."
)
, base->name);
535 MSO_CheckAddNewMessage(NT_PRODUCTION_FAILED, _("Notice")gettext("Notice"), cp_messageBuffer);
536 prod->spaceMessage = true;
537 }
538 state = false;
539 }
540 }
541
542 if (!state)
543 PR_ProductionRollBottom(base);
544
545 return state;
546}
547
548/**
549 * @brief Runs the production of an item or an aircraft
550 * @sa PR_DisassemblingFrame
551 * @sa PR_ProductionRun
552 * @param base The base to produce in
553 * @param prod The production that is running
554 */
555static void PR_ProductionFrame (base_t* base, production_t *prod)
556{
557 if (!PR_IsProduction(prod)(!(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
)))
)
558 return;
559
560 if (PR_IsReady(prod)((prod)->frame > (prod)->totalFrames)) {
561 const char *name = PR_GetName(&prod->data);
562 technology_t *tech = PR_GetTech(&prod->data);
563
564 prod->frame = 0;
565 prod->amount--;
566
567 if (PR_IsItem(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_ITEM))) {
568 CP_UpdateCredits(ccs.credits - PR_GetPrice(prod->data.data.item)((prod->data.data.item)->productionCost * 1 / 1));
569 /* Now add it to equipment and update capacity. */
570 B_UpdateStorageAndCapacity(base, prod->data.data.item, 1, false);
571 } else if (PR_IsAircraft(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_AIRCRAFT
))
) {
572 CP_UpdateCredits(ccs.credits - PR_GetPrice(prod->data.data.aircraft)((prod->data.data.aircraft)->productionCost * 1 / 1));
573 /* Now add new aircraft. */
574 AIR_NewAircraft(base, prod->data.data.aircraft);
575 }
576
577 /* queue the next production */
578 if (prod->amount <= 0) {
579 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("The production of %s at %s has finished.")gettext("The production of %s at %s has finished."), name, base->name);
580 MSO_CheckAddNewMessage(NT_PRODUCTION_FINISHED, _("Production finished")gettext("Production finished"), cp_messageBuffer, MSG_PRODUCTION, tech);
581 PR_QueueNext(base);
582 }
583 }
584}
585
586/**
587 * @brief Runs the disassembling of a ufo
588 * @sa PR_ProductionFrame
589 * @sa PR_ProductionRun
590 * @param base The base to produce in
591 * @param prod The production that is running
592 */
593static void PR_DisassemblingFrame (base_t* base, production_t* prod)
594{
595 if (!PR_IsDisassembly(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
)
596 return;
597
598 if (PR_IsReady(prod)((prod)->frame > (prod)->totalFrames)) {
599 storedUFO_t *ufo = prod->data.data.ufo;
600 PR_DisassembleItem(base, ufo->comp, ufo->condition, false);
601
602 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("The disassembling of %s at %s has finished.")gettext("The disassembling of %s at %s has finished."),
603 UFO_TypeToName(ufo->ufoTemplate->ufotype), base->name);
604 MSO_CheckAddNewMessage(NT_PRODUCTION_FINISHED, _("Production finished")gettext("Production finished"), cp_messageBuffer, MSG_PRODUCTION, ufo->ufoTemplate->tech);
605
606 /* Removing UFO will remove the production too */
607 US_RemoveStoredUFO(ufo);
608 }
609}
610
611/**
612 * @brief increases production amount if possible
613 * @param[in,out] prod Pointer to the production
614 * @param[in] amount Additional amount to add
615 * @returns the amount added
616 */
617int PR_IncreaseProduction (production_t *prod, int amount)
618{
619 base_t *base;
620 const technology_t *tech;
621
622 if (PR_IsDisassembly(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
)
623 return 0;
624
625 base = PR_ProductionBase(prod);
626 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 626, "base") : (void)0)
;
627
628 /* amount limit per one production */
629 if (prod->amount + amount > MAX_PRODUCTION_AMOUNT500) {
630 amount = std::max(0, MAX_PRODUCTION_AMOUNT500 - prod->amount);
631 }
632
633 tech = PR_GetTech(&prod->data);
634 assert(tech)(__builtin_expect(!(tech), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 634, "tech") : (void)0)
;
635
636 amount = PR_RequirementsMet(amount, &tech->requireForProduction, base);
637 if (amount == 0)
638 return 0;
639
640 prod->amount += amount;
641 PR_UpdateRequiredItemsInBasestorage(base, -amount, &tech->requireForProduction);
642
643 return amount;
644}
645
646/**
647 * @brief decreases production amount
648 * @param[in,out] prod Pointer to the production
649 * @param[in] amount Additional amount to remove (positive number)
650 * @returns the amount removed
651 * @note if production amount falls below 1 it removes the whole production from the queue as well
652 */
653int PR_DecreaseProduction (production_t *prod, int amount)
654{
655 base_t *base;
656 const technology_t *tech;
657
658 assert(prod)(__builtin_expect(!(prod), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 658, "prod") : (void)0)
;
659 base = PR_ProductionBase(prod);
660 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 660, "base") : (void)0)
;
661
662 if (PR_IsDisassembly(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
)
663 return 0;
664
665 if (prod->amount <= amount) {
666 production_queue_t *queue = PR_GetProductionForBase(base)(&((base)->productions));
667 amount = prod->amount;
668 PR_QueueDelete(base, queue, prod->idx);
669 return amount;
670 }
671
672 tech = PR_GetTech(&prod->data);
673 if (tech == NULL__null)
674 Com_Error(ERR_DROP1, "No tech pointer for production");
675
676 prod->amount += -amount;
677 PR_UpdateRequiredItemsInBasestorage(base, amount, &tech->requireForProduction);
678
679 return amount;
680}
681
682/**
683 * @brief Checks whether an item is finished.
684 * @note One call each game time minute
685 * @sa CP_CampaignRun
686 * @sa PR_DisassemblingFrame
687 * @sa PR_ProductionFrame
688 */
689void PR_ProductionRun (void)
690{
691 base_t *base;
692
693 /* Loop through all founded bases. Then check productions
694 * in global data array. Then increase prod->percentDone and check
695 * whether an item is produced. Then add to base storage. */
696 base = NULL__null;
697 while ((base = B_GetNext(base)) != NULL__null) {
698 production_t *prod;
699 production_queue_t *q = PR_GetProductionForBase(base)(&((base)->productions));
700
701 /* not actually any active productions */
702 if (q->numItems <= 0)
703 continue;
704
705 /* Workshop is disabled because their dependences are disabled */
706 if (!PR_ProductionAllowed(base))
707 continue;
708
709 prod = &q->items[0];
710 prod->totalFrames = PR_CalculateTotalFrames(base, &prod->data);
711
712 if (!PR_CheckFrame(base, prod))
713 return;
714
715 prod->frame++;
716
717 PR_ProductionFrame(base, prod);
718 PR_DisassemblingFrame(base, prod);
719 }
720}
721
722/**
723 * @brief Returns true if the current base is able to produce items
724 * @param[in] base Pointer to the base.
725 * @sa B_BaseInit_f
726 */
727bool PR_ProductionAllowed (const base_t* base)
728{
729 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 729, "base") : (void)0)
;
730 return !B_IsUnderAttack(base)((base)->baseStatus == BASE_UNDER_ATTACK) && B_GetBuildingStatus(base, B_WORKSHOP) && E_CountHired(base, EMPL_WORKER) > 0;
731}
732
733void PR_ProductionInit (void)
734{
735 mn_production_limit = Cvar_Get("mn_production_limit", "0");
736 mn_production_workers = Cvar_Get("mn_production_workers", "0");
737 mn_production_amount = Cvar_Get("mn_production_amount", "0");
738}
739
740/**
741 * @brief Update the current capacity of Workshop
742 * @param[in] base Pointer to the base containing workshop.
743 */
744void PR_UpdateProductionCap (base_t *base)
745{
746 capacities_t *workspaceCapacity = CAP_Get(base, CAP_WORKSPACE)&((base)->capacities[(CAP_WORKSPACE)]);
747 int workers;
748 assert(base)(__builtin_expect(!(base), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 748, "base") : (void)0)
;
749
750 if (workspaceCapacity->max <= 0)
751 PR_EmptyQueue(base);
752
753 workers = E_CountHired(base, EMPL_WORKER);
754 if (workspaceCapacity->max >= workers)
755 workspaceCapacity->cur = workers;
756 else
757 workspaceCapacity->cur = workspaceCapacity->max;
758
759 /* recalculate time to finish */
760 production_queue_t *q = PR_GetProductionForBase(base)(&((base)->productions));
761 /* not actually any active productions */
762 if (q->numItems <= 0)
763 return;
764 /* Workshop is disabled because their dependences are disabled */
765 if (!PR_ProductionAllowed(base))
766 return;
767
768 for (int i = 0; i < q->numItems; i++) {
769 production_t *prod = &q->items[i];
770 prod->totalFrames = std::max(prod->frame, PR_CalculateTotalFrames(base, &prod->data));
771 }
772}
773
774/**
775 * @brief check if an item is producable.
776 * @param[in] item Pointer to the item that should be checked.
777 */
778bool PR_ItemIsProduceable (const objDef_t *item)
779{
780 const technology_t *tech = RS_GetTechForItem(item);
781 return tech->produceTime != -1;
782}
783
784/**
785 * @brief Returns the base pointer the production belongs to
786 * @param[in] production pointer to the production entry
787 * @return base_t pointer to the base
788 */
789base_t *PR_ProductionBase (const production_t *production)
790{
791 base_t *base = NULL__null;
792 while ((base = B_GetNext(base)) != NULL__null) {
793 const ptrdiff_t diff = ((ptrdiff_t)((production) - PR_GetProductionForBase(base)(&((base)->productions))->items));
794
795 if (diff >= 0 && diff < MAX_PRODUCTIONS256)
796 return base;
797 }
798 return NULL__null;
799}
800
801/**
802 * @brief Save callback for savegames in XML Format
803 * @param[out] p XML Node structure, where we write the information to
804 * @sa PR_LoadXML
805 * @sa SAV_GameSaveXML
806 */
807bool PR_SaveXML (xmlNode_tmxml_node_t *p)
808{
809 base_t *base;
810 xmlNode_tmxml_node_t *node = XML_AddNode(p, SAVE_PRODUCE_PRODUCTION"production");
811
812 base = NULL__null;
813 while ((base = B_GetNext(base)) != NULL__null) {
814 const production_queue_t *pq = PR_GetProductionForBase(base)(&((base)->productions));
815 int j;
816
817 xmlNode_tmxml_node_t *snode = XML_AddNode(node, SAVE_PRODUCE_QUEUE"queue");
818 /** @todo this should not be the base index */
819 XML_AddInt(snode, SAVE_PRODUCE_QUEUEIDX"IDX", base->idx);
820
821 for (j = 0; j < pq->numItems; j++) {
822 const production_t *prod = &pq->items[j];
823 xmlNode_tmxml_node_t * ssnode = XML_AddNode(snode, SAVE_PRODUCE_ITEM"item");
824 if (PR_IsItem(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_ITEM)))
825 XML_AddString(ssnode, SAVE_PRODUCE_ITEMID"itemid", prod->data.data.item->id);
826 else if (PR_IsAircraft(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_AIRCRAFT
))
)
827 XML_AddString(ssnode, SAVE_PRODUCE_AIRCRAFTID"aircraftid", prod->data.data.aircraft->id);
828 else if (PR_IsDisassembly(prod)(((&(prod)->data)->type == PRODUCTION_TYPE_DISASSEMBLY
))
)
829 XML_AddInt(ssnode, SAVE_PRODUCE_UFOIDX"UFOIDX", prod->data.data.ufo->idx);
830 XML_AddInt(ssnode, SAVE_PRODUCE_AMOUNT"amount", prod->amount);
831 XML_AddInt(ssnode, SAVE_PRODUCE_PROGRESS"progress", prod->frame);
832 }
833 }
834 return true;
835}
836
837/**
838 * @brief Load callback for xml savegames
839 * @param[in] p XML Node structure, where we get the information from
840 * @sa PR_SaveXML
841 * @sa SAV_GameLoadXML
842 */
843bool PR_LoadXML (xmlNode_tmxml_node_t *p)
844{
845 xmlNode_tmxml_node_t *node, *snode;
846
847 node = XML_GetNode(p, SAVE_PRODUCE_PRODUCTION"production");
848
849 for (snode = XML_GetNode(node, SAVE_PRODUCE_QUEUE"queue"); snode;
850 snode = XML_GetNextNode(snode, node, SAVE_PRODUCE_QUEUE"queue")) {
851 xmlNode_tmxml_node_t *ssnode;
852 const int baseIDX = XML_GetInt(snode, SAVE_PRODUCE_QUEUEIDX"IDX", MAX_BASES8);
853 base_t *base = B_GetBaseByIDX(baseIDX);
854 production_queue_t *pq;
855
856 if (base == NULL__null) {
857 Com_Printf("Invalid production queue index %i\n", baseIDX);
858 continue;
859 }
860
861 pq = PR_GetProductionForBase(base)(&((base)->productions));
862
863 for (ssnode = XML_GetNode(snode, SAVE_PRODUCE_ITEM"item"); pq->numItems < MAX_PRODUCTIONS256 && ssnode;
864 ssnode = XML_GetNextNode(ssnode, snode, SAVE_PRODUCE_ITEM"item")) {
865 const char *s1 = XML_GetString(ssnode, SAVE_PRODUCE_ITEMID"itemid");
866 const char *s2;
867 int ufoIDX;
868 production_t *prod = &pq->items[pq->numItems];
869
870 prod->idx = pq->numItems;
871 prod->amount = XML_GetInt(ssnode, SAVE_PRODUCE_AMOUNT"amount", 0);
872 prod->frame = XML_GetInt(ssnode, SAVE_PRODUCE_PROGRESS"progress", 0);
873
874 /* amount */
875 if (prod->amount <= 0) {
876 Com_Printf("PR_LoadXML: Production with amount <= 0 dropped (baseidx=%i, production idx=%i).\n",
877 baseIDX, pq->numItems);
878 continue;
879 }
880 /* item */
881 if (s1[0] != '\0')
882 PR_SetData(&prod->data, PRODUCTION_TYPE_ITEM, INVSH_GetItemByID(s1))do { (__builtin_expect(!(INVSH_GetItemByID(s1)), 0) ? __assert_rtn
(__func__, "src/client/cgame/campaign/cp_produce.cpp", 882, "INVSH_GetItemByID(s1)"
) : (void)0); (&prod->data)->data.pointer = (INVSH_GetItemByID
(s1)); (&prod->data)->type = (PRODUCTION_TYPE_ITEM)
; } while (0);
;
883 /* UFO */
884 ufoIDX = XML_GetInt(ssnode, SAVE_PRODUCE_UFOIDX"UFOIDX", -1);
885 if (ufoIDX != -1) {
886 storedUFO_t *ufo = US_GetStoredUFOByIDX(ufoIDX);
887
888 if (!ufo) {
889 Com_Printf("PR_LoadXML: Could not find ufo idx: %i\n", ufoIDX);
890 continue;
891 }
892
893 PR_SetData(&prod->data, PRODUCTION_TYPE_DISASSEMBLY, ufo)do { (__builtin_expect(!(ufo), 0) ? __assert_rtn(__func__, "src/client/cgame/campaign/cp_produce.cpp"
, 893, "ufo") : (void)0); (&prod->data)->data.pointer
= (ufo); (&prod->data)->type = (PRODUCTION_TYPE_DISASSEMBLY
); } while (0);
;
894 prod->data.data.ufo->disassembly = prod;
895 }
896 /* aircraft */
897 s2 = XML_GetString(ssnode, SAVE_PRODUCE_AIRCRAFTID"aircraftid");
898 if (s2[0] != '\0')
899 PR_SetData(&prod->data, PRODUCTION_TYPE_AIRCRAFT, AIR_GetAircraft(s2))do { (__builtin_expect(!(AIR_GetAircraft(s2)), 0) ? __assert_rtn
(__func__, "src/client/cgame/campaign/cp_produce.cpp", 899, "AIR_GetAircraft(s2)"
) : (void)0); (&prod->data)->data.pointer = (AIR_GetAircraft
(s2)); (&prod->data)->type = (PRODUCTION_TYPE_AIRCRAFT
); } while (0);
;
900
901 if (!PR_IsDataValid(&prod->data)((&prod->data)->data.pointer != __null)) {
902 Com_Printf("PR_LoadXML: Production is not an item an aircraft nor a disassembly\n");
903 continue;
904 }
905
906 pq->numItems++;
907 }
908 }
909 return true;
910}
911
912/**
913 * @brief Set percentDone values after loading the campaign
914 * @note it need to be done after B_PostLoadInitCapacity
915 * @sa PR_PostLoadInit
916 */
917static bool PR_PostLoadInitProgress (void)
918{
919 base_t *base = NULL__null;
920 while ((base = B_GetNext(base)) != NULL__null) {
921 production_queue_t *pq = PR_GetProductionForBase(base)(&((base)->productions));
922 int j;
923
924 for (j = 0; j < pq->numItems; j++) {
925 production_t *prod = &pq->items[j];
926 prod->totalFrames = PR_CalculateTotalFrames(base, &prod->data);
927 }
928 }
929
930 return true;
931}
932
933/**
934 * @brief actions to do with productions after loading a savegame
935 * @sa SAV_GameActionsAfterLoad
936 */
937bool PR_PostLoadInit (void)
938{
939 return PR_PostLoadInitProgress();
940}