2019-12-26 11:13:05 -05:00
|
|
|
package d2term
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"image/color"
|
2020-06-28 21:40:52 -04:00
|
|
|
"log"
|
2019-12-26 11:13:05 -05:00
|
|
|
"math"
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2020-02-01 21:06:22 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
2020-06-28 21:40:52 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
|
|
|
|
2020-01-31 23:18:11 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
2019-12-26 11:13:05 -05:00
|
|
|
)
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
type TermCategory d2interface.TermCategory
|
|
|
|
|
|
|
|
const (
|
|
|
|
TermCategoryNone = TermCategory(d2interface.TermCategoryNone)
|
|
|
|
TermCategoryInfo = TermCategory(d2interface.TermCategoryInfo)
|
|
|
|
TermCategoryWarning = TermCategory(d2interface.TermCategoryWarning)
|
|
|
|
TermCategoryError = TermCategory(d2interface.TermCategoryError)
|
|
|
|
)
|
2019-12-26 11:13:05 -05:00
|
|
|
const (
|
|
|
|
termCharWidth = 6
|
|
|
|
termCharHeight = 16
|
|
|
|
termRowCount = 24
|
|
|
|
termRowCountMax = 32
|
|
|
|
termColCountMax = 128
|
|
|
|
termAnimLength = 0.5
|
|
|
|
)
|
|
|
|
|
|
|
|
type termVis int
|
|
|
|
|
|
|
|
const (
|
|
|
|
termVisHidden termVis = iota
|
|
|
|
termVisShowing
|
|
|
|
termVisShown
|
|
|
|
termVisHiding
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2020-02-01 21:51:49 -05:00
|
|
|
termBgColor = color.RGBA{R: 0x2e, G: 0x34, B: 0x36, A: 0xb0}
|
|
|
|
termFgColor = color.RGBA{R: 0x55, G: 0x57, B: 0x53, A: 0xb0}
|
|
|
|
termInfoColor = color.RGBA{R: 0x34, G: 0x65, B: 0xa4, A: 0xb0}
|
|
|
|
termWarningColor = color.RGBA{R: 0xfc, G: 0xe9, B: 0x4f, A: 0xb0}
|
|
|
|
termErrorColor = color.RGBA{R: 0xcc, A: 0xb0}
|
2019-12-26 11:13:05 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type termHistroyEntry struct {
|
|
|
|
text string
|
2020-06-28 21:40:52 -04:00
|
|
|
category d2interface.TermCategory
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
type termActionEntry struct {
|
|
|
|
action interface{}
|
|
|
|
description string
|
|
|
|
}
|
|
|
|
|
|
|
|
type terminal struct {
|
|
|
|
outputHistory []termHistroyEntry
|
|
|
|
outputIndex int
|
|
|
|
|
|
|
|
command string
|
|
|
|
commandHistory []string
|
|
|
|
commandIndex int
|
|
|
|
|
|
|
|
lineCount int
|
|
|
|
visState termVis
|
|
|
|
visAnim float64
|
|
|
|
|
|
|
|
actions map[string]termActionEntry
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) Advance(elapsed float64) error {
|
2019-12-26 11:13:05 -05:00
|
|
|
switch t.visState {
|
|
|
|
case termVisShowing:
|
|
|
|
t.visAnim = math.Min(1.0, t.visAnim+elapsed/termAnimLength)
|
|
|
|
if t.visAnim == 1.0 {
|
|
|
|
t.visState = termVisShown
|
|
|
|
}
|
|
|
|
case termVisHiding:
|
|
|
|
t.visAnim = math.Max(0.0, t.visAnim-elapsed/termAnimLength)
|
|
|
|
if t.visAnim == 0.0 {
|
|
|
|
t.visState = termVisHidden
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
if !t.IsVisible() {
|
2019-12-26 11:13:05 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *terminal) OnKeyDown(event d2input.KeyEvent) bool {
|
2020-02-02 21:26:08 -05:00
|
|
|
if t.visState == termVisHiding || t.visState == termVisHidden && event.Key == d2input.KeyGraveAccent {
|
2020-06-28 21:40:52 -04:00
|
|
|
t.Show()
|
2020-02-02 21:26:08 -05:00
|
|
|
return true
|
|
|
|
}
|
2020-01-25 23:28:21 -05:00
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
if !t.IsVisible() {
|
2020-02-01 14:35:55 -05:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if event.Key == d2input.KeyGraveAccent {
|
2020-06-28 21:40:52 -04:00
|
|
|
t.Hide()
|
2020-01-25 23:28:21 -05:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if event.Key == d2input.KeyEscape {
|
|
|
|
t.command = ""
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
maxoutputIndex := d2common.MaxInt(0, len(t.outputHistory)-t.lineCount)
|
2020-02-02 21:26:08 -05:00
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
if event.Key == d2input.KeyHome {
|
2020-06-28 21:40:52 -04:00
|
|
|
t.outputIndex = maxoutputIndex
|
2020-01-25 23:28:21 -05:00
|
|
|
return true
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
if event.Key == d2input.KeyEnd {
|
2019-12-26 11:13:05 -05:00
|
|
|
t.outputIndex = 0
|
2020-01-25 23:28:21 -05:00
|
|
|
return true
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
if event.Key == d2input.KeyPageUp {
|
2020-06-28 21:40:52 -04:00
|
|
|
if t.outputIndex += t.lineCount; t.outputIndex >= maxoutputIndex {
|
|
|
|
t.outputIndex = maxoutputIndex
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
2020-01-25 23:28:21 -05:00
|
|
|
|
|
|
|
return true
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
if event.Key == d2input.KeyPageDown {
|
2019-12-26 11:13:05 -05:00
|
|
|
if t.outputIndex -= t.lineCount; t.outputIndex < 0 {
|
|
|
|
t.outputIndex = 0
|
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
return true
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
if event.Key == d2input.KeyUp {
|
|
|
|
if event.KeyMod == d2input.KeyModControl {
|
2020-02-01 21:06:22 -05:00
|
|
|
t.lineCount = d2common.MaxInt(0, t.lineCount-1)
|
2019-12-26 11:13:05 -05:00
|
|
|
} else if len(t.commandHistory) > 0 {
|
|
|
|
t.command = t.commandHistory[t.commandIndex]
|
|
|
|
if t.commandIndex == 0 {
|
|
|
|
t.commandIndex = len(t.commandHistory) - 1
|
|
|
|
} else {
|
|
|
|
t.commandIndex--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
return true
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
if event.Key == d2input.KeyDown && event.KeyMod == d2input.KeyModControl {
|
2020-02-01 21:06:22 -05:00
|
|
|
t.lineCount = d2common.MinInt(t.lineCount+1, termRowCountMax)
|
2020-01-25 23:28:21 -05:00
|
|
|
return true
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
if event.Key == d2input.KeyEnter && len(t.command) > 0 {
|
2019-12-26 11:13:05 -05:00
|
|
|
var commandHistory []string
|
|
|
|
for _, command := range t.commandHistory {
|
|
|
|
if command != t.command {
|
|
|
|
commandHistory = append(commandHistory, command)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
t.commandHistory = append(commandHistory, t.command)
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
t.Output(t.command)
|
|
|
|
if err := t.Execute(t.command); err != nil {
|
|
|
|
t.OutputError(err.Error())
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
t.commandIndex = len(t.commandHistory) - 1
|
|
|
|
t.command = ""
|
2020-01-25 23:28:21 -05:00
|
|
|
|
|
|
|
return true
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
if event.Key == d2input.KeyBackspace && len(t.command) > 0 {
|
|
|
|
t.command = t.command[:len(t.command)-1]
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-02-01 14:35:55 -05:00
|
|
|
return true
|
2020-01-25 23:28:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *terminal) OnKeyChars(event d2input.KeyCharsEvent) bool {
|
2020-06-28 21:40:52 -04:00
|
|
|
if !t.IsVisible() {
|
2020-02-02 21:26:08 -05:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
var handled bool
|
|
|
|
for _, c := range event.Chars {
|
2019-12-26 11:13:05 -05:00
|
|
|
if c != '`' {
|
|
|
|
t.command += string(c)
|
2020-01-25 23:28:21 -05:00
|
|
|
handled = true
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
return handled
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-06-29 00:41:58 -04:00
|
|
|
func (t *terminal) Render(surface d2interface.Surface) error {
|
2020-06-28 21:40:52 -04:00
|
|
|
if !t.IsVisible() {
|
2019-12-26 11:13:05 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
totalWidth, _ := surface.GetSize()
|
|
|
|
outputHeight := t.lineCount * termCharHeight
|
|
|
|
totalHeight := outputHeight + termCharHeight
|
|
|
|
|
|
|
|
offset := -int((1.0 - easeInOut(t.visAnim)) * float64(totalHeight))
|
|
|
|
surface.PushTranslation(0, offset)
|
|
|
|
|
|
|
|
surface.DrawRect(totalWidth, outputHeight, termBgColor)
|
|
|
|
|
|
|
|
for i := 0; i < t.lineCount; i++ {
|
|
|
|
historyIndex := len(t.outputHistory) - i - t.outputIndex - 1
|
|
|
|
if historyIndex < 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
historyEntry := t.outputHistory[historyIndex]
|
|
|
|
surface.PushTranslation(termCharWidth*2, outputHeight-(i+1)*termCharHeight)
|
|
|
|
surface.DrawText(historyEntry.text)
|
|
|
|
surface.PushTranslation(-termCharWidth*2, 0)
|
|
|
|
switch historyEntry.category {
|
2020-06-28 21:40:52 -04:00
|
|
|
case d2interface.TermCategoryInfo:
|
2019-12-26 11:13:05 -05:00
|
|
|
surface.DrawRect(termCharWidth, termCharHeight, termInfoColor)
|
2020-06-28 21:40:52 -04:00
|
|
|
case d2interface.TermCategoryWarning:
|
2019-12-26 11:13:05 -05:00
|
|
|
surface.DrawRect(termCharWidth, termCharHeight, termWarningColor)
|
2020-06-28 21:40:52 -04:00
|
|
|
case d2interface.TermCategoryError:
|
2019-12-26 11:13:05 -05:00
|
|
|
surface.DrawRect(termCharWidth, termCharHeight, termErrorColor)
|
|
|
|
}
|
|
|
|
surface.Pop()
|
|
|
|
surface.Pop()
|
|
|
|
}
|
|
|
|
|
|
|
|
surface.PushTranslation(0, outputHeight)
|
|
|
|
surface.DrawRect(totalWidth, termCharHeight, termFgColor)
|
|
|
|
surface.DrawText("> " + t.command)
|
|
|
|
surface.Pop()
|
|
|
|
|
|
|
|
surface.Pop()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) Execute(command string) error {
|
2019-12-26 11:13:05 -05:00
|
|
|
params := parseCommand(command)
|
|
|
|
if len(params) == 0 {
|
|
|
|
return errors.New("invalid command")
|
|
|
|
}
|
|
|
|
|
|
|
|
actionName := params[0]
|
|
|
|
actionParams := params[1:]
|
|
|
|
|
|
|
|
actionEntry, ok := t.actions[actionName]
|
|
|
|
if !ok {
|
|
|
|
return errors.New("action not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
actionType := reflect.TypeOf(actionEntry.action)
|
|
|
|
if actionType.Kind() != reflect.Func {
|
|
|
|
return errors.New("action is not a function")
|
|
|
|
}
|
|
|
|
if len(actionParams) != actionType.NumIn() {
|
|
|
|
return errors.New("action requires different argument count")
|
|
|
|
}
|
|
|
|
|
|
|
|
var paramValues []reflect.Value
|
|
|
|
for i := 0; i < actionType.NumIn(); i++ {
|
|
|
|
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 err
|
|
|
|
}
|
|
|
|
paramValues = append(paramValues, reflect.ValueOf(int(value)))
|
|
|
|
case reflect.Uint:
|
|
|
|
value, err := strconv.ParseUint(actionParam, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
paramValues = append(paramValues, reflect.ValueOf(uint(value)))
|
|
|
|
case reflect.Float64:
|
|
|
|
value, err := strconv.ParseFloat(actionParam, 64)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
paramValues = append(paramValues, reflect.ValueOf(value))
|
|
|
|
case reflect.Bool:
|
|
|
|
value, err := strconv.ParseBool(actionParam)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
paramValues = append(paramValues, reflect.ValueOf(value))
|
|
|
|
default:
|
|
|
|
return errors.New("action has unsupported arguments")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
actionValue := reflect.ValueOf(actionEntry.action)
|
|
|
|
actionReturnValues := actionValue.Call(paramValues)
|
|
|
|
|
|
|
|
if actionReturnValueCount := len(actionReturnValues); actionReturnValueCount > 0 {
|
2020-06-28 21:40:52 -04:00
|
|
|
t.OutputInfo("function returned %d values:", actionReturnValueCount)
|
2019-12-26 11:13:05 -05:00
|
|
|
for _, actionReturnValue := range actionReturnValues {
|
2020-06-28 21:40:52 -04:00
|
|
|
t.OutputInfo("%v: %s", actionReturnValue.Interface(), actionReturnValue.String())
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) OutputRaw(text string, category d2interface.TermCategory) {
|
2019-12-26 11:13:05 -05:00
|
|
|
var line string
|
|
|
|
for _, word := range strings.Split(text, " ") {
|
|
|
|
if len(line) > 0 {
|
|
|
|
line += " "
|
|
|
|
}
|
|
|
|
|
|
|
|
lineLength := len(line)
|
|
|
|
wordLength := len(word)
|
|
|
|
|
|
|
|
if lineLength+wordLength >= termColCountMax {
|
|
|
|
t.outputHistory = append(t.outputHistory, termHistroyEntry{line, category})
|
|
|
|
line = word
|
|
|
|
} else {
|
|
|
|
line += word
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
t.outputHistory = append(t.outputHistory, termHistroyEntry{line, category})
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) Output(format string, params ...interface{}) {
|
|
|
|
t.OutputRaw(fmt.Sprintf(format, params...), d2interface.TermCategoryNone)
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) OutputInfo(format string, params ...interface{}) {
|
|
|
|
t.OutputRaw(fmt.Sprintf(format, params...), d2interface.TermCategoryInfo)
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) OutputWarning(format string, params ...interface{}) {
|
|
|
|
t.OutputRaw(fmt.Sprintf(format, params...), d2interface.TermCategoryWarning)
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) OutputError(format string, params ...interface{}) {
|
|
|
|
t.OutputRaw(fmt.Sprintf(format, params...), d2interface.TermCategoryError)
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) OutputClear() {
|
2019-12-26 11:13:05 -05:00
|
|
|
t.outputHistory = nil
|
|
|
|
t.outputIndex = 0
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) IsVisible() bool {
|
2019-12-26 11:13:05 -05:00
|
|
|
return t.visState != termVisHidden
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) Hide() {
|
2019-12-26 11:13:05 -05:00
|
|
|
if t.visState != termVisHidden {
|
|
|
|
t.visState = termVisHiding
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) Show() {
|
2019-12-26 11:13:05 -05:00
|
|
|
if t.visState != termVisShown {
|
|
|
|
t.visState = termVisShowing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) BindAction(name, description string, action interface{}) error {
|
2019-12-26 11:13:05 -05:00
|
|
|
actionType := reflect.TypeOf(action)
|
|
|
|
if actionType.Kind() != reflect.Func {
|
|
|
|
return errors.New("action is not a function")
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < actionType.NumIn(); i++ {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) BindLogger() {
|
|
|
|
log.SetOutput(&terminalLogger{writer: log.Writer(), terminal: t})
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
func (t *terminal) UnbindAction(name string) error {
|
|
|
|
delete(t.actions, name)
|
|
|
|
return nil
|
2019-12-26 11:13:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func easeInOut(t float64) float64 {
|
|
|
|
t *= 2
|
|
|
|
if t < 1 {
|
|
|
|
return 0.5 * t * t * t * t
|
|
|
|
} else {
|
|
|
|
t -= 2
|
|
|
|
return -0.5 * (t*t*t*t - 2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseCommand(command string) []string {
|
|
|
|
var (
|
|
|
|
quoted bool
|
|
|
|
escape bool
|
|
|
|
param string
|
|
|
|
params []string
|
|
|
|
)
|
|
|
|
|
|
|
|
for _, c := range command {
|
|
|
|
switch c {
|
|
|
|
case '"':
|
|
|
|
if escape {
|
|
|
|
param += string(c)
|
|
|
|
escape = false
|
|
|
|
} else {
|
|
|
|
quoted = !quoted
|
|
|
|
}
|
|
|
|
case ' ':
|
|
|
|
if quoted {
|
|
|
|
param += string(c)
|
|
|
|
} else if len(param) > 0 {
|
|
|
|
params = append(params, param)
|
|
|
|
param = ""
|
|
|
|
}
|
|
|
|
case '\\':
|
|
|
|
if escape {
|
|
|
|
param += string(c)
|
|
|
|
escape = false
|
|
|
|
} else {
|
|
|
|
escape = true
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
param += string(c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(param) > 0 {
|
|
|
|
params = append(params, param)
|
|
|
|
}
|
|
|
|
|
|
|
|
return params
|
|
|
|
}
|
2020-06-28 21:40:52 -04:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|