1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-11 10:20:41 +00:00
OpenDiablo2/d2game/d2player/party_panel.go

553 lines
14 KiB
Go
Raw Normal View History

2021-01-13 16:34:33 +00:00
package d2player
import (
2021-01-18 11:39:41 +00:00
"log"
2021-01-17 17:46:38 +00:00
"strconv"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
2021-01-13 16:34:33 +00:00
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
2021-01-13 16:34:33 +00:00
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
const (
lightGreen = 0x18ff00ff
red = 0xff0000ff
)
2021-01-13 16:34:33 +00:00
const ( // for the dc6 frames
2021-01-14 18:24:18 +00:00
partyPanelTopLeft = iota
partyPanelTopRight
partyPanelBottomLeft
partyPanelBottomRight
2021-01-13 16:34:33 +00:00
)
const (
partyPanelOffsetX, partyPanelOffsetY = 80, 64
2021-01-13 16:34:33 +00:00
)
const (
2021-01-14 18:24:18 +00:00
partyPanelCloseButtonX, partyPanelCloseButtonY = 358, 453
partyPanelHeroNameX, partyPanelHeroNameY = 180, 80
2021-01-13 16:34:33 +00:00
)
const (
listeningButtonFrame = iota * 4
relationshipsFrame
seeingButtonFrame
2021-01-16 20:45:48 +00:00
// nolint:deadcode,varcheck,unused // will be used
lockButtonFrame
nextButtonFrame = 2
)
const (
buttonSize = 19
)
const (
maxPlayersInGame = 8
barX = 90
relationshipSwitcherX = 95
listeningSwitcherX = 345
seeingSwitcherX = 365
nameLabelX = 115
classLabelX = 115
2021-01-17 17:46:38 +00:00
levelLabelX = 383
baseBarY = 134
baseRelationshipSwitcherY = 150
2021-01-17 17:46:38 +00:00
baseSeeingSwitcherY = 140
baseListeningSwitcherY = 140
baseNameLabelY = 145
baseClassLabelY = 158
2021-01-17 17:46:38 +00:00
baseLevelLabelY = 160
nextBar = 52
)
2021-01-16 20:45:48 +00:00
// newPartyIndex creates new party index
2021-01-18 11:39:41 +00:00
func (s *PartyPanel) newPartyIndex() *partyIndex {
result := &partyIndex{}
nameLabel := s.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteSky)
result.name = nameLabel
classLabel := s.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteSky)
result.class = classLabel
2021-01-17 17:46:38 +00:00
levelLabel := s.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteSky)
levelLabel.Alignment = d2ui.HorizontalAlignRight
result.level = levelLabel
relationships := s.createSwitcher(relationshipsFrame)
result.relationshipSwitcher = relationships
seeing := s.createSwitcher(seeingButtonFrame)
result.seeingActiveTooltip = s.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteSky, d2ui.TooltipXCenter, d2ui.TooltipYTop)
result.seeingActiveTooltip.SetText(s.asset.TranslateString("strParty19"))
seeing.SetActiveTooltip(result.seeingActiveTooltip)
result.seeingInactiveTooltip = s.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteSky, d2ui.TooltipXCenter, d2ui.TooltipYTop)
result.seeingInactiveTooltip.SetText(s.asset.TranslateString("strParty22"))
seeing.SetInactiveTooltip(result.seeingInactiveTooltip)
result.seeingSwitcher = seeing
listening := s.createSwitcher(listeningButtonFrame)
result.listeningActiveTooltip = s.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteSky, d2ui.TooltipXCenter, d2ui.TooltipYTop)
result.listeningActiveTooltip.SetText(s.asset.TranslateString("strParty17") + "\n" + s.asset.TranslateString("strParty18"))
listening.SetActiveTooltip(result.listeningActiveTooltip)
result.listeningInactiveTooltip = s.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteSky, d2ui.TooltipXCenter, d2ui.TooltipYTop)
result.listeningInactiveTooltip.SetText(s.asset.TranslateString("strParty11") + "\n" + s.asset.TranslateString("strParty16"))
listening.SetInactiveTooltip(result.listeningInactiveTooltip)
result.listeningSwitcher = listening
return result
}
// partyIndex represents a party index
type partyIndex struct {
hero *d2mapentity.Player
name *d2ui.Label
class *d2ui.Label
level *d2ui.Label
relationshipSwitcher *d2ui.SwitchableButton
seeingSwitcher *d2ui.SwitchableButton
seeingActiveTooltip *d2ui.Tooltip
seeingInactiveTooltip *d2ui.Tooltip
listeningSwitcher *d2ui.SwitchableButton
listeningActiveTooltip *d2ui.Tooltip
listeningInactiveTooltip *d2ui.Tooltip
relationships d2enum.PlayersRelationships
}
// setColor sets appropriate labels' colors
func (pi *partyIndex) setColor(relations d2enum.PlayersRelationships) {
var color = d2util.Color(white)
switch relations {
case d2enum.PlayerRelationEnemy:
color = d2util.Color(red)
pi.relationshipSwitcher.SetState(false)
case d2enum.PlayerRelationFriend:
color = d2util.Color(lightGreen)
}
pi.name.Color[0] = color
pi.class.Color[0] = color
pi.level.Color[0] = color
}
// setPositions sets party-index's position to given
func (pi *partyIndex) setPositions(idx int) {
var h int
pi.name.SetPosition(nameLabelX, baseNameLabelY+nextBar*idx)
pi.class.SetPosition(classLabelX, baseClassLabelY+nextBar*idx)
pi.level.SetPosition(levelLabelX, baseLevelLabelY+nextBar*idx)
pi.relationshipSwitcher.SetPosition(relationshipSwitcherX, baseRelationshipSwitcherY+nextBar*idx)
pi.seeingSwitcher.SetPosition(seeingSwitcherX, baseSeeingSwitcherY+idx*nextBar)
_, h = pi.seeingActiveTooltip.GetSize()
pi.seeingActiveTooltip.SetPosition(seeingSwitcherX+buttonSize, baseSeeingSwitcherY+idx*nextBar-h)
_, h = pi.seeingInactiveTooltip.GetSize()
pi.seeingInactiveTooltip.SetPosition(seeingSwitcherX+buttonSize, baseSeeingSwitcherY+idx*nextBar-h)
pi.listeningSwitcher.SetPosition(listeningSwitcherX, baseListeningSwitcherY+idx*nextBar)
_, h = pi.listeningActiveTooltip.GetSize()
pi.listeningActiveTooltip.SetPosition(listeningSwitcherX+buttonSize, baseListeningSwitcherY+idx*nextBar-h)
_, h = pi.listeningInactiveTooltip.GetSize()
pi.listeningInactiveTooltip.SetPosition(listeningSwitcherX+buttonSize, baseListeningSwitcherY+idx*nextBar-h)
}
2021-01-18 11:39:41 +00:00
// AddPlayer adds a new player to the party panel
func (s *PartyPanel) AddPlayer(player *d2mapentity.Player, relations d2enum.PlayersRelationships) {
idx := 0
for n, i := range s.partyIndexes {
if i.hero == nil {
idx = n
break
}
}
2021-01-18 11:39:41 +00:00
s.partyIndexes[idx].hero = player
s.partyIndexes[idx].name.SetText(player.Name())
s.partyIndexes[idx].class.SetText(s.asset.TranslateString(player.Class.String()))
s.partyIndexes[idx].level.SetText(s.asset.TranslateString("level") + ": " + strconv.Itoa(player.Stats.Level))
s.partyIndexes[idx].relationships = relations
s.partyIndexes[idx].setColor(relations)
s.partyIndexes[idx].setPositions(idx)
}
// DeletePlayer deletes player from PartyIndexes
func (s *PartyPanel) DeletePlayer(player *d2mapentity.Player) bool {
for n, i := range s.partyIndexes {
if i.hero == player {
2021-01-18 11:39:41 +00:00
s.Debugf("removing player at index %d", n)
s.partyIndexes[n].hero = nil
s.Sort()
return true
}
}
return false
}
// Sort sorts party indexes
func (s *PartyPanel) Sort() {
2021-01-18 11:39:41 +00:00
var emptySlots []*partyIndex
2021-01-18 11:39:41 +00:00
var emptySlotsNumbers []int
2021-01-18 11:39:41 +00:00
var fullSlots []*partyIndex
var fullSlotsNumbers []int
// split s.partyIndexes to empty and non-empty
for n, i := range s.partyIndexes {
if i.hero == nil {
emptySlots = append(emptySlots, i)
emptySlotsNumbers = append(emptySlotsNumbers, n)
} else {
fullSlots = append(fullSlots, i)
fullSlotsNumbers = append(fullSlotsNumbers, n)
}
}
2021-01-18 11:39:41 +00:00
// adds non-empty indexes befor empty indexes
for n, i := range fullSlots {
s.partyIndexes[n] = i
}
// adds empty indexes
for n, i := range emptySlots {
s.partyIndexes[len(fullSlots)+n] = i
}
// sorts widget groups
var sortedWG [maxPlayersInGame]*d2ui.WidgetGroup
// first add non empty WG's
for n, i := range fullSlotsNumbers {
sortedWG[n] = s.indexes[i]
}
// after that, adds empty WG's
for n, i := range emptySlotsNumbers {
sortedWG[len(fullSlotsNumbers)+n] = s.indexes[i]
}
// overwrite existing order
s.indexes = sortedWG
2021-01-18 11:39:41 +00:00
// sets appropriate positions
for n, i := range s.partyIndexes {
2021-01-18 11:39:41 +00:00
if i.hero != nil {
i.setPositions(n)
}
}
}
// setBarPosition sets party-panel bar's position
func (s *PartyPanel) setBarPosition() {
for n, i := range s.partyIndexes {
currentN := n
2021-01-18 11:39:41 +00:00
if i.hero == nil {
s.barX, s.barY = barX, baseBarY+currentN*nextBar
break
}
}
}
2021-01-15 12:04:46 +00:00
// NewPartyPanel creates a new party panel
2021-01-14 18:24:18 +00:00
func NewPartyPanel(asset *d2asset.AssetManager,
2021-01-13 16:34:33 +00:00
ui *d2ui.UIManager,
heroName string,
l d2util.LogLevel,
// example Player structure (me)
testPlayer *d2mapentity.Player,
2021-01-14 18:24:18 +00:00
heroState *d2hero.HeroStatsState) *PartyPanel {
2021-01-18 11:39:41 +00:00
log.Print("OpenDiablo2 - Party Panel - development")
2021-01-13 16:34:33 +00:00
originX := 0
originY := 0
2021-01-18 11:39:41 +00:00
pp := &PartyPanel{
asset: asset,
uiManager: ui,
originX: originX,
originY: originY,
heroState: heroState,
heroName: heroName,
labels: &StatsPanelLabels{},
barX: barX,
barY: baseBarY,
testPlayer: testPlayer,
}
2021-01-18 11:39:41 +00:00
var partyIndexes [maxPlayersInGame]*partyIndex
2021-01-18 11:39:41 +00:00
var indexes [maxPlayersInGame]*d2ui.WidgetGroup
for i := 0; i < maxPlayersInGame; i++ {
partyIndexes[i] = pp.newPartyIndex()
indexes[i] = pp.uiManager.NewWidgetGroup(d2ui.RenderPriorityHeroStatsPanel)
2021-01-13 16:34:33 +00:00
}
2021-01-18 11:39:41 +00:00
pp.partyIndexes = partyIndexes
pp.Logger = d2util.NewLogger()
pp.Logger.SetLevel(l)
pp.Logger.SetPrefix(logPrefix)
2021-01-13 16:34:33 +00:00
2021-01-18 11:39:41 +00:00
return pp
2021-01-13 16:34:33 +00:00
}
2021-01-15 12:04:46 +00:00
// PartyPanel represents the party panel
2021-01-14 18:24:18 +00:00
type PartyPanel struct {
2021-01-18 11:39:41 +00:00
asset *d2asset.AssetManager
uiManager *d2ui.UIManager
panel *d2ui.Sprite
bar *d2ui.Sprite
heroState *d2hero.HeroStatsState
heroName string
labels *StatsPanelLabels
onCloseCb func()
panelGroup *d2ui.WidgetGroup
partyIndexes [maxPlayersInGame]*partyIndex
2021-01-18 11:39:41 +00:00
indexes [maxPlayersInGame]*d2ui.WidgetGroup
2021-01-13 16:34:33 +00:00
originX int
originY int
isOpen bool
barX int
barY int
2021-01-13 16:34:33 +00:00
*d2util.Logger
testPlayer *d2mapentity.Player
2021-01-13 16:34:33 +00:00
}
// Load the data for the hero status panel
2021-01-14 18:24:18 +00:00
func (s *PartyPanel) Load() {
2021-01-13 16:34:33 +00:00
var err error
var w, h int
// create widgetGroups
2021-01-13 16:34:33 +00:00
s.panelGroup = s.uiManager.NewWidgetGroup(d2ui.RenderPriorityHeroStatsPanel)
2021-01-18 11:39:41 +00:00
for i := 0; i < maxPlayersInGame; i++ {
s.indexes[i] = s.uiManager.NewWidgetGroup(d2ui.RenderPriorityHeroStatsPanel)
}
2021-01-13 16:34:33 +00:00
// create frame
2021-01-13 16:34:33 +00:00
frame := s.uiManager.NewUIFrame(d2ui.FrameLeft)
s.panelGroup.AddWidget(frame)
2021-01-14 18:24:18 +00:00
s.panel, err = s.uiManager.NewSprite(d2resource.PartyPanel, d2resource.PaletteSky)
2021-01-13 16:34:33 +00:00
if err != nil {
s.Error(err.Error())
}
// create panel
w, h = frame.GetSize()
staticPanel := s.uiManager.NewCustomWidgetCached(s.renderStaticPanelFrames, w, h)
2021-01-13 16:34:33 +00:00
s.panelGroup.AddWidget(staticPanel)
// create close button
2021-01-13 16:34:33 +00:00
closeButton := s.uiManager.NewButton(d2ui.ButtonTypeSquareClose, "")
closeButton.SetVisible(false)
2021-01-14 18:24:18 +00:00
closeButton.SetPosition(partyPanelCloseButtonX, partyPanelCloseButtonY)
2021-01-13 16:34:33 +00:00
closeButton.OnActivated(func() { s.Close() })
s.panelGroup.AddWidget(closeButton)
// our name label
heroName := s.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteSky)
heroName.SetText(s.heroName)
heroName.SetPosition(partyPanelHeroNameX, partyPanelHeroNameY)
heroName.Alignment = d2ui.HorizontalAlignCenter
s.panelGroup.AddWidget(heroName)
// example data - create an example players and add them
p0 := s.testPlayer
s.AddPlayer(p0, d2enum.PlayerRelationEnemy)
p1 := s.testPlayer
// nolint:gomnd // only test
p1.Stats.Level = 99
p1.Class = d2enum.HeroNecromancer
s.AddPlayer(p1, d2enum.PlayerRelationFriend)
// create WidgetGroups of party indexes
2021-01-18 11:39:41 +00:00
for n, i := range s.partyIndexes {
s.indexes[n].AddWidget(i.name)
s.indexes[n].AddWidget(i.class)
s.indexes[n].AddWidget(i.relationshipSwitcher)
s.indexes[n].AddWidget(i.seeingSwitcher)
s.indexes[n].AddWidget(i.listeningSwitcher)
s.indexes[n].AddWidget(i.level)
}
// further test data (to check if DeletePlayer works after creating WG's)
2021-01-18 11:39:41 +00:00
if !s.DeletePlayer(p0) {
s.Warning("cannot remove player: DeletePlayer returned false")
2021-01-17 20:00:21 +00:00
}
s.AddPlayer(p0, d2enum.PlayerRelationEnemy)
// create bar
s.bar, err = s.uiManager.NewSprite(d2resource.PartyBar, d2resource.PaletteSky)
if err != nil {
s.Error(err.Error())
}
2021-01-17 20:00:21 +00:00
w, h = s.bar.GetCurrentFrameSize()
v := s.uiManager.NewCustomWidget(s.renderBar, w, h)
s.panelGroup.AddWidget(v)
s.setBarPosition()
2021-01-13 16:34:33 +00:00
s.panelGroup.SetVisible(false)
}
// createSwitcher creates party-panel switcher using frame given
func (s *PartyPanel) createSwitcher(frame int) *d2ui.SwitchableButton {
active := s.uiManager.NewCustomButton(d2resource.PartyBoxes, frame)
inactive := s.uiManager.NewCustomButton(d2resource.PartyBoxes, frame+nextButtonFrame)
switcher := s.uiManager.NewSwitchableButton(active, inactive, true)
switcher.SetVisible(false)
return switcher
}
2021-01-13 16:34:33 +00:00
// IsOpen returns true if the hero status panel is open
2021-01-14 18:24:18 +00:00
func (s *PartyPanel) IsOpen() bool {
2021-01-13 16:34:33 +00:00
return s.isOpen
}
// Toggle toggles the visibility of the hero status panel
2021-01-14 18:24:18 +00:00
func (s *PartyPanel) Toggle() {
2021-01-13 16:34:33 +00:00
if s.isOpen {
s.Close()
} else {
s.Open()
}
}
// Open opens the hero status panel
2021-01-14 18:24:18 +00:00
func (s *PartyPanel) Open() {
2021-01-13 16:34:33 +00:00
s.isOpen = true
s.panelGroup.SetVisible(true)
2021-01-18 11:39:41 +00:00
for n, i := range s.indexes {
if s.partyIndexes[n].hero != nil {
i.SetVisible(true)
}
}
2021-01-13 16:34:33 +00:00
}
// Close closed the hero status panel
2021-01-14 18:24:18 +00:00
func (s *PartyPanel) Close() {
2021-01-13 16:34:33 +00:00
s.isOpen = false
s.panelGroup.SetVisible(false)
2021-01-18 11:39:41 +00:00
for _, i := range s.indexes {
i.SetVisible(false)
}
2021-01-13 16:34:33 +00:00
}
2021-01-15 12:04:46 +00:00
// SetOnCloseCb the callback run on closing the PartyPanel
func (s *PartyPanel) SetOnCloseCb(cb func()) {
2021-01-13 16:34:33 +00:00
s.onCloseCb = cb
}
// Advance advances panel
func (s *PartyPanel) Advance(_ float64) {
// noop
2021-01-13 16:34:33 +00:00
}
// nolint:dupl // see quest_log.go.renderStaticPanelFrames comment
2021-01-14 18:24:18 +00:00
func (s *PartyPanel) renderStaticPanelFrames(target d2interface.Surface) {
2021-01-13 16:34:33 +00:00
frames := []int{
2021-01-14 18:24:18 +00:00
partyPanelTopLeft,
partyPanelTopRight,
partyPanelBottomRight,
partyPanelBottomLeft,
2021-01-13 16:34:33 +00:00
}
currentX := s.originX + partyPanelOffsetX
currentY := s.originY + partyPanelOffsetY
2021-01-13 16:34:33 +00:00
for _, frameIndex := range frames {
if err := s.panel.SetCurrentFrame(frameIndex); err != nil {
s.Error(err.Error())
}
w, h := s.panel.GetCurrentFrameSize()
switch frameIndex {
case statsPanelTopLeft:
s.panel.SetPosition(currentX, currentY+h)
currentX += w
case statsPanelTopRight:
s.panel.SetPosition(currentX, currentY+h)
currentY += h
case statsPanelBottomRight:
s.panel.SetPosition(currentX, currentY+h)
case statsPanelBottomLeft:
s.panel.SetPosition(currentX-w, currentY+h)
}
s.panel.Render(target)
}
}
// renderBar renders party panel's bar
func (s *PartyPanel) renderBar(target d2interface.Surface) {
frames := []int{
partyPanelTopLeft,
partyPanelTopRight,
}
currentX := s.originX + s.barX
currentY := s.originY + s.barY
for _, frameIndex := range frames {
if err := s.bar.SetCurrentFrame(frameIndex); err != nil {
s.Error(err.Error())
}
w, h := s.bar.GetCurrentFrameSize()
switch frameIndex {
case statsPanelTopLeft:
s.bar.SetPosition(currentX, currentY)
currentX += w
case statsPanelTopRight:
s.bar.SetPosition(currentX, currentY)
currentY += h
}
s.bar.Render(target)
}
}