1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-05 08:07:51 -05:00

Minor edits: debug entity frame bounds, debug spawnmon command (#666)

* adding debug printing boxes for entity bounds

* minor edits

- adding `spawnmon` command. currently does not have a netpacket, just for debug
- adding `GetSize` method to map entities for getting frame bounds (for debug printing)

* bug fix
This commit is contained in:
lord 2020-08-01 16:03:09 -07:00 committed by GitHub
parent 6514fd15de
commit 6a8b9aada1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 177 additions and 21 deletions

View File

@ -8,6 +8,7 @@ type MapEntity interface {
Advance(tickTime float64) Advance(tickTime float64)
GetPosition() d2vector.Position GetPosition() d2vector.Position
GetVelocity() d2vector.Vector GetVelocity() d2vector.Vector
GetSize() (width, height int)
GetLayer() int GetLayer() int
GetPositionF() (float64, float64) GetPositionF() (float64, float64)
Name() string Name() string

View File

@ -20,6 +20,12 @@ type Composite struct {
direction int direction int
equipment [d2enum.CompositeTypeMax]string equipment [d2enum.CompositeTypeMax]string
mode *compositeMode mode *compositeMode
size *size
}
type size struct {
Width int
Height int
} }
// CreateComposite creates a Composite from a given ObjectLookupRecord and palettePath. // CreateComposite creates a Composite from a given ObjectLookupRecord and palettePath.
@ -300,6 +306,43 @@ func (c *Composite) loadCompositeLayer(layerKey, layerValue, animationMode, weap
return nil, errors.New("animation not found") return nil, errors.New("animation not found")
} }
func (c *Composite) GetSize() (w, h int) {
c.updateSize()
return c.size.Width, c.size.Height
}
func (c *Composite) updateSize() {
if c.mode == nil {
return
}
direction := d2cof.Dir64ToCof(c.direction, c.mode.cof.NumberOfDirections)
biggestW, biggestH := 0, 0
for _, layerIndex := range c.mode.cof.Priority[direction][c.mode.frameIndex] {
layer := c.mode.layers[layerIndex]
if layer != nil {
w, h := layer.GetCurrentFrameSize()
if biggestW < w {
biggestW = w
}
if biggestH < h {
biggestH = h
}
}
}
if c.size == nil {
c.size = &size{}
}
c.size.Width = biggestW
c.size.Height = biggestH
}
func baseString(baseType d2enum.ObjectType) string { func baseString(baseType d2enum.ObjectType) string {
switch baseType { switch baseType {
case d2enum.ObjectTypePlayer: case d2enum.ObjectTypePlayer:

View File

@ -21,16 +21,17 @@ type AnimatedEntity struct {
// Render draws this animated entity onto the target // Render draws this animated entity onto the target
func (ae *AnimatedEntity) Render(target d2interface.Surface) { func (ae *AnimatedEntity) Render(target d2interface.Surface) {
renderOffset := ae.Position.RenderOffset() renderOffset := ae.Position.RenderOffset()
target.PushTranslation( ox, oy := renderOffset.X(), renderOffset.Y()
int((renderOffset.X()-renderOffset.Y())*16), tx, ty := int((ox-oy)*16), int((ox+oy)*8)-5
int(((renderOffset.X()+renderOffset.Y())*8)-5),
) target.PushTranslation(tx, ty)
defer target.Pop() defer target.Pop()
if ae.highlight { if ae.highlight {
target.PushBrightness(2) target.PushBrightness(2)
defer target.Pop() defer target.Pop()
ae.highlight = false ae.highlight = false
} }

View File

@ -39,3 +39,18 @@ func (i *Item) Highlight() {
func (i *Item) Name() string { func (i *Item) Name() string {
return i.Item.Name() return i.Item.Name()
} }
// GetSize returns the current frame size
func (i *Item) GetSize() (width, height int) {
w, h := i.animation.GetCurrentFrameSize()
if w < minHitboxSize {
w = minHitboxSize
}
if h < minHitboxSize {
h = minHitboxSize
}
return w, h
}

View File

@ -4,6 +4,10 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
) )
const (
minHitboxSize = 30
)
// mapEntity represents an entity on the map that can be animated // mapEntity represents an entity on the map that can be animated
type mapEntity struct { type mapEntity struct {
Position d2vector.Position Position d2vector.Position
@ -206,3 +210,8 @@ func (m *mapEntity) Highlight() {
func (m *mapEntity) Selectable() bool { func (m *mapEntity) Selectable() bool {
return false return false
} }
// GetSize returns the current frame size
func (m *mapEntity) GetSize() (width, height int) {
return minHitboxSize, minHitboxSize
}

View File

@ -175,3 +175,8 @@ func (v *NPC) GetPosition() d2vector.Position {
func (v *NPC) GetVelocity() d2vector.Vector { func (v *NPC) GetVelocity() d2vector.Vector {
return v.mapEntity.velocity return v.mapEntity.velocity
} }
// GetSize returns the current frame size
func (v* NPC) GetSize() (width, height int) {
return v.composite.GetSize()
}

View File

@ -192,3 +192,12 @@ func (p *Player) GetPosition() d2vector.Position {
func (p *Player) GetVelocity() d2vector.Vector { func (p *Player) GetVelocity() d2vector.Vector {
return p.mapEntity.velocity return p.mapEntity.velocity
} }
// GetSize returns the current frame size
func (p *Player) GetSize() (width, height int) {
width, height = p.composite.GetSize()
// hack: we need to get full size of composite animations, currently only gets legs
height = (height * 2) - (height / 2)
return width, height
}

View File

@ -361,6 +361,41 @@ func (mr *MapRenderer) renderEntityDebug(target d2interface.Surface) {
offX, offY := 40, -40 offX, offY := 40, -40
entScreenXf, entScreenYf := mr.WorldToScreenF(e.GetPositionF())
entScreenX := int(math.Floor(entScreenXf))
entScreenY := int(math.Floor(entScreenYf))
entityWidth, entityHeight := e.GetSize()
halfWidth, halfHeight := entityWidth/2, entityHeight/2
l, r := entScreenX-halfWidth, entScreenX+halfWidth
t, b := entScreenY-halfHeight, entScreenY+halfHeight
mx, my := mr.renderer.GetCursorPos()
xWithin := (l <= mx) && (r >= mx)
yWithin := (t <= my) && (b >= my)
within := xWithin && yWithin
boxLineColor := color.RGBA{255, 0, 255, 128}
boxHoverColor := color.RGBA{255, 255, 0, 128}
boxColor := boxLineColor
if within {
boxColor = boxHoverColor
}
// box
mr.viewport.PushTranslationWorld(x, y)
target.PushTranslation(screenX, screenY)
target.PushTranslation(-halfWidth, -halfHeight)
target.DrawLine(0, entityHeight, boxColor)
target.DrawLine(entityWidth, 0, boxColor)
target.PushTranslation(entityWidth, entityHeight)
target.DrawLine(-entityWidth, 0, boxColor)
target.DrawLine(0, -entityHeight, boxColor)
target.PopN(3)
mr.viewport.PopTranslation()
// hover
if within {
mr.viewport.PushTranslationWorld(x, y) mr.viewport.PushTranslationWorld(x, y)
target.PushTranslation(screenX, screenY) target.PushTranslation(screenX, screenY)
target.DrawLine(offX, offY, color.RGBA{255, 255, 255, 128}) target.DrawLine(offX, offY, color.RGBA{255, 255, 255, 128})
@ -374,6 +409,7 @@ func (mr *MapRenderer) renderEntityDebug(target d2interface.Surface) {
target.Pop() target.Pop()
mr.viewport.PopTranslation() mr.viewport.PopTranslation()
} }
}
} }
// WorldToScreen returns the screen (pixel) position for the given isometric world position as two ints. // WorldToScreen returns the screen (pixel) position for the given isometric world position as two ints.

View File

@ -142,3 +142,8 @@ func (ob *Object) GetPosition() d2vector.Position {
func (ob *Object) GetVelocity() d2vector.Vector { func (ob *Object) GetVelocity() d2vector.Vector {
return *d2vector.VectorZero() return *d2vector.VectorZero()
} }
// GetSize returns the current frame size
func (ob *Object) GetSize() (width, height int) {
return ob.composite.GetSize()
}

View File

@ -110,6 +110,26 @@ func (v *Game) OnLoad(_ d2screen.LoadingState) {
v.debugSpawnItemAtLocation(x, y, codes...) v.debugSpawnItemAtLocation(x, y, codes...)
}, },
) )
v.terminal.BindAction(
"spawnmon",
"spawn monster at the local player position",
func(name string) {
x := int(v.localPlayer.Position.X())
y := int(v.localPlayer.Position.Y())
monstat := d2datadict.MonStats[name]
if monstat == nil {
v.terminal.OutputErrorf("no monstat entry for \"%s\"", name)
return
}
monster, err := d2mapentity.NewNPC(x, y, monstat, 0)
if err != nil {
v.terminal.OutputErrorf("error generating monster \"%s\": %v", name, err)
return
}
v.gameClient.MapEngine.AddEntity(monster)
},
)
} }
// OnUnload releases the resources of Gameplay screen // OnUnload releases the resources of Gameplay screen

View File

@ -37,6 +37,7 @@ const (
staminaBarWidth = 102.0 staminaBarWidth = 102.0
globeHeight = 80 globeHeight = 80
globeWidth = 80 globeWidth = 80
hoverLabelOuterPad = 5
) )
// GameControls represents the game's controls on the screen // GameControls represents the game's controls on the screen
@ -423,14 +424,25 @@ func (g *GameControls) Render(target d2interface.Surface) error {
continue continue
} }
entPos := entity.GetPosition()
entOffset := entPos.RenderOffset()
entScreenXf, entScreenYf := g.mapRenderer.WorldToScreenF(entity.GetPositionF()) entScreenXf, entScreenYf := g.mapRenderer.WorldToScreenF(entity.GetPositionF())
entScreenX := int(math.Floor(entScreenXf)) entScreenX := int(math.Floor(entScreenXf))
entScreenY := int(math.Floor(entScreenYf)) entScreenY := int(math.Floor(entScreenYf))
entityWidth, entityHeight := entity.GetSize()
halfWidth, halfHeight := entityWidth/2, entityHeight/2
l, r := entScreenX-halfWidth-hoverLabelOuterPad, entScreenX+halfWidth+hoverLabelOuterPad
t, b := entScreenY-halfHeight-hoverLabelOuterPad, entScreenY+halfHeight-hoverLabelOuterPad
mx, my := g.lastMouseX, g.lastMouseY
xWithin := (l <= mx) && (r >= mx)
yWithin := (t <= my) && (b >= my)
within := xWithin && yWithin
if ((entScreenX - 20) <= g.lastMouseX) && ((entScreenX + 20) >= g.lastMouseX) && if within {
((entScreenY - 80) <= g.lastMouseY) && (entScreenY >= g.lastMouseY) { xOff, yOff := int(entOffset.X()), int(entOffset.Y())
g.nameLabel.SetText(entity.Name()) g.nameLabel.SetText(entity.Name())
g.nameLabel.SetPosition(entScreenX, entScreenY-100) xLabel, yLabel := entScreenX-xOff, entScreenY-yOff-entityHeight-hoverLabelOuterPad
g.nameLabel.SetPosition(xLabel, yLabel)
g.nameLabel.Render(target) g.nameLabel.Render(target)
entity.Highlight() entity.Highlight()