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)
GetPosition() d2vector.Position
GetVelocity() d2vector.Vector
GetSize() (width, height int)
GetLayer() int
GetPositionF() (float64, float64)
Name() string

View File

@ -20,6 +20,12 @@ type Composite struct {
direction int
equipment [d2enum.CompositeTypeMax]string
mode *compositeMode
size *size
}
type size struct {
Width int
Height int
}
// 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")
}
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 {
switch baseType {
case d2enum.ObjectTypePlayer:

View File

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

View File

@ -39,3 +39,18 @@ func (i *Item) Highlight() {
func (i *Item) Name() string {
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"
)
const (
minHitboxSize = 30
)
// mapEntity represents an entity on the map that can be animated
type mapEntity struct {
Position d2vector.Position
@ -206,3 +210,8 @@ func (m *mapEntity) Highlight() {
func (m *mapEntity) Selectable() bool {
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 {
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 {
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,18 +361,54 @@ func (mr *MapRenderer) renderEntityDebug(target d2interface.Surface) {
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.DrawLine(offX, offY, color.RGBA{255, 255, 255, 128})
target.PushTranslation(offX+10, offY-20)
target.PushTranslation(-10, -10)
target.DrawRect(200, 50, color.RGBA{0, 0, 0, 64})
target.Pop()
target.DrawTextf("World (%.2f, %.2f)\nVelocity (%.2f, %.2f)", x, y, vx, vy)
target.Pop()
target.DrawLine(int(vx), int(vy), color.RGBA{64, 255, 0, 255})
target.Pop()
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)
target.PushTranslation(screenX, screenY)
target.DrawLine(offX, offY, color.RGBA{255, 255, 255, 128})
target.PushTranslation(offX+10, offY-20)
target.PushTranslation(-10, -10)
target.DrawRect(200, 50, color.RGBA{0, 0, 0, 64})
target.Pop()
target.DrawTextf("World (%.2f, %.2f)\nVelocity (%.2f, %.2f)", x, y, vx, vy)
target.Pop()
target.DrawLine(int(vx), int(vy), color.RGBA{64, 255, 0, 255})
target.Pop()
mr.viewport.PopTranslation()
}
}
}

View File

@ -142,3 +142,8 @@ func (ob *Object) GetPosition() d2vector.Position {
func (ob *Object) GetVelocity() d2vector.Vector {
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.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

View File

@ -32,11 +32,12 @@ type Panel interface {
}
const (
initialMissileID = 59
expBarWidth = 120.0
staminaBarWidth = 102.0
globeHeight = 80
globeWidth = 80
initialMissileID = 59
expBarWidth = 120.0
staminaBarWidth = 102.0
globeHeight = 80
globeWidth = 80
hoverLabelOuterPad = 5
)
// GameControls represents the game's controls on the screen
@ -423,14 +424,25 @@ func (g *GameControls) Render(target d2interface.Surface) error {
continue
}
entPos := entity.GetPosition()
entOffset := entPos.RenderOffset()
entScreenXf, entScreenYf := g.mapRenderer.WorldToScreenF(entity.GetPositionF())
entScreenX := int(math.Floor(entScreenXf))
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) &&
((entScreenY - 80) <= g.lastMouseY) && (entScreenY >= g.lastMouseY) {
if within {
xOff, yOff := int(entOffset.X()), int(entOffset.Y())
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)
entity.Highlight()