A Stab At Roguish Go Part 02
Tags: go, gamedev, rogue, atlg
Published: 2019-04-06 19:46:23

---
title: A Stab At Roguish Go Part 02
published: true
description: "Let's go back into the dungeons with Go!"
tags: go, gamedev, rogue, atlg
cover_image: https://dev.shindakun.net/img/attemptinggo_w.png
series: "ATLG Rogue"
---

# ATLG Rogue

## Continuing Adventures

After we finished the last post I continued to mess with the code a little bit more. So, we're going to continue our short trip down the rogue-like way. This time we're hacking in a static map and some basic "collision detection". And on that note, let's jump right in and get started.

![in action](https://thepracticaldev.s3.amazonaws.com/i/e6xppb1q3ix1jf09tt3l.gif)

***

## Code Walkthrough

The first bit of our code is more or less the same. We altered our player struct to add the `rune` which stands for our player character. We'll have to cast to a `string()` when we emit the string to the screen.

```go
package main

import (
  "fmt"
  "math/rand"
  "os"
  "time"

  "github.com/gdamore/tcell"
  "github.com/mattn/go-runewidth"
)

type player struct {
  r      rune
  x      int
  y      int
}

func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) {
  for _, c := range str {
    var comb []rune
    w := runewidth.RuneWidth(c)
    if w == 0 {
      comb = []rune{c}
      c = ' '
      w = 1
    }
    s.SetContent(x, y, c, comb, style)
    x += w
  }
}

func main() {

  debug := false

  player := player{
    r: '@',
    x: 3,
    y: 3,
  }

  var msg string
```

***

## Get Mapping

Since we're using a static "map" we're just going to use a multidimensional array. For walls we're using `#` and the floor is `.`.

```go
  mapp := [9][9]rune{
    {'#', '#', '#', '#', '#', '#', '#', '#', '#'},
    {'#', '.', '.', '.', '.', '.', '.', '.', '#'},
    {'#', '.', '.', '.', '.', '.', '.', '.', '#'},
    {'#', '.', '.', '.', '#', '.', '.', '.', '#'},
    {'#', '.', '.', '#', '#', '#', '.', '.', '#'},
    {'#', '.', '.', '.', '#', '.', '.', '.', '#'},
    {'#', '.', '.', '.', '.', '.', '.', '.', '#'},
    {'#', '.', '.', '.', '.', '.', '.', '.', '#'},
    {'#', '#', '#', '#', '#', '#', '#', '#', '#'},
  }
```

***

## Colors

We have added a couple more colors which we'll use for the walls and the ground when we draw. Nothing too exciting, just a couple of browns and grey.

```go
  tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)
  s, e := tcell.NewScreen()
  if e != nil {
    fmt.Fprintf(os.Stderr, "%v\n", e)
    os.Exit(1)
  }
  if e = s.Init(); e != nil {
    fmt.Fprintf(os.Stderr, "%v\n", e)
    os.Exit(1)
  }

  white := tcell.StyleDefault.
    Foreground(tcell.ColorWhite).
    Background(tcell.ColorBlack)
  grey := tcell.StyleDefault.
    Foreground(tcell.ColorGrey).
    Background(tcell.ColorBlack)
  burlyWood := tcell.StyleDefault.
    Foreground(tcell.ColorBurlyWood).
    Background(tcell.ColorBlack)
  brown := tcell.StyleDefault.
    Foreground(tcell.ColorBrown).
    Background(tcell.ColorBlack)

  s.SetStyle(tcell.StyleDefault.
    Foreground(tcell.ColorWhite).
    Background(tcell.ColorBlack))
  s.Clear()
```

***

## Controls and Collision

Our controls section is still a work in progress. Due to the way that `tcell.EventKey` works we have to check `tcell.KeyRune` and `ev.Rune()`. This way we can see if we have received a normal letter key. We want to be able to use Vim keys to move around so for now, we have a bit of duplication in our code. Before that, though let's take a closer look at how we are achieving our basic collision detection.

>```go
> r, _, _, _ := s.GetContent(player.x-1, player.y)
> if r == '#' {
>
> } else if player.x-1 >= 0 {
>   player.x--
> }
>```

We are using `GetContent()` to get the rune that is currently in the location that the player wants to move to. If that is a wall rune the nothing happens. We could extend this in the future to allow for combat, opening doors, and other interactions.

For now, we're going to deal with the duplication. I'll take a closer look at the `tcell` code and see if there is another alternative.

```go
  quit := make(chan struct{})
  go func() {
    for {
      x, y := s.Size()
      ev := s.PollEvent()
      switch ev := ev.(type) {
      case *tcell.EventKey:

        switch ev.Key() {
        case tcell.KeyRune:
          switch ev.Rune() {
          case 'h':
            r, _, _, _ := s.GetContent(player.x-1, player.y)
            if r == '#' {

            } else if player.x-1 >= 0 {
              player.x--
            }
          case 'l':
            r, _, _, _ := s.GetContent(player.x+1, player.y)
            if r == '#' {

            } else if player.x+1 < x {
              player.x++
            }
          case 'k':
            r, _, _, _ := s.GetContent(player.x, player.y-1)
            if r == '#' {

            } else if player.y-1 >= 0 {
              player.y--
            }
          case 'j':
            r, _, _, _ := s.GetContent(player.x, player.y+1)
            if r == '#' {

            } else if player.y+1 < y {
              player.y++
            }
          }
        case tcell.KeyEscape, tcell.KeyEnter:
          close(quit)
          return
        case tcell.KeyRight:
          r, _, _, _ := s.GetContent(player.x+1, player.y)
          if r == '#' {

          } else if player.x+1 < x {
            player.x++
          }
        case tcell.KeyLeft:
          r, _, _, _ := s.GetContent(player.x-1, player.y)
          if r == '#' {

          } else if player.x-1 >= 0 {
            player.x--
          }
        case tcell.KeyUp:
          r, _, _, _ := s.GetContent(player.x, player.y-1)
          if r == '#' {

          } else if player.y-1 >= 0 {
            player.y--
          }
        case tcell.KeyDown:
          r, _, _, _ := s.GetContent(player.x, player.y+1)
          if r == '#' {

          } else if player.y+1 < y {
            player.y++
          }
        case tcell.KeyCtrlD:
          debug = !debug
        case tcell.KeyCtrlL:
          s.Sync()
        }
      case *tcell.EventResize:
        s.Sync()
      }
    }
  }()
```

The first part of our labeled loop hasn't changed.

```go
loop:
  for {
    select {
    case <-quit:
      break loop
    case <-time.After(time.Millisecond * 50):
    }
    s.Clear()
    dbg := fmt.Sprintf("player x: %d y: %d", player.x, player.y)
    if debug == true {
      var yy int
      if player.y == 0 {
        _, yy = s.Size()
        yy--
      } else {
        yy = 0
      }
      emitStr(s, 0, yy, white, dbg)
    }
    var color tcell.Style
```

***

## Mapping

Now we're finally to our basic map drawing routine. As you can see it's a basic loop. We'll draw our map to the screen before our player character.

```go
    for i := 0; i < 9; i++ {
      for j := 0; j < 9; j++ {
        if mapp[i][j] == '#' {
          color = grey
        }
        if mapp[i][j] == '.' {
            color = burlyWood
          }
        }
        emitStr(s, i+1, j+1, color, string(mapp[i][j]))
      }
    }

    emitStr(s, 0, 0, white, msg)

    emitStr(s, player.x, player.y, white, player.r)
    s.Show()
  }

  s.Fini()
}
```

***

## Wrapping Up

And there we have it a nice simple addition to our original code and a sort of framework that we can build off if we want to. I am a bit tempted to work on this a bit long to see if I can add a simple system for adding creatures and items. I may start by seeing if we can simplify the movement code first - we'll see where the mood takes us.

***

You can find the code for this and most of the other Attempting to Learn Go posts in the repo on GitHub.

{% github shindakun/atlg %}

***

| Enjoy this post? |
|:----:|
| [How about buying me a coffee?](https://ko-fi.com/shindakun) |

***