mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-12-27 04:26:29 -05:00
Handle casting summon skills and skills that have multiple missile references. Add background to skill select's skill hover tooltip. (#786)
Co-authored-by: Presiyan Ivanov <presiyan-ivanov@users.noreply.github.com>
This commit is contained in:
parent
16a82442bb
commit
c4b128ac2e
@ -213,6 +213,7 @@ func (p *Player) IsCasting() bool {
|
|||||||
// StartCasting sets a flag indicating the player is casting a skill and
|
// StartCasting sets a flag indicating the player is casting a skill and
|
||||||
// sets the animation mode to the casting animation.
|
// sets the animation mode to the casting animation.
|
||||||
// This handles all types of skills - melee, ranged, kick, summon, etc.
|
// This handles all types of skills - melee, ranged, kick, summon, etc.
|
||||||
|
// NB: onFinishedCasting is called when the casting animation is >50% complete
|
||||||
func (p *Player) StartCasting(animMode d2enum.PlayerAnimationMode, onFinishedCasting func()) {
|
func (p *Player) StartCasting(animMode d2enum.PlayerAnimationMode, onFinishedCasting func()) {
|
||||||
// passive skills, auras, etc.
|
// passive skills, auras, etc.
|
||||||
if animMode == d2enum.PlayerAnimationModeNone {
|
if animMode == d2enum.PlayerAnimationModeNone {
|
||||||
|
@ -2,6 +2,7 @@ package d2player
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image/color"
|
||||||
"log"
|
"log"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
@ -25,6 +26,9 @@ const (
|
|||||||
leftPanelStartX = 90
|
leftPanelStartX = 90
|
||||||
skillPanelOffsetY = 465
|
skillPanelOffsetY = 465
|
||||||
skillListsLength = 5 // 0 to 4. 0 - General Skills, 1 to 3 - Class-specific skills(based on the 3 different skill trees), 4 - Other skills
|
skillListsLength = 5 // 0 to 4. 0 - General Skills, 1 to 3 - Class-specific skills(based on the 3 different skill trees), 4 - Other skills
|
||||||
|
tooltipPadLeft = 3
|
||||||
|
tooltipPadRight = 3
|
||||||
|
tooltipPadBottom = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// SkillPanel represents a skill select menu popup that is displayed when the player left clicks on his active left/right skill.
|
// SkillPanel represents a skill select menu popup that is displayed when the player left clicks on his active left/right skill.
|
||||||
@ -39,8 +43,7 @@ type SkillPanel struct {
|
|||||||
renderer d2interface.Renderer
|
renderer d2interface.Renderer
|
||||||
ui *d2ui.UIManager
|
ui *d2ui.UIManager
|
||||||
hoveredSkill *d2hero.HeroSkill
|
hoveredSkill *d2hero.HeroSkill
|
||||||
hoverTooltipPos d2geom.Point
|
hoverTooltipRect *d2geom.Rectangle
|
||||||
//TODO: should be a cached image which contains the skill text + the tooltip background
|
|
||||||
hoverTooltipText *d2ui.Label
|
hoverTooltipText *d2ui.Label
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +135,14 @@ func (s *SkillPanel) Render(target d2interface.Surface) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.hoveredSkill != nil {
|
if s.hoveredSkill != nil {
|
||||||
target.PushTranslation(s.hoverTooltipPos.X, s.hoverTooltipPos.Y)
|
target.PushTranslation(s.hoverTooltipRect.Left, s.hoverTooltipRect.Top)
|
||||||
|
target.DrawRect(s.hoverTooltipRect.Width, s.hoverTooltipRect.Height, color.RGBA{0, 0, 0, uint8(200)})
|
||||||
|
|
||||||
|
// the text should be centered horizontally in the tooltip rect
|
||||||
|
target.PushTranslation(s.hoverTooltipRect.Width/2, 0)
|
||||||
s.hoverTooltipText.Render(target)
|
s.hoverTooltipText.Render(target)
|
||||||
target.Pop()
|
|
||||||
|
target.PopN(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -326,19 +334,26 @@ func (s *SkillPanel) HandleMouseMove(X int, Y int) bool {
|
|||||||
|
|
||||||
if previousHovered != s.hoveredSkill && s.hoveredSkill != nil {
|
if previousHovered != s.hoveredSkill && s.hoveredSkill != nil {
|
||||||
skillDescription := d2tbl.TranslateString(s.hoveredSkill.ShortKey)
|
skillDescription := d2tbl.TranslateString(s.hoveredSkill.ShortKey)
|
||||||
//TODO: should generate a cached image for the tooltip instead
|
|
||||||
s.hoverTooltipText.SetText(fmt.Sprintf("%s\n%s", s.hoveredSkill.Skill, skillDescription))
|
s.hoverTooltipText.SetText(fmt.Sprintf("%s\n%s", s.hoveredSkill.Skill, skillDescription))
|
||||||
|
|
||||||
listRow := s.GetListRowByPos(X, Y)
|
listRow := s.GetListRowByPos(X, Y)
|
||||||
tooltipWidth, _ := s.hoverTooltipText.GetSize()
|
textWidth, textHeight := s.hoverTooltipText.GetSize()
|
||||||
tooltipX := (s.getSkillIdxAtPos(X, Y) * skillIconWidth) + s.getRowStartX(listRow) + (tooltipWidth / 2)
|
|
||||||
|
tooltipX := (s.getSkillIdxAtPos(X, Y) * skillIconWidth) + s.getRowStartX(listRow)
|
||||||
|
tooltipWidth := textWidth + tooltipPadLeft + tooltipPadRight
|
||||||
|
|
||||||
if tooltipX+tooltipWidth >= screenWidth {
|
if tooltipX+tooltipWidth >= screenWidth {
|
||||||
tooltipX = screenWidth - (tooltipWidth / 2)
|
tooltipX = screenWidth - tooltipWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
tooltipY := listRow.Rectangle.Top + listRow.Rectangle.Height
|
tooltipY := listRow.Rectangle.Top + listRow.Rectangle.Height
|
||||||
|
tooltipHeight := textHeight + tooltipPadBottom
|
||||||
|
|
||||||
s.hoverTooltipPos = d2geom.Point{X: tooltipX, Y: tooltipY}
|
s.hoverTooltipRect = &d2geom.Rectangle{
|
||||||
|
Left: tooltipX,
|
||||||
|
Top: tooltipY,
|
||||||
|
Width: tooltipWidth,
|
||||||
|
Height: tooltipHeight}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -280,16 +280,32 @@ func (g *GameClient) handleCastSkillPacket(packet d2netpacket.NetPacket) error {
|
|||||||
|
|
||||||
skillRecord := g.asset.Records.Skill.Details[playerCast.SkillID]
|
skillRecord := g.asset.Records.Skill.Details[playerCast.SkillID]
|
||||||
|
|
||||||
missileEntity, err := g.createMissileEntity(skillRecord, player, castX, castY)
|
missileEntities, err := g.createMissileEntities(skillRecord, player, castX, castY)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var summonedNpcEntity *d2mapentity.NPC
|
||||||
|
if skillRecord.Summon != "" {
|
||||||
|
summonedNpcEntity, err = g.createSummonedNpcEntity(skillRecord, int(castX), int(castY))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
player.StartCasting(skillRecord.Anim, func() {
|
player.StartCasting(skillRecord.Anim, func() {
|
||||||
if missileEntity != nil {
|
if len(missileEntities) > 0 {
|
||||||
// shoot the missile after the player has finished casting
|
// shoot the missiles of the skill after the player has finished casting
|
||||||
|
for _, missileEntity := range missileEntities {
|
||||||
g.MapEngine.AddEntity(missileEntity)
|
g.MapEngine.AddEntity(missileEntity)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if summonedNpcEntity != nil {
|
||||||
|
// summon the referenced NPC after the player has finished casting
|
||||||
|
g.MapEngine.AddEntity(summonedNpcEntity)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
overlayRecord := g.asset.Records.Layout.Overlays[skillRecord.Castoverlay]
|
overlayRecord := g.asset.Records.Layout.Overlays[skillRecord.Castoverlay]
|
||||||
@ -298,14 +314,53 @@ func (g *GameClient) handleCastSkillPacket(packet d2netpacket.NetPacket) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GameClient) createMissileEntity(skillRecord *d2records.SkillRecord, player *d2mapentity.Player, castX float64, castY float64) (*d2mapentity.Missile, error) {
|
func (g *GameClient) createSummonedNpcEntity(skillRecord *d2records.SkillRecord, X, Y int) (*d2mapentity.NPC, error) {
|
||||||
missileRecord := g.asset.Records.GetMissileByName(skillRecord.Cltmissile)
|
monsterStatsRecord := g.asset.Records.Monster.Stats[skillRecord.Summon]
|
||||||
|
|
||||||
|
if monsterStatsRecord == nil {
|
||||||
|
return nil, fmt.Errorf("Cannot cast skill - No monstat entry for \"%s\"", skillRecord.Summon)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: overlay animations for the summon
|
||||||
|
summonedNpcEntity, err := g.MapEngine.NewNPC(X, Y, monsterStatsRecord, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return summonedNpcEntity, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameClient) createMissileEntities(skillRecord *d2records.SkillRecord, player *d2mapentity.Player, castX float64, castY float64) ([]*d2mapentity.Missile, error) {
|
||||||
|
missileRecords := []*d2records.MissileRecord{
|
||||||
|
g.asset.Records.GetMissileByName(skillRecord.Cltmissile),
|
||||||
|
g.asset.Records.GetMissileByName(skillRecord.Cltmissilea),
|
||||||
|
g.asset.Records.GetMissileByName(skillRecord.Cltmissileb),
|
||||||
|
g.asset.Records.GetMissileByName(skillRecord.Cltmissilec),
|
||||||
|
g.asset.Records.GetMissileByName(skillRecord.Cltmissiled),
|
||||||
|
}
|
||||||
|
|
||||||
|
missileEntities := make([]*d2mapentity.Missile, 0)
|
||||||
|
for _, missileRecord := range missileRecords {
|
||||||
|
if missileRecord == nil {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
missileEntity, err := g.createMissileEntity(missileRecord, player, castX, castY)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
missileEntities = append(missileEntities, missileEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
return missileEntities, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameClient) createMissileEntity(missileRecord *d2records.MissileRecord, player *d2mapentity.Player, castX, castY float64) (*d2mapentity.Missile, error){
|
||||||
if missileRecord == nil {
|
if missileRecord == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var missileEntity *d2mapentity.Missile
|
|
||||||
|
|
||||||
radians := d2math.GetRadiansBetween(
|
radians := d2math.GetRadiansBetween(
|
||||||
player.Position.X(),
|
player.Position.X(),
|
||||||
player.Position.Y(),
|
player.Position.Y(),
|
||||||
|
Loading…
Reference in New Issue
Block a user