diff --git a/README.md b/README.md index b725bef..4116f4d 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,5 @@ space-dragons-in-outer-space ============================ -there's no url because it's not a web app - -it's just a tcp server - -you go - -like this - -`nc 104.236.57.163 9220` - -and that is how - -you be a dragon - -in space - -in space - -in space - - +This is a [real-time strategy +game](https://en.wikipedia.org/wiki/Real-time_strategy) in the style of a [MUD](https://en.wikipedia.org/wiki/MUD). diff --git a/commands.go b/commands.go index da6909a..1d29652 100644 --- a/commands.go +++ b/commands.go @@ -2,8 +2,6 @@ package main import ( "fmt" - "sort" - // "strconv" "strings" ) @@ -54,31 +52,34 @@ var helpCommand = Command{ help: "helpful things to help you", handler: func(conn *Connection, args ...string) { msg := ` -Star Dragons is a stupid name, but it's the name that Brian suggested. It has -nothing to do with Dragons. - -Anyway, Star Dragons is a game of cunning text-based, real-time strategy. You -play as some kind of space-faring entity, faring space in your inspecific -space-faring vessel. If you want a big one, it's big; if you want a small one, -it's small. If you want a pink one, it's pink, if you want a black one, it's -black. And so on, and so forth. It is the space craft of your dreams. Or -perhaps you are one of those insect-like alien races and you play as the queen. -Yeah, that's the ticket! You're the biggest baddest queen bug in space. - -In Star Dragons, you issue your spacecraft (which is *not* called a Dragon) -textual commands to control it. The objective of the game is to be the first -person or alien or bug or magical space ponycorn to eradicate three enemy -species. Right now that is the only win condition. - -All of the systems present in Star Dragons are named and positioned after known -exoplanet systems. When attempting to communicate from one star system to -another, it takes time for the light of your message to reach the other star -systems. Star systems that are farther away take longer to communicate with. +Exocolonus is a game of cunning text-based, real-time strategy. You play as +some kind of space-faring entity, faring space in your inspecific space-faring +vessel. If you want a big one, it's big; if you want a small one, it's small. +If you want a pink one, it's pink, if you want a black one, it's black. And so +on, and so forth. It is the space craft of your dreams. Or perhaps you are +one of those insect-like alien races and you play as the queen. Yeah, that's +the ticket! You're the biggest baddest queen bug in space. + +In Exocolonus, you issue your spacecraft textual commands to control it. The +objective of the game is to be the first person or alien or bug or magical +space ponycorn to eradicate three enemy species. Right now that is the only +win condition. + +All of the systems present in Exocolonus are named and positioned after known +exoplanet systems. Each star system in Exocolonus is a real star system that +has been researched by astronomers, and the number of planets in each system +corresponds to the number of known exoplanets in those systems. When +attempting to communicate from one star system to another, it takes time for +the light of your message to reach the other star systems. Star systems that +are farther away take longer to communicate with. ` msg = strings.TrimSpace(msg) fmt.Fprintln(conn, msg) if len(args) == 0 { + fmt.Fprint(conn, "\n") + conn.Line() + fmt.Fprint(conn, "\n") fmt.Fprintln(conn, `use the "commands" command for a list of commands.`) fmt.Fprintln(conn, `use "help [command-name]" to get info for a specific command.`) return @@ -94,22 +95,11 @@ systems. Star systems that are farther away take longer to communicate with. }, } +// this isn't a real command it just puts command in the list of commands, this +// is weird and circular, this is a special case. var commandsCommand = Command{ name: "commands", help: "gives you a handy list of commands", - handler: func(conn *Connection, args ...string) { - names := make([]string, 0, len(commandRegistry)) - for name, _ := range commandRegistry { - names = append(names, name) - } - sort.Strings(names) - fmt.Fprintln(conn, "--------------------------------------------------------------------------------") - for _, name := range names { - cmd := commandRegistry[name] - conn.Printf("%-16s %s\n", name, cmd.help) - } - fmt.Fprintln(conn, "--------------------------------------------------------------------------------") - }, } func BroadcastCommand(sys *System) Command { diff --git a/connection.go b/connection.go index b07895d..2155dc1 100644 --- a/connection.go +++ b/connection.go @@ -31,44 +31,10 @@ func NewConnection(conn net.Conn) *Connection { bombs: options.startBombs, money: options.startMoney, } - c.SetState(new(LobbyState)) + c.SetState(EnterLobby()) return c } -func (c *Connection) Login() { - for { - c.Printf("what is your name, adventurer?\n") - name, err := c.ReadString('\n') - if err == nil { - name = strings.TrimSpace(name) - } else { - log_error("player failed to connect: %v", err) - return - } - - if !ValidName(name) { - c.Printf("that name is illegal.\n") - continue - } - log_info("player connected: %v", name) - profile, err := loadProfile(name) - if err != nil { - log_error("could not read profile: %v", err) - profile = &Profile{name: name} - if err := profile.Create(); err != nil { - log_error("unable to create profile record: %v", err) - } - c.Printf("you look new around these parts, %s.\n", profile.name) - c.Printf(`if you'd like a description of how to play, type the "help" command\n`) - c.profile = profile - } else { - c.profile = profile - c.Printf("welcome back, %s.\n", profile.name) - } - break - } -} - func (c *Connection) Dead() bool { return false } @@ -260,63 +226,3 @@ func SpawnRandomly() ConnectionState { } return Idle(sys) } - -type LobbyState struct { - NopExit -} - -func (st *LobbyState) String() string { return "Lobby" } - -func (st *LobbyState) Enter(c *Connection) { - c.Login() -} - -func (st *LobbyState) Tick(c *Connection, frame int64) ConnectionState { return st } - -func (st *LobbyState) Commands() []Command { - return []Command{newGameCommand, joinGameCommand} -} - -func (st *LobbyState) GetCommand(name string) *Command { - switch name { - case "new": - return &newGameCommand - case "join": - return &joinGameCommand - default: - return nil - } -} - -var newGameCommand = Command{ - name: "new", - help: "starts a new game", - arity: 0, - variadic: false, - handler: func(c *Connection, args ...string) { - c.Printf("Starting a new game...\n") - game := gm.NewGame() - log_info("Created game: %s", game.id) - go game.Run() - c.game = game - c.Printf("Now playing in game: %s\n\n", game.id) - c.Line() - c.game.Join(c) - c.SetState(SpawnRandomly()) - }, - debug: false, -} - -var joinGameCommand = Command{ - name: "join", - help: "joins an existing game", - arity: 1, - variadic: false, - handler: func(c *Connection, args ...string) { - id := args[0] - c.game = gm.Get(id) - c.SetState(SpawnRandomly()) - c.game.Join(c) - }, - debug: false, -} diff --git a/lobby.go b/lobby.go new file mode 100644 index 0000000..8767674 --- /dev/null +++ b/lobby.go @@ -0,0 +1,128 @@ +package main + +import ( + "strings" + "time" +) + +var banner = ` +############################################################################################## + + + /$$$$$$$$ /$$ +| $$_____/ | $$ +| $$ /$$ /$$ /$$$$$$ /$$$$$$$ /$$$$$$ | $$ /$$$$$$ /$$$$$$$ /$$ /$$ /$$$$$$$ +| $$$$$ | $$ /$$/ /$$__ $$ /$$_____/ /$$__ $$| $$ /$$__ $$| $$__ $$| $$ | $$ /$$_____/ +| $$__/ \ $$$$/ | $$ \ $$| $$ | $$ \ $$| $$| $$ \ $$| $$ \ $$| $$ | $$| $$$$$$ +| $$ >$$ $$ | $$ | $$| $$ | $$ | $$| $$| $$ | $$| $$ | $$| $$ | $$ \____ $$ +| $$$$$$$$ /$$/\ $$| $$$$$$/| $$$$$$$| $$$$$$/| $$| $$$$$$/| $$ | $$| $$$$$$/ /$$$$$$$/ +|________/|__/ \__/ \______/ \_______/ \______/ |__/ \______/ |__/ |__/ \______/ |_______/ + + + ~+ + + * + + ' | + () .-.,="''"=. - o - + '=/_ \ | + * | '=._ | + \ '=./', ' + . '=.__.=' '=' * + + + + O * ' . + + + +############################################################################################## +` + +type LobbyState struct { + CommandSuite + NopExit +} + +func EnterLobby() ConnectionState { + return &LobbyState{ + CommandSuite: CommandSet{ + commandsCommand, + helpCommand, + newGameCommand, + joinGameCommand, + }, + } +} + +func (st *LobbyState) String() string { return "Lobby" } + +func (st *LobbyState) Enter(c *Connection) { + c.Printf(strings.TrimSpace(banner)) + time.Sleep(1 * time.Second) + + for { + c.Printf("\n\nwhat is your name, adventurer?\n") + name, err := c.ReadString('\n') + if err == nil { + name = strings.TrimSpace(name) + } else { + log_error("player failed to connect: %v", err) + return + } + + if !ValidName(name) { + c.Printf("that name is illegal.\n") + continue + } + log_info("player connected: %v", name) + profile, err := loadProfile(name) + if err != nil { + log_error("could not read profile: %v", err) + profile = &Profile{name: name} + if err := profile.Create(); err != nil { + log_error("unable to create profile record: %v", err) + } + c.Printf("you look new around these parts, %s.\n", profile.name) + c.Printf(`if you'd like a description of how to play, type the "help" command\n`) + c.profile = profile + } else { + c.profile = profile + c.Printf("welcome back, %s.\n", profile.name) + } + break + } + +} + +func (st *LobbyState) Tick(c *Connection, frame int64) ConnectionState { return st } + +var newGameCommand = Command{ + name: "new", + help: "starts a new game", + arity: 0, + variadic: false, + handler: func(c *Connection, args ...string) { + c.Printf("Starting a new game...\n") + game := gm.NewGame() + log_info("Created game: %s", game.id) + go game.Run() + c.game = game + c.Printf("Now playing in game: %s\n\n", game.id) + c.Line() + c.game.Join(c) + c.SetState(SpawnRandomly()) + }, + debug: false, +} + +var joinGameCommand = Command{ + name: "join", + help: "joins an existing game", + arity: 1, + variadic: false, + handler: func(c *Connection, args ...string) { + id := args[0] + c.game = gm.Get(id) + c.SetState(SpawnRandomly()) + c.game.Join(c) + }, + debug: false, +}