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') |
1 | /** | ||
2 | * @file | ||
3 | * @brief Single player production stuff | ||
4 | * @note Production stuff functions prefix: PR_ | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | Copyright (C) 2002-2012 UFO: Alien Invasion. | ||
9 | |||
10 | This program is free software; you can redistribute it and/or | ||
11 | modify it under the terms of the GNU General Public License | ||
12 | as published by the Free Software Foundation; either version 2 | ||
13 | of the License, or (at your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
18 | |||
19 | See the GNU General Public License for more details. | ||
20 | |||
21 | You should have received a copy of the GNU General Public License | ||
22 | along with this program; if not, write to the Free Software | ||
23 | Foundation, 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. */ | ||
37 | static const int PRODUCE_WORKERS = 10; | ||
38 | |||
39 | static cvar_t* mn_production_limit; /**< Maximum items in queue. */ | ||
40 | static cvar_t* mn_production_workers; /**< Amount of hired workers in base. */ | ||
41 | static 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 | */ | ||
52 | static 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))) { | ||
| |||
61 | const technology_t *tech = PR_GetTech(prodData); | ||
62 | /* This is the default production time for PRODUCE_WORKERS workers. */ | ||
63 | timeDefault = tech->produceTime; | ||
| |||
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 | */ | ||
83 | static 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 | */ | ||
92 | int 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 | */ | ||
102 | int 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 | */ | ||
112 | int 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 | */ | ||
123 | void 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 | */ | ||
164 | int 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 | */ | ||
201 | int 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 | */ | ||
218 | const 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 | */ | ||
235 | technology_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 | */ | ||
255 | production_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 | */ | ||
327 | void 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 | */ | ||
361 | void 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 | */ | ||
403 | void 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 | */ | ||
418 | static 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 | */ | ||
433 | static 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 | */ | ||
450 | static 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 | */ | ||
485 | static 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 | */ | ||
555 | static 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 | */ | ||
593 | static 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 | */ | ||
617 | int 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 | */ | ||
653 | int 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 | */ | ||
689 | void 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 | */ | ||
727 | bool 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 | |||
733 | void 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 | */ | ||
744 | void 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 | */ | ||
778 | bool 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 | */ | ||
789 | base_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 | */ | ||
807 | bool 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 | */ | ||
843 | bool 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 | */ | ||
917 | static 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 | */ | ||
937 | bool PR_PostLoadInit (void) | ||
938 | { | ||
939 | return PR_PostLoadInitProgress(); | ||
940 | } |