mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-12-25 19:46:50 -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
|
||||
// sets the animation mode to the casting animation.
|
||||
// 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()) {
|
||||
// passive skills, auras, etc.
|
||||
if animMode == d2enum.PlayerAnimationModeNone {
|
||||
|
@ -2,6 +2,7 @@ package d2player
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"log"
|
||||
"sort"
|
||||
|
||||
@ -25,6 +26,9 @@ const (
|
||||
leftPanelStartX = 90
|
||||
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
|
||||
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.
|
||||
@ -39,8 +43,7 @@ type SkillPanel struct {
|
||||
renderer d2interface.Renderer
|
||||
ui *d2ui.UIManager
|
||||
hoveredSkill *d2hero.HeroSkill
|
||||
hoverTooltipPos d2geom.Point
|
||||
//TODO: should be a cached image which contains the skill text + the tooltip background
|
||||
hoverTooltipRect *d2geom.Rectangle
|
||||
hoverTooltipText *d2ui.Label
|
||||
}
|
||||
|
||||
@ -132,9 +135,14 @@ func (s *SkillPanel) Render(target d2interface.Surface) error {
|
||||
}
|
||||
|
||||
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)
|
||||
target.Pop()
|
||||
|
||||
target.PopN(2)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -326,19 +334,26 @@ func (s *SkillPanel) HandleMouseMove(X int, Y int) bool {
|
||||
|
||||
if previousHovered != s.hoveredSkill && s.hoveredSkill != nil {
|
||||
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))
|
||||
|
||||
listRow := s.GetListRowByPos(X, Y)
|
||||
tooltipWidth, _ := s.hoverTooltipText.GetSize()
|
||||
tooltipX := (s.getSkillIdxAtPos(X, Y) * skillIconWidth) + s.getRowStartX(listRow) + (tooltipWidth / 2)
|
||||
textWidth, textHeight := s.hoverTooltipText.GetSize()
|
||||
|
||||
tooltipX := (s.getSkillIdxAtPos(X, Y) * skillIconWidth) + s.getRowStartX(listRow)
|
||||
tooltipWidth := textWidth + tooltipPadLeft + tooltipPadRight
|
||||
|
||||
if tooltipX+tooltipWidth >= screenWidth {
|
||||
tooltipX = screenWidth - (tooltipWidth / 2)
|
||||
tooltipX = screenWidth - tooltipWidth
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -280,15 +280,31 @@ func (g *GameClient) handleCastSkillPacket(packet d2netpacket.NetPacket) error {
|
||||
|
||||
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 {
|
||||
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() {
|
||||
if missileEntity != nil {
|
||||
// shoot the missile after the player has finished casting
|
||||
g.MapEngine.AddEntity(missileEntity)
|
||||
if len(missileEntities) > 0 {
|
||||
// shoot the missiles of the skill after the player has finished casting
|
||||
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
|
||||
}
|
||||
|
||||
func (g *GameClient) createMissileEntity(skillRecord *d2records.SkillRecord, player *d2mapentity.Player, castX float64, castY float64) (*d2mapentity.Missile, error) {
|
||||
missileRecord := g.asset.Records.GetMissileByName(skillRecord.Cltmissile)
|
||||
func (g *GameClient) createSummonedNpcEntity(skillRecord *d2records.SkillRecord, X, Y int) (*d2mapentity.NPC, error) {
|
||||
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 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var missileEntity *d2mapentity.Missile
|
||||
|
||||
radians := d2math.GetRadiansBetween(
|
||||
player.Position.X(),
|
||||
player.Position.Y(),
|
||||
|
Loading…
Reference in New Issue
Block a user