Sfoglia il codice sorgente

Working menu and demo.

Bar is also working, but commented out because it takes awhile
to run.
Steve Thielemann 3 anni fa
parent
commit
fec8c86bd4
6 ha cambiato i file con 586 aggiunte e 196 eliminazioni
  1. 153 0
      door/bar.go
  2. 15 10
      door/box.go
  3. 0 149
      door/line.go
  4. 185 0
      door/menu.go
  5. 155 0
      door/panel.go
  6. 78 37
      testdoor/testdoor.go

+ 153 - 0
door/bar.go

@@ -0,0 +1,153 @@
+package door
+
+import (
+	"fmt"
+	"strings"
+)
+
+type BarStyle int
+
+const (
+	SOLID BarStyle = iota
+	HALF_STEP
+	GRADIENT
+	PERCENTAGE
+	PERCENT_SPACE
+)
+
+type BarRange struct {
+	Percent int64
+	Color   string
+}
+
+type BarLine struct {
+	Width      int
+	Style      BarStyle
+	Percent    int64 // percentage * 100
+	ColorRange []BarRange
+	UpdateP    func() int64
+	Line
+}
+
+func (bl *BarLine) CheckRange() {
+	if len(bl.ColorRange) != 0 {
+		// Ok, there is a color range.  Get checking
+
+		for _, br := range bl.ColorRange {
+			if bl.Percent <= br.Percent {
+				bl.DefaultColor = br.Color
+				break
+			}
+		}
+	}
+}
+
+func (bl *BarLine) Output() string {
+	var output string
+	var step_width int64
+
+	if bl.UpdateP != nil {
+		bl.Percent = bl.UpdateP()
+	}
+
+	bl.CheckRange()
+
+	switch bl.Style {
+	case SOLID, PERCENTAGE, PERCENT_SPACE:
+		step_width = int64(100 * 100 / bl.Width)
+		var steps int = int(bl.Percent / step_width)
+
+		if Unicode {
+			output += strings.Repeat("\u2588", steps)
+		} else {
+			output += strings.Repeat("\xdb", steps)
+		}
+		// This will work, because we aren't trying to len(output) with unicode.
+		output += strings.Repeat(" ", int(bl.Width-steps))
+
+		if bl.Style == PERCENTAGE || bl.Style == PERCENT_SPACE {
+			percent := fmt.Sprintf("%d", bl.Percent/100)
+
+			var pos int = bl.Width/2 - 1
+			if percent != "100" {
+				percent += "%"
+				if len(percent) < 3 {
+					percent = " " + percent
+				}
+			}
+
+			if bl.Style == PERCENT_SPACE {
+				percent = " " + percent + " "
+				pos--
+			}
+			// to process/slice the string (with unicode) do this:
+			// convert to []rune, slice that, convert back to string
+			//
+			// sliceable := []rune(output)
+			// newString := string(sliceable[:5]) + " new content " + (sliceable[10:])
+			// fmt.Printf("%d %d [%s] %d [%s]\n", bl.Width, pos, percent, len(output), output)
+
+			if Unicode {
+				runes := []rune(output)
+				output = string(runes[:pos]) + percent + string(runes[pos+len(percent):])
+			} else {
+				output = output[:pos] + percent + output[pos+len(percent):]
+			}
+		}
+	case HALF_STEP:
+		step_width = int64(100 * 100 / bl.Width)
+		var steps int = int(bl.Percent * 2 / step_width)
+
+		if Unicode {
+			output += strings.Repeat("\u2588", steps/2)
+		} else {
+			output += strings.Repeat("\xdb", steps/2)
+		}
+		if steps%1 == 1 {
+			if Unicode {
+				output += "\u258c"
+			} else {
+				output += "\xdd"
+			}
+			steps++
+		}
+		output += strings.Repeat(" ", bl.Width-(steps/2))
+
+	case GRADIENT:
+		step_width = int64(100 * 100 / bl.Width)
+		var steps int = int(bl.Percent * 4 / step_width)
+		if Unicode {
+			output += strings.Repeat("\u2588", steps/4)
+		} else {
+			output += strings.Repeat("\xb0", steps/4)
+		}
+		if steps%4 != 0 {
+			switch steps % 4 {
+			case 1:
+				if Unicode {
+					output += "\u2591"
+				} else {
+					output += "\xb0"
+				}
+			case 2:
+				if Unicode {
+					output += "\u2592"
+				} else {
+					output += "\xb1"
+				}
+			case 3:
+				if Unicode {
+					output += "\u2593"
+				} else {
+					output += "\xb2"
+				}
+			}
+			for steps%4 != 0 {
+				steps++
+			}
+		}
+		output += strings.Repeat(" ", bl.Width-(steps/4))
+	}
+
+	return bl.DefaultColor + output
+}

+ 15 - 10
door/box.go

@@ -13,7 +13,12 @@ type BoxStyle struct {
 	middle_right string
 }
 
-var boxes = [4]BoxStyle{
+// FUTURE:
+// Once we detect CP437 or Unicode, set BOXES to point to
+// BOXES_CP437 or BOXES_UNICODE.  That'll save us from having
+// to if Unicode when building boxes.  ;)
+
+var BOXES = [4]BoxStyle{
 	/*
 		┌──┐
 		│  │
@@ -43,7 +48,7 @@ var boxes = [4]BoxStyle{
 	*/
 	BoxStyle{"\xd6", "\xb7", "\xc4", "\xba", "\xd3", "\xbd", "\xc7", "\xb6"}}
 
-var unicode_boxes = []BoxStyle{
+var BOXES_UNICODE = []BoxStyle{
 	BoxStyle{"\u250c", "\u2510", "\u2500", "\u2502", "\u2514", "\u2518", "\u251c", "\u2524"},
 	BoxStyle{"\u2554", "\u2557", "\u2550", "\u2551", "\u255a", "\u255d", "\u2560", "\u2563"},
 	BoxStyle{"\u2553", "\u2556", "\u2500", "\u2551", "\u2559", "\u255c", "\u255f", "\u2562"},
@@ -58,9 +63,9 @@ type Box struct {
 func (b *Box) Top() string {
 	var style *BoxStyle
 	if Unicode {
-		style = &unicode_boxes[b.Style]
+		style = &BOXES_UNICODE[b.Style]
 	} else {
-		style = &boxes[b.Style]
+		style = &BOXES[b.Style]
 	}
 	return style.top_left + strings.Repeat(style.top, b.Width) + style.top_right
 }
@@ -68,9 +73,9 @@ func (b *Box) Top() string {
 func (b *Box) Row(text string) string {
 	var style *BoxStyle
 	if Unicode {
-		style = &unicode_boxes[b.Style]
+		style = &BOXES_UNICODE[b.Style]
 	} else {
-		style = &boxes[b.Style]
+		style = &BOXES[b.Style]
 	}
 	return style.side + text + style.side
 }
@@ -78,9 +83,9 @@ func (b *Box) Row(text string) string {
 func (b *Box) Middle() string {
 	var style *BoxStyle
 	if Unicode {
-		style = &unicode_boxes[b.Style]
+		style = &BOXES_UNICODE[b.Style]
 	} else {
-		style = &boxes[b.Style]
+		style = &BOXES[b.Style]
 	}
 	return style.middle_left + strings.Repeat(style.top, b.Width) + style.middle_right
 }
@@ -88,9 +93,9 @@ func (b *Box) Middle() string {
 func (b *Box) Bottom() string {
 	var style *BoxStyle
 	if Unicode {
-		style = &unicode_boxes[b.Style]
+		style = &BOXES_UNICODE[b.Style]
 	} else {
-		style = &boxes[b.Style]
+		style = &BOXES[b.Style]
 	}
 	return style.bottom_left + strings.Repeat(style.top, b.Width) + style.bottom_right
 }

+ 0 - 149
door/line.go

@@ -1,8 +1,6 @@
 package door
 
 import (
-	"fmt"
-	"strings"
 	"unicode"
 )
 
@@ -59,150 +57,3 @@ func RenderBlueYellow(text string) string {
 	}
 	return output
 }
-
-type BarStyle int
-
-const (
-	SOLID BarStyle = iota
-	HALF_STEP
-	GRADIENT
-	PERCENTAGE
-	PERCENT_SPACE
-)
-
-type BarRange struct {
-	Percent int64
-	Color   string
-}
-
-type BarLine struct {
-	Width      int
-	Style      BarStyle
-	Percent    int64 // percentage * 100
-	ColorRange []BarRange
-	UpdateP    func() int64
-	Line
-}
-
-func (bl *BarLine) CheckRange() {
-	if len(bl.ColorRange) != 0 {
-		// Ok, there is a color range.  Get checking
-
-		for _, br := range bl.ColorRange {
-			if bl.Percent <= br.Percent {
-				bl.DefaultColor = br.Color
-				break
-			}
-		}
-	}
-}
-
-func (bl *BarLine) Output() string {
-	var output string
-	var step_width int64
-
-	if bl.UpdateP != nil {
-		bl.Percent = bl.UpdateP()
-	}
-
-	bl.CheckRange()
-
-	switch bl.Style {
-	case SOLID, PERCENTAGE, PERCENT_SPACE:
-		step_width = int64(100 * 100 / bl.Width)
-		var steps int = int(bl.Percent / step_width)
-
-		if Unicode {
-			output += strings.Repeat("\u2588", steps)
-		} else {
-			output += strings.Repeat("\xdb", steps)
-		}
-		// This will work, because we aren't trying to len(output) with unicode.
-		output += strings.Repeat(" ", int(bl.Width-steps))
-
-		if bl.Style == PERCENTAGE || bl.Style == PERCENT_SPACE {
-			percent := fmt.Sprintf("%d", bl.Percent/100)
-
-			var pos int = bl.Width/2 - 1
-			if percent != "100" {
-				percent += "%"
-				if len(percent) < 3 {
-					percent = " " + percent
-				}
-			}
-
-			if bl.Style == PERCENT_SPACE {
-				percent = " " + percent + " "
-				pos--
-			}
-			// to process/slice the string (with unicode) do this:
-			// convert to []rune, slice that, convert back to string
-			//
-			// sliceable := []rune(output)
-			// newString := string(sliceable[:5]) + " new content " + (sliceable[10:])
-			// fmt.Printf("%d %d [%s] %d [%s]\n", bl.Width, pos, percent, len(output), output)
-
-			if Unicode {
-				runes := []rune(output)
-				output = string(runes[:pos]) + percent + string(runes[pos+len(percent):])
-			} else {
-				output = output[:pos] + percent + output[pos+len(percent):]
-			}
-		}
-	case HALF_STEP:
-		step_width = int64(100 * 100 / bl.Width)
-		var steps int = int(bl.Percent * 2 / step_width)
-
-		if Unicode {
-			output += strings.Repeat("\u2588", steps/2)
-		} else {
-			output += strings.Repeat("\xdb", steps/2)
-		}
-		if steps%1 == 1 {
-			if Unicode {
-				output += "\u258c"
-			} else {
-				output += "\xdd"
-			}
-			steps++
-		}
-		output += strings.Repeat(" ", bl.Width-(steps/2))
-
-	case GRADIENT:
-		step_width = int64(100 * 100 / bl.Width)
-		var steps int = int(bl.Percent * 4 / step_width)
-		if Unicode {
-			output += strings.Repeat("\u2588", steps/4)
-		} else {
-			output += strings.Repeat("\xb0", steps/4)
-		}
-		if steps%4 != 0 {
-			switch steps % 4 {
-			case 1:
-				if Unicode {
-					output += "\u2591"
-				} else {
-					output += "\xb0"
-				}
-			case 2:
-				if Unicode {
-					output += "\u2592"
-				} else {
-					output += "\xb1"
-				}
-			case 3:
-				if Unicode {
-					output += "\u2593"
-				} else {
-					output += "\xb2"
-				}
-			}
-			for steps%4 != 0 {
-				steps++
-			}
-		}
-		output += strings.Repeat(" ", bl.Width-(steps/4))
-	}
-
-	return bl.DefaultColor + output
-}

+ 185 - 0
door/menu.go

@@ -0,0 +1,185 @@
+package door
+
+import (
+	"strings"
+	"unicode"
+)
+
+type MenuOption struct {
+	Ch   rune
+	Text string
+}
+
+type Menu struct {
+	Chosen      int
+	SelectedR   func(string) string
+	UnselectedR func(string) string
+	Options     []rune
+	MenuOptions []MenuOption
+	Panel
+}
+
+func MakeMenuRender(c1, c2, c3, c4 string) func(string) string {
+	f := func(text string) string {
+		var output string
+		var lastColor string
+		option := true
+		for _, c := range text {
+			if option {
+				if c == '[' || c == ']' {
+					if lastColor != c1 {
+						output += c1
+						lastColor = c1
+					}
+					output += string(c)
+					option = (c == '[')
+				} else {
+					if lastColor != c2 {
+						output += c2
+						lastColor = c2
+					}
+					output += string(c)
+				}
+			} else {
+				if unicode.IsUpper(c) {
+					if lastColor != c3 {
+						output += c3
+						lastColor = c3
+					}
+					output += string(c)
+				} else {
+					if lastColor != c4 {
+						output += c4
+						lastColor = c4
+					}
+					output += string(c)
+				}
+			}
+		}
+		return output
+	}
+	return f
+}
+
+func (m *Menu) Build() {
+	// Take MenuOptions and build the Menu
+
+	// Reset
+	m.Lines = make([]Line, 0)
+	m.Options = make([]rune, 0)
+	m.Chosen = 0
+
+	for _, option := range m.MenuOptions {
+		m.Options = append(m.Options, option.Ch)
+		text := "[" + string(option.Ch) + "] " + option.Text + strings.Repeat(" ", m.Width-(4+len(option.Text)))
+		m.Lines = append(m.Lines, Line{Text: text, RenderF: m.UnselectedR})
+	}
+}
+
+func (m *Menu) Choose(d *Door) int {
+	var changed []int
+	updated := true
+	update_and_exit := false
+	blank := ColorText("BLACK")
+
+	for {
+		if updated {
+			for x := range m.Lines {
+				if x == m.Chosen {
+					m.Lines[x].RenderF = m.SelectedR
+				} else {
+					m.Lines[x].RenderF = m.UnselectedR
+				}
+			}
+			if len(changed) == 0 {
+				d.Write(m.Output() + blank)
+			} else {
+				// Update just the lines that have changed
+				for _, line := range changed {
+					d.Write(m.UpdateLine(line))
+				}
+				d.Write(m.GotoEnd() + blank)
+			}
+		}
+
+		if update_and_exit {
+			return m.Chosen + 1
+		}
+
+		updated = false
+
+		var event int = d.WaitKey(Inactivity, 0)
+
+		if event < 0 {
+			return event
+		}
+		previous_choice := m.Chosen
+		changed = make([]int, 0)
+
+		use_numberpad := true
+		for _, option := range m.MenuOptions {
+			if option.Ch == rune('8') || option.Ch == rune('2') {
+				use_numberpad = false
+			}
+		}
+
+		switch event {
+		case '2':
+			if use_numberpad {
+				if m.Chosen > 0 {
+					m.Chosen--
+					updated = true
+				}
+			}
+		case XKEY_UP_ARROW:
+			if m.Chosen > 0 {
+				m.Chosen--
+				updated = true
+			}
+		case '8':
+			if use_numberpad {
+				if m.Chosen < len(m.Lines)-1 {
+					m.Chosen++
+					updated = true
+				}
+
+			}
+		case XKEY_DOWN_ARROW:
+			if m.Chosen < len(m.Lines)-1 {
+				m.Chosen++
+				updated = true
+			}
+		case XKEY_HOME:
+			if m.Chosen != 0 {
+				m.Chosen = 0
+				updated = true
+			}
+		case XKEY_END:
+			if m.Chosen != len(m.Lines)-1 {
+				m.Chosen = len(m.Lines) - 1
+				updated = true
+			}
+		case 0x0d:
+			// use current selection
+			return m.Chosen + 1
+		default:
+			// Is the key in the list of options?
+			for x, option := range m.MenuOptions {
+				if int(option.Ch) == event {
+					if m.Chosen == x {
+						return x + 1
+					}
+				}
+				updated = true
+				m.Chosen = x
+				update_and_exit = true
+			}
+		}
+
+		if previous_choice != m.Chosen {
+			changed = append(changed, previous_choice)
+			changed = append(changed, m.Chosen)
+			updated = true
+		}
+	}
+}

+ 155 - 0
door/panel.go

@@ -0,0 +1,155 @@
+package door
+
+import "strings"
+
+type BorderStyle int
+
+const (
+	NONE BorderStyle = iota
+	SINGLE
+	DOUBLE
+	DOUBLE_SINGLE
+	SINGLE_DOUBLE
+)
+
+type Panel struct {
+	X           int
+	Y           int
+	Width       int
+	Style       BorderStyle
+	BorderColor string
+	Lines       []Line
+	Title       string
+	TitleOffset int
+}
+
+// Output the panel
+func (p *Panel) Output() string {
+	var style int = int(p.Style)
+	var box_style *BoxStyle
+	var output string
+
+	if style > 0 {
+		if Unicode {
+			box_style = &BOXES_UNICODE[style-1]
+		} else {
+			box_style = &BOXES[style-1]
+		}
+	}
+
+	row := p.Y
+	if style > 0 {
+		// Top line / border
+		output += Goto(p.X, row) + p.BorderColor + box_style.top_left
+		if p.Title != "" {
+			output += strings.Repeat(box_style.top, p.TitleOffset) + p.Title
+		}
+		output += strings.Repeat(box_style.top, p.Width-(p.TitleOffset+len(p.Title))) + box_style.top_right
+		row++
+	}
+
+	for _, line := range p.Lines {
+		output += Goto(p.X, row)
+
+		// "Joining" lines code
+
+		// We are not joining lines at this time.
+
+		if style > 0 {
+			output += p.BorderColor + box_style.side
+		}
+		output += line.Output()
+		if style > 0 {
+			output += p.BorderColor + box_style.side
+		}
+		row++
+	}
+
+	if style > 0 {
+		// Bottom / border
+		output += Goto(p.X, row) + p.BorderColor + box_style.bottom_left
+		output += strings.Repeat(box_style.top, p.Width) + box_style.bottom_right
+	}
+	return output
+}
+
+// Output anything that has updated
+func (p *Panel) Update() string {
+	var output string
+	var style int = int(p.Style)
+
+	row := p.Y
+	col := p.X
+	if style > 0 {
+		row++
+		col++
+	}
+
+	for _, line := range p.Lines {
+		if line.Update() {
+			// Yes, line was updated
+			output += Goto(col, row) + line.Output()
+		}
+		row++
+	}
+	return output
+}
+
+// Output the updated line
+func (p *Panel) UpdateLine(index int) string {
+	var output string
+	var style int = int(p.Style)
+
+	row := p.Y + index
+	col := p.X
+	if style > 0 {
+		row++
+		col++
+	}
+	line := &p.Lines[index]
+	output += Goto(col, row) + line.Output()
+	return output
+}
+
+// Goto the end
+func (p *Panel) GotoEnd() string {
+	row := p.Y
+	col := p.X
+	if p.Style > 0 {
+		row++
+		col += 2
+	}
+	row += len(p.Lines)
+	col += p.Width
+
+	return Goto(col, row)
+}
+
+// Is the top line of this style a single line?
+func Single(bs BorderStyle) bool {
+	switch bs {
+	case SINGLE, SINGLE_DOUBLE:
+		return true
+	default:
+		return false
+	}
+}
+
+// Create a spacer line that will be connected maybe to the sides.
+func (p *Panel) Spacer() Line {
+	l := Line{}
+	var pos int
+
+	if Single(p.Style) {
+		pos = 0
+	} else {
+		pos = 1
+	}
+
+	if Unicode {
+		l.Text = strings.Repeat(BOXES[pos].top, p.Width)
+	} else {
+		l.Text = strings.Repeat(BOXES_UNICODE[pos].top, p.Width)
+	}
+	return l
+}

+ 78 - 37
testdoor/testdoor.go

@@ -48,47 +48,88 @@ func main() {
 	name := d.Input(25)
 	d.Write(door.Reset + door.CRNL)
 
-	bar := door.BarLine{Line: door.Line{DefaultColor: door.ColorText("BOLD YELLOW")}, Width: 20, Style: door.HALF_STEP}
-
-	bar2 := door.BarLine{Width: 30, Style: door.PERCENT_SPACE}
-
-	bar2.ColorRange = []door.BarRange{
-		{2500, door.ColorText("RED")},
-		{5000, door.ColorText("BROWN")},
-		{7500, door.ColorText("BOLD YEL")},
-		{9500, door.ColorText("GREEN")},
-		{10100, door.ColorText("BRI GRE")}}
-
-	bar3 := door.BarLine{Width: 15, Style: door.GRADIENT, Line: door.Line{DefaultColor: door.ColorText("CYAN")}}
-
-	var percentage int64
-	bar.UpdateP = pctUpdate(&percentage)
-	bar2.UpdateP = pctUpdate(&percentage)
-	bar3.UpdateP = pctUpdate(&percentage)
-
-	bar_start := door.Goto(3, 15)
-
-	for f := 0; f <= 100; f++ {
-		percentage = int64(f * 100)
-		d.Write(bar_start)
-		bar.Update()
-		d.Write(bar.Output())
-		d.Write("  " + door.Reset)
-		bar2.Update()
-		d.Write(bar2.Output())
-		d.Write(door.Reset + "  ")
-		bar3.Update()
-		d.Write(bar3.Output())
-
-		if d.Disconnected {
-			break
+	if false {
+		bar := door.BarLine{Line: door.Line{DefaultColor: door.ColorText("BOLD YELLOW")}, Width: 20, Style: door.HALF_STEP}
+
+		bar2 := door.BarLine{Width: 30, Style: door.PERCENT_SPACE}
+
+		bar2.ColorRange = []door.BarRange{
+			{2500, door.ColorText("RED")},
+			{5000, door.ColorText("BROWN")},
+			{7500, door.ColorText("BOLD YEL")},
+			{9500, door.ColorText("GREEN")},
+			{10100, door.ColorText("BRI GRE")}}
+
+		bar3 := door.BarLine{Width: 15, Style: door.GRADIENT, Line: door.Line{DefaultColor: door.ColorText("CYAN")}}
+
+		var percentage int64
+		bar.UpdateP = pctUpdate(&percentage)
+		bar2.UpdateP = pctUpdate(&percentage)
+		bar3.UpdateP = pctUpdate(&percentage)
+
+		bar_start := door.Goto(3, 15)
+
+		for f := 0; f <= 100; f++ {
+			percentage = int64(f * 100)
+			d.Write(bar_start)
+			bar.Update()
+			d.Write(bar.Output())
+			d.Write("  " + door.Reset)
+			bar2.Update()
+			d.Write(bar2.Output())
+			d.Write(door.Reset + "  ")
+			bar3.Update()
+			d.Write(bar3.Output())
+
+			if d.Disconnected {
+				break
+			}
+
+			time.Sleep(time.Millisecond * 100)
+			// d.WaitKey(0, 55000)
 		}
 
-		time.Sleep(time.Millisecond * 100)
-		// d.WaitKey(0, 55000)
 	}
 
-	d.Write(door.CRNL)
+	d.Write(door.Reset + door.CRNL + "Press a key to continue...")
+	d.WaitKey(120, 0)
+
+	m := door.Menu{Panel: door.Panel{Width: 25,
+		X:           5,
+		Y:           5,
+		Style:       door.DOUBLE,
+		Title:       "Please Select:",
+		TitleOffset: 3,
+		BorderColor: door.ColorText("BRI CYAN ON BLA")}}
+	m.SelectedR = door.MakeMenuRender(door.ColorText("BOLD CYAN"),
+		door.ColorText("BOLD BLUE"),
+		door.ColorText("BOLD CYAN"),
+		door.ColorText("BOLD BLUE"))
+	m.UnselectedR = door.MakeMenuRender(door.ColorText("BOLD YEL ON BLUE"),
+		door.ColorText("BOLD WHI ON BLUE"),
+		door.ColorText("BOLD YEL ON BLUE"),
+		door.ColorText("BOLD CYAN ON BLUE"))
+	m.MenuOptions = []door.MenuOption{{rune('1'), "Play A Game"},
+		{rune('A'), "Ask BUGZ for Help"},
+		{rune('D'), "Drop to DOS"},
+		{rune('Q'), "Quit"}}
+	m.Build()
+
+	test := door.Line{Text: "[T] Testing Render...", RenderF: m.SelectedR}
+	d.Write(test.Output() + door.Reset + door.CRNL)
+	test.RenderF = m.UnselectedR
+	d.Write(test.Output() + door.Reset + door.CRNL)
+	d.Write(door.Reset + door.CRNL + "Press a key to continue...")
+	d.WaitKey(120, 0)
+
+	d.Write(door.Clrscr)
+
+	_ = m.Choose(&d)
+
+	// d.Write(m.Output())
+	// d.WaitKey(120, 0)
+
+	d.Write(door.Reset + door.CRNL + door.CRNL)
 
 	message = fmt.Sprintf("Returning %s to the BBS..."+door.CRNL, name)
 	d.Write(message)