From 4e195c6ba3af2dbf28c8318c7c71df93bef7bd17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Ryb=C3=A1rsky?= Date: Sat, 8 Jun 2024 23:23:20 +0200 Subject: [PATCH] Update --- client.go | 5 +- commands.go | 117 +++++++++++++++++++---------- gun.go | 39 ++++++---- item.go | 11 +++ match.go | 179 +++++++++++++++++++++++++++++++++++++++++++- terminal_handler.go | 20 ++++- 6 files changed, 311 insertions(+), 60 deletions(-) diff --git a/client.go b/client.go index b2a9bde..3954818 100644 --- a/client.go +++ b/client.go @@ -16,7 +16,8 @@ type Client struct { reader *telnet.Reader match *Match isHost bool - hp int + HP int + items []Item // Add more fields as needed } @@ -34,7 +35,7 @@ func NewClient(context *telnet.Context, writer *telnet.Writer, reader *telnet.Re clients[clientID].writer = writer clients[clientID].reader = reader clients[clientID].isHost = false - clients[clientID].hp = -1 + clients[clientID].HP = -1 return clientID } diff --git a/commands.go b/commands.go index 63106ca..7afc8cd 100644 --- a/commands.go +++ b/commands.go @@ -7,47 +7,85 @@ import ( "sync" ) +type Command struct { + commandHandlerFunc CommandHandlerFunc + description string +} + type CommandHandlerFunc func(args []string, clientID int) string type CommandRegistry struct { - commands map[string]CommandHandlerFunc + commands map[string]Command mutex sync.Mutex // Mutex to safely access commands map } func NewCommandRegistry() *CommandRegistry { return &CommandRegistry{ - commands: make(map[string]CommandHandlerFunc), + commands: make(map[string]Command), } } -func (cr *CommandRegistry) RegisterCommand(command string, handler CommandHandlerFunc) { +func (cr *CommandRegistry) RegisterCommand(command string, handler CommandHandlerFunc, desc string) { cr.mutex.Lock() defer cr.mutex.Unlock() - cr.commands[command] = handler + cr.commands[command] = Command{ + commandHandlerFunc: handler, + description: desc, + } } func (cr *CommandRegistry) GetCommandHandler(command string, clientID int) (CommandHandlerFunc, bool, int) { cr.mutex.Lock() defer cr.mutex.Unlock() - handler, ok := cr.commands[command] + commandEntry, ok := cr.commands[command] + handler := commandEntry.commandHandlerFunc return handler, ok, clientID } func RegisterCommands(registry *CommandRegistry) { - registry.RegisterCommand("help", HelpCommand) - registry.RegisterCommand("setname", SetNameCommand) - registry.RegisterCommand("list", ListClientsCommand) - registry.RegisterCommand("tell", TellClientCommand) - registry.RegisterCommand("clear", ClearCommand) - registry.RegisterCommand("rng", RandomCommand) - registry.RegisterCommand("challenge", StartCommand) - registry.RegisterCommand("accept", AcceptCommand) - registry.RegisterCommand("shoot", ShootCommand) + registry.RegisterCommand("clear", ClearCommand, `Clears your screen (clear)`) + registry.RegisterCommand("setname", SetNameCommand, `Sets your name (setname yourname[string])`) + registry.RegisterCommand("list", ListClientsCommand, `Lists clients (list)`) + registry.RegisterCommand("rng", RandomCommand, `Generates a random integer (rng from[int] to[int{bigger than from}], both need to be above zero)`) + registry.RegisterCommand("tell", TellClientCommand, `Tell a client (tell client[id or string{username}])`) + registry.RegisterCommand("challenge", StartCommand, `Challenge a client (challenge client[id or string{username}])`) + registry.RegisterCommand("accept", AcceptCommand, `Accepts a challenge (accept)`) + registry.RegisterCommand("shoot", ShootCommand, `Shoots at a client (shoot target[int or string], accepts self as a parameter) {Shooting self with a blank keeps your turn}`) + registry.RegisterCommand("useitem", UseItemCommand, `Uses an item in a match (useitem index[number])`) // Add more commands here... } +func UseItemCommand(args []string, clientID int) string { + param := -1 + itemID := -1 + if len(args) >= 2 { + var err error + param, err = strconv.Atoi(args[1]) + if err != nil { + param = -1 + } + } + if len(args) >= 1 { + var err error + itemID, err = strconv.Atoi(args[0]) + if err != nil { + itemID = -1 + } + } + if itemID >= 0 && itemID < clients[clientID].match.itemCount { + clients[clientID].items[itemID].useItem(param) + clients[clientID].match.SendMessage("\r\nUsing item "+clients[clientID].items[itemID].name(), clients[clientID]) + return "Used" + } else { + return "ERROR using item" + } +} + func ShootCommand(args []string, clientID int) string { - if clients[clientID].match.round >= 0 { + if clients[clientID].match != nil && clients[clientID].match.round > 0 && clients[clientID].match.Guestturn != clients[clientID].isHost { + defer clients[clientID].match.CheckStatus() + defer clients[clientID].match.announceHP() + defer clients[clientID].match.announceRounds() self := false if len(args) == 1 { if strings.ToLower(args[0]) == "self" { @@ -56,26 +94,33 @@ func ShootCommand(args []string, clientID int) string { self = true } } - var fired bool + var fired int + var target *Client if self { - fired = clients[clientID].match.gun.Shoot(clients[clientID]) - if fired { - return "Shot self with a live" + target = clients[clientID] + fired, clients[clientID].match.gun.magazine = clients[clientID].match.gun.Shoot(target) + if fired == 1 { + clients[clientID].match.Guestturn = clients[clientID].isHost + clients[clientID].match.SendMessage("Shot myself with a live", clients[clientID]) } else { - return "Shot self with a blank" + clients[clientID].match.SendMessage("Shot myself with a blank", clients[clientID]) } } else { if clients[clientID].isHost { - fired = clients[clientID].match.gun.Shoot(clients[clientID].match.guest) + target = clients[clientID].match.guest } else { - fired = clients[clientID].match.gun.Shoot(clients[clientID].match.host) + target = clients[clientID].match.host } - if fired { - return "Shot " + args[0] + "with a live" + fired, clients[clientID].match.gun.magazine = clients[clientID].match.gun.Shoot(target) + if fired == 1 { + clients[clientID].match.Guestturn = clients[clientID].isHost + clients[clientID].match.SendMessage("Shot "+target.Username+"("+strconv.Itoa(target.ID)+")"+"with a live", clients[clientID]) } else { - return "Shot " + args[0] + "with a blank" + clients[clientID].match.Guestturn = clients[clientID].isHost + clients[clientID].match.SendMessage("Shot "+target.Username+"("+strconv.Itoa(target.ID)+")"+"with a blank", clients[clientID]) } } + return "SHOT" } return "Error" } @@ -85,11 +130,11 @@ func AcceptCommand(args []string, clientID int) string { defer clientsMutex.Unlock() matchesMutex.Lock() defer matchesMutex.Unlock() - if clients[clientID].match.guest == clients[clientID] { - clients[clientID].match.round = 0 + if clients[clientID].match != nil && clients[clientID].match.guest == clients[clientID] { clients[clientID].match.host.sendMessage("Accepted", clients[clientID]) clients[clientID].isHost = false - clients[clientID].match.gun.Reload(8) + clients[clientID].match.Guestturn = false + clients[clientID].match.Start() return "Accepted" } return "No match to accept" @@ -98,11 +143,11 @@ func AcceptCommand(args []string, clientID int) string { func StartCommand(args []string, clientID int) string { if len(args) == 1 { targetID := getByIDOrName(args[0]) - if targetID >= 0 { + if targetID >= 0 && targetID != clientID && (clients[clientID].match == nil || clients[clientID].match.round <= 0) { match := Match{ host: clients[clientID], guest: clients[targetID], - round: -1, + round: 0, gun: Gun{ doubled: false, }, @@ -110,11 +155,13 @@ func StartCommand(args []string, clientID int) string { clientsMutex.Lock() clients[clientID].match = &match clients[targetID].match = &match + clients[clientID].isHost = true clients[targetID].sendMessage("Type \"accept\" to accept a match.", clients[clientID]) clientsMutex.Unlock() matchesMutex.Lock() matches = append(matches, &match) matchesMutex.Unlock() + return "Successfully challenged" } else { return "Unknown user" } @@ -122,14 +169,6 @@ func StartCommand(args []string, clientID int) string { return "Invalid argument" } -func HelpCommand(args []string, clientID int) string { - // Handle help command - if len(clients) > 0 { - return "Just help yourself, " + clients[clientID].Username - } - return "No clients available" -} - func RandomCommand(args []string, clientID int) string { // Handle help command if len(args) == 2 { @@ -153,7 +192,7 @@ func ClearCommand(args []string, clientID int) string { func SetNameCommand(args []string, clientID int) string { // Handle help command - if len(args) == 1 { + if len(args) >= 1 { newName := args[0] clients[clientID].Username = newName return "Name set to " + newName diff --git a/gun.go b/gun.go index 0ca61e6..7267a56 100644 --- a/gun.go +++ b/gun.go @@ -8,25 +8,38 @@ type Gun struct { } // fire -func (g Gun) Shoot(target *Client) bool { - shot := false - if g.magazine[len(g.magazine)-1] { - shot = true - if g.doubled { - target.hp -= 2 - } else { - target.hp -= 1 - } +func (g Gun) Shoot(target *Client) (int, []bool) { + shot := 2 + if len(g.magazine) == 0 { + return shot, g.magazine } - g.magazine = g.magazine[:len(g.magazine)-1] + shot = 0 + if g.magazine[len(g.magazine)-1] { + shot = 1 + if g.doubled { + target.HP -= 2 + } else { + target.HP -= 1 + } + target.send("\a") + target.match.Send("") + } + + g.magazine = g.Rack() g.doubled = false - return shot + return shot, g.magazine } -func (g Gun) Reload(count int) { +func (g Gun) Rack() []bool { + g.magazine = g.magazine[:len(g.magazine)-1] + return g.magazine +} + +func (g Gun) Reload(count int) []bool { g.magazine = make([]bool, count) for i := 0; i < count; i++ { - g.magazine[i] = rand.Intn(2) == 1 + g.magazine[i] = rand.Intn(6) <= 2 } + return g.magazine } diff --git a/item.go b/item.go index dd66d71..fa06c49 100644 --- a/item.go +++ b/item.go @@ -3,12 +3,15 @@ package main type Item struct { kind int match *Match + owner *Client } func (i Item) name() string { switch i.kind { case 0: return "Doubler" + case 1: + return "Beer" default: return "None" } @@ -18,5 +21,13 @@ func (i Item) useItem(param1 int) { switch i.kind { case 0: i.match.gun.doubled = true + i.match.SendMessage("I doubled the damage for 1 round", i.owner) + break + case 1: + i.match.gun.Rack() + i.match.SendMessage("I rack the magazine", i.owner) + i.match.announceRounds() + break + } } diff --git a/match.go b/match.go index 0958a8b..093e3da 100644 --- a/match.go +++ b/match.go @@ -1,8 +1,179 @@ package main +import ( + "math/rand" + "strconv" +) + type Match struct { - host *Client - guest *Client - round int - gun Gun + host *Client + guest *Client + round int + itemCount int + gun Gun + Guestturn bool +} + +func (m *Match) LoadGun() { + switch m.round { + case 1: + m.gun.magazine = m.gun.Reload(4) + break + case 2: + m.gun.magazine = m.gun.Reload(8) + break + case 3: + m.gun.magazine = m.gun.Reload(16) + break + default: + m.gun.magazine = m.gun.Reload(1) + break + } +} + +func (m *Match) Start() { + m.NextRound() +} + +func (m *Match) resetHP() { + switch m.round { + case 1: + m.host.HP = 5 + m.guest.HP = 5 + break + case 2: + m.host.HP = 2 + m.guest.HP = 2 + break + case 3: + m.host.HP = 4 + m.guest.HP = 4 + break + default: + m.host.HP = 1 + m.guest.HP = 1 + break + } +} + +func (m *Match) announceRounds() { + live := 0 + blank := 0 + for _, round := range m.gun.magazine { + if round { + live++ + } else { + blank++ + } + } + + doubled := "Shotgun is normal" + + if m.gun.doubled { + doubled = "Shotgun is doubled" + } + + m.Send("\r\nRounds are:\r\nLive - " + strconv.Itoa(live) + "\r\nBlank - " + strconv.Itoa(blank) + "\r\n" + doubled) +} + +func (m *Match) announceHP() { + m.Send("\r\nHPs are:\r\n" + + m.host.Username + "(" + strconv.Itoa(m.host.ID) + ")" + " - " + strconv.Itoa(m.host.HP) + "\r\n" + + m.guest.Username + "(" + strconv.Itoa(m.guest.ID) + ")" + " - " + strconv.Itoa(m.guest.HP)) +} + +func (m *Match) giveItems() { + count := 0 + switch m.round { + case 2: + count = 2 + break + case 3: + count = 3 + break + } + + for i := 0; i < count; i++ { + m.host.items = append(m.host.items, Item{ + kind: rand.Intn(m.itemCount), //currently only item 0 exists + match: m, + owner: m.host, + }) + m.guest.items = append(m.host.items, Item{ + kind: rand.Intn(m.itemCount), //currently only item 0 exists + match: m, + owner: m.guest, + }) + } +} + +func (m *Match) announceItems() { + out := "" + for i := 0; i < len(m.host.items); i++ { + name := m.host.items[i].name() + out += strconv.Itoa(i) + " - " + name + "\r\n" + } + m.SendMessage("My items:\r\n"+out, m.host) + + out = "" + for i := 0; i < len(m.guest.items); i++ { + name := m.guest.items[i].name() + out += strconv.Itoa(i) + " - " + name + "\r\n" + } + m.SendMessage("My items:\r\n"+out, m.guest) +} + +func (m *Match) NextRound() bool { + defer m.announceItems() + defer m.announceRounds() + defer m.announceHP() + defer m.LoadGun() + defer m.resetHP() + defer m.giveItems() + if m.round < 3 { + m.round++ + } else if m.round == 3 { + return true //final + } + return false +} + +func (m *Match) Send(message string) { + m.host.sendMessage(message, m.host) + m.guest.sendMessage(message, m.guest) +} + +func (m *Match) SendMessage(message string, sender *Client) { + m.host.sendMessage(message, sender) + m.guest.sendMessage(message, sender) +} + +func (m *Match) announceTurn() { + if m.Guestturn { + m.guest.sendMessage("It is your turn to shoot", m.host) + } else { + m.host.sendMessage("It is your turn to shoot", m.guest) + } +} + +func (m *Match) CheckStatus() { + if m.host.HP < 1 { + m.SendMessage("I died", m.host) + m.SendMessage("I won", m.guest) + } + if m.guest.HP < 1 { + m.SendMessage("I died", m.guest) + m.SendMessage("I won", m.host) + } + if m.host.HP > 0 && m.guest.HP > 0 { + if m.round >= 3 { + m.SendMessage("I won", m.host) + m.SendMessage("I won", m.guest) + } else { + m.announceTurn() + } + } + if len(m.gun.magazine) == 0 { + m.NextRound() + } } diff --git a/terminal_handler.go b/terminal_handler.go index 461b7b3..cbd7e55 100644 --- a/terminal_handler.go +++ b/terminal_handler.go @@ -85,13 +85,29 @@ func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet if len(args) > 0 { cmd := args[0] + internalCommand := false if cmd == "exit" { + internalCommand = true return + } else if cmd == "help" { + internalCommand = true + commands := "" + for command, commandEntry := range handler.CommandRegistry.commands { + commands += command + " - " + commandEntry.description + "\r\n" + } + // Handle help command + if len(clients) > 0 { + clients[clientID].send("Just help yourself, " + clients[clientID].Username + "\r\nCommands:\r\n" + commands) + } else { + clients[clientID].send("No clients available" + "\r\nCommands:\r\n" + commands) + } } handlerFunc, ok, clientID := handler.CommandRegistry.GetCommandHandler(cmd, clientID) clients[clientID].HistoryIndex = 0 - if ok { - message = handlerFunc(args[1:], clientID) + if ok || internalCommand { + if ok { + message = handlerFunc(args[1:], clientID) + } clients[clientID].History = append(clients[clientID].History, command) } else { message = "Command not found"