1 ////////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2011 by The Allacrost Project
3 //            Copyright (C) 2012-2018 by Bertram (Valyria Tear)
4 //                         All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software
7 // and you may modify it and/or redistribute it under the terms of this license.
8 // See http://www.gnu.org/copyleft/gpl.html for details.
9 ////////////////////////////////////////////////////////////////////////////////
10 
11 #include "global_inventory_handler.h"
12 
13 #include "script/script_read.h"
14 
15 using namespace vt_script;
16 
17 namespace vt_global
18 {
19 
~InventoryHandler()20 InventoryHandler::~InventoryHandler()
21 {
22     CloseScripts();
23 }
24 
LoadScripts()25 bool InventoryHandler::LoadScripts()
26 {
27     // Open up the persistent script files
28     if(!_items_script.OpenFile("data/inventory/items.lua") || !_items_script.OpenTable("items"))
29         return false;
30 
31     if(!_weapons_script.OpenFile("data/inventory/weapons.lua") || !_weapons_script.OpenTable("weapons"))
32         return false;
33 
34     if(!_head_armor_script.OpenFile("data/inventory/head_armor.lua") || !_head_armor_script.OpenTable("armor"))
35         return false;
36 
37     if(!_torso_armor_script.OpenFile("data/inventory/torso_armor.lua") || !_torso_armor_script.OpenTable("armor"))
38         return false;
39 
40     if(!_arm_armor_script.OpenFile("data/inventory/arm_armor.lua") || !_arm_armor_script.OpenTable("armor"))
41         return false;
42 
43     if(!_leg_armor_script.OpenFile("data/inventory/leg_armor.lua") || !_leg_armor_script.OpenTable("armor"))
44         return false;
45 
46     if(!_spirits_script.OpenFile("data/inventory/spirits.lua") || !_spirits_script.OpenTable("spirits"))
47         return false;
48 
49     return true;
50 }
51 
CloseScripts()52 void InventoryHandler::CloseScripts()
53 {
54     // Close all persistent script files
55     _items_script.CloseTable();
56     _items_script.CloseFile();
57 
58     _weapons_script.CloseTable();
59     _weapons_script.CloseFile();
60 
61     _head_armor_script.CloseTable();
62     _head_armor_script.CloseFile();
63 
64     _torso_armor_script.CloseTable();
65     _torso_armor_script.CloseFile();
66 
67     _arm_armor_script.CloseTable();
68     _arm_armor_script.CloseFile();
69 
70     _leg_armor_script.CloseTable();
71     _leg_armor_script.CloseFile();
72 
73     _spirits_script.CloseTable();
74     _spirits_script.CloseFile();
75 }
76 
ClearAllData()77 void InventoryHandler::ClearAllData()
78 {
79     _inventory.clear();
80     _inventory_items.clear();
81     _inventory_weapons.clear();
82     _inventory_head_armors.clear();
83     _inventory_torso_armors.clear();
84     _inventory_arm_armors.clear();
85     _inventory_leg_armors.clear();
86     _inventory_spirits.clear();
87     _inventory_key_items.clear();
88 }
89 
GetInventoryArmors(GLOBAL_OBJECT object_type)90 std::vector<std::shared_ptr<GlobalArmor>>& InventoryHandler::GetInventoryArmors(GLOBAL_OBJECT object_type)
91 {
92     switch(object_type) {
93     default:
94         PRINT_WARNING << "Invalid object type provided. Returning default container" << std::endl;
95         /* Falls through. */
96     case GLOBAL_OBJECT_HEAD_ARMOR:
97         return _inventory_head_armors;
98     case GLOBAL_OBJECT_TORSO_ARMOR:
99         return _inventory_torso_armors;
100     case GLOBAL_OBJECT_ARM_ARMOR:
101         return _inventory_arm_armors;
102     case GLOBAL_OBJECT_LEG_ARMOR:
103         return _inventory_leg_armors;
104     }
105 }
106 
AddToInventory(uint32_t obj_id,uint32_t obj_count)107 void InventoryHandler::AddToInventory(uint32_t obj_id, uint32_t obj_count)
108 {
109     // Don't add object instance without at least one actual item.
110     if (obj_count == 0)
111         return;
112 
113     // If the object is already in the inventory, increment the count of the object.
114     if (_inventory.find(obj_id) != _inventory.end()) {
115         _inventory[obj_id]->IncrementCount(obj_count);
116         return;
117     }
118 
119     // Otherwise, create a new object instance and add it to the inventory.
120     std::shared_ptr<GlobalObject> new_object = nullptr;
121     if ((obj_id > 0 && obj_id <= MAX_ITEM_ID) ||
122         (obj_id > MAX_SPIRIT_ID && obj_id <= MAX_KEY_ITEM_ID)) {
123         auto new_item = std::make_shared<GlobalItem>(obj_id, obj_count);
124         new_object = new_item;
125         _inventory.insert(std::make_pair(obj_id, new_object));
126         _inventory_items.push_back(new_item);
127     } else if ((obj_id > MAX_ITEM_ID) && (obj_id <= MAX_WEAPON_ID)) {
128         auto new_wpn = std::make_shared<GlobalWeapon>(obj_id, obj_count);
129         new_object = new_wpn;
130         _inventory.insert(std::make_pair(obj_id, new_wpn));
131         _inventory_weapons.push_back(new_wpn);
132     } else if ((obj_id > MAX_WEAPON_ID) && (obj_id <= MAX_HEAD_ARMOR_ID)) {
133         auto new_arm = std::make_shared<GlobalArmor>(obj_id, obj_count);
134         new_object = new_arm;
135         _inventory.insert(std::make_pair(obj_id, new_arm));
136         _inventory_head_armors.push_back(new_arm);
137     } else if ((obj_id > MAX_HEAD_ARMOR_ID) && (obj_id <= MAX_TORSO_ARMOR_ID)) {
138         auto new_arm = std::make_shared<GlobalArmor>(obj_id, obj_count);
139         new_object = new_arm;
140         _inventory.insert(std::make_pair(obj_id, new_arm));
141         _inventory_torso_armors.push_back(new_arm);
142     } else if ((obj_id > MAX_TORSO_ARMOR_ID) && (obj_id <= MAX_ARM_ARMOR_ID)) {
143         auto new_arm = std::make_shared<GlobalArmor>(obj_id, obj_count);
144         new_object = new_arm;
145         _inventory.insert(std::make_pair(obj_id, new_arm));
146         _inventory_arm_armors.push_back(new_arm);
147     } else if ((obj_id > MAX_ARM_ARMOR_ID) && (obj_id <= MAX_LEG_ARMOR_ID)) {
148         auto new_arm = std::make_shared<GlobalArmor>(obj_id, obj_count);
149         new_object = new_arm;
150         _inventory.insert(std::make_pair(obj_id, new_arm));
151         _inventory_leg_armors.push_back(new_arm);
152     } else if ((obj_id > MAX_LEG_ARMOR_ID) && (obj_id <= MAX_SPIRIT_ID)) {
153         auto new_spirit = std::make_shared<GlobalSpirit>(obj_id, obj_count);
154         new_object = new_spirit;
155         _inventory.insert(std::make_pair(obj_id, new_spirit));
156         _inventory_spirits.push_back(new_spirit);
157     } else {
158         PRINT_WARNING << "attempted to add invalid object to inventory with id: " << obj_id << std::endl;
159     }
160 
161     // Update the key items list.
162     if (new_object != nullptr &&
163         new_object->IsKeyItem()) {
164         _inventory_key_items.push_back(new_object);
165     }
166 }
167 
AddToInventory(const std::shared_ptr<GlobalObject> & object)168 void InventoryHandler::AddToInventory(const std::shared_ptr<GlobalObject>& object)
169 {
170     // Don't process null object addition.
171     if (object == nullptr) {
172         return;
173     }
174 
175     uint32_t obj_id = object->GetID();
176     uint32_t obj_count = object->GetCount();
177 
178     // Don't add object instance without at least one actual item.
179     if (obj_count == 0) {
180         return;
181     }
182 
183     // If an instance of the same object is already inside the inventory, just increment the count.
184     if (_inventory.find(obj_id) != _inventory.end()) {
185         _inventory[obj_id]->IncrementCount(obj_count);
186         return;
187     }
188 
189     // Figure out which type of object this is, cast it to the correct type, and add it to the inventory
190     if((obj_id > 0 && obj_id <= MAX_ITEM_ID) ||
191        (obj_id > MAX_SPIRIT_ID && obj_id <= MAX_KEY_ITEM_ID)) {
192         auto new_obj = std::dynamic_pointer_cast<GlobalItem>(object);
193         _inventory.insert(std::make_pair(obj_id, new_obj));
194         _inventory_items.push_back(new_obj);
195     } else if((obj_id > MAX_ITEM_ID) && (obj_id <= MAX_WEAPON_ID)) {
196         auto new_obj = std::dynamic_pointer_cast<GlobalWeapon>(object);
197         _inventory.insert(std::make_pair(obj_id, new_obj));
198         _inventory_weapons.push_back(new_obj);
199     } else if((obj_id > MAX_WEAPON_ID) && (obj_id <= MAX_HEAD_ARMOR_ID)) {
200         auto new_obj = std::dynamic_pointer_cast<GlobalArmor>(object);
201         _inventory.insert(std::make_pair(obj_id, new_obj));
202         _inventory_head_armors.push_back(new_obj);
203     } else if((obj_id > MAX_HEAD_ARMOR_ID) && (obj_id <= MAX_TORSO_ARMOR_ID)) {
204         auto new_obj = std::dynamic_pointer_cast<GlobalArmor>(object);
205         _inventory.insert(std::make_pair(obj_id, new_obj));
206         _inventory_torso_armors.push_back(new_obj);
207     } else if((obj_id > MAX_TORSO_ARMOR_ID) && (obj_id <= MAX_ARM_ARMOR_ID)) {
208         auto new_obj = std::dynamic_pointer_cast<GlobalArmor>(object);
209         _inventory.insert(std::make_pair(obj_id, new_obj));
210         _inventory_arm_armors.push_back(new_obj);
211     } else if((obj_id > MAX_ARM_ARMOR_ID) && (obj_id <= MAX_LEG_ARMOR_ID)) {
212         auto new_obj = std::dynamic_pointer_cast<GlobalArmor>(object);
213         _inventory.insert(std::make_pair(obj_id, new_obj));
214         _inventory_leg_armors.push_back(new_obj);
215     } else if((obj_id > MAX_LEG_ARMOR_ID) && (obj_id <= MAX_SPIRIT_ID)) {
216         auto new_obj = std::dynamic_pointer_cast<GlobalSpirit>(object);
217         _inventory.insert(std::make_pair(obj_id, new_obj));
218         _inventory_spirits.push_back(new_obj);
219     } else {
220         PRINT_WARNING << "attempted to add invalid object to inventory with id: " << obj_id << std::endl;
221         return;
222     }
223 
224     // Updates the key items list.
225     if (object->IsKeyItem()) {
226         _inventory_key_items.push_back(object);
227     }
228 }
229 
RemoveFromInventory(uint32_t obj_id)230 void InventoryHandler::RemoveFromInventory(uint32_t obj_id)
231 {
232     auto it = _inventory.find(obj_id);
233     if (it == _inventory.end()) {
234         PRINT_WARNING << "attempted to remove an object from inventory that didn't exist with id: " << obj_id << std::endl;
235         return;
236     }
237 
238     // Check whether the item is a key item to remove.
239     auto object = it->second;
240     if (object != nullptr && object->IsKeyItem()) {
241         for (auto it2 = _inventory_key_items.begin(); it2 != _inventory_key_items.end(); ++it2) {
242 
243             if ((*it2)->GetID() != obj_id)
244                 continue;
245 
246             _inventory_key_items.erase(it2);
247             break;
248         }
249     }
250 
251     // Use the id value to figure out what type of object it is, and remove it from the object vector
252     if((obj_id > 0 && obj_id <= MAX_ITEM_ID) ||
253        (obj_id > MAX_SPIRIT_ID && obj_id <= MAX_KEY_ITEM_ID)) {
254         if(_RemoveFromInventory(obj_id, _inventory_items) == false)
255             PRINT_WARNING << "object to remove was not found in inventory items: " << obj_id << std::endl;
256     } else if((obj_id > MAX_ITEM_ID) && (obj_id <= MAX_WEAPON_ID)) {
257         if(_RemoveFromInventory(obj_id, _inventory_weapons) == false)
258             PRINT_WARNING << "object to remove was not found in inventory weapons: " << obj_id << std::endl;
259     } else if((obj_id > MAX_WEAPON_ID) && (obj_id <= MAX_HEAD_ARMOR_ID)) {
260         if(_RemoveFromInventory(obj_id, _inventory_head_armors) == false)
261             PRINT_WARNING << "object to remove was not found in inventory head armor: " << obj_id << std::endl;
262     } else if((obj_id > MAX_HEAD_ARMOR_ID) && (obj_id <= MAX_TORSO_ARMOR_ID)) {
263         if(_RemoveFromInventory(obj_id, _inventory_torso_armors) == false)
264             PRINT_WARNING << "object to remove was not found in inventory torso armor: " << obj_id << std::endl;
265     } else if((obj_id > MAX_TORSO_ARMOR_ID) && (obj_id <= MAX_ARM_ARMOR_ID)) {
266         if(_RemoveFromInventory(obj_id, _inventory_arm_armors) == false)
267             PRINT_WARNING << "object to remove was not found in inventory arm armor: " << obj_id << std::endl;
268     } else if((obj_id > MAX_ARM_ARMOR_ID) && (obj_id <= MAX_LEG_ARMOR_ID)) {
269         if(_RemoveFromInventory(obj_id, _inventory_leg_armors) == false)
270             PRINT_WARNING << "object to remove was not found in inventory leg armor: " << obj_id << std::endl;
271     } else if((obj_id > MAX_LEG_ARMOR_ID) && (obj_id <= MAX_SPIRIT_ID)) {
272         if(_RemoveFromInventory(obj_id, _inventory_spirits) == false)
273             PRINT_WARNING << "object to remove was not found in inventory spirits: " << obj_id << std::endl;
274     } else {
275         PRINT_WARNING << "attempted to remove an object from inventory with an invalid id: " << obj_id << std::endl;
276     }
277 }
278 
GetGlobalObject(uint32_t obj_id)279 std::shared_ptr<GlobalObject> InventoryHandler::GetGlobalObject(uint32_t obj_id)
280 {
281     if (_inventory.find(obj_id) == _inventory.end()) {
282         return nullptr;
283     }
284 
285     std::shared_ptr<GlobalObject> return_object = nullptr;
286     // Use the id value to figure out what type of object it is, and remove it from the object vector
287     if((obj_id > 0 && obj_id <= MAX_ITEM_ID) ||
288        (obj_id > MAX_SPIRIT_ID && obj_id <= MAX_KEY_ITEM_ID)) {
289         return_object = _GetFromInventory(obj_id, _inventory_items);
290         if(return_object == nullptr)
291             PRINT_WARNING << "object to retrieve was not found in inventory items: " << obj_id << std::endl;
292     } else if((obj_id > MAX_ITEM_ID) && (obj_id <= MAX_WEAPON_ID)) {
293         return_object = _GetFromInventory(obj_id, _inventory_weapons);
294         if(return_object == nullptr)
295             PRINT_WARNING << "object to retrieve was not found in inventory weapons: " << obj_id << std::endl;
296     } else if((obj_id > MAX_WEAPON_ID) && (obj_id <= MAX_HEAD_ARMOR_ID)) {
297         return_object = _GetFromInventory(obj_id, _inventory_head_armors);
298         if(return_object == nullptr)
299             PRINT_WARNING << "object to retrieve was not found in inventory head armor: " << obj_id << std::endl;
300     } else if((obj_id > MAX_HEAD_ARMOR_ID) && (obj_id <= MAX_TORSO_ARMOR_ID)) {
301         return_object = _GetFromInventory(obj_id, _inventory_torso_armors);
302         if(return_object == nullptr)
303             PRINT_WARNING << "object to retrieve was not found in inventory torso armor: " << obj_id << std::endl;
304     } else if((obj_id > MAX_TORSO_ARMOR_ID) && (obj_id <= MAX_ARM_ARMOR_ID)) {
305         return_object = _GetFromInventory(obj_id, _inventory_arm_armors);
306         if(return_object == nullptr)
307             PRINT_WARNING << "object to retrieve was not found in inventory arm armor: " << obj_id << std::endl;
308     } else if((obj_id > MAX_ARM_ARMOR_ID) && (obj_id <= MAX_LEG_ARMOR_ID)) {
309         return_object = _GetFromInventory(obj_id, _inventory_leg_armors);
310         if(return_object == nullptr)
311             PRINT_WARNING << "object to retrieve was not found in inventory leg armor: " << obj_id << std::endl;
312     } else if((obj_id > MAX_LEG_ARMOR_ID) && (obj_id <= MAX_SPIRIT_ID)) {
313         return_object = _GetFromInventory(obj_id, _inventory_spirits);
314         if(return_object == nullptr)
315             PRINT_WARNING << "object to retrieve was not found in inventory spirits: " << obj_id << std::endl;
316     } else {
317         PRINT_WARNING << "attempted to retrieve an object from inventory with an invalid id: " << obj_id << std::endl;
318     }
319 
320     return return_object;
321 }
322 
IncrementItemCount(uint32_t obj_id,uint32_t count)323 void InventoryHandler::IncrementItemCount(uint32_t obj_id, uint32_t count)
324 {
325     // Do nothing if the item does not exist in the inventory
326     if(_inventory.find(obj_id) == _inventory.end()) {
327         PRINT_WARNING << "attempted to increment count for an object that was not present in the inventory: " << obj_id << std::endl;
328         return;
329     }
330 
331     _inventory[obj_id]->IncrementCount(count);
332 }
333 
DecrementItemCount(uint32_t obj_id,uint32_t count)334 void InventoryHandler::DecrementItemCount(uint32_t obj_id, uint32_t count)
335 {
336     // Do nothing if the item does not exist in the inventory
337     if(_inventory.find(obj_id) == _inventory.end()) {
338         PRINT_WARNING << "attempted to decrement count for an object that was not present in the inventory: " << obj_id << std::endl;
339         return;
340     }
341 
342     // Print a warning if the amount to decrement by exceeds the object's current count
343     if(count > _inventory[obj_id]->GetCount()) {
344         PRINT_WARNING << "amount to decrement count by exceeded available count: " << obj_id << std::endl;
345     }
346 
347     // Decrement the number of objects so long as the number to decrement by does not equal or exceed the count
348     if(count < _inventory[obj_id]->GetCount())
349         _inventory[obj_id]->DecrementCount(count);
350     // Otherwise remove the object from the inventory completely
351     else
352         RemoveFromInventory(obj_id);
353 }
354 
SaveInventory(vt_script::WriteScriptDescriptor & file)355 void InventoryHandler::SaveInventory(vt_script::WriteScriptDescriptor& file)
356 {
357     // Save the inventory (object id + object count pairs)
358     // NOTE: This does not save any weapons/armor that are equipped on the characters. That data
359     // is stored alongside the character data when it is saved
360     _SaveInventory(file, "items", _inventory_items);
361     _SaveInventory(file, "weapons", _inventory_weapons);
362     _SaveInventory(file, "head_armor", _inventory_head_armors);
363     _SaveInventory(file, "torso_armor", _inventory_torso_armors);
364     _SaveInventory(file, "arm_armor", _inventory_arm_armors);
365     _SaveInventory(file, "leg_armor", _inventory_leg_armors);
366     _SaveInventory(file, "spirits", _inventory_spirits);
367 }
368 
LoadInventory(vt_script::ReadScriptDescriptor & file)369 void InventoryHandler::LoadInventory(vt_script::ReadScriptDescriptor& file)
370 {
371     ClearAllData();
372 
373     _LoadInventory(file, "items");
374     _LoadInventory(file, "weapons");
375     _LoadInventory(file, "head_armor");
376     _LoadInventory(file, "torso_armor");
377     _LoadInventory(file, "arm_armor");
378     _LoadInventory(file, "leg_armor");
379     _LoadInventory(file, "spirits");
380 }
381 
_LoadInventory(ReadScriptDescriptor & file,const std::string & category_name)382 void InventoryHandler::_LoadInventory(ReadScriptDescriptor& file, const std::string& category_name)
383 {
384     if(file.IsFileOpen() == false) {
385         PRINT_WARNING << "the file provided in the function argument was not open" << std::endl;
386         return;
387     }
388 
389     // The table keys are the inventory object ID numbers. The value of each key is the count of that object
390     if (file.OpenTable(category_name)) {
391         std::vector<uint32_t> object_ids;
392         file.ReadTableKeys(object_ids);
393         for(uint32_t i = 0; i < object_ids.size(); i++) {
394             AddToInventory(object_ids[i], file.ReadUInt(object_ids[i]));
395         }
396         file.CloseTable();
397     }
398 }
399 
400 } // namespace vt_global
401