From 56f344766661bc8b615773449f8ef2ed6f7c734e Mon Sep 17 00:00:00 2001 From: "anton.gurov" Date: Fri, 25 Oct 2019 11:27:47 +0300 Subject: [PATCH 1/2] diverge --- main.go | 12 ++-- ui/mainwindow/console.go | 12 ++++ ui/mainwindow/layer.go | 136 ++++++------------------------------ ui/mainwindow/primitives.go | 97 +++++++++++++++++++++++++ 4 files changed, 137 insertions(+), 120 deletions(-) create mode 100644 ui/mainwindow/console.go create mode 100644 ui/mainwindow/primitives.go diff --git a/main.go b/main.go index 41cc6db..c6f4427 100644 --- a/main.go +++ b/main.go @@ -34,12 +34,16 @@ func mainLoop(ctx util.ClientCtx) { var exit = false baseLayer := mainwindow.AddLayer(0,"white") - bgLayer := mainwindow.AddLayer(1,"white") - menuLayer := mainwindow.AddLayer(2, "white") + upperBaseLayer := mainwindow.AddLayer(1, "red") + menuBgLayer := mainwindow.AddLayer(2,"white") + menuLayer := mainwindow.AddLayer(3, "white") + + + baseLayer.WithColor("white").NewRect( 3,0, 17, 6).Fill() baseLayer.WithColor("grey").NewRect( 22,0, 38, 6).Splash() baseLayer.WithColor("blue").NewRect( 3,8, 57, 6).Fill() - menuLayer.WithColor("#99de7c26").NewRect(50, 1, 10, 10).Splash() + upperBaseLayer.WithColor("#99de7c26").NewRect(50, 1, 10, 10).Splash() menuLayer.WithColor("#aa26edca").DrawWindow("Testing with long title", 0,5, 40, 20) blt.PrintExt(1, 6, 40, 19, 1, `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio morbi quis commodo odio aenean sed. Egestas sed sed risus pretium quam vulputate dignissim suspendisse in. Viverra nam libero justo laoreet sit amet cursus sit. Sed egestas egestas fringilla phasellus faucibus. Ultrices neque ornare aenean euismod elementum nisi quis eleifend. Eget lorem dolor sed viverra ipsum nunc aliquet bibendum. Egestas maecenas pharetra convallis posuere morbi. In hac habitasse platea dictumst quisque. Aenean vel elit scelerisque mauris pellentesque pulvinar. @@ -52,7 +56,7 @@ Cursus in hac habitasse platea. Aliquet risus feugiat in ante metus dictum. Maec Sed euismod nisi porta lorem mollis aliquam ut porttitor leo. Ut tellus elementum sagittis vitae et leo duis ut diam. Elementum curabitur vitae nunc sed velit dignissim. Auctor elit sed vulputate mi sit. Consectetur adipiscing elit ut aliquam purus. Feugiat vivamus at augue eget arcu. Duis ut diam quam nulla porttitor massa id neque. Pharetra magna ac placerat vestibulum lectus mauris ultrices. Non sodales neque sodales ut etiam. Massa ultricies mi quis hendrerit dolor. Est sit amet facilisis magna etiam. Ornare suspendisse sed nisi lacus sed viverra tellus in. `) - bgLayer.WithColor("#77cfcfcf").NewRect(45, 5, 40, 40).Splash() + menuBgLayer.WithColor("#77cfcfcf").NewRect(45, 5, 40, 40).Splash() menuLayer.WithColor("#aaed26ca").DrawWindow("Transparent BG window test",45, 5, 40, 40) blt.PrintExt(46, 6, 40, 39, 1, `Lorem mollis aliquam ut porttitor leo a diam sollicitudin tempor. Convallis tellus id interdum velit. Enim nunc faucibus a pellentesque. Tincidunt augue interdum velit euismod in pellentesque massa placerat duis. Leo duis ut diam quam nulla porttitor massa id. Eu feugiat pretium nibh ipsum consequat nisl. Eget est lorem ipsum dolor sit amet. Et sollicitudin ac orci phasellus egestas. Donec adipiscing tristique risus nec. Et molestie ac feugiat sed. Ante in nibh mauris cursus mattis molestie a iaculis at. Neque laoreet suspendisse interdum consectetur. Vitae et leo duis ut diam quam nulla. Sed ullamcorper morbi tincidunt ornare massa eget egestas purus viverra. Ornare lectus sit amet est placerat in egestas erat. diff --git a/ui/mainwindow/console.go b/ui/mainwindow/console.go new file mode 100644 index 0000000..72682f6 --- /dev/null +++ b/ui/mainwindow/console.go @@ -0,0 +1,12 @@ +package mainwindow + +//Console is a pair of layers (BG and FG) used to render something +// All because of lack of background colors in libbearterminal + +type Console struct { + x,y,w,h int + FgLayer *Layer + BgLayer *Layer +} + +func NewConsole \ No newline at end of file diff --git a/ui/mainwindow/layer.go b/ui/mainwindow/layer.go index 6fc933e..2575145 100644 --- a/ui/mainwindow/layer.go +++ b/ui/mainwindow/layer.go @@ -2,160 +2,64 @@ package mainwindow import blt "lab.zaar.be/thefish/bearlibterminal" -var emptyCorners = [4]uint32{0,0,0,0} - type LayerInterface interface { Render() Put(x, y int, symbol rune, color string) } -type LayerInterfaceImpl struct { +type Layer struct { idx int defaultColor uint32 } -func AddLayer(idx int, colorName string) LayerInterfaceImpl { +func AddLayer(idx int, colorName string) Layer { c := blt.ColorFromName(colorName) - return LayerInterfaceImpl{idx: idx, defaultColor: c} + return Layer{idx: idx, defaultColor: c} } -func (lii *LayerInterfaceImpl) before() *LayerInterfaceImpl { - blt.Layer(lii.idx) - return lii +func (layer *Layer) before() *Layer { + blt.Layer(layer.idx) + return layer } -func (lii *LayerInterfaceImpl) WithColor(colorName string) *LayerInterfaceImpl { - lii.before() +func (layer *Layer) WithColor(colorName string) *Layer { + layer.before() c := blt.ColorFromName(colorName) blt.Color(c) - return lii + return layer } -func (lii *LayerInterfaceImpl) after() *LayerInterfaceImpl { - blt.Color(lii.defaultColor) +func (layer *Layer) after() *Layer { + blt.Color(layer.defaultColor) blt.Layer(0) - return lii + return layer } -func (lii LayerInterfaceImpl) Put(x,y int, symbol string) { +func (layer Layer) Put(x,y int, symbol string) { rnes := []rune(symbol) if (len(rnes)) > 0 { blt.Put(x, y, int(rnes[0])) } } -func (lii LayerInterfaceImpl) Print(x,y int, txt string) (w,h int) { +func (layer Layer) Print(x,y int, txt string) (w,h int) { return blt.Print(x,y, txt) } -type Rect struct { - x,y,w,h int - layer *LayerInterfaceImpl -} - -type rectFill struct { - top, bottom, left, right, topLeft, topRight, bottomLeft, bottomRight, body string -} - -func (lii *LayerInterfaceImpl) NewRect(x,y,w,h int) *Rect { - return &Rect{x,y,w,h, lii} -} - -var noborder = rectFill{ - top: "▄", - bottom: "▀", - left: "▐", - right: "▌", - topLeft: "▗", - topRight: "▖", - bottomLeft: "▝", - bottomRight: "▘", - body: "█", -} - -var splash = rectFill{ - top: "█", - bottom: "█", - left: "█", - right: "█", - topLeft: "█", - topRight: "█", - bottomLeft: "█", - bottomRight: "█", - body: "█", -} - -var doubleBorder = rectFill { - top: "═", - bottom: "═", - left: "║", - right: "║", - topLeft: "╔", - topRight: "╗", - bottomLeft: "╚", - bottomRight: "╝", -} - -func (r *Rect) Fill() { - r.render(noborder) -} - -func (r *Rect) Splash() { - r.render(splash) -} - -func (r *Rect) DrawBorder() { - r.render(doubleBorder) -} - -func (r *Rect) render (fillage rectFill) { - - if fillage.body != "" { - for i := r.x + 1; i < r.x+r.w; i++ { - for j := r.y + 1; j < r.y+r.h; j++ { - r.layer.Put(i, j, fillage.body); - //lii.Put(i, j, "X"); - } - } - } - - for i := r.x + 1; i < r.x+r.w; i++ { - r.layer.Put(i, r.y, fillage.top); - //lii.Put(i, y-1, "Q"); - r.layer.Put(i, r.y+r.h, fillage.bottom); - //lii.Put(i, y+h, "H"); - } - - for j := r.y + 1; j < r.y+r.h; j++ { - r.layer.Put(r.x, j, fillage.left); - //lii.Put(x-1, j, "U"); - r.layer.Put(r.x+r.w, j, fillage.right); - //lii.Put(x+w, j, "M"); - } - - r.layer.Put(r.x, r.y, fillage.topLeft); - //lii.Put(x-1, y-1, "T"); - r.layer.Put(r.x, r.y+r.h, fillage.bottomLeft); - //lii.Put(x-1, y+h, "q"); - r.layer.Put(r.x+r.w, r.y, fillage.topRight); - //lii.Put(x+w, y-1, "L"); - r.layer.Put(r.x+r.w, r.y+r.h, fillage.bottomRight); -}; - -func (lii LayerInterfaceImpl) DrawWindow (title string, x, y, w, h int) { +func (layer Layer) DrawWindow (title string, x, y, w, h int) { if len(title) > (w -2) { title = title[:(w-2)] } - lii.NewRect(x,y,w,h).DrawBorder() + layer.NewRect(x,y,w,h).DrawBorder() centerX := x + (w / 2) - lii.Print(centerX - (len(title) / 2) - 1, y, "╡" + title + "╞") + layer.Print(centerX - (len(title) / 2) - 1, y, "╡" + title + "╞") }; -func (lii *LayerInterfaceImpl) Decorate (f func (args ...interface{})) func (args ...interface{}) { +func (layer *Layer) Decorate (f func (args ...interface{})) func (args ...interface{}) { return func (args ...interface{}) { - lii.before() + layer.before() f(args) - lii.after() + layer.after() } } \ No newline at end of file diff --git a/ui/mainwindow/primitives.go b/ui/mainwindow/primitives.go new file mode 100644 index 0000000..c3b8699 --- /dev/null +++ b/ui/mainwindow/primitives.go @@ -0,0 +1,97 @@ +package mainwindow + +type Rect struct { + x,y,w,h int + layer *Layer +} + +type rectFill struct { + top, bottom, left, right, topLeft, topRight, bottomLeft, bottomRight, body string +} + +func (layer *Layer) NewRect(x,y,w,h int) *Rect { + return &Rect{x,y,w,h, layer} +} + +var noborder = rectFill{ + top: "▄", + bottom: "▀", + left: "▐", + right: "▌", + topLeft: "▗", + topRight: "▖", + bottomLeft: "▝", + bottomRight: "▘", + body: "█", +} + +var splash = rectFill{ + top: "█", + bottom: "█", + left: "█", + right: "█", + topLeft: "█", + topRight: "█", + bottomLeft: "█", + bottomRight: "█", + body: "█", +} + +var doubleBorder = rectFill { + top: "═", + bottom: "═", + left: "║", + right: "║", + topLeft: "╔", + topRight: "╗", + bottomLeft: "╚", + bottomRight: "╝", +} + +func (r *Rect) Fill() { + r.render(noborder) +} + +func (r *Rect) Splash() { + r.render(splash) +} + +func (r *Rect) DrawBorder() { + r.render(doubleBorder) +} + +func (r *Rect) render (fillage rectFill) { + + if fillage.body != "" { + for i := r.x + 1; i < r.x+r.w; i++ { + for j := r.y + 1; j < r.y+r.h; j++ { + r.layer.Put(i, j, fillage.body); + //lii.Put(i, j, "X"); + } + } + } + + for i := r.x + 1; i < r.x+r.w; i++ { + r.layer.Put(i, r.y, fillage.top); + //lii.Put(i, y-1, "Q"); + r.layer.Put(i, r.y+r.h, fillage.bottom); + //lii.Put(i, y+h, "H"); + } + + for j := r.y + 1; j < r.y+r.h; j++ { + r.layer.Put(r.x, j, fillage.left); + //lii.Put(x-1, j, "U"); + r.layer.Put(r.x+r.w, j, fillage.right); + //lii.Put(x+w, j, "M"); + } + + r.layer.Put(r.x, r.y, fillage.topLeft); + //lii.Put(x-1, y-1, "T"); + r.layer.Put(r.x, r.y+r.h, fillage.bottomLeft); + //lii.Put(x-1, y+h, "q"); + r.layer.Put(r.x+r.w, r.y, fillage.topRight); + //lii.Put(x+w, y-1, "L"); + r.layer.Put(r.x+r.w, r.y+r.h, fillage.bottomRight); + +}; + From ed5a425dabe94fbdb1fb96f6c1e0ce8508151e57 Mon Sep 17 00:00:00 2001 From: "anton.gurov" Date: Fri, 25 Oct 2019 18:47:10 +0300 Subject: [PATCH 2/2] updates --- engine/ecs/entity.go | 5 ++ engine/fov/fov.go | 88 ++++++++++++++++++++++++ engine/gamemap/level.go | 14 ++++ engine/gamemap/mapgen.go | 20 ++++++ engine/gamemap/tile.go | 143 +++++++++++++++++++++++++++++++++++++++ engine/gamemap/types.go | 5 ++ main.go | 1 - ui/mainwindow/camera.go | 43 ++++++++++++ ui/mainwindow/console.go | 12 ---- ui/mainwindow/window.go | 1 + util/rng.go | 94 +++++++++++++++++++++++++ 11 files changed, 413 insertions(+), 13 deletions(-) create mode 100644 engine/ecs/entity.go create mode 100644 engine/fov/fov.go create mode 100644 engine/gamemap/level.go create mode 100644 engine/gamemap/mapgen.go create mode 100644 engine/gamemap/tile.go create mode 100644 engine/gamemap/types.go create mode 100644 ui/mainwindow/camera.go delete mode 100644 ui/mainwindow/console.go create mode 100644 util/rng.go diff --git a/engine/ecs/entity.go b/engine/ecs/entity.go new file mode 100644 index 0000000..627d608 --- /dev/null +++ b/engine/ecs/entity.go @@ -0,0 +1,5 @@ +package ecs + +type Entity struct { + ID int +} diff --git a/engine/fov/fov.go b/engine/fov/fov.go new file mode 100644 index 0000000..c19616c --- /dev/null +++ b/engine/fov/fov.go @@ -0,0 +1,88 @@ +package fov + +import ( + "github.com/jcerise/gogue/gamemap" + "math" +) + +type FieldOfVision struct { + cosTable map[int]float64 + sinTable map[int]float64 + torchRadius int +} + +func (f *FieldOfVision) Initialize() { + + f.cosTable = make(map[int]float64) + f.sinTable = make(map[int]float64) + + for i := 0; i < 360; i++ { + ax := math.Sin(float64(i) / (float64(180) / math.Pi)) + ay := math.Cos(float64(i) / (float64(180) / math.Pi)) + + f.sinTable[i] = ax + f.cosTable[i] = ay + } +} + +func (f *FieldOfVision) SetTorchRadius(radius int) { + if radius > 1 { + f.torchRadius = radius + } +} + +func (f *FieldOfVision) SetAllInvisible(gameMap *gamemap.Map) { + for x := 0; x < gameMap.Width; x++ { + for y := 0; y < gameMap.Height; y++ { + gameMap.Tiles[x][y].Visible = false + } + } +} + +func (f *FieldOfVision) RayCast(playerX, playerY int, gameMap *gamemap.Map) { + // Cast out rays each degree in a 360 circle from the player. If a ray passes over a floor (does not block sight) + // tile, keep going, up to the maximum torch radius (view radius) of the player. If the ray intersects a wall + // (blocks sight), stop, as the player will not be able to see past that. Every visible tile will get the Visible + // and Explored properties set to true. + + for i := 0; i < 360; i++ { + + ax := f.sinTable[i] + ay := f.cosTable[i] + + x := float64(playerX) + y := float64(playerY) + + // Mark the players current position as explored + tile := gameMap.Tiles[playerX][playerY] + tile.Explored = true + tile.Visible = true + + for j := 0; j < f.torchRadius; j++ { + x -= ax + y -= ay + + roundedX := int(Round(x)) + roundedY := int(Round(y)) + + if x < 0 || x > float64(gameMap.Width-1) || y < 0 || y > float64(gameMap.Height-1) { + // If the ray is cast outside of the gamemap, stop + break + } + + tile := gameMap.Tiles[roundedX][roundedY] + + tile.Explored = true + tile.Visible = true + + if gameMap.Tiles[roundedX][roundedY].BlocksSight == true { + // The ray hit a wall, go no further + break + } + } + } +} + +func Round(f float64) float64 { + return math.Floor(f + .5) +} \ No newline at end of file diff --git a/engine/gamemap/level.go b/engine/gamemap/level.go new file mode 100644 index 0000000..d70beb8 --- /dev/null +++ b/engine/gamemap/level.go @@ -0,0 +1,14 @@ +package gamemap + +import "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + +type Level struct { + Name string + Branch string + Depth int + MaxRooms int + Width int + Height int + Objects []ecs.Entity + Tiles [][]*Tile +} \ No newline at end of file diff --git a/engine/gamemap/mapgen.go b/engine/gamemap/mapgen.go new file mode 100644 index 0000000..ea69cf9 --- /dev/null +++ b/engine/gamemap/mapgen.go @@ -0,0 +1,20 @@ +package gamemap + +type mapGen interface { + generate(l *Level) *Level +} + +type defaultGen struct {} + +func (d defaultGen) generate(l *Level) *Level { + + l.Tiles, rooms = addRooms(l) + l. Tiles = connectRooms(rooms) + l.Objects = populate(rooms) + + return l +} + +func addRooms(l *Level) { + +} \ No newline at end of file diff --git a/engine/gamemap/tile.go b/engine/gamemap/tile.go new file mode 100644 index 0000000..ce76a3b --- /dev/null +++ b/engine/gamemap/tile.go @@ -0,0 +1,143 @@ +package gamemap + +import "lab.zaar.be/thefish/alchemyst-go/util" +import blt "lab.zaar.be/thefish/bearlibterminal" + +type ColorHolder struct { + R uint8 + G uint8 + B uint8 +} + +type TileColorSet struct { + Fg func() uint32 + Bg func() uint32 + DarkFg func() uint32 + DarkBg func() uint32 + current *ColorHolder +} + +type Appearance struct { + Char string + ColorSet *TileColorSet +} + +var crng = util.NewRNG() + +func colordance(colorValue uint8, minGlow, maxGlow, step int) uint8 { + color := crng.Range(0, step) + int(colorValue) + if color > maxGlow { + color = crng.Range(0, step) + minGlow + } + return uint8(color) +} + +type Tile struct { + *Appearance + Name string + Description string + BlocksPass bool + BlocksSight bool + Explored bool + MustDraw bool +} + + +func NewWall() *Tile { + return &Tile{ + Name: "Wall", + Description: "A dull rock wall", + BlocksPass: true, + BlocksSight: true, + Explored: false, + MustDraw: false, + Appearance: &Appearance{ + Char: "#", + ColorSet: &TileColorSet{ + Fg: func() uint32 {return blt.ColorFromARGB(255, 130,110,150)}, + Bg: func() uint32 {return blt.ColorFromARGB(255, 172,170,173)}, + DarkFg: func() uint32 {return blt.ColorFromARGB(255, 20,20,68)}, + DarkBg: func() uint32 {return blt.ColorFromARGB(255, 7,7,30)}, + }, + }, + } +} + +func NewFloor() *Tile { + return &Tile{ + Name: "Floor", + Description: "Dusty rock floor", + BlocksPass: false, + BlocksSight: false, + Explored: false, + MustDraw: false, + Appearance: &Appearance{ + Char: ".", + ColorSet: &TileColorSet{ + Fg: func() uint32 {return blt.ColorFromARGB(255, 220,220,250)}, + Bg: func() uint32 {return blt.ColorFromARGB(255, 19,19,70)}, + DarkFg: func() uint32 {return blt.ColorFromARGB(255, 30,20,50)}, + DarkBg: func() uint32 {return blt.ColorFromARGB(255, 7,7,30)}, + }, + }, + } +} + +func NewWaterTile() *Tile { + ch := &ColorHolder{19,19,70} + return &Tile { + Name: "Water", + Description: "Murky water", + BlocksPass: false, + BlocksSight: false, + Explored: false, + MustDraw: true, //fixme debug + Appearance: &Appearance{ + Char: ".", + ColorSet: &TileColorSet{ + current: ch, + Fg: func() uint32 {return blt.ColorFromARGB(255, 220,220,250)}, + Bg: func() uint32 { + return blt.ColorFromARGB( + 255, + ch.R, + colordance(ch.G, 2, 42, 4 ), + colordance(ch.B, 180,229,12), + ) + }, + DarkFg: func() uint32 {return blt.ColorFromARGB(255, 30,20,50)}, + DarkBg: func() uint32 {return blt.ColorFromARGB(255, 7,7,30)}, + }, + }, + } +} + +func NewDeepWaterTile() *Tile { + ch := &ColorHolder{5,2,154} + return &Tile { + Name: "Deep Water", + Description: "Deep water", + BlocksPass: false, + BlocksSight: false, + Explored: false, + MustDraw: true, //fixme debug + Appearance: &Appearance{ + Char: " ", + ColorSet: &TileColorSet{ + current: ch, + Fg: func() uint32 {return blt.ColorFromARGB(255, 220,220,250)}, + Bg: func() uint32 { + return blt.ColorFromARGB( + 255, + ch.R, + colordance(ch.G, 0, 15, 2 ), + colordance(ch.B, 120,180,5), + ) + }, + DarkFg: func() uint32 {return blt.ColorFromARGB(255, 30,20,50)}, + DarkBg: func() uint32 {return blt.ColorFromARGB(255, 7,7,30)}, + }, + }, + } +} + diff --git a/engine/gamemap/types.go b/engine/gamemap/types.go new file mode 100644 index 0000000..db2fe44 --- /dev/null +++ b/engine/gamemap/types.go @@ -0,0 +1,5 @@ +package gamemap + +type Coords struct { + x,y int +} diff --git a/main.go b/main.go index 549c2b9..9fafc49 100644 --- a/main.go +++ b/main.go @@ -59,7 +59,6 @@ func main() { } mainCtx.Logger().Info().Msg("pre-shutdown sequence") - } // do runs f on the main thread. diff --git a/ui/mainwindow/camera.go b/ui/mainwindow/camera.go new file mode 100644 index 0000000..d23a32f --- /dev/null +++ b/ui/mainwindow/camera.go @@ -0,0 +1,43 @@ +package mainwindow + +type GameCamera struct { + X int + Y int + Width int + Height int +} + +func (c *GameCamera) MoveCamera(targetX int, targetY int, mapWidth int, mapHeight int) { + // Update the camera coordinates to the target coordinates + x := targetX - c.Width/2 + y := targetY - c.Height/2 + + if x < 0 { + x = 0 + } + + if y < 0 { + y = 0 + } + + if x > mapWidth - c.Width { + x = mapWidth - c.Width + } + + if y > mapHeight - c.Height { + y = mapHeight - c.Height + } + + c.X, c.Y = x, y +} + +func (c *GameCamera) ToCameraCoordinates(mapX int, mapY int) (cameraX int, cameraY int) { + // Convert coordinates on the gamemap, to coordinates on the viewport + x, y := mapX-c.X, mapY-c.Y + + if x < 0 || y < 0 || x >= c.Width || y >= c.Height { + return -1, -1 + } + + return x, y +} diff --git a/ui/mainwindow/console.go b/ui/mainwindow/console.go deleted file mode 100644 index 72682f6..0000000 --- a/ui/mainwindow/console.go +++ /dev/null @@ -1,12 +0,0 @@ -package mainwindow - -//Console is a pair of layers (BG and FG) used to render something -// All because of lack of background colors in libbearterminal - -type Console struct { - x,y,w,h int - FgLayer *Layer - BgLayer *Layer -} - -func NewConsole \ No newline at end of file diff --git a/ui/mainwindow/window.go b/ui/mainwindow/window.go index f4d3d75..34f6582 100644 --- a/ui/mainwindow/window.go +++ b/ui/mainwindow/window.go @@ -26,6 +26,7 @@ func (mw *MainWindow) Open() { fmt.Sprintf( //"window: size=%dx%d, title='%s v%s'; font: ./resources/fonts-bitmap/ibmnew8x12.png, size=8x12;", "window: size=%dx%d, title='%s v%s'; font: %s, size=8x16;", + //"window: size=%dx%d, title='%s v%s'", config.MainWindowSizeX, config.MainWindowSizeY, config.Title, diff --git a/util/rng.go b/util/rng.go new file mode 100644 index 0000000..a37b65a --- /dev/null +++ b/util/rng.go @@ -0,0 +1,94 @@ +package util + +import ( + "math" + "math/rand" + "time" +) + +type RNG struct { + seed int64 + rand *rand.Rand +} + +func NewRNG() *RNG { + rng := RNG{} + + // Set the seed to the current time. This can be updated later by the user. + rng.seed = time.Now().UTC().UnixNano() + rng.rand = rand.New(rand.NewSource(rng.seed)) + + return &rng +} + +func (rng *RNG) GetSeed() int64 { + return rng.seed +} + +func (rng *RNG) SetSeed(seed int64) { + rng.seed = seed +} + +func (rng *RNG) Uniform() float64 { + return rng.rand.Float64() +} + +func (rng *RNG) UniformRange(a, b float64) float64 { + return a + rng.Uniform() * (b - a) +} + +func (rng *RNG) Normal(mean, stddev float64) float64 { + + var r, x float64 + + for r >= 1 || r == 0 { + x = rng.UniformRange(-1.0, 1.0) + y := rng.UniformRange(-1.0, 1.0) + r = x*x + y*y + } + + result := x * math.Sqrt(-2 * math.Log(r) / r) + + return mean + stddev * result + +} + +func (rng *RNG) Percentage() int { + return rng.rand.Intn(100) +} + +func (rng *RNG) Range(min, max int) int { + if min == max { + return min + } else { + return rng.rand.Intn(max - min) + min + } +} + +func (rng *RNG) RangeNegative(min, max int) int { + if min == max { + return min + } else { + return rng.rand.Intn(max - min + 1) + min + } +} + +func (rng *RNG) GetWeightedEntity(values map[int]int) int { + // First up, get the total weight value from the gamemap + totalWeight := 0 + for weight := range values { + totalWeight += weight + } + + // Next, get a random integer in the range of the total weight + r := rng.Range(0, totalWeight) + + for weight, value := range values { + r -= value + if r <= 0 { + return weight + } + } + + return -1 +} \ No newline at end of file