/* Copyright (C) 2008, 2009 TitanFiesta Dev Team * Licensed under GNU GPL v3 * For license details, see LICENCE in the root folder. */ #pragma once // 4 Tabs, 6x4 slots each. #define MAXINVSLOT 96 // (0 - 95) #define MAXEQSLOT 29 // (0 - 28) // Make our structs 1-byte packed. This way everything is properly aligned. #pragma pack (push, 1) // Disable the warning issued for having var[] at end of struct. Used for dynamic struct size. #pragma warning( push ) #pragma warning( disable: 4200 ) #include "main.h" #include "..\Common\CShn.hpp" // Data Type Definitions enum ItemClass { Regular, // 0 Unused1, Sen, Quest, Accessory, Weapon, // 5 Armor, Shield, MinorArmor, HouseItem, Costume, // 10 Skill, Scroll, Wing, Stone, PresentBox, // 15 License, Key, House, RedEye, BlueMile, // 20 Unused2, MoverFood, Mover, SuperPot, GoldNine, // 25 CostumeWeapon, ItemClassMax }; struct SItemStat { byte Stat; // 0: Str - 1: End - 2: Dex - 3: Int - 4: Spr word Value; }; struct SWeaponTitle { word MonsterId; dword KillCount; }; struct SItemBase { byte Length; byte Slot; byte Type; word Id; }; // Item Types struct SItemRegular : SItemBase { byte Count; }; struct SItemSen : SItemBase { // TODO: SItemSen Structure }; struct SItemQuest : SItemBase { word Count; }; struct SItemAccessory : SItemBase { f_date ExpireDate; byte Visible : 1; byte StatCount : 7; SItemStat Stats[]; }; struct SItemWeapon : SItemBase { byte Refine; word Unknown1; SWeaponTitle Title[3]; word Unknown2; byte Titler[0x11]; f_date ExpireDate; byte Visible : 1; byte StatCount : 7; SItemStat Stats[]; }; struct SItemArmor : SItemBase { byte Refine; word Unknown1; f_date ExpireDate; byte Visible : 1; byte StatCount : 7; SItemStat Stats[]; }; struct SItemShield : SItemBase { byte Refine; word Unknown1; f_date ExpireDate; byte Visible : 1; byte StatCount : 7; SItemStat Stats[]; }; struct SItemMinorArmor : SItemBase { byte Refine; word Unknown1; f_date ExpireDate; byte Visible : 1; byte StatCount : 7; SItemStat Stats[]; }; struct SItemHouseItem : SItemBase { // TODO: SItemHouseItem }; struct SItemCostume : SItemBase { // TODO: SItemCostume Structure }; struct SItemSkill : SItemBase { // TODO: SItemSkill Structure }; struct SItemScroll : SItemBase { byte Count; }; struct SItemWing : SItemBase { // TODO: SItemWing }; struct SItemStone : SItemBase { byte Count; }; struct SItemPresentBox : SItemBase { // TODO: SItemPresentBox }; struct SItemLicense : SItemBase { byte Unknown; }; struct SItemKey : SItemBase { // TODO: SItemKey }; struct SItemHouse : SItemBase { word Unknown1; // Matches SItemMover word Unknown2; // Matches SItemMover }; struct SItemRedEye : SItemBase { // TODO: SItemRedEye }; struct SItemBlueMile : SItemBase { // TODO: SItemBlueMile }; struct SItemMoverFood : SItemBase { byte Count; }; struct SItemMover : SItemBase { word Fuel; word Unknown1; // Matches SItemHouse word Unknown2; // Matches SItemHouse word Unknown3; }; struct SItemSuperPot : SItemBase { // TODO: SItemSuperPot }; struct SItemGoldNine : SItemBase { // TODO: SItemGoldNine }; struct SItemCostumeWeapon : SItemBase { // TODO: SItemCostumeWeapon }; #pragma pack ( pop ) #pragma warning ( pop ) // Class class CItemManager { public: CItemManager(CShn* ItemInfo, byte SlotCount) { _ItemInfo = ItemInfo; _SlotCount = SlotCount; _ItemCount = 0; _DataLength = 0; _Items = new SItemBase*[SlotCount]; memset(_Items, 0, sizeof(SItemBase*) * SlotCount); }; ~CItemManager() { for (int i = 0; i < _SlotCount; i++) { if (_Items[i] != NULL) FreeItem(_Items[i]); } delete[] _Items; }; // Returns how many items were loaded. byte LoadItemDump(byte* ItemBin) { // Don't load in if there's already items. if (_ItemCount > 0) return 0; byte* Pos = ItemBin; _ItemCount = *Pos++; _ItemType = *Pos++; for (int i = 0; i < _ItemCount; i++) { byte ItemSize = *Pos++; SItemBase* Item = (SItemBase*)new byte[ItemSize + 1]; memcpy((byte*)Item + 1, Pos, ItemSize); Item->Length = ItemSize; _Items[Item->Slot] = Item; _DataLength += ItemSize + 1; Pos += ItemSize; } return _ItemCount; }; // Dump item data to a binary blob. Sets &DataLength to size of blob. byte* DumpItems(word &DataLength) { byte* Buffer = new byte[_DataLength + 2]; byte* Pos = Buffer; *Pos++ = _ItemCount; *Pos++ = _ItemType; DataLength = _DataLength + 2; // If there are no items, we still return the count/type if (_ItemCount == 0) return Buffer; for (int i = 0; i < _SlotCount; i++) { if (_Items[i] == NULL) continue; memcpy(Pos, _Items[i], _Items[i]->Length + 1); Pos += _Items[i]->Length + 1; } return Buffer; }; void FreeDump(byte* ItemDump) { delete[] ItemDump; } SItemBase* CreateItem(word ItemId, byte ItemType, byte Stats) { if (_ItemInfo == NULL) return NULL; ItemClass Class = GetItemClass(ItemId); if (Class == ItemClassMax) return NULL; // If Class == 0 it could be item not found. Check if ID == 0 if (!Class && !_ItemInfo->GetDwordId(ItemId, 0)) return NULL; int Size = GetItemSize(Class); if (Size == -1) return NULL; // Allocate space for statcount/stats if required. if (Stats != 0) Size += Stats * sizeof(SItemStat) + 1; SItemBase* Item = (SItemBase*)new byte[Size]; memset(Item, 0, Size); Item->Id = ItemId; Item->Length = Size - 1; // -1 to account for Size byte Item->Slot = 0; Item->Type = ItemType << 2; // Special case for weapons, they have titles that need to be 0xff'd if (Class == 5) { SItemWeapon* Weapon = (SItemWeapon*)Item; memset(Weapon->Titler, 0xff, 0x11); Weapon->Title[0].MonsterId = 0xffff; Weapon->Title[1].MonsterId = 0xffff; Weapon->Title[2].MonsterId = 0xffff; } return Item; }; void FreeItem(SItemBase*& Item) { delete[] (byte*)Item; Item = NULL; }; bool SetItem(SItemBase* Item, byte Slot) { if (Slot > _SlotCount || _Items[Slot] != NULL) return false; _Items[Slot] = Item; Item->Slot = Slot; Item->Type = _ItemType << 2; _ItemCount++; _DataLength += Item->Length + 1; return true; } SItemBase* GetItem(byte Slot) { if (Slot > _SlotCount) return NULL; return _Items[Slot]; } word GetItemId(byte Slot) { // We return 0xffff if the item doesn't exist. It's how we handle null items in Fiesta. if (Slot > _SlotCount || _Items[Slot] == NULL) return 0xffff; return _Items[Slot]->Id; } bool RemoveItem(byte Slot, bool FreeMemory = true) { if (Slot > _SlotCount || _Items[Slot] == NULL) return false; _DataLength -= _Items[Slot]->Length + 1; if (FreeMemory) FreeItem(_Items[Slot]); _Items[Slot] = NULL; _ItemCount--; return true; } bool SwapItems(byte SourceSlot, byte DestSlot) { if (SourceSlot > _SlotCount || DestSlot > _SlotCount) return false; SItemBase* tmp = _Items[SourceSlot]; _Items[SourceSlot] = _Items[DestSlot]; _Items[DestSlot] = tmp; if (_Items[SourceSlot]) _Items[SourceSlot]->Slot = SourceSlot; if (_Items[DestSlot]) _Items[DestSlot]->Slot = DestSlot; return true; } // Function Definitions int GetItemSize(ItemClass c) { byte BaseSize = sizeof(SItemBase); switch (c) { case Regular: return sizeof(SItemRegular); case Sen: return sizeof(SItemSen); case Quest: return sizeof(SItemQuest); case Accessory: return sizeof(SItemAccessory); case Weapon: return sizeof(SItemWeapon); case Armor: return sizeof(SItemArmor); case Shield: return sizeof(SItemShield); case MinorArmor: return sizeof(SItemMinorArmor); case HouseItem: return sizeof(SItemHouseItem); case Costume: return sizeof(SItemCostume); case Skill: return sizeof(SItemSkill); case Scroll: return sizeof(SItemScroll); case Wing: return sizeof(SItemWing); case Stone: return sizeof(SItemStone); case PresentBox: return sizeof(SItemPresentBox); case License: return sizeof(SItemLicense); case Key: return sizeof(SItemKey); case House: return sizeof(SItemHouse); case RedEye: return sizeof(SItemRedEye); case BlueMile: return sizeof(SItemBlueMile); case MoverFood: return sizeof(SItemMoverFood); case Mover: return sizeof(SItemMover); case SuperPot: return sizeof(SItemSuperPot); case GoldNine: return sizeof(SItemGoldNine); case CostumeWeapon: return sizeof(SItemCostumeWeapon); default: return -1;//TODO: add error } }; ItemClass GetItemClass(word ItemId) { if (_ItemInfo == NULL) return ItemClassMax; return (ItemClass)_ItemInfo->GetDwordId(ItemId, 4); } word GetEquipSlot(word ItemId) { if (_ItemInfo == NULL) return 0; return _ItemInfo->GetDwordId(ItemId, 6); } // Returns the refine of the weapon in slot Slot. 0 if empty slot. // Does not check if the item is _actually_ refineable, just that there is // data in the Refine position. byte GetRefine(byte Slot) { if (Slot > _SlotCount || _Items[Slot] == NULL) return 0; if (_Items[Slot]->Length <= sizeof(SItemBase)) return 0; // Fetch ->Count as that is the location of Refine on any refine-able item. SItemRegular* Item = (SItemRegular*)_Items[Slot]; return Item->Count; } byte GetItemCount() {return _ItemCount;}; byte GetSlotCount() {return _SlotCount;}; byte ItemType() {return _ItemType;}; void ItemType(byte Type) {_ItemType = Type;}; int GetNextSlot() { for (int i = 0; i < _SlotCount; i++) { if (_Items[i] == NULL) return i; } return -1; } private: CShn* _ItemInfo; byte _SlotCount; byte _ItemCount; byte _ItemType; word _DataLength; SItemBase** _Items; };