initial commit, with support for basic lake generation.
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
module rpg_city_maker_reborn
|
||||||
|
|
||||||
|
go 1.25.6
|
||||||
|
|
||||||
|
require (
|
||||||
|
fyne.io/fyne/v2 v2.7.2
|
||||||
|
github.com/aquilax/go-perlin v1.1.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
fyne.io/systray v1.12.0 // indirect
|
||||||
|
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/fredbi/uri v1.1.1 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/fyne-io/gl-js v0.2.0 // indirect
|
||||||
|
github.com/fyne-io/glfw-js v0.3.0 // indirect
|
||||||
|
github.com/fyne-io/image v0.1.1 // indirect
|
||||||
|
github.com/fyne-io/oksvg v0.2.0 // indirect
|
||||||
|
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
|
||||||
|
github.com/go-text/render v0.2.0 // indirect
|
||||||
|
github.com/go-text/typesetting v0.2.1 // indirect
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
|
github.com/hack-pad/go-indexeddb v0.3.2 // indirect
|
||||||
|
github.com/hack-pad/safejs v0.1.0 // indirect
|
||||||
|
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
|
||||||
|
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/rymdport/portal v0.4.2 // indirect
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||||
|
github.com/stretchr/testify v1.11.1 // indirect
|
||||||
|
github.com/yuin/goldmark v1.7.8 // indirect
|
||||||
|
golang.org/x/image v0.24.0 // indirect
|
||||||
|
golang.org/x/net v0.35.0 // indirect
|
||||||
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
|
golang.org/x/text v0.22.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
fyne.io/fyne/v2 v2.7.2 h1:XiNpWkn0PzX43ZCjbb0QYGg1RCxVbugwfVgikWZBCMw=
|
||||||
|
fyne.io/fyne/v2 v2.7.2/go.mod h1:PXbqY3mQmJV3J1NRUR2VbVgUUx3vgvhuFJxyjRK/4Ug=
|
||||||
|
fyne.io/systray v1.12.0 h1:CA1Kk0e2zwFlxtc02L3QFSiIbxJ/P0n582YrZHT7aTM=
|
||||||
|
fyne.io/systray v1.12.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
|
||||||
|
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||||
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
github.com/aquilax/go-perlin v1.1.0 h1:Gg+3jQ24wT4Y5GI7TCRLmYarzUG0k+n/JATFqOimb7s=
|
||||||
|
github.com/aquilax/go-perlin v1.1.0/go.mod h1:z9Rl7EM4BZY0Ikp2fEN1I5mKSOJ26HQpk0O2TBdN2HE=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
|
||||||
|
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
||||||
|
github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
|
||||||
|
github.com/fredbi/uri v1.1.1/go.mod h1:4+DZQ5zBjEwQCDmXW5JdIjz0PUA+yJbvtBv+u+adr5o=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs=
|
||||||
|
github.com/fyne-io/gl-js v0.2.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
|
||||||
|
github.com/fyne-io/glfw-js v0.3.0 h1:d8k2+Y7l+zy2pc7wlGRyPfTgZoqDf3AI4G+2zOWhWUk=
|
||||||
|
github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
|
||||||
|
github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
|
||||||
|
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
|
||||||
|
github.com/fyne-io/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8=
|
||||||
|
github.com/fyne-io/oksvg v0.2.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
|
||||||
|
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
|
||||||
|
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
|
||||||
|
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
|
||||||
|
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
||||||
|
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
||||||
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
||||||
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
|
||||||
|
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||||
|
github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A=
|
||||||
|
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
|
||||||
|
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
|
||||||
|
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
||||||
|
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=
|
||||||
|
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
|
||||||
|
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
|
||||||
|
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||||
|
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
|
||||||
|
github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||||
|
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||||
|
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||||
|
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||||
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aquilax/go-perlin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
alpha = 2.
|
||||||
|
beta = 2.
|
||||||
|
n = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateHeightmap(width, height, octaves int) image.Image {
|
||||||
|
p := perlin.NewPerlin(alpha, beta, n, rand.New(rand.NewSource(time.Now().UnixNano())).Int63())
|
||||||
|
img := image.NewGray(image.Rect(0, 0, width, height))
|
||||||
|
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
var noise float64
|
||||||
|
frequency := 1.0
|
||||||
|
amplitude := 1.0
|
||||||
|
maxAmplitude := 0.0
|
||||||
|
|
||||||
|
for i := 0; i < octaves; i++ {
|
||||||
|
noise += p.Noise2D(float64(x)*frequency/100, float64(y)*frequency/100) * amplitude
|
||||||
|
maxAmplitude += amplitude
|
||||||
|
amplitude /= 2.0
|
||||||
|
frequency *= 2.0
|
||||||
|
}
|
||||||
|
|
||||||
|
noise /= maxAmplitude
|
||||||
|
grayColor := uint8((noise + 1) * 127.5)
|
||||||
|
img.SetGray(x, y, color.Gray{Y: grayColor})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyRoughness(heightmap image.Image, roughness float64) image.Image {
|
||||||
|
bounds := heightmap.Bounds()
|
||||||
|
composite := image.NewRGBA(bounds)
|
||||||
|
draw.Draw(composite, bounds, heightmap, image.Point{}, draw.Src)
|
||||||
|
|
||||||
|
alphaValue := 255 - uint8(roughness*2.55)
|
||||||
|
overlay := image.NewUniform(color.RGBA{R: 128, G: 128, B: 128, A: alphaValue})
|
||||||
|
draw.Draw(composite, bounds, overlay, image.Point{}, draw.Over)
|
||||||
|
|
||||||
|
return composite
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/app"
|
||||||
|
"fyne.io/fyne/v2/canvas"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := app.New()
|
||||||
|
w := a.NewWindow("RPG City Maker")
|
||||||
|
w.Resize(fyne.NewSize(800, 600))
|
||||||
|
|
||||||
|
settings, err := LoadSettings()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error loading settings:", err)
|
||||||
|
// Use default settings if loading fails
|
||||||
|
settings = &Settings{Detail: 1, Roughness: 0, Width: 300, Height: 300, Lakes: 0, LakeSize: 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.SetOnClosed(func() {
|
||||||
|
if err := settings.Save(); err != nil {
|
||||||
|
log.Println("Error saving settings:", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
canvasImg := &canvas.Image{
|
||||||
|
FillMode: canvas.ImageFillContain,
|
||||||
|
}
|
||||||
|
|
||||||
|
heightmapImg := &canvas.Image{
|
||||||
|
FillMode: canvas.ImageFillContain,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial image generation
|
||||||
|
noiseImg := GenerateHeightmap(settings.Width, settings.Height, int(settings.Detail))
|
||||||
|
canvasWithLakes, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSize)
|
||||||
|
darkenedHeightmap := DarkenLakeAreas(noiseImg, lakePixels)
|
||||||
|
compositeImg := ApplyRoughness(darkenedHeightmap, settings.Roughness)
|
||||||
|
heightmapImg.Image = compositeImg
|
||||||
|
canvasImg.Image = canvasWithLakes
|
||||||
|
|
||||||
|
detailLabel := widget.NewLabel(fmt.Sprintf("Detail: %.0f", settings.Detail))
|
||||||
|
detailSlider := widget.NewSlider(1, 16)
|
||||||
|
detailSlider.OnChanged = func(val float64) {
|
||||||
|
settings.Detail = val
|
||||||
|
detailLabel.SetText(fmt.Sprintf("Detail: %.0f", settings.Detail))
|
||||||
|
}
|
||||||
|
detailSlider.SetValue(settings.Detail)
|
||||||
|
|
||||||
|
roughnessLabel := widget.NewLabel(fmt.Sprintf("Roughness: %.0f%%", settings.Roughness))
|
||||||
|
roughnessSlider := widget.NewSlider(0, 100)
|
||||||
|
roughnessSlider.OnChanged = func(val float64) {
|
||||||
|
settings.Roughness = val
|
||||||
|
roughnessLabel.SetText(fmt.Sprintf("Roughness: %.0f%%", settings.Roughness))
|
||||||
|
}
|
||||||
|
roughnessSlider.SetValue(settings.Roughness)
|
||||||
|
|
||||||
|
lakesLabel := widget.NewLabel(fmt.Sprintf("Lakes: %d", settings.Lakes))
|
||||||
|
lakesSlider := widget.NewSlider(0, 15)
|
||||||
|
lakesSlider.OnChanged = func(val float64) {
|
||||||
|
settings.Lakes = int(val)
|
||||||
|
lakesLabel.SetText(fmt.Sprintf("Lakes: %d", settings.Lakes))
|
||||||
|
}
|
||||||
|
lakesSlider.SetValue(float64(settings.Lakes))
|
||||||
|
|
||||||
|
lakeSizeLabel := widget.NewLabel(fmt.Sprintf("Lake Size: %.0f%%", settings.LakeSize))
|
||||||
|
lakeSizeSlider := widget.NewSlider(1, 100)
|
||||||
|
lakeSizeSlider.OnChanged = func(val float64) {
|
||||||
|
settings.LakeSize = val
|
||||||
|
lakeSizeLabel.SetText(fmt.Sprintf("Lake Size: %.0f%%", settings.LakeSize))
|
||||||
|
}
|
||||||
|
lakeSizeSlider.SetValue(settings.LakeSize)
|
||||||
|
|
||||||
|
widthEntry := widget.NewEntry()
|
||||||
|
widthEntry.SetText(strconv.Itoa(settings.Width))
|
||||||
|
widthEntry.OnChanged = func(s string) {
|
||||||
|
val, err := strconv.Atoi(s)
|
||||||
|
if err == nil {
|
||||||
|
settings.Width = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
heightEntry := widget.NewEntry()
|
||||||
|
heightEntry.SetText(strconv.Itoa(settings.Height))
|
||||||
|
heightEntry.OnChanged = func(s string) {
|
||||||
|
val, err := strconv.Atoi(s)
|
||||||
|
if err == nil {
|
||||||
|
settings.Height = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateBtn := widget.NewButton("Generate", func() {
|
||||||
|
noiseImg := GenerateHeightmap(settings.Width, settings.Height, int(settings.Detail))
|
||||||
|
canvasWithLakes, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSize)
|
||||||
|
darkenedHeightmap := DarkenLakeAreas(noiseImg, lakePixels)
|
||||||
|
compositeImg := ApplyRoughness(darkenedHeightmap, settings.Roughness)
|
||||||
|
heightmapImg.Image = compositeImg
|
||||||
|
heightmapImg.Refresh()
|
||||||
|
canvasImg.Image = canvasWithLakes
|
||||||
|
canvasImg.Refresh()
|
||||||
|
})
|
||||||
|
|
||||||
|
terrainTab := container.NewTabItem("Terrain", container.NewVBox(
|
||||||
|
detailLabel,
|
||||||
|
detailSlider,
|
||||||
|
roughnessLabel,
|
||||||
|
roughnessSlider,
|
||||||
|
lakesLabel,
|
||||||
|
lakesSlider,
|
||||||
|
lakeSizeLabel,
|
||||||
|
lakeSizeSlider,
|
||||||
|
))
|
||||||
|
|
||||||
|
imageTab := container.NewTabItem("Image", container.NewVBox(
|
||||||
|
widget.NewLabel("Width:"),
|
||||||
|
widthEntry,
|
||||||
|
widget.NewLabel("Height:"),
|
||||||
|
heightEntry,
|
||||||
|
generateBtn,
|
||||||
|
))
|
||||||
|
|
||||||
|
tabs := container.NewAppTabs(
|
||||||
|
imageTab,
|
||||||
|
terrainTab,
|
||||||
|
)
|
||||||
|
|
||||||
|
left := container.NewVBox(
|
||||||
|
widget.NewLabel("Hello World!"),
|
||||||
|
tabs,
|
||||||
|
)
|
||||||
|
|
||||||
|
right := container.NewGridWithRows(2,
|
||||||
|
canvasImg,
|
||||||
|
heightmapImg,
|
||||||
|
)
|
||||||
|
|
||||||
|
split := container.NewHSplit(
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
)
|
||||||
|
split.SetOffset(0.3)
|
||||||
|
|
||||||
|
w.SetContent(split)
|
||||||
|
w.ShowAndRun()
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
Detail float64 `json:"detail"`
|
||||||
|
Roughness float64 `json:"roughness"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
Lakes int `json:"lakes"`
|
||||||
|
LakeSize float64 `json:"lake_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Settings) Save() error {
|
||||||
|
configDir, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
appConfigDir := filepath.Join(configDir, "rpgcitymakerreborn")
|
||||||
|
if err := os.MkdirAll(appConfigDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile := filepath.Join(appConfigDir, "settings.json")
|
||||||
|
file, err := os.Create(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(file)
|
||||||
|
return encoder.Encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadSettings() (*Settings, error) {
|
||||||
|
configDir, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile := filepath.Join(configDir, "rpgcitymakerreborn", "settings.json")
|
||||||
|
file, err := os.Open(configFile)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return &Settings{Detail: 1, Roughness: 0, Width: 300, Height: 300, Lakes: 0, LakeSize: 1}, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var settings Settings
|
||||||
|
decoder := json.NewDecoder(file)
|
||||||
|
if err := decoder.Decode(&settings); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &settings, nil
|
||||||
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aquilax/go-perlin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateLakes(width, height, numLakes int, lakeSize float64) (image.Image, []image.Point) {
|
||||||
|
canvas := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||||
|
draw.Draw(canvas, canvas.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src)
|
||||||
|
var allLakePixels []image.Point
|
||||||
|
|
||||||
|
if numLakes == 0 {
|
||||||
|
return canvas, allLakePixels
|
||||||
|
}
|
||||||
|
|
||||||
|
p := perlin.NewPerlin(2, 2, 5, rand.New(rand.NewSource(time.Now().UnixNano())).Int63())
|
||||||
|
blobSize := int(math.Sqrt(float64(width*height) * (lakeSize / 100.0)))
|
||||||
|
|
||||||
|
for i := 0; i < numLakes; i++ {
|
||||||
|
// Create a noise map for the lake
|
||||||
|
noiseMap := image.NewGray(image.Rect(0, 0, blobSize*2, blobSize*2))
|
||||||
|
for x := 0; x < blobSize*2; x++ {
|
||||||
|
for y := 0; y < blobSize*2; y++ {
|
||||||
|
noise := p.Noise2D(float64(x)/float64(blobSize), float64(y)/float64(blobSize))
|
||||||
|
grayColor := uint8((noise + 1) * 127.5)
|
||||||
|
noiseMap.SetGray(x, y, color.Gray{Y: grayColor})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the largest contiguous area in the noise map
|
||||||
|
var largestLake []*image.Point
|
||||||
|
visited := make([][]bool, blobSize*2)
|
||||||
|
for i := range visited {
|
||||||
|
visited[i] = make([]bool, blobSize*2)
|
||||||
|
}
|
||||||
|
|
||||||
|
for x := 0; x < blobSize*2; x++ {
|
||||||
|
for y := 0; y < blobSize*2; y++ {
|
||||||
|
if !visited[x][y] {
|
||||||
|
c := noiseMap.At(x, y)
|
||||||
|
r, _, _, _ := c.RGBA()
|
||||||
|
if r < 32768 {
|
||||||
|
var currentLake []*image.Point
|
||||||
|
q := []*image.Point{{X: x, Y: y}}
|
||||||
|
visited[x][y] = true
|
||||||
|
|
||||||
|
for len(q) > 0 {
|
||||||
|
p := q[0]
|
||||||
|
q = q[1:]
|
||||||
|
currentLake = append(currentLake, p)
|
||||||
|
|
||||||
|
for dx := -1; dx <= 1; dx++ {
|
||||||
|
for dy := -1; dy <= 1; dy++ {
|
||||||
|
if dx == 0 && dy == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nx, ny := p.X+dx, p.Y+dy
|
||||||
|
if nx >= 0 && nx < blobSize*2 && ny >= 0 && ny < blobSize*2 && !visited[nx][ny] {
|
||||||
|
c := noiseMap.At(nx, ny)
|
||||||
|
r, _, _, _ := c.RGBA()
|
||||||
|
if r < 32768 {
|
||||||
|
visited[nx][ny] = true
|
||||||
|
q = append(q, &image.Point{X: nx, Y: ny})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(currentLake) > len(largestLake) {
|
||||||
|
largestLake = currentLake
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the largest lake on the canvas
|
||||||
|
lakeX := rand.Intn(width)
|
||||||
|
lakeY := rand.Intn(height)
|
||||||
|
for _, p := range largestLake {
|
||||||
|
nx, ny := lakeX+p.X-blobSize, lakeY+p.Y-blobSize
|
||||||
|
if nx >= 0 && nx < width && ny >= 0 && ny < height {
|
||||||
|
canvas.Set(nx, ny, color.RGBA{R: 0, G: 0, B: 255, A: 255})
|
||||||
|
allLakePixels = append(allLakePixels, image.Point{X: nx, Y: ny})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas, allLakePixels
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkenLakeAreas(heightmap image.Image, lakePixels []image.Point) image.Image {
|
||||||
|
bounds := heightmap.Bounds()
|
||||||
|
composite := image.NewRGBA(bounds)
|
||||||
|
draw.Draw(composite, bounds, heightmap, image.Point{}, draw.Src)
|
||||||
|
|
||||||
|
for _, p := range lakePixels {
|
||||||
|
c := composite.At(p.X, p.Y)
|
||||||
|
r, g, b, a := c.RGBA()
|
||||||
|
// Darken by 15%
|
||||||
|
r = uint32(float64(r) * 0.85)
|
||||||
|
g = uint32(float64(g) * 0.85)
|
||||||
|
b = uint32(float64(b) * 0.85)
|
||||||
|
composite.Set(p.X, p.Y, color.RGBA64{R: uint16(r), G: uint16(g), B: uint16(b), A: uint16(a)})
|
||||||
|
}
|
||||||
|
|
||||||
|
return composite
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user