diff --git a/d2core/d2map/d2mapengine/engine.go b/d2core/d2map/d2mapengine/engine.go index 1fae3662..13bfe073 100644 --- a/d2core/d2map/d2mapengine/engine.go +++ b/d2core/d2map/d2mapengine/engine.go @@ -209,7 +209,7 @@ func (m *MapEngine) RemoveEntity(entity d2mapentity.MapEntity) { if entity == nil { return } - panic("Removing entities is not currently implemented") + //panic("Removing entities is not currently implemented") //m.entities.Remove(entity) } diff --git a/d2core/d2map/d2mapentity/map_entity.go b/d2core/d2map/d2mapentity/map_entity.go index 648b715a..e6f23618 100644 --- a/d2core/d2map/d2mapentity/map_entity.go +++ b/d2core/d2map/d2mapentity/map_entity.go @@ -55,6 +55,10 @@ func (m *mapEntity) SetPath(path []d2astar.Pather, done func()) { m.done = done } +func (m *mapEntity) ClearPath() { + m.path = nil +} + func (m *mapEntity) SetSpeed(speed float64) { m.Speed = speed } @@ -173,7 +177,7 @@ func (m *mapEntity) GetPosition() (float64, float64) { } func (m *mapEntity) GetPositionF() (float64, float64) { - return float64(m.TileX) + (float64(m.subcellX)/5.0), float64(m.TileY) + (float64(m.subcellY)/5.0) + return float64(m.TileX) + (float64(m.subcellX) / 5.0), float64(m.TileY) + (float64(m.subcellY) / 5.0) } func (m *mapEntity) Name() string { diff --git a/d2core/d2map/d2mapentity/player.go b/d2core/d2map/d2mapentity/player.go index 38b1ecce..4fc41f74 100644 --- a/d2core/d2map/d2mapentity/player.go +++ b/d2core/d2map/d2mapentity/player.go @@ -28,6 +28,7 @@ type Player struct { animationMode string isRunToggled bool isRunning bool + isCasting bool } // run speed should be walkspeed * 1.5, since in the original game it is 6 yards walk and 9 yards run. @@ -117,6 +118,10 @@ func (p Player) IsInTown() bool { func (v *Player) Advance(tickTime float64) { v.Step(tickTime) + if v.IsCasting() && v.composite.GetPlayedCount() >= 1 { + v.isCasting = false + v.SetAnimationMode(v.GetAnimationMode().String()) + } v.composite.Advance(tickTime) if v.lastPathSize != len(v.path) { v.lastPathSize = len(v.path) @@ -170,6 +175,10 @@ func (v *Player) GetAnimationMode() d2enum.PlayerAnimationMode { return d2enum.AnimationModePlayerWalk } + if v.IsCasting() { + return d2enum.AnimationModePlayerCast + } + return d2enum.AnimationModePlayerNeutral } @@ -189,3 +198,12 @@ func (v *Player) rotate(direction int) { func (v *Player) Name() string { return v.name } + +func (v *Player) IsCasting() bool { + return v.isCasting +} + +func (v *Player) SetCasting() { + v.isCasting = true + v.SetAnimationMode(d2enum.AnimationModePlayerCast.String()) +} diff --git a/d2game/d2gamescreen/escape_menu.go b/d2game/d2gamescreen/escape_menu.go index fbcf1747..d3dc77b7 100644 --- a/d2game/d2gamescreen/escape_menu.go +++ b/d2game/d2gamescreen/escape_menu.go @@ -335,6 +335,7 @@ func (m *EscapeMenu) showLayout(id layoutID) { if id == saveLayoutID { mainMenu := CreateMainMenu() + mainMenu.SetScreenMode(ScreenModeMainMenu) d2screen.SetNextScreen(mainMenu) return } diff --git a/d2game/d2gamescreen/game.go b/d2game/d2gamescreen/game.go index f9453db3..3583957d 100644 --- a/d2game/d2gamescreen/game.go +++ b/d2game/d2gamescreen/game.go @@ -135,3 +135,7 @@ func (v *Game) OnPlayerMove(x, y float64) { heroPosY := v.localPlayer.LocationY / 5.0 v.gameClient.SendPacketToServer(d2netpacket.CreateMovePlayerPacket(v.gameClient.PlayerId, heroPosX, heroPosY, x, y)) } + +func (v *Game) OnPlayerCast(missleID int, targetX, targetY float64) { + v.gameClient.SendPacketToServer(d2netpacket.CreateCastPacket(v.gameClient.PlayerId, missleID, targetX, targetY)) +} diff --git a/d2game/d2player/game_controls.go b/d2game/d2player/game_controls.go index fb9b8715..5ec7630f 100644 --- a/d2game/d2player/game_controls.go +++ b/d2game/d2player/game_controls.go @@ -7,7 +7,6 @@ import ( "time" "github.com/OpenDiablo2/OpenDiablo2/d2common" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" @@ -197,7 +196,7 @@ func (g *GameControls) OnMouseButtonRepeat(event d2input.MouseEvent) bool { if event.Button == d2input.MouseButtonRight && now-lastRightBtnActionTime >= mouseBtnActionsTreshhold && !g.isInActiveMenusRect(event.X, event.Y) { lastRightBtnActionTime = now - g.ShootMissile(px, py) + g.inputListener.OnPlayerCast(missileID, px, py) return true } @@ -241,37 +240,13 @@ func (g *GameControls) OnMouseButtonDown(event d2input.MouseEvent) bool { if event.Button == d2input.MouseButtonRight && !g.isInActiveMenusRect(mx, my) { lastRightBtnActionTime = d2common.Now() - return g.ShootMissile(px, py) + g.inputListener.OnPlayerCast(missileID, px, py) + return true } return false } -func (g *GameControls) ShootMissile(px float64, py float64) bool { - missile, err := d2mapentity.CreateMissile( - int(g.hero.LocationX), - int(g.hero.LocationY), - d2datadict.Missiles[missileID], - ) - if err != nil { - return false - } - - rads := d2common.GetRadiansBetween( - g.hero.LocationX, - g.hero.LocationY, - px*5, - py*5, - ) - - missile.SetRadians(rads, func() { - g.mapEngine.RemoveEntity(missile) - }) - - g.mapEngine.AddEntity(missile) - return true -} - func (g *GameControls) Load() { animation, _ := d2asset.LoadAnimation(d2resource.GameGlobeOverlap, d2resource.PaletteSky) g.globeSprite, _ = d2ui.LoadSprite(animation) diff --git a/d2game/d2player/input_callback_listener.go b/d2game/d2player/input_callback_listener.go index dba83509..698186a9 100644 --- a/d2game/d2player/input_callback_listener.go +++ b/d2game/d2player/input_callback_listener.go @@ -2,4 +2,5 @@ package d2player type InputCallbackListener interface { OnPlayerMove(x, y float64) + OnPlayerCast(skillID int, x, y float64) } diff --git a/d2networking/d2client/game_client.go b/d2networking/d2client/game_client.go index cbdaff3b..d8f6527f 100644 --- a/d2networking/d2client/game_client.go +++ b/d2networking/d2client/game_client.go @@ -5,6 +5,9 @@ import ( "log" "os" + "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine" @@ -103,6 +106,33 @@ func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error { player.SetAnimationMode(player.GetAnimationMode().String()) }) } + case d2netpackettype.CastSkill: + playerCast := packet.PacketData.(d2netpacket.CastPacket) + player := g.Players[playerCast.SourceEntityID] + player.SetCasting() + player.ClearPath() + // currently hardcoded to missile skill + missile, err := d2mapentity.CreateMissile( + int(player.LocationX), + int(player.LocationY), + d2datadict.Missiles[playerCast.SkillID], + ) + if err != nil { + return err + } + + rads := d2common.GetRadiansBetween( + player.LocationX, + player.LocationY, + playerCast.TargetX*5, + playerCast.TargetY*5, + ) + + missile.SetRadians(rads, func() { + g.MapEngine.RemoveEntity(missile) + }) + + g.MapEngine.AddEntity(missile) case d2netpackettype.Ping: g.clientConnection.SendPacketToServer(d2netpacket.CreatePongPacket(g.PlayerId)) case d2netpackettype.ServerClosed: diff --git a/d2networking/d2netpacket/d2netpackettype/message_type.go b/d2networking/d2netpacket/d2netpackettype/message_type.go index e499c2cc..0dd68f86 100644 --- a/d2networking/d2netpacket/d2netpackettype/message_type.go +++ b/d2networking/d2netpacket/d2netpackettype/message_type.go @@ -16,4 +16,5 @@ const ( Ping // Ping message type Pong // Pong message type ServerClosed // Local host has closed the server + CastSkill // Sent to the client or server to indicate entity casting skill ) diff --git a/d2networking/d2netpacket/packet_player_cast.go b/d2networking/d2netpacket/packet_player_cast.go new file mode 100644 index 00000000..52e884dd --- /dev/null +++ b/d2networking/d2netpacket/packet_player_cast.go @@ -0,0 +1,26 @@ +package d2netpacket + +import "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype" + +// TODO: Need to handle being on different maps + +type CastPacket struct { + SourceEntityID string `json:"sourceEntityId"` + SkillID int `json:"skillId"` + TargetX float64 `json:"targetX"` + TargetY float64 `json:"targetY"` + TargetEntityID string `json:"targetEntityId"` +} + +func CreateCastPacket(entityID string, skillID int, targetX, targetY float64) NetPacket { + return NetPacket{ + PacketType: d2netpackettype.CastSkill, + PacketData: CastPacket{ + SourceEntityID: entityID, + SkillID: skillID, + TargetX: targetX, + TargetY: targetY, + TargetEntityID: "", // TODO implement targeting entities + }, + } +} diff --git a/d2networking/d2server/game_server.go b/d2networking/d2server/game_server.go index d28f13a6..a223005b 100644 --- a/d2networking/d2server/game_server.go +++ b/d2networking/d2server/game_server.go @@ -204,6 +204,10 @@ func OnPacketReceived(client ClientConnection, packet d2netpacket.NetPacket) err for _, player := range singletonServer.clientConnections { player.SendPacketToClient(packet) } + case d2netpackettype.CastSkill: + for _, player := range singletonServer.clientConnections { + player.SendPacketToClient(packet) + } } return nil }