func (self *Session) getInt(prompt string, min, max int) (int, bool) { for { input := self.getRawUserInput(prompt) if input == "" { return 0, false } val, err := utils.Atoir(input, min, max) if err != nil { self.printError(err.Error()) } else { return val, true } } }
func init() { commands = map[string]*command{ "help": { usage: "/help <command name>", exec: func(self *command, s *Session, arg string) { if arg == "" { s.WriteLine("List of commands:") var names []string for name, command := range commands { if command.alias == "" { names = append(names, name) } } sort.Strings(names) width, height := s.user.GetWindowSize() pages := utils.Paginate(names, width, height/2) for _, page := range pages { s.WriteLine(page) } } else { command, found := commands[arg] if found { command.Usage(s) } else { s.printError("Command not found") } } }, }, "store": { admin: true, exec: func(self *command, s *Session, arg string) { utils.ExecMenu("Store", s, func(menu *utils.Menu) { store := model.StoreIn(s.pc.GetRoomId()) if store != nil { menu.AddAction("r", "Rename", func() bool { name := s.getCleanUserInput("New name: ") if name != "" { store.SetName("Name") } return true }) menu.AddAction("d", "Delete", func() bool { model.DeleteStore(store.GetId()) return true }) } else { menu.AddAction("n", "New Store", func() bool { name := s.getCleanUserInput("Store name: ") if name != "" { model.CreateStore(name, s.pc.GetRoomId()) } return true }) } }) }, }, "loc": cAlias("location"), "location": { admin: false, exec: func(self *command, s *Session, arg string) { s.WriteLine("%v", s.GetRoom().GetLocation()) }, }, "room": { admin: true, exec: func(self *command, s *Session, arg string) { utils.ExecMenu( "Room", s, func(menu *utils.Menu) { menu.AddAction("t", fmt.Sprintf("Title - %s", s.GetRoom().GetTitle()), func() bool { title := s.getRawUserInput("Enter new title: ") if title != "" { s.GetRoom().SetTitle(title) } return true }) menu.AddAction("d", "Description", func() bool { description := s.getRawUserInput("Enter new description: ") if description != "" { s.GetRoom().SetDescription(description) } return true }) menu.AddAction("e", "Exits", func() bool { toggleExitMenu(s) return true }) areaId := s.GetRoom().GetAreaId() areaName := "(None)" if areaId != nil { area := model.GetArea(areaId) areaName = area.GetName() } menu.AddAction("a", fmt.Sprintf("Area - %s", areaName), func() bool { utils.ExecMenu("Change Area", s, func(menu *utils.Menu) { menu.AddAction("n", "None", func() bool { s.GetRoom().SetAreaId(nil) return true }) for i, area := range model.GetAreas(s.currentZone()) { actionText := area.GetName() if area.GetId() == s.GetRoom().GetAreaId() { actionText += "*" } a := area menu.AddActionI(i, actionText, func() bool { s.GetRoom().SetAreaId(a.GetId()) return true }) } }) return true }) }) s.PrintRoom() }, }, "map": { admin: false, exec: func(self *command, s *Session, arg string) { zoneRooms := model.GetRoomsInZone(s.currentZone().GetId()) roomsByLocation := map[types.Coordinate]types.Room{} for _, room := range zoneRooms { roomsByLocation[room.GetLocation()] = room } width, height := s.user.GetWindowSize() height /= 2 width /= 2 // width and height need to be odd numbers so that we keep the current location centered // and we don't go off the edge of the available space width += (width % 2) - 1 height += (height % 2) - 1 builder := newMapBuilder(width, height, 1) builder.setUserRoom(s.GetRoom()) center := s.GetRoom().GetLocation() startX := center.X - (width / 2) endX := center.X + (width / 2) startY := center.Y - (height / 2) endY := center.Y + (height / 2) for y := startY; y <= endY; y++ { for x := startX; x <= endX; x++ { loc := types.Coordinate{X: x, Y: y, Z: center.Z} room := roomsByLocation[loc] if room != nil { // Translate to 0-based coordinates builder.addRoom(room, x-startX, y-startY, 0) } } } s.WriteLine(utils.TrimEmptyRows(builder.toString())) }, }, "zone": { admin: true, usage: "/zone [list|rename <name>|new <name>|delete <name>]", exec: func(self *command, s *Session, arg string) { subcommand, arg := utils.Argify(arg) if subcommand == "" { s.WriteLine("Current zone: " + types.Colorize(types.ColorBlue, s.currentZone().GetName())) } else if subcommand == "list" { s.WriteLineColor(types.ColorBlue, "Zones") s.WriteLineColor(types.ColorBlue, "-----") for _, zone := range model.GetZones() { s.WriteLine(zone.GetName()) } } else if arg != "" { if subcommand == "rename" { zone := model.GetZoneByName(arg) if zone != nil { s.printError("A zone with that name already exists") return } s.currentZone().SetName(arg) } else if subcommand == "new" { newZone, err := model.CreateZone(arg) if err != nil { s.printError(err.Error()) return } newRoom, err := model.CreateRoom(newZone, types.Coordinate{X: 0, Y: 0, Z: 0}) utils.HandleError(err) model.MoveCharacterToRoom(s.pc, newRoom) s.PrintRoom() } else if subcommand == "delete" { zone := model.GetZoneByName(arg) if zone != nil { if zone == s.currentZone() { s.printError("You can't delete the zone you are in") } else { model.DeleteZone(zone.GetId()) s.WriteLine("Zone deleted") } } else { s.printError("Zone not found") } } else { self.Usage(s) } } else { self.Usage(s) } }, }, "b": cAlias("broadcast"), "broadcast": { admin: false, exec: func(self *command, s *Session, arg string) { if arg == "" { s.printError("Nothing to say") } else { model.BroadcastMessage(s.pc, arg) } }, }, "s": cAlias("say"), "say": { admin: false, exec: func(self *command, s *Session, arg string) { if arg == "" { s.printError("Nothing to say") } else { model.Say(s.pc, arg) } }, }, "me": { admin: false, exec: func(self *command, s *Session, arg string) { model.Emote(s.pc, arg) }, }, "w": cAlias("whisper"), "tell": cAlias("whisper"), "whisper": { admin: false, usage: "/whisper <player> <message>", exec: whisper, }, "tp": cAlias("teleport"), "teleport": { admin: true, usage: "/teleport [<zone>|<X> <Y> <Z>]", exec: func(self *command, s *Session, arg string) { newZone := model.GetZoneByName(arg) var newRoom types.Room if newZone != nil { if newZone.GetId() == s.GetRoom().GetZoneId() { s.WriteLine("You're already in that zone") } else { zoneRooms := model.GetRoomsInZone(newZone.GetId()) if len(zoneRooms) > 0 { newRoom = zoneRooms[0] } } } else { coords, err := utils.Atois(strings.Fields(arg)) var x, y, z int if len(coords) != 3 || err != nil { s.printError("Zone not found: %s", arg) self.Usage(s) } else { x, y, z = coords[0], coords[1], coords[2] } newRoom = model.GetRoomByLocation(types.Coordinate{X: x, Y: y, Z: z}, s.currentZone().GetId()) if newRoom == nil { s.printError("Invalid coordinates") } } if newRoom != nil { model.MoveCharacterToRoom(s.pc, newRoom) s.PrintRoom() } }, }, "who": { admin: false, exec: func(self *command, s *Session, arg string) { chars := model.GetOnlinePlayerCharacters() s.WriteLine("") s.WriteLine("Online Players") s.WriteLine("--------------") for _, char := range chars { s.WriteLine(char.GetName()) } s.WriteLine("") }, }, "colors": { admin: false, exec: func(self *command, s *Session, arg string) { s.WriteLineColor(types.ColorNormal, "Normal") s.WriteLineColor(types.ColorRed, "Red") s.WriteLineColor(types.ColorDarkRed, "Dark Red") s.WriteLineColor(types.ColorGreen, "Green") s.WriteLineColor(types.ColorDarkGreen, "Dark Green") s.WriteLineColor(types.ColorBlue, "Blue") s.WriteLineColor(types.ColorDarkBlue, "Dark Blue") s.WriteLineColor(types.ColorYellow, "Yellow") s.WriteLineColor(types.ColorDarkYellow, "Dark Yellow") s.WriteLineColor(types.ColorMagenta, "Magenta") s.WriteLineColor(types.ColorDarkMagenta, "Dark Magenta") s.WriteLineColor(types.ColorCyan, "Cyan") s.WriteLineColor(types.ColorDarkCyan, "Dark Cyan") s.WriteLineColor(types.ColorBlack, "Black") s.WriteLineColor(types.ColorWhite, "White") s.WriteLineColor(types.ColorGray, "Gray") }, }, "cm": cAlias("colormode"), "colormode": { admin: false, exec: func(self *command, s *Session, arg string) { if arg == "" { message := "Current color mode is: " switch s.user.GetColorMode() { case types.ColorModeNone: message = message + "None" case types.ColorModeLight: message = message + "Light" case types.ColorModeDark: message = message + "Dark" } s.WriteLine(message) } else { switch strings.ToLower(arg) { case "none": s.user.SetColorMode(types.ColorModeNone) s.WriteLine("Color mode set to: None") case "light": s.user.SetColorMode(types.ColorModeLight) s.WriteLine("Color mode set to: Light") case "dark": s.user.SetColorMode(types.ColorModeDark) s.WriteLine("Color mode set to: Dark") default: s.WriteLine("Valid color modes are: None, Light, Dark") } } }, }, "dr": cAlias("destroyroom"), "destroyroom": { admin: true, usage: "/destroyroom <direction>", exec: func(self *command, s *Session, arg string) { if arg == "" { self.Usage(s) } else { direction := types.StringToDirection(arg) if direction == types.DirectionNone { s.printError("Not a valid direction") } else { loc := s.GetRoom().NextLocation(direction) roomToDelete := model.GetRoomByLocation(loc, s.GetRoom().GetZoneId()) if roomToDelete != nil { model.DeleteRoom(roomToDelete) s.WriteLine("Room destroyed") } else { s.printError("No room in that direction") } } } }, }, "npc": { admin: true, exec: func(self *command, s *Session, arg string) { utils.ExecMenu("NPCs", s, func(menu *utils.Menu) { var npcs types.NPCList npcs = model.GetNpcs() menu.AddAction("n", "New", func() bool { name := getNpcName(s) if name != "" { model.CreateNpc(name, s.pc.GetRoomId(), nil) } return true }) for i, npc := range npcs { n := npc menu.AddActionI(i, npc.GetName(), func() bool { specificNpcMenu(s, n) return true }) } }) s.PrintRoom() }, }, "items": { admin: true, usage: "Usage: /items", exec: func(self *command, s *Session, arg string) { utils.ExecMenu("Items", s, func(menu *utils.Menu) { menu.AddAction("n", "New", func() bool { name := s.getRawUserInput("Item name: ") if name != "" { template := model.CreateTemplate(name) templateMenu(s, template) } return true }) for i, template := range model.GetAllTemplates() { t := template menu.AddActionI(i, template.GetName(), func() bool { templateMenu(s, t) return true }) } }) }, }, "destroyitem": { admin: true, usage: "/destroyitem <item name>", exec: func(self *command, s *Session, arg string) { if arg == "" { self.Usage(s) return } else { itemsInRoom := model.ItemsIn(s.GetRoom().GetId()) name := strings.ToLower(arg) for _, item := range itemsInRoom { if strings.ToLower(item.GetName()) == name { model.DeleteItem(item.GetId()) s.WriteLine("Item destroyed") return } } s.printError("Item not found") } }, }, "roomid": { admin: true, exec: func(self *command, s *Session, arg string) { s.WriteLine("Room ID: %v", s.GetRoom().GetId()) }, }, "cash": { admin: true, usage: "/cash give <amount>", exec: func(self *command, s *Session, arg string) { subcommand, arg := utils.Argify(arg) if subcommand == "give" { amount, err := utils.Atoir(arg, 1, math.MaxInt32) if err == nil { s.pc.AddCash(amount) s.WriteLine("Received: %v monies", amount) } else { s.printError(err.Error()) self.Usage(s) } } else { self.Usage(s) } }, }, "ws": cAlias("windowsize"), "windowsize": { admin: false, exec: func(self *command, s *Session, arg string) { width, height := s.user.GetWindowSize() header := fmt.Sprintf("Width: %v, Height: %v", width, height) topBar := header + " " + strings.Repeat("-", int(width)-2-len(header)) + "+" bottomBar := "+" + strings.Repeat("-", int(width)-2) + "+" outline := "|" + strings.Repeat(" ", int(width)-2) + "|" s.WriteLine(topBar) for i := 0; i < int(height)-3; i++ { s.WriteLine(outline) } s.WriteLine(bottomBar) }, }, "tt": cAlias("terminaltype"), "terminaltype": { admin: false, exec: func(self *command, s *Session, arg string) { s.WriteLine("Terminal type: %s", s.user.GetTerminalType()) }, }, "silent": { admin: false, usage: "/silent [on|off]", exec: func(self *command, s *Session, arg string) { if arg == "" { s.silentMode = !s.silentMode } else if arg == "on" { s.silentMode = true } else if arg == "off" { s.silentMode = false } else { self.Usage(s) } if s.silentMode { s.WriteLine("Silent mode on") } else { s.WriteLine("Silent mode off") } }, }, "r": cAlias("reply"), "reply": { admin: false, exec: func(self *command, s *Session, arg string) { targetChar := model.GetPlayerCharacter(s.replyId) if targetChar == nil { s.asyncMessage("No one to reply to") } else if arg == "" { prompt := "Reply to " + targetChar.GetName() + ": " input := s.getRawUserInput(prompt) if input != "" { newArg := fmt.Sprintf("%s %s", targetChar.GetName(), input) whisper(commands["whisper"], s, newArg) } } else { newArg := fmt.Sprintf("%s %s", targetChar.GetName(), arg) whisper(commands["whisper"], s, newArg) } }, }, "area": { admin: true, exec: func(self *command, s *Session, arg string) { utils.ExecMenu("Areas", s, func(menu *utils.Menu) { menu.AddAction("n", "New", func() bool { name := s.getRawUserInput("Area name: ") if name != "" { model.CreateArea(name, s.currentZone()) } return true }) for i, area := range model.GetAreas(s.currentZone()) { a := area menu.AddActionI(i, area.GetName(), func() bool { specificAreaMenu(s, a) return true }) } }) }, }, "link": { admin: true, usage: "Usage: /link <name> [single|double*] to start, /link to finish, /link remove <name> [single|double*], /link rename <old name> <new name>, /link cancel", exec: func(self *command, s *Session, arg string) { args := strings.Split(arg, " ") StateName := "Linking" linkName, linking := s.states[StateName] if linking { if len(args) == 1 && args[0] == "cancel" { linkData.source = nil delete(s.states, StateName) } else if len(args) != 0 { self.Usage(s) } else { sourceRoom := model.GetRoom(linkData.source) sourceRoom.SetLink(linkName, s.pc.GetRoomId()) if linkData.mode == LinkDouble { s.GetRoom().SetLink(linkName, linkData.source) } linkData.source = nil delete(s.states, StateName) s.PrintRoom() } } else { if len(args) == 0 { self.Usage(s) return } if args[0] == "remove" { mode := "double" if len(args) == 3 { if args[2] == "single" || args[2] == "double" { mode = args[2] } else { self.Usage(s) return } } if len(args) != 2 { self.Usage(s) return } linkNames := s.GetRoom().LinkNames() index := utils.BestMatch(args[1], linkNames) if index == -2 { s.printError("Which one do you mean?") } else if index == -1 { s.printError("Link not found") } else { linkName := linkNames[index] if mode == "double" { links := s.GetRoom().GetLinks() linkedRoom := model.GetRoom(links[linkName]) linkedRoom.RemoveLink(linkName) } s.GetRoom().RemoveLink(linkName) s.PrintRoom() } } else if args[0] == "rename" { // TODO } else { if len(args) == 2 { if args[1] == LinkSingle || args[1] == LinkDouble { linkData.mode = args[1] } else { self.Usage(s) return } } else { linkData.mode = LinkDouble } // New link s.states[StateName] = utils.FormatName(args[0]) linkData.source = s.pc.GetRoomId() } } }, }, "kill": { admin: true, usage: "/kill [npc name]", exec: func(self *command, s *Session, arg string) { if arg == "" { self.Usage(s) } else { npcs := model.NpcsIn(s.pc.GetRoomId()) index := utils.BestMatch(arg, npcs.Characters().Names()) if index == -1 { s.printError("Not found") } else if index == -2 { s.printError("Which one do you mean?") } else { npc := npcs[index] combat.Kill(npc) s.WriteLine("Killed %s", npc.GetName()) } } }, }, "inspect": { admin: true, usage: "/inspect [name]", exec: func(self *command, s *Session, arg string) { if arg == "" { self.Usage(s) } else { characters := model.CharactersIn(s.pc.GetRoomId()) index := utils.BestMatch(arg, characters.Names()) if index == -1 { s.printError("Not found") } else if index == -2 { s.printError("Which one do you mean?") } else { char := characters[index] s.WriteLine(char.GetName()) s.WriteLine("Health: %v/%v", char.GetHitPoints(), char.GetHealth()) } } }, }, "skills": { admin: true, exec: func(self *command, s *Session, arg string) { utils.ExecMenu("Skills", s, func(menu *utils.Menu) { menu.AddAction("n", "New", func() bool { for { name := s.getCleanUserInput("Skill name: ") if name == "" { break } skill := model.GetSkillByName(name) if skill != nil { s.printError("A skill with that name already exists") } else { model.CreateSkill(name) break } } return true }) skills := model.GetAllSkills() for i, skill := range skills { sk := skill menu.AddActionI(i, skill.GetName(), func() bool { specificSkillMenu(s, sk) return true }) } }) }, }, "testmenu": { admin: true, exec: func(self *command, s *Session, arg string) { utils.ExecMenu("Menu Test", s, func(menu *utils.Menu) { for i := 0; i < 500; i++ { menu.AddActionI(i, "Test Item", func() bool { return false }) } }) }, }, "path": { admin: true, usage: "/path <coordinates>", exec: func(self *command, s *Session, arg string) { coords, err := utils.Atois(strings.Fields(arg)) if err == nil { if len(coords) == 2 || len(coords) == 3 { x, y := coords[0], coords[1] z := s.GetRoom().GetLocation().Z if len(coords) == 3 { z = coords[2] } room := model.GetRoomByLocation(types.Coordinate{X: x, Y: y, Z: z}, s.GetRoom().GetZoneId()) if room != nil { path := engine.FindPath(s.GetRoom(), room) /* s.WriteLine("Path:") for _, room := range path { s.WriteLine(fmt.Sprintf("%v", room.GetLocation())) } */ if len(path) > 0 { for _, room := range path { time.Sleep(200 * time.Millisecond) model.MoveCharacterToRoom(s.pc, room) s.PrintRoom() s.handleCommand("map", "") } } else { s.printError("No path found") } } else { s.printError("No room found at the given coordinates") } } else { self.Usage(s) } } else { self.Usage(s) } }, }, "time": { admin: false, usage: "/time", exec: func(self *command, s *Session, arg string) { s.WriteLine("%v", model.GetWorld().GetTime()) }, }, "join": { admin: true, usage: "/join <player name>", exec: func(self *command, s *Session, arg string) { target := model.GetCharacterByName(arg) if target == nil { s.printError("Target not found") } else { model.MoveCharacterToRoom(s.pc, model.GetRoom(target.GetRoomId())) s.PrintRoom() } }, }, "bring": { admin: true, usage: "/bring <player name>", exec: func(self *command, s *Session, arg string) { // TODO - The target receives no indication that they've been moved target := model.GetCharacterByName(arg) if target == nil { s.printError("Target not found") } else { model.MoveCharacterToRoom(target, s.GetRoom()) } }, }, } }