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