From 693b8a54f17bf8cba6f16251235e113ce8a6d876 Mon Sep 17 00:00:00 2001
From: Marc Di Luzio <marc.diluzio@gmail.com>
Date: Sat, 27 Jun 2020 01:35:09 +0100
Subject: [PATCH] Add repair command to repair using inventory item

---
 cmd/rove/main.go       |  6 +++-
 cmd/rove/main_test.go  |  1 +
 docs/status.md         |  2 +-
 pkg/game/command.go    |  5 +++
 pkg/game/world.go      | 16 ++++++++--
 pkg/game/world_test.go | 71 +++++++++++++++++++++++++++++++++++++++++-
 proto/rove/rove.proto  |  1 +
 7 files changed, 97 insertions(+), 5 deletions(-)

diff --git a/cmd/rove/main.go b/cmd/rove/main.go
index 3a10de3..15014a1 100644
--- a/cmd/rove/main.go
+++ b/cmd/rove/main.go
@@ -26,12 +26,16 @@ func printUsage() {
 	fmt.Fprintln(os.Stderr, "\nCommands")
 	fmt.Fprintln(os.Stderr, "\tstatus                     prints the server status")
 	fmt.Fprintln(os.Stderr, "\tregister NAME              registers an account and stores it (use with -name)")
-	fmt.Fprintln(os.Stderr, "\tcommands COMMAND [VAL...]  issues move command to rover")
+	fmt.Fprintln(os.Stderr, "\tcommands COMMAND [VAL...]  issues move command to rover, see below")
 	fmt.Fprintln(os.Stderr, "\tradar                      gathers radar data for the current rover")
 	fmt.Fprintln(os.Stderr, "\trover                      gets data for current rover")
 	fmt.Fprintln(os.Stderr, "\tconfig [HOST]              outputs the local config info, optionally sets host")
 	fmt.Fprintln(os.Stderr, "\thelp                       outputs this usage information")
 	fmt.Fprintln(os.Stderr, "\tversion                    outputs version info")
+	fmt.Fprintln(os.Stderr, "\nRover commands:")
+	fmt.Fprintln(os.Stderr, "\tmove BEARING               moves the rover in the chosen direction")
+	fmt.Fprintln(os.Stderr, "\tstash                      stores the object at the rover location in the inventory")
+	fmt.Fprintln(os.Stderr, "\trepair                     uses an inventory object to repair the rover")
 	fmt.Fprintln(os.Stderr, "\nEnvironment")
 	fmt.Fprintln(os.Stderr, "\tROVE_USER_DATA             path to user data, defaults to "+defaultDataPath)
 }
diff --git a/cmd/rove/main_test.go b/cmd/rove/main_test.go
index d958d35..1c06374 100644
--- a/cmd/rove/main_test.go
+++ b/cmd/rove/main_test.go
@@ -52,6 +52,7 @@ func Test_InnerMain(t *testing.T) {
 	// Give it commands
 	assert.NoError(t, InnerMain("commands", "move", "N"))
 	assert.NoError(t, InnerMain("commands", "stash"))
+	assert.NoError(t, InnerMain("commands", "repair"))
 
 	// Give it malformed commands
 	assert.Error(t, InnerMain("commands", "move", "stash"))
diff --git a/docs/status.md b/docs/status.md
index 3386a60..5045f0a 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -14,10 +14,10 @@ This page tracks the current feature set and the features to implement next
 * Rover inventory
 * Collectable materials
 * Rover integrity and damage
+* Rover can repair
 
 ### Incoming features
 
-* Basic rover actions (e.g. repair)
 * Rover replacement and advancement mechanic
 * HTTPS
 
diff --git a/pkg/game/command.go b/pkg/game/command.go
index 0bc9c5b..2a5f875 100644
--- a/pkg/game/command.go
+++ b/pkg/game/command.go
@@ -1,9 +1,14 @@
 package game
 
 const (
+	// Moves the rover in the chosen bearing
 	CommandMove = "move"
 
+	// Will attempt to stash the object at the current location
 	CommandStash = "stash"
+
+	// Will attempt to repair the rover with an inventory object
+	CommandRepair = "repair"
 )
 
 // Command represends a single command to execute
diff --git a/pkg/game/world.go b/pkg/game/world.go
index ad8dc15..8491a7d 100644
--- a/pkg/game/world.go
+++ b/pkg/game/world.go
@@ -363,11 +363,12 @@ func (w *World) Enqueue(rover string, commands ...Command) error {
 	// First validate the commands
 	for _, c := range commands {
 		switch c.Command {
-		case "move":
+		case CommandMove:
 			if _, err := bearing.FromString(c.Bearing); err != nil {
 				return fmt.Errorf("unknown bearing: %s", c.Bearing)
 			}
-		case "stash":
+		case CommandStash:
+		case CommandRepair:
 			// Nothing to verify
 		default:
 			return fmt.Errorf("unknown command: %s", c.Command)
@@ -446,6 +447,17 @@ func (w *World) ExecuteCommand(c *Command, rover string) (err error) {
 			return err
 		}
 
+	case CommandRepair:
+		if r, err := w.GetRover(rover); err != nil {
+			return err
+		} else {
+			// Consume an inventory item to repair
+			if len(r.Inventory) > 0 {
+				r.Inventory = r.Inventory[:len(r.Inventory)-1]
+				r.Integrity = r.Integrity + 1
+				w.Rovers[rover] = r
+			}
+		}
 	default:
 		return fmt.Errorf("unknown command: %s", c.Command)
 	}
diff --git a/pkg/game/world_test.go b/pkg/game/world_test.go
index 49cfdac..5a48231 100644
--- a/pkg/game/world_test.go
+++ b/pkg/game/world_test.go
@@ -161,5 +161,74 @@ func TestWorld_RoverStash(t *testing.T) {
 	inv, err := world.RoverInventory(a)
 	assert.NoError(t, err, "Failed to get inventory")
 	assert.Equal(t, objects.SmallRock, inv[0])
-
+}
+
+func TestWorld_RoverDamage(t *testing.T) {
+	world := NewWorld(2, 2)
+	a, err := world.SpawnRover()
+	assert.NoError(t, err)
+
+	pos := vector.Vector{
+		X: 0.0,
+		Y: 0.0,
+	}
+
+	err = world.WarpRover(a, pos)
+	assert.NoError(t, err, "Failed to set position for rover")
+
+	info, err := world.GetRover(a)
+	assert.NoError(t, err, "couldn't get rover info")
+
+	err = world.Atlas.SetTile(vector.Vector{X: 0.0, Y: 1.0}, objects.LargeRock)
+	assert.NoError(t, err, "Failed to set tile to rock")
+
+	vec, err := world.MoveRover(a, bearing.North)
+	assert.NoError(t, err, "Failed to move rover")
+	assert.Equal(t, pos, vec, "Rover managed to move into large rock")
+
+	newinfo, err := world.GetRover(a)
+	assert.NoError(t, err, "couldn't get rover info")
+	assert.Equal(t, info.Integrity-1, newinfo.Integrity, "rover should have lost integrity")
+}
+
+func TestWorld_RoverRepair(t *testing.T) {
+	world := NewWorld(2, 2)
+	a, err := world.SpawnRover()
+	assert.NoError(t, err)
+
+	pos := vector.Vector{
+		X: 0.0,
+		Y: 0.0,
+	}
+
+	err = world.WarpRover(a, pos)
+	assert.NoError(t, err, "Failed to set position for rover")
+
+	originalInfo, err := world.GetRover(a)
+	assert.NoError(t, err, "couldn't get rover info")
+
+	err = world.Atlas.SetTile(pos, objects.SmallRock)
+	assert.NoError(t, err, "Failed to set tile to rock")
+
+	o, err := world.RoverStash(a)
+	assert.NoError(t, err, "Failed to stash")
+	assert.Equal(t, objects.SmallRock, o, "Failed to get correct object")
+
+	err = world.Atlas.SetTile(vector.Vector{X: 0.0, Y: 1.0}, objects.LargeRock)
+	assert.NoError(t, err, "Failed to set tile to rock")
+
+	vec, err := world.MoveRover(a, bearing.North)
+	assert.NoError(t, err, "Failed to move rover")
+	assert.Equal(t, pos, vec, "Rover managed to move into large rock")
+
+	newinfo, err := world.GetRover(a)
+	assert.NoError(t, err, "couldn't get rover info")
+	assert.Equal(t, originalInfo.Integrity-1, newinfo.Integrity, "rover should have lost integrity")
+
+	err = world.ExecuteCommand(&Command{Command: CommandRepair}, a)
+	assert.NoError(t, err, "Failed to repair rover")
+
+	newinfo, err = world.GetRover(a)
+	assert.NoError(t, err, "couldn't get rover info")
+	assert.Equal(t, originalInfo.Integrity, newinfo.Integrity, "rover should have gained integrity")
 }
diff --git a/proto/rove/rove.proto b/proto/rove/rove.proto
index 943f254..f1c6c3d 100644
--- a/proto/rove/rove.proto
+++ b/proto/rove/rove.proto
@@ -64,6 +64,7 @@ message Command {
     // The command to execute
     // "move" - Move the rover in a direction, requires bearing
     // "stash" - Stashes item at current location in rover inventory
+    // "repair" - Repairs the rover using an inventory object
     string command = 1;
 
     // The bearing, example: NE