1 ///////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2011 by The Allacrost Project
3 //            Copyright (C) 2012-2016 by Bertram (Valyria Tear)
4 //                         All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software
7 // and you may modify it and/or redistribute it under the terms of this license.
8 // See https://www.gnu.org/copyleft/gpl.html for details.
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 /** ****************************************************************************
12 *** \file    shop_buy.cpp
13 *** \author  Tyler Olsen, roots@allacrost.org
14 *** \author  Yohann Ferreira, yohann ferreira orange fr
15 *** \brief   Source file for buy interface of shop mode
16 ***
17 *** \note The contents of this file are near identical to the contents of
18 *** shop_sell.cpp. When making any changes to this file, please look to shop_sell.cpp
19 *** to see if it should have similar changes made.
20 *** ***************************************************************************/
21 
22 #include "shop_buy.h"
23 
24 #include "shop.h"
25 
26 #include "engine/audio/audio.h"
27 #include "engine/input.h"
28 #include "engine/system.h"
29 #include "engine/video/video.h"
30 
31 #include "common/global/global.h"
32 
33 using namespace vt_utils;
34 using namespace vt_audio;
35 using namespace vt_input;
36 using namespace vt_system;
37 using namespace vt_video;
38 using namespace vt_gui;
39 using namespace vt_global;
40 
41 namespace vt_shop
42 {
43 
44 namespace private_shop
45 {
46 
47 // *****************************************************************************
48 // ***** BuyInterface class methods
49 // *****************************************************************************
50 
BuyInterface()51 BuyInterface::BuyInterface() :
52     _view_mode(SHOP_VIEW_MODE_INVALID),
53     _selected_object(nullptr),
54     _buy_deal_types(0),
55     _number_categories(0),
56     _current_category(0)
57 {
58     _category_header.SetStyle(TextStyle("title24"));
59     _category_header.SetText(UTranslate("Category"));
60 
61     _name_header.SetStyle(TextStyle("title24"));
62     _name_header.SetText(UTranslate("Name"));
63 
64     _properties_header.SetDimensions(300.0f, 30.0f, 3, 1, 3, 1);
65     _properties_header.SetOptionAlignment(VIDEO_X_RIGHT, VIDEO_Y_CENTER);
66     _properties_header.SetTextStyle(TextStyle("title24"));
67     _properties_header.SetCursorState(VIDEO_CURSOR_STATE_HIDDEN);
68     _properties_header.AddOption(UTranslate("Price"));
69     _properties_header.AddOption(UTranslate("Stock"));
70     _properties_header.AddOption(UTranslate("Own"));
71 
72     _selected_name.SetStyle(TextStyle("text22"));
73 
74     _selected_properties.SetOwner(ShopMode::CurrentInstance()->GetBottomWindow());
75     _selected_properties.SetPosition(480.0f, 70.0f);
76     _selected_properties.SetDimensions(300.0f, 30.0f, 3, 1, 3, 1);
77     _selected_properties.SetOptionAlignment(VIDEO_X_RIGHT, VIDEO_Y_CENTER);
78     _selected_properties.SetTextStyle(TextStyle("text22"));
79     _selected_properties.SetCursorState(VIDEO_CURSOR_STATE_HIDDEN);
80     _selected_properties.AddOption(ustring());
81     _selected_properties.AddOption(ustring());
82     _selected_properties.AddOption(ustring());
83 }
84 
85 
~BuyInterface()86 BuyInterface::~BuyInterface()
87 {
88     for(uint32_t i = 0; i < _list_displays.size(); i++) {
89         delete _list_displays[i];
90     }
91 }
92 
93 
_UpdateAvailableBuyDealTypes()94 void BuyInterface::_UpdateAvailableBuyDealTypes()
95 {
96     _buy_deal_types = 0;
97 
98     // Determine what types of objects the shop deals in based on the managed object list
99     std::map<uint32_t, ShopObject *>* buy_objects = ShopMode::CurrentInstance()->GetAvailableBuy();
100     for(std::map<uint32_t, ShopObject *>::iterator it = buy_objects->begin(); it != buy_objects->end(); ++it) {
101         vt_global::GLOBAL_OBJECT object_type = it->second->GetObject()->GetObjectType();
102         switch(object_type) {
103         case GLOBAL_OBJECT_ITEM:
104             _buy_deal_types |= DEALS_ITEMS;
105             break;
106         case GLOBAL_OBJECT_WEAPON:
107             _buy_deal_types |= DEALS_WEAPONS;
108             break;
109         case GLOBAL_OBJECT_HEAD_ARMOR:
110             _buy_deal_types |= DEALS_HEAD_ARMOR;
111             break;
112         case GLOBAL_OBJECT_TORSO_ARMOR:
113             _buy_deal_types |= DEALS_TORSO_ARMOR;
114             break;
115         case GLOBAL_OBJECT_ARM_ARMOR:
116             _buy_deal_types |= DEALS_ARM_ARMOR;
117             break;
118         case GLOBAL_OBJECT_LEG_ARMOR:
119             _buy_deal_types |= DEALS_LEG_ARMOR;
120             break;
121         case GLOBAL_OBJECT_SPIRIT:
122             _buy_deal_types |= DEALS_SPIRIT;
123             break;
124         default:
125             IF_PRINT_WARNING(SHOP_DEBUG) << "unknown object type sold in shop: " << object_type << std::endl;
126             break;
127         }
128         // Test whether this is a key item
129         if (it->second->GetObject()->IsKeyItem())
130             _buy_deal_types |= DEALS_KEY_ITEMS;
131     }
132 }
133 
134 
_RefreshItemCategories()135 void BuyInterface::_RefreshItemCategories()
136 {
137     // Clear the data
138     _category_icons.clear();
139     _category_names.clear();
140     ShopMedia *shop_media = ShopMode::CurrentInstance()->Media();
141     std::vector<ustring>* all_category_names = shop_media->GetAllCategoryNames();
142     std::vector<StillImage>* all_category_icons = GlobalManager->Media().GetAllItemCategoryIcons();
143 
144     // Determine which categories are used in this shop and populate the true containers with that data
145     _UpdateAvailableBuyDealTypes();
146 
147     uint8_t bit_x = 0x01; // Used to do a bit-by-bit analysis of the obj_types variable
148     for(uint8_t i = 0; i < GLOBAL_OBJECT_TOTAL; i++, bit_x <<= 1) {
149         // Check whether the type is available by doing a bit-wise comparison
150         if(_buy_deal_types & bit_x) {
151             _category_names.push_back(all_category_names->at(i));
152             _category_icons.push_back(&all_category_icons->at(i));
153         }
154     }
155 
156     // If here is more than one category, add the text/icon for all wares
157     if(_category_names.size() > 1) {
158         _category_names.push_back(all_category_names->at(8));
159         _category_icons.push_back(&all_category_icons->at(8));
160     }
161 
162     _number_categories = _category_names.size();
163 }
164 
165 
Reinitialize()166 void BuyInterface::Reinitialize()
167 {
168     _RefreshItemCategories();
169 
170     // Set the initial category to the last category that was added (this is usually "All Wares")
171     _current_category = _number_categories > 0 ? _number_categories - 1 : 0;
172     // Initialize the category display with the initial category
173     if(_number_categories > 0)
174         _category_display.ChangeCategory(_category_names[_current_category], _category_icons[_current_category]);
175 
176     // Prepare object data containers and determine category index mappings
177     // Containers of object data used to populate the display lists
178     std::vector<std::vector<ShopObject *> > object_data;
179 
180     for(uint32_t i = 0; i < _number_categories; ++i) {
181         object_data.push_back(std::vector<ShopObject *>());
182     }
183 
184     // Holds the index to the _object_data vector where the container for a specific object type is located
185     std::vector<uint32_t> type_index(GLOBAL_OBJECT_TOTAL + 1, 0); // + key items
186     // Used to set the appropriate data in the type_index vector
187     uint32_t next_index = 0;
188     // Used to do a bit-by-bit analysis of the deal_types variable
189     uint8_t bit_x = 0x01;
190 
191     // This loop determines where each type of object should be placed in the object_data container. For example,
192     // if the available categories in the shop are items, weapons, spirits, and all wares, the size of object_data
193     // will be four. When we go to add an object of one of these types into the object_data container, we need
194     // to know the correct index for each type of object. These indeces are stored in the type_index vector. The
195     // size of this vector is the number of object types, so it becomes simple to map each object type to its correct
196     // location in object_data.
197     for(uint8_t i = 0; i < GLOBAL_OBJECT_TOTAL; i++, bit_x <<= 1) {
198         // Check if the type is available by doing a bit-wise comparison
199         if(_buy_deal_types & bit_x) {
200             type_index[i] = next_index++;
201         }
202     }
203 
204     // Populate the object_data containers
205 
206     // Pointer to the container of all objects that are bought/sold/traded in the shop
207     std::map<uint32_t, ShopObject *>* buy_objects = ShopMode::CurrentInstance()->GetAvailableBuy();
208 
209     for(std::map<uint32_t, ShopObject *>::iterator it = buy_objects->begin(); it != buy_objects->end(); ++it) {
210         ShopObject* obj = it->second;
211         switch(obj->GetObject()->GetObjectType()) {
212         case GLOBAL_OBJECT_ITEM:
213             object_data[type_index[0]].push_back(obj);
214             break;
215         case GLOBAL_OBJECT_WEAPON:
216             object_data[type_index[1]].push_back(obj);
217             break;
218         case GLOBAL_OBJECT_HEAD_ARMOR:
219             object_data[type_index[2]].push_back(obj);
220             break;
221         case GLOBAL_OBJECT_TORSO_ARMOR:
222             object_data[type_index[3]].push_back(obj);
223             break;
224         case GLOBAL_OBJECT_ARM_ARMOR:
225             object_data[type_index[4]].push_back(obj);
226             break;
227         case GLOBAL_OBJECT_LEG_ARMOR:
228             object_data[type_index[5]].push_back(obj);
229             break;
230         case GLOBAL_OBJECT_SPIRIT:
231             object_data[type_index[6]].push_back(obj);
232             break;
233         default:
234             IF_PRINT_WARNING(SHOP_DEBUG) << "added object of unknown type: " << obj->GetObject()->GetObjectType() << std::endl;
235             break;
236         }
237 
238         // Test whether this is a key item
239         if (it->second->GetObject()->IsKeyItem())
240             object_data[type_index[7]].push_back(obj);
241 
242         // If there is an "All Wares" category, make sure the object gets added there as well
243         if(_number_categories > 1) {
244             object_data.back().push_back(obj);
245         }
246     }
247 
248     // Create the buy displays using the object data that is now ready
249     for(uint32_t i = 0; i < _list_displays.size(); ++i) {
250         delete _list_displays[i];
251     }
252     _list_displays.clear();
253 
254     for(uint32_t i = 0; i < object_data.size(); ++i) {
255         BuyListDisplay *new_list = new BuyListDisplay();
256         new_list->PopulateList(object_data[i]);
257         _list_displays.push_back(new_list);
258     }
259 
260     if(_number_categories > 0)
261         _selected_object = _list_displays[_current_category]->GetSelectedObject();
262     ChangeViewMode(SHOP_VIEW_MODE_LIST);
263 }
264 
MakeActive()265 void BuyInterface::MakeActive()
266 {
267     Reinitialize();
268 
269     if(_list_displays.empty()) {
270         ShopMode::CurrentInstance()->ChangeState(SHOP_STATE_ROOT);
271         return;
272     }
273 
274     _selected_object = _list_displays[_current_category]->GetSelectedObject();
275     ShopMode::CurrentInstance()->ObjectViewer()->ChangeViewMode(_view_mode);
276     ShopMode::CurrentInstance()->ObjectViewer()->SetSelectedObject(_selected_object);
277     _category_display.ChangeViewMode(_view_mode);
278     _category_display.SetSelectedObject(_selected_object);
279 }
280 
TransactionNotification()281 void BuyInterface::TransactionNotification()
282 {
283     Reinitialize();
284 
285     for(uint32_t i = 0; i < _list_displays.size(); ++i) {
286         _list_displays[i]->ReconstructList();
287         _list_displays[i]->ResetSelection();
288     }
289 
290     _current_category = _number_categories > 0 ? _number_categories - 1 : 0;
291     _view_mode = SHOP_VIEW_MODE_LIST;
292 }
293 
Update()294 void BuyInterface::Update()
295 {
296     ShopMode* shop = ShopMode::CurrentInstance();
297 
298     if(_view_mode == SHOP_VIEW_MODE_LIST && shop->IsInputEnabled()) {
299         if(InputManager->ConfirmPress()) {
300             ChangeViewMode(SHOP_VIEW_MODE_INFO);
301         } else if(InputManager->CancelPress()) {
302             shop->ChangeState(SHOP_STATE_ROOT);
303             GlobalManager->Media().PlaySound("cancel");
304         }
305 
306         // Swap cycles through the object categories
307         else if(InputManager->MenuPress() && (_number_categories > 1)) {
308             if(_ChangeCategory(true))
309                 shop->ObjectViewer()->SetSelectedObject(_selected_object);
310             GlobalManager->Media().PlaySound("confirm");
311         }
312 
313         // Up/down changes the selected object in the current list
314         else if(InputManager->UpPress()) {
315             if(_ChangeSelection(false)) {
316                 shop->ObjectViewer()->SetSelectedObject(_selected_object);
317                 GlobalManager->Media().PlaySound("bump");
318             }
319         } else if(InputManager->DownPress()) {
320             if(_ChangeSelection(true)) {
321                 shop->ObjectViewer()->SetSelectedObject(_selected_object);
322                 GlobalManager->Media().PlaySound("bump");
323             }
324         }
325     } // if (_view_mode == SHOP_VIEW_MODE_LIST)
326 
327     else if(_view_mode == SHOP_VIEW_MODE_INFO && shop->IsInputEnabled()) {
328         if(InputManager->ConfirmPress()) {
329             ChangeViewMode(SHOP_VIEW_MODE_LIST);
330             shop->ChangeState(SHOP_STATE_ROOT);
331             if (shop->CompleteTransaction()) {
332                 GlobalManager->Media().PlaySound("coins");
333             }
334             else {
335                 GlobalManager->Media().PlaySound("cancel");
336             }
337             shop->ClearOrder();
338             shop->ChangeState(SHOP_STATE_SELL); //If the entire sell list is emptied, this somehow helps
339             shop->ChangeState(SHOP_STATE_BUY);
340         } else if(InputManager->CancelPress()) {
341             ChangeViewMode(SHOP_VIEW_MODE_LIST);
342             while(_list_displays[_current_category]->ChangeBuyQuantity(false)) {}
343             GlobalManager->Media().PlaySound("cancel");
344             shop->ClearOrder();
345         }
346 
347         // Left/right change the quantity of the object to buy
348         else if(InputManager->LeftPress()) {
349             if(_list_displays[_current_category]->ChangeBuyQuantity(false)) {
350                 shop->ObjectViewer()->UpdateCountText();
351                 GlobalManager->Media().PlaySound("confirm");
352             } else
353                 GlobalManager->Media().PlaySound("bump");
354         } else if(InputManager->RightPress()) {
355             if(_list_displays[_current_category]->ChangeBuyQuantity(true)) {
356                 shop->ObjectViewer()->UpdateCountText();
357                 GlobalManager->Media().PlaySound("confirm");
358             } else
359                 GlobalManager->Media().PlaySound("bump");
360         }
361     }
362 
363     _category_display.Update();
364     _list_displays[_current_category]->Update();
365     shop->ObjectViewer()->Update();
366 }
367 
Draw()368 void BuyInterface::Draw()
369 {
370     if(_view_mode == SHOP_VIEW_MODE_LIST) {
371         VideoManager->SetDrawFlags(VIDEO_X_CENTER, VIDEO_Y_BOTTOM, 0);
372         VideoManager->Move(200.0f, 210.0f);
373         _category_header.Draw();
374 
375         VideoManager->SetDrawFlags(VIDEO_X_LEFT, 0);
376         VideoManager->MoveRelative(95.0f, 0.0f);
377         _name_header.Draw();
378 
379         _properties_header.Draw();
380 
381         _category_display.Draw();
382         _list_displays[_current_category]->Draw();
383     } else if(_view_mode == SHOP_VIEW_MODE_INFO) {
384         VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_BOTTOM, 0);
385         VideoManager->Move(295.0f, 593.0f);
386         _name_header.Draw();
387         _properties_header.Draw();
388 
389         VideoManager->MoveRelative(0.0f, 50.0f);
390         if (!_selected_icon.GetFilename().empty())
391             _selected_icon.Draw();
392         VideoManager->MoveRelative(30.0f, 0.0f);
393         _selected_name.Draw();
394         _selected_properties.Draw();
395 
396         _category_display.Draw();
397     }
398 
399     ShopMode::CurrentInstance()->ObjectViewer()->Draw();
400 }
401 
ChangeViewMode(SHOP_VIEW_MODE new_mode)402 void BuyInterface::ChangeViewMode(SHOP_VIEW_MODE new_mode)
403 {
404     if(_view_mode == new_mode)
405         return;
406 
407     if(new_mode == SHOP_VIEW_MODE_LIST) {
408         _view_mode = new_mode;
409         ShopMode::CurrentInstance()->ObjectViewer()->ChangeViewMode(_view_mode);
410         _category_display.ChangeViewMode(_view_mode);
411 
412         _properties_header.SetOwner(ShopMode::CurrentInstance()->GetMiddleWindow());
413         _properties_header.SetPosition(480.0f, 10.0f);
414     } else if(new_mode == SHOP_VIEW_MODE_INFO) {
415         _view_mode = new_mode;
416         ShopMode::CurrentInstance()->ObjectViewer()->ChangeViewMode(_view_mode);
417         _category_display.ChangeViewMode(_view_mode);
418         _category_display.SetSelectedObject(_selected_object);
419 
420         _properties_header.SetOwner(ShopMode::CurrentInstance()->GetBottomWindow());
421         _properties_header.SetPosition(480.0f, 15.0f);
422 
423         _selected_name.SetText(_selected_object->GetObject()->GetName());
424         _selected_icon = _selected_object->GetObject()->GetIconImage();
425         _selected_icon.SetDimensions(30.0f, 30.0f);
426         _selected_properties.SetOptionText(0, MakeUnicodeString(NumberToString(_selected_object->GetBuyPrice())));
427         if (_selected_object->IsInfiniteAmount())
428             _selected_properties.SetOptionText(1, MakeUnicodeString("∞"));
429         else
430             _selected_properties.SetOptionText(1, MakeUnicodeString("×" + NumberToString(_selected_object->GetStockCount())));
431         uint32_t own_count = GlobalManager->GetInventoryHandler().HowManyObjectsInInventory(_selected_object->GetObject()->GetID());
432         _selected_properties.SetOptionText(2, MakeUnicodeString("×" + NumberToString(own_count)));
433 
434         // Set the buy count to one, permitting quick one-buy sequences when affordable.
435         if (_list_displays[_current_category]->ChangeBuyQuantity(true, 1))
436             ShopMode::CurrentInstance()->ObjectViewer()->UpdateCountText();
437     } else {
438         IF_PRINT_WARNING(SHOP_DEBUG) << "tried to change to an invalid/unsupported view mode: " << new_mode << std::endl;
439     }
440 }
441 
442 
443 
_ChangeCategory(bool left_or_right)444 bool BuyInterface::_ChangeCategory(bool left_or_right)
445 {
446     if(_number_categories <= 1)
447         return false;
448 
449     if(left_or_right == false) {
450         _current_category = (_current_category == 0) ? (_number_categories - 1) : (_current_category - 1);
451     } else {
452         _current_category = (_current_category >= (_number_categories - 1)) ? 0 : (_current_category + 1);
453     }
454 
455     _category_display.ChangeCategory(_category_names[_current_category], _category_icons[_current_category]);
456 
457     ShopObject *last_obj = _selected_object;
458     _selected_object = _list_displays[_current_category]->GetSelectedObject();
459     if(last_obj == _selected_object)
460         return false;
461     else
462         return true;
463 }
464 
465 
466 
_ChangeSelection(bool up_or_down)467 bool BuyInterface::_ChangeSelection(bool up_or_down)
468 {
469     if(_current_category >= _list_displays.size())
470         return false;
471 
472     BuyListDisplay *selected_list = _list_displays[_current_category];
473 
474     if(!selected_list)
475         return false;
476 
477     if(up_or_down == false)
478         selected_list->InputUp();
479     else
480         selected_list->InputDown();
481 
482     ShopObject *last_obj = _selected_object;
483     _selected_object = _list_displays[_current_category]->GetSelectedObject();
484     if(last_obj == _selected_object)
485         return false;
486     else
487         return true;
488 }
489 
490 // *****************************************************************************
491 // ***** BuyListDisplay class methods
492 // *****************************************************************************
493 
ReconstructList()494 void BuyListDisplay::ReconstructList()
495 {
496     _identify_list.ClearOptions();
497     _property_list.ClearOptions();
498 
499     for(uint32_t i = 0; i < _objects.size(); i++) {
500         ShopObject* obj = _objects[i];
501         // Add an entry with the icon image of the object (scaled down by 4x to 30x30 pixels) followed by the object name
502         if (obj->GetObject()->GetIconImage().GetFilename().empty()) {
503             _identify_list.AddOption(MakeUnicodeString("<30>") + obj->GetObject()->GetName());
504         }
505         else {
506             _identify_list.AddOption(MakeUnicodeString("<" + obj->GetObject()->GetIconImage().GetFilename() + "><30>")
507                                      + obj->GetObject()->GetName());
508             _identify_list.GetEmbeddedImage(i)->SetDimensions(30.0f, 30.0f);
509         }
510 
511         // Add an option for each object property in the order of: price, stock, and number owned.
512         _property_list.AddOption(MakeUnicodeString(NumberToString(obj->GetBuyPrice())));
513         if (obj->IsInfiniteAmount())
514             _property_list.AddOption(MakeUnicodeString("∞"));
515         else
516             _property_list.AddOption(MakeUnicodeString("×" + NumberToString(obj->GetStockCount())));
517         uint32_t own_count = GlobalManager->GetInventoryHandler().HowManyObjectsInInventory(obj->GetObject()->GetID());
518         _property_list.AddOption(MakeUnicodeString("×" + NumberToString(own_count)));
519     }
520 
521     if(_objects.empty() == false) {
522         _identify_list.SetSelection(0);
523         _property_list.SetSelection(0);
524     }
525 }
526 
527 
ChangeBuyQuantity(bool more,uint32_t amount)528 bool BuyListDisplay::ChangeBuyQuantity(bool more, uint32_t amount)
529 {
530     ShopObject* obj = GetSelectedObject();
531     if(obj == nullptr) {
532         IF_PRINT_WARNING(SHOP_DEBUG) << "function could not perform operation because list was empty" << std::endl;
533         return false;
534     }
535 
536     // Holds the amount that the quantity will actually increase or decrease by. May be less than the
537     // amount requested if there is an limitation such as shop stock or available funds
538     uint32_t change_amount = amount;
539 
540     if(more == false) { // Decrement
541         // Ensure that at least one count of this object is already marked for purchase
542         if(obj->GetBuyCount() == 0) {
543             return false;
544         }
545 
546         // Determine if there's a sufficient count selected to decrement by the desire amount. If not, return as many as possible
547         if(amount > obj->GetBuyCount()) {
548             change_amount = obj->GetBuyCount();
549         }
550 
551         obj->DecrementBuyCount(change_amount);
552         ShopMode::CurrentInstance()->UpdateFinances(obj->GetBuyPrice() * change_amount);
553         return true;
554     }
555 
556     // increment
557 
558     // Make sure that there is at least one more object in stock and the player has enough funds to purchase it
559     if((!obj->IsInfiniteAmount() && obj->GetBuyCount() >= obj->GetStockCount()) ||
560             (obj->GetBuyPrice() > ShopMode::CurrentInstance()->GetTotalRemaining())) {
561         return false;
562     }
563 
564     // Determine if there's enough of the object in stock to buy. If not, buy as many left as possible
565     if(!obj->IsInfiniteAmount() && (obj->GetStockCount() - obj->GetBuyCount()) < change_amount) {
566         change_amount = obj->GetStockCount() - obj->GetBuyCount();
567     }
568 
569     // Determine how many of the possible amount to buy the player can actually afford
570     int32_t total_cost = change_amount * obj->GetBuyPrice();
571     int32_t total_remaining = static_cast<int32_t>(ShopMode::CurrentInstance()->GetTotalRemaining());
572     while(total_cost > total_remaining) {
573         --change_amount;
574         total_cost -= obj->GetBuyPrice();
575     }
576 
577     obj->IncrementBuyCount(change_amount);
578     ShopMode::CurrentInstance()->UpdateFinances(-obj->GetBuyPrice() * change_amount);
579     return true;
580 } // bool BuyListDisplay::ChangeBuyQuantity(bool more, uint32_t amount)
581 
582 } // namespace private_shop
583 
584 } // namespace vt_shop
585