2019-11-10 03:36:53 -05:00
|
|
|
package d2core
|
2019-10-24 09:31:59 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
2019-10-25 20:28:14 -04:00
|
|
|
"math"
|
2019-11-09 01:13:49 -05:00
|
|
|
"runtime"
|
2019-11-09 18:56:45 -05:00
|
|
|
"strconv"
|
2019-10-24 09:31:59 -04:00
|
|
|
|
2019-11-17 00:16:33 -05:00
|
|
|
"github.com/OpenDiablo2/D2Shared/d2common/d2resource"
|
2019-11-10 08:51:02 -05:00
|
|
|
|
2019-11-17 00:16:33 -05:00
|
|
|
"github.com/OpenDiablo2/D2Shared/d2helper"
|
2019-11-06 18:25:19 -05:00
|
|
|
|
2019-11-17 00:16:33 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface"
|
2019-12-26 11:13:05 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2term"
|
2019-11-01 14:12:23 -04:00
|
|
|
|
2019-11-10 03:36:53 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2render"
|
|
|
|
|
2019-11-17 00:16:33 -05:00
|
|
|
"github.com/OpenDiablo2/D2Shared/d2data"
|
2019-11-10 03:36:53 -05:00
|
|
|
|
2019-11-17 00:16:33 -05:00
|
|
|
"github.com/OpenDiablo2/D2Shared/d2data/d2datadict"
|
2019-11-10 03:36:53 -05:00
|
|
|
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
|
|
|
|
|
2019-11-17 00:16:33 -05:00
|
|
|
"github.com/OpenDiablo2/D2Shared/d2common"
|
2019-12-21 20:53:18 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2asset"
|
2019-11-17 00:16:33 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon"
|
2019-12-26 11:13:05 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
|
2019-11-10 08:51:02 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui"
|
2019-10-24 09:31:59 -04:00
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten"
|
2019-11-09 18:56:45 -05:00
|
|
|
"github.com/hajimehoshi/ebiten/ebitenutil"
|
|
|
|
"github.com/hajimehoshi/ebiten/inpututil"
|
2019-10-24 09:31:59 -04:00
|
|
|
)
|
|
|
|
|
2019-10-25 18:40:27 -04:00
|
|
|
// Engine is the core OpenDiablo2 engine
|
2019-10-24 09:31:59 -04:00
|
|
|
type Engine struct {
|
2019-11-17 00:16:33 -05:00
|
|
|
Settings *d2corecommon.Configuration // Engine configuration settings from json file
|
2019-11-21 21:40:12 -05:00
|
|
|
CheckedPatch map[string]bool // First time we check a file, we'll check if it's in the patch. This notes that we've already checked that.
|
2019-12-21 20:53:18 -05:00
|
|
|
LoadingSprite *d2render.Sprite // The sprite shown when loading stuff
|
2019-11-21 21:40:12 -05:00
|
|
|
loadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays.
|
|
|
|
loadingIndex int // Determines which load function is currently being called
|
|
|
|
thingsToLoad []func() // The load functions for the next scene
|
|
|
|
stepLoadingSize float64 // The size for each loading step
|
2019-11-17 00:16:33 -05:00
|
|
|
CurrentScene d2coreinterface.Scene // The current scene being rendered
|
2019-11-21 21:40:12 -05:00
|
|
|
UIManager *d2ui.Manager // The UI manager
|
|
|
|
SoundManager *d2audio.Manager // The sound manager
|
2019-11-17 00:16:33 -05:00
|
|
|
nextScene d2coreinterface.Scene // The next scene to be loaded at the end of the game loop
|
2019-11-21 21:40:12 -05:00
|
|
|
fullscreenKey bool // When true, the fullscreen toggle is still being pressed
|
|
|
|
lastTime float64 // Last time we updated the scene
|
2019-11-09 18:56:45 -05:00
|
|
|
showFPS bool
|
2019-12-26 11:13:05 -05:00
|
|
|
timeScale float64
|
2019-10-24 09:31:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateEngine creates and instance of the OpenDiablo2 engine
|
2019-12-26 11:13:05 -05:00
|
|
|
func CreateEngine() *Engine {
|
|
|
|
result := &Engine{timeScale: 1.0}
|
2019-12-17 21:32:37 -05:00
|
|
|
|
|
|
|
result.Settings = d2corecommon.LoadConfiguration()
|
|
|
|
if err := result.Settings.Save(); err != nil {
|
|
|
|
log.Printf("could not load settings: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
d2asset.Initialize(result.Settings)
|
2019-11-10 08:51:02 -05:00
|
|
|
d2resource.LanguageCode = result.Settings.Language
|
2019-12-26 11:13:05 -05:00
|
|
|
d2datadict.LoadPalettes(nil, result)
|
|
|
|
d2common.LoadTextDictionary(result)
|
|
|
|
d2datadict.LoadLevelTypes(result)
|
|
|
|
d2datadict.LoadLevelPresets(result)
|
|
|
|
d2datadict.LoadLevelWarps(result)
|
|
|
|
d2datadict.LoadObjectTypes(result)
|
|
|
|
d2datadict.LoadObjects(result)
|
|
|
|
d2datadict.LoadWeapons(result)
|
|
|
|
d2datadict.LoadArmors(result)
|
|
|
|
d2datadict.LoadMiscItems(result)
|
|
|
|
d2datadict.LoadUniqueItems(result)
|
|
|
|
d2datadict.LoadMissiles(result)
|
|
|
|
d2datadict.LoadSounds(result)
|
|
|
|
d2data.LoadAnimationData(result)
|
|
|
|
d2datadict.LoadMonStats(result)
|
2019-11-14 22:20:01 -05:00
|
|
|
LoadHeroObjects()
|
2019-12-21 20:53:18 -05:00
|
|
|
result.SoundManager = d2audio.CreateManager()
|
2019-10-26 20:09:33 -04:00
|
|
|
result.SoundManager.SetVolumes(result.Settings.BgmVolume, result.Settings.SfxVolume)
|
2019-12-21 20:53:18 -05:00
|
|
|
result.UIManager = d2ui.CreateManager(*result.SoundManager)
|
|
|
|
result.LoadingSprite, _ = d2render.LoadSprite(d2resource.LoadingScreen, d2resource.PaletteLoading)
|
|
|
|
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetCurrentFrameSize()
|
|
|
|
result.LoadingSprite.SetPosition(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2)))
|
2019-12-26 11:13:05 -05:00
|
|
|
|
|
|
|
d2term.BindAction("timescale", "set scalar for elapsed time", func(scale float64) {
|
|
|
|
if scale <= 0 {
|
|
|
|
d2term.OutputError("invalid time scale value")
|
|
|
|
} else {
|
|
|
|
d2term.OutputInfo("timescale changed from %f to %f", result.timeScale, scale)
|
|
|
|
result.timeScale = scale
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2019-10-24 09:31:59 -04:00
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2019-11-06 18:25:19 -05:00
|
|
|
func (v *Engine) LoadFile(fileName string) []byte {
|
2019-12-21 20:53:18 -05:00
|
|
|
data, _ := d2asset.LoadFile(fileName)
|
|
|
|
return data
|
2019-11-06 18:25:19 -05:00
|
|
|
}
|
2019-10-24 09:31:59 -04:00
|
|
|
|
|
|
|
// IsLoading returns true if the engine is currently in a loading state
|
2019-11-10 10:44:13 -05:00
|
|
|
func (v Engine) IsLoading() bool {
|
2019-10-25 20:28:14 -04:00
|
|
|
return v.loadingProgress < 1.0
|
2019-10-24 09:31:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// updateScene handles the scene maintenance for the engine
|
|
|
|
func (v *Engine) updateScene() {
|
|
|
|
if v.nextScene == nil {
|
2019-11-08 11:05:51 -05:00
|
|
|
if v.thingsToLoad != nil {
|
|
|
|
if v.loadingIndex < len(v.thingsToLoad) {
|
|
|
|
v.thingsToLoad[v.loadingIndex]()
|
|
|
|
v.loadingIndex++
|
|
|
|
if v.loadingIndex < len(v.thingsToLoad) {
|
|
|
|
v.StepLoading()
|
|
|
|
} else {
|
|
|
|
v.FinishLoading()
|
|
|
|
v.thingsToLoad = nil
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2019-10-24 09:31:59 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if v.CurrentScene != nil {
|
|
|
|
v.CurrentScene.Unload()
|
2019-11-09 01:13:49 -05:00
|
|
|
runtime.GC()
|
2019-10-24 09:31:59 -04:00
|
|
|
}
|
|
|
|
v.CurrentScene = v.nextScene
|
|
|
|
v.nextScene = nil
|
2019-10-25 19:12:42 -04:00
|
|
|
v.UIManager.Reset()
|
2019-11-08 11:05:51 -05:00
|
|
|
v.thingsToLoad = v.CurrentScene.Load()
|
|
|
|
v.loadingIndex = 0
|
|
|
|
v.SetLoadingStepSize(1.0 / float64(len(v.thingsToLoad)))
|
2019-10-25 20:28:14 -04:00
|
|
|
v.ResetLoading()
|
2019-10-25 15:06:47 -04:00
|
|
|
}
|
|
|
|
|
2019-10-24 09:31:59 -04:00
|
|
|
// Update updates the internal state of the engine
|
|
|
|
func (v *Engine) Update() {
|
2019-10-25 17:15:44 -04:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyAlt) && ebiten.IsKeyPressed(ebiten.KeyEnter) {
|
|
|
|
if !v.fullscreenKey {
|
|
|
|
ebiten.SetFullscreen(!ebiten.IsFullscreen())
|
|
|
|
}
|
|
|
|
v.fullscreenKey = true
|
|
|
|
} else {
|
|
|
|
v.fullscreenKey = false
|
|
|
|
}
|
|
|
|
|
2019-11-09 18:56:45 -05:00
|
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyF6) {
|
|
|
|
v.showFPS = !v.showFPS
|
|
|
|
}
|
|
|
|
|
|
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyF8) {
|
|
|
|
ebiten.SetVsyncEnabled(!ebiten.IsVsyncEnabled())
|
|
|
|
}
|
|
|
|
|
2019-10-24 09:31:59 -04:00
|
|
|
v.updateScene()
|
|
|
|
if v.CurrentScene == nil {
|
2019-10-25 15:06:47 -04:00
|
|
|
log.Fatal("no scene loaded")
|
|
|
|
}
|
2019-10-25 19:12:42 -04:00
|
|
|
|
|
|
|
if v.IsLoading() {
|
|
|
|
return
|
|
|
|
}
|
2019-10-25 20:28:14 -04:00
|
|
|
|
2019-12-26 11:13:05 -05:00
|
|
|
currentTime := d2helper.Now()
|
|
|
|
deltaTime := (currentTime - v.lastTime) * v.timeScale
|
2019-11-09 16:17:01 -05:00
|
|
|
v.lastTime = currentTime
|
|
|
|
|
|
|
|
v.CurrentScene.Update(deltaTime)
|
2019-10-25 19:12:42 -04:00
|
|
|
v.UIManager.Update()
|
2019-12-26 11:13:05 -05:00
|
|
|
d2term.Advance(deltaTime)
|
2019-10-24 09:31:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Draw draws the game
|
2019-11-10 10:44:13 -05:00
|
|
|
func (v Engine) Draw(screen *ebiten.Image) {
|
2019-10-25 20:28:14 -04:00
|
|
|
if v.loadingProgress < 1.0 {
|
2019-12-21 20:53:18 -05:00
|
|
|
v.LoadingSprite.SetCurrentFrame(int(d2helper.Max(0, d2helper.Min(uint32(v.LoadingSprite.GetFrameCount()-1), uint32(float64(v.LoadingSprite.GetFrameCount()-1)*v.loadingProgress)))))
|
|
|
|
v.LoadingSprite.Render(screen)
|
2019-10-24 09:31:59 -04:00
|
|
|
} else {
|
|
|
|
if v.CurrentScene == nil {
|
2019-10-25 15:06:47 -04:00
|
|
|
log.Fatal("no scene loaded")
|
2019-10-24 09:31:59 -04:00
|
|
|
}
|
|
|
|
v.CurrentScene.Render(screen)
|
2019-12-21 20:53:18 -05:00
|
|
|
v.UIManager.Render(screen)
|
2019-10-24 09:31:59 -04:00
|
|
|
}
|
2019-11-11 23:48:55 -05:00
|
|
|
if v.showFPS {
|
2019-11-09 18:56:45 -05:00
|
|
|
ebitenutil.DebugPrintAt(screen, "vsync:"+strconv.FormatBool(ebiten.IsVsyncEnabled())+"\nFPS:"+strconv.Itoa(int(ebiten.CurrentFPS())), 5, 565)
|
2019-11-10 20:13:10 -05:00
|
|
|
var m runtime.MemStats
|
|
|
|
runtime.ReadMemStats(&m)
|
2019-12-16 11:04:39 -05:00
|
|
|
ebitenutil.DebugPrintAt(screen, "Alloc "+strconv.FormatInt(int64(m.Alloc)/1024/1024, 10), 680, 0)
|
|
|
|
ebitenutil.DebugPrintAt(screen, "Pause "+strconv.FormatInt(int64(m.PauseTotalNs/1024/1024), 10), 680, 10)
|
|
|
|
ebitenutil.DebugPrintAt(screen, "HeapSys "+strconv.FormatInt(int64(m.HeapSys/1024/1024), 10), 680, 20)
|
|
|
|
ebitenutil.DebugPrintAt(screen, "NumGC "+strconv.FormatInt(int64(m.NumGC), 10), 680, 30)
|
|
|
|
cx, cy := ebiten.CursorPosition()
|
2019-12-17 21:32:37 -05:00
|
|
|
ebitenutil.DebugPrintAt(screen, "Coords "+strconv.FormatInt(int64(cx), 10)+","+strconv.FormatInt(int64(cy), 10), 680, 40)
|
2019-11-09 18:56:45 -05:00
|
|
|
}
|
2019-11-10 20:13:10 -05:00
|
|
|
|
2019-12-26 11:13:05 -05:00
|
|
|
d2term.Render(d2surface.CreateSurface(screen))
|
2019-10-24 09:31:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetNextScene tells the engine what scene to load on the next update cycle
|
2019-11-17 00:16:33 -05:00
|
|
|
func (v *Engine) SetNextScene(nextScene d2coreinterface.Scene) {
|
2019-10-24 09:31:59 -04:00
|
|
|
v.nextScene = nextScene
|
|
|
|
}
|
|
|
|
|
2019-10-25 20:28:14 -04:00
|
|
|
// SetLoadingStepSize sets the size of the loading step
|
|
|
|
func (v *Engine) SetLoadingStepSize(size float64) {
|
|
|
|
v.stepLoadingSize = size
|
|
|
|
}
|
2019-10-25 15:06:47 -04:00
|
|
|
|
2019-10-25 20:28:14 -04:00
|
|
|
// ResetLoading resets the loading progress
|
|
|
|
func (v *Engine) ResetLoading() {
|
|
|
|
v.loadingProgress = 0.0
|
|
|
|
}
|
|
|
|
|
|
|
|
// StepLoading increments the loading progress
|
|
|
|
func (v *Engine) StepLoading() {
|
|
|
|
v.loadingProgress = math.Min(1.0, v.loadingProgress+v.stepLoadingSize)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FinishLoading terminates the loading phase
|
|
|
|
func (v *Engine) FinishLoading() {
|
|
|
|
v.loadingProgress = 1.0
|
2019-10-25 15:06:47 -04:00
|
|
|
}
|