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