1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2010 by The Allacrost Project
3 // All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ///////////////////////////////////////////////////////////////////////////////
9
10 /** ****************************************************************************
11 *** \file shop.h
12 *** \author Tyler Olsen, roots@allacrost.org
13 *** \brief Source file for shop mode interface
14 ***
15 *** This code provides an interface for the user to purchase wares from a
16 *** merchant. This mode is usually entered from a map after discussing with a
17 *** shop keeper.
18 *** ***************************************************************************/
19
20 #include <iostream>
21
22 #include "defs.h"
23 #include "utils.h"
24
25 #include "audio.h"
26 #include "video.h"
27 #include "input.h"
28 #include "system.h"
29
30 #include "global.h"
31
32 #include "mode_manager.h"
33 #include "pause.h"
34
35 #include "shop.h"
36 #include "shop_root.h"
37 #include "shop_buy.h"
38 #include "shop_sell.h"
39 #include "shop_trade.h"
40 #include "shop_confirm.h"
41 #include "shop_leave.h"
42
43 using namespace std;
44 using namespace hoa_utils;
45 using namespace hoa_audio;
46 using namespace hoa_video;
47 using namespace hoa_gui;
48 using namespace hoa_input;
49 using namespace hoa_system;
50 using namespace hoa_global;
51 using namespace hoa_mode_manager;
52 using namespace hoa_shop::private_shop;
53 using namespace hoa_pause;
54
55 namespace hoa_shop {
56
57 bool SHOP_DEBUG = false;
58 // Initialize static class variable
59 ShopMode* ShopMode::_current_instance = NULL;
60
61 namespace private_shop {
62
63 // *****************************************************************************
64 // ***** ShopMedia class methods
65 // *****************************************************************************
66
ShopMedia()67 ShopMedia::ShopMedia() {
68 if (_drunes_icon.Load("img/icons/drunes.png") == false)
69 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load drunes icon image" << endl;
70
71 if (_star_icon.Load("img/menus/star.png") == false) {
72 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load star icon image" << endl;
73 }
74
75 if (_check_icon.Load("img/menus/green_check.png") == false)
76 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load check icon image" << endl;
77
78 if (_x_icon.Load("img/menus/red_x.png") == false)
79 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load x icon image" << endl;
80
81 if (_socket_icon.Load("img/menus/socket.png") == false)
82 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load socket icon image" << endl;
83
84 if (_socket_icon.Load("img/menus/socket.png") == false)
85 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load socket icon image" << endl;
86
87 if (_equip_icon.Load("img/menus/equip.png") == false)
88 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load equip icon image" << endl;
89
90 if (ImageDescriptor::LoadMultiImageFromElementGrid(_elemental_icons, "img/icons/effects/elemental.png", 8, 9) == false) {
91 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load elemental icon images" << endl;
92 return;
93 }
94
95 _sounds["confirm"] = new SoundDescriptor();
96 _sounds["cancel"] = new SoundDescriptor();
97 _sounds["coins"] = new SoundDescriptor();
98 _sounds["bump"] = new SoundDescriptor();
99
100 uint32 sound_load_failures = 0;
101 if (_sounds["confirm"]->LoadAudio("snd/confirm.wav") == false)
102 sound_load_failures++;
103 if (_sounds["cancel"]->LoadAudio("snd/cancel.wav") == false)
104 sound_load_failures++;
105 if (_sounds["coins"]->LoadAudio("snd/coins.wav") == false)
106 sound_load_failures++;
107 if (_sounds["bump"]->LoadAudio("snd/bump.wav") == false)
108 sound_load_failures++;
109
110 if (sound_load_failures > 0) {
111 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load " << sound_load_failures << " sounds needed by shop mode" << endl;
112 }
113 }
114
115
116
~ShopMedia()117 ShopMedia::~ShopMedia() {
118 for (map<string, SoundDescriptor*>::iterator i = _sounds.begin(); i != _sounds.end(); i++)
119 delete i->second;
120 _sounds.clear();
121 }
122
123
124
Initialize()125 void ShopMedia::Initialize() {
126 _all_category_names.push_back(UTranslate("Items"));
127 _all_category_names.push_back(UTranslate("Weapons"));
128 _all_category_names.push_back(UTranslate("Head Armor"));
129 _all_category_names.push_back(UTranslate("Torso Armor"));
130 _all_category_names.push_back(UTranslate("Arm Armor"));
131 _all_category_names.push_back(UTranslate("Leg Armor"));
132 _all_category_names.push_back(UTranslate("Shards"));
133 _all_category_names.push_back(UTranslate("Key Items"));
134 _all_category_names.push_back(UTranslate("All Wares"));
135
136 if (ImageDescriptor::LoadMultiImageFromElementGrid(_all_category_icons, "img/icons/object_category_icons.png", 3, 4) == false) {
137 IF_PRINT_WARNING(SHOP_DEBUG) << "failed to load object category icon images" << endl;
138 return;
139 }
140 // The last three images in this multi image are blank, so they are removed
141 _all_category_icons.pop_back();
142 _all_category_icons.pop_back();
143 _all_category_icons.pop_back();
144
145 // Determine which categories are used in this shop and populate the true containers with that data
146 uint8 deal_types = ShopMode::CurrentInstance()->GetDealTypes();
147 uint8 bit_x = 0x01; // Used to do a bit-by-bit analysis of the obj_types variable
148 for (uint8 i = 0; i < GLOBAL_OBJECT_TOTAL; i++, bit_x <<= 1) {
149 // Check if the type is available by doing a bit-wise comparison
150 if (deal_types & bit_x) {
151 _sale_category_names.push_back(_all_category_names[i]);
152 _sale_category_icons.push_back(_all_category_icons[i]);
153 }
154 }
155
156 // If here is more than one category, add the text/icon for all wares
157 if (_sale_category_names.size() > 1) {
158 _sale_category_names.push_back(_all_category_names[8]);
159 _sale_category_icons.push_back(_all_category_icons[8]);
160 }
161
162 // Grab the sprite frames for all characters in the active party
163 vector<GlobalCharacter*>* characters = GlobalManager->GetCharacterOrder();
164 for (uint32 i = 0; i < characters->size(); i++) {
165 _character_sprites.push_back(characters->at(i)->GetStandardSpriteFrames()->at(0));
166 }
167 }
168
169
170
GetCategoryName(GLOBAL_OBJECT object_type)171 ustring* ShopMedia::GetCategoryName(GLOBAL_OBJECT object_type) {
172 uint32 index = 0;
173
174 switch (object_type) {
175 case GLOBAL_OBJECT_ITEM:
176 index = 0;
177 break;
178 case GLOBAL_OBJECT_WEAPON:
179 index = 1;
180 break;
181 case GLOBAL_OBJECT_HEAD_ARMOR:
182 index = 2;
183 break;
184 case GLOBAL_OBJECT_TORSO_ARMOR:
185 index = 3;
186 break;
187 case GLOBAL_OBJECT_ARM_ARMOR:
188 index = 4;
189 break;
190 case GLOBAL_OBJECT_LEG_ARMOR:
191 index = 5;
192 break;
193 case GLOBAL_OBJECT_SHARD:
194 index = 6;
195 break;
196 case GLOBAL_OBJECT_KEY_ITEM:
197 index = 7;
198 break;
199 case GLOBAL_OBJECT_TOTAL:
200 index = 8;
201 break;
202 default:
203 return NULL;
204 }
205
206 return &(_all_category_names[index]);
207 }
208
209
210
GetCategoryIcon(GLOBAL_OBJECT object_type)211 StillImage* ShopMedia::GetCategoryIcon(GLOBAL_OBJECT object_type) {
212 uint32 index = 0;
213
214 switch (object_type) {
215 case GLOBAL_OBJECT_ITEM:
216 index = 0;
217 break;
218 case GLOBAL_OBJECT_WEAPON:
219 index = 1;
220 break;
221 case GLOBAL_OBJECT_HEAD_ARMOR:
222 index = 2;
223 break;
224 case GLOBAL_OBJECT_TORSO_ARMOR:
225 index = 3;
226 break;
227 case GLOBAL_OBJECT_ARM_ARMOR:
228 index = 4;
229 break;
230 case GLOBAL_OBJECT_LEG_ARMOR:
231 index = 5;
232 break;
233 case GLOBAL_OBJECT_SHARD:
234 index = 6;
235 break;
236 case GLOBAL_OBJECT_KEY_ITEM:
237 index = 7;
238 break;
239 case GLOBAL_OBJECT_TOTAL:
240 index = 8;
241 break;
242 default:
243 return NULL;
244 }
245
246 return &(_all_category_icons[index]);
247 }
248
249
250
GetElementalIcon(GLOBAL_ELEMENTAL element_type,GLOBAL_INTENSITY intensity)251 StillImage* ShopMedia::GetElementalIcon(GLOBAL_ELEMENTAL element_type, GLOBAL_INTENSITY intensity) {
252 const uint32 NUMBER_INTENSTIY_LEVELS = 9;
253
254 // Row/col coordinates for where the specific icon can be found in the multi image array
255 uint32 row = 0, col = 0;
256
257 // Elemental type determines the icon's row
258 switch (element_type) {
259 case GLOBAL_ELEMENTAL_FIRE:
260 row = 0;
261 break;
262 case GLOBAL_ELEMENTAL_WATER:
263 row = 1;
264 break;
265 case GLOBAL_ELEMENTAL_VOLT:
266 row = 2;
267 break;
268 case GLOBAL_ELEMENTAL_EARTH:
269 row = 3;
270 break;
271 case GLOBAL_ELEMENTAL_SLICING:
272 row = 4;
273 break;
274 case GLOBAL_ELEMENTAL_SMASHING:
275 row = 5;
276 break;
277 case GLOBAL_ELEMENTAL_MAULING:
278 row = 6;
279 break;
280 case GLOBAL_ELEMENTAL_PIERCING:
281 row = 7;
282 break;
283 default:
284 IF_PRINT_WARNING(SHOP_DEBUG) << "invalid elemental type: " << element_type << endl;
285 return NULL;
286 }
287
288 // Intensity determines the icon's column
289 switch (intensity) {
290 case GLOBAL_INTENSITY_POS_EXTREME:
291 col = 0;
292 break;
293 case GLOBAL_INTENSITY_POS_GREATER:
294 col = 1;
295 break;
296 case GLOBAL_INTENSITY_POS_MODERATE:
297 col = 2;
298 break;
299 case GLOBAL_INTENSITY_POS_LESSER:
300 col = 3;
301 break;
302 case GLOBAL_INTENSITY_NEUTRAL:
303 col = 4;
304 break;
305 case GLOBAL_INTENSITY_NEG_LESSER:
306 col = 5;
307 break;
308 case GLOBAL_INTENSITY_NEG_MODERATE:
309 col = 6;
310 break;
311 case GLOBAL_INTENSITY_NEG_GREATER:
312 col = 7;
313 break;
314 case GLOBAL_INTENSITY_NEG_EXTREME:
315 col = 8;
316 break;
317 default:
318 IF_PRINT_WARNING(SHOP_DEBUG) << "invalid intensity level: " << intensity << endl;
319 return NULL;
320 }
321
322 return &(_elemental_icons[(row * NUMBER_INTENSTIY_LEVELS) + col]);
323 }
324
325
326
GetStatusIcon(GLOBAL_STATUS status_type,GLOBAL_INTENSITY intensity)327 StillImage* GetStatusIcon(GLOBAL_STATUS status_type, GLOBAL_INTENSITY intensity) {
328 // TODO: implement this function once status effects are ready
329 return NULL;
330 }
331
332
333
GetSound(string identifier)334 SoundDescriptor* ShopMedia::GetSound(string identifier) {
335 map<string, SoundDescriptor*>::iterator sound = _sounds.find(identifier);
336 if (sound != _sounds.end()) {
337 return sound->second;
338 }
339 else {
340 return NULL;
341 }
342 }
343
344 // *****************************************************************************
345 // ***** ShopObjectViewer class methods
346 // *****************************************************************************
347
ShopObjectViewer()348 ShopObjectViewer::ShopObjectViewer() :
349 _view_mode(SHOP_VIEW_MODE_LIST),
350 _selected_object(NULL),
351 _object_type(SHOP_OBJECT_INVALID),
352 _map_usable(false),
353 _battle_usable(false),
354 _target_type_index(0)
355 {
356 // Initialize all properties of class members that we can
357 _object_name.SetStyle(TextStyle("title24"));
358
359 // Position and dimensions for _description_text are set by _SetDescriptionText()
360 _description_text.SetTextStyle(TextStyle("text20"));
361 _description_text.SetDisplayMode(VIDEO_TEXT_INSTANT);
362 _description_text.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
363 _description_text.SetTextAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
364 _SetDescriptionText(); // Will set the position and dimensions of _description_text
365
366 _lore_text.SetOwner(ShopMode::CurrentInstance()->GetMiddleWindow());
367 _lore_text.SetPosition(25.0f, 100.0f);
368 _lore_text.SetDimensions(760.0f, 80.0f);
369 _lore_text.SetTextStyle(TextStyle("text20"));
370 _lore_text.SetDisplayMode(VIDEO_TEXT_INSTANT);
371 _lore_text.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
372 _lore_text.SetTextAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
373
374 _field_use_header.SetStyle(TextStyle("text22"));
375 _field_use_header.SetText(UTranslate("Field Use:"));
376 _battle_use_header.SetStyle(TextStyle("text22"));
377 _battle_use_header.SetText(UTranslate("Battle Use:"));
378 _target_type_header.SetStyle(TextStyle("text22"));
379 _target_type_header.SetText(UTranslate("Target:"));
380
381 _target_type_text.push_back(TextImage(GetTargetText(GLOBAL_TARGET_SELF_POINT), TextStyle("text22")));
382 _target_type_text.push_back(TextImage(GetTargetText(GLOBAL_TARGET_ALLY_POINT), TextStyle("text22")));
383 _target_type_text.push_back(TextImage(GetTargetText(GLOBAL_TARGET_FOE_POINT), TextStyle("text22")));
384 _target_type_text.push_back(TextImage(GetTargetText(GLOBAL_TARGET_SELF), TextStyle("text22")));
385 _target_type_text.push_back(TextImage(GetTargetText(GLOBAL_TARGET_ALLY), TextStyle("text22")));
386 _target_type_text.push_back(TextImage(GetTargetText(GLOBAL_TARGET_FOE), TextStyle("text22")));
387 _target_type_text.push_back(TextImage(GetTargetText(GLOBAL_TARGET_ALL_ALLIES), TextStyle("text22")));
388 _target_type_text.push_back(TextImage(GetTargetText(GLOBAL_TARGET_ALL_FOES), TextStyle("text22")));
389
390 _phys_header.SetStyle(TextStyle("text22"));
391 _phys_header.SetText(UTranslate("Phys:"));
392 _meta_header.SetStyle(TextStyle("text22"));
393 _meta_header.SetText(UTranslate("Meta:"));
394
395 _phys_rating.SetStyle(TextStyle("text22"));
396 _meta_rating.SetStyle(TextStyle("text22"));
397 _socket_text.SetStyle(TextStyle("text22"));
398
399 // Size elemental and status icon containers to the total number of avaiable elementals/status effects
400 _elemental_icons.resize(GLOBAL_ELEMENTAL_TOTAL, StillImage());
401 // TODO
402 // _status_icons.resize(GLOBAL_STATUS_TOTAL, StillImage());
403 }
404
405
406
Initialize()407 void ShopObjectViewer::Initialize() {
408 _description_text.SetOwner(ShopMode::CurrentInstance()->GetBottomWindow());
409
410 _check_icon = *(ShopMode::CurrentInstance()->Media()->GetCheckIcon());
411 _x_icon = *(ShopMode::CurrentInstance()->Media()->GetXIcon());
412 _socket_icon = *(ShopMode::CurrentInstance()->Media()->GetSocketIcon());
413 _equip_icon = *(ShopMode::CurrentInstance()->Media()->GetEquipIcon());
414
415 uint32 number_character = ShopMode::CurrentInstance()->Media()->GetCharacterSprites()->size();
416
417 for (uint32 i = 0; i < number_character; i++) {
418 _character_sprites.push_back(ShopMode::CurrentInstance()->Media()->GetCharacterSprites()->at(i));
419 _character_equipped.push_back(false);
420 _phys_change_text.push_back(TextImage());
421 _meta_change_text.push_back(TextImage());
422 }
423 }
424
425
426
Update()427 void ShopObjectViewer::Update() {
428 _description_text.Update();
429 _lore_text.Update();
430 }
431
432
433
Draw()434 void ShopObjectViewer::Draw() {
435 if (_selected_object == NULL) {
436 return;
437 }
438
439 // Set the initial draw cursor position to the top left corner of the proper window
440 VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_CENTER, 0);
441 if (_view_mode == SHOP_VIEW_MODE_LIST) {
442 VideoManager->Move(135.0f, 188.0f);
443 }
444 else if (_view_mode == SHOP_VIEW_MODE_INFO) {
445 VideoManager->Move(135.0f, 568.0f);
446 }
447 else { // An unknown/unsupported view mode is active, so draw nothing
448 return;
449 }
450
451 // Object's name and icon are drawn in the same position for all objects
452 _object_name.Draw();
453 VideoManager->MoveRelative(0.0f, -55.0f);
454 _selected_object->GetObject()->GetIconImage().Draw();
455
456 switch (_object_type) {
457 case SHOP_OBJECT_ITEM:
458 _DrawItem();
459 break;
460 case SHOP_OBJECT_EQUIPMENT:
461 _DrawEquipment();
462 break;
463 case SHOP_OBJECT_SHARD:
464 _DrawShard();
465 break;
466 case SHOP_OBJECT_KEY_ITEM:
467 _DrawKeyItem();
468 break;
469 default: // unknown/unsupported object type, draw no further information
470 break;
471 }
472
473 // In the info view mode, description text and lore text is always drawn near the bottom of the middle window
474 if (_view_mode == SHOP_VIEW_MODE_INFO) {
475 _description_text.Draw();
476 _lore_text.Draw();
477 }
478 }
479
480
481
SetSelectedObject(ShopObject * object)482 void ShopObjectViewer::SetSelectedObject(ShopObject* object) {
483 if (object == NULL) {
484 _selected_object = NULL;
485 return;
486 }
487
488 if (_selected_object == object) {
489 return;
490 }
491
492 _selected_object = object;
493 _object_type = _selected_object->DetermineShopObjectType();
494
495 // Get a pointer to the global object type of the new object selection
496 switch (_object_type) {
497 case SHOP_OBJECT_ITEM:
498 _SetItemData();
499 break;
500 case SHOP_OBJECT_EQUIPMENT:
501 _SetEquipmentData();
502 break;
503 case SHOP_OBJECT_SHARD:
504 _SetShardData();
505 break;
506 case SHOP_OBJECT_KEY_ITEM:
507 _SetDescriptionText();
508 break;
509 default:
510 IF_PRINT_WARNING(SHOP_DEBUG) << "invalid object type: " << _object_type << endl;
511 break;
512 }
513
514 _object_name.SetText(_selected_object->GetObject()->GetName());
515 _description_text.SetDisplayText(_selected_object->GetObject()->GetDescription());
516 // TODO: this data is not yet available in the global code
517 // _lore_text.SetDisplayText(_selected_object->GetObject()->GetLore());
518 } // void ShopObjectViewer::SetSelectedObject(ShopObject* object)
519
520
521
ChangeViewMode(SHOP_VIEW_MODE new_mode)522 void ShopObjectViewer::ChangeViewMode(SHOP_VIEW_MODE new_mode) {
523 if (_view_mode == new_mode) {
524 return;
525 }
526
527 if (new_mode == SHOP_VIEW_MODE_LIST) {
528 _view_mode = new_mode;
529 }
530 else if (new_mode == SHOP_VIEW_MODE_INFO) {
531 _view_mode = new_mode;
532 }
533 else {
534 IF_PRINT_WARNING(SHOP_DEBUG) << "unknown/unsupported view mode passed in function argument: " << new_mode << endl;
535 }
536 _SetDescriptionText(); // Necessary because description text must change its owner window
537 }
538
539
540
_SetItemData()541 void ShopObjectViewer::_SetItemData() {
542 if (_object_type != SHOP_OBJECT_ITEM) {
543 IF_PRINT_WARNING(SHOP_DEBUG) << "function invoked when selected object was not an item: " << _object_type << endl;
544 return;
545 }
546
547 // Ensure that the position of description text is correct
548 _SetDescriptionText();
549
550 // Set map/battle usability status
551 GlobalItem* item = dynamic_cast<GlobalItem*>(_selected_object->GetObject());
552 _map_usable = item->IsUsableInField();
553 _battle_usable = item->IsUsableInBattle();
554
555 // Determine the target type text to display for this item
556 GLOBAL_TARGET target_type = item->GetTargetType();
557 _target_type_index = static_cast<uint32>(target_type);
558 if (_target_type_index >= _target_type_text.size()) {
559 IF_PRINT_WARNING(SHOP_DEBUG) << "unknown/invalid target type, defaulting to 'Self': " << target_type << endl;
560 _target_type_index = 0;
561 }
562 }
563
564
565
_SetEquipmentData()566 void ShopObjectViewer::_SetEquipmentData() {
567 if (_object_type != SHOP_OBJECT_EQUIPMENT) {
568 IF_PRINT_WARNING(SHOP_DEBUG) << "function invoked when selected object was not a piece of equipment: " << _object_type << endl;
569 return;
570 }
571
572 // ---------- (1): Determine whether the selected object is a weapon or piece of armor
573 GlobalWeapon* selected_weapon = NULL;
574 GlobalArmor* selected_armor = NULL;
575 uint32 usable_status = 0; // This is a bit mask that will hold the selected object's usablility information
576 uint32 armor_index = 0; // Will hold the correct index into a GlobalCharacter object's equipped armor container
577
578 if (_selected_object->GetObject()->GetObjectType() == GLOBAL_OBJECT_WEAPON) {
579 selected_weapon = dynamic_cast<GlobalWeapon*>(_selected_object->GetObject());
580 usable_status = selected_weapon->GetUsableBy();
581 }
582 else {
583 selected_armor = dynamic_cast<GlobalArmor*>(_selected_object->GetObject());
584 usable_status = selected_armor->GetUsableBy();
585
586 // Armor on GlobalCharacter objects are stored in 4-element vectors. The different armor type maps to one of these four elements
587 switch (selected_armor->GetObjectType()) {
588 case GLOBAL_OBJECT_HEAD_ARMOR:
589 armor_index = 0;
590 break;
591 case GLOBAL_OBJECT_TORSO_ARMOR:
592 armor_index = 1;
593 break;
594 case GLOBAL_OBJECT_ARM_ARMOR:
595 armor_index = 2;
596 break;
597 case GLOBAL_OBJECT_LEG_ARMOR:
598 armor_index = 3;
599 break;
600 default:
601 IF_PRINT_WARNING(SHOP_DEBUG) << "object type was not armor: " << _selected_object->GetObject()->GetObjectType() << endl;
602 return;
603 }
604 }
605
606 // ---------- (2): Determine equipment's rating, socket, elemental effects, and status effects to report
607
608 if (selected_weapon != NULL) {
609 _phys_rating.SetText(NumberToString(selected_weapon->GetPhysicalAttack()));
610 _meta_rating.SetText(NumberToString(selected_weapon->GetMetaphysicalAttack()));
611 _socket_text.SetText("x" + NumberToString(selected_weapon->GetSockets().size()));
612 _SetElementalIcons(selected_weapon->GetElementalEffects());
613 // TODO
614 //_SetStatusIcons(selected_weapon->GetStatusEffects());
615 }
616 else if (selected_armor != NULL) {
617 _phys_rating.SetText(NumberToString(selected_armor->GetPhysicalDefense()));
618 _meta_rating.SetText(NumberToString(selected_armor->GetMetaphysicalDefense()));
619 _socket_text.SetText("x" + NumberToString(selected_armor->GetSockets().size()));
620 _SetElementalIcons(selected_armor->GetElementalEffects());
621 // TODO
622 //_SetStatusIcons(selected_weapon->GetStatusEffects());
623 }
624
625 // ---------- (3): For each character, determine if they already have the selection equipped or determine the change in pricing
626 vector<GlobalCharacter*>* party = GlobalManager->GetCharacterOrder();
627 GlobalCharacter* character = NULL;
628 GlobalWeapon* equipped_weapon = NULL;
629 GlobalArmor* equipped_armor = NULL;
630 int32 phys_diff = 0, meta_diff = 0; // Holds the difference in attack power from equipped weapon/armor to selected weapon/armor
631
632 // NOTE: In this block of code, entries to the _phys_change_text and _meta_change_text members are only modified if that information is to be
633 // displayed for the character (meaning that the character can use the weapon/armor and does not already have it equipped). This means
634 // that these two container members may contain stale data from previous objects. This is acceptable, however, as the stale data should
635 // never be drawn. The stale data is allowed to remain so that we do not waste time re-rendering text for which we will not display.
636 if (selected_weapon != NULL) {
637 for (uint32 i = 0; i < party->size(); i++) {
638 character = party->at(i);
639 equipped_weapon = character->GetWeaponEquipped();
640
641 // Initially assume that the character does not have this weapon equipped
642 _character_equipped[i] = false;
643
644 // Case 1: determine if the character can use the weapon and if not, move on to the next character
645 // Toggle grayscale mode appropriately to indicate whether or not the character can equip this
646 if (usable_status & (character->GetID())) {
647 if (_character_sprites[i].IsGrayScale() == true)
648 _character_sprites[i].DisableGrayScale();
649 }
650 else {
651 if (_character_sprites[i].IsGrayScale() == false)
652 _character_sprites[i].EnableGrayScale();
653 continue;
654 }
655 // Case 2: if the player does not have any weapon equipped, the stat diff is equal to the selected weapon's ratings
656 if (equipped_weapon == NULL) {
657 phys_diff = static_cast<int32>(selected_weapon->GetPhysicalAttack());
658 meta_diff = static_cast<int32>(selected_weapon->GetMetaphysicalAttack());
659 }
660 // Case 3: if the player already has this weapon equipped, indicate thus and move on to the next character
661 if (selected_weapon->GetID() == equipped_weapon->GetID()) {
662 _character_equipped[i] = true;
663 continue;
664 }
665 // Case 4: the player can use this weapon and does not already have it equipped
666 else {
667 phys_diff = static_cast<int32>(selected_weapon->GetPhysicalAttack()) -
668 static_cast<int32>(equipped_weapon->GetPhysicalAttack());
669 meta_diff = static_cast<int32>(selected_weapon->GetMetaphysicalAttack()) -
670 static_cast<int32>(equipped_weapon->GetMetaphysicalAttack());
671 }
672
673 // If this line has been reached, either case (2) or case (4) were evaluated as true. Render the phys/meta stat variation text
674 _SetChangeText(i, phys_diff, meta_diff);
675 }
676 }
677 else { // (selected_armor != NULL)
678 for (uint32 i = 0; i < party->size(); i++) {
679 character = party->at(i);
680 equipped_armor = character->GetArmorEquipped().at(armor_index);
681
682 // Initially assume that the character does not have this armor equipped
683 _character_equipped[i] = false;
684
685 // Case 1: determine if the character can use the armor and if not, move on to the next character
686 // Toggle grayscale mode appropriately to indicate whether or not the character can equip this
687 if (usable_status & (character->GetID())) {
688 if (_character_sprites[i].IsGrayScale() == true)
689 _character_sprites[i].DisableGrayScale();
690 }
691 else {
692 if (_character_sprites[i].IsGrayScale() == false)
693 _character_sprites[i].EnableGrayScale();
694 continue;
695 }
696 // Case 2: if the player does not have any armor equipped, the stat diff is equal to the selected armor's ratings
697 if (equipped_armor == NULL) {
698 phys_diff = static_cast<int32>(selected_armor->GetPhysicalDefense());
699 meta_diff = static_cast<int32>(selected_armor->GetMetaphysicalDefense());
700 }
701 // Case 3: if the player already has this armor equipped, indicate thus and move on to the next character
702 if (selected_armor->GetID() == equipped_armor->GetID()) {
703 _character_equipped[i] = true;
704 continue;
705 }
706 // Case 4: the player can use this armor and does not already have it equipped
707 else {
708 phys_diff = static_cast<int32>(selected_armor->GetPhysicalDefense()) -
709 static_cast<int32>(equipped_armor->GetPhysicalDefense());
710 meta_diff = static_cast<int32>(selected_armor->GetMetaphysicalDefense()) -
711 static_cast<int32>(equipped_armor->GetMetaphysicalDefense());
712 }
713
714 // If this line has been reached, either case (2) or case (4) were evaluated as true. Render the phys/meta stat variation text
715 _SetChangeText(i, phys_diff, meta_diff);
716 }
717 }
718 } // void ShopObjectViewer::_SetEquipmentData()
719
720
721
_SetShardData()722 void ShopObjectViewer::_SetShardData() {
723 // TODO: implement when GlobalShard class is ready for use
724 }
725
726
727
_SetDescriptionText()728 void ShopObjectViewer::_SetDescriptionText() {
729 if (_view_mode == SHOP_VIEW_MODE_LIST) {
730 _description_text.SetOwner(ShopMode::CurrentInstance()->GetBottomWindow());
731 // For key items, draw position is a little higher than other cases to center it in the blank area
732 if (_object_type == SHOP_OBJECT_KEY_ITEM) {
733 _description_text.SetPosition(102.0f, 76.0f);
734 }
735 else {
736 _description_text.SetPosition(102.0f, 56.0f);
737 }
738 _description_text.SetDimensions(675.0f, 50.0f);
739 }
740
741 else if (_view_mode == SHOP_VIEW_MODE_INFO) {
742 _description_text.SetOwner(ShopMode::CurrentInstance()->GetMiddleWindow());
743 _description_text.SetPosition(25.0f, 140.0f);
744 _description_text.SetDimensions(750.0f, 50.0f);
745 }
746
747 else {
748 IF_PRINT_WARNING(SHOP_DEBUG) << "unknown/unsupported view mode was active: " << _view_mode << endl;
749 }
750 }
751
752
753
_SetChangeText(uint32 index,int32 phys_diff,int32 meta_diff)754 void ShopObjectViewer::_SetChangeText(uint32 index, int32 phys_diff, int32 meta_diff) {
755 if (index >= _character_sprites.size()) {
756 IF_PRINT_WARNING(SHOP_DEBUG) << "index argument was out of bounds: " << index << endl;
757 return;
758 }
759
760 _phys_change_text[index].Clear();
761 if (phys_diff > 0) {
762 _phys_change_text[index].SetStyle(TextStyle("text18", Color::green));
763 _phys_change_text[index].SetText("+" + NumberToString(phys_diff));
764 }
765 else if (phys_diff < 0) {
766 _phys_change_text[index].SetStyle(TextStyle("text18", Color::red));
767 _phys_change_text[index].SetText(NumberToString(phys_diff));
768 }
769 else { // (phys_diff == 0)
770 _phys_change_text[index].SetStyle(TextStyle("text18", Color::white));
771 _phys_change_text[index].SetText(NumberToString(phys_diff));
772 }
773
774 _meta_change_text[index].Clear();
775 if (meta_diff > 0) {
776 _meta_change_text[index].SetStyle(TextStyle("text18", Color::green));
777 _meta_change_text[index].SetText("+" + NumberToString(meta_diff));
778 }
779 else if (meta_diff < 0) {
780 _meta_change_text[index].SetStyle(TextStyle("text18", Color::red));
781 _meta_change_text[index].SetText(NumberToString(meta_diff));
782 }
783 else { // (meta_diff == 0)
784 _meta_change_text[index].SetStyle(TextStyle("text18", Color::white));
785 _meta_change_text[index].SetText(NumberToString(meta_diff));
786 }
787 }
788
789
790
_SetElementalIcons(const map<GLOBAL_ELEMENTAL,GLOBAL_INTENSITY> & elemental_effects)791 void ShopObjectViewer::_SetElementalIcons(const map<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>& elemental_effects) {
792 uint32 index = 0;
793
794 for (map<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>::const_iterator i = elemental_effects.begin(); i != elemental_effects.end(); i++) {
795 switch (i->first) {
796 case GLOBAL_ELEMENTAL_FIRE:
797 index = 0;
798 break;
799 case GLOBAL_ELEMENTAL_WATER:
800 index = 1;
801 break;
802 case GLOBAL_ELEMENTAL_VOLT:
803 index = 2;
804 break;
805 case GLOBAL_ELEMENTAL_EARTH:
806 index = 3;
807 break;
808 case GLOBAL_ELEMENTAL_SLICING:
809 index = 4;
810 break;
811 case GLOBAL_ELEMENTAL_SMASHING:
812 index = 5;
813 break;
814 case GLOBAL_ELEMENTAL_MAULING:
815 index = 6;
816 break;
817 case GLOBAL_ELEMENTAL_PIERCING:
818 index = 7;
819 break;
820 default:
821 IF_PRINT_WARNING(SHOP_DEBUG) << "invalid elemental type: " << i->first << endl;
822 break;
823 }
824
825 _elemental_icons[index] = *(ShopMode::CurrentInstance()->Media()->GetElementalIcon(i->first, i->second));
826 if (i->second == GLOBAL_INTENSITY_NEUTRAL) {
827 _elemental_icons[index].EnableGrayScale();
828 }
829 }
830 }
831
832
833
_SetStatusIcons(const map<GLOBAL_STATUS,GLOBAL_INTENSITY> & status_effects)834 void ShopObjectViewer::_SetStatusIcons(const map<GLOBAL_STATUS, GLOBAL_INTENSITY>& status_effects) {
835 // TODO: Implement this method when status effects are available.
836 // It should work very much the same way as _SetElementalIcons()
837 }
838
839
840
_DrawItem()841 void ShopObjectViewer::_DrawItem() {
842 float move_offset = 0.0f; // Used to save image widths in determining relative movement
843 VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_CENTER, 0);
844
845 VideoManager->MoveRelative(80.0f, 15.0f);
846 _field_use_header.Draw();
847 move_offset = _field_use_header.GetWidth() + 5.0f; // 5.0f is a small buffer space between text and graphic
848 VideoManager->MoveRelative(move_offset, 0.0f);
849 if (_map_usable == true) {
850 _check_icon.Draw();
851 }
852 else {
853 _x_icon.Draw();
854 }
855
856 VideoManager->MoveRelative(175.0f - move_offset, 0.0f);
857 _battle_use_header.Draw();
858 move_offset = _battle_use_header.GetWidth() + 5.0f;
859 VideoManager->MoveRelative(move_offset, 0.0f);
860 if (_battle_usable == true) {
861 _check_icon.Draw();
862 }
863 else {
864 _x_icon.Draw();
865 }
866
867 VideoManager->MoveRelative(175.0f - move_offset, 0.0f);
868 _target_type_header.Draw();
869 move_offset = _target_type_header.GetWidth() + 5.0f;
870 VideoManager->MoveRelative(move_offset, 0.0f);
871 _target_type_text[_target_type_index].Draw();
872
873 _description_text.Draw();
874 }
875
876
877
_DrawEquipment()878 void ShopObjectViewer::_DrawEquipment() {
879 VideoManager->MoveRelative(80.0f, 15.0f);
880 _phys_header.Draw();
881 VideoManager->MoveRelative(0.0f, -30.0f);
882 _meta_header.Draw();
883
884 VideoManager->SetDrawFlags(VIDEO_X_RIGHT, 0);
885 VideoManager->MoveRelative(90.0f, 30.0f);
886 _phys_rating.Draw();
887 VideoManager->MoveRelative(0.0f, -30.0f);
888 _meta_rating.Draw();
889
890 VideoManager->SetDrawFlags(VIDEO_X_LEFT, 0);
891 VideoManager->MoveRelative(20.0f, 15.0f);
892 _socket_icon.Draw();
893 VideoManager->MoveRelative(20.0f, 0.0f);
894 _socket_text.Draw();
895
896 VideoManager->SetDrawFlags(VIDEO_X_CENTER, 0);
897 VideoManager->MoveRelative(50.0f, 55.0f);
898 for (uint32 i = 0; i < GLOBAL_ELEMENTAL_TOTAL / 2; i++) {
899 _elemental_icons[i].Draw();
900 VideoManager->MoveRelative(0.0f, -25.0f);
901 }
902 VideoManager->MoveRelative(40.0f, 100.0f);
903 for (uint32 i = GLOBAL_ELEMENTAL_TOTAL / 2; i < GLOBAL_ELEMENTAL_TOTAL; i++) {
904 _elemental_icons[i].Draw();
905 VideoManager->MoveRelative(0.0f, -25.0f);
906 }
907
908 // TODO: Draw two columns of status icons
909 VideoManager->MoveRelative(80.0f, 0.0f); // TEMP: remove once commented code block below is added
910 // VideoManager->MoveRelative(40.0f, 100.0f);
911 // for (uint32 i = 0; i < 4; i++) {
912 // _status_icons[i].Draw();
913 // VideoManager->MoveRelative(0.0f, -25.0f);
914 // }
915 // VideoManager->MoveRelative(40.0f, 100.0f);
916 // for (uint32 i = 4; i < 8; i++) {
917 // _status_icons[i].Draw();
918 // VideoManager->MoveRelative(0.0f, -25.0f);
919 // }
920
921 VideoManager->SetDrawFlags(VIDEO_Y_TOP, 0);
922 if (_view_mode == SHOP_VIEW_MODE_LIST) {
923 // In list view mode, draw the sprites to the right of the icons
924 VideoManager->MoveRelative(60.0f, 140.0f);
925 }
926 else { // (_view_mode == SHOP_VIEW_MODE_INFO)
927 // In info view mode, draw the spites centered on the screen in a row below the other equipment data
928 VideoManager->Move(512.0f, 475.0f);
929 float x_offset = -20.0f * _character_sprites.size();
930 VideoManager->MoveRelative(x_offset, 0.0f);
931 }
932 for (uint32 i = 0; i < _character_sprites.size(); i++) {
933 // In list mode, there's only enough room to show 8 sprites
934 if ((_view_mode == SHOP_VIEW_MODE_LIST) && (i >= 8)) {
935 break;
936 }
937
938 _character_sprites[i].Draw();
939
940 // Case 1: Draw the equip icon below the character sprite
941 if (_character_equipped[i] == true) {
942 VideoManager->MoveRelative(0.0f, -78.0f);
943 _equip_icon.Draw();
944 VideoManager->MoveRelative(0.0f, 78.0f);
945 }
946 // Case 2: Draw the phys/meta change text below the sprite
947 else if (_character_sprites[i].IsGrayScale() == false) {
948 VideoManager->MoveRelative(0.0f, -65.0f);
949 _phys_change_text[i].Draw();
950 VideoManager->MoveRelative(0.0f, -20.0f);
951 _meta_change_text[i].Draw();
952 VideoManager->MoveRelative(0.0f, 85.0f);
953 }
954 // Case 3: Nothing needs to be drawn below the sprite
955 VideoManager->MoveRelative(40.0f, 0.0f);
956 }
957 } // void ShopObjectViewer::_DrawEquipment()
958
959
960
_DrawShard()961 void ShopObjectViewer::_DrawShard() {
962 // TODO: implement when GlobalShard class is ready for use
963 }
964
965
966
_DrawKeyItem()967 void ShopObjectViewer::_DrawKeyItem() {
968 _description_text.Draw();
969 }
970
971
972 } // namespace private_shop
973
974 // *****************************************************************************
975 // ***** ShopMode class methods
976 // *****************************************************************************
977
ShopMode()978 ShopMode::ShopMode() :
979 _initialized(false),
980 _state(SHOP_STATE_ROOT),
981 _deal_types(0),
982 _buy_price_level(SHOP_PRICE_STANDARD),
983 _sell_price_level(SHOP_PRICE_STANDARD),
984 _total_costs(0),
985 _total_sales(0),
986 _shop_media(NULL),
987 _object_viewer(NULL),
988 _root_interface(NULL),
989 _buy_interface(NULL),
990 _sell_interface(NULL),
991 _trade_interface(NULL),
992 _confirm_interface(NULL)
993 {
994 mode_type = MODE_MANAGER_SHOP_MODE;
995 _current_instance = this;
996
997 // ---------- (1): Create the menu windows and set their properties
998 _top_window.Create(800.0f, 96.0f, ~VIDEO_MENU_EDGE_BOTTOM);
999 _top_window.SetPosition(112.0f, 684.0f);
1000 _top_window.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
1001 _top_window.SetDisplayMode(VIDEO_MENU_INSTANT);
1002 _top_window.Show();
1003
1004 _middle_window.Create(800.0f, 400.0f, VIDEO_MENU_EDGE_ALL, VIDEO_MENU_EDGE_TOP | VIDEO_MENU_EDGE_BOTTOM);
1005 _middle_window.SetPosition(112.0f, 604.0f);
1006 _middle_window.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
1007 _middle_window.SetDisplayMode(VIDEO_MENU_INSTANT);
1008 _middle_window.Show();
1009
1010 _bottom_window.Create(800.0f, 140.0f, ~VIDEO_MENU_EDGE_TOP);
1011 _bottom_window.SetPosition(112.0f, 224.0f);
1012 _bottom_window.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
1013 _bottom_window.SetDisplayMode(VIDEO_MENU_INSTANT);
1014 _bottom_window.Show();
1015
1016 // (2) Create the list of shop actions
1017 _action_options.SetOwner(&_top_window);
1018 _action_options.SetPosition(80.0f, 90.0f);
1019 _action_options.SetDimensions(640.0f, 30.0f, 4, 1, 4, 1);
1020 _action_options.SetOptionAlignment(VIDEO_X_CENTER, VIDEO_Y_TOP);
1021 _action_options.SetTextStyle(TextStyle("title28"));
1022 _action_options.SetSelectMode(VIDEO_SELECT_SINGLE);
1023 _action_options.SetCursorOffset(-55.0f, 30.0f);
1024 _action_options.SetVerticalWrapMode(VIDEO_WRAP_MODE_STRAIGHT);
1025
1026 vector<ustring> option_text;
1027 option_text.push_back(UTranslate("Buy"));
1028 option_text.push_back(UTranslate("Sell"));
1029 option_text.push_back(UTranslate("Trade"));
1030 option_text.push_back(UTranslate("Confirm"));
1031 _action_options.SetOptions(option_text);
1032 _action_options.SetSelection(0);
1033
1034 _action_titles.push_back(TextImage(option_text[0], TextStyle("title28")));
1035 _action_titles.push_back(TextImage(option_text[1], TextStyle("title28")));
1036 _action_titles.push_back(TextImage(option_text[2], TextStyle("title28")));
1037 _action_titles.push_back(TextImage(option_text[3], TextStyle("title28")));
1038 _action_titles.push_back(TextImage(UTranslate("Leave"), TextStyle("title28")));
1039
1040 // (3) Create the financial table text
1041 _finance_table.SetOwner(&_top_window);
1042 _finance_table.SetPosition(80.0f, 45.0f);
1043 _finance_table.SetDimensions(640.0f, 20.0f, 4, 1, 4, 1);
1044 _finance_table.SetOptionAlignment(VIDEO_X_CENTER, VIDEO_Y_CENTER);
1045 _finance_table.SetTextStyle(TextStyle("text22"));
1046 _finance_table.SetCursorState(VIDEO_CURSOR_STATE_HIDDEN);
1047 // Initialize all four options with an empty string that will be overwritten by the following method call
1048 for (uint32 i = 0; i < 4; i++)
1049 _finance_table.AddOption(ustring());
1050 UpdateFinances(0, 0);
1051
1052 _shop_media = new ShopMedia();
1053 _object_viewer = new ShopObjectViewer();
1054 _root_interface = new RootInterface();
1055 _buy_interface = new BuyInterface();
1056 _sell_interface = new SellInterface();
1057 _trade_interface = new TradeInterface();
1058 _confirm_interface = new ConfirmInterface();
1059 _leave_interface = new LeaveInterface();
1060
1061 try {
1062 _screen_backdrop = VideoManager->CaptureScreen();
1063 }
1064 catch (Exception e) {
1065 IF_PRINT_WARNING(SHOP_DEBUG) << e.ToString() << endl;
1066 }
1067 } // ShopMode::ShopMode()
1068
1069
1070
~ShopMode()1071 ShopMode::~ShopMode() {
1072 for (uint32 i = 0; i < _created_objects.size(); i++) {
1073 delete(_created_objects[i]);
1074 }
1075 _created_objects.clear();
1076
1077 delete _shop_media;
1078 delete _object_viewer;
1079 delete _root_interface;
1080 delete _buy_interface;
1081 delete _sell_interface;
1082 delete _trade_interface;
1083 delete _confirm_interface;
1084 delete _leave_interface;
1085
1086 _top_window.Destroy();
1087 _middle_window.Destroy();
1088 _bottom_window.Destroy();
1089
1090 if (_current_instance == this) {
1091 _current_instance = NULL;
1092 }
1093 }
1094
1095
1096
Reset()1097 void ShopMode::Reset() {
1098 VideoManager->SetCoordSys(0.0f, 1023.0f, 0.0f, 767.0f);
1099 VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_BOTTOM, 0);
1100
1101 _current_instance = this;
1102 if (IsInitialized() == false)
1103 Initialize();
1104 }
1105
1106
1107
Initialize()1108 void ShopMode::Initialize() {
1109 if (IsInitialized() == true) {
1110 IF_PRINT_WARNING(SHOP_DEBUG) << "shop was already initialized previously" << endl;
1111 return;
1112 }
1113
1114 _initialized = true;
1115
1116 // ---------- (1): Determine what types of objects the shop deals in based on the managed object list
1117 for (uint32 i = 0; i < _created_objects.size(); i++) {
1118 switch (_created_objects[i]->GetObjectType()) {
1119 case GLOBAL_OBJECT_ITEM:
1120 _deal_types |= DEALS_ITEMS;
1121 break;
1122 case GLOBAL_OBJECT_WEAPON:
1123 _deal_types |= DEALS_WEAPONS;
1124 break;
1125 case GLOBAL_OBJECT_HEAD_ARMOR:
1126 _deal_types |= DEALS_HEAD_ARMOR;
1127 break;
1128 case GLOBAL_OBJECT_TORSO_ARMOR:
1129 _deal_types |= DEALS_TORSO_ARMOR;
1130 break;
1131 case GLOBAL_OBJECT_ARM_ARMOR:
1132 _deal_types |= DEALS_ARM_ARMOR;
1133 break;
1134 case GLOBAL_OBJECT_LEG_ARMOR:
1135 _deal_types |= DEALS_LEG_ARMOR;
1136 break;
1137 case GLOBAL_OBJECT_SHARD:
1138 _deal_types |= DEALS_SHARDS;
1139 break;
1140 case GLOBAL_OBJECT_KEY_ITEM:
1141 _deal_types |= DEALS_KEY_ITEMS;
1142 break;
1143 default:
1144 IF_PRINT_WARNING(SHOP_DEBUG) << "unknown object type sold in shop: " << _created_objects[i]->GetObjectType() << endl;
1145 break;
1146 }
1147 }
1148
1149 // ---------- (2): Add objects from the player's inventory to the list of shop objects
1150 map<uint32, GlobalObject*>* inventory = GlobalManager->GetInventory();
1151 for (map<uint32, GlobalObject*>::iterator i = inventory->begin(); i != inventory->end(); i++) {
1152 // Check if the object already exists in the shop list and if so, set its ownership count
1153 map<uint32, ShopObject>::iterator shop_obj_iter = _shop_objects.find(i->second->GetID());
1154 if (shop_obj_iter != _shop_objects.end()) {
1155 shop_obj_iter->second.IncrementOwnCount(i->second->GetCount());
1156 }
1157 // Otherwise, add the shop object to the list
1158 else {
1159 ShopObject new_shop_object(i->second, false);
1160 new_shop_object.IncrementOwnCount(i->second->GetCount());
1161 _shop_objects.insert(make_pair(i->second->GetID(), new_shop_object));
1162 }
1163 }
1164
1165 // ---------- (3): Initialize pricing for all shop objects
1166 for (map<uint32, ShopObject>::iterator i = _shop_objects.begin(); i != _shop_objects.end(); i++) {
1167 i->second.SetPricing(_buy_price_level, _sell_price_level);
1168 }
1169
1170 // ---------- (4): Initialize multimedia data and viewer
1171 _shop_media->Initialize();
1172 _object_viewer->Initialize();
1173
1174 // ---------- (5): Initialize all shop interfaces
1175 _root_interface->Initialize();
1176 _buy_interface->Initialize();
1177 _sell_interface->Initialize();
1178 _trade_interface->Initialize();
1179 _confirm_interface->Initialize();
1180 _leave_interface->Initialize();
1181 } // void ShopMode::Initialize()
1182
1183
1184
Update()1185 void ShopMode::Update() {
1186 // Pause and quit events have highest priority. If either type of event is detected, no other update processing will be done
1187 if (InputManager->QuitPress() == true) {
1188 ModeManager->Push(new PauseMode(true));
1189 return;
1190 }
1191 else if (InputManager->PausePress() == true) {
1192 ModeManager->Push(new PauseMode(false));
1193 return;
1194 }
1195
1196 // When the state is at the root interface ,ShopMode needs to process user input and possibly change state
1197 if (_state == SHOP_STATE_ROOT) {
1198 SoundDescriptor* sound = NULL; // Used to hold pointers of sound objects to play
1199
1200 if (InputManager->ConfirmPress()) {
1201 if (_action_options.GetSelection() < 0 || _action_options.GetSelection() > 4) {
1202 IF_PRINT_WARNING(SHOP_DEBUG) << "invalid selection in action window: " << _action_options.GetSelection() << endl;
1203 _action_options.SetSelection(0);
1204 return;
1205 }
1206
1207 _action_options.InputConfirm();
1208 sound = ShopMode::CurrentInstance()->Media()->GetSound("confirm");
1209 assert(sound != NULL);
1210 sound->Play();
1211
1212 if (_action_options.GetSelection() == 0) { // Buy
1213 ChangeState(SHOP_STATE_BUY);
1214 }
1215 else if (_action_options.GetSelection() == 1) { // Sell
1216 ChangeState(SHOP_STATE_SELL);
1217 }
1218 else if (_action_options.GetSelection() == 2) { // Trade
1219 ChangeState(SHOP_STATE_TRADE);
1220 }
1221 else if (_action_options.GetSelection() == 3) { // Confirm
1222 ChangeState(SHOP_STATE_CONFIRM);
1223 }
1224 }
1225 else if (InputManager->CancelPress()) {
1226 ChangeState(SHOP_STATE_LEAVE);
1227 }
1228 else if (InputManager->LeftPress()) {
1229 _action_options.InputLeft();
1230 }
1231 else if (InputManager->RightPress()) {
1232 _action_options.InputRight();
1233 }
1234 _action_options.Update();
1235
1236 _root_interface->Update();
1237 } // if (_state == SHOP_STATE_ROOT)
1238 else {
1239 // Update the active interface
1240 switch (_state) {
1241 case SHOP_STATE_BUY:
1242 _buy_interface->Update();
1243 break;
1244 case SHOP_STATE_SELL:
1245 _sell_interface->Update();
1246 break;
1247 case SHOP_STATE_TRADE:
1248 _trade_interface->Update();
1249 break;
1250 case SHOP_STATE_CONFIRM:
1251 _confirm_interface->Update();
1252 break;
1253 case SHOP_STATE_LEAVE:
1254 _leave_interface->Update();
1255 break;
1256 default:
1257 IF_PRINT_WARNING(SHOP_DEBUG) << "invalid shop state: " << _state << ", reseting to root state" << endl;
1258 _state = SHOP_STATE_ROOT;
1259 break;
1260 } // switch (_state)
1261 }
1262 } // void ShopMode::Update()
1263
1264
1265
Draw()1266 void ShopMode::Draw() {
1267 // ---------- (1): Draw the background image. Set the system coordinates to the size of the window (same as the screen backdrop)
1268 VideoManager->SetCoordSys(0.0f, static_cast<float>(VideoManager->GetScreenWidth()), 0.0f, static_cast<float>(VideoManager->GetScreenHeight()));
1269 VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_BOTTOM, 0);
1270 VideoManager->Move(0.0f, 0.0f);
1271 _screen_backdrop.Draw();
1272
1273 // ---------- (2): Draw all menu windows
1274 VideoManager->SetCoordSys(0.0f, 1024.0f, 0.0f, 768.0f); // Restore the standard shop coordinate system before drawing the shop windows
1275 _top_window.Draw();
1276 _bottom_window.Draw();
1277 _middle_window.Draw(); // Drawn last because the middle window has the middle upper and lower window borders attached
1278
1279 // ---------- (3): Draw the contents of the top window
1280 VideoManager->Move(130.0f, 605.0f);
1281 ShopMode::CurrentInstance()->Media()->GetDrunesIcon()->Draw();
1282 VideoManager->MoveRelative(705.0f, 0.0f);
1283 ShopMode::CurrentInstance()->Media()->GetDrunesIcon()->Draw();
1284
1285 VideoManager->SetDrawFlags(VIDEO_X_CENTER, VIDEO_Y_CENTER, 0);
1286 VideoManager->Move(512.0f, 657.0f);
1287 switch (_state) {
1288 case SHOP_STATE_ROOT:
1289 _action_options.Draw();
1290 break;
1291 case SHOP_STATE_BUY:
1292 _action_titles[0].Draw();
1293 break;
1294 case SHOP_STATE_SELL:
1295 _action_titles[1].Draw();
1296 break;
1297 case SHOP_STATE_TRADE:
1298 _action_titles[2].Draw();
1299 break;
1300 case SHOP_STATE_CONFIRM:
1301 _action_titles[3].Draw();
1302 break;
1303 case SHOP_STATE_LEAVE:
1304 _action_titles[4].Draw();
1305 break;
1306 default:
1307 IF_PRINT_WARNING(SHOP_DEBUG) << "invalid shop state: " << _state << endl;
1308 break;
1309 }
1310
1311 // TODO: This method isn't working correctly (know idea why these coordinates work). When the call is fixed, the args should be updated
1312 VideoManager->DrawLine(-315.0f, -20.0f, 315.0f, -20.0f, 1.0f, Color::white);
1313
1314 _finance_table.Draw();
1315
1316 // ---------- (4): Call the draw function on the active interface to fill the contents of the other two windows
1317 switch (_state) {
1318 case SHOP_STATE_ROOT:
1319 _root_interface->Draw();
1320 break;
1321 case SHOP_STATE_BUY:
1322 _buy_interface->Draw();
1323 break;
1324 case SHOP_STATE_SELL:
1325 _sell_interface->Draw();
1326 break;
1327 case SHOP_STATE_TRADE:
1328 _trade_interface->Draw();
1329 break;
1330 case SHOP_STATE_CONFIRM:
1331 _confirm_interface->Draw();
1332 break;
1333 case SHOP_STATE_LEAVE:
1334 _leave_interface->Draw();
1335 break;
1336 default:
1337 IF_PRINT_WARNING(SHOP_DEBUG) << "invalid shop state: " << _state << endl;
1338 break;
1339 }
1340 } // void ShopMode::Draw()
1341
1342
1343
AddObjectToBuyList(ShopObject * object)1344 void ShopMode::AddObjectToBuyList(ShopObject* object) {
1345 if (object == NULL) {
1346 IF_PRINT_WARNING(SHOP_DEBUG) << "function was passed a NULL argument" << endl;
1347 return;
1348 }
1349
1350 if (object->GetBuyCount() == 0) {
1351 IF_PRINT_WARNING(SHOP_DEBUG) << "object to be added had a buy count of zero" << endl;
1352 }
1353
1354 uint32 object_id = object->GetObject()->GetID();
1355 pair<map<uint32, ShopObject*>::iterator, bool> ret_val;
1356 ret_val = _buy_list.insert(make_pair(object_id, object));
1357 if (ret_val.second == false) {
1358 IF_PRINT_WARNING(SHOP_DEBUG) << "object to be added already existed in buy list" << endl;
1359 }
1360 }
1361
1362
1363
RemoveObjectFromBuyList(ShopObject * object)1364 void ShopMode::RemoveObjectFromBuyList(ShopObject* object) {
1365 if (object == NULL) {
1366 IF_PRINT_WARNING(SHOP_DEBUG) << "function was passed a NULL argument" << endl;
1367 return;
1368 }
1369
1370 if (object->GetBuyCount() > 0) {
1371 IF_PRINT_WARNING(SHOP_DEBUG) << "object to be removed had a buy count that was non-zero" << endl;
1372 }
1373
1374 uint32 object_id = object->GetObject()->GetID();
1375 map<uint32, ShopObject*>::iterator object_entry = _buy_list.find(object_id);
1376 if (object_entry == _buy_list.end()) {
1377 IF_PRINT_WARNING(SHOP_DEBUG) << "object to be removed did not exist on the buy list" << endl;
1378 }
1379 else {
1380 _buy_list.erase(object_entry);
1381 }
1382 }
1383
1384
1385
AddObjectToSellList(ShopObject * object)1386 void ShopMode::AddObjectToSellList(ShopObject* object) {
1387 if (object == NULL) {
1388 IF_PRINT_WARNING(SHOP_DEBUG) << "function was passed a NULL argument" << endl;
1389 return;
1390 }
1391
1392 if (object->GetSellCount() == 0) {
1393 IF_PRINT_WARNING(SHOP_DEBUG) << "object to be added had a sell count of zero" << endl;
1394 }
1395
1396 uint32 object_id = object->GetObject()->GetID();
1397 pair<map<uint32, ShopObject*>::iterator, bool> ret_val;
1398 ret_val = _sell_list.insert(make_pair(object_id, object));
1399 if (ret_val.second == false) {
1400 IF_PRINT_WARNING(SHOP_DEBUG) << "object to be added already existed in sell list" << endl;
1401 }
1402 }
1403
1404
1405
RemoveObjectFromSellList(ShopObject * object)1406 void ShopMode::RemoveObjectFromSellList(ShopObject* object) {
1407 if (object == NULL) {
1408 IF_PRINT_WARNING(SHOP_DEBUG) << "function was passed a NULL argument" << endl;
1409 return;
1410 }
1411
1412 if (object->GetSellCount() > 0) {
1413 IF_PRINT_WARNING(SHOP_DEBUG) << "object to be removed had a sell count that was non-zero" << endl;
1414 }
1415
1416 uint32 object_id = object->GetObject()->GetID();
1417 map<uint32, ShopObject*>::iterator object_entry = _sell_list.find(object_id);
1418 if (object_entry == _sell_list.end()) {
1419 IF_PRINT_WARNING(SHOP_DEBUG) << "object to be removed did not exist on the sell list" << endl;
1420 }
1421 else {
1422 _sell_list.erase(object_entry);
1423 }
1424 }
1425
1426
1427
ClearOrder()1428 void ShopMode::ClearOrder() {
1429 for (map<uint32, ShopObject*>::iterator i = _buy_list.begin(); i != _buy_list.end(); i++)
1430 i->second->ResetBuyCount();
1431 for (map<uint32, ShopObject*>::iterator i = _sell_list.begin(); i != _sell_list.end(); i++)
1432 i->second->ResetSellCount();
1433
1434 _buy_list.clear();
1435 _sell_list.clear();
1436
1437 _total_costs = 0;
1438 _total_sales = 0;
1439 UpdateFinances(0, 0);
1440 }
1441
1442
1443
CompleteTransaction()1444 void ShopMode::CompleteTransaction() {
1445 uint32 count = 0;
1446 uint32 id = 0;
1447
1448 // Add all objects on the buy list to inventory and update shop object status
1449 for (map<uint32, ShopObject*>::iterator i = _buy_list.begin(); i != _buy_list.end(); i++) {
1450 count = i->second->GetBuyCount();
1451 id = i->second->GetObject()->GetID();
1452
1453 // The player may have reduced the buy count to zero in the confirm interface before completing the transaction
1454 // We simply ignore any objects on the buy list with this condition
1455 if (count == 0)
1456 continue;
1457
1458 i->second->ResetBuyCount();
1459 i->second->IncrementOwnCount(count);
1460 i->second->DecrementStockCount(count);
1461 GlobalManager->AddToInventory(id, count);
1462 }
1463 _buy_list.clear();
1464
1465 // Remove all objects on the sell list from the inventory and update shop object status
1466 for (map<uint32, ShopObject*>::iterator i = _sell_list.begin(); i != _sell_list.end(); i++) {
1467 count = i->second->GetSellCount();
1468 id = i->second->GetObject()->GetID();
1469
1470 if (count == 0)
1471 continue;
1472
1473 i->second->ResetSellCount();
1474 i->second->DecrementOwnCount(count);
1475 GlobalManager->DecrementObjectCount(id, count);
1476
1477 // When all owned instances of this object have been sold off, the object is automatically removed
1478 // from the player's inventory. If the object is not sold in the shop, this means it must be removed
1479 // from all shop object containers as the object data (GlobalObject pointer) is now invalid.
1480 if ((i->second->GetOwnCount() == 0) && (i->second->IsSoldInShop() == false)) {
1481 RemoveObject(id);
1482 }
1483 }
1484 _sell_list.clear();
1485
1486 // Update the player's drune count by subtracting costs and adding revenue and update the shop's financial display
1487 GlobalManager->AddDrunes(_total_sales);
1488 GlobalManager->SubtractDrunes(_total_costs);
1489 _total_costs = 0;
1490 _total_sales = 0;
1491 UpdateFinances(0, 0);
1492
1493 // Notify all interfaces that a transaction has just been completed
1494 _root_interface->TransactionNotification();
1495 _buy_interface->TransactionNotification();
1496 _sell_interface->TransactionNotification();
1497 _trade_interface->TransactionNotification();
1498 _confirm_interface->TransactionNotification();
1499 _leave_interface->TransactionNotification();
1500 } // void ShopMode::CompleteTransaction()
1501
1502
1503
UpdateFinances(int32 costs_amount,int32 sales_amount)1504 void ShopMode::UpdateFinances(int32 costs_amount, int32 sales_amount) {
1505 int32 updated_costs = _total_costs + costs_amount;
1506 int32 updated_sales = _total_sales + sales_amount;
1507
1508 if (updated_costs < 0) {
1509 IF_PRINT_WARNING(SHOP_DEBUG) << "updated amount causes costs to become negative: " << costs_amount << endl;
1510 return;
1511 }
1512 if (updated_sales < 0) {
1513 IF_PRINT_WARNING(SHOP_DEBUG) << "updated amount causes sales to become negative: " << sales_amount << endl;
1514 return;
1515 }
1516 if ((static_cast<int32>(GlobalManager->GetDrunes()) + updated_sales - updated_costs) < 0) {
1517 IF_PRINT_WARNING(SHOP_DEBUG) << "updated costs and sales values cause negative balance: " << costs_amount << ", " << sales_amount << endl;
1518 return;
1519 }
1520
1521 _total_costs = static_cast<uint32>(updated_costs);
1522 _total_sales = static_cast<uint32>(updated_sales);
1523
1524 _finance_table.SetOptionText(0, UTranslate("Funds: ") + MakeUnicodeString(NumberToString(GlobalManager->GetDrunes())));
1525 _finance_table.SetOptionText(1, UTranslate("Purchases: -") + MakeUnicodeString(NumberToString(_total_costs)));
1526 _finance_table.SetOptionText(2, UTranslate("Sales: +") + MakeUnicodeString(NumberToString(_total_sales)));
1527 _finance_table.SetOptionText(3, UTranslate("Total: ") + MakeUnicodeString(NumberToString(GetTotalRemaining())));
1528 }
1529
1530
1531
ChangeState(SHOP_STATE new_state)1532 void ShopMode::ChangeState(SHOP_STATE new_state) {
1533 if (_state == new_state) {
1534 IF_PRINT_WARNING(SHOP_DEBUG) << "shop was already in the state to change to: " << _state << endl;
1535 return;
1536 }
1537
1538 _state = new_state;
1539
1540 // When state changes to the leave state, leave immediately if there are no marked purchases, sales, or trades
1541 if (_state == SHOP_STATE_LEAVE) {
1542 if ((GetTotalCosts() == 0) && (GetTotalSales() == 0)) {
1543 ModeManager->Pop();
1544 return;
1545 }
1546 }
1547
1548 switch (_state) {
1549 case SHOP_STATE_ROOT:
1550 _root_interface->MakeActive();
1551 break;
1552 case SHOP_STATE_BUY:
1553 _buy_interface->MakeActive();
1554 break;
1555 case SHOP_STATE_SELL:
1556 _sell_interface->MakeActive();
1557 break;
1558 case SHOP_STATE_TRADE:
1559 _trade_interface->MakeActive();
1560 break;
1561 case SHOP_STATE_CONFIRM:
1562 _confirm_interface->MakeActive();
1563 break;
1564 case SHOP_STATE_LEAVE:
1565 _leave_interface->MakeActive();
1566 break;
1567 default:
1568 IF_PRINT_WARNING(SHOP_DEBUG) << "invalid shop state: " << _state << endl;
1569 break;
1570 }
1571 }
1572
1573
1574
SetShopName(ustring name)1575 void ShopMode::SetShopName(ustring name) {
1576 if (IsInitialized() == true) {
1577 IF_PRINT_WARNING(SHOP_DEBUG) << "function called after shop was already initialized" << endl;
1578 return;
1579 }
1580
1581 _root_interface->SetShopName(name);
1582 }
1583
1584
1585
SetGreetingText(ustring greeting)1586 void ShopMode::SetGreetingText(ustring greeting) {
1587 if (IsInitialized() == true) {
1588 IF_PRINT_WARNING(SHOP_DEBUG) << "function called after shop was already initialized" << endl;
1589 return;
1590 }
1591
1592 _root_interface->SetGreetingText(greeting);
1593 }
1594
1595
1596
SetPriceLevels(SHOP_PRICE_LEVEL buy_level,SHOP_PRICE_LEVEL sell_level)1597 void ShopMode::SetPriceLevels(SHOP_PRICE_LEVEL buy_level, SHOP_PRICE_LEVEL sell_level) {
1598 if (IsInitialized() == true) {
1599 IF_PRINT_WARNING(SHOP_DEBUG) << "function called after shop was already initialized" << endl;
1600 return;
1601 }
1602
1603 _buy_price_level = buy_level;
1604 _sell_price_level = sell_level;
1605 }
1606
1607
1608
AddObject(uint32 object_id,uint32 stock)1609 void ShopMode::AddObject(uint32 object_id, uint32 stock) {
1610 if (IsInitialized() == true) {
1611 IF_PRINT_WARNING(SHOP_DEBUG) << "function called after shop was already initialized" << endl;
1612 return;
1613 }
1614
1615 if (object_id == private_global::OBJECT_ID_INVALID || object_id >= private_global::OBJECT_ID_EXCEEDS) {
1616 IF_PRINT_WARNING(SHOP_DEBUG) << "attempted to add object with invalid id: " << object_id << endl;
1617 return;
1618 }
1619
1620 if (_shop_objects.find(object_id) != _shop_objects.end()) {
1621 IF_PRINT_WARNING(SHOP_DEBUG) << "attempted to add object that already existed: " << object_id << endl;
1622 return;
1623 }
1624
1625 GlobalObject* new_object = GlobalCreateNewObject(object_id, 1);
1626 _created_objects.push_back(new_object);
1627 ShopObject new_shop_object(new_object, true);
1628 new_shop_object.IncrementStockCount(stock);
1629 _shop_objects.insert(make_pair(object_id, new_shop_object));
1630 }
1631
1632
1633
RemoveObject(uint32 object_id)1634 void ShopMode::RemoveObject(uint32 object_id) {
1635 map<uint32, ShopObject>::iterator shop_iter = _shop_objects.find(object_id);
1636 if (shop_iter == _shop_objects.end()) {
1637 IF_PRINT_WARNING(SHOP_DEBUG) << "attempted to remove object that did not exist: " << object_id << endl;
1638 return;
1639 }
1640
1641 if (shop_iter->second.IsSoldInShop() == true) {
1642 IF_PRINT_WARNING(SHOP_DEBUG) << "tried to remove object that is sold in shop: " << object_id << endl;
1643 return;
1644 }
1645
1646 if (shop_iter->second.GetOwnCount() != 0) {
1647 IF_PRINT_WARNING(SHOP_DEBUG) << "object's ownership count was non-zero: " << object_id << endl;
1648 return;
1649 }
1650
1651 _shop_objects.erase(shop_iter);
1652 }
1653
1654 } // namespace hoa_shop
1655