package game import ( "fmt" "os" "strings" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" ) func (g *Game) handleNewCharName(sess *net.Session, input string) { name := strings.TrimSpace(input) if sess.PendingChar == "connect" && len(sess.Account.Characters) > 0 { if idx, err := parseChoiceIndex(name); err == nil && idx > 0 && idx <= len(sess.Account.Characters) { sess.PendingChar = "" g.connectCharacter(sess, sess.Account.Characters[idx-1]) return } sess.WriteLine("Invalid choice.") sess.Write("\n> ") return } if name == "" { sess.Write("Character name: ") return } if verr := player.ValidName(name); verr != nil { sess.WriteLine(verr.Error()) sess.Write("Character name: ") return } if g.AccountStore.CharacterExists(name) { sess.WriteLine("A character with that name already exists.") sess.Write("Character name: ") return } p := player.New(name) p.RoomID = g.StartingRoom p.Stats.RecordRoomVisit(g.StartingRoom) if err := g.AccountStore.SaveCharacter(p); err != nil { sess.WriteLine(fmt.Sprintf("Error creating character: %v", err)) sess.State = net.StateMenu g.showMenu(sess) return } acc, err := g.AccountStore.LoadAccount(sess.Account.Name) if err != nil { sess.WriteLine(fmt.Sprintf("Error creating character: %v", err)) sess.State = net.StateMenu g.showMenu(sess) return } acc.Characters = append(acc.Characters, name) g.AccountStore.SaveAccount(acc) sess.Account.Characters = acc.Characters g.connectCharacter(sess, name) if sess.Player != nil { g.runEnterSteps(sess, sess.Player.RoomID) } } func (g *Game) connectCharacter(sess *net.Session, name string) { g.charsMu.Lock() if existing := g.loggedInChars[name]; existing != nil { g.charsMu.Unlock() sess.WriteLine("This character is logged in elsewhere.") sess.State = net.StateMenu g.showMenu(sess) return } p, err := g.AccountStore.LoadCharacter(name) if err != nil { g.charsMu.Unlock() sess.WriteLine(fmt.Sprintf("Error loading character: %v", err)) sess.State = net.StateMenu g.showMenu(sess) return } sess.Player = p sess.State = net.StateGame g.loggedInChars[name] = sess g.charsMu.Unlock() if sess.Account != nil && sess.Account.Options != nil { p.Options = make(map[string]any) for k, v := range sess.Account.Options { p.Options[k] = v } } p.Stats.RecordRoomVisit(p.RoomID) g.World.SeedGroundItems(p.RoomID) g.seedRoomMobs(p.RoomID) g.seedRoomObjects(p.RoomID) if g.Hub != nil { g.Hub.EnterRoom(sess, p.RoomID) } g.doLook(sess) g.checkAggro(sess) // Resume an interrupted LOCKED trigger sequence saved across disconnect. g.resumePendingSequence(sess, p) sess.WritePrompt("> ") } func (g *Game) handleRenameChar(sess *net.Session, input string) { name := strings.TrimSpace(input) if idx, err := parseChoiceIndex(name); err == nil && idx > 0 && idx <= len(sess.Account.Characters) { sess.PendingChar = sess.Account.Characters[idx-1] } else { sess.PendingChar = name } found := false for _, c := range sess.Account.Characters { if c == sess.PendingChar { found = true break } } if !found { sess.WriteLine(fmt.Sprintf("No character named %s on this account.", sess.PendingChar)) g.showMenu(sess) return } sess.State = net.StateRenameCharName sess.WriteLine(fmt.Sprintf("Renaming %s.", sess.PendingChar)) sess.Write("New name: ") } func (g *Game) handleRenameCharName(sess *net.Session, input string) { newName := strings.TrimSpace(input) oldName := sess.PendingChar if newName == "" { sess.Write("New name: ") return } if verr := player.ValidName(newName); verr != nil { sess.WriteLine(verr.Error()) sess.Write("New name: ") return } if newName == oldName { sess.WriteLine("That's already the character's name.") g.showMenu(sess) return } if g.AccountStore.CharacterExists(newName) { sess.WriteLine("A character with that name already exists.") sess.Write("New name: ") return } oldPath := g.AccountStore.CharPath(oldName) newPath := g.AccountStore.CharPath(newName) if err := os.Rename(oldPath, newPath); err != nil { sess.WriteLine(fmt.Sprintf("Error renaming: %v", err)) g.showMenu(sess) return } p, _ := g.AccountStore.LoadCharacter(newName) if p != nil { p.Name = newName g.AccountStore.SaveCharacter(p) } acc, err := g.AccountStore.LoadAccount(sess.Account.Name) if err != nil { sess.WriteLine(fmt.Sprintf("Error renaming: %v", err)) g.showMenu(sess) return } for i, c := range acc.Characters { if c == oldName { acc.Characters[i] = newName break } } g.AccountStore.SaveAccount(acc) sess.Account.Characters = acc.Characters sess.PendingChar = "" sess.WriteLine(fmt.Sprintf("%s renamed to %s.", oldName, newName)) g.showMenu(sess) } func (g *Game) showDeleteConfirm(sess *net.Session) { sess.State = net.StateDeleteChar sess.WriteLine(fmt.Sprintf("\nDelete %s? Type DELETE %s to confirm, or anything else to cancel.", sess.PendingChar, sess.PendingChar)) } func (g *Game) handleDeleteChar(sess *net.Session, input string) { if sess.PendingChar == "" { name := strings.TrimSpace(input) if idx, err := parseChoiceIndex(name); err == nil && idx > 0 && idx <= len(sess.Account.Characters) { sess.PendingChar = sess.Account.Characters[idx-1] } else { sess.PendingChar = name } found := false for _, c := range sess.Account.Characters { if c == sess.PendingChar { found = true break } } if !found { sess.WriteLine(fmt.Sprintf("No character named %s on this account.", sess.PendingChar)) sess.PendingChar = "" g.showMenu(sess) return } g.showDeleteConfirm(sess) return } input = strings.TrimSpace(input) expected := "DELETE " + sess.PendingChar if strings.ToUpper(input) != strings.ToUpper(expected) { sess.WriteLine("Delete cancelled.") sess.PendingChar = "" g.showMenu(sess) return } charPath := g.AccountStore.CharPath(sess.PendingChar) if err := os.Remove(charPath); err != nil { sess.WriteLine(fmt.Sprintf("Error deleting: %v", err)) sess.PendingChar = "" g.showMenu(sess) return } acc, err := g.AccountStore.LoadAccount(sess.Account.Name) if err != nil { sess.WriteLine(fmt.Sprintf("Error deleting: %v", err)) sess.PendingChar = "" g.showMenu(sess) return } var newChars []string for _, c := range acc.Characters { if c != sess.PendingChar { newChars = append(newChars, c) } } acc.Characters = newChars g.AccountStore.SaveAccount(acc) sess.Account.Characters = newChars sess.WriteLine(fmt.Sprintf("%s has been deleted.", sess.PendingChar)) sess.PendingChar = "" g.showMenu(sess) }