2020-06-13 20:36:20 -04:00
|
|
|
package d2gamescreen
|
2019-11-11 23:48:55 -05:00
|
|
|
|
|
|
|
import (
|
2019-12-21 20:53:18 -05:00
|
|
|
"image/color"
|
|
|
|
|
2020-06-28 19:31:10 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
|
|
|
|
2020-06-24 18:46:03 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
|
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
"fmt"
|
|
|
|
|
2020-06-22 15:55:32 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
2020-06-13 18:32:09 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
2020-06-22 15:55:32 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer"
|
2020-06-21 18:40:37 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
2019-11-11 23:48:55 -05:00
|
|
|
)
|
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
const hideZoneTextAfterSeconds = 2.0
|
|
|
|
|
|
|
|
// Game represents the Gameplay screen
|
2019-11-11 23:48:55 -05:00
|
|
|
type Game struct {
|
2020-06-19 02:19:27 -04:00
|
|
|
gameClient *d2client.GameClient
|
2020-06-26 17:12:19 -04:00
|
|
|
mapRenderer *d2maprenderer.MapRenderer
|
2020-06-19 02:19:27 -04:00
|
|
|
gameControls *d2player.GameControls // TODO: Hack
|
2020-06-21 18:40:37 -04:00
|
|
|
localPlayer *d2mapentity.Player
|
2020-06-22 15:55:32 -04:00
|
|
|
lastRegionType d2enum.RegionIdType
|
2020-06-19 02:19:27 -04:00
|
|
|
ticksSinceLevelCheck float64
|
2020-06-25 10:16:17 -04:00
|
|
|
escapeMenu *EscapeMenu
|
2020-07-03 14:00:56 -04:00
|
|
|
|
|
|
|
renderer d2interface.Renderer
|
|
|
|
audioProvider d2interface.AudioProvider
|
|
|
|
terminal d2interface.Terminal
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
// CreateGame creates the Gameplay screen and returns a pointer to it
|
2020-07-03 14:00:56 -04:00
|
|
|
func CreateGame(renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, gameClient *d2client.GameClient,
|
|
|
|
term d2interface.Terminal) *Game {
|
2020-06-21 18:40:37 -04:00
|
|
|
result := &Game{
|
2020-06-19 02:19:27 -04:00
|
|
|
gameClient: gameClient,
|
|
|
|
gameControls: nil,
|
|
|
|
localPlayer: nil,
|
2020-06-22 15:55:32 -04:00
|
|
|
lastRegionType: d2enum.RegionNone,
|
2020-06-19 02:19:27 -04:00
|
|
|
ticksSinceLevelCheck: 0,
|
2020-07-03 14:00:56 -04:00
|
|
|
mapRenderer: d2maprenderer.CreateMapRenderer(renderer, gameClient.MapEngine, term),
|
|
|
|
escapeMenu: NewEscapeMenu(renderer, audioProvider, term),
|
2020-06-28 19:31:10 -04:00
|
|
|
audioProvider: audioProvider,
|
2020-07-03 14:00:56 -04:00
|
|
|
renderer: renderer,
|
2020-06-28 21:40:52 -04:00
|
|
|
terminal: term,
|
2020-06-13 18:32:09 -04:00
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
result.escapeMenu.onLoad()
|
2020-07-06 20:13:55 -04:00
|
|
|
|
|
|
|
if err := d2input.BindHandler(result.escapeMenu); err != nil {
|
|
|
|
fmt.Println("failed to add gameplay screen as event handler")
|
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-06-21 18:40:37 -04:00
|
|
|
return result
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
// OnLoad loads the resources for the Gameplay screen
|
2020-07-03 14:00:56 -04:00
|
|
|
func (v *Game) OnLoad(_ d2screen.LoadingState) {
|
2020-06-28 19:31:10 -04:00
|
|
|
v.audioProvider.PlayBGM("")
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
// OnUnload releases the resources of Gameplay screen
|
2020-02-08 21:02:37 -05:00
|
|
|
func (v *Game) OnUnload() error {
|
2020-07-06 20:13:55 -04:00
|
|
|
if err := d2input.UnbindHandler(v.gameControls); err != nil { // TODO: hack
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := d2input.UnbindHandler(v.escapeMenu); err != nil { // TODO: hack
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := v.gameClient.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-02-08 21:02:37 -05:00
|
|
|
return nil
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
// Render renders the Gameplay screen
|
2020-06-29 00:41:58 -04:00
|
|
|
func (v *Game) Render(screen d2interface.Surface) error {
|
2020-06-21 18:40:37 -04:00
|
|
|
if v.gameClient.RegenMap {
|
|
|
|
v.gameClient.RegenMap = false
|
2020-06-26 17:12:19 -04:00
|
|
|
v.mapRenderer.RegenerateTileCache()
|
2020-06-21 18:40:37 -04:00
|
|
|
}
|
2020-06-25 00:39:09 -04:00
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
if err := screen.Clear(color.Black); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-26 17:12:19 -04:00
|
|
|
v.mapRenderer.Render(screen)
|
2020-06-25 00:39:09 -04:00
|
|
|
|
2020-06-13 18:32:09 -04:00
|
|
|
if v.gameControls != nil {
|
|
|
|
v.gameControls.Render(screen)
|
|
|
|
}
|
2020-06-25 00:39:09 -04:00
|
|
|
|
2020-02-08 21:02:37 -05:00
|
|
|
return nil
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
// Advance runs the update logic on the Gameplay screen
|
2020-02-08 21:02:37 -05:00
|
|
|
func (v *Game) Advance(tickTime float64) error {
|
2020-07-02 13:55:43 -04:00
|
|
|
if (v.escapeMenu != nil && !v.escapeMenu.isOpen) || len(v.gameClient.Players) != 1 {
|
2020-06-21 16:06:52 -04:00
|
|
|
v.gameClient.MapEngine.Advance(tickTime) // TODO: Hack
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.gameControls != nil {
|
2020-07-06 20:13:55 -04:00
|
|
|
if err := v.gameControls.Advance(tickTime); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-06-21 16:06:52 -04:00
|
|
|
}
|
2020-06-13 18:32:09 -04:00
|
|
|
|
2020-06-19 02:19:27 -04:00
|
|
|
v.ticksSinceLevelCheck += tickTime
|
2020-06-20 00:40:49 -04:00
|
|
|
if v.ticksSinceLevelCheck > 1.0 {
|
2020-06-19 02:19:27 -04:00
|
|
|
v.ticksSinceLevelCheck = 0
|
|
|
|
if v.localPlayer != nil {
|
2020-06-21 18:40:37 -04:00
|
|
|
tile := v.gameClient.MapEngine.TileAt(v.localPlayer.TileX, v.localPlayer.TileY)
|
|
|
|
if tile != nil {
|
2020-06-27 03:16:53 -04:00
|
|
|
musicInfo := d2common.GetMusicDef(tile.RegionType)
|
2020-06-28 19:31:10 -04:00
|
|
|
v.audioProvider.PlayBGM(musicInfo.MusicFile)
|
2020-06-22 15:55:32 -04:00
|
|
|
|
|
|
|
// skip showing zone change text the first time we enter the world
|
2020-06-26 17:12:19 -04:00
|
|
|
if v.lastRegionType != d2enum.RegionNone && v.lastRegionType != tile.RegionType {
|
|
|
|
//TODO: Should not be using RegionType as an index - this will return incorrect LevelDetails record for most of the zones.
|
|
|
|
v.gameControls.SetZoneChangeText(fmt.Sprintf("Entering The %s", d2datadict.LevelDetails[int(tile.RegionType)].LevelDisplayName))
|
2020-06-22 15:55:32 -04:00
|
|
|
v.gameControls.ShowZoneChangeText()
|
2020-06-26 17:12:19 -04:00
|
|
|
v.gameControls.HideZoneChangeTextAfter(hideZoneTextAfterSeconds)
|
2020-06-22 15:55:32 -04:00
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-06-22 15:55:32 -04:00
|
|
|
v.lastRegionType = tile.RegionType
|
2020-06-19 02:19:27 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 18:32:09 -04:00
|
|
|
// Bind the game controls to the player once it exists
|
|
|
|
if v.gameControls == nil {
|
2020-07-06 20:13:55 -04:00
|
|
|
v.bindGameControls()
|
2020-06-13 18:32:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update the camera to focus on the player
|
2020-06-21 20:46:00 -04:00
|
|
|
if v.localPlayer != nil && !v.gameControls.FreeCam {
|
2020-06-26 17:12:19 -04:00
|
|
|
rx, ry := v.mapRenderer.WorldToOrtho(v.localPlayer.LocationX/5, v.localPlayer.LocationY/5)
|
|
|
|
v.mapRenderer.MoveCameraTo(rx, ry)
|
2020-06-13 18:32:09 -04:00
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-02-08 21:02:37 -05:00
|
|
|
return nil
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
2020-06-18 14:11:04 -04:00
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
func (v *Game) bindGameControls() {
|
|
|
|
for _, player := range v.gameClient.Players {
|
|
|
|
if player.Id != v.gameClient.PlayerId {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
v.localPlayer = player
|
|
|
|
v.gameControls = d2player.NewGameControls(v.renderer, player, v.gameClient.MapEngine, v.mapRenderer, v, v.terminal)
|
|
|
|
v.gameControls.Load()
|
|
|
|
|
|
|
|
if err := d2input.BindHandler(v.gameControls); err != nil {
|
|
|
|
fmt.Printf("failed to add gameControls as input handler for player: %s\n", player.Id)
|
|
|
|
}
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// OnPlayerMove sends the player move action to the server
|
2020-06-26 17:12:19 -04:00
|
|
|
func (v *Game) OnPlayerMove(x, y float64) {
|
|
|
|
heroPosX := v.localPlayer.LocationX / 5.0
|
|
|
|
heroPosY := v.localPlayer.LocationY / 5.0
|
2020-07-06 20:13:55 -04:00
|
|
|
|
|
|
|
err := v.gameClient.SendPacketToServer(d2netpacket.CreateMovePlayerPacket(v.gameClient.PlayerId, heroPosX, heroPosY, x, y))
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("failed to send MovePlayer packet to the server, playerId: %s, x: %g, x: %g\n", v.gameClient.PlayerId, x, y)
|
|
|
|
}
|
2020-06-18 14:11:04 -04:00
|
|
|
}
|
2020-06-26 20:03:00 -04:00
|
|
|
|
2020-07-06 20:13:55 -04:00
|
|
|
// OnPlayerCast sends the casting skill action to the server
|
|
|
|
func (v *Game) OnPlayerCast(missileID int, targetX, targetY float64) {
|
|
|
|
err := v.gameClient.SendPacketToServer(d2netpacket.CreateCastPacket(v.gameClient.PlayerId, missileID, targetX, targetY))
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf(
|
|
|
|
"failed to send CastSkill packet to the server, playerId: %s, missileId: %d, x: %g, x: %g\n",
|
|
|
|
v.gameClient.PlayerId, missileID, targetX, targetY,
|
|
|
|
)
|
|
|
|
}
|
2020-06-26 20:03:00 -04:00
|
|
|
}
|