1 /**
2 * @file
3 * @brief Single player market stuff.
4 * @note Buy/Sell functions prefix: BS_
5 */
6
7 /*
8 Copyright (C) 2002-2013 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_market.h"
29 #include "cp_popup.h"
30 #include "save/save_market.h"
31
32 #define BS_GetMarket() (&ccs.eMarket)
33
34 /**
35 * @brief Check if an item is on market
36 * @param[in] item Pointer to the item to check
37 * @note this function doesn't check if the item is available on market (buyable > 0)
38 */
BS_IsOnMarket(const objDef_t * item)39 bool BS_IsOnMarket (const objDef_t* item)
40 {
41 assert(item);
42 return !(item->isVirtual || item->notOnMarket);
43 }
44
45 /**
46 * @brief Get the number of items of the given type on the market
47 * @param[in] od the item (objDef) to search the market for
48 * @return The amount of items for the given type
49 */
BS_GetItemOnMarket(const objDef_t * od)50 int BS_GetItemOnMarket (const objDef_t* od)
51 {
52 const market_t* market = BS_GetMarket();
53 return BS_IsOnMarket(od) ? market->numItems[od->idx] : 0;
54 }
55
56 /**
57 * @brief Internal function to add items to the market
58 * @param[in] od Object definition (the item itself)
59 * @param[in] amount Non-negative number of items to add
60 */
BS_AddItemToMarket(const objDef_t * od,int amount)61 static void BS_AddItemToMarket (const objDef_t* od, int amount)
62 {
63 market_t* market = BS_GetMarket();
64 assert(amount >= 0);
65 market->numItems[od->idx] += amount;
66 }
67
68 /**
69 * @brief Internal function to remove items from the market
70 * @param[in] od Object definition (the item itself)
71 * @param[in] amount Non-negative number of items to remove
72 */
BS_RemoveItemFromMarket(const objDef_t * od,int amount)73 static void BS_RemoveItemFromMarket (const objDef_t* od, int amount)
74 {
75 market_t* market = BS_GetMarket();
76
77 assert(amount >= 0);
78
79 market->numItems[od->idx] = std::max(market->numItems[od->idx] - amount, 0);
80 }
81
82 /**
83 * @brief Get the price for an item that you want to sell on the market
84 * @param[in] od The item to sell
85 * @return The price of the item
86 */
BS_GetItemSellingPrice(const objDef_t * od)87 int BS_GetItemSellingPrice (const objDef_t* od)
88 {
89 const market_t* market = BS_GetMarket();
90 return market->bidItems[od->idx];
91 }
92
93 /**
94 * @brief Get the price for an item that you want to buy on the market
95 * @param[in] od The item to buy
96 * @return The price of the item
97 */
BS_GetItemBuyingPrice(const objDef_t * od)98 int BS_GetItemBuyingPrice (const objDef_t* od)
99 {
100 const market_t* market = BS_GetMarket();
101 return market->askItems[od->idx];
102 }
103
104 /**
105 * @brief Checks whether a given aircraft should appear on the market
106 * @param aircraft The aircraft to check
107 * @return @c true if the aircraft should appear on the market
108 */
BS_AircraftIsOnMarket(const aircraft_t * aircraft)109 bool BS_AircraftIsOnMarket (const aircraft_t* aircraft)
110 {
111 if (aircraft->type == AIRCRAFT_UFO)
112 return false;
113 if (aircraft->price == -1)
114 return false;
115
116 return true;
117 }
118
119 /**
120 * @brief Get the number of aircraft of the given type on the market
121 * @param[in] aircraft The aircraft to search the market for
122 * @return The amount of aircraft for the given type
123 */
BS_GetAircraftOnMarket(const aircraft_t * aircraft)124 int BS_GetAircraftOnMarket (const aircraft_t* aircraft)
125 {
126 const humanAircraftType_t type = cgi->Com_DropShipShortNameToID(aircraft->id);
127 const market_t* market = BS_GetMarket();
128
129 return BS_AircraftIsOnMarket(aircraft) ? market->numAircraft[type] : 0;
130 }
131
132 /**
133 * @brief Internal function to add aircraft to the market
134 * @param[in] aircraft Aircraft template definition
135 * @param[in] amount Non-negative number of aircraft to add
136 */
BS_AddAircraftToMarket(const aircraft_t * aircraft,int amount)137 static void BS_AddAircraftToMarket (const aircraft_t* aircraft, int amount)
138 {
139 const humanAircraftType_t type = cgi->Com_DropShipShortNameToID(aircraft->id);
140 market_t* market = BS_GetMarket();
141 assert(amount >= 0);
142 assert(aircraft->type != AIRCRAFT_UFO);
143 market->numAircraft[type] += amount;
144 }
145
146 /**
147 * @brief Internal function to remove aircraft from the market
148 * @param[in] aircraft Aircraft template definition
149 * @param[in] amount Non-negative number of aircraft to remove
150 */
BS_RemoveAircraftFromMarket(const aircraft_t * aircraft,int amount)151 static void BS_RemoveAircraftFromMarket (const aircraft_t* aircraft, int amount)
152 {
153 const humanAircraftType_t type = cgi->Com_DropShipShortNameToID(aircraft->id);
154 market_t* market = BS_GetMarket();
155
156 assert(amount >= 0);
157 assert(aircraft->type != AIRCRAFT_UFO);
158
159 market->numAircraft[type] = std::max(market->numAircraft[type] - amount, 0);
160 }
161
162 /**
163 * @brief Get the price for an aircraft that you want to sell on the market
164 * @param[in] aircraft The aircraft to sell
165 * @return The price of the aircraft
166 */
BS_GetAircraftSellingPrice(const aircraft_t * aircraft)167 int BS_GetAircraftSellingPrice (const aircraft_t* aircraft)
168 {
169 const humanAircraftType_t type = cgi->Com_DropShipShortNameToID(aircraft->id);
170 const market_t* market = BS_GetMarket();
171 int sellPrice = market->bidAircraft[type];
172
173 assert(aircraft->type != AIRCRAFT_UFO);
174
175 if (aircraft->tpl != aircraft) {
176 int i;
177
178 if (aircraft->stats[AIR_STATS_DAMAGE] > 0)
179 sellPrice *= (float)aircraft->damage / aircraft->stats[AIR_STATS_DAMAGE];
180
181 if (aircraft->shield.item)
182 sellPrice += BS_GetItemSellingPrice(aircraft->shield.item);
183 if (aircraft->shield.ammo)
184 sellPrice += BS_GetItemSellingPrice(aircraft->shield.ammo);
185
186 for (i = 0; i < aircraft->maxWeapons; i++) {
187 if (aircraft->weapons[i].item)
188 sellPrice += BS_GetItemSellingPrice(aircraft->weapons[i].item);
189 if (aircraft->weapons[i].ammo)
190 sellPrice += BS_GetItemSellingPrice(aircraft->weapons[i].ammo);
191 }
192
193 for (i = 0; i < aircraft->maxElectronics; i++) {
194 if (aircraft->electronics[i].item)
195 sellPrice += BS_GetItemSellingPrice(aircraft->electronics[i].item);
196 if (aircraft->electronics[i].ammo)
197 sellPrice += BS_GetItemSellingPrice(aircraft->electronics[i].ammo);
198 }
199 }
200 return sellPrice;
201 }
202
203 /**
204 * @brief Get the price for an aircraft that you want to buy on the market
205 * @param[in] aircraft The aircraft to buy
206 * @return The price of the aircraft
207 */
BS_GetAircraftBuyingPrice(const aircraft_t * aircraft)208 int BS_GetAircraftBuyingPrice (const aircraft_t* aircraft)
209 {
210 const humanAircraftType_t type = cgi->Com_DropShipShortNameToID(aircraft->id);
211 const market_t* market = BS_GetMarket();
212 assert(aircraft->type != AIRCRAFT_UFO);
213 return market->askAircraft[type];
214 }
215
216 /**
217 * @brief Update storage, the market, and the player's credits
218 * @note Don't update capacity here because we can sell items directly from aircraft (already removed from storage).
219 */
BS_ProcessCraftItemSale(const objDef_t * craftitem,const int numItems)220 static void BS_ProcessCraftItemSale (const objDef_t* craftitem, const int numItems)
221 {
222 if (craftitem) {
223 BS_AddItemToMarket(craftitem, numItems);
224 CP_UpdateCredits(ccs.credits + BS_GetItemSellingPrice(craftitem) * numItems);
225 }
226 }
227
228 /**
229 * @brief Buys an aircraft
230 * @param[in] aircraftTemplate The aircraft template to buy
231 * @param[out] base Base to buy at
232 * @return @c true if the aircraft could get bought, @c false otherwise
233 */
BS_BuyAircraft(const aircraft_t * aircraftTemplate,base_t * base)234 bool BS_BuyAircraft (const aircraft_t* aircraftTemplate, base_t* base)
235 {
236 if (!base)
237 cgi->Com_Error(ERR_DROP, "BS_BuyAircraft: No base given.");
238 if (!aircraftTemplate)
239 cgi->Com_Error(ERR_DROP, "BS_BuyAircraft: No aircraft template given.");
240
241 int price = BS_GetAircraftBuyingPrice(aircraftTemplate);
242 if (ccs.credits < price)
243 return false;
244
245 /* Hangar capacities are being updated in AIR_NewAircraft().*/
246 BS_RemoveAircraftFromMarket(aircraftTemplate, 1);
247 CP_UpdateCredits(ccs.credits - price);
248 AIR_NewAircraft(base, aircraftTemplate);
249
250 return true;
251 }
252
253 /**
254 * @brief Sells the given aircraft with all the equipment.
255 * @param aircraft The aircraft to sell
256 * @return @c true if the aircraft could get sold, @c false otherwise
257 */
BS_SellAircraft(aircraft_t * aircraft)258 bool BS_SellAircraft (aircraft_t* aircraft)
259 {
260 int j;
261
262 if (AIR_GetTeamSize(aircraft) > 0)
263 return false;
264
265 if (!AIR_IsAircraftInBase(aircraft))
266 return false;
267
268 /* sell off any items which are mounted on it */
269 for (j = 0; j < aircraft->maxWeapons; j++) {
270 const aircraftSlot_t* slot = &aircraft->weapons[j];
271 BS_ProcessCraftItemSale(slot->item, 1);
272 BS_ProcessCraftItemSale(slot->ammo, 1);
273 }
274
275 BS_ProcessCraftItemSale(aircraft->shield.item, 1);
276 /* there should be no ammo here, but checking can't hurt */
277 BS_ProcessCraftItemSale(aircraft->shield.ammo, 1);
278
279 for (j = 0; j < aircraft->maxElectronics; j++) {
280 const aircraftSlot_t* slot = &aircraft->electronics[j];
281 BS_ProcessCraftItemSale(slot->item, 1);
282 /* there should be no ammo here, but checking can't hurt */
283 BS_ProcessCraftItemSale(slot->ammo, 1);
284 }
285
286 /* the capacities are also updated here */
287 BS_AddAircraftToMarket(aircraft, 1);
288 CP_UpdateCredits(ccs.credits + BS_GetAircraftSellingPrice(aircraft));
289 AIR_DeleteAircraft(aircraft);
290
291 return true;
292 }
293
294 /**
295 * @brief Buys the given UGV
296 * @param[in] ugv The ugv template of the UGV to buy
297 * @param[out] base Base to buy at
298 * @return @c true if the ugv could get bought, @c false otherwise
299 * @todo Implement this correctly once we have UGV
300 */
BS_BuyUGV(const ugv_t * ugv,base_t * base)301 bool BS_BuyUGV (const ugv_t* ugv, base_t* base)
302 {
303 const objDef_t* ugvWeapon;
304
305 if (!ugv)
306 cgi->Com_Error(ERR_DROP, "BS_BuyUGV: Called on nullptr UGV!");
307 if (!base)
308 cgi->Com_Error(ERR_DROP, "BS_BuyUGV: Called on nullptr base!");
309 ugvWeapon = INVSH_GetItemByID(ugv->weapon);
310 if (!ugvWeapon)
311 cgi->Com_Error(ERR_DROP, "BS_BuyItem_f: Could not get weapon '%s' for ugv/tank '%s'.", ugv->weapon, ugv->id);
312
313 if (ccs.credits < ugv->price)
314 return false;
315 if (E_CountUnhiredRobotsByType(ugv) <= 0)
316 return false;
317 if (BS_GetItemOnMarket(ugvWeapon) <= 0)
318 return false;
319 if (CAP_GetFreeCapacity(base, CAP_ITEMS) < UGV_SIZE + ugvWeapon->size)
320 return false;
321 if (!E_HireRobot(base, ugv))
322 return false;
323
324 BS_RemoveItemFromMarket(ugvWeapon, 1);
325 CP_UpdateCredits(ccs.credits - ugv->price);
326 B_AddToStorage(base, ugvWeapon, 1);
327
328 return true;
329 }
330
331 /**
332 * @brief Sells the given UGV with all the equipment.
333 * @param robot The employee record of the UGV to sell
334 * @return @c true if the ugv could get sold, @c false otherwise
335 * @todo Implement this correctly once we have UGV
336 */
BS_SellUGV(Employee * robot)337 bool BS_SellUGV (Employee* robot)
338 {
339 const objDef_t* ugvWeapon;
340 const ugv_t* ugv;
341 base_t* base;
342
343 if (!robot)
344 cgi->Com_Error(ERR_DROP, "Selling nullptr UGV!");
345 if (!robot->getUGV())
346 cgi->Com_Error(ERR_DROP, "Selling invalid UGV with UCN: %i", robot->chr.ucn);
347 ugv = robot->getUGV();
348 base = robot->baseHired;
349
350 /* Check if we have a weapon for this ugv in the market to sell it. */
351 ugvWeapon = INVSH_GetItemByID(ugv->weapon);
352 if (!ugvWeapon)
353 cgi->Com_Error(ERR_DROP, "BS_BuyItem_f: Could not get wepaon '%s' for ugv/tank '%s'.", ugv->weapon, ugv->id);
354
355 if (!robot->unhire()) {
356 /** @todo message - Couldn't fire employee. */
357 Com_DPrintf(DEBUG_CLIENT, "Couldn't sell/fire robot/ugv.\n");
358 return false;
359 }
360
361 BS_AddItemToMarket(ugvWeapon, 1);
362 CP_UpdateCredits(ccs.credits + ugv->price);
363 B_AddToStorage(base, ugvWeapon, -1);
364
365 return true;
366 }
367
368 /**
369 * @brief Buys items from the market
370 * @param[in] od pointer to the item (Object Definition record)
371 * @param[out] base Base to buy at
372 * @param[in ] count Number of items to buy
373 * @return @c true if the ugv could get bought, @c false otherwise
374 */
BS_BuyItem(const objDef_t * od,base_t * base,int count)375 bool BS_BuyItem (const objDef_t* od, base_t* base, int count)
376 {
377 if (!od)
378 cgi->Com_Error(ERR_DROP, "BS_BuyItem: Called on nullptr objDef!");
379 if (!base)
380 cgi->Com_Error(ERR_DROP, "BS_BuyItem: Called on nullptr base!");
381
382 if (count <= 0)
383 return false;
384 if (!BS_IsOnMarket(od))
385 return false;
386 if (ccs.credits < BS_GetItemBuyingPrice(od) * count)
387 return false;
388 if (BS_GetItemOnMarket(od) < count)
389 return false;
390 if (CAP_GetFreeCapacity(base, CAP_ITEMS) < od->size * count)
391 return false;
392
393 B_AddToStorage(base, od, count);
394 BS_RemoveItemFromMarket(od, count);
395 CP_UpdateCredits(ccs.credits - BS_GetItemBuyingPrice(od) * count);
396
397 return true;
398 }
399
400 /**
401 * @brief Sells items from the market
402 * @param[in] od pointer to the item (Object Definition record)
403 * @param[out] base Base to sell at
404 * @param[in ] count Number of items to sell
405 * @return @c true if the ugv could get sold, @c false otherwise
406 */
BS_SellItem(const objDef_t * od,base_t * base,int count)407 bool BS_SellItem (const objDef_t* od, base_t* base, int count)
408 {
409 if (!od)
410 cgi->Com_Error(ERR_DROP, "BS_SellItem: Called on nullptr objDef!");
411
412 if (count <= 0)
413 return false;
414 if (!BS_IsOnMarket(od))
415 return false;
416
417 if (base) {
418 if (B_ItemInBase(od, base) < count)
419 return false;
420 B_AddToStorage(base, od, -1 * count);
421 }
422
423 BS_AddItemToMarket(od, count);
424 CP_UpdateCredits(ccs.credits + BS_GetItemSellingPrice(od) * count);
425
426 return true;
427 }
428
429 /**
430 * @brief Save callback for savegames
431 * @param[out] parent XML Node structure, where we write the information to
432 * @sa BS_LoadXML
433 * @sa SAV_GameSaveXML
434 */
BS_SaveXML(xmlNode_t * parent)435 bool BS_SaveXML (xmlNode_t* parent)
436 {
437 int i;
438 xmlNode_t* node;
439 const market_t* market = BS_GetMarket();
440
441 /* store market */
442 node = cgi->XML_AddNode(parent, SAVE_MARKET_MARKET);
443 for (i = 0; i < cgi->csi->numODs; i++) {
444 const objDef_t* od = INVSH_GetItemByIDX(i);
445 if (BS_IsOnMarket(od)) {
446 xmlNode_t* snode = cgi->XML_AddNode(node, SAVE_MARKET_ITEM);
447 cgi->XML_AddString(snode, SAVE_MARKET_ID, od->id);
448 cgi->XML_AddIntValue(snode, SAVE_MARKET_NUM, market->numItems[i]);
449 cgi->XML_AddIntValue(snode, SAVE_MARKET_BID, market->bidItems[i]);
450 cgi->XML_AddIntValue(snode, SAVE_MARKET_ASK, market->askItems[i]);
451 cgi->XML_AddDoubleValue(snode, SAVE_MARKET_EVO, market->currentEvolutionItems[i]);
452 cgi->XML_AddBoolValue(snode, SAVE_MARKET_AUTOSELL, market->autosell[i]);
453 }
454 }
455 for (i = 0; i < AIRCRAFTTYPE_MAX; i++) {
456 if (market->bidAircraft[i] > 0 || market->askAircraft[i] > 0) {
457 xmlNode_t* snode = cgi->XML_AddNode(node, SAVE_MARKET_AIRCRAFT);
458 const char* shortName = cgi->Com_DropShipTypeToShortName((humanAircraftType_t)i);
459 cgi->XML_AddString(snode, SAVE_MARKET_ID, shortName);
460 cgi->XML_AddIntValue(snode, SAVE_MARKET_NUM, market->numAircraft[i]);
461 cgi->XML_AddIntValue(snode, SAVE_MARKET_BID, market->bidAircraft[i]);
462 cgi->XML_AddIntValue(snode, SAVE_MARKET_ASK, market->askAircraft[i]);
463 cgi->XML_AddDoubleValue(snode, SAVE_MARKET_EVO, market->currentEvolutionAircraft[i]);
464 }
465 }
466 return true;
467 }
468
469 /**
470 * @brief Load callback for savegames
471 * @param[in] parent XML Node structure, where we get the information from
472 * @sa BS_Save
473 * @sa SAV_GameLoad
474 */
BS_LoadXML(xmlNode_t * parent)475 bool BS_LoadXML (xmlNode_t* parent)
476 {
477 xmlNode_t* node, *snode;
478 market_t* market = BS_GetMarket();
479
480 node = cgi->XML_GetNode(parent, SAVE_MARKET_MARKET);
481 if (!node)
482 return false;
483
484 for (snode = cgi->XML_GetNode(node, SAVE_MARKET_ITEM); snode; snode = cgi->XML_GetNextNode(snode, node, SAVE_MARKET_ITEM)) {
485 const char* s = cgi->XML_GetString(snode, SAVE_MARKET_ID);
486 const objDef_t* od = INVSH_GetItemByID(s);
487
488 if (!od) {
489 Com_Printf("BS_LoadXML: Could not find item '%s'\n", s);
490 continue;
491 }
492
493 market->numItems[od->idx] = cgi->XML_GetInt(snode, SAVE_MARKET_NUM, 0);
494 market->bidItems[od->idx] = cgi->XML_GetInt(snode, SAVE_MARKET_BID, 0);
495 market->askItems[od->idx] = cgi->XML_GetInt(snode, SAVE_MARKET_ASK, 0);
496 market->currentEvolutionItems[od->idx] = cgi->XML_GetDouble(snode, SAVE_MARKET_EVO, 0.0);
497 market->autosell[od->idx] = cgi->XML_GetBool(snode, SAVE_MARKET_AUTOSELL, false);
498 }
499 for (snode = cgi->XML_GetNode(node, SAVE_MARKET_AIRCRAFT); snode; snode = cgi->XML_GetNextNode(snode, node, SAVE_MARKET_AIRCRAFT)) {
500 const char* s = cgi->XML_GetString(snode, SAVE_MARKET_ID);
501 const humanAircraftType_t type = cgi->Com_DropShipShortNameToID(s);
502
503 market->numAircraft[type] = cgi->XML_GetInt(snode, SAVE_MARKET_NUM, 0);
504 market->bidAircraft[type] = cgi->XML_GetInt(snode, SAVE_MARKET_BID, 0);
505 market->askAircraft[type] = cgi->XML_GetInt(snode, SAVE_MARKET_ASK, 0);
506 market->currentEvolutionAircraft[type] = cgi->XML_GetDouble(snode, SAVE_MARKET_EVO, 0.0);
507 }
508
509 return true;
510 }
511
512 /**
513 * @brief sets market prices at start of the game
514 * @sa CP_CampaignInit
515 * @sa B_SetUpFirstBase
516 * @sa BS_Load (Market load function)
517 */
BS_InitMarket(const campaign_t * campaign)518 void BS_InitMarket (const campaign_t* campaign)
519 {
520 int i;
521 market_t* market = BS_GetMarket();
522
523 for (i = 0; i < cgi->csi->numODs; i++) {
524 const objDef_t* od = INVSH_GetItemByIDX(i);
525 if (market->askItems[i] == 0) {
526 market->askItems[i] = od->price;
527 market->bidItems[i] = floor(market->askItems[i] * BID_FACTOR);
528 }
529
530 if (campaign->marketDef->numItems[i] <= 0)
531 continue;
532
533 if (RS_IsResearched_ptr(RS_GetTechForItem(od))) {
534 /* the other relevant values were already set above */
535 market->numItems[i] = campaign->marketDef->numItems[i];
536 } else {
537 Com_Printf("BS_InitMarket: Could not add item %s to the market - not marked as researched in campaign %s\n",
538 od->id, campaign->id);
539 }
540 }
541
542 for (i = 0; i < AIRCRAFTTYPE_MAX; i++) {
543 const char* name = cgi->Com_DropShipTypeToShortName((humanAircraftType_t)i);
544 const aircraft_t* aircraft = AIR_GetAircraft(name);
545 if (market->askAircraft[i] == 0) {
546 market->askAircraft[i] = aircraft->price;
547 market->bidAircraft[i] = floor(market->askAircraft[i] * BID_FACTOR);
548 }
549
550 if (campaign->marketDef->numAircraft[i] <= 0)
551 continue;
552
553 if (RS_IsResearched_ptr(aircraft->tech)) {
554 /* the other relevant values were already set above */
555 market->numAircraft[i] = campaign->marketDef->numAircraft[i];
556 } else {
557 Com_Printf("BS_InitMarket: Could not add aircraft %s to the market - not marked as researched in campaign %s\n",
558 aircraft->id, campaign->id);
559 }
560 }
561 }
562
563 /**
564 * @brief make number of items change every day.
565 * @sa CP_CampaignRun
566 * @sa daily called
567 * @note This function makes items number on market slowly reach the asymptotic number of items defined in equipment.ufo
568 * If an item has just been researched, it's not available on market until RESEARCH_LIMIT_DELAY days is reached.
569 */
CP_CampaignRunMarket(campaign_t * campaign)570 void CP_CampaignRunMarket (campaign_t* campaign)
571 {
572 int i;
573 const float TYPICAL_TIME = 10.f; /**< Number of days to reach the asymptotic number of items */
574 const int RESEARCH_LIMIT_DELAY = 30; /**< Numbers of days after end of research to wait in order to have
575 * items added on market */
576 market_t* market = BS_GetMarket();
577
578 assert(campaign->marketDef);
579 assert(campaign->asymptoticMarketDef);
580
581 for (i = 0; i < cgi->csi->numODs; i++) {
582 const objDef_t* od = INVSH_GetItemByIDX(i);
583 const technology_t* tech = RS_GetTechForItem(od);
584 int asymptoticNumber;
585
586 if (RS_IsResearched_ptr(tech) && (campaign->marketDef->numItems[i] != 0 || ccs.date.day > tech->researchedDate.day + RESEARCH_LIMIT_DELAY)) {
587 /* if items are researched for more than RESEARCH_LIMIT_DELAY or was on the initial market,
588 * there number tend to the value defined in equipment.ufo.
589 * This value is the asymptotic value if it is not 0, or initial value else */
590 asymptoticNumber = campaign->asymptoticMarketDef->numItems[i] ? campaign->asymptoticMarketDef->numItems[i] : campaign->marketDef->numItems[i];
591 } else {
592 /* items that have just been researched don't appear on market, but they can disappear */
593 asymptoticNumber = 0;
594 }
595
596 /* Store the evolution of the market in currentEvolution */
597 market->currentEvolutionItems[i] += (asymptoticNumber - market->numItems[i]) / TYPICAL_TIME;
598
599 /* Check if new items appeared or disappeared on market */
600 if (fabs(market->currentEvolutionItems[i]) >= 1.0f) {
601 const int num = (int)(market->currentEvolutionItems[i]);
602 if (num >= 0)
603 BS_AddItemToMarket(od, num);
604 else
605 BS_RemoveItemFromMarket(od, -num);
606 market->currentEvolutionItems[i] -= num;
607 }
608 }
609
610 for (i = 0; i < AIRCRAFTTYPE_MAX; i++) {
611 const humanAircraftType_t type = (humanAircraftType_t)i;
612 const char* aircraftID = cgi->Com_DropShipTypeToShortName(type);
613 const aircraft_t* aircraft = AIR_GetAircraft(aircraftID);
614 const technology_t* tech = aircraft->tech;
615 int asymptoticNumber;
616
617 if (RS_IsResearched_ptr(tech) && (campaign->marketDef->numAircraft[i] != 0 || ccs.date.day > tech->researchedDate.day + RESEARCH_LIMIT_DELAY)) {
618 /* if aircraft is researched for more than RESEARCH_LIMIT_DELAY or was on the initial market,
619 * there number tend to the value defined in equipment.ufo.
620 * This value is the asymptotic value if it is not 0, or initial value else */
621 asymptoticNumber = campaign->asymptoticMarketDef->numAircraft[i] ? campaign->asymptoticMarketDef->numAircraft[i] : campaign->marketDef->numAircraft[i];
622 } else {
623 /* items that have just been researched don't appear on market, but they can disappear */
624 asymptoticNumber = 0;
625 }
626 /* Store the evolution of the market in currentEvolution */
627 market->currentEvolutionAircraft[i] += (asymptoticNumber - market->numAircraft[i]) / TYPICAL_TIME;
628
629 /* Check if new items appeared or disappeared on market */
630 if (fabs(market->currentEvolutionAircraft[i]) >= 1.0f) {
631 const int num = (int)(market->currentEvolutionAircraft[i]);
632 if (num >= 0)
633 BS_AddAircraftToMarket(aircraft, num);
634 else
635 BS_RemoveAircraftFromMarket(aircraft, -num);
636 market->currentEvolutionAircraft[i] -= num;
637 }
638 }
639 }
640
641 /**
642 * @brief Returns true if you can buy or sell equipment
643 * @param[in] base Pointer to base to check on
644 * @sa B_BaseInit_f
645 */
BS_BuySellAllowed(const base_t * base)646 bool BS_BuySellAllowed (const base_t* base)
647 {
648 return !B_IsUnderAttack(base) && B_GetBuildingStatus(base, B_STORAGE);
649 }
650