Files
RPG_City_Maker_Reborn/main.go
T

324 lines
9.7 KiB
Go
Raw Normal View History

package main
import (
"fmt"
"image"
"image/color"
"log"
"strconv"
"time"
"image/png"
"os"
"path/filepath"
"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
2026-01-27 12:00:19 -06:00
settings = &Settings{Detail: 1, Roughness: 0, Width: 300, Height: 300, Lakes: 0, LakeSizeLower: 1, LakeSizeUpper: 5}
}
canvasImg := &canvas.Image{
FillMode: canvas.ImageFillContain,
}
heightmapImg := &canvas.Image{
FillMode: canvas.ImageFillContain,
}
configDir, err := os.UserConfigDir()
if err != nil {
log.Fatal("Failed to get user config dir:", err)
}
appConfigDir := filepath.Join(configDir, "rpgcitymakerreborn")
canvasPath := filepath.Join(appConfigDir, "canvas.png")
heightmapPath := filepath.Join(appConfigDir, "heightmap.png")
w.SetOnClosed(func() {
if err := settings.Save(); err != nil {
log.Println("Error saving settings:", err)
}
if canvasImg.Image != nil {
canvasFile, err := os.Create(canvasPath)
if err != nil {
log.Println("Failed to create canvas file:", err)
} else {
defer canvasFile.Close()
if err := png.Encode(canvasFile, canvasImg.Image); err != nil {
log.Println("Failed to encode canvas image:", err)
}
}
}
if heightmapImg.Image != nil {
heightmapFile, err := os.Create(heightmapPath)
if err != nil {
log.Println("Failed to create heightmap file:", err)
} else {
defer heightmapFile.Close()
if err := png.Encode(heightmapFile, heightmapImg.Image); err != nil {
log.Println("Failed to encode heightmap image:", err)
}
}
}
})
canvasFile, err := os.Open(canvasPath)
if err == nil {
defer canvasFile.Close()
img, err := png.Decode(canvasFile)
if err == nil {
canvasImg.Image = img
}
}
heightmapFile, err := os.Open(heightmapPath)
if err == nil {
defer heightmapFile.Close()
img, err := png.Decode(heightmapFile)
if err == nil {
heightmapImg.Image = img
}
}
if canvasImg.Image == nil || heightmapImg.Image == nil {
// Initial image generation
noiseImg := GenerateHeightmap(settings.Width, settings.Height, int(settings.Detail), 100.0, settings.Seed)
lakeImage, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed)
finalImage := lakeImage.(*image.RGBA)
GenerateTrees(finalImage, lakePixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, settings.Seed)
darkenedHeightmap := DarkenLakeAreas(noiseImg, lakePixels)
compositeImg := ApplyRoughness(darkenedHeightmap, settings.Roughness)
heightmapImg.Image = compositeImg
canvasImg.Image = finalImage
}
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))
2026-01-27 12:00:19 -06:00
lakeSizeLowerLabel := widget.NewLabel(fmt.Sprintf("Min Lake Size: %.0f%%", settings.LakeSizeLower))
lakeSizeLowerSlider := widget.NewSlider(1, 100)
lakeSizeUpperLabel := widget.NewLabel(fmt.Sprintf("Max Lake Size: %.0f%%", settings.LakeSizeUpper))
lakeSizeUpperSlider := widget.NewSlider(1, 100)
lakeSizeLowerSlider.OnChanged = func(val float64) {
settings.LakeSizeLower = val
if settings.LakeSizeLower > settings.LakeSizeUpper {
settings.LakeSizeUpper = settings.LakeSizeLower
lakeSizeUpperSlider.SetValue(settings.LakeSizeUpper)
}
lakeSizeLowerLabel.SetText(fmt.Sprintf("Min Lake Size: %.0f%%", settings.LakeSizeLower))
}
2026-01-27 12:00:19 -06:00
lakeSizeLowerSlider.SetValue(settings.LakeSizeLower)
lakeSizeUpperSlider.OnChanged = func(val float64) {
settings.LakeSizeUpper = val
if settings.LakeSizeUpper < settings.LakeSizeLower {
settings.LakeSizeLower = settings.LakeSizeUpper
lakeSizeLowerSlider.SetValue(settings.LakeSizeLower)
}
lakeSizeUpperLabel.SetText(fmt.Sprintf("Max Lake Size: %.0f%%", settings.LakeSizeUpper))
}
lakeSizeUpperSlider.SetValue(settings.LakeSizeUpper)
minTreeSizeLabel := widget.NewLabel(fmt.Sprintf("Min Tree Size: %.0fpx", settings.MinTreeSize))
minTreeSizeSlider := widget.NewSlider(1, 150)
maxTreeSizeLabel := widget.NewLabel(fmt.Sprintf("Max Tree Size: %.0fpx", settings.MaxTreeSize))
maxTreeSizeSlider := widget.NewSlider(1, 150)
minTreeSizeSlider.OnChanged = func(val float64) {
settings.MinTreeSize = val
if settings.MinTreeSize > settings.MaxTreeSize {
settings.MaxTreeSize = settings.MinTreeSize
maxTreeSizeSlider.SetValue(settings.MaxTreeSize)
}
minTreeSizeLabel.SetText(fmt.Sprintf("Min Tree Size: %.0fpx", settings.MinTreeSize))
}
minTreeSizeSlider.SetValue(settings.MinTreeSize)
maxTreeSizeSlider.OnChanged = func(val float64) {
settings.MaxTreeSize = val
if settings.MaxTreeSize < settings.MinTreeSize {
settings.MinTreeSize = settings.MaxTreeSize
minTreeSizeSlider.SetValue(settings.MinTreeSize)
}
maxTreeSizeLabel.SetText(fmt.Sprintf("Max Tree Size: %.0fpx", settings.MaxTreeSize))
}
maxTreeSizeSlider.SetValue(settings.MaxTreeSize)
treeCoverageLabel := widget.NewLabel(fmt.Sprintf("Tree Coverage: %.0f%%", settings.TreeCoverage))
treeCoverageSlider := widget.NewSlider(1, 100)
treeCoverageSlider.OnChanged = func(val float64) {
settings.TreeCoverage = val
treeCoverageLabel.SetText(fmt.Sprintf("Tree Coverage: %.0f%%", settings.TreeCoverage))
}
treeCoverageSlider.SetValue(settings.TreeCoverage)
treeClumpinessLabel := widget.NewLabel(fmt.Sprintf("Tree Clumpiness: %.0f%%", settings.TreeClumpiness))
treeClumpinessSlider := widget.NewSlider(0, 100)
treeClumpinessSlider.OnChanged = func(val float64) {
settings.TreeClumpiness = val
treeClumpinessLabel.SetText(fmt.Sprintf("Tree Clumpiness: %.0f%%", settings.TreeClumpiness))
}
treeClumpinessSlider.SetValue(settings.TreeClumpiness)
errorLabel := canvas.NewText("", color.RGBA{R: 255, A: 255})
errorLabel.TextSize = 12
errorLabel.Hide()
generateBtn := widget.NewButton("Generate", func() {
noiseImg := GenerateHeightmap(settings.Width, settings.Height, int(settings.Detail), 100.0, settings.Seed)
lakeImage, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed)
finalImage := lakeImage.(*image.RGBA)
GenerateTrees(finalImage, lakePixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, settings.Seed)
darkenedHeightmap := DarkenLakeAreas(noiseImg, lakePixels)
compositeImg := ApplyRoughness(darkenedHeightmap, settings.Roughness)
heightmapImg.Image = compositeImg
heightmapImg.Refresh()
canvasImg.Image = finalImage
canvasImg.Refresh()
})
widthEntry := widget.NewEntry()
widthEntry.SetText(strconv.Itoa(settings.Width))
widthEntry.OnChanged = func(s string) {
val, err := strconv.Atoi(s)
if err != nil {
errorLabel.Text = "Width must be a number"
errorLabel.Show()
generateBtn.Disable()
return
}
errorLabel.Hide()
generateBtn.Enable()
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 {
errorLabel.Text = "Height must be a number"
errorLabel.Show()
generateBtn.Disable()
return
}
errorLabel.Hide()
generateBtn.Enable()
settings.Height = val
}
seedEntry := widget.NewEntry()
seedEntry.SetText(strconv.FormatInt(settings.Seed, 10))
seedEntry.OnChanged = func(s string) {
val, err := strconv.ParseInt(s, 10, 64)
if err != nil {
errorLabel.Text = "Seed must be a number"
errorLabel.Show()
generateBtn.Disable()
return
}
errorLabel.Hide()
generateBtn.Enable()
settings.Seed = val
}
randomizeBtn := widget.NewButton("Randomize", func() {
settings.Seed = time.Now().UnixNano()
seedEntry.SetText(strconv.FormatInt(settings.Seed, 10))
})
terrainTab := container.NewTabItem("Terrain", container.NewVBox(
detailLabel,
detailSlider,
roughnessLabel,
roughnessSlider,
lakesLabel,
lakesSlider,
2026-01-27 12:00:19 -06:00
lakeSizeLowerLabel,
lakeSizeLowerSlider,
lakeSizeUpperLabel,
lakeSizeUpperSlider,
minTreeSizeLabel,
minTreeSizeSlider,
maxTreeSizeLabel,
maxTreeSizeSlider,
treeCoverageLabel,
treeCoverageSlider,
treeClumpinessLabel,
treeClumpinessSlider,
))
imageTab := container.NewTabItem("Image", container.NewVBox(
widget.NewLabel("Width:"),
widthEntry,
widget.NewLabel("Height:"),
heightEntry,
widget.NewLabel("Seed:"),
seedEntry,
randomizeBtn,
generateBtn,
errorLabel,
))
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()
}