2020-05-09 11:55:37 +00:00
|
|
|
package tins2020
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2020-05-23 08:18:14 +00:00
|
|
|
"opslag.de/schobers/zntg/play"
|
|
|
|
|
2020-05-17 08:56:56 +00:00
|
|
|
"opslag.de/schobers/geom"
|
|
|
|
"opslag.de/schobers/zntg/ui"
|
2020-05-09 11:55:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type terrainRenderer struct {
|
2020-05-17 08:56:56 +00:00
|
|
|
ui.ControlBase
|
|
|
|
|
2020-05-23 08:18:14 +00:00
|
|
|
game *Game
|
|
|
|
hover *geom.Point
|
|
|
|
viewBounds geom.RectangleF32
|
|
|
|
interactBounds geom.RectangleF32
|
|
|
|
isometric *play.IsometricProjection
|
2020-05-09 11:55:37 +00:00
|
|
|
|
2020-05-17 08:56:56 +00:00
|
|
|
drag ui.Dragable
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 08:56:56 +00:00
|
|
|
func NewTerrainRenderer(game *Game) ui.Control {
|
2020-05-23 08:18:14 +00:00
|
|
|
renderer := &terrainRenderer{game: game, isometric: play.NewIsometricProjection(geom.PtF32(128, 64), geom.RectF32(0, 0, 100, 100))}
|
2020-05-17 08:56:56 +00:00
|
|
|
|
|
|
|
renderer.game.CenterChanged().AddHandler(func(ctx ui.Context, state interface{}) {
|
|
|
|
center := state.(geom.Point)
|
2020-05-23 08:18:14 +00:00
|
|
|
renderer.isometric.MoveCenterTo(center.ToF32())
|
2020-05-17 08:56:56 +00:00
|
|
|
})
|
|
|
|
return renderer
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-23 08:18:14 +00:00
|
|
|
func (r *terrainRenderer) Arrange(ctx ui.Context, bounds geom.RectangleF32, _ geom.PointF32, _ ui.Control) {
|
|
|
|
r.viewBounds = geom.RectF32(buttonBarWidth, 0, bounds.Dx()-buttonBarWidth, bounds.Dy())
|
|
|
|
r.isometric.SetViewBounds(r.viewBounds)
|
|
|
|
r.interactBounds = r.viewBounds
|
|
|
|
r.interactBounds.Min.Y += 64
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 08:56:56 +00:00
|
|
|
func isControlKeyDown(ctx ui.Context) bool {
|
2020-05-23 07:11:08 +00:00
|
|
|
modifiers := ctx.KeyModifiers()
|
|
|
|
return modifiers&(ui.KeyModifierControl|ui.KeyModifierOSCommand) != 0
|
2020-05-10 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 08:56:56 +00:00
|
|
|
func (r *terrainRenderer) Handle(ctx ui.Context, event ui.Event) bool {
|
2020-05-09 11:55:37 +00:00
|
|
|
switch e := event.(type) {
|
2020-05-17 08:56:56 +00:00
|
|
|
case *ui.MouseButtonDownEvent:
|
|
|
|
pos := e.Pos()
|
2020-05-23 08:18:14 +00:00
|
|
|
if pos.In(r.interactBounds) {
|
2020-05-17 08:56:56 +00:00
|
|
|
controlKeyDown := isControlKeyDown(ctx)
|
|
|
|
if e.Button == ui.MouseButtonMiddle || (e.Button == ui.MouseButtonLeft && controlKeyDown) {
|
|
|
|
if _, ok := r.drag.IsDragging(); !ok {
|
|
|
|
r.drag.Start(pos)
|
2020-05-10 23:15:48 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-17 08:56:56 +00:00
|
|
|
if e.Button == ui.MouseButtonLeft && !controlKeyDown {
|
2020-05-23 08:18:14 +00:00
|
|
|
pos := r.isometric.ViewToTileInt(pos)
|
2020-05-17 08:56:56 +00:00
|
|
|
r.game.UserClickedTile(pos)
|
|
|
|
}
|
|
|
|
if e.Button == ui.MouseButtonRight {
|
|
|
|
r.game.CancelTool(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case *ui.MouseButtonUpEvent:
|
2020-05-23 08:18:14 +00:00
|
|
|
pos := e.Pos()
|
|
|
|
if pos.In(r.interactBounds) {
|
2020-05-17 08:56:56 +00:00
|
|
|
if _, ok := r.drag.IsDragging(); ok {
|
2020-05-23 08:18:14 +00:00
|
|
|
r.game.Terrain.Center = r.isometric.TileInt(r.isometric.Center())
|
2020-05-17 08:56:56 +00:00
|
|
|
r.drag.Cancel()
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-17 08:56:56 +00:00
|
|
|
case *ui.MouseMoveEvent:
|
|
|
|
pos := e.Pos()
|
2020-05-23 08:18:14 +00:00
|
|
|
if pos.In(r.interactBounds) {
|
|
|
|
hover := r.isometric.ViewToTileInt(pos)
|
2020-05-10 18:44:20 +00:00
|
|
|
r.hover = &hover
|
2020-05-09 22:19:44 +00:00
|
|
|
} else {
|
|
|
|
r.hover = nil
|
|
|
|
}
|
2020-05-17 08:56:56 +00:00
|
|
|
if _, ok := r.drag.IsDragging(); ok {
|
|
|
|
delta, _ := r.drag.Move(pos)
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.Pan(r.isometric.ViewToTileRelative(delta.Invert()))
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
2020-05-10 10:40:44 +00:00
|
|
|
if r.hover != nil {
|
2020-05-18 09:39:27 +00:00
|
|
|
if e.MouseWheel < 0 {
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.ZoomOut(r.hover.ToF32())
|
2020-05-18 09:39:27 +00:00
|
|
|
} else if e.MouseWheel > 0 {
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.ZoomIn(r.hover.ToF32())
|
2020-05-10 10:21:51 +00:00
|
|
|
}
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
2020-05-17 08:56:56 +00:00
|
|
|
case *ui.MouseLeaveEvent:
|
|
|
|
r.hover = nil
|
|
|
|
case *ui.KeyDownEvent:
|
|
|
|
switch e.Key {
|
|
|
|
case ui.KeyPadPlus:
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.ZoomIn(r.isometric.Center())
|
2020-05-17 08:56:56 +00:00
|
|
|
case ui.KeyMinus:
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.ZoomOut(r.isometric.Center())
|
2020-05-17 08:56:56 +00:00
|
|
|
case ui.KeyPadMinus:
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.ZoomOut(r.isometric.Center())
|
2020-05-17 08:56:56 +00:00
|
|
|
case ui.KeyW:
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.Pan(geom.PtF32(-1, -1))
|
2020-05-17 08:56:56 +00:00
|
|
|
case ui.KeyA:
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.Pan(geom.PtF32(-1, 1))
|
2020-05-17 08:56:56 +00:00
|
|
|
case ui.KeyS:
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.Pan(geom.PtF32(1, 1))
|
2020-05-17 08:56:56 +00:00
|
|
|
case ui.KeyD:
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.Pan(geom.PtF32(1, -1))
|
2020-05-11 10:05:54 +00:00
|
|
|
}
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
2020-05-11 09:44:50 +00:00
|
|
|
return false
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 08:56:56 +00:00
|
|
|
func (r *terrainRenderer) Render(ctx ui.Context) {
|
2020-05-23 08:18:14 +00:00
|
|
|
zoom := r.isometric.Zoom()
|
2020-05-11 01:09:01 +00:00
|
|
|
terrain := r.game.Terrain
|
2020-05-23 08:18:14 +00:00
|
|
|
toTileTexture := func(tile geom.Point) ui.Texture {
|
|
|
|
temp := terrain.Temp.Value(tile.X, tile.Y)
|
2020-05-09 11:55:37 +00:00
|
|
|
if temp < .35 {
|
2020-05-23 07:11:08 +00:00
|
|
|
return ctx.Textures().ScaledByName("tile-snow", zoom)
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
|
|
|
if temp > .65 {
|
2020-05-23 07:11:08 +00:00
|
|
|
return ctx.Textures().ScaledByName("tile-dirt", zoom)
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
2020-05-23 07:11:08 +00:00
|
|
|
return ctx.Textures().ScaledByName("tile-grass", zoom)
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-05-17 08:56:56 +00:00
|
|
|
variantToTexture := func(format string, variant float64) ui.Texture {
|
2020-05-09 11:55:37 +00:00
|
|
|
textName := fmt.Sprintf(format, variantToInt(variant))
|
2020-05-23 07:11:08 +00:00
|
|
|
return ctx.Textures().ScaledByName(textName, zoom)
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|
|
|
|
stretch := func(x, from, to float64) float64 { return (x - from) * 1 / (to - from) }
|
|
|
|
|
2020-05-17 08:56:56 +00:00
|
|
|
toPropTexture := func(temp, humid, variant float64) ui.Texture {
|
2020-05-09 11:55:37 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2020-05-17 08:56:56 +00:00
|
|
|
toItemTexture := func(x, y int) ui.Texture {
|
2020-05-11 01:09:01 +00:00
|
|
|
variant := terrain.Variant.Value(x, y)
|
2020-05-17 08:56:56 +00:00
|
|
|
flower, ok := terrain.Flowers[geom.Pt(x, y)]
|
2020-05-09 17:34:43 +00:00
|
|
|
if ok {
|
2020-05-10 22:00:35 +00:00
|
|
|
desc, _ := r.game.Herbarium.Find(flower.ID)
|
2020-05-23 07:11:08 +00:00
|
|
|
return ctx.Textures().ScaledByName(desc.IconTemplate.Variant(variantToInt(variant)), zoom)
|
2020-05-09 17:34:43 +00:00
|
|
|
}
|
2020-05-11 01:09:01 +00:00
|
|
|
temp := terrain.Temp.Value(x, y)
|
|
|
|
humid := terrain.Humid.Value(x, y)
|
2020-05-09 17:34:43 +00:00
|
|
|
return toPropTexture(temp, humid, variant)
|
|
|
|
}
|
|
|
|
|
2020-05-10 08:56:20 +00:00
|
|
|
// horizontal: [0, 128) = 128
|
|
|
|
// vertical (tile): [96,160) = 64
|
|
|
|
// vertical (total): [0,160) = 160
|
2020-05-09 11:55:37 +00:00
|
|
|
|
2020-05-23 08:18:14 +00:00
|
|
|
topLeft := geom.PtF32(-64*zoom, -112*zoom)
|
|
|
|
bottomRight := geom.PtF32(64*zoom, 48*zoom)
|
|
|
|
textureRect := func(center geom.PointF32) geom.RectangleF32 {
|
|
|
|
return geom.RectangleF32{Min: center.Add(topLeft), Max: center.Add(bottomRight)}
|
|
|
|
}
|
|
|
|
hoverTexture := ctx.Textures().ScaledByName("tile-hover", zoom)
|
|
|
|
|
|
|
|
r.isometric.EnumerateInt(func(tile geom.Point, view geom.PointF32) {
|
|
|
|
text := toTileTexture(tile)
|
|
|
|
rect := textureRect(view)
|
|
|
|
ctx.Renderer().DrawTexture(text, rect)
|
|
|
|
// if r.game.Debug {
|
|
|
|
// ctx.Renderer().FillRectangle(view.Add2D(-1, -1).RectRel2D(2, 2), color.White)
|
|
|
|
// ctx.Fonts().TextAlign("debug", view, color.White, fmt.Sprintf("%d, %d", tile.X, tile.Y), ui.AlignCenter)
|
|
|
|
// }
|
|
|
|
if r.hover != nil && tile.X == r.hover.X && tile.Y == r.hover.Y {
|
|
|
|
ctx.Renderer().DrawTexture(hoverTexture, rect)
|
2020-05-09 20:50:11 +00:00
|
|
|
}
|
2020-05-09 14:48:39 +00:00
|
|
|
})
|
|
|
|
|
2020-05-23 08:18:14 +00:00
|
|
|
r.isometric.EnumerateInt(func(tile geom.Point, view geom.PointF32) {
|
|
|
|
text := toItemTexture(tile.X, tile.Y)
|
2020-05-09 14:48:39 +00:00
|
|
|
if text == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-23 08:18:14 +00:00
|
|
|
placeX, placeY := terrain.PlaceX.Value(tile.X, tile.Y), terrain.PlaceY.Value(tile.X, tile.Y)
|
|
|
|
displaced := r.isometric.TileToView(tile.ToF32().Add2D(-.2+.9*float32(placeX)-.45, -.2+.9*float32(placeY)-.45))
|
|
|
|
rect := textureRect(displaced)
|
|
|
|
ctx.Renderer().DrawTexture(text, rect)
|
2020-05-09 14:48:39 +00:00
|
|
|
})
|
2020-05-09 11:55:37 +00:00
|
|
|
}
|