From 2332bd7b58f129b2ad97178fb473afee9eb8a4c8 Mon Sep 17 00:00:00 2001 From: Ripolak Date: Tue, 23 Jun 2020 21:12:30 +0300 Subject: [PATCH] Feature/player equipment ui (#419) * Add basic EquipmentSlot struct * Add Load function to EquipmentSlot * Move EquipmentSlot struct to inventory_grid.go * Add equipmentSlots arg to ItemGrid struct * Add basic rendering of equipment slots. * Change rendering of equipment slots to simply use their static x and y * Add ChangeEquippedSlot function * Add initialization to all equipped slots types. * Fix Y locations of equipment slots * Move default equipment slots to a genEquipmentSlotsMap function. * Change Item to item * Fix coordinates * Change usage of string to EquippedSlotType when dealing with different slots in rendering. * Fix import error * Remove neck example * Add loading sprites of equipped items. * Clean code in Inventory rendering * Clean code in Inventory rendering * Clean code in Inventory rendering * Change default items that get rendered. * Change default items that get rendered. * Add width and height to EquipementSlot struct * Fill in width and height to current equipment slots. * Fix Y setting of equipment slots * Rename variables for clean code. * Change handling nil itemSprite to condition instead of loop return. * Split Render function to 2 functions. * Add TODO * Change comment to start with capital I --- d2common/d2enum/equipped_slot_type.go | 16 ++++ d2game/d2player/equipment_slot.go | 86 ++++++++++++++++++++ d2game/d2player/inventory.go | 11 ++- d2game/d2player/inventory_grid.go | 113 ++++++++++++++++---------- 4 files changed, 184 insertions(+), 42 deletions(-) create mode 100644 d2common/d2enum/equipped_slot_type.go create mode 100644 d2game/d2player/equipment_slot.go diff --git a/d2common/d2enum/equipped_slot_type.go b/d2common/d2enum/equipped_slot_type.go new file mode 100644 index 00000000..660cf875 --- /dev/null +++ b/d2common/d2enum/equipped_slot_type.go @@ -0,0 +1,16 @@ +package d2enum + +type EquippedSlotType int + +const ( + Head EquippedSlotType = 1 + Torso EquippedSlotType = 2 + Legs EquippedSlotType = 3 + RightArm EquippedSlotType = 4 + LeftArm EquippedSlotType = 5 + LeftHand EquippedSlotType = 6 + RightHand EquippedSlotType = 7 + Neck EquippedSlotType = 8 + Belt EquippedSlotType = 9 + Gloves EquippedSlotType = 10 +) diff --git a/d2game/d2player/equipment_slot.go b/d2game/d2player/equipment_slot.go new file mode 100644 index 00000000..30ae5582 --- /dev/null +++ b/d2game/d2player/equipment_slot.go @@ -0,0 +1,86 @@ +package d2player + +import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + +type EquipmentSlot struct { + item InventoryItem + x int + y int + width int + height int +} + +func genEquipmentSlotsMap() map[d2enum.EquippedSlotType]EquipmentSlot { + return map[d2enum.EquippedSlotType]EquipmentSlot{ + d2enum.LeftArm: { + item: nil, + x: 418, + y: 224, + width: 61, + height: 116, + }, + d2enum.RightArm: { + item: nil, + x: 648, + y: 224, + width: 61, + height: 116, + }, + d2enum.Head: { + item: nil, + x: 532, + y: 125, + width: 62, + height: 62, + }, + d2enum.Neck: { + item: nil, + x: 604, + y: 125, + width: 32, + height: 32, + }, + d2enum.Torso: { + item: nil, + x: 532, + y: 224, + width: 62, + height: 90, + }, + d2enum.Belt: { + item: nil, + x: 533, + y: 269, + width: 62, + height: 32, + }, + d2enum.LeftHand: { + item: nil, + x: 491, + y: 268, + width: 32, + height: 32, + }, + d2enum.RightHand: { + item: nil, + x: 606, + y: 268, + width: 32, + height: 32, + }, + d2enum.Gloves: { + item: nil, + x: 417, + y: 299, + width: 62, + height: 62, + }, + d2enum.Legs: { + item: nil, + x: 648, + y: 299, + width: 62, + height: 62, + }, + } +} diff --git a/d2game/d2player/inventory.go b/d2game/d2player/inventory.go index a14f1b1f..bb4f6047 100644 --- a/d2game/d2player/inventory.go +++ b/d2game/d2player/inventory.go @@ -1,6 +1,7 @@ package d2player import ( + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" @@ -49,14 +50,22 @@ func (g *Inventory) Load() { animation, _ = d2asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky) g.panel, _ = d2ui.LoadSprite(animation) - items := []InventoryItem{ d2inventory.GetWeaponItemByCode("wnd"), d2inventory.GetWeaponItemByCode("sst"), d2inventory.GetWeaponItemByCode("jav"), d2inventory.GetArmorItemByCode("buc"), d2inventory.GetWeaponItemByCode("clb"), + // TODO: Load the player's actual items } + g.grid.ChangeEquippedSlot(d2enum.LeftArm, d2inventory.GetWeaponItemByCode("wnd")) + g.grid.ChangeEquippedSlot(d2enum.RightArm, d2inventory.GetArmorItemByCode("buc")) + g.grid.ChangeEquippedSlot(d2enum.Head, d2inventory.GetArmorItemByCode("crn")) + g.grid.ChangeEquippedSlot(d2enum.Torso, d2inventory.GetArmorItemByCode("plt")) + g.grid.ChangeEquippedSlot(d2enum.Legs, d2inventory.GetArmorItemByCode("vbt")) + g.grid.ChangeEquippedSlot(d2enum.Belt, d2inventory.GetArmorItemByCode("vbl")) + g.grid.ChangeEquippedSlot(d2enum.Gloves, d2inventory.GetArmorItemByCode("lgl")) + // TODO: Load the player's actual items g.grid.Add(items...) } diff --git a/d2game/d2player/inventory_grid.go b/d2game/d2player/inventory_grid.go index 8a47f49c..561da31a 100644 --- a/d2game/d2player/inventory_grid.go +++ b/d2game/d2player/inventory_grid.go @@ -3,6 +3,7 @@ package d2player import ( "errors" "fmt" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "log" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" @@ -24,23 +25,25 @@ var ErrorInventoryFull = errors.New("inventory full") // Reusable grid for use with player and merchant inventory. // Handles layout and rendering item icons based on code. type ItemGrid struct { - items []InventoryItem - width int - height int - originX int - originY int - sprites map[string]*d2ui.Sprite - slotSize int + items []InventoryItem + equipmentSlots map[d2enum.EquippedSlotType]EquipmentSlot + width int + height int + originX int + originY int + sprites map[string]*d2ui.Sprite + slotSize int } func NewItemGrid(width int, height int, originX int, originY int) *ItemGrid { return &ItemGrid{ - width: width, - height: height, - originX: originX, - originY: originY, - slotSize: 29, - sprites: make(map[string]*d2ui.Sprite), + width: width, + height: height, + originX: originX, + originY: originY, + slotSize: 29, + sprites: make(map[string]*d2ui.Sprite), + equipmentSlots: genEquipmentSlotsMap(), } } @@ -69,6 +72,12 @@ func (g *ItemGrid) GetSlot(x int, y int) InventoryItem { return nil } +func (g *ItemGrid) ChangeEquippedSlot(slot d2enum.EquippedSlotType, item InventoryItem) { + var curItem = g.equipmentSlots[slot] + curItem.item = item + g.equipmentSlots[slot] = curItem +} + // Add places a given set of items into the first available slots. // Returns a count of the number of items which could be inserted. func (g *ItemGrid) Add(items ...InventoryItem) (int, error) { @@ -89,15 +98,9 @@ func (g *ItemGrid) Add(items ...InventoryItem) (int, error) { return added, err } -// Load reads the inventory sprites for items into local cache for rendering. -func (g *ItemGrid) Load(items ...InventoryItem) { - var itemSprite *d2ui.Sprite - - for _, item := range items { - if _, exists := g.sprites[item.GetItemCode()]; exists { - // Already loaded, don't reload. - continue - } +func (g *ItemGrid) loadItem(item InventoryItem) { + if _, exists := g.sprites[item.GetItemCode()]; !exists { + var itemSprite *d2ui.Sprite // TODO: Put the pattern into D2Shared animation, err := d2asset.LoadAnimation( @@ -106,13 +109,26 @@ func (g *ItemGrid) Load(items ...InventoryItem) { ) if err != nil { log.Printf("failed to load sprite for item (%s): %v", item.GetItemCode(), err) - continue + return } itemSprite, err = d2ui.LoadSprite(animation) - + if err != nil { + log.Printf("Failed to load sprite, error: " + err.Error()) + } g.sprites[item.GetItemCode()] = itemSprite } +} +// Load reads the inventory sprites for items into local cache for rendering. +func (g *ItemGrid) Load(items ...InventoryItem) { + for _, item := range items { + g.loadItem(item) + } + for _, eq := range g.equipmentSlots { + if eq.item != nil { + g.loadItem(eq.item) + } + } } // Walk from top left to bottom right until a position large enough to hold the item is found. @@ -183,23 +199,38 @@ func (g *ItemGrid) Remove(item InventoryItem) { g.items = g.items[:n] } -func (g *ItemGrid) Render(target d2render.Surface) { - for _, item := range g.items { - if item == nil { - continue - } - - itemSprite := g.sprites[item.GetItemCode()] - if itemSprite == nil { - // In case it failed to load. - // TODO: fallback to something - continue - } - - slotX, slotY := g.SlotToScreen(item.InventoryGridSlot()) - _, h := itemSprite.GetCurrentFrameSize() - itemSprite.SetPosition(slotX, slotY+h) +func (g *ItemGrid) renderItem(item InventoryItem, target d2render.Surface, x int, y int) { + itemSprite := g.sprites[item.GetItemCode()] + if itemSprite != nil { + itemSprite.SetPosition(x, y) + itemSprite.GetCurrentFrameSize() _ = itemSprite.Render(target) - + } +} + +func (g *ItemGrid) Render(target d2render.Surface) { + g.renderInventoryItems(target) + g.renderEquippedItems(target) +} + +func (g *ItemGrid) renderInventoryItems(target d2render.Surface) { + for _, item := range g.items { + itemSprite := g.sprites[item.GetItemCode()] + slotX, slotY := g.SlotToScreen(item.InventoryGridSlot()) + _, h := itemSprite.GetCurrentFrameSize() + slotY = slotY + h + g.renderItem(item, target, slotX, slotY) + } +} + +func (g *ItemGrid) renderEquippedItems(target d2render.Surface) { + for _, eq := range g.equipmentSlots { + if eq.item != nil { + itemSprite := g.sprites[eq.item.GetItemCode()] + itemWidth, itemHeight := itemSprite.GetCurrentFrameSize() + var x = eq.x + ((eq.width - itemWidth) / 2) + var y = eq.y - ((eq.height - itemHeight) / 2) + g.renderItem(eq.item, target, x, y) + } } }