D2mapengine remove entity, minor edits (#694)
* implement entity removal * add rgba color func, fix some lint errors in d2map * bugfix for map entity tests
This commit is contained in:
parent
0a6915a040
commit
8b2b991b12
|
@ -4,6 +4,7 @@ import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
|
||||||
|
|
||||||
// MapEntity is something that can be positioned on and rendered on the game map
|
// MapEntity is something that can be positioned on and rendered on the game map
|
||||||
type MapEntity interface {
|
type MapEntity interface {
|
||||||
|
ID() string
|
||||||
Render(target Surface)
|
Render(target Surface)
|
||||||
Advance(tickTime float64)
|
Advance(tickTime float64)
|
||||||
GetPosition() d2vector.Position
|
GetPosition() d2vector.Position
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package d2common
|
||||||
|
|
||||||
|
import "image/color"
|
||||||
|
|
||||||
|
func Color(rgba uint32) color.RGBA {
|
||||||
|
result := color.RGBA{}
|
||||||
|
a, b, g, r := 0, 1, 2, 3
|
||||||
|
byteWidth := 8
|
||||||
|
byteMask := 0xff
|
||||||
|
|
||||||
|
for idx := 0; idx < 4; idx++ {
|
||||||
|
shift := idx * byteWidth
|
||||||
|
component := uint8(rgba>>shift) & uint8(byteMask)
|
||||||
|
|
||||||
|
switch idx {
|
||||||
|
case a:
|
||||||
|
result.A = component
|
||||||
|
case b:
|
||||||
|
result.B = component
|
||||||
|
case g:
|
||||||
|
result.G = component
|
||||||
|
case r:
|
||||||
|
result.R = component
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -20,8 +20,8 @@ import (
|
||||||
|
|
||||||
// MapEngine loads the tiles which make up the isometric map and the entities
|
// MapEngine loads the tiles which make up the isometric map and the entities
|
||||||
type MapEngine struct {
|
type MapEngine struct {
|
||||||
seed int64 // The map seed
|
seed int64 // The map seed
|
||||||
entities []d2interface.MapEntity // Entities on the map
|
entities map[string]d2interface.MapEntity // Entities on the map
|
||||||
tiles []MapTile
|
tiles []MapTile
|
||||||
size d2common.Size // Size of the map, in tiles
|
size d2common.Size // Size of the map, in tiles
|
||||||
levelType d2datadict.LevelTypeRecord // Level type of this map
|
levelType d2datadict.LevelTypeRecord // Level type of this map
|
||||||
|
@ -44,7 +44,7 @@ func (m *MapEngine) GetStartingPosition() (x, y int) {
|
||||||
|
|
||||||
// ResetMap clears all map and entity data and reloads it from the cached files.
|
// ResetMap clears all map and entity data and reloads it from the cached files.
|
||||||
func (m *MapEngine) ResetMap(levelType d2enum.RegionIdType, width, height int) {
|
func (m *MapEngine) ResetMap(levelType d2enum.RegionIdType, width, height int) {
|
||||||
m.entities = make([]d2interface.MapEntity, 0)
|
m.entities = make(map[string]d2interface.MapEntity)
|
||||||
m.levelType = d2datadict.LevelTypes[levelType]
|
m.levelType = d2datadict.LevelTypes[levelType]
|
||||||
m.size = d2common.Size{Width: width, Height: height}
|
m.size = d2common.Size{Width: width, Height: height}
|
||||||
m.tiles = make([]MapTile, width*height)
|
m.tiles = make([]MapTile, width*height)
|
||||||
|
@ -155,7 +155,7 @@ func (m *MapEngine) PlaceStamp(stamp *d2mapstamp.Stamp, tileOffsetX, tileOffsetY
|
||||||
// Copy over the map tile data
|
// Copy over the map tile data
|
||||||
for y := 0; y < stampH; y++ {
|
for y := 0; y < stampH; y++ {
|
||||||
for x := 0; x < stampW; x++ {
|
for x := 0; x < stampW; x++ {
|
||||||
targetTileIndex := m.tileCoordinateToIndex((x + xMin), (y + yMin))
|
targetTileIndex := m.tileCoordinateToIndex(x+xMin, y+yMin)
|
||||||
stampTile := *stamp.Tile(x, y)
|
stampTile := *stamp.Tile(x, y)
|
||||||
m.tiles[targetTileIndex].RegionType = stamp.RegionID()
|
m.tiles[targetTileIndex].RegionType = stamp.RegionID()
|
||||||
m.tiles[targetTileIndex].Components = stampTile
|
m.tiles[targetTileIndex].Components = stampTile
|
||||||
|
@ -164,7 +164,11 @@ func (m *MapEngine) PlaceStamp(stamp *d2mapstamp.Stamp, tileOffsetX, tileOffsetY
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy over the entities
|
// Copy over the entities
|
||||||
m.entities = append(m.entities, stamp.Entities(tileOffsetX, tileOffsetY)...)
|
stampEntities := stamp.Entities(tileOffsetX, tileOffsetY)
|
||||||
|
for idx := range stampEntities {
|
||||||
|
e := stampEntities[idx]
|
||||||
|
m.entities[e.ID()] = e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts x,y tile coordinate into index in MapEngine.tiles
|
// converts x,y tile coordinate into index in MapEngine.tiles
|
||||||
|
@ -172,7 +176,7 @@ func (m *MapEngine) tileCoordinateToIndex(x, y int) int {
|
||||||
return x + (y * m.size.Width)
|
return x + (y * m.size.Width)
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts tile index from MapEngine.tiles to x,y coordinate
|
// tileIndexToCoordinate converts tile index from MapEngine.tiles to x,y coordinate
|
||||||
func (m *MapEngine) tileIndexToCoordinate(index int) (x, y int) {
|
func (m *MapEngine) tileIndexToCoordinate(index int) (x, y int) {
|
||||||
return index % m.size.Width, index / m.size.Width
|
return index % m.size.Width, index / m.size.Width
|
||||||
}
|
}
|
||||||
|
@ -196,8 +200,8 @@ func (m *MapEngine) TileAt(tileX, tileY int) *MapTile {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entities returns a pointer a slice of all map entities.
|
// Entities returns a pointer a slice of all map entities.
|
||||||
func (m *MapEngine) Entities() *[]d2interface.MapEntity {
|
func (m *MapEngine) Entities() map[string]d2interface.MapEntity {
|
||||||
return &m.entities
|
return m.entities
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seed returns the map generation seed.
|
// Seed returns the map generation seed.
|
||||||
|
@ -207,15 +211,16 @@ func (m *MapEngine) Seed() int64 {
|
||||||
|
|
||||||
// AddEntity adds an entity to a slice containing all entities.
|
// AddEntity adds an entity to a slice containing all entities.
|
||||||
func (m *MapEngine) AddEntity(entity d2interface.MapEntity) {
|
func (m *MapEngine) AddEntity(entity d2interface.MapEntity) {
|
||||||
m.entities = append(m.entities, entity)
|
m.entities[entity.ID()] = entity
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveEntity is not currently implemented.
|
// RemoveEntity removes an entity from the map engine
|
||||||
func (m *MapEngine) RemoveEntity(entity d2interface.MapEntity) {
|
func (m *MapEngine) RemoveEntity(entity d2interface.MapEntity) {
|
||||||
if entity == nil {
|
if entity == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// m.entities.Remove(entity)
|
|
||||||
|
delete(m.entities, entity.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTiles returns a slice of all tiles matching the given style,
|
// GetTiles returns a slice of all tiles matching the given style,
|
||||||
|
@ -264,8 +269,8 @@ func (m *MapEngine) GetCenterPosition() (x, y float64) {
|
||||||
// Advance calls the Advance() method for all entities,
|
// Advance calls the Advance() method for all entities,
|
||||||
// processing a single tick.
|
// processing a single tick.
|
||||||
func (m *MapEngine) Advance(tickTime float64) {
|
func (m *MapEngine) Advance(tickTime float64) {
|
||||||
for idx := range m.entities {
|
for ID := range m.entities {
|
||||||
m.entities[idx].Advance(tickTime)
|
m.entities[ID].Advance(tickTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package d2mapentity
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
|
@ -50,7 +49,6 @@ func NewPlayer(id, name string, x, y, direction int, heroType d2enum.Hero,
|
||||||
stats.Stamina = stats.MaxStamina
|
stats.Stamina = stats.MaxStamina
|
||||||
|
|
||||||
result := &Player{
|
result := &Player{
|
||||||
ID: id,
|
|
||||||
mapEntity: newMapEntity(x, y),
|
mapEntity: newMapEntity(x, y),
|
||||||
composite: composite,
|
composite: composite,
|
||||||
Equipment: equipment,
|
Equipment: equipment,
|
||||||
|
@ -62,6 +60,8 @@ func NewPlayer(id, name string, x, y, direction int, heroType d2enum.Hero,
|
||||||
isInTown: true,
|
isInTown: true,
|
||||||
isRunning: true,
|
isRunning: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.mapEntity.uuid = id
|
||||||
result.SetSpeed(baseRunSpeed)
|
result.SetSpeed(baseRunSpeed)
|
||||||
result.mapEntity.directioner = result.rotate
|
result.mapEntity.directioner = result.rotate
|
||||||
err = composite.SetMode(d2enum.PlayerAnimationModeTownNeutral, equipment.RightHand.GetWeaponClass())
|
err = composite.SetMode(d2enum.PlayerAnimationModeTownNeutral, equipment.RightHand.GetWeaponClass())
|
||||||
|
|
|
@ -19,6 +19,11 @@ type Item struct {
|
||||||
Item *diablo2item.Item
|
Item *diablo2item.Item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ID returns the item uuid
|
||||||
|
func (i *Item) ID() string {
|
||||||
|
return i.AnimatedEntity.uuid
|
||||||
|
}
|
||||||
|
|
||||||
// GetPosition returns the item position vector
|
// GetPosition returns the item position vector
|
||||||
func (i *Item) GetPosition() d2vector.Position {
|
func (i *Item) GetPosition() d2vector.Position {
|
||||||
return i.AnimatedEntity.Position
|
return i.AnimatedEntity.Position
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package d2mapentity
|
package d2mapentity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,6 +12,7 @@ const (
|
||||||
|
|
||||||
// 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 {
|
||||||
|
uuid string
|
||||||
Position d2vector.Position
|
Position d2vector.Position
|
||||||
Target d2vector.Position
|
Target d2vector.Position
|
||||||
velocity d2vector.Vector
|
velocity d2vector.Vector
|
||||||
|
@ -29,6 +32,7 @@ func newMapEntity(x, y int) mapEntity {
|
||||||
pos := d2vector.NewPosition(float64(x), float64(y))
|
pos := d2vector.NewPosition(float64(x), float64(y))
|
||||||
|
|
||||||
return mapEntity{
|
return mapEntity{
|
||||||
|
uuid: uuid.NewV4().String(),
|
||||||
Position: pos,
|
Position: pos,
|
||||||
Target: pos,
|
Target: pos,
|
||||||
velocity: *d2vector.VectorZero(),
|
velocity: *d2vector.VectorZero(),
|
||||||
|
|
|
@ -14,6 +14,11 @@ type Missile struct {
|
||||||
record *d2datadict.MissileRecord
|
record *d2datadict.MissileRecord
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ID returns the missile uuid
|
||||||
|
func (m *Missile) ID() string {
|
||||||
|
return m.AnimatedEntity.uuid
|
||||||
|
}
|
||||||
|
|
||||||
// GetPosition returns the position of the missile
|
// GetPosition returns the position of the missile
|
||||||
func (m *Missile) GetPosition() d2vector.Position {
|
func (m *Missile) GetPosition() d2vector.Position {
|
||||||
return m.AnimatedEntity.Position
|
return m.AnimatedEntity.Position
|
||||||
|
|
|
@ -43,6 +43,11 @@ func selectEquip(slice []string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ID returns the NPC uuid
|
||||||
|
func (v *NPC) ID() string {
|
||||||
|
return v.mapEntity.uuid
|
||||||
|
}
|
||||||
|
|
||||||
// Render renders this entity's animated composite.
|
// Render renders this entity's animated composite.
|
||||||
func (v *NPC) Render(target d2interface.Surface) {
|
func (v *NPC) Render(target d2interface.Surface) {
|
||||||
renderOffset := v.Position.RenderOffset()
|
renderOffset := v.Position.RenderOffset()
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
// Player is the player character entity.
|
// Player is the player character entity.
|
||||||
type Player struct {
|
type Player struct {
|
||||||
mapEntity
|
mapEntity
|
||||||
ID string
|
|
||||||
name string
|
name string
|
||||||
animationMode string
|
animationMode string
|
||||||
composite *d2asset.Composite
|
composite *d2asset.Composite
|
||||||
|
@ -35,6 +34,11 @@ type Player struct {
|
||||||
const baseWalkSpeed = 6.0
|
const baseWalkSpeed = 6.0
|
||||||
const baseRunSpeed = 9.0
|
const baseRunSpeed = 9.0
|
||||||
|
|
||||||
|
// ID returns the Player uuid
|
||||||
|
func (p *Player) ID() string {
|
||||||
|
return p.mapEntity.uuid
|
||||||
|
}
|
||||||
|
|
||||||
// SetIsInTown sets a flag indicating that the player is in town.
|
// SetIsInTown sets a flag indicating that the player is in town.
|
||||||
func (p *Player) SetIsInTown(isInTown bool) {
|
func (p *Player) SetIsInTown(isInTown bool) {
|
||||||
p.isInTown = isInTown
|
p.isInTown = isInTown
|
||||||
|
@ -86,7 +90,7 @@ func (p *Player) Advance(tickTime float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.composite.Advance(tickTime); err != nil {
|
if err := p.composite.Advance(tickTime); err != nil {
|
||||||
fmt.Printf("failed to advance composite animation of player: %s, err: %v\n", p.ID, err)
|
fmt.Printf("failed to advance composite animation of player: %s, err: %v\n", p.ID(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.lastPathSize != len(p.path) {
|
if p.lastPathSize != len(p.path) {
|
||||||
|
@ -109,7 +113,7 @@ func (p *Player) Render(target d2interface.Surface) {
|
||||||
defer target.Pop()
|
defer target.Pop()
|
||||||
|
|
||||||
if err := p.composite.Render(target); err != nil {
|
if err := p.composite.Render(target); err != nil {
|
||||||
fmt.Printf("failed to render the composite of player: %s, err: %v\n", p.ID, err)
|
fmt.Printf("failed to render the composite of player: %s, err: %v\n", p.ID(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +177,8 @@ func (p *Player) IsCasting() bool {
|
||||||
func (p *Player) SetCasting() {
|
func (p *Player) SetCasting() {
|
||||||
p.isCasting = true
|
p.isCasting = true
|
||||||
if err := p.SetAnimationMode(d2enum.PlayerAnimationModeCast); err != nil {
|
if err := p.SetAnimationMode(d2enum.PlayerAnimationModeCast); err != nil {
|
||||||
fmt.Printf("failed to set animationMode of player: %s to: %d, err: %v\n", p.ID, d2enum.PlayerAnimationModeCast, err)
|
fmtStr := "failed to set animationMode of player: %s to: %d, err: %v\n"
|
||||||
|
fmt.Printf(fmtStr, p.ID(), d2enum.PlayerAnimationModeCast, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
|
@ -19,6 +20,31 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
screenMiddleX = 400
|
screenMiddleX = 400
|
||||||
|
two = 2
|
||||||
|
|
||||||
|
dbgOffsetXY = 40
|
||||||
|
dbgBoxWidth = 220
|
||||||
|
dbgBoxHeight = 60
|
||||||
|
dbgBoxPadding = 10
|
||||||
|
|
||||||
|
dbgCollisionSize = 5
|
||||||
|
dbgCollisionOffsetX = -3
|
||||||
|
dbgCollisionOffsetY = 4
|
||||||
|
|
||||||
|
whiteHalfOpacity = 0xffffff7f
|
||||||
|
blackQuarterOpacity = 0x00000040
|
||||||
|
lightGreenFullOpacity = 0x40ff00ff
|
||||||
|
magentaFullOpacity = 0xff00ffff
|
||||||
|
yellowFullOpacity = 0xffff00ff
|
||||||
|
lightBlueQuarterOpacity = 0x5050ff32
|
||||||
|
whiteQuarterOpacity = 0xffffff64
|
||||||
|
redQuarterOpacity = 0x74000064
|
||||||
|
|
||||||
|
subtilesPerTile = 5
|
||||||
|
orthoSubTileWidth = 16
|
||||||
|
orthoSubTileHeight = 8
|
||||||
|
orthoTileWidth = subtilesPerTile * orthoSubTileWidth
|
||||||
|
orthoTileHeight = subtilesPerTile * orthoSubTileHeight
|
||||||
)
|
)
|
||||||
|
|
||||||
// MapRenderer manages the game viewport and Camera. It requests tile and entity data from MapEngine and renders it.
|
// MapRenderer manages the game viewport and Camera. It requests tile and entity data from MapEngine and renders it.
|
||||||
|
@ -172,7 +198,7 @@ func (mr *MapRenderer) renderPass2(target d2interface.Surface, startX, startY, e
|
||||||
mr.viewport.PushTranslationWorld(float64(tileX), float64(tileY))
|
mr.viewport.PushTranslationWorld(float64(tileX), float64(tileY))
|
||||||
|
|
||||||
// TODO: Do not loop over every entity every frame
|
// TODO: Do not loop over every entity every frame
|
||||||
for _, mapEntity := range *mr.mapEngine.Entities() {
|
for _, mapEntity := range mr.mapEngine.Entities() {
|
||||||
pos := mapEntity.GetPosition()
|
pos := mapEntity.GetPosition()
|
||||||
vec := pos.World()
|
vec := pos.World()
|
||||||
entityX, entityY := vec.X(), vec.Y()
|
entityX, entityY := vec.X(), vec.Y()
|
||||||
|
@ -204,7 +230,7 @@ func (mr *MapRenderer) renderPass3(target d2interface.Surface, startX, startY, e
|
||||||
mr.renderTilePass2(tile, target)
|
mr.renderTilePass2(tile, target)
|
||||||
|
|
||||||
// TODO: Do not loop over every entity every frame
|
// TODO: Do not loop over every entity every frame
|
||||||
for _, mapEntity := range *mr.mapEngine.Entities() {
|
for _, mapEntity := range mr.mapEngine.Entities() {
|
||||||
pos := mapEntity.GetPosition()
|
pos := mapEntity.GetPosition()
|
||||||
vec := pos.World()
|
vec := pos.World()
|
||||||
entityX, entityY := vec.X(), vec.Y()
|
entityX, entityY := vec.X(), vec.Y()
|
||||||
|
@ -327,9 +353,10 @@ func (mr *MapRenderer) renderShadow(tile d2ds1.FloorShadowRecord, target d2inter
|
||||||
defer mr.viewport.PushTranslationOrtho(-80, float64(tile.YAdjust)).PopTranslation()
|
defer mr.viewport.PushTranslationOrtho(-80, float64(tile.YAdjust)).PopTranslation()
|
||||||
|
|
||||||
target.PushTranslation(mr.viewport.GetTranslationScreen())
|
target.PushTranslation(mr.viewport.GetTranslationScreen())
|
||||||
target.PushColor(color.RGBA{R: 255, G: 255, B: 255, A: 160}) //nolint:gomnd // Not a magic number...
|
defer target.Pop()
|
||||||
|
|
||||||
defer target.PopN(2)
|
target.PushColor(color.RGBA{R: 255, G: 255, B: 255, A: 160}) //nolint:gomnd // Not a magic number...
|
||||||
|
defer target.Pop()
|
||||||
|
|
||||||
if err := target.Render(img); err != nil {
|
if err := target.Render(img); err != nil {
|
||||||
fmt.Printf("failed to render the shadow, err: %v\n", err)
|
fmt.Printf("failed to render the shadow, err: %v\n", err)
|
||||||
|
@ -346,26 +373,27 @@ func (mr *MapRenderer) renderMapDebug(mapDebugVisLevel int, target d2interface.S
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:funlen // doesn't make sense to split this function
|
||||||
func (mr *MapRenderer) renderEntityDebug(target d2interface.Surface) {
|
func (mr *MapRenderer) renderEntityDebug(target d2interface.Surface) {
|
||||||
entities := *mr.mapEngine.Entities()
|
entities := mr.mapEngine.Entities()
|
||||||
|
|
||||||
for idx := range entities {
|
for idx := range entities {
|
||||||
e := entities[idx]
|
e := entities[idx]
|
||||||
pos := e.GetPosition()
|
pos := e.GetPosition()
|
||||||
world := pos
|
world := pos
|
||||||
x, y := world.X()/5, world.Y()/5
|
x, y := world.X()/subtilesPerTile, world.Y()/subtilesPerTile
|
||||||
velocity := e.GetVelocity()
|
velocity := e.GetVelocity()
|
||||||
velocity = *velocity.Clone()
|
velocity = *velocity.Clone()
|
||||||
vx, vy := mr.viewport.WorldToOrtho(velocity.X(), velocity.Y())
|
vx, vy := mr.viewport.WorldToOrtho(velocity.X(), velocity.Y())
|
||||||
screenX, screenY := mr.viewport.WorldToScreen(x, y)
|
screenX, screenY := mr.viewport.WorldToScreen(x, y)
|
||||||
|
|
||||||
offX, offY := 40, -40
|
offX, offY := dbgOffsetXY, -dbgOffsetXY
|
||||||
|
|
||||||
entScreenXf, entScreenYf := mr.WorldToScreenF(e.GetPositionF())
|
entScreenXf, entScreenYf := mr.WorldToScreenF(e.GetPositionF())
|
||||||
entScreenX := int(math.Floor(entScreenXf))
|
entScreenX := int(math.Floor(entScreenXf))
|
||||||
entScreenY := int(math.Floor(entScreenYf))
|
entScreenY := int(math.Floor(entScreenYf))
|
||||||
entityWidth, entityHeight := e.GetSize()
|
entityWidth, entityHeight := e.GetSize()
|
||||||
halfWidth, halfHeight := entityWidth/2, entityHeight/2
|
halfWidth, halfHeight := entityWidth/two, entityHeight/two
|
||||||
l, r := entScreenX-halfWidth, entScreenX+halfWidth
|
l, r := entScreenX-halfWidth, entScreenX+halfWidth
|
||||||
t, b := entScreenY-halfHeight, entScreenY+halfHeight
|
t, b := entScreenY-halfHeight, entScreenY+halfHeight
|
||||||
mx, my := mr.renderer.GetCursorPos()
|
mx, my := mr.renderer.GetCursorPos()
|
||||||
|
@ -373,8 +401,8 @@ func (mr *MapRenderer) renderEntityDebug(target d2interface.Surface) {
|
||||||
yWithin := (t <= my) && (b >= my)
|
yWithin := (t <= my) && (b >= my)
|
||||||
within := xWithin && yWithin
|
within := xWithin && yWithin
|
||||||
|
|
||||||
boxLineColor := color.RGBA{255, 0, 255, 128}
|
boxLineColor := d2common.Color(magentaFullOpacity)
|
||||||
boxHoverColor := color.RGBA{255, 255, 0, 128}
|
boxHoverColor := d2common.Color(yellowFullOpacity)
|
||||||
|
|
||||||
boxColor := boxLineColor
|
boxColor := boxLineColor
|
||||||
|
|
||||||
|
@ -382,30 +410,39 @@ func (mr *MapRenderer) renderEntityDebug(target d2interface.Surface) {
|
||||||
boxColor = boxHoverColor
|
boxColor = boxHoverColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stack := 0
|
||||||
// box
|
// box
|
||||||
mr.viewport.PushTranslationWorld(x, y)
|
mr.viewport.PushTranslationWorld(x, y)
|
||||||
|
|
||||||
target.PushTranslation(screenX, screenY)
|
target.PushTranslation(screenX, screenY)
|
||||||
|
stack++
|
||||||
|
|
||||||
target.PushTranslation(-halfWidth, -halfHeight)
|
target.PushTranslation(-halfWidth, -halfHeight)
|
||||||
|
stack++
|
||||||
|
|
||||||
target.DrawLine(0, entityHeight, boxColor)
|
target.DrawLine(0, entityHeight, boxColor)
|
||||||
target.DrawLine(entityWidth, 0, boxColor)
|
target.DrawLine(entityWidth, 0, boxColor)
|
||||||
|
|
||||||
target.PushTranslation(entityWidth, entityHeight)
|
target.PushTranslation(entityWidth, entityHeight)
|
||||||
|
stack++
|
||||||
|
|
||||||
target.DrawLine(-entityWidth, 0, boxColor)
|
target.DrawLine(-entityWidth, 0, boxColor)
|
||||||
target.DrawLine(0, -entityHeight, boxColor)
|
target.DrawLine(0, -entityHeight, boxColor)
|
||||||
target.PopN(3)
|
target.PopN(stack)
|
||||||
mr.viewport.PopTranslation()
|
mr.viewport.PopTranslation()
|
||||||
|
|
||||||
// hover
|
// hover
|
||||||
if within {
|
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, d2common.Color(whiteHalfOpacity))
|
||||||
target.PushTranslation(offX+10, offY-20)
|
target.PushTranslation(offX+dbgBoxPadding, offY-dbgBoxPadding*two)
|
||||||
target.PushTranslation(-10, -10)
|
target.PushTranslation(-dbgOffsetXY, -dbgOffsetXY)
|
||||||
target.DrawRect(200, 50, color.RGBA{0, 0, 0, 64})
|
target.DrawRect(dbgBoxWidth, dbgBoxHeight, d2common.Color(blackQuarterOpacity))
|
||||||
target.Pop()
|
target.Pop()
|
||||||
target.DrawTextf("World (%.2f, %.2f)\nVelocity (%.2f, %.2f)", x, y, vx, vy)
|
target.DrawTextf("World (%.2f, %.2f)\nVelocity (%.2f, %.2f)", x, y, vx, vy)
|
||||||
target.Pop()
|
target.Pop()
|
||||||
target.DrawLine(int(vx), int(vy), color.RGBA{64, 255, 0, 255})
|
target.DrawLine(int(vx), int(vy), d2common.Color(lightGreenFullOpacity))
|
||||||
target.Pop()
|
target.Pop()
|
||||||
mr.viewport.PopTranslation()
|
mr.viewport.PopTranslation()
|
||||||
}
|
}
|
||||||
|
@ -423,9 +460,9 @@ func (mr *MapRenderer) WorldToScreenF(x, y float64) (screenX, screenY float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *MapRenderer) renderTileDebug(ax, ay, debugVisLevel int, target d2interface.Surface) {
|
func (mr *MapRenderer) renderTileDebug(ax, ay, debugVisLevel int, target d2interface.Surface) {
|
||||||
subTileColor := color.RGBA{R: 80, G: 80, B: 255, A: 50}
|
subTileColor := d2common.Color(lightBlueQuarterOpacity)
|
||||||
tileColor := color.RGBA{R: 255, G: 255, B: 255, A: 100}
|
tileColor := d2common.Color(whiteQuarterOpacity)
|
||||||
tileCollisionColor := color.RGBA{R: 128, G: 0, B: 0, A: 100}
|
tileCollisionColor := d2common.Color(redQuarterOpacity)
|
||||||
|
|
||||||
screenX1, screenY1 := mr.viewport.WorldToScreen(float64(ax), float64(ay))
|
screenX1, screenY1 := mr.viewport.WorldToScreen(float64(ax), float64(ay))
|
||||||
screenX2, screenY2 := mr.viewport.WorldToScreen(float64(ax+1), float64(ay))
|
screenX2, screenY2 := mr.viewport.WorldToScreen(float64(ax+1), float64(ay))
|
||||||
|
@ -442,15 +479,15 @@ func (mr *MapRenderer) renderTileDebug(ax, ay, debugVisLevel int, target d2inter
|
||||||
|
|
||||||
if debugVisLevel > 1 {
|
if debugVisLevel > 1 {
|
||||||
for i := 1; i <= 4; i++ {
|
for i := 1; i <= 4; i++ {
|
||||||
x2 := i * 16
|
x2 := i * orthoSubTileWidth
|
||||||
y2 := i * 8
|
y2 := i * orthoSubTileHeight
|
||||||
|
|
||||||
target.PushTranslation(-x2, y2)
|
target.PushTranslation(-x2, y2)
|
||||||
target.DrawLine(80, 40, subTileColor)
|
target.DrawLine(orthoTileWidth, orthoTileHeight, subTileColor)
|
||||||
target.Pop()
|
target.Pop()
|
||||||
|
|
||||||
target.PushTranslation(x2, y2)
|
target.PushTranslation(x2, y2)
|
||||||
target.DrawLine(-80, 40, subTileColor)
|
target.DrawLine(-orthoTileWidth, orthoTileHeight, subTileColor)
|
||||||
target.Pop()
|
target.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +495,7 @@ func (mr *MapRenderer) renderTileDebug(ax, ay, debugVisLevel int, target d2inter
|
||||||
|
|
||||||
for i, wall := range tile.Components.Walls {
|
for i, wall := range tile.Components.Walls {
|
||||||
if wall.Type.Special() {
|
if wall.Type.Special() {
|
||||||
target.PushTranslation(-20, 10+(i+1)*14)
|
target.PushTranslation(-20, 10+(i+1)*14) // what are these magic numbers??
|
||||||
target.DrawTextf("s: %v-%v", wall.Style, wall.Sequence)
|
target.DrawTextf("s: %v-%v", wall.Style, wall.Sequence)
|
||||||
target.Pop()
|
target.Pop()
|
||||||
}
|
}
|
||||||
|
@ -466,14 +503,14 @@ func (mr *MapRenderer) renderTileDebug(ax, ay, debugVisLevel int, target d2inter
|
||||||
|
|
||||||
for yy := 0; yy < 5; yy++ {
|
for yy := 0; yy < 5; yy++ {
|
||||||
for xx := 0; xx < 5; xx++ {
|
for xx := 0; xx < 5; xx++ {
|
||||||
isoX := (xx - yy) * 16
|
isoX := (xx - yy) * orthoSubTileWidth
|
||||||
isoY := (xx + yy) * 8
|
isoY := (xx + yy) * orthoSubTileHeight
|
||||||
|
|
||||||
blocked := tile.GetSubTileFlags(xx, yy).BlockWalk
|
blocked := tile.GetSubTileFlags(xx, yy).BlockWalk
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
target.PushTranslation(isoX-3, isoY+4)
|
target.PushTranslation(isoX+dbgCollisionOffsetX, isoY+dbgCollisionOffsetY)
|
||||||
target.DrawRect(5, 5, tileCollisionColor)
|
target.DrawRect(dbgCollisionSize, dbgCollisionSize, tileCollisionColor)
|
||||||
target.Pop()
|
target.Pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ package d2object
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
|
@ -15,6 +17,7 @@ import (
|
||||||
|
|
||||||
// Object represents a composite of animations that can be projected onto the map.
|
// Object represents a composite of animations that can be projected onto the map.
|
||||||
type Object struct {
|
type Object struct {
|
||||||
|
uuid string
|
||||||
Position d2vector.Position
|
Position d2vector.Position
|
||||||
composite *d2asset.Composite
|
composite *d2asset.Composite
|
||||||
highlight bool
|
highlight bool
|
||||||
|
@ -28,6 +31,7 @@ type Object struct {
|
||||||
func CreateObject(x, y int, objectRec *d2datadict.ObjectRecord, palettePath string) (*Object, error) {
|
func CreateObject(x, y int, objectRec *d2datadict.ObjectRecord, palettePath string) (*Object, error) {
|
||||||
locX, locY := float64(x), float64(y)
|
locX, locY := float64(x), float64(y)
|
||||||
entity := &Object{
|
entity := &Object{
|
||||||
|
uuid: uuid.NewV4().String(),
|
||||||
objectRecord: objectRec,
|
objectRecord: objectRec,
|
||||||
Position: d2vector.NewPosition(locX, locY),
|
Position: d2vector.NewPosition(locX, locY),
|
||||||
name: d2common.TranslateString(objectRec.Name),
|
name: d2common.TranslateString(objectRec.Name),
|
||||||
|
@ -84,6 +88,11 @@ func (ob *Object) setMode(animationMode d2enum.ObjectAnimationMode, direction in
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ID returns the object uuid
|
||||||
|
func (ob *Object) ID() string {
|
||||||
|
return ob.uuid
|
||||||
|
}
|
||||||
|
|
||||||
// Highlight sets the entity highlighted flag to true.
|
// Highlight sets the entity highlighted flag to true.
|
||||||
func (ob *Object) Highlight() {
|
func (ob *Object) Highlight() {
|
||||||
ob.highlight = true
|
ob.highlight = true
|
||||||
|
|
|
@ -56,7 +56,7 @@ func CreateGame(
|
||||||
// find the local player and its initial location
|
// find the local player and its initial location
|
||||||
var startX, startY float64
|
var startX, startY float64
|
||||||
for _, player := range gameClient.Players {
|
for _, player := range gameClient.Players {
|
||||||
if player.ID != gameClient.PlayerID {
|
if player.ID() != gameClient.PlayerID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
worldPosition := player.Position.World()
|
worldPosition := player.Position.World()
|
||||||
|
@ -237,7 +237,7 @@ func (v *Game) Advance(elapsed float64) error {
|
||||||
|
|
||||||
func (v *Game) bindGameControls() error {
|
func (v *Game) bindGameControls() error {
|
||||||
for _, player := range v.gameClient.Players {
|
for _, player := range v.gameClient.Players {
|
||||||
if player.ID != v.gameClient.PlayerID {
|
if player.ID() != v.gameClient.PlayerID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +253,7 @@ func (v *Game) bindGameControls() error {
|
||||||
v.gameControls.Load()
|
v.gameControls.Load()
|
||||||
|
|
||||||
if err := v.inputManager.BindHandler(v.gameControls); err != nil {
|
if err := v.inputManager.BindHandler(v.gameControls); err != nil {
|
||||||
fmt.Printf(bindControlsErrStr, player.ID)
|
fmt.Printf(bindControlsErrStr, player.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
|
@ -421,8 +421,8 @@ func (g *GameControls) isInActiveMenusRect(px int, py int) bool {
|
||||||
|
|
||||||
// TODO: consider caching the panels to single image that is reused.
|
// TODO: consider caching the panels to single image that is reused.
|
||||||
func (g *GameControls) Render(target d2interface.Surface) error {
|
func (g *GameControls) Render(target d2interface.Surface) error {
|
||||||
for entityIdx := range *g.mapEngine.Entities() {
|
for entityIdx := range g.mapEngine.Entities() {
|
||||||
entity := (*g.mapEngine.Entities())[entityIdx]
|
entity := (g.mapEngine.Entities())[entityIdx]
|
||||||
if !entity.Selectable() {
|
if !entity.Selectable() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,7 +171,7 @@ func (g *GameClient) handleAddPlayerPacket(packet d2netpacket.NetPacket) error {
|
||||||
newPlayer := d2mapentity.NewPlayer(player.ID, player.Name, player.X, player.Y, 0,
|
newPlayer := d2mapentity.NewPlayer(player.ID, player.Name, player.X, player.Y, 0,
|
||||||
player.HeroType, player.Stats, &player.Equipment)
|
player.HeroType, player.Stats, &player.Equipment)
|
||||||
|
|
||||||
g.Players[newPlayer.ID] = newPlayer
|
g.Players[newPlayer.ID()] = newPlayer
|
||||||
g.MapEngine.AddEntity(newPlayer)
|
g.MapEngine.AddEntity(newPlayer)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -214,7 +214,8 @@ func (g *GameClient) handleMovePlayerPacket(packet d2netpacket.NetPacket) error
|
||||||
err := player.SetAnimationMode(player.GetAnimationMode())
|
err := player.SetAnimationMode(player.GetAnimationMode())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("GameClient: error setting animation mode for player %s: %s", player.ID, err)
|
fmtStr := "GameClient: error setting animation mode for player %s: %s"
|
||||||
|
log.Printf(fmtStr, player.ID(), err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue