From 09a28c282274f877142cd1c366ee0163cde6c0c4 Mon Sep 17 00:00:00 2001 From: dk Date: Sun, 28 Jun 2020 18:40:52 -0700 Subject: [PATCH] removed d2term singleton (#483) --- d2common/d2interface/terminal.go | 58 +++++++ d2core/d2asset/d2asset.go | 26 +-- d2core/d2map/d2maprenderer/renderer.go | 6 +- d2core/d2term/d2term.go | 81 +-------- d2core/d2term/terminal.go | 192 +++++++++------------- d2core/d2term/terminal_logger.go | 44 +++++ d2game/d2gamescreen/character_select.go | 8 +- d2game/d2gamescreen/credits.go | 3 +- d2game/d2gamescreen/escape_menu.go | 6 +- d2game/d2gamescreen/game.go | 10 +- d2game/d2gamescreen/main_menu.go | 12 +- d2game/d2gamescreen/map_engine_testing.go | 7 +- d2game/d2gamescreen/select_hero_class.go | 5 +- d2game/d2player/game_controls.go | 8 +- main.go | 63 +++---- 15 files changed, 276 insertions(+), 253 deletions(-) create mode 100644 d2common/d2interface/terminal.go create mode 100644 d2core/d2term/terminal_logger.go diff --git a/d2common/d2interface/terminal.go b/d2common/d2interface/terminal.go new file mode 100644 index 00000000..b4c1551f --- /dev/null +++ b/d2common/d2interface/terminal.go @@ -0,0 +1,58 @@ +package d2interface + +import ( + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" +) + +type TermCategory int + +const ( + TermCategoryNone TermCategory = iota + TermCategoryInfo + TermCategoryWarning + TermCategoryError +) + +const ( + termCharWidth = 6 + termCharHeight = 16 + termRowCount = 24 + termRowCountMax = 32 + termColCountMax = 128 + termAnimLength = 0.5 +) + +type termVis int + +const ( + termVisHidden termVis = iota + termVisShowing + termVisShown + termVisHiding +) + +type Terminal interface { + BindLogger() + + Advance(elapsed float64) error + OnKeyDown(event d2input.KeyEvent) bool + OnKeyChars(event d2input.KeyCharsEvent) bool + Render(surface d2render.Surface) error + Execute(command string) error + OutputRaw(text string, category TermCategory) + Output(format string, params ...interface{}) + OutputInfo(format string, params ...interface{}) + OutputWarning(format string, params ...interface{}) + OutputError(format string, params ...interface{}) + OutputClear() + IsVisible() bool + Hide() + Show() + BindAction(name, description string, action interface{}) error + UnbindAction(name string) error +} + +type TerminalLogger interface { + Write(p []byte) (int, error) +} diff --git a/d2core/d2asset/d2asset.go b/d2core/d2asset/d2asset.go index 3cf327ae..6a4192f7 100644 --- a/d2core/d2asset/d2asset.go +++ b/d2core/d2asset/d2asset.go @@ -6,15 +6,15 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dat" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2mpq" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2pl2" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2term" ) var singleton *assetManager -func Initialize() error { +func Initialize(term d2interface.Terminal) error { verifyNotInit() var ( @@ -36,11 +36,11 @@ func Initialize() error { fontManager, } - d2term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) { + term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) { if verbose { - d2term.OutputInfo("asset manager verbose logging enabled") + term.OutputInfo("asset manager verbose logging enabled") } else { - d2term.OutputInfo("asset manager verbose logging disabled") + term.OutputInfo("asset manager verbose logging disabled") } archiveManager.cache.SetVerbose(verbose) @@ -50,16 +50,16 @@ func Initialize() error { animationManager.cache.SetVerbose(verbose) }) - d2term.BindAction("assetstat", "display asset manager cache statistics", func() { - d2term.OutputInfo("archive cache: %f", float64(archiveManager.cache.GetWeight())/float64(archiveManager.cache.GetBudget())*100.0) - d2term.OutputInfo("file cache: %f", float64(fileManager.cache.GetWeight())/float64(fileManager.cache.GetBudget())*100.0) - d2term.OutputInfo("palette cache: %f", float64(paletteManager.cache.GetWeight())/float64(paletteManager.cache.GetBudget())*100.0) - d2term.OutputInfo("palette transform cache: %f", float64(paletteTransformManager.cache.GetWeight())/float64(paletteTransformManager.cache.GetBudget())*100.0) - d2term.OutputInfo("animation cache: %f", float64(animationManager.cache.GetWeight())/float64(animationManager.cache.GetBudget())*100.0) - d2term.OutputInfo("font cache: %f", float64(fontManager.cache.GetWeight())/float64(fontManager.cache.GetBudget())*100.0) + term.BindAction("assetstat", "display asset manager cache statistics", func() { + term.OutputInfo("archive cache: %f", float64(archiveManager.cache.GetWeight())/float64(archiveManager.cache.GetBudget())*100.0) + term.OutputInfo("file cache: %f", float64(fileManager.cache.GetWeight())/float64(fileManager.cache.GetBudget())*100.0) + term.OutputInfo("palette cache: %f", float64(paletteManager.cache.GetWeight())/float64(paletteManager.cache.GetBudget())*100.0) + term.OutputInfo("palette transform cache: %f", float64(paletteTransformManager.cache.GetWeight())/float64(paletteTransformManager.cache.GetBudget())*100.0) + term.OutputInfo("animation cache: %f", float64(animationManager.cache.GetWeight())/float64(animationManager.cache.GetBudget())*100.0) + term.OutputInfo("font cache: %f", float64(fontManager.cache.GetWeight())/float64(fontManager.cache.GetBudget())*100.0) }) - d2term.BindAction("assetclear", "clear asset manager cache", func() { + term.BindAction("assetclear", "clear asset manager cache", func() { archiveManager.cache.Clear() fileManager.cache.Clear() paletteManager.cache.Clear() diff --git a/d2core/d2map/d2maprenderer/renderer.go b/d2core/d2map/d2maprenderer/renderer.go index b1b0b16d..857bc889 100644 --- a/d2core/d2map/d2maprenderer/renderer.go +++ b/d2core/d2map/d2maprenderer/renderer.go @@ -14,8 +14,8 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2term" ) // The map renderer, used to render the map @@ -30,7 +30,7 @@ type MapRenderer struct { } // Creates an instance of the map renderer -func CreateMapRenderer(mapEngine *d2mapengine.MapEngine) *MapRenderer { +func CreateMapRenderer(mapEngine *d2mapengine.MapEngine, term d2interface.Terminal) *MapRenderer { result := &MapRenderer{ mapEngine: mapEngine, viewport: NewViewport(0, 0, 800, 600), @@ -38,7 +38,7 @@ func CreateMapRenderer(mapEngine *d2mapengine.MapEngine) *MapRenderer { result.viewport.SetCamera(&result.camera) - d2term.BindAction("mapdebugvis", "set map debug visualization level", func(level int) { + term.BindAction("mapdebugvis", "set map debug visualization level", func(level int) { result.debugVisLevel = level }) diff --git a/d2core/d2term/d2term.go b/d2core/d2term/d2term.go index 68079005..7ae7e6f9 100644 --- a/d2core/d2term/d2term.go +++ b/d2core/d2term/d2term.go @@ -1,88 +1,19 @@ package d2term import ( - "errors" - "log" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" ) -var ( - ErrWasInit = errors.New("terminal system is already initialized") - ErrNotInit = errors.New("terminal system is not initialized") -) +func Initialize() (*terminal, error) { -var singleton *terminal - -func Initialize() error { - verifyNotInit() - - terminal, err := createTerminal() + term, err := createTerminal() if err != nil { - return err + return nil, err } - if err := d2input.BindHandlerWithPriority(terminal, d2input.PriorityHigh); err != nil { - return err + if err := d2input.BindHandlerWithPriority(term, d2input.PriorityHigh); err != nil { + return nil, err } - singleton = terminal - return nil -} - -func Advance(elapsed float64) error { - verifyWasInit() - return singleton.advance(elapsed) -} - -func Output(format string, params ...interface{}) { - verifyWasInit() - singleton.output(format, params...) -} - -func OutputInfo(format string, params ...interface{}) { - verifyWasInit() - singleton.outputInfo(format, params...) -} - -func OutputWarning(format string, params ...interface{}) { - verifyWasInit() - singleton.outputWarning(format, params...) -} - -func OutputError(format string, params ...interface{}) { - verifyWasInit() - singleton.outputError(format, params...) -} - -func BindAction(name, description string, action interface{}) error { - verifyWasInit() - return singleton.bindAction(name, description, action) -} - -func UnbindAction(name string) error { - verifyWasInit() - return singleton.unbindAction(name) -} - -func Render(surface d2render.Surface) error { - verifyWasInit() - return singleton.render(surface) -} - -func BindLogger() { - log.SetOutput(&terminalLogger{writer: log.Writer()}) -} - -func verifyWasInit() { - if singleton == nil { - panic(ErrNotInit) - } -} - -func verifyNotInit() { - if singleton != nil { - panic(ErrWasInit) - } + return term, nil } diff --git a/d2core/d2term/terminal.go b/d2core/d2term/terminal.go index 36322f6e..7bfb2bdc 100644 --- a/d2core/d2term/terminal.go +++ b/d2core/d2term/terminal.go @@ -1,12 +1,10 @@ package d2term import ( - "bufio" - "bytes" "errors" "fmt" "image/color" - "io" + "log" "math" "reflect" "sort" @@ -14,10 +12,20 @@ import ( "strings" "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" ) +type TermCategory d2interface.TermCategory + +const ( + TermCategoryNone = TermCategory(d2interface.TermCategoryNone) + TermCategoryInfo = TermCategory(d2interface.TermCategoryInfo) + TermCategoryWarning = TermCategory(d2interface.TermCategoryWarning) + TermCategoryError = TermCategory(d2interface.TermCategoryError) +) const ( termCharWidth = 6 termCharHeight = 16 @@ -27,15 +35,6 @@ const ( termAnimLength = 0.5 ) -type termCategory int - -const ( - termCategoryNone termCategory = iota - termCategoryInfo - termCategoryWarning - termCategoryError -) - type termVis int const ( @@ -55,7 +54,7 @@ var ( type termHistroyEntry struct { text string - category termCategory + category d2interface.TermCategory } type termActionEntry struct { @@ -78,37 +77,7 @@ type terminal struct { actions map[string]termActionEntry } -func createTerminal() (*terminal, error) { - terminal := &terminal{ - lineCount: termRowCount, - actions: make(map[string]termActionEntry), - } - - terminal.outputInfo("::: OpenDiablo2 Terminal :::") - terminal.outputInfo("type \"ls\" for a list of actions") - - terminal.bindAction("ls", "list available actions", func() { - var names []string - for name := range terminal.actions { - names = append(names, name) - } - - sort.Strings(names) - - terminal.outputInfo("available actions (%d):", len(names)) - for _, name := range names { - entry := terminal.actions[name] - terminal.outputInfo("%s: %s; %s", name, entry.description, reflect.TypeOf(entry.action).String()) - } - }) - terminal.bindAction("clear", "clear terminal", func() { - terminal.outputClear() - }) - - return terminal, nil -} - -func (t *terminal) advance(elapsed float64) error { +func (t *terminal) Advance(elapsed float64) error { switch t.visState { case termVisShowing: t.visAnim = math.Min(1.0, t.visAnim+elapsed/termAnimLength) @@ -122,7 +91,7 @@ func (t *terminal) advance(elapsed float64) error { } } - if !t.isVisible() { + if !t.IsVisible() { return nil } @@ -131,16 +100,16 @@ func (t *terminal) advance(elapsed float64) error { func (t *terminal) OnKeyDown(event d2input.KeyEvent) bool { if t.visState == termVisHiding || t.visState == termVisHidden && event.Key == d2input.KeyGraveAccent { - t.show() + t.Show() return true } - if !t.isVisible() { + if !t.IsVisible() { return false } if event.Key == d2input.KeyGraveAccent { - t.hide() + t.Hide() return true } @@ -149,10 +118,10 @@ func (t *terminal) OnKeyDown(event d2input.KeyEvent) bool { return true } - maxOutputIndex := d2common.MaxInt(0, len(t.outputHistory)-t.lineCount) + maxoutputIndex := d2common.MaxInt(0, len(t.outputHistory)-t.lineCount) if event.Key == d2input.KeyHome { - t.outputIndex = maxOutputIndex + t.outputIndex = maxoutputIndex return true } @@ -162,8 +131,8 @@ func (t *terminal) OnKeyDown(event d2input.KeyEvent) bool { } if event.Key == d2input.KeyPageUp { - if t.outputIndex += t.lineCount; t.outputIndex >= maxOutputIndex { - t.outputIndex = maxOutputIndex + if t.outputIndex += t.lineCount; t.outputIndex >= maxoutputIndex { + t.outputIndex = maxoutputIndex } return true @@ -207,9 +176,9 @@ func (t *terminal) OnKeyDown(event d2input.KeyEvent) bool { t.commandHistory = append(commandHistory, t.command) - t.output(t.command) - if err := t.execute(t.command); err != nil { - t.outputError(err.Error()) + t.Output(t.command) + if err := t.Execute(t.command); err != nil { + t.OutputError(err.Error()) } t.commandIndex = len(t.commandHistory) - 1 @@ -227,7 +196,7 @@ func (t *terminal) OnKeyDown(event d2input.KeyEvent) bool { } func (t *terminal) OnKeyChars(event d2input.KeyCharsEvent) bool { - if !t.isVisible() { + if !t.IsVisible() { return false } @@ -242,8 +211,8 @@ func (t *terminal) OnKeyChars(event d2input.KeyCharsEvent) bool { return handled } -func (t *terminal) render(surface d2render.Surface) error { - if !t.isVisible() { +func (t *terminal) Render(surface d2render.Surface) error { + if !t.IsVisible() { return nil } @@ -267,11 +236,11 @@ func (t *terminal) render(surface d2render.Surface) error { surface.DrawText(historyEntry.text) surface.PushTranslation(-termCharWidth*2, 0) switch historyEntry.category { - case termCategoryInfo: + case d2interface.TermCategoryInfo: surface.DrawRect(termCharWidth, termCharHeight, termInfoColor) - case termCategoryWarning: + case d2interface.TermCategoryWarning: surface.DrawRect(termCharWidth, termCharHeight, termWarningColor) - case termCategoryError: + case d2interface.TermCategoryError: surface.DrawRect(termCharWidth, termCharHeight, termErrorColor) } surface.Pop() @@ -288,7 +257,7 @@ func (t *terminal) render(surface d2render.Surface) error { return nil } -func (t *terminal) execute(command string) error { +func (t *terminal) Execute(command string) error { params := parseCommand(command) if len(params) == 0 { return errors.New("invalid command") @@ -349,16 +318,16 @@ func (t *terminal) execute(command string) error { actionReturnValues := actionValue.Call(paramValues) if actionReturnValueCount := len(actionReturnValues); actionReturnValueCount > 0 { - t.outputInfo("function returned %d values:", actionReturnValueCount) + t.OutputInfo("function returned %d values:", actionReturnValueCount) for _, actionReturnValue := range actionReturnValues { - t.outputInfo("%v: %s", actionReturnValue.Interface(), actionReturnValue.String()) + t.OutputInfo("%v: %s", actionReturnValue.Interface(), actionReturnValue.String()) } } return nil } -func (t *terminal) outputRaw(text string, category termCategory) { +func (t *terminal) OutputRaw(text string, category d2interface.TermCategory) { var line string for _, word := range strings.Split(text, " ") { if len(line) > 0 { @@ -379,44 +348,44 @@ func (t *terminal) outputRaw(text string, category termCategory) { t.outputHistory = append(t.outputHistory, termHistroyEntry{line, category}) } -func (t *terminal) output(format string, params ...interface{}) { - t.outputRaw(fmt.Sprintf(format, params...), termCategoryNone) +func (t *terminal) Output(format string, params ...interface{}) { + t.OutputRaw(fmt.Sprintf(format, params...), d2interface.TermCategoryNone) } -func (t *terminal) outputInfo(format string, params ...interface{}) { - t.outputRaw(fmt.Sprintf(format, params...), termCategoryInfo) +func (t *terminal) OutputInfo(format string, params ...interface{}) { + t.OutputRaw(fmt.Sprintf(format, params...), d2interface.TermCategoryInfo) } -func (t *terminal) outputWarning(format string, params ...interface{}) { - t.outputRaw(fmt.Sprintf(format, params...), termCategoryWarning) +func (t *terminal) OutputWarning(format string, params ...interface{}) { + t.OutputRaw(fmt.Sprintf(format, params...), d2interface.TermCategoryWarning) } -func (t *terminal) outputError(format string, params ...interface{}) { - t.outputRaw(fmt.Sprintf(format, params...), termCategoryError) +func (t *terminal) OutputError(format string, params ...interface{}) { + t.OutputRaw(fmt.Sprintf(format, params...), d2interface.TermCategoryError) } -func (t *terminal) outputClear() { +func (t *terminal) OutputClear() { t.outputHistory = nil t.outputIndex = 0 } -func (t *terminal) isVisible() bool { +func (t *terminal) IsVisible() bool { return t.visState != termVisHidden } -func (t *terminal) hide() { +func (t *terminal) Hide() { if t.visState != termVisHidden { t.visState = termVisHiding } } -func (t *terminal) show() { +func (t *terminal) Show() { if t.visState != termVisShown { t.visState = termVisShowing } } -func (t *terminal) bindAction(name, description string, action interface{}) error { +func (t *terminal) BindAction(name, description string, action interface{}) error { actionType := reflect.TypeOf(action) if actionType.Kind() != reflect.Func { return errors.New("action is not a function") @@ -438,42 +407,15 @@ func (t *terminal) bindAction(name, description string, action interface{}) erro return nil } -func (t *terminal) unbindAction(name string) error { +func (t *terminal) BindLogger() { + log.SetOutput(&terminalLogger{writer: log.Writer(), terminal: t}) +} + +func (t *terminal) UnbindAction(name string) error { delete(t.actions, name) return nil } -type terminalLogger struct { - buffer bytes.Buffer - writer io.Writer -} - -func (t *terminalLogger) Write(p []byte) (int, error) { - n, err := t.buffer.Write(p) - if err != nil { - return n, err - } - - reader := bufio.NewReader(&t.buffer) - termBytes, _, err := reader.ReadLine() - if err != nil { - return n, err - } - - line := string(termBytes[:]) - lineLower := strings.ToLower(line) - - if strings.Index(lineLower, "error") > 0 { - OutputError(line) - } else if strings.Index(lineLower, "warning") > 0 { - OutputWarning(line) - } else { - Output(line) - } - - return t.writer.Write(p) -} - func easeInOut(t float64) float64 { t *= 2 if t < 1 { @@ -526,3 +468,33 @@ func parseCommand(command string) []string { return params } + +func createTerminal() (*terminal, error) { + terminal := &terminal{ + lineCount: termRowCount, + actions: make(map[string]termActionEntry), + } + + terminal.OutputInfo("::: OpenDiablo2 Terminal :::") + terminal.OutputInfo("type \"ls\" for a list of actions") + + terminal.BindAction("ls", "list available actions", func() { + var names []string + for name := range terminal.actions { + names = append(names, name) + } + + sort.Strings(names) + + terminal.OutputInfo("available actions (%d):", len(names)) + for _, name := range names { + entry := terminal.actions[name] + terminal.OutputInfo("%s: %s; %s", name, entry.description, reflect.TypeOf(entry.action).String()) + } + }) + terminal.BindAction("clear", "clear terminal", func() { + terminal.OutputClear() + }) + + return terminal, nil +} diff --git a/d2core/d2term/terminal_logger.go b/d2core/d2term/terminal_logger.go new file mode 100644 index 00000000..fc3059a4 --- /dev/null +++ b/d2core/d2term/terminal_logger.go @@ -0,0 +1,44 @@ +package d2term + +import ( + "bufio" + "bytes" + "io" + "strings" +) + +type terminalLogger struct { + terminal *terminal + buffer bytes.Buffer + writer io.Writer +} + +func (tl *terminalLogger) Write(p []byte) (int, error) { + n, err := tl.buffer.Write(p) + if err != nil { + return n, err + } + + reader := bufio.NewReader(&tl.buffer) + termBytes, _, err := reader.ReadLine() + if err != nil { + return n, err + } + + line := string(termBytes[:]) + lineLower := strings.ToLower(line) + + if strings.Index(lineLower, "error") > 0 { + tl.terminal.OutputError(line) + } else if strings.Index(lineLower, "warning") > 0 { + tl.terminal.OutputWarning(line) + } else { + tl.terminal.Output(line) + } + + return tl.writer.Write(p) +} + +func (tl *terminalLogger) BindToTerminal(t *terminal) { + tl.terminal = t +} diff --git a/d2game/d2gamescreen/character_select.go b/d2game/d2gamescreen/character_select.go index b591522a..cecc0b33 100644 --- a/d2game/d2gamescreen/character_select.go +++ b/d2game/d2gamescreen/character_select.go @@ -47,15 +47,17 @@ type CharacterSelect struct { connectionType d2clientconnectiontype.ClientConnectionType connectionHost string audioProvider d2interface.AudioProvider + terminal d2interface.Terminal } func CreateCharacterSelect(audioProvider d2interface.AudioProvider, - connectionType d2clientconnectiontype.ClientConnectionType, connectionHost string) *CharacterSelect { + connectionType d2clientconnectiontype.ClientConnectionType, connectionHost string, term d2interface.Terminal) *CharacterSelect { return &CharacterSelect{ selectedCharacter: -1, connectionType: connectionType, connectionHost: connectionHost, audioProvider: audioProvider, + terminal: term, } } @@ -180,7 +182,7 @@ func (v *CharacterSelect) onNewCharButtonClicked() { } func (v *CharacterSelect) onExitButtonClicked() { - mainMenu := CreateMainMenu(v.audioProvider) + mainMenu := CreateMainMenu(v.audioProvider, v.terminal) mainMenu.SetScreenMode(ScreenModeMainMenu) d2screen.SetNextScreen(mainMenu) } @@ -311,5 +313,5 @@ func (v *CharacterSelect) onOkButtonClicked() { gameClient.Open("", v.gameStates[v.selectedCharacter].FilePath) } - d2screen.SetNextScreen(CreateGame(v.audioProvider, gameClient)) + d2screen.SetNextScreen(CreateGame(v.audioProvider, gameClient, v.terminal)) } diff --git a/d2game/d2gamescreen/credits.go b/d2game/d2gamescreen/credits.go index 434af10e..93bc04f9 100644 --- a/d2game/d2gamescreen/credits.go +++ b/d2game/d2gamescreen/credits.go @@ -35,6 +35,7 @@ type Credits struct { cyclesTillNextLine int doneWithCredits bool audioProvider d2interface.AudioProvider + terminal d2interface.Terminal } // CreateCredits creates an instance of the credits screen @@ -138,7 +139,7 @@ func (v *Credits) Advance(tickTime float64) error { } func (v *Credits) onExitButtonClicked() { - mainMenu := CreateMainMenu(v.audioProvider) + mainMenu := CreateMainMenu(v.audioProvider, v.terminal) mainMenu.SetScreenMode(ScreenModeMainMenu) d2screen.SetNextScreen(mainMenu) } diff --git a/d2game/d2gamescreen/escape_menu.go b/d2game/d2gamescreen/escape_menu.go index 5b97cba4..b63176c7 100644 --- a/d2game/d2gamescreen/escape_menu.go +++ b/d2game/d2gamescreen/escape_menu.go @@ -65,6 +65,7 @@ type EscapeMenu struct { rightPent *d2gui.AnimatedSprite layouts []*layout audioProvider d2interface.AudioProvider + terminal d2interface.Terminal } type layout struct { @@ -112,9 +113,10 @@ type actionableElement interface { Trigger() } -func NewEscapeMenu(audioProvider d2interface.AudioProvider) *EscapeMenu { +func NewEscapeMenu(audioProvider d2interface.AudioProvider, term d2interface.Terminal) *EscapeMenu { m := &EscapeMenu{ audioProvider: audioProvider, + terminal: term, } m.layouts = []*layout{ @@ -339,7 +341,7 @@ func (m *EscapeMenu) showLayout(id layoutID) { } if id == saveLayoutID { - mainMenu := CreateMainMenu(m.audioProvider) + mainMenu := CreateMainMenu(m.audioProvider, m.terminal) mainMenu.SetScreenMode(ScreenModeMainMenu) d2screen.SetNextScreen(mainMenu) return diff --git a/d2game/d2gamescreen/game.go b/d2game/d2gamescreen/game.go index cbfa05ec..55f56926 100644 --- a/d2game/d2gamescreen/game.go +++ b/d2game/d2gamescreen/game.go @@ -27,20 +27,22 @@ type Game struct { localPlayer *d2mapentity.Player lastRegionType d2enum.RegionIdType audioProvider d2interface.AudioProvider + terminal d2interface.Terminal ticksSinceLevelCheck float64 escapeMenu *EscapeMenu } -func CreateGame(audioProvider d2interface.AudioProvider, gameClient *d2client.GameClient) *Game { +func CreateGame(audioProvider d2interface.AudioProvider, gameClient *d2client.GameClient, term d2interface.Terminal) *Game { result := &Game{ gameClient: gameClient, gameControls: nil, localPlayer: nil, lastRegionType: d2enum.RegionNone, ticksSinceLevelCheck: 0, - mapRenderer: d2maprenderer.CreateMapRenderer(gameClient.MapEngine), - escapeMenu: NewEscapeMenu(audioProvider), + mapRenderer: d2maprenderer.CreateMapRenderer(gameClient.MapEngine, term), + escapeMenu: NewEscapeMenu(audioProvider, term), audioProvider: audioProvider, + terminal: term, } result.escapeMenu.OnLoad() d2input.BindHandler(result.escapeMenu) @@ -112,7 +114,7 @@ func (v *Game) Advance(tickTime float64) error { continue } v.localPlayer = player - v.gameControls = d2player.NewGameControls(player, v.gameClient.MapEngine, v.mapRenderer, v) + v.gameControls = d2player.NewGameControls(player, v.gameClient.MapEngine, v.mapRenderer, v, v.terminal) v.gameControls.Load() d2input.BindHandler(v.gameControls) diff --git a/d2game/d2gamescreen/main_menu.go b/d2game/d2gamescreen/main_menu.go index 2a3834bd..ffeb42a2 100644 --- a/d2game/d2gamescreen/main_menu.go +++ b/d2game/d2gamescreen/main_menu.go @@ -71,14 +71,16 @@ type MainMenu struct { screenMode MainMenuScreenMode leftButtonHeld bool audioProvider d2interface.AudioProvider + terminal d2interface.Terminal } // CreateMainMenu creates an instance of MainMenu -func CreateMainMenu(audioProvider d2interface.AudioProvider) *MainMenu { +func CreateMainMenu(audioProvider d2interface.AudioProvider, term d2interface.Terminal) *MainMenu { return &MainMenu{ screenMode: ScreenModeUnknown, leftButtonHeld: true, audioProvider: audioProvider, + terminal: term, } } @@ -255,7 +257,7 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) { } func (v *MainMenu) onMapTestClicked() { - d2screen.SetNextScreen(CreateMapEngineTest(0, 1)) + d2screen.SetNextScreen(CreateMapEngineTest(0, 1, v.terminal)) } func openbrowser(url string) { @@ -280,7 +282,7 @@ func openbrowser(url string) { func (v *MainMenu) onSinglePlayerClicked() { // Go here only if existing characters are available to select if d2player.HasGameStates() { - d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText())) + d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText(), v.terminal)) return } d2screen.SetNextScreen(CreateSelectHeroClass(v.audioProvider, d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText())) @@ -410,7 +412,7 @@ func (v *MainMenu) onTcpIpCancelClicked() { } func (v *MainMenu) onTcpIpHostGameClicked() { - d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANServer, "")) + d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANServer, "", v.terminal)) } func (v *MainMenu) onTcpIpJoinGameClicked() { @@ -422,5 +424,5 @@ func (v *MainMenu) onBtnTcpIpCancelClicked() { } func (v *MainMenu) onBtnTcpIpOkClicked() { - d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANClient, v.tcpJoinGameEntry.GetText())) + d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANClient, v.tcpJoinGameEntry.GetText(), v.terminal)) } diff --git a/d2game/d2gamescreen/map_engine_testing.go b/d2game/d2gamescreen/map_engine_testing.go index e1ec1563..8f84b2ad 100644 --- a/d2game/d2gamescreen/map_engine_testing.go +++ b/d2game/d2gamescreen/map_engine_testing.go @@ -11,6 +11,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" @@ -82,6 +83,7 @@ type MapEngineTest struct { gameState *d2player.PlayerState mapEngine *d2mapengine.MapEngine mapRenderer *d2maprenderer.MapRenderer + terminal d2interface.Terminal //TODO: this is region specific properties, should be refactored for multi-region rendering currentRegion int @@ -92,13 +94,14 @@ type MapEngineTest struct { debugVisLevel int } -func CreateMapEngineTest(currentRegion int, levelPreset int) *MapEngineTest { +func CreateMapEngineTest(currentRegion int, levelPreset int, term d2interface.Terminal) *MapEngineTest { result := &MapEngineTest{ currentRegion: currentRegion, levelPreset: levelPreset, fileIndex: 0, regionSpec: RegionSpec{}, filesCount: 0, + terminal: term, } result.gameState = d2player.CreateTestGameState() return result @@ -148,7 +151,7 @@ func (met *MapEngineTest) OnLoad(loading d2screen.LoadingState) { loading.Progress(0.2) met.mapEngine = d2mapengine.CreateMapEngine() loading.Progress(0.5) - met.mapRenderer = d2maprenderer.CreateMapRenderer(met.mapEngine) + met.mapRenderer = d2maprenderer.CreateMapRenderer(met.mapEngine, met.terminal) loading.Progress(0.7) met.LoadRegionByIndex(met.currentRegion, met.levelPreset, met.fileIndex) } diff --git a/d2game/d2gamescreen/select_hero_class.go b/d2game/d2gamescreen/select_hero_class.go index 089bcdb6..849ae310 100644 --- a/d2game/d2gamescreen/select_hero_class.go +++ b/d2game/d2gamescreen/select_hero_class.go @@ -69,6 +69,7 @@ type SelectHeroClass struct { connectionType d2clientconnectiontype.ClientConnectionType connectionHost string audioProvider d2interface.AudioProvider + terminal d2interface.Terminal } func CreateSelectHeroClass(audioProvider d2interface.AudioProvider, @@ -434,14 +435,14 @@ func (v *SelectHeroClass) OnUnload() error { } func (v *SelectHeroClass) onExitButtonClicked() { - d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, v.connectionType, v.connectionHost)) + d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, v.connectionType, v.connectionHost, v.terminal)) } func (v *SelectHeroClass) onOkButtonClicked() { gameState := d2player.CreatePlayerState(v.heroNameTextbox.GetText(), v.selectedHero, *d2datadict.CharStats[v.selectedHero], v.hardcoreCheckbox.GetCheckState()) gameClient, _ := d2client.Create(d2clientconnectiontype.Local) gameClient.Open(v.connectionHost, gameState.FilePath) - d2screen.SetNextScreen(CreateGame(v.audioProvider, gameClient)) + d2screen.SetNextScreen(CreateGame(v.audioProvider, gameClient, v.terminal)) } func (v *SelectHeroClass) Render(screen d2render.Surface) error { diff --git a/d2game/d2player/game_controls.go b/d2game/d2player/game_controls.go index 91a01582..12dca5e8 100644 --- a/d2game/d2player/game_controls.go +++ b/d2game/d2player/game_controls.go @@ -7,6 +7,7 @@ import ( "time" "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" @@ -14,7 +15,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2term" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) @@ -82,8 +82,8 @@ const ( rightSkill = ActionableType(iota) ) -func NewGameControls(hero *d2mapentity.Player, mapEngine *d2mapengine.MapEngine, mapRenderer *d2maprenderer.MapRenderer, inputListener InputCallbackListener) *GameControls { - d2term.BindAction("setmissile", "set missile id to summon on right click", func(id int) { +func NewGameControls(hero *d2mapentity.Player, mapEngine *d2mapengine.MapEngine, mapRenderer *d2maprenderer.MapRenderer, inputListener InputCallbackListener, term d2interface.Terminal) *GameControls { + term.BindAction("setmissile", "set missile id to summon on right click", func(id int) { missileID = id }) @@ -117,7 +117,7 @@ func NewGameControls(hero *d2mapentity.Player, mapEngine *d2mapengine.MapEngine, }, } - d2term.BindAction("freecam", "toggle free camera movement", func() { + term.BindAction("freecam", "toggle free camera movement", func() { gc.FreeCam = !gc.FreeCam }) diff --git a/main.go b/main.go index 7e02a480..0e5961db 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + ebiten_input "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input/ebiten" "image" "image/gif" "image/png" @@ -27,7 +28,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" - ebiten_input "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input/ebiten" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render/ebiten" @@ -65,6 +65,8 @@ var singleton struct { captureFrames []*image.RGBA } +var terminal_hack d2interface.Terminal // we need to make this available inside of advance/update/render + func main() { region := kingpin.Arg("region", "Region type id").Int() preset := kingpin.Arg("preset", "Level preset").Int() @@ -81,7 +83,15 @@ func main() { panic(err) } - if err := initialize(audioProvider); err != nil { + + d2input.Initialize(ebiten_input.InputService{}) // TODO d2input singleton must be init before d2term + term, err := d2term.Initialize() + terminal_hack = term // needs to be used in advance, no easy way for that right now + if err != nil { + log.Fatal(err) + } + + if err := initialize(audioProvider, term); err != nil { if os.IsNotExist(err) { run(updateInitError) } @@ -96,15 +106,15 @@ func main() { } if *region == 0 { - d2screen.SetNextScreen(d2gamescreen.CreateMainMenu(audioProvider)) + d2screen.SetNextScreen(d2gamescreen.CreateMainMenu(audioProvider, term)) } else { - d2screen.SetNextScreen(d2gamescreen.CreateMapEngineTest(*region, *preset)) + d2screen.SetNextScreen(d2gamescreen.CreateMapEngineTest(*region, *preset, term)) } run(update) } -func initialize(audioProvider d2interface.AudioProvider) error { +func initialize(audioProvider d2interface.AudioProvider, term d2interface.Terminal) error { singleton.timeScale = 1.0 singleton.lastTime = d2common.Now() singleton.lastScreenAdvance = singleton.lastTime @@ -116,8 +126,6 @@ func initialize(audioProvider d2interface.AudioProvider) error { config := d2config.Get() d2resource.LanguageCode = config.Language - d2input.Initialize(ebiten_input.InputService{}) - renderer, err := ebiten.CreateRenderer() if err != nil { return err @@ -128,60 +136,57 @@ func initialize(audioProvider d2interface.AudioProvider) error { } d2render.SetWindowIcon("d2logo.png") - if err := d2term.Initialize(); err != nil { - return err - } - d2term.BindLogger() - d2term.BindAction("dumpheap", "dumps the heap to pprof/heap.pprof", func() { + term.BindLogger() + term.BindAction("dumpheap", "dumps the heap to pprof/heap.pprof", func() { os.Mkdir("./pprof/", 0755) fileOut, _ := os.Create("./pprof/heap.pprof") pprof.WriteHeapProfile(fileOut) fileOut.Close() }) - d2term.BindAction("fullscreen", "toggles fullscreen", func() { + term.BindAction("fullscreen", "toggles fullscreen", func() { fullscreen := !d2render.IsFullScreen() d2render.SetFullScreen(fullscreen) - d2term.OutputInfo("fullscreen is now: %v", fullscreen) + term.OutputInfo("fullscreen is now: %v", fullscreen) }) - d2term.BindAction("capframe", "captures a still frame", func(path string) { + term.BindAction("capframe", "captures a still frame", func(path string) { singleton.captureState = captureStateFrame singleton.capturePath = path singleton.captureFrames = nil }) - d2term.BindAction("capgifstart", "captures an animation (start)", func(path string) { + term.BindAction("capgifstart", "captures an animation (start)", func(path string) { singleton.captureState = captureStateGif singleton.capturePath = path singleton.captureFrames = nil }) - d2term.BindAction("capgifstop", "captures an animation (stop)", func() { + term.BindAction("capgifstop", "captures an animation (stop)", func() { singleton.captureState = captureStateNone }) - d2term.BindAction("vsync", "toggles vsync", func() { + term.BindAction("vsync", "toggles vsync", func() { vsync := !d2render.GetVSyncEnabled() d2render.SetVSyncEnabled(vsync) - d2term.OutputInfo("vsync is now: %v", vsync) + term.OutputInfo("vsync is now: %v", vsync) }) - d2term.BindAction("fps", "toggle fps counter", func() { + term.BindAction("fps", "toggle fps counter", func() { singleton.showFPS = !singleton.showFPS - d2term.OutputInfo("fps counter is now: %v", singleton.showFPS) + term.OutputInfo("fps counter is now: %v", singleton.showFPS) }) - d2term.BindAction("timescale", "set scalar for elapsed time", func(timeScale float64) { + term.BindAction("timescale", "set scalar for elapsed time", func(timeScale float64) { if timeScale <= 0 { - d2term.OutputError("invalid time scale value") + term.OutputError("invalid time scale value") } else { - d2term.OutputInfo("timescale changed from %f to %f", singleton.timeScale, timeScale) + term.OutputInfo("timescale changed from %f to %f", singleton.timeScale, timeScale) singleton.timeScale = timeScale } }) - d2term.BindAction("quit", "exits the game", func() { + term.BindAction("quit", "exits the game", func() { os.Exit(0) }) - d2term.BindAction("screen-gui", "enters the gui playground screen", func() { + term.BindAction("screen-gui", "enters the gui playground screen", func() { d2screen.SetNextScreen(d2gamescreen.CreateGuiTestMain()) }) - if err := d2asset.Initialize(); err != nil { + if err := d2asset.Initialize(term); err != nil { return err } @@ -268,7 +273,7 @@ func advance(elapsed, current float64) error { return err } - if err := d2term.Advance(elapsed); err != nil { + if err := terminal_hack.Advance(elapsed); err != nil { return err } @@ -294,7 +299,7 @@ func render(target d2render.Surface) error { return err } - if err := d2term.Render(target); err != nil { + if err := terminal_hack.Render(target); err != nil { return err }