Selaa lähdekoodia

Added Progress Bars for Health and Optional Magic

Currently the percentage shown won't handle no magic, but the progress
bar isn't computed/shown.
Apollo 8 kuukautta sitten
vanhempi
commit
62e43508ea
4 muutettua tiedostoa jossa 185 lisäystä ja 49 poistoa
  1. 31 11
      db.go
  2. 4 3
      go.mod
  3. 8 6
      go.sum
  4. 142 29
      main.go

+ 31 - 11
db.go

@@ -2,9 +2,9 @@ package main
 
 import (
 	"log"
-	"strings"
 	"time"
 
+	"golang.org/x/crypto/bcrypt"
 	"gorm.io/driver/sqlite"
 	"gorm.io/gorm"
 )
@@ -31,13 +31,12 @@ func (u *UserDB) Open(filename string) error {
 
 type User struct {
 	Model
-	BBSName             string
-	BBSNick             string
-	Name                string   `gorm:"unique,collate:nocase"`
-	Faction             *Faction `gorm:"-"`
+	Name                string   `gorm:"uniqueIndex,collate:no_case"`
+	Password            string   // bcrypt
+	Faction             *Faction `gorm:"-"` // Hot loaded in memory
 	FactionId           uint64
-	FactionSO           bool
-	FactionO            bool
+	FactionSO           bool // Faction.Senior_Officer
+	FactionO            bool // Faction.Officer
 	X                   uint64
 	Y                   uint64
 	World               uint32
@@ -45,7 +44,7 @@ type User struct {
 	MaxHealth           uint32
 	Magic               uint32
 	MaxMagic            uint32
-	Class               uint8
+	Class               uint8 // 1=Knight (Cyan), 2=Thief (Brown), 3=Cleric (Green), 4=Wizard (Magenta), 5=Ranger (Red)
 	Level               uint32
 	Xp                  uint64
 	Weapon              uint8
@@ -64,10 +63,9 @@ type User struct {
 	GemBlue             uint32
 }
 
-func (u *UserDB) FindUser(name string) *User {
-	parts := strings.Split(name, "@")
+func (u *UserDB) FindUserByName(name string) *User {
 	var usr *User
-	result := u.DB.First(&usr, &User{BBSName: parts[1], BBSNick: parts[0]})
+	result := u.DB.First(&usr, &User{Name: name})
 	if result.Error != nil {
 		log.Printf("UserDB.FindUser(name='%s') > %v", name, result.Error)
 		return nil
@@ -82,6 +80,28 @@ func (u *UserDB) FindUser(name string) *User {
 	return usr
 }
 
+func (u *User) SetPassword(password string) error {
+	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
+	if err != nil {
+		return err
+	}
+	u.Password = string(hash)
+	return nil
+}
+
+func (u *User) VerifyPassword(password string) error {
+	return bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
+}
+
+func (u *User) PasswordCost() int {
+	cost, err := bcrypt.Cost([]byte(u.Password))
+	if err != nil {
+		log.Printf("User.PasswordCost() > %v", err)
+		return -1
+	}
+	return cost
+}
+
 func (u *UserDB) SaveUser(usr *User) error {
 	result := u.DB.Save(usr)
 	return result.Error

+ 4 - 3
go.mod

@@ -6,6 +6,7 @@ replace git.red-green.com/RedGreen/doorgo => ./doorgo/door
 
 require (
 	git.red-green.com/RedGreen/doorgo v0.0.0-00010101000000-000000000000
+	golang.org/x/crypto v0.25.0
 	gorm.io/driver/sqlite v1.5.6
 	gorm.io/gorm v1.25.11
 )
@@ -14,7 +15,7 @@ require (
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/mattn/go-sqlite3 v1.14.22 // indirect
-	golang.org/x/sys v0.5.0 // indirect
-	golang.org/x/term v0.2.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
+	golang.org/x/term v0.22.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
 )

+ 8 - 6
go.sum

@@ -4,12 +4,14 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
 github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
-golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
+golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
 gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
 gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
 gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=

+ 142 - 29
main.go

@@ -3,7 +3,9 @@ package main
 import (
 	"errors"
 	"fmt"
+	"log"
 	"strings"
+	"time"
 
 	door "git.red-green.com/RedGreen/doorgo"
 )
@@ -23,45 +25,156 @@ func main() {
 		fmt.Printf("UserDB.Open(filename='users.db3') > %v\r\n", err)
 		return
 	}
-	u = udb.FindUser(d.Config.Handle + "@" + d.Config.BBSID)
-	if u != nil {
-		d.WriteS(door.ColorTextS("BRI YEL ON BLA") + "Geode" + door.ColorTextS("WHI ON BLA") + " by " + door.ColorTextS("BRI RED ON BLA") + "Apollo" + door.ColorTextS("BRI BLA ON BLA") + "@" + door.ColorTextS("BRI GRE ON BLA") + "21:1/236" + door.Reset + door.CRNL + door.ColorTextS("BRI CYA ON BLA") + fmt.Sprintf("Welcome back %s%s", door.ColorTextS("BRI WHI ON BLA"), u.Name) + door.CRNL)
-		d.WaitKey(door.Inactivity)
-	} else {
-		u = &User{}
-		d.WriteS(door.ColorTextS("BRI YEL ON BLA") + "Geode" + door.ColorTextS("WHI ON BLA") + " by " + door.ColorTextS("BRI RED ON BLA") + "Apollo" + door.ColorTextS("BRI BLA ON BLA") + "@" + door.ColorTextS("BRI GRE ON BLA") + "21:1/236" + door.Reset + door.CRNL + door.ColorTextS("BRI CYA ON BLA") + "Welcome new user" + door.CRNL)
-	NEW_NAME:
-		for {
-			d.WriteS(door.GotoS(1, 4) + door.Reset + strings.Repeat(" ", door.Width-1))
-			d.WriteS(door.GotoS(1, 3) + door.ColorTextS("BRI YEL ON BLA") + "Your Name: " + door.ColorTextS("BRI WHI ON BLU"))
-			name := d.Input(30)
-			if len(name) == 0 {
+	d.WriteS(door.ColorTextS("BRI YEL ON BLA") + "Geode" + door.ColorTextS("WHI ON BLA") + " by " + door.ColorTextS("BRI RED ON BLA") + "Apollo" + door.ColorTextS("BRI BLA ON BLA") + "@" + door.ColorTextS("BRI GRE ON BLA") + "21:1/236" + door.Reset + door.CRNL)
+LOGIN:
+	for {
+		d.WriteS(door.GotoS(1, 3) + door.Reset + strings.Repeat(" ", door.Width-1) + door.GotoS(1, 4) + strings.Repeat(" ", door.Width-1) + door.GotoS(1, 5) + strings.Repeat(" ", door.Width-1) + door.GotoS(1, 6) + strings.Repeat(" ", door.Width-1) + door.GotoS(1, 7) + strings.Repeat(" ", door.Width-1) + door.GotoS(1, 8) + strings.Repeat(" ", door.Width-1) + door.GotoS(1, 9) + strings.Repeat(" ", door.Width-1))
+		d.WriteS(door.GotoS(1, 3) + door.ColorTextS("BRI YEL ON BLA") + "Login:    " + door.ColorTextS("BRI WHI ON BLU"))
+		name := d.Input(30)
+		if len(name) == 0 {
+			d.WriteS(door.Reset + door.CRNL + "Goodbye!")
+			return
+		}
+		u = udb.FindUserByName(name)
+		if u == nil {
+			u = &User{Name: name}
+			// New User, So get their password and their class of choice
+			d.WriteS(door.GotoS(1, 4) + door.ColorTextS("BRI YEL ON BLA") + "Password: " + door.ColorTextS("BLU ON BLU"))
+			pwd := d.Input(30)
+			if len(pwd) == 0 {
 				d.WriteS(door.Reset + door.CRNL + "Goodbye!")
 				return
 			}
-			d.WriteS(door.Reset + door.CRNL + door.ColorTextS("BRI WHI ON BLA") + name + door.ColorTextS("BRI GRE ON BLA") + " ok?" + door.Reset + " ")
+			err = u.SetPassword(pwd)
+			if err != nil {
+				d.WriteS(door.Reset + door.CRNL + "Err: " + err.Error() + door.CRNL)
+				_, _, err = d.WaitKey(door.Inactivity)
+				if err != nil {
+					if errors.Is(err, door.ErrDisconnected) {
+						return
+					}
+				}
+				continue LOGIN
+			}
+			d.WriteS(door.Reset + door.CRNL)
+			bb := door.ColorTextS("BRI BLA ON BLA")
+			d.WriteS(bb + "[" + door.ColorTextS("BRI CYA ON BLA") + "K" + bb + "] " + door.ColorTextS("CYA ON BLA") + "Knight" + door.Reset + door.CRNL)
+			d.WriteS(bb + "[" + door.ColorTextS("BRO ON BLA") + "T" + bb + "] " + door.ColorTextS("BRO ON BLA") + "Thief" + door.Reset + door.CRNL)
+			d.WriteS(bb + "[" + door.ColorTextS("BRI GRE ON BLA") + "C" + bb + "] " + door.ColorTextS("GRE ON BLA") + "Cleric" + door.Reset + door.CRNL)
+			d.WriteS(bb + "[" + door.ColorTextS("BRI MAG ON BLA") + "W" + bb + "] " + door.ColorTextS("MAG ON BLA") + "Wizard" + door.Reset + door.CRNL)
+			d.WriteS(bb + "[" + door.ColorTextS("BRI RED ON BLA") + "R" + bb + "] " + door.ColorTextS("RED ON BLA") + "Ranger" + door.Reset + door.CRNL)
 			r, _, err := d.WaitKey(door.Inactivity)
 			if err != nil {
 				if errors.Is(err, door.ErrDisconnected) {
 					return
-				} else {
-					continue NEW_NAME
 				}
+				continue LOGIN
 			}
-			if r == 'y' || r == 'Y' {
-				u.BBSName = d.Config.BBSID
-				u.BBSNick = d.Config.Handle
-				u.Name = name
-				err = udb.SaveUser(u)
-				if err != nil {
-					d.WriteS(fmt.Sprintf("%sErr: %v", door.ColorTextS("BRI RED ON BLA"), err) + door.CRNL)
-					d.WaitKey(door.Inactivity)
-					return
-				} else {
-					d.WriteS(door.CRNL + door.Reset + "Saved!" + door.CRNL)
-				}
-				break NEW_NAME
+			switch r {
+			case 'k', 'K':
+				u.Class = 1
+				u.Health = 30
+				u.MaxHealth = 30
+				u.MaxMagic = 0
+				u.Magic = 0
+				u.Gold = 200
+			case 't', 'T':
+				u.Class = 2
+				u.Health = 20
+				u.MaxHealth = 20
+				u.MaxMagic = 0
+				u.Magic = 0
+				u.Gold = 250 // Thieves get bonus gold (+50)
+			case 'c', 'C':
+				u.Class = 3
+				u.Health = 25
+				u.MaxHealth = 25
+				u.MaxMagic = 7
+				u.Magic = 7
+				u.Gold = 200
+			case 'w', 'W':
+				u.Class = 4
+				u.Health = 20
+				u.MaxHealth = 20
+				u.MaxMagic = 12
+				u.Magic = 12
+				u.Gold = 200
+			case 'r', 'R':
+				u.Class = 5
+				u.Health = 20
+				u.MaxHealth = 20
+				u.MaxMagic = 0
+				u.Magic = 0
+				u.Gold = 200
+			default:
+				continue LOGIN
+			}
+			u.Level = 1
+			err = udb.SaveUser(u)
+			if err != nil {
+				log.Printf("u = %#v", u)
+				log.Printf("UserDB.SaveUser(u) > %v", err)
+				d.WriteS(door.Reset + door.CRNL + "Err: " + err.Error() + door.CRNL)
+				d.WaitKey(door.Inactivity)
+				return
+			}
+			break LOGIN
+		} else {
+			// Existing User, So verify their password
+			d.WriteS(door.GotoS(1, 4) + door.ColorTextS("BRI YEL ON BLA") + "Password: " + door.ColorTextS("BLU ON BLU"))
+			pwd := d.Input(30)
+			if len(pwd) == 0 {
+				d.WriteS(door.Reset + door.CRNL + "Goodbye!")
+				return
+			}
+			if u.VerifyPassword(pwd) != nil {
+				d.WriteS(door.Reset + door.CRNL + door.ColorTextS("BRI RED ON BLA") + "Invalid Username or Password!" + door.Reset + door.CRNL)
+				d.WaitKey(time.Duration(10) * time.Second)
+				u = nil
+				continue LOGIN
+			} else {
+				d.WriteS(door.Reset + door.CRNL)
+				break LOGIN
 			}
 		}
 	}
+	d.WriteS(door.ColorTextS("BRI CYA ON BLA") + "Welcome " + door.ColorTextS("BRI WHI ON BLA") + u.Name + door.ColorTextS("BRI CYA ON BLA") + " the ")
+	switch u.Class {
+	case 1:
+		d.WriteS(door.ColorTextS("CYA ON BLA") + "Knight")
+	case 2:
+		d.WriteS(door.ColorTextS("BRO ON BLA") + "Thief")
+	case 3:
+		d.WriteS(door.ColorTextS("GRE ON BLA") + "Cleric")
+	case 4:
+		d.WriteS(door.ColorTextS("MAG ON BLA") + "Wizard")
+	case 5:
+		d.WriteS(door.ColorTextS("RED ON BLA") + "Ranger")
+	default:
+		d.WriteS(door.ColorTextS("BLI BRI RED ON BLA") + "Unknown")
+	}
+	d.WriteS(door.Reset + door.CRNL)
+	go renderStatusBar()
+	d.WaitKey(door.Inactivity)
+}
+
+func renderStatusBar() {
+	if d.Disconnected || u == nil {
+		return
+	}
+	d.WriteS(door.SavePos + door.GotoS(1, door.Height-1) + door.Reset + strings.Repeat(" ", door.Width-1) + door.RestorePos)
+	d.WriteS(door.SavePos + door.GotoS(1, door.Height) + door.Reset + strings.Repeat(" ", door.Width-1) + door.RestorePos)
+	bb := door.ColorTextS("BRI BLA ON BLA")
+	bar_width := ((door.Width - 3) / 2) - 1
+	hp := int(float64(bar_width) * (float64(u.Health) / float64(u.MaxHealth)))
+	pos_txt := fmt.Sprintf("%-3d%% (%d, %d) %-3d%%", int((float64(u.Health)/float64(u.MaxHealth))*100.0), u.X, u.Y, int((float64(u.Magic)/float64(u.MaxMagic))*100.0))
+	d.WriteS(door.SavePos + door.GotoS((door.Width/2)-(len(pos_txt)/2), door.Height-1) + door.ColorTextS("BRI CYA ON BLA") + pos_txt + door.RestorePos)
+	if u.MaxMagic != 0 {
+		mp := int(float64(bar_width) * (float64(u.Magic) / float64(u.MaxMagic)))
+		log.Printf("HP=%d/%d %d%% (%d) MP=%d/%d %d%% (%d) bar_width=%d", u.Health, u.MaxHealth, int((float64(u.Health)/float64(u.MaxHealth))*100.0), hp, u.Magic, u.MaxMagic, int((float64(u.Magic)/float64(u.MaxMagic))*100.0), mp, bar_width)
+		d.WriteS(door.SavePos + door.GotoS(1, door.Height) + bb + "[" + door.ColorTextS("BRI RED ON BLA") + strings.Repeat(door.BARS.Solid, hp) + strings.Repeat(" ", bar_width-hp) + bb + "] [" + door.ColorTextS("BRI MAG ON BLA") + strings.Repeat(door.BARS.Solid, mp) + strings.Repeat(" ", bar_width-mp) + bb + "]" + door.RestorePos)
+	} else {
+		log.Printf("HP=%d/%d %d%% (%d) bar_width=%d", u.Health, u.MaxHealth, int((float64(u.Health)/float64(u.MaxHealth))*100.0), hp, bar_width)
+		d.WriteS(door.SavePos + door.GotoS(1, door.Height) + bb + "[" + door.ColorTextS("BRI RED ON BLA") + strings.Repeat(door.BARS.Solid, hp) + strings.Repeat(" ", bar_width-hp) + bb + "]" + door.RestorePos)
+	}
 }