mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-11-18 02:16:23 -05:00
Cleaned up d2term
This commit is contained in:
parent
8a55e1bd4b
commit
04ec879035
132
d2app/app.go
132
d2app/app.go
@ -91,12 +91,6 @@ type Options struct {
|
|||||||
LogLevel *d2util.LogLevel
|
LogLevel *d2util.LogLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
type bindTerminalEntry struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
action interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bytesToMegabyte = 1024 * 1024
|
bytesToMegabyte = 1024 * 1024
|
||||||
nSamplesTAlloc = 100
|
nSamplesTAlloc = 100
|
||||||
@ -184,11 +178,6 @@ func (a *App) loadEngine() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.asset.BindTerminalCommands(term)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptEngine := d2script.CreateScriptEngine()
|
scriptEngine := d2script.CreateScriptEngine()
|
||||||
|
|
||||||
uiManager := d2ui.NewUIManager(a.asset, renderer, inputManager, *a.Options.LogLevel, audio)
|
uiManager := d2ui.NewUIManager(a.asset, renderer, inputManager, *a.Options.LogLevel, audio)
|
||||||
@ -351,25 +340,28 @@ func (a *App) initialize() error {
|
|||||||
a.renderer.SetWindowIcon("d2logo.png")
|
a.renderer.SetWindowIcon("d2logo.png")
|
||||||
a.terminal.BindLogger()
|
a.terminal.BindLogger()
|
||||||
|
|
||||||
terminalActions := [...]bindTerminalEntry{
|
terminalCommands := []struct {
|
||||||
{"dumpheap", "dumps the heap to pprof/heap.pprof", a.dumpHeap},
|
name string
|
||||||
{"fullscreen", "toggles fullscreen", a.toggleFullScreen},
|
desc string
|
||||||
{"capframe", "captures a still frame", a.setupCaptureFrame},
|
args []string
|
||||||
{"capgifstart", "captures an animation (start)", a.startAnimationCapture},
|
fn func(args []string) error
|
||||||
{"capgifstop", "captures an animation (stop)", a.stopAnimationCapture},
|
}{
|
||||||
{"vsync", "toggles vsync", a.toggleVsync},
|
{"dumpheap", "dumps the heap to pprof/heap.pprof", nil, a.dumpHeap},
|
||||||
{"fps", "toggle fps counter", a.toggleFpsCounter},
|
{"fullscreen", "toggles fullscreen", nil, a.toggleFullScreen},
|
||||||
{"timescale", "set scalar for elapsed time", a.setTimeScale},
|
{"capframe", "captures a still frame", []string{"filename"}, a.setupCaptureFrame},
|
||||||
{"quit", "exits the game", a.quitGame},
|
{"capgifstart", "captures an animation (start)", []string{"filename"}, a.startAnimationCapture},
|
||||||
{"screen-gui", "enters the gui playground screen", a.enterGuiPlayground},
|
{"capgifstop", "captures an animation (stop)", nil, a.stopAnimationCapture},
|
||||||
{"js", "eval JS scripts", a.evalJS},
|
{"vsync", "toggles vsync", nil, a.toggleVsync},
|
||||||
|
{"fps", "toggle fps counter", nil, a.toggleFpsCounter},
|
||||||
|
{"timescale", "set scalar for elapsed time", []string{"float"}, a.setTimeScale},
|
||||||
|
{"quit", "exits the game", nil, a.quitGame},
|
||||||
|
{"screen-gui", "enters the gui playground screen", nil, a.enterGuiPlayground},
|
||||||
|
{"js", "eval JS scripts", []string{"code"}, a.evalJS},
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx := range terminalActions {
|
for _, cmd := range terminalCommands {
|
||||||
action := &terminalActions[idx]
|
if err := a.terminal.Bind(cmd.name, cmd.desc, cmd.args, cmd.fn); err != nil {
|
||||||
|
a.Fatalf("failed to bind action %q: %v", cmd.name, err.Error())
|
||||||
if err := a.terminal.BindAction(action.name, action.description, action.action); err != nil {
|
|
||||||
a.Fatal(err.Error())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,7 +636,7 @@ func (a *App) allocRate(totalAlloc uint64, fps float64) float64 {
|
|||||||
return deltaAllocPerFrame * fps / bytesToMegabyte
|
return deltaAllocPerFrame * fps / bytesToMegabyte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) dumpHeap() {
|
func (a *App) dumpHeap([]string) error {
|
||||||
if _, err := os.Stat("./pprof/"); os.IsNotExist(err) {
|
if _, err := os.Stat("./pprof/"); os.IsNotExist(err) {
|
||||||
if err := os.Mkdir("./pprof/", 0750); err != nil {
|
if err := os.Mkdir("./pprof/", 0750); err != nil {
|
||||||
a.Fatal(err.Error())
|
a.Fatal(err.Error())
|
||||||
@ -663,48 +655,56 @@ func (a *App) dumpHeap() {
|
|||||||
if err := fileOut.Close(); err != nil {
|
if err := fileOut.Close(); err != nil {
|
||||||
a.Fatal(err.Error())
|
a.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) evalJS(code string) {
|
func (a *App) evalJS(args []string) error {
|
||||||
val, err := a.scriptEngine.Eval(code)
|
val, err := a.scriptEngine.Eval(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.terminal.OutputErrorf("%s", err)
|
a.terminal.Errorf(err.Error())
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Info("%s" + val)
|
a.Info("%s" + val)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) toggleFullScreen() {
|
func (a *App) toggleFullScreen([]string) error {
|
||||||
fullscreen := !a.renderer.IsFullScreen()
|
fullscreen := !a.renderer.IsFullScreen()
|
||||||
a.renderer.SetFullScreen(fullscreen)
|
a.renderer.SetFullScreen(fullscreen)
|
||||||
a.terminal.OutputInfof("fullscreen is now: %v", fullscreen)
|
a.terminal.Infof("fullscreen is now: %v", fullscreen)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) setupCaptureFrame(path string) {
|
func (a *App) setupCaptureFrame(args []string) error {
|
||||||
a.captureState = captureStateFrame
|
a.captureState = captureStateFrame
|
||||||
a.capturePath = path
|
a.capturePath = args[0]
|
||||||
a.captureFrames = nil
|
a.captureFrames = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) doCaptureFrame(target d2interface.Surface) error {
|
func (a *App) doCaptureFrame(target d2interface.Surface) error {
|
||||||
fp, err := os.Create(a.capturePath)
|
fp, err := os.Create(a.capturePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
a.terminal.Errorf("failed to create %q", a.capturePath)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := fp.Close(); err != nil {
|
|
||||||
a.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
screenshot := target.Screenshot()
|
screenshot := target.Screenshot()
|
||||||
if err := png.Encode(fp, screenshot); err != nil {
|
if err := png.Encode(fp, screenshot); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Infof("saved frame to %s", a.capturePath)
|
if err := fp.Close(); err != nil {
|
||||||
|
a.terminal.Errorf("failed to create %q", a.capturePath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
a.terminal.Infof("saved frame to %s", a.capturePath)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -769,42 +769,56 @@ func (a *App) convertFramesToGif() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) startAnimationCapture(path string) {
|
func (a *App) startAnimationCapture(args []string) error {
|
||||||
a.captureState = captureStateGif
|
a.captureState = captureStateGif
|
||||||
a.capturePath = path
|
a.capturePath = args[0]
|
||||||
a.captureFrames = nil
|
a.captureFrames = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) stopAnimationCapture() {
|
func (a *App) stopAnimationCapture([]string) error {
|
||||||
a.captureState = captureStateNone
|
a.captureState = captureStateNone
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) toggleVsync() {
|
func (a *App) toggleVsync([]string) error {
|
||||||
vsync := !a.renderer.GetVSyncEnabled()
|
vsync := !a.renderer.GetVSyncEnabled()
|
||||||
a.renderer.SetVSyncEnabled(vsync)
|
a.renderer.SetVSyncEnabled(vsync)
|
||||||
a.terminal.OutputInfof("vsync is now: %v", vsync)
|
a.terminal.Infof("vsync is now: %v", vsync)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) toggleFpsCounter() {
|
func (a *App) toggleFpsCounter([]string) error {
|
||||||
a.showFPS = !a.showFPS
|
a.showFPS = !a.showFPS
|
||||||
a.terminal.OutputInfof("fps counter is now: %v", a.showFPS)
|
a.terminal.Infof("fps counter is now: %v", a.showFPS)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) setTimeScale(timeScale float64) {
|
func (a *App) setTimeScale(args []string) error {
|
||||||
if timeScale <= 0 {
|
timeScale, err := strconv.ParseFloat(args[0], 64)
|
||||||
a.terminal.OutputErrorf("invalid time scale value")
|
if err != nil || timeScale <= 0 {
|
||||||
} else {
|
a.terminal.Errorf("invalid time scale value")
|
||||||
a.terminal.OutputInfof("timescale changed from %f to %f", a.timeScale, timeScale)
|
return nil
|
||||||
a.timeScale = timeScale
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.terminal.Infof("timescale changed from %f to %f", a.timeScale, timeScale)
|
||||||
|
a.timeScale = timeScale
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) quitGame() {
|
func (a *App) quitGame([]string) error {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) enterGuiPlayground() {
|
func (a *App) enterGuiPlayground([]string) error {
|
||||||
a.screen.SetNextScreen(d2gamescreen.CreateGuiTestMain(a.renderer, a.guiManager, *a.Options.LogLevel, a.asset))
|
a.screen.SetNextScreen(d2gamescreen.CreateGuiTestMain(a.renderer, a.guiManager, *a.Options.LogLevel, a.asset))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createZeroedRing(n int) *ring.Ring {
|
func createZeroedRing(n int) *ring.Ring {
|
||||||
|
@ -13,17 +13,17 @@ type Terminal interface {
|
|||||||
OnKeyChars(event KeyCharsEvent) bool
|
OnKeyChars(event KeyCharsEvent) bool
|
||||||
Render(surface Surface) error
|
Render(surface Surface) error
|
||||||
Execute(command string) error
|
Execute(command string) error
|
||||||
OutputRaw(text string, category d2enum.TermCategory)
|
Rawf(category d2enum.TermCategory, format string, params ...interface{})
|
||||||
Outputf(format string, params ...interface{})
|
Printf(format string, params ...interface{})
|
||||||
OutputInfof(format string, params ...interface{})
|
Infof(format string, params ...interface{})
|
||||||
OutputWarningf(format string, params ...interface{})
|
Warningf(format string, params ...interface{})
|
||||||
OutputErrorf(format string, params ...interface{})
|
Errorf(format string, params ...interface{})
|
||||||
OutputClear()
|
Clear()
|
||||||
IsVisible() bool
|
Visible() bool
|
||||||
Hide()
|
Hide()
|
||||||
Show()
|
Show()
|
||||||
BindAction(name, description string, action interface{}) error
|
Bind(name, description string, arguments []string, fn func(args []string) error) error
|
||||||
UnbindAction(name string) error
|
Unbind(name ...string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// TerminalLogger is used tomake the Terminal write out
|
// TerminalLogger is used tomake the Terminal write out
|
||||||
|
@ -3,6 +3,7 @@ package d2asset
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
||||||
@ -409,43 +410,70 @@ func (am *AssetManager) loadDCC(path string,
|
|||||||
|
|
||||||
// BindTerminalCommands binds the in-game terminal comands for the asset manager.
|
// BindTerminalCommands binds the in-game terminal comands for the asset manager.
|
||||||
func (am *AssetManager) BindTerminalCommands(term d2interface.Terminal) error {
|
func (am *AssetManager) BindTerminalCommands(term d2interface.Terminal) error {
|
||||||
if err := term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) {
|
if err := term.Bind("assetspam", "display verbose asset manager logs", nil, am.commandAssetSpam(term)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := term.Bind("assetstat", "display asset manager cache statistics", nil, am.commandAssetStat(term)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := term.Bind("assetclear", "clear asset manager cache", nil, am.commandAssetClear); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnbindTerminalCommands unbinds commands from the terminal
|
||||||
|
func (am *AssetManager) UnbindTerminalCommands(term d2interface.Terminal) error {
|
||||||
|
return term.Unbind("assetspam", "assetstat", "assetclear")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *AssetManager) commandAssetSpam(term d2interface.Terminal) func([]string) error {
|
||||||
|
return func(args []string) error {
|
||||||
|
verbose, err := strconv.ParseBool(args[0])
|
||||||
|
if err != nil {
|
||||||
|
term.Errorf("asset manager verbose invalid argument")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
term.OutputInfof("asset manager verbose logging enabled")
|
term.Infof("asset manager verbose logging enabled")
|
||||||
} else {
|
} else {
|
||||||
term.OutputInfof("asset manager verbose logging disabled")
|
term.Infof("asset manager verbose logging disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
am.palettes.SetVerbose(verbose)
|
am.palettes.SetVerbose(verbose)
|
||||||
am.fonts.SetVerbose(verbose)
|
am.fonts.SetVerbose(verbose)
|
||||||
am.transforms.SetVerbose(verbose)
|
am.transforms.SetVerbose(verbose)
|
||||||
am.animations.SetVerbose(verbose)
|
am.animations.SetVerbose(verbose)
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := term.BindAction("assetstat", "display asset manager cache statistics", func() {
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *AssetManager) commandAssetStat(term d2interface.Terminal) func([]string) error {
|
||||||
|
return func([]string) error {
|
||||||
var cacheStatistics = func(c d2interface.Cache) float64 {
|
var cacheStatistics = func(c d2interface.Cache) float64 {
|
||||||
const percent = 100.0
|
const percent = 100.0
|
||||||
return float64(c.GetWeight()) / float64(c.GetBudget()) * percent
|
return float64(c.GetWeight()) / float64(c.GetBudget()) * percent
|
||||||
}
|
}
|
||||||
|
|
||||||
term.OutputInfof("palette cache: %f", cacheStatistics(am.palettes))
|
term.Infof("palette cache: %f", cacheStatistics(am.palettes))
|
||||||
term.OutputInfof("palette transform cache: %f", cacheStatistics(am.transforms))
|
term.Infof("palette transform cache: %f", cacheStatistics(am.transforms))
|
||||||
term.OutputInfof("Animation cache: %f", cacheStatistics(am.animations))
|
term.Infof("Animation cache: %f", cacheStatistics(am.animations))
|
||||||
term.OutputInfof("font cache: %f", cacheStatistics(am.fonts))
|
term.Infof("font cache: %f", cacheStatistics(am.fonts))
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := term.BindAction("assetclear", "clear asset manager cache", func() {
|
return nil
|
||||||
am.palettes.Clear()
|
|
||||||
am.transforms.Clear()
|
|
||||||
am.animations.Clear()
|
|
||||||
am.fonts.Clear()
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *AssetManager) commandAssetClear([]string) error {
|
||||||
|
am.palettes.Clear()
|
||||||
|
am.transforms.Clear()
|
||||||
|
am.animations.Clear()
|
||||||
|
am.fonts.Clear()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package d2audio
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ func (s *Sound) SetPan(pan float64) {
|
|||||||
|
|
||||||
// Play the sound
|
// Play the sound
|
||||||
func (s *Sound) Play() {
|
func (s *Sound) Play() {
|
||||||
s.Info("starting sound" + s.entry.Handle)
|
s.Info("starting sound " + s.entry.Handle)
|
||||||
s.effect.Play()
|
s.effect.Play()
|
||||||
|
|
||||||
if s.entry.FadeIn != 0 {
|
if s.entry.FadeIn != 0 {
|
||||||
@ -103,6 +104,11 @@ func (s *Sound) Stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns the sound filename
|
||||||
|
func (s *Sound) String() string {
|
||||||
|
return s.entry.Handle
|
||||||
|
}
|
||||||
|
|
||||||
// SoundEngine provides functions for playing sounds
|
// SoundEngine provides functions for playing sounds
|
||||||
type SoundEngine struct {
|
type SoundEngine struct {
|
||||||
asset *d2asset.AssetManager
|
asset *d2asset.AssetManager
|
||||||
@ -128,43 +134,25 @@ func NewSoundEngine(provider d2interface.AudioProvider,
|
|||||||
r.Logger.SetPrefix(logPrefix)
|
r.Logger.SetPrefix(logPrefix)
|
||||||
r.Logger.SetLevel(l)
|
r.Logger.SetLevel(l)
|
||||||
|
|
||||||
err := term.BindAction("playsoundid", "plays the sound for a given id", func(id int) {
|
if err := term.Bind("playsoundid", "plays the sound for a given id", []string{"id"}, r.commandPlaySoundID); err != nil {
|
||||||
r.PlaySoundID(id)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
r.Error(err.Error())
|
r.Error(err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = term.BindAction("playsound", "plays the sound for a given handle string", func(handle string) {
|
if err := term.Bind("playsound", "plays the sound for a given handle string", []string{"name"}, r.commandPlaySound); err != nil {
|
||||||
r.PlaySoundHandle(handle)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
r.Error(err.Error())
|
r.Error(err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = term.BindAction("activesounds", "list currently active sounds", func() {
|
if err := term.Bind("activesounds", "list currently active sounds", nil, r.commandActiveSounds); err != nil {
|
||||||
for s := range r.sounds {
|
r.Error(err.Error())
|
||||||
if err != nil {
|
return nil
|
||||||
r.Error(err.Error())
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Info(fmt.Sprint(s))
|
if err := term.Bind("killsounds", "kill active sounds", nil, r.commandKillSounds); err != nil {
|
||||||
}
|
r.Error(err.Error())
|
||||||
})
|
return nil
|
||||||
|
}
|
||||||
err = term.BindAction("killsounds", "kill active sounds", func() {
|
|
||||||
for s := range r.sounds {
|
|
||||||
if err != nil {
|
|
||||||
r.Error(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Stop()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return &r
|
return &r
|
||||||
}
|
}
|
||||||
@ -194,6 +182,11 @@ func (s *SoundEngine) Advance(elapsed float64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnbindTerminalCommands unbinds commands from the terminal
|
||||||
|
func (s *SoundEngine) UnbindTerminalCommands(term d2interface.Terminal) error {
|
||||||
|
return term.Unbind("playsoundid", "playsound", "activesounds", "killsounds")
|
||||||
|
}
|
||||||
|
|
||||||
// Reset stop all sounds and reset state
|
// Reset stop all sounds and reset state
|
||||||
func (s *SoundEngine) Reset() {
|
func (s *SoundEngine) Reset() {
|
||||||
for snd := range s.sounds {
|
for snd := range s.sounds {
|
||||||
@ -242,3 +235,35 @@ func (s *SoundEngine) PlaySoundHandle(handle string) *Sound {
|
|||||||
sound := s.asset.Records.Sound.Details[handle].Index
|
sound := s.asset.Records.Sound.Details[handle].Index
|
||||||
return s.PlaySoundID(sound)
|
return s.PlaySoundID(sound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SoundEngine) commandPlaySoundID(args []string) error {
|
||||||
|
id, err := strconv.Atoi(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.PlaySoundID(id)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SoundEngine) commandPlaySound(args []string) error {
|
||||||
|
s.PlaySoundHandle(args[0])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SoundEngine) commandActiveSounds([]string) error {
|
||||||
|
for sound := range s.sounds {
|
||||||
|
s.Info(sound.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (s *SoundEngine) commandKillSounds([]string) error {
|
||||||
|
for sound := range s.sounds {
|
||||||
|
sound.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,8 +2,10 @@ package d2maprenderer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
"math"
|
"math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1"
|
||||||
@ -86,20 +88,11 @@ func CreateMapRenderer(asset *d2asset.AssetManager, renderer d2interface.Rendere
|
|||||||
result.Camera.position = &startPosition
|
result.Camera.position = &startPosition
|
||||||
result.viewport.SetCamera(&result.Camera)
|
result.viewport.SetCamera(&result.Camera)
|
||||||
|
|
||||||
var err error
|
if err := term.Bind("mapdebugvis", "set map debug visualization level", nil, result.commandMapDebugVis); err != nil {
|
||||||
err = term.BindAction("mapdebugvis", "set map debug visualization level", func(level int) {
|
|
||||||
result.mapDebugVisLevel = level
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
result.Errorf("could not bind the mapdebugvis action, err: %v", err)
|
result.Errorf("could not bind the mapdebugvis action, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = term.BindAction("entitydebugvis", "set entity debug visualization level", func(level int) {
|
if err := term.Bind("entitydebugvis", "set entity debug visualization level", nil, result.commandEntityDebugVis); err != nil {
|
||||||
result.entityDebugVisLevel = level
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
result.Errorf("could not bind the entitydebugvis action, err: %v", err)
|
result.Errorf("could not bind the entitydebugvis action, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +103,33 @@ func CreateMapRenderer(asset *d2asset.AssetManager, renderer d2interface.Rendere
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnbindTerminalCommands unbinds commands from the terminal
|
||||||
|
func (mr *MapRenderer) UnbindTerminalCommands(term d2interface.Terminal) error {
|
||||||
|
return term.Unbind("mapdebugvis", "entitydebugvis")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mr *MapRenderer) commandMapDebugVis(args []string) error {
|
||||||
|
level, err := strconv.Atoi(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid argument supplied")
|
||||||
|
}
|
||||||
|
|
||||||
|
mr.mapDebugVisLevel = level
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mr *MapRenderer) commandEntityDebugVis(args []string) error {
|
||||||
|
level, err := strconv.Atoi(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid argument supplied")
|
||||||
|
}
|
||||||
|
|
||||||
|
mr.entityDebugVisLevel = level
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RegenerateTileCache calls MapRenderer.generateTileCache().
|
// RegenerateTileCache calls MapRenderer.generateTileCache().
|
||||||
func (mr *MapRenderer) RegenerateTileCache() {
|
func (mr *MapRenderer) RegenerateTileCache() {
|
||||||
mr.generateTileCache()
|
mr.generateTileCache()
|
||||||
|
33
d2core/d2term/commmand.go
Normal file
33
d2core/d2term/commmand.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package d2term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *Terminal) commandList([]string) error {
|
||||||
|
names := make([]string, 0, len(t.commands))
|
||||||
|
for name := range t.commands {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(names)
|
||||||
|
t.Infof("available actions (%d):", len(names))
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
entry := t.commands[name]
|
||||||
|
if entry.arguments != nil {
|
||||||
|
t.Infof("%s: %s; %v", name, entry.description, entry.arguments)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Infof("%s: %s", name, entry.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) commandClear([]string) error {
|
||||||
|
t.Clear()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -6,8 +6,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// New creates and initializes the terminal
|
// New creates and initializes the terminal
|
||||||
func New(inputManager d2interface.InputManager) (d2interface.Terminal, error) {
|
func New(inputManager d2interface.InputManager) (*Terminal, error) {
|
||||||
term, err := createTerminal()
|
term, err := NewTerminal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,6 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
@ -18,13 +15,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
termCharWidth = 6
|
charWidth = 6
|
||||||
termCharHeight = 16
|
charHeight = 16
|
||||||
termCharDoubleWidth = termCharWidth * 2
|
charDoubleWidth = charWidth * 2
|
||||||
termRowCount = 24
|
rowCount = 24
|
||||||
termRowCountMax = 32
|
rowCountMax = 32
|
||||||
termColCountMax = 128
|
colCountMax = 128
|
||||||
termAnimLength = 0.5
|
animLength = 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -35,13 +32,13 @@ const (
|
|||||||
red = 0xcc0000b0
|
red = 0xcc0000b0
|
||||||
)
|
)
|
||||||
|
|
||||||
type termVis int
|
type visibility int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
termVisHidden termVis = iota
|
visHidden visibility = iota
|
||||||
termVisShowing
|
visShowing
|
||||||
termVisShown
|
visShown
|
||||||
termVisHiding
|
visHiding
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -49,18 +46,22 @@ const (
|
|||||||
minVisAnim = 0.0
|
minVisAnim = 0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
type termHistoryEntry struct {
|
type historyEntry struct {
|
||||||
text string
|
text string
|
||||||
category d2enum.TermCategory
|
category d2enum.TermCategory
|
||||||
}
|
}
|
||||||
|
|
||||||
type termActionEntry struct {
|
type commandEntry struct {
|
||||||
action interface{}
|
|
||||||
description string
|
description string
|
||||||
|
arguments []string
|
||||||
|
fn func([]string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type terminal struct {
|
}
|
||||||
outputHistory []termHistoryEntry
|
|
||||||
|
// Terminal handles the in-game terminal
|
||||||
|
type Terminal struct {
|
||||||
|
outputHistory []historyEntry
|
||||||
outputIndex int
|
outputIndex int
|
||||||
|
|
||||||
command string
|
command string
|
||||||
@ -68,7 +69,7 @@ type terminal struct {
|
|||||||
commandIndex int
|
commandIndex int
|
||||||
|
|
||||||
lineCount int
|
lineCount int
|
||||||
visState termVis
|
visState visibility
|
||||||
visAnim float64
|
visAnim float64
|
||||||
|
|
||||||
bgColor color.RGBA
|
bgColor color.RGBA
|
||||||
@ -77,36 +78,88 @@ type terminal struct {
|
|||||||
warningColor color.RGBA
|
warningColor color.RGBA
|
||||||
errorColor color.RGBA
|
errorColor color.RGBA
|
||||||
|
|
||||||
actions map[string]termActionEntry
|
commands map[string]commandEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) Advance(elapsed float64) error {
|
// NewTerminal creates and returns a terminal
|
||||||
|
func NewTerminal() (*Terminal, error) {
|
||||||
|
term := &Terminal{
|
||||||
|
lineCount: rowCount,
|
||||||
|
bgColor: d2util.Color(darkGrey),
|
||||||
|
fgColor: d2util.Color(lightGrey),
|
||||||
|
infoColor: d2util.Color(lightBlue),
|
||||||
|
warningColor: d2util.Color(yellow),
|
||||||
|
errorColor: d2util.Color(red),
|
||||||
|
commands: make(map[string]commandEntry),
|
||||||
|
}
|
||||||
|
|
||||||
|
term.Infof("::: OpenDiablo2 Terminal :::")
|
||||||
|
term.Infof("type \"ls\" for a list of commands")
|
||||||
|
|
||||||
|
if err := term.Bind("ls", "list available commands", nil, term.commandList); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := term.Bind("clear", "clear terminal", nil, term.commandClear); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return term, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind binds commands to the terminal
|
||||||
|
func (t *Terminal) Bind(name, description string, arguments []string, fn func(args []string) error) error {
|
||||||
|
if name == "" || description == "" {
|
||||||
|
return fmt.Errorf("missing name or description")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := t.commands[name]; ok {
|
||||||
|
t.Warningf("rebinding command with name: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.commands[name] = commandEntry{description, arguments, fn}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unbind unbinds commands from the terminal
|
||||||
|
func (t *Terminal) Unbind(names ...string) error {
|
||||||
|
for _, name := range names {
|
||||||
|
delete(t.commands, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance advances the terminal animation
|
||||||
|
func (t *Terminal) Advance(elapsed float64) error {
|
||||||
switch t.visState {
|
switch t.visState {
|
||||||
case termVisShowing:
|
case visShowing:
|
||||||
t.visAnim = math.Min(maxVisAnim, t.visAnim+elapsed/termAnimLength)
|
t.visAnim = math.Min(maxVisAnim, t.visAnim+elapsed/animLength)
|
||||||
if t.visAnim == maxVisAnim {
|
if t.visAnim == maxVisAnim {
|
||||||
t.visState = termVisShown
|
t.visState = visShown
|
||||||
}
|
}
|
||||||
case termVisHiding:
|
case visHiding:
|
||||||
t.visAnim = math.Max(minVisAnim, t.visAnim-elapsed/termAnimLength)
|
t.visAnim = math.Max(minVisAnim, t.visAnim-elapsed/animLength)
|
||||||
if t.visAnim == minVisAnim {
|
if t.visAnim == minVisAnim {
|
||||||
t.visState = termVisHidden
|
t.visState = visHidden
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !t.IsVisible() {
|
if !t.Visible() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) OnKeyDown(event d2interface.KeyEvent) bool {
|
// OnKeyDown handles key down in the terminal
|
||||||
|
func (t *Terminal) OnKeyDown(event d2interface.KeyEvent) bool {
|
||||||
if event.Key() == d2enum.KeyGraveAccent {
|
if event.Key() == d2enum.KeyGraveAccent {
|
||||||
t.toggleTerminal()
|
t.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !t.IsVisible() {
|
if !t.Visible() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +192,7 @@ func (t *terminal) OnKeyDown(event d2interface.KeyEvent) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) processCommand() {
|
func (t *Terminal) processCommand() {
|
||||||
if t.command == "" {
|
if t.command == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -156,17 +209,17 @@ func (t *terminal) processCommand() {
|
|||||||
t.commandHistory = t.commandHistory[:n]
|
t.commandHistory = t.commandHistory[:n]
|
||||||
t.commandHistory = append(t.commandHistory, t.command)
|
t.commandHistory = append(t.commandHistory, t.command)
|
||||||
|
|
||||||
t.Outputf(t.command)
|
t.Printf(t.command)
|
||||||
|
|
||||||
if err := t.Execute(t.command); err != nil {
|
if err := t.Execute(t.command); err != nil {
|
||||||
t.OutputErrorf(err.Error())
|
t.Errorf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
t.commandIndex = len(t.commandHistory) - 1
|
t.commandIndex = len(t.commandHistory) - 1
|
||||||
t.command = ""
|
t.command = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) handleControlKey(eventKey d2enum.Key, keyMod d2enum.KeyMod) {
|
func (t *Terminal) handleControlKey(eventKey d2enum.Key, keyMod d2enum.KeyMod) {
|
||||||
switch eventKey {
|
switch eventKey {
|
||||||
case d2enum.KeyUp:
|
case d2enum.KeyUp:
|
||||||
if keyMod == d2enum.KeyModControl {
|
if keyMod == d2enum.KeyModControl {
|
||||||
@ -181,21 +234,14 @@ func (t *terminal) handleControlKey(eventKey d2enum.Key, keyMod d2enum.KeyMod) {
|
|||||||
}
|
}
|
||||||
case d2enum.KeyDown:
|
case d2enum.KeyDown:
|
||||||
if keyMod == d2enum.KeyModControl {
|
if keyMod == d2enum.KeyModControl {
|
||||||
t.lineCount = d2math.MinInt(t.lineCount+1, termRowCountMax)
|
t.lineCount = d2math.MinInt(t.lineCount+1, rowCountMax)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) toggleTerminal() {
|
// OnKeyChars handles char key in terminal
|
||||||
if t.visState == termVisHiding || t.visState == termVisHidden {
|
func (t *Terminal) OnKeyChars(event d2interface.KeyCharsEvent) bool {
|
||||||
t.Show()
|
if !t.Visible() {
|
||||||
} else {
|
|
||||||
t.Hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *terminal) OnKeyChars(event d2interface.KeyCharsEvent) bool {
|
|
||||||
if !t.IsVisible() {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,14 +257,15 @@ func (t *terminal) OnKeyChars(event d2interface.KeyCharsEvent) bool {
|
|||||||
return handled
|
return handled
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) Render(surface d2interface.Surface) error {
|
// Render renders the terminal
|
||||||
if !t.IsVisible() {
|
func (t *Terminal) Render(surface d2interface.Surface) error {
|
||||||
|
if !t.Visible() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
totalWidth, _ := surface.GetSize()
|
totalWidth, _ := surface.GetSize()
|
||||||
outputHeight := t.lineCount * termCharHeight
|
outputHeight := t.lineCount * charHeight
|
||||||
totalHeight := outputHeight + termCharHeight
|
totalHeight := outputHeight + charHeight
|
||||||
|
|
||||||
offset := -int((1.0 - easeInOut(t.visAnim)) * float64(totalHeight))
|
offset := -int((1.0 - easeInOut(t.visAnim)) * float64(totalHeight))
|
||||||
surface.PushTranslation(0, offset)
|
surface.PushTranslation(0, offset)
|
||||||
@ -231,19 +278,19 @@ func (t *terminal) Render(surface d2interface.Surface) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
historyEntry := t.outputHistory[historyIndex]
|
entry := t.outputHistory[historyIndex]
|
||||||
|
|
||||||
surface.PushTranslation(termCharDoubleWidth, outputHeight-(i+1)*termCharHeight)
|
surface.PushTranslation(charDoubleWidth, outputHeight-(i+1)*charHeight)
|
||||||
surface.DrawTextf(historyEntry.text)
|
surface.DrawTextf(entry.text)
|
||||||
surface.PushTranslation(-termCharDoubleWidth, 0)
|
surface.PushTranslation(-charDoubleWidth, 0)
|
||||||
|
|
||||||
switch historyEntry.category {
|
switch entry.category {
|
||||||
case d2enum.TermCategoryInfo:
|
case d2enum.TermCategoryInfo:
|
||||||
surface.DrawRect(termCharWidth, termCharHeight, t.infoColor)
|
surface.DrawRect(charWidth, charHeight, t.infoColor)
|
||||||
case d2enum.TermCategoryWarning:
|
case d2enum.TermCategoryWarning:
|
||||||
surface.DrawRect(termCharWidth, termCharHeight, t.warningColor)
|
surface.DrawRect(charWidth, charHeight, t.warningColor)
|
||||||
case d2enum.TermCategoryError:
|
case d2enum.TermCategoryError:
|
||||||
surface.DrawRect(termCharWidth, termCharHeight, t.errorColor)
|
surface.DrawRect(charWidth, charHeight, t.errorColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
surface.Pop()
|
surface.Pop()
|
||||||
@ -251,7 +298,7 @@ func (t *terminal) Render(surface d2interface.Surface) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
surface.PushTranslation(0, outputHeight)
|
surface.PushTranslation(0, outputHeight)
|
||||||
surface.DrawRect(totalWidth, termCharHeight, t.fgColor)
|
surface.DrawRect(totalWidth, charHeight, t.fgColor)
|
||||||
surface.DrawTextf("> " + t.command)
|
surface.DrawTextf("> " + t.command)
|
||||||
surface.Pop()
|
surface.Pop()
|
||||||
|
|
||||||
@ -260,174 +307,105 @@ func (t *terminal) Render(surface d2interface.Surface) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) Execute(command string) error {
|
// Execute executes a command with arguments
|
||||||
|
func (t *Terminal) Execute(command string) error {
|
||||||
params := parseCommand(command)
|
params := parseCommand(command)
|
||||||
if len(params) == 0 {
|
if len(params) == 0 {
|
||||||
return errors.New("invalid command")
|
return errors.New("invalid command")
|
||||||
}
|
}
|
||||||
|
|
||||||
actionName := params[0]
|
name := params[0]
|
||||||
actionParams := params[1:]
|
args := params[1:]
|
||||||
|
|
||||||
actionEntry, ok := t.actions[actionName]
|
entry, ok := t.commands[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("action not found")
|
return errors.New("command not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
actionType := reflect.TypeOf(actionEntry.action)
|
if len(args) != len(entry.arguments) {
|
||||||
if actionType.Kind() != reflect.Func {
|
return errors.New("command requires different argument count")
|
||||||
return errors.New("action is not a function")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(actionParams) != actionType.NumIn() {
|
if err := entry.fn(args); err != nil {
|
||||||
return errors.New("action requires different argument count")
|
|
||||||
}
|
|
||||||
|
|
||||||
paramValues, err := parseActionParams(actionType, actionParams)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
actionValue := reflect.ValueOf(actionEntry.action)
|
|
||||||
actionReturnValues := actionValue.Call(paramValues)
|
|
||||||
|
|
||||||
if actionReturnValueCount := len(actionReturnValues); actionReturnValueCount > 0 {
|
|
||||||
t.OutputInfof("function returned %d values:", actionReturnValueCount)
|
|
||||||
|
|
||||||
for _, actionReturnValue := range actionReturnValues {
|
|
||||||
t.OutputInfof("%v: %s", actionReturnValue.Interface(), actionReturnValue.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseActionParams(actionType reflect.Type, actionParams []string) ([]reflect.Value, error) {
|
// Rawf writes a raw message to the terminal
|
||||||
var paramValues []reflect.Value
|
func (t *Terminal) Rawf(category d2enum.TermCategory, format string, params ...interface{}) {
|
||||||
|
text := fmt.Sprintf(format, params...)
|
||||||
for i := 0; i < actionType.NumIn(); i++ {
|
lines := d2util.SplitIntoLinesWithMaxWidth(text, colCountMax)
|
||||||
actionParam := actionParams[i]
|
|
||||||
|
|
||||||
switch actionType.In(i).Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
paramValues = append(paramValues, reflect.ValueOf(actionParam))
|
|
||||||
case reflect.Int:
|
|
||||||
value, err := strconv.ParseInt(actionParam, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
paramValues = append(paramValues, reflect.ValueOf(int(value)))
|
|
||||||
case reflect.Uint:
|
|
||||||
value, err := strconv.ParseUint(actionParam, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
paramValues = append(paramValues, reflect.ValueOf(uint(value)))
|
|
||||||
case reflect.Float64:
|
|
||||||
value, err := strconv.ParseFloat(actionParam, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
paramValues = append(paramValues, reflect.ValueOf(value))
|
|
||||||
case reflect.Bool:
|
|
||||||
value, err := strconv.ParseBool(actionParam)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
paramValues = append(paramValues, reflect.ValueOf(value))
|
|
||||||
default:
|
|
||||||
return nil, errors.New("action has unsupported arguments")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return paramValues, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *terminal) OutputRaw(text string, category d2enum.TermCategory) {
|
|
||||||
lines := d2util.SplitIntoLinesWithMaxWidth(text, termColCountMax)
|
|
||||||
|
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
// removes color token (this token ends with [0m )
|
// removes color token (this token ends with [0m )
|
||||||
l := strings.Split(line, "\033[0m")
|
l := strings.Split(line, "\033[0m")
|
||||||
line = l[len(l)-1]
|
line = l[len(l)-1]
|
||||||
|
|
||||||
t.outputHistory = append(t.outputHistory, termHistoryEntry{line, category})
|
t.outputHistory = append(t.outputHistory, historyEntry{line, category})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) Outputf(format string, params ...interface{}) {
|
// Printf writes a message to the terminal
|
||||||
t.OutputRaw(fmt.Sprintf(format, params...), d2enum.TermCategoryNone)
|
func (t *Terminal) Printf(format string, params ...interface{}) {
|
||||||
|
t.Rawf(d2enum.TermCategoryNone, format, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) OutputInfof(format string, params ...interface{}) {
|
// Infof writes a warning message to the terminal
|
||||||
t.OutputRaw(fmt.Sprintf(format, params...), d2enum.TermCategoryInfo)
|
func (t *Terminal) Infof(format string, params ...interface{}) {
|
||||||
|
t.Rawf(d2enum.TermCategoryInfo, format, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) OutputWarningf(format string, params ...interface{}) {
|
// Warningf writes a warning message to the terminal
|
||||||
t.OutputRaw(fmt.Sprintf(format, params...), d2enum.TermCategoryWarning)
|
func (t *Terminal) Warningf(format string, params ...interface{}) {
|
||||||
|
t.Rawf(d2enum.TermCategoryWarning, format, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) OutputErrorf(format string, params ...interface{}) {
|
// Errorf writes a error message to the terminal
|
||||||
t.OutputRaw(fmt.Sprintf(format, params...), d2enum.TermCategoryError)
|
func (t *Terminal) Errorf(format string, params ...interface{}) {
|
||||||
|
t.Rawf(d2enum.TermCategoryError, format, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) OutputClear() {
|
// Clear clears the terminal
|
||||||
|
func (t *Terminal) Clear() {
|
||||||
t.outputHistory = nil
|
t.outputHistory = nil
|
||||||
t.outputIndex = 0
|
t.outputIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) IsVisible() bool {
|
// Visible returns visible state
|
||||||
return t.visState != termVisHidden
|
func (t *Terminal) Visible() bool {
|
||||||
|
return t.visState != visHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) Hide() {
|
// Hide hides the terminal
|
||||||
if t.visState != termVisHidden {
|
func (t *Terminal) Hide() {
|
||||||
t.visState = termVisHiding
|
if t.visState != visHidden {
|
||||||
|
t.visState = visHiding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) Show() {
|
// Show shows the terminal
|
||||||
if t.visState != termVisShown {
|
func (t *Terminal) Show() {
|
||||||
t.visState = termVisShowing
|
if t.visState != visShown {
|
||||||
|
t.visState = visShowing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) BindAction(name, description string, action interface{}) error {
|
func (t *Terminal) toggle() {
|
||||||
actionType := reflect.TypeOf(action)
|
if t.visState == visHiding || t.visState == visHidden {
|
||||||
if actionType.Kind() != reflect.Func {
|
t.Show()
|
||||||
return errors.New("action is not a function")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < actionType.NumIn(); i++ {
|
t.Hide()
|
||||||
switch actionType.In(i).Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
case reflect.Int:
|
|
||||||
case reflect.Uint:
|
|
||||||
case reflect.Float64:
|
|
||||||
case reflect.Bool:
|
|
||||||
default:
|
|
||||||
return errors.New("action has unsupported arguments")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.actions[name] = termActionEntry{action, description}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) BindLogger() {
|
// BindLogger binds a log.Writer to the output
|
||||||
|
func (t *Terminal) BindLogger() {
|
||||||
log.SetOutput(&terminalLogger{writer: log.Writer(), terminal: t})
|
log.SetOutput(&terminalLogger{writer: log.Writer(), terminal: t})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminal) UnbindAction(name string) error {
|
|
||||||
delete(t.actions, name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func easeInOut(t float64) float64 {
|
func easeInOut(t float64) float64 {
|
||||||
t *= 2
|
t *= 2
|
||||||
if t < 1 {
|
if t < 1 {
|
||||||
@ -481,45 +459,3 @@ func parseCommand(command string) []string {
|
|||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTerminal() (*terminal, error) {
|
|
||||||
terminal := &terminal{
|
|
||||||
lineCount: termRowCount,
|
|
||||||
bgColor: d2util.Color(darkGrey),
|
|
||||||
fgColor: d2util.Color(lightGrey),
|
|
||||||
infoColor: d2util.Color(lightBlue),
|
|
||||||
warningColor: d2util.Color(yellow),
|
|
||||||
errorColor: d2util.Color(red),
|
|
||||||
actions: make(map[string]termActionEntry),
|
|
||||||
}
|
|
||||||
|
|
||||||
terminal.OutputInfof("::: OpenDiablo2 Terminal :::")
|
|
||||||
terminal.OutputInfof("type \"ls\" for a list of actions")
|
|
||||||
|
|
||||||
err := terminal.BindAction("ls", "list available actions", func() {
|
|
||||||
var names []string
|
|
||||||
for name := range terminal.actions {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(names)
|
|
||||||
|
|
||||||
terminal.OutputInfof("available actions (%d):", len(names))
|
|
||||||
for _, name := range names {
|
|
||||||
entry := terminal.actions[name]
|
|
||||||
terminal.OutputInfof("%s: %s; %s", name, entry.description, reflect.TypeOf(entry.action).String())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to bind the '%s' action, err: %w", "ls", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = terminal.BindAction("clear", "clear terminal", func() {
|
|
||||||
terminal.OutputClear()
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to bind the '%s' action, err: %w", "clear", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return terminal, nil
|
|
||||||
}
|
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type terminalLogger struct {
|
type terminalLogger struct {
|
||||||
terminal *terminal
|
terminal *Terminal
|
||||||
buffer bytes.Buffer
|
buffer bytes.Buffer
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
}
|
}
|
||||||
@ -31,16 +31,16 @@ func (tl *terminalLogger) Write(p []byte) (int, error) {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case strings.Index(lineLower, "error") > 0:
|
case strings.Index(lineLower, "error") > 0:
|
||||||
tl.terminal.OutputErrorf(line)
|
tl.terminal.Errorf(line)
|
||||||
case strings.Index(lineLower, "warning") > 0:
|
case strings.Index(lineLower, "warning") > 0:
|
||||||
tl.terminal.OutputWarningf(line)
|
tl.terminal.Errorf(line)
|
||||||
default:
|
default:
|
||||||
tl.terminal.Outputf(line)
|
tl.terminal.Printf(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tl.writer.Write(p)
|
return tl.writer.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tl *terminalLogger) BindToTerminal(t *terminal) {
|
func (tl *terminalLogger) BindToTerminal(t *Terminal) {
|
||||||
tl.terminal = t
|
tl.terminal = t
|
||||||
}
|
}
|
||||||
|
71
d2core/d2term/terminal_test.go
Normal file
71
d2core/d2term/terminal_test.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package d2term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTerminal(t *testing.T) {
|
||||||
|
term, err := NewTerminal()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lenOutput := len(term.outputHistory)
|
||||||
|
|
||||||
|
const expected1 = 2
|
||||||
|
if lenOutput != expected1 {
|
||||||
|
t.Fatalf("got %d expected %d", lenOutput, expected1)
|
||||||
|
}
|
||||||
|
|
||||||
|
term.Execute("clear")
|
||||||
|
term.Execute("ls")
|
||||||
|
|
||||||
|
lenOutput = len(term.outputHistory)
|
||||||
|
|
||||||
|
const expected2 = 3
|
||||||
|
if lenOutput != expected2 {
|
||||||
|
t.Fatalf("got %d expected %d", lenOutput, expected2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBind(t *testing.T) {
|
||||||
|
term, err := NewTerminal()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
term.Clear()
|
||||||
|
|
||||||
|
if err := term.Bind("hello", "world", []string{"world"}, func(args []string) error {
|
||||||
|
const expected = "world"
|
||||||
|
if args[0] != expected {
|
||||||
|
return fmt.Errorf("got %s expected %s", args[0], expected)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := term.Execute("hello world"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnbind(t *testing.T) {
|
||||||
|
term, err := NewTerminal()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
term.Unbind("clear")
|
||||||
|
term.Clear()
|
||||||
|
term.Execute("ls")
|
||||||
|
|
||||||
|
lenOutput := len(term.outputHistory)
|
||||||
|
|
||||||
|
const expected = 2
|
||||||
|
if lenOutput != expected {
|
||||||
|
t.Fatalf("got %d expected %d", lenOutput, expected)
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||||
@ -130,58 +131,36 @@ type Game struct {
|
|||||||
func (v *Game) OnLoad(_ d2screen.LoadingState) {
|
func (v *Game) OnLoad(_ d2screen.LoadingState) {
|
||||||
v.audioProvider.PlayBGM("")
|
v.audioProvider.PlayBGM("")
|
||||||
|
|
||||||
err := v.terminal.BindAction(
|
commands := []struct {
|
||||||
"spawnitem",
|
name string
|
||||||
"spawns an item at the local player position",
|
desc string
|
||||||
func(code1, code2, code3, code4, code5 string) {
|
args []string
|
||||||
codes := []string{code1, code2, code3, code4, code5}
|
fn func([]string) error
|
||||||
v.debugSpawnItemAtPlayer(codes...)
|
}{
|
||||||
},
|
{"spawnitem", "spawns an item at the local player position",
|
||||||
)
|
[]string{"code1", "code2", "code3", "code4", "code5"}, v.commandSpawnItem},
|
||||||
if err != nil {
|
{"spawnitemat", "spawns an item at the x,y coordinates",
|
||||||
v.Errorf("failed to bind the '%s' action, err: %v\n", "spawnitem", err)
|
[]string{"x", "y", "code1", "code2", "code3", "code4", "code5"}, v.commandSpawnItemAt},
|
||||||
|
{"spawnmon", "spawn monster at the local player position", []string{"name"}, v.commandSpawnMon},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = v.terminal.BindAction(
|
for _, cmd := range commands {
|
||||||
"spawnitemat",
|
if err := v.terminal.Bind(cmd.name, cmd.desc, cmd.args, cmd.fn); err != nil {
|
||||||
"spawns an item at the x,y coordinates",
|
v.Errorf(err.Error())
|
||||||
func(x, y int, code1, code2, code3, code4, code5 string) {
|
}
|
||||||
codes := []string{code1, code2, code3, code4, code5}
|
|
||||||
v.debugSpawnItemAtLocation(x, y, codes...)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
v.Errorf("failed to bind the '%s' action, err: %v\n", "spawnitemat", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = v.terminal.BindAction(
|
if err := v.asset.BindTerminalCommands(v.terminal); err != nil {
|
||||||
"spawnmon",
|
v.Errorf(err.Error())
|
||||||
"spawn monster at the local player position",
|
|
||||||
func(name string) {
|
|
||||||
x := int(v.localPlayer.Position.X())
|
|
||||||
y := int(v.localPlayer.Position.Y())
|
|
||||||
monstat := v.asset.Records.Monster.Stats[name]
|
|
||||||
if monstat == nil {
|
|
||||||
v.terminal.OutputErrorf("no monstat entry for \"%s\"", name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
monster, npcErr := v.gameClient.MapEngine.NewNPC(x, y, monstat, 0)
|
|
||||||
if npcErr != nil {
|
|
||||||
v.terminal.OutputErrorf("error generating monster \"%s\": %v", name, npcErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v.gameClient.MapEngine.AddEntity(monster)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
v.Errorf("failed to bind the '%s' action, err: %v\n", "spawnmon", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnUnload releases the resources of Gameplay screen
|
// OnUnload releases the resources of Gameplay screen
|
||||||
func (v *Game) OnUnload() error {
|
func (v *Game) OnUnload() error {
|
||||||
|
if err := v.gameControls.UnbindTerminalCommands(v.terminal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/OpenDiablo2/OpenDiablo2/issues/792
|
// https://github.com/OpenDiablo2/OpenDiablo2/issues/792
|
||||||
if err := v.inputManager.UnbindHandler(v.gameControls); err != nil {
|
if err := v.inputManager.UnbindHandler(v.gameControls); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -192,11 +171,7 @@ func (v *Game) OnUnload() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := v.terminal.UnbindAction("spawnItemAt"); err != nil {
|
if err := v.terminal.Unbind("spawnitemat", "spawnitem", "spawnmon"); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.terminal.UnbindAction("spawnItem"); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,6 +183,18 @@ func (v *Game) OnUnload() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := v.asset.UnbindTerminalCommands(v.terminal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := v.mapRenderer.UnbindTerminalCommands(v.terminal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := v.soundEngine.UnbindTerminalCommands(v.terminal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
v.soundEngine.Reset()
|
v.soundEngine.Reset()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -395,3 +382,47 @@ func (v *Game) debugSpawnItemAtLocation(x, y int, codes ...string) {
|
|||||||
v.Errorf(spawnItemErrStr, x, y, codes)
|
v.Errorf(spawnItemErrStr, x, y, codes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Game) commandSpawnItem(args []string) error {
|
||||||
|
v.debugSpawnItemAtPlayer(args...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Game) commandSpawnItemAt(args []string) error {
|
||||||
|
x, err := strconv.Atoi(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
y, err := strconv.Atoi(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
v.debugSpawnItemAtLocation(x, y, args[2:]...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Game) commandSpawnMon(args []string) error {
|
||||||
|
name := args[0]
|
||||||
|
x := int(v.localPlayer.Position.X())
|
||||||
|
y := int(v.localPlayer.Position.Y())
|
||||||
|
|
||||||
|
monstat := v.asset.Records.Monster.Stats[name]
|
||||||
|
if monstat == nil {
|
||||||
|
v.terminal.Errorf("no monstat entry for \"%s\"", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
monster, npcErr := v.gameClient.MapEngine.NewNPC(x, y, monstat, 0)
|
||||||
|
if npcErr != nil {
|
||||||
|
v.terminal.Errorf("error generating monster \"%s\": %v", name, npcErr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v.gameClient.MapEngine.AddEntity(monster)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package d2player
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -936,59 +937,137 @@ func (g *GameControls) onClickActionable(item actionableType) {
|
|||||||
action()
|
action()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GameControls) bindFreeCamCommand(term d2interface.Terminal) error {
|
func (g *GameControls) bindTerminalCommands(term d2interface.Terminal) error {
|
||||||
return term.BindAction("freecam", "toggle free camera movement", func() {
|
if err := term.Bind("freecam", "toggle free camera movement", nil, g.commandFreeCam); err != nil {
|
||||||
g.FreeCam = !g.FreeCam
|
return err
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if err := term.Bind("setleftskill", "set skill to fire on left click", []string{"id"}, g.commandSetLeftSkill(term)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := term.Bind("setrightskill", "set skill to fire on right click", []string{"id"}, g.commandSetRightSkill(term)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := term.Bind("learnskills", "learn all skills for the a given class", []string{"token"}, g.commandLearnSkills(term)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := term.Bind("learnskillid", "learn a skill by a given ID", []string{"id"}, g.commandLearnSkillID(term)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GameControls) bindSetLeftSkillCommand(term d2interface.Terminal) error {
|
// UnbindTerminalCommands unbinds commands from the terminal
|
||||||
setLeftSkill := func(id int) {
|
func (g *GameControls) UnbindTerminalCommands(term d2interface.Terminal) error {
|
||||||
|
return term.Unbind("freecam", "setleftskill", "setrightskill", "learnskills", "learnskillid")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameControls) setAddButtons() {
|
||||||
|
g.hud.addStatsButton.SetEnabled(g.hero.Stats.StatsPoints > 0)
|
||||||
|
g.hud.addSkillButton.SetEnabled(g.hero.Stats.SkillPoints > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameControls) loadAddButtons() {
|
||||||
|
g.hud.addStatsButton.OnActivated(func() { g.toggleHeroStatsPanel() })
|
||||||
|
g.hud.addSkillButton.OnActivated(func() { g.toggleSkilltreePanel() })
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameControls) commandFreeCam([]string) error {
|
||||||
|
g.FreeCam = !g.FreeCam
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameControls) commandSetLeftSkill(term d2interface.Terminal) func(args []string) error {
|
||||||
|
return func(args []string) error {
|
||||||
|
id, err := strconv.Atoi(args[0])
|
||||||
|
if err != nil {
|
||||||
|
term.Errorf("invalid argument")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
skillRecord := g.asset.Records.Skill.Details[id]
|
skillRecord := g.asset.Records.Skill.Details[id]
|
||||||
skill, err := g.heroState.CreateHeroSkill(1, skillRecord.Skill)
|
skill, err := g.heroState.CreateHeroSkill(1, skillRecord.Skill)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
term.OutputErrorf("cannot create skill with ID of %d, error: %s", id, err)
|
term.Errorf("cannot create skill with ID of %d, error: %s", id, err)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
g.hero.LeftSkill = skill
|
g.hero.LeftSkill = skill
|
||||||
}
|
|
||||||
|
|
||||||
return term.BindAction(
|
return nil
|
||||||
"setleftskill",
|
}
|
||||||
"set skill to fire on left click",
|
|
||||||
setLeftSkill,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GameControls) bindSetRightSkillCommand(term d2interface.Terminal) error {
|
func (g *GameControls) commandSetRightSkill(term d2interface.Terminal) func(args []string) error {
|
||||||
setRightSkill := func(id int) {
|
return func(args []string) error {
|
||||||
|
id, err := strconv.Atoi(args[0])
|
||||||
|
if err != nil {
|
||||||
|
term.Errorf("invalid argument")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
skillRecord := g.asset.Records.Skill.Details[id]
|
skillRecord := g.asset.Records.Skill.Details[id]
|
||||||
skill, err := g.heroState.CreateHeroSkill(0, skillRecord.Skill)
|
skill, err := g.heroState.CreateHeroSkill(0, skillRecord.Skill)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
term.OutputErrorf("cannot create skill with ID of %d, error: %s", id, err)
|
term.Errorf("cannot create skill with ID of %d, error: %s", id, err)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
g.hero.RightSkill = skill
|
g.hero.RightSkill = skill
|
||||||
}
|
|
||||||
|
|
||||||
return term.BindAction(
|
return nil
|
||||||
"setrightskill",
|
}
|
||||||
"set skill to fire on right click",
|
|
||||||
setRightSkill,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const classTokenLength = 3
|
func (g *GameControls) commandLearnSkillID(term d2interface.Terminal) func(args []string) error {
|
||||||
|
return func(args []string) error {
|
||||||
|
id, err := strconv.Atoi(args[0])
|
||||||
|
if err != nil {
|
||||||
|
term.Errorf("invalid argument")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (g *GameControls) bindLearnSkillsCommand(term d2interface.Terminal) error {
|
skillRecord := g.asset.Records.Skill.Details[id]
|
||||||
learnSkills := func(token string) {
|
if skillRecord == nil {
|
||||||
|
term.Errorf("cannot find a skill record for ID: %d", id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
skill, err := g.heroState.CreateHeroSkill(1, skillRecord.Skill)
|
||||||
|
if skill == nil {
|
||||||
|
term.Errorf("cannot create skill: %s", skillRecord.Skill)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
g.hero.Skills[skill.ID] = skill
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
term.Errorf("cannot learn skill for class, error: %s", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
g.hud.skillSelectMenu.RegenerateImageCache()
|
||||||
|
g.Infof("Learned skill: " + skill.Skill)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameControls) commandLearnSkills(term d2interface.Terminal) func(args []string) error {
|
||||||
|
const classTokenLength = 3
|
||||||
|
|
||||||
|
return func(args []string) error {
|
||||||
|
token := args[0]
|
||||||
if len(token) < classTokenLength {
|
if len(token) < classTokenLength {
|
||||||
term.OutputErrorf("The given class token should be at least 3 characters")
|
term.Errorf("The given class token should be at least 3 characters")
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
validPrefixes := []string{"ama", "ass", "nec", "bar", "sor", "dru", "pal"}
|
validPrefixes := []string{"ama", "ass", "nec", "bar", "sor", "dru", "pal"}
|
||||||
@ -1004,9 +1083,9 @@ func (g *GameControls) bindLearnSkillsCommand(term d2interface.Terminal) error {
|
|||||||
|
|
||||||
if !isValidToken {
|
if !isValidToken {
|
||||||
fmtInvalid := "Invalid class, must be a value starting with(case insensitive): %s"
|
fmtInvalid := "Invalid class, must be a value starting with(case insensitive): %s"
|
||||||
term.OutputErrorf(fmtInvalid, strings.Join(validPrefixes, ", "))
|
term.Errorf(fmtInvalid, strings.Join(validPrefixes, ", "))
|
||||||
|
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -1042,80 +1121,10 @@ func (g *GameControls) bindLearnSkillsCommand(term d2interface.Terminal) error {
|
|||||||
g.Infof("Learned %d skills", learnedSkillsCount)
|
g.Infof("Learned %d skills", learnedSkillsCount)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
term.OutputErrorf("cannot learn skill for class, error: %s", err)
|
term.Errorf("cannot learn skill for class, error: %s", err)
|
||||||
return
|
return nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return term.BindAction(
|
|
||||||
"learnskills",
|
|
||||||
"learn all skills for the a given class",
|
|
||||||
learnSkills,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GameControls) bindLearnSkillByIDCommand(term d2interface.Terminal) error {
|
|
||||||
learnByID := func(id int) {
|
|
||||||
skillRecord := g.asset.Records.Skill.Details[id]
|
|
||||||
if skillRecord == nil {
|
|
||||||
term.OutputErrorf("cannot find a skill record for ID: %d", id)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
skill, err := g.heroState.CreateHeroSkill(1, skillRecord.Skill)
|
return nil
|
||||||
if skill == nil {
|
|
||||||
term.OutputErrorf("cannot create skill: %s", skillRecord.Skill)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
g.hero.Skills[skill.ID] = skill
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
term.OutputErrorf("cannot learn skill for class, error: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
g.hud.skillSelectMenu.RegenerateImageCache()
|
|
||||||
g.Info("Learned skill: " + skill.Skill)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return term.BindAction(
|
|
||||||
"learnskillid",
|
|
||||||
"learn a skill by a given ID",
|
|
||||||
learnByID,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GameControls) bindTerminalCommands(term d2interface.Terminal) error {
|
|
||||||
if err := g.bindFreeCamCommand(term); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := g.bindSetLeftSkillCommand(term); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := g.bindSetRightSkillCommand(term); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := g.bindLearnSkillsCommand(term); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := g.bindLearnSkillByIDCommand(term); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GameControls) setAddButtons() {
|
|
||||||
g.hud.addStatsButton.SetEnabled(g.hero.Stats.StatsPoints > 0)
|
|
||||||
g.hud.addSkillButton.SetEnabled(g.hero.Stats.SkillPoints > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GameControls) loadAddButtons() {
|
|
||||||
g.hud.addStatsButton.OnActivated(func() { g.toggleHeroStatsPanel() })
|
|
||||||
g.hud.addSkillButton.OnActivated(func() { g.toggleSkilltreePanel() })
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user