1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-09-21 18:56:15 -04:00
OpenDiablo2/d2game/d2player/inventory.go
juander-ux e5dae4e5d8
Inital skilltree panel implementation (#782)
* d2ui/UIFrame: Refactor into its own class

it's not useful to have the handling of frames for the
inventory/herostate/skilltree/quest panels individually in each of
those.

* d2ui/button: Fix crash when a buttonlayout was not allowing FrameChange

When AllowFrameChange is false we do not create pressedSurface. So if we
press the button the game will crash.

* d2ui/button: Allow label-only buttons

At least for the skillmenu we need buttons were the graphic size does
not match the buttonsize. So let's render the graphic in there and make
the button label only.

* d2hero/hero_state_factory: Give all heroes their class specific skills

* d2player/gamecontrols: Fix wrong inventory/stats layouts for exp chars

For Druid/Assassin the inventory frame was rendered for a 640x480
resolution. This brings it in line with all other characters.

* d2player: Add inital Skilltree panel

* d2player/game_controls: Enable skilltree

Note here, that the inventory panel and skilltree panel can overlap.

* d2player/skilltree: Add skillicon rendering

Note here, that I couldn't figure out how to render them dark if no
skillpoints are invested.

Signed-off-by: juander <juander@rumtueddeln.de>
2020-10-22 12:54:45 -04:00

270 lines
6.0 KiB
Go

package d2player
import (
"fmt"
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2item/diablo2item"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
// Inventory represents the inventory
type Inventory struct {
asset *d2asset.AssetManager
item *diablo2item.ItemFactory
uiManager *d2ui.UIManager
frame *d2ui.UIFrame
panel *d2ui.Sprite
grid *ItemGrid
hoverLabel *d2ui.Label
hoverX int
hoverY int
originX int
originY int
lastMouseX int
lastMouseY int
hovering bool
isOpen bool
}
// NewInventory creates an inventory instance and returns a pointer to it
func NewInventory(asset *d2asset.AssetManager, ui *d2ui.UIManager,
record *d2records.InventoryRecord) *Inventory {
hoverLabel := ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteStatic)
hoverLabel.Alignment = d2gui.HorizontalAlignCenter
itemFactory, _ := diablo2item.NewItemFactory(asset) // TODO handle errors
return &Inventory{
asset: asset,
uiManager: ui,
item: itemFactory,
grid: NewItemGrid(asset, ui, record),
originX: record.Panel.Left,
hoverLabel: hoverLabel,
// originY: record.Panel.Top,
originY: 0, // expansion data has these all offset by +60 ...
}
}
// IsOpen returns true if the inventory is open
func (g *Inventory) IsOpen() bool {
return g.isOpen
}
// Toggle negates the open state of the inventory
func (g *Inventory) Toggle() {
g.isOpen = !g.isOpen
}
// Open opens the inventory
func (g *Inventory) Open() {
g.isOpen = true
}
// Close closes the inventory
func (g *Inventory) Close() {
g.isOpen = false
}
// Load the resources required by the inventory
func (g *Inventory) Load() {
g.frame = d2ui.NewUIFrame(g.asset, g.uiManager, d2ui.FrameRight)
g.panel, _ = g.uiManager.NewSprite(d2resource.InventoryCharacterPanel, d2resource.PaletteSky)
// TODO: remove this item test code
testInventoryCodes := [][]string{
{"kit", "Crimson", "of the Bat", "of Frost"},
{"rin", "Steel", "of Shock"},
{"jav"},
{"buc"},
}
inventoryItems := make([]InventoryItem, 0)
for idx := range testInventoryCodes {
item, err := g.item.NewItem(testInventoryCodes[idx]...)
if err != nil {
continue
}
item.Identify()
inventoryItems = append(inventoryItems, item)
}
testEquippedItemCodes := map[d2enum.EquippedSlot][]string{
d2enum.EquippedSlotLeftArm: {"wnd"},
d2enum.EquippedSlotRightArm: {"buc"},
d2enum.EquippedSlotHead: {"crn"},
d2enum.EquippedSlotTorso: {"plt"},
d2enum.EquippedSlotLegs: {"vbt"},
d2enum.EquippedSlotBelt: {"vbl"},
d2enum.EquippedSlotGloves: {"lgl"},
d2enum.EquippedSlotLeftHand: {"rin"},
d2enum.EquippedSlotRightHand: {"rin"},
d2enum.EquippedSlotNeck: {"amu"},
}
for slot := range testEquippedItemCodes {
item, err := g.item.NewItem(testEquippedItemCodes[slot]...)
if err != nil {
continue
}
g.grid.ChangeEquippedSlot(slot, item)
}
// TODO: Load the player's actual items
_, err := g.grid.Add(inventoryItems...)
if err != nil {
fmt.Printf("could not add items to the inventory, err: %v\n", err)
}
}
// Render draws the inventory onto the given surface
func (g *Inventory) Render(target d2interface.Surface) error {
if !g.isOpen {
return nil
}
g.frame.Render(target)
x, y := g.originX+1, g.originY
y += 64
// Panel
// Top left
if err := g.panel.SetCurrentFrame(4); err != nil {
return err
}
w, h := g.panel.GetCurrentFrameSize()
g.panel.SetPosition(x, y+h)
if err := g.panel.Render(target); err != nil {
return err
}
x += w
// Top right
if err := g.panel.SetCurrentFrame(5); err != nil {
return err
}
_, h = g.panel.GetCurrentFrameSize()
g.panel.SetPosition(x, y+h)
if err := g.panel.Render(target); err != nil {
return err
}
y += h
// Bottom right
if err := g.panel.SetCurrentFrame(7); err != nil {
return err
}
_, h = g.panel.GetCurrentFrameSize()
g.panel.SetPosition(x, y+h)
if err := g.panel.Render(target); err != nil {
return err
}
// Bottom left
if err := g.panel.SetCurrentFrame(6); err != nil {
return err
}
w, h = g.panel.GetCurrentFrameSize()
g.panel.SetPosition(x-w, y+h)
if err := g.panel.Render(target); err != nil {
return err
}
g.grid.Render(target)
hovering := false
for idx := range g.grid.items {
item := g.grid.items[idx]
ix, iy := g.grid.SlotToScreen(item.InventoryGridSlot())
iw, ih := g.grid.sprites[item.GetItemCode()].GetCurrentFrameSize()
mx, my := g.lastMouseX, g.lastMouseY
hovering = hovering || ((mx > ix) && (mx < ix+iw) && (my > iy) && (my < iy+ih))
if hovering {
if !g.hovering {
// set the initial hover coordinates
// this is so that moving mouse doesnt move the description
g.hoverX, g.hoverY = mx, my
}
g.renderItemDescription(target, item)
break
}
}
g.hovering = hovering
return nil
}
func (g *Inventory) renderItemDescription(target d2interface.Surface, i InventoryItem) {
lines := i.GetItemDescription()
maxW, maxH := 0, 0
_, iy := g.grid.SlotToScreen(i.InventoryGridSlot())
for idx := range lines {
w, h := g.hoverLabel.GetTextMetrics(lines[idx])
if maxW < w {
maxW = w
}
maxH += h
}
halfW, halfH := maxW/2, maxH/2
centerX, centerY := g.hoverX, iy-halfH
if (centerX + halfW) > 800 {
centerX = 800 - halfW
}
if (centerY + halfH) > 600 {
centerY = 600 - halfH
}
target.PushTranslation(centerX, centerY)
target.PushTranslation(-halfW, -halfH)
target.DrawRect(maxW, maxH, color.RGBA{0, 0, 0, uint8(200)})
target.PushTranslation(halfW, 0)
for idx := range lines {
g.hoverLabel.SetText(lines[idx])
_, h := g.hoverLabel.GetTextMetrics(lines[idx])
g.hoverLabel.Render(target)
target.PushTranslation(0, h)
}
target.PopN(len(lines))
target.PopN(3)
}