tins2020/terrainrenderer.go

222 lines
5.8 KiB
Go

package tins2020
import (
"fmt"
"opslag.de/schobers/geom"
"opslag.de/schobers/zntg/ui"
)
type terrainRenderer struct {
ui.ControlBase
game *Game
hover *geom.Point
project projection
drag ui.Dragable
}
func NewTerrainRenderer(game *Game) ui.Control {
renderer := &terrainRenderer{game: game, project: newProjection()}
renderer.game.CenterChanged().AddHandler(func(ctx ui.Context, state interface{}) {
center := state.(geom.Point)
renderer.project.center = center.ToF32()
renderer.project.update(ctx.Renderer())
})
// renderer.project.update(ctx.Renderer)
return renderer
}
func (r *terrainRenderer) Arrange(ctx ui.Context, _ geom.RectangleF32, _ geom.PointF32, _ ui.Control) {
r.project.update(ctx.Renderer())
}
func (r *terrainRenderer) Init(ctx ui.Context) error {
return nil
}
func isControlKeyDown(ctx ui.Context) bool {
return false
// ctx.MousePosition()
// state := ui.GetKeyboardState()
// return state[ui.SCANCODE_LCTRL] == 1 || state[ui.SCANCODE_RCTRL] == 1 || state[ui.SCANCODE_LGUI] == 1 || state[ui.SCANCODE_RGUI] == 1
}
func (r *terrainRenderer) Handle(ctx ui.Context, event ui.Event) bool {
switch e := event.(type) {
case *ui.MouseButtonDownEvent:
pos := e.Pos()
if pos.ToInt().In(r.project.windowInteractRect) {
controlKeyDown := isControlKeyDown(ctx)
if e.Button == ui.MouseButtonMiddle || (e.Button == ui.MouseButtonLeft && controlKeyDown) {
if _, ok := r.drag.IsDragging(); !ok {
r.drag.Start(pos)
}
}
if e.Button == ui.MouseButtonLeft && !controlKeyDown {
pos := r.project.screenToMapInt(int(e.X), int(e.Y))
r.game.UserClickedTile(pos)
}
if e.Button == ui.MouseButtonRight {
r.game.CancelTool(ctx)
}
}
case *ui.MouseButtonUpEvent:
pos := e.Pos().ToInt()
if pos.In(r.project.windowInteractRect) {
if _, ok := r.drag.IsDragging(); ok {
r.game.Terrain.Center = mapToTile(r.project.center)
r.drag.Cancel()
}
}
case *ui.MouseMoveEvent:
pos := e.Pos()
if pos.ToInt().In(r.project.windowInteractRect) {
hover := r.project.screenToMapInt(int(e.X), int(e.Y))
r.hover = &hover
} else {
r.hover = nil
}
if _, ok := r.drag.IsDragging(); ok {
delta, _ := r.drag.Move(pos)
r.project.center = r.project.center.Sub(r.project.screenToMapRel(int(delta.X), int(delta.Y)))
r.project.update(ctx.Renderer())
}
if r.hover != nil {
if e.Y < 0 {
r.project.ZoomOut(ctx, r.hover.ToF32())
} else {
r.project.ZoomIn(ctx, r.hover.ToF32())
}
}
case *ui.MouseLeaveEvent:
r.hover = nil
r.project.update(ctx.Renderer())
case *ui.KeyDownEvent:
switch e.Key {
case ui.KeyPadPlus:
r.project.ZoomIn(ctx, r.project.center)
case ui.KeyMinus:
r.project.ZoomOut(ctx, r.project.center)
case ui.KeyPadMinus:
r.project.ZoomOut(ctx, r.project.center)
case ui.KeyW:
r.project.Pan(ctx, geom.PtF32(-1, -1))
case ui.KeyA:
r.project.Pan(ctx, geom.PtF32(-1, 1))
case ui.KeyS:
r.project.Pan(ctx, geom.PtF32(1, 1))
case ui.KeyD:
r.project.Pan(ctx, geom.PtF32(1, -1))
}
}
return false
}
func (r *terrainRenderer) Render(ctx ui.Context) {
terrain := r.game.Terrain
toTileTexture := func(x, y int) ui.Texture {
temp := terrain.Temp.Value(x, y)
if temp < .35 {
return ctx.Textures().Texture("tile-snow")
}
if temp > .65 {
return ctx.Textures().Texture("tile-dirt")
}
return ctx.Textures().Texture("tile-grass")
}
variantToInt := func(variant float64) int {
if variant < .25 {
return 1
}
if variant < .5 {
return 2
}
if variant < .75 {
return 3
}
if variant < 1 {
return 4
}
return -1
}
variantToTexture := func(format string, variant float64) ui.Texture {
textName := fmt.Sprintf(format, variantToInt(variant))
return ctx.Textures().Texture(textName)
}
stretch := func(x, from, to float64) float64 { return (x - from) * 1 / (to - from) }
toPropTexture := func(temp, humid, variant float64) ui.Texture {
if temp < .35 {
if humid < .2 {
return nil
} else if humid < .7 {
return variantToTexture("bush-small-%d", variant*5)
}
return variantToTexture("tree-pine-%d", variant*5)
}
if temp > .65 {
if humid < .2 {
return nil
}
if humid < .7 {
return variantToTexture("cactus-short-%d", variant*7)
}
return variantToTexture("cactus-tall-%d", variant*2)
}
if humid < .2 {
return nil
}
multiplier := 1 - stretch(humid, 0.2, 1)
if variant < .5 {
return variantToTexture("tree-fat-%d", stretch(variant, 0, .5)*multiplier*3)
} else if variant < .8 {
return variantToTexture("grass-small-%d", stretch(variant, .5, .8)*multiplier*2)
}
return variantToTexture("bush-large-%d", stretch(variant, .8, 1)*multiplier)
}
toItemTexture := func(x, y int) ui.Texture {
variant := terrain.Variant.Value(x, y)
flower, ok := terrain.Flowers[geom.Pt(x, y)]
if ok {
desc, _ := r.game.Herbarium.Find(flower.ID)
return ctx.Textures().Texture(desc.IconTemplate.Variant(variantToInt(variant)))
}
temp := terrain.Temp.Value(x, y)
humid := terrain.Humid.Value(x, y)
return toPropTexture(temp, humid, variant)
}
// horizontal: [0, 128) = 128
// vertical (tile): [96,160) = 64
// vertical (total): [0,160) = 160
r.project.visibleTiles(func(x, y int, pos geom.Point) {
text := toTileTexture(x, y)
rect := r.project.screenToTileRect(pos)
ctx.Renderer().DrawTexture(text, rect.ToF32())
if r.hover != nil && x == r.hover.X && y == r.hover.Y {
ctx.Renderer().DrawTexture(ctx.Textures().Texture("tile-hover"), rect.ToF32())
}
})
r.project.visibleTiles(func(x, y int, pos geom.Point) {
text := toItemTexture(x, y)
if text == nil {
return
}
placeX, placeY := terrain.PlaceX.Value(x, y), terrain.PlaceY.Value(x, y)
pos = r.project.mapToScreenF(float32(x)-.2+float32(.9*placeX-.45), float32(y)-.2+float32(.9*placeY-.45))
rect := r.project.screenToTileRect(pos)
ctx.Renderer().DrawTexture(text, rect.ToF32())
})
}