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_utils.cpp
13 *** \author  Tyler Olsen, roots@allacrost.org
14 *** \author  Yohann Ferreira, yohann ferreira orange fr
15 *** \brief   Source file for shop mode utility code.
16 ***
17 *** This file contains utility code that is shared among the various shop mode
18 *** classes.
19 *** ***************************************************************************/
20 
21 #include "shop_utils.h"
22 
23 #include "engine/video/video.h"
24 
25 #include "common/global/global.h"
26 
27 #include "shop.h"
28 
29 using namespace vt_utils;
30 using namespace vt_system;
31 using namespace vt_video;
32 using namespace vt_gui;
33 
34 using namespace vt_global;
35 
36 namespace vt_shop
37 {
38 
39 namespace private_shop
40 {
41 
42 // *****************************************************************************
43 // ***** ShopObject class methods
44 // *****************************************************************************
45 
ShopObject(const std::shared_ptr<vt_global::GlobalObject> & object)46 ShopObject::ShopObject(const std::shared_ptr<vt_global::GlobalObject>& object) :
47     _object(object),
48     _buy_price(0),
49     _sell_price(0),
50     _own_count(0),
51     _stock_count(0),
52     _infinite_buy_amount(false),
53     _buy_count(0),
54     _sell_count(0),
55     _trade_count(0)
56 {
57     assert(_object != nullptr);
58 
59     // Init the trading price
60     _trade_price = _object->GetTradingPrice();
61 }
62 
DetermineShopObjectType(GLOBAL_OBJECT global_type)63 SHOP_OBJECT ShopObject::DetermineShopObjectType(GLOBAL_OBJECT global_type)
64 {
65     SHOP_OBJECT shop_type;
66 
67     switch(global_type) {
68     case GLOBAL_OBJECT_ITEM:
69         shop_type = SHOP_OBJECT_ITEM;
70         break;
71     case GLOBAL_OBJECT_WEAPON:
72     case GLOBAL_OBJECT_HEAD_ARMOR:
73     case GLOBAL_OBJECT_TORSO_ARMOR:
74     case GLOBAL_OBJECT_ARM_ARMOR:
75     case GLOBAL_OBJECT_LEG_ARMOR:
76         shop_type = SHOP_OBJECT_EQUIPMENT;
77         break;
78     case GLOBAL_OBJECT_SPIRIT:
79         shop_type = SHOP_OBJECT_SPIRIT;
80         break;
81     case GLOBAL_OBJECT_INVALID:
82     case GLOBAL_OBJECT_TOTAL:
83     default:
84         IF_PRINT_WARNING(SHOP_DEBUG) << "no conversion type existed for global object: " << global_type << std::endl;
85         shop_type = SHOP_OBJECT_INVALID;
86         break;
87     }
88 
89     return shop_type;
90 }
91 
92 
93 
DetermineShopObjectType()94 SHOP_OBJECT ShopObject::DetermineShopObjectType()
95 {
96     return DetermineShopObjectType(GetObject()->GetObjectType());
97 }
98 
99 
100 
SetPricing(SHOP_PRICE_LEVEL buy_level,SHOP_PRICE_LEVEL sell_level)101 void ShopObject::SetPricing(SHOP_PRICE_LEVEL buy_level,
102                             SHOP_PRICE_LEVEL sell_level)
103 {
104     _buy_price = _object->GetPrice();
105     _sell_price = _object->GetPrice();
106     _trade_price = _object->GetTradingPrice();
107     _trade_conditions = _object->GetTradeConditions();
108 
109     switch(buy_level) {
110     case SHOP_PRICE_VERY_GOOD:
111         _buy_price *= BUY_PRICE_VERY_GOOD;
112         break;
113     case SHOP_PRICE_GOOD:
114         _buy_price *= BUY_PRICE_GOOD;
115         break;
116     case SHOP_PRICE_STANDARD:
117         _buy_price *= BUY_PRICE_STANDARD;
118         break;
119     case SHOP_PRICE_POOR:
120         _buy_price *= BUY_PRICE_POOR;
121         break;
122     case SHOP_PRICE_VERY_POOR:
123         _buy_price *= BUY_PRICE_VERY_POOR;
124         break;
125     default:
126         IF_PRINT_WARNING(SHOP_DEBUG) << "unknown buy level: " << buy_level << std::endl;
127     }
128 
129     switch(sell_level) {
130     case SHOP_PRICE_VERY_GOOD:
131         _sell_price *= SELL_PRICE_VERY_GOOD;
132         break;
133     case SHOP_PRICE_GOOD:
134         _sell_price *= SELL_PRICE_GOOD;
135         break;
136     case SHOP_PRICE_STANDARD:
137         _sell_price *= SELL_PRICE_STANDARD;
138         break;
139     case SHOP_PRICE_POOR:
140         _sell_price *= SELL_PRICE_POOR;
141         break;
142     case SHOP_PRICE_VERY_POOR:
143         _sell_price *= SELL_PRICE_VERY_POOR;
144         break;
145     default:
146         IF_PRINT_WARNING(SHOP_DEBUG) << "unknown sell level: " << sell_level << std::endl;
147     }
148 }
149 
150 
151 
IncrementOwnCount(uint32_t inc)152 void ShopObject::IncrementOwnCount(uint32_t inc)
153 {
154     _own_count += inc;
155 }
156 
157 
158 
IncrementStockCount(uint32_t inc)159 void ShopObject::IncrementStockCount(uint32_t inc)
160 {
161     _stock_count += inc;
162 }
163 
164 
165 
IncrementBuyCount(uint32_t inc)166 void ShopObject::IncrementBuyCount(uint32_t inc)
167 {
168     uint32_t old_count = _buy_count;
169     if(inc == 0) {
170         IF_PRINT_WARNING(SHOP_DEBUG) << "function received an argument with a value of zero" << std::endl;
171         return;
172     }
173 
174     _buy_count += inc;
175     if(!IsInfiniteAmount() && _stock_count < _buy_count) {
176         IF_PRINT_WARNING(SHOP_DEBUG) << "incremented buy count beyond the amount available in stock" << std::endl;
177         _buy_count = old_count;
178         return;
179     }
180     if(old_count == 0) {
181         ShopMode::CurrentInstance()->AddObjectToBuyList(this);
182     }
183 }
184 
185 
186 
IncrementSellCount(uint32_t inc)187 void ShopObject::IncrementSellCount(uint32_t inc)
188 {
189     uint32_t old_count = _sell_count;
190     if(inc == 0) {
191         IF_PRINT_WARNING(SHOP_DEBUG) << "function received an argument with a value of zero" << std::endl;
192         return;
193     }
194 
195     _sell_count += inc;
196     if(_sell_count > _own_count) {
197         IF_PRINT_WARNING(SHOP_DEBUG) << "incremented sell count beyond the amount available to be sold" << std::endl;
198         _sell_count -= inc;
199         return;
200     }
201     if(old_count == 0) {
202         ShopMode::CurrentInstance()->AddObjectToSellList(this);
203     }
204 }
205 
206 
207 
IncrementTradeCount(uint32_t inc)208 void ShopObject::IncrementTradeCount(uint32_t inc)
209 {
210     uint32_t old_count = _trade_count;
211     if(inc == 0) {
212         IF_PRINT_WARNING(SHOP_DEBUG) << "function received an argument with a value of zero" << std::endl;
213         return;
214     }
215 
216     _trade_count += inc;
217     if(!IsInfiniteAmount() && _trade_count > _stock_count) {
218         IF_PRINT_WARNING(SHOP_DEBUG) << "incremented sell count beyond the amount available to be sold" << std::endl;
219         _trade_count -= inc;
220         return;
221     }
222     if(old_count == 0) {
223         ShopMode::CurrentInstance()->AddObjectToTradeList(this);
224     }
225 }
226 
227 
228 
DecrementOwnCount(uint32_t dec)229 void ShopObject::DecrementOwnCount(uint32_t dec)
230 {
231     if(dec > _own_count) {
232         IF_PRINT_WARNING(SHOP_DEBUG) << "attempted to decrement own count below zero" << std::endl;
233         return;
234     }
235 
236     _own_count -= dec;
237 
238     if(_own_count < _sell_count) {
239         IF_PRINT_WARNING(SHOP_DEBUG) << "decremented own count below that of the sell count" << std::endl;
240         _own_count += dec;
241     }
242 }
243 
244 
245 
DecrementStockCount(uint32_t dec)246 void ShopObject::DecrementStockCount(uint32_t dec)
247 {
248     // Doesn't apply when there is an infinity of such items.
249     if (IsInfiniteAmount())
250         return;
251 
252     if(dec > _stock_count) {
253         IF_PRINT_WARNING(SHOP_DEBUG) << "attempted to decrement stock count below zero" << std::endl;
254         return;
255     }
256 
257     _stock_count -= dec;
258 
259     if(_stock_count < _buy_count) {
260         IF_PRINT_WARNING(SHOP_DEBUG) << "decremented stock count below that of the buy count" << std::endl;
261         _stock_count += dec;
262     }
263 }
264 
265 
266 
DecrementBuyCount(uint32_t dec)267 void ShopObject::DecrementBuyCount(uint32_t dec)
268 {
269     if(dec == 0) {
270         IF_PRINT_WARNING(SHOP_DEBUG) << "function received an argument with a value of zero" << std::endl;
271         return;
272     }
273 
274     if(dec > _buy_count) {
275         IF_PRINT_WARNING(SHOP_DEBUG) << "attempted to decrement buy count below zero" << std::endl;
276         return;
277     }
278 
279     _buy_count -= dec;
280     if(_buy_count == 0) {
281         ShopMode::CurrentInstance()->RemoveObjectFromBuyList(this);
282     }
283 }
284 
285 
286 
DecrementSellCount(uint32_t dec)287 void ShopObject::DecrementSellCount(uint32_t dec)
288 {
289     if(dec == 0) {
290         IF_PRINT_WARNING(SHOP_DEBUG) << "function received an argument with a value of zero" << std::endl;
291         return;
292     }
293 
294     if(dec > _sell_count) {
295         IF_PRINT_WARNING(SHOP_DEBUG) << "attempted to decrement sell count below zero" << std::endl;
296         return;
297     }
298 
299     _sell_count -= dec;
300     if(_sell_count == 0) {
301         ShopMode::CurrentInstance()->RemoveObjectFromSellList(this);
302     }
303 }
304 
305 
DecrementTradeCount(uint32_t dec)306 void ShopObject::DecrementTradeCount(uint32_t dec)
307 {
308     if(dec == 0) {
309         IF_PRINT_WARNING(SHOP_DEBUG) << "function received an argument with a value of zero" << std::endl;
310         return;
311     }
312 
313     if(dec > _trade_count) {
314         IF_PRINT_WARNING(SHOP_DEBUG) << "attempted to decrement sell count below zero" << std::endl;
315         return;
316     }
317 
318     _trade_count -= dec;
319     if(_trade_count == 0) {
320         ShopMode::CurrentInstance()->RemoveObjectFromTradeList(this);
321     }
322 }
323 
324 // *****************************************************************************
325 // ***** ObjectCategoryDisplay class methods
326 // *****************************************************************************
327 
328 // The time it takes to transition graphics to a new category (in milliseconds)
329 const uint32_t TRANSITION_TIME_ICON = 500;
330 // Represents the display speed when transitioning to new category text
331 const float TRANSITION_TIME_TEXT = 25.0f;
332 
ObjectCategoryDisplay()333 ObjectCategoryDisplay::ObjectCategoryDisplay() :
334     _view_mode(SHOP_VIEW_MODE_LIST),
335     _selected_object(nullptr),
336     _current_icon(nullptr),
337     _last_icon(nullptr),
338     _object_icon(nullptr)
339 {
340     _name_text.SetStyle(TextStyle("text22"));
341 
342     _name_textbox.SetOwner(ShopMode::CurrentInstance()->GetMiddleWindow());
343     _name_textbox.SetPosition(25.0f, 225.0f);
344     _name_textbox.SetDimensions(125.0f, 30.0f);
345     _name_textbox.SetTextStyle(TextStyle("text22"));
346     _name_textbox.SetDisplayMode(VIDEO_TEXT_FADECHAR);
347     _name_textbox.SetDisplaySpeed(TRANSITION_TIME_TEXT);
348     _name_textbox.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
349     // TODO: Alignment should be VIDEO_X_CENTER, but a bug is preventing it from working correctly right now
350     _name_textbox.SetTextAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
351 
352     _transition_timer.Initialize(TRANSITION_TIME_ICON, SYSTEM_TIMER_NO_LOOPS);
353     _transition_timer.EnableAutoUpdate(ShopMode::CurrentInstance());
354 }
355 
356 
357 
~ObjectCategoryDisplay()358 ObjectCategoryDisplay::~ObjectCategoryDisplay()
359 {
360     _selected_object = nullptr;
361     _current_icon = nullptr;
362     _last_icon = nullptr;
363     _object_icon = nullptr;
364 }
365 
366 
367 
Update()368 void ObjectCategoryDisplay::Update()
369 {
370     _name_textbox.Update();
371 }
372 
373 
374 
Draw()375 void ObjectCategoryDisplay::Draw()
376 {
377     VideoManager->SetDrawFlags(VIDEO_X_CENTER, VIDEO_Y_CENTER, 0);
378 
379     if(_view_mode == SHOP_VIEW_MODE_LIST) {
380         VideoManager->Move(200.0f, 358.0f);
381 
382         if(_transition_timer.IsRunning()) {
383             // Alpha ranges from 0.0f at timer start to 1.0f at end
384             float alpha = static_cast<float>(_transition_timer.GetTimeExpired()) / static_cast<float>(TRANSITION_TIME_ICON);
385 
386             if(_last_icon != nullptr)
387                 _last_icon->Draw(Color(1.0f, 1.0f, 1.0f, 1.0f - alpha));
388             if(_current_icon != nullptr)
389                 _current_icon->Draw(Color(1.0f, 1.0f, 1.0f, alpha));
390         } else if(_current_icon != nullptr) {
391             _current_icon->Draw();
392         }
393         _name_textbox.Draw();
394     } else if((_view_mode == SHOP_VIEW_MODE_INFO) && (_selected_object != nullptr)) {
395         VideoManager->Move(200.0f, 603.0f);
396         _object_icon->Draw();
397         VideoManager->MoveRelative(0.0f, 45.0f);
398         _name_text.Draw();
399     }
400 }
401 
402 
403 
ChangeViewMode(SHOP_VIEW_MODE new_mode)404 void ObjectCategoryDisplay::ChangeViewMode(SHOP_VIEW_MODE new_mode)
405 {
406     if(_view_mode == new_mode) {
407         return;
408     }
409 
410     if(new_mode == SHOP_VIEW_MODE_LIST) {
411         _view_mode = new_mode;
412         _transition_timer.Finish();
413     } else if(new_mode == SHOP_VIEW_MODE_INFO) {
414         _view_mode = new_mode;
415     } else {
416         IF_PRINT_WARNING(SHOP_DEBUG) << "invalid/unknown view mode requested: " << new_mode << std::endl;
417         return;
418     }
419 }
420 
421 
422 
SetSelectedObject(ShopObject * shop_object)423 void ObjectCategoryDisplay::SetSelectedObject(ShopObject *shop_object)
424 {
425     if(_selected_object == shop_object) {
426         return;
427     }
428 
429     _selected_object = shop_object;
430 
431     if(_selected_object == nullptr) {
432         _object_icon = nullptr;
433         _name_text.SetText("");
434         return;
435     }
436     GLOBAL_OBJECT object_type = _selected_object->GetObject()->GetObjectType();
437     _name_text.SetText(*(ShopMode::CurrentInstance()->Media()->GetCategoryName(object_type)));
438     _object_icon = GlobalManager->Media().GetItemCategoryIcon(object_type);
439 }
440 
441 
442 
ChangeCategory(ustring & name,const StillImage * icon)443 void ObjectCategoryDisplay::ChangeCategory(ustring &name, const StillImage *icon)
444 {
445     if(icon == nullptr) {
446         IF_PRINT_WARNING(SHOP_DEBUG) << "function was passed a nullptr pointer argument" << std::endl;
447     }
448 
449     _name_textbox.SetDisplayText(name);
450 
451     _last_icon = _current_icon;
452     _current_icon = icon;
453 
454     if(_view_mode == SHOP_VIEW_MODE_LIST) {
455         _transition_timer.Reset();
456         _transition_timer.Run();
457     }
458 }
459 
460 // *****************************************************************************
461 // ***** ObjectListDisplay class methods
462 // *****************************************************************************
463 
ObjectListDisplay()464 ObjectListDisplay::ObjectListDisplay()
465 {
466     _identify_list.SetOwner(ShopMode::CurrentInstance()->GetMiddleWindow());
467     _identify_list.SetPosition(180.0f, 70.0f);
468     _identify_list.SetDimensions(300.0f, 300.0f, 1, 255, 1, 8);
469     _identify_list.SetOptionAlignment(VIDEO_X_LEFT, VIDEO_Y_CENTER);
470     _identify_list.SetTextStyle(TextStyle("text22"));
471     _identify_list.SetCursorState(VIDEO_CURSOR_STATE_VISIBLE);
472     _identify_list.SetSelectMode(VIDEO_SELECT_SINGLE);
473     _identify_list.SetCursorOffset(-50.0f, -20.0f);
474     _identify_list.SetHorizontalWrapMode(VIDEO_WRAP_MODE_NONE);
475     _identify_list.SetVerticalWrapMode(VIDEO_WRAP_MODE_STRAIGHT);
476 
477     _property_list.SetOwner(ShopMode::CurrentInstance()->GetMiddleWindow());
478     _property_list.SetPosition(480.0f, 70.0f);
479     if(ShopMode::CurrentInstance()->GetState() == SHOP_STATE_SELL) {
480         _property_list.SetDimensions(300.0f, 300.0f, 2, 255, 2, 8);
481     } else {
482         _property_list.SetDimensions(300.0f, 300.0f, 3, 255, 3, 8);
483     }
484     _property_list.SetOptionAlignment(VIDEO_X_RIGHT, VIDEO_Y_CENTER);
485     _property_list.SetTextStyle(TextStyle("text22"));
486     _property_list.SetCursorState(VIDEO_CURSOR_STATE_HIDDEN);
487     _property_list.SetHorizontalWrapMode(VIDEO_WRAP_MODE_NONE);
488     _property_list.SetVerticalWrapMode(VIDEO_WRAP_MODE_STRAIGHT);
489 }
490 
491 
492 
Clear()493 void ObjectListDisplay::Clear()
494 {
495     _objects.clear();
496     _identify_list.ClearOptions();
497     _property_list.ClearOptions();
498 }
499 
PopulateList(const std::vector<ShopObject * > & objects)500 void ObjectListDisplay::PopulateList(const std::vector<ShopObject *>& objects)
501 {
502     _objects = objects;
503     ReconstructList();
504 }
505 
GetSelectedObject()506 ShopObject *ObjectListDisplay::GetSelectedObject()
507 {
508     if(IsListEmpty())
509         return nullptr;
510 
511     if(static_cast<uint32_t>(_identify_list.GetSelection()) >= _objects.size()) {
512         IF_PRINT_WARNING(SHOP_DEBUG) << "current selection index exceeds available objects: " << _identify_list.GetSelection() << std::endl;
513         return nullptr;
514     }
515 
516     return _objects[_identify_list.GetSelection()];
517 }
518 
519 
520 
ResetSelection()521 void ObjectListDisplay::ResetSelection()
522 {
523     if(IsListEmpty() == false) {
524         _identify_list.SetSelection(0);
525         _property_list.SetSelection(0);
526     }
527 }
528 
529 
530 
GetCurrentSelection()531 uint32_t ObjectListDisplay::GetCurrentSelection()
532 {
533     if(IsListEmpty())
534         return 0;
535     return static_cast<uint32_t>(_identify_list.GetSelection());
536 }
537 
538 
539 
InputUp()540 void ObjectListDisplay::InputUp()
541 {
542     _identify_list.InputUp();
543     _property_list.InputUp();
544 }
545 
546 
547 
InputDown()548 void ObjectListDisplay::InputDown()
549 {
550     _identify_list.InputDown();
551     _property_list.InputDown();
552 }
553 
554 
555 
Update()556 void ObjectListDisplay::Update()
557 {
558     _identify_list.Update();
559     _property_list.Update();
560 }
561 
562 
563 
Draw()564 void ObjectListDisplay::Draw()
565 {
566     _identify_list.Draw();
567     _property_list.Draw();
568 }
569 
570 } // namespace private_shop
571 
572 } // namespace vt_shop
573