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.
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.
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 .
.
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.
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.
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.
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.
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.
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.
shindakun / atlg
Source repo for the "Attempting to Learn Go" posts I've been putting up over on dev.to
Attempting to Learn Go
Here you can find the code I've been writing for my Attempting to Learn Go posts that I've been writing and posting over on Dev.to.
Post Index
Enjoy this post? |
---|
How about buying me a coffee? |
Top comments (0)