1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-12-26 12:06:24 -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:
presiyan-ivanov 2020-10-24 17:08:45 +03:00 committed by GitHub
parent 16a82442bb
commit c4b128ac2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 17 deletions

View File

@ -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 {

View File

@ -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

View File

@ -280,15 +280,31 @@ 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
g.MapEngine.AddEntity(missileEntity) for _, missileEntity := range missileEntities {
g.MapEngine.AddEntity(missileEntity)
}
}
if summonedNpcEntity != nil {
// summon the referenced NPC after the player has finished casting
g.MapEngine.AddEntity(summonedNpcEntity)
} }
}) })
@ -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(),