package tins2020 import ( "fmt" "opslag.de/schobers/zntg/play" "opslag.de/schobers/geom" "opslag.de/schobers/zntg/ui" ) type terrainRenderer struct { ui.ControlBase game *Game hover *geom.Point viewBounds geom.RectangleF32 interactBounds geom.RectangleF32 isometric *play.IsometricProjection drag ui.Dragable } func NewTerrainRenderer(game *Game) ui.Control { renderer := &terrainRenderer{game: game, isometric: play.NewIsometricProjection(geom.PtF32(128, 64), geom.RectF32(0, 0, 100, 100))} renderer.game.CenterChanged().AddHandler(func(ctx ui.Context, state interface{}) { center := state.(geom.Point) renderer.isometric.MoveCenterTo(center.ToF32()) }) return renderer } 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 } func isControlKeyDown(ctx ui.Context) bool { modifiers := ctx.KeyModifiers() return modifiers&(ui.KeyModifierControl|ui.KeyModifierOSCommand) != 0 } func (r *terrainRenderer) Handle(ctx ui.Context, event ui.Event) bool { switch e := event.(type) { case *ui.MouseButtonDownEvent: pos := e.Pos() if pos.In(r.interactBounds) { 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.isometric.ViewToTileInt(pos) r.game.UserClickedTile(pos) } if e.Button == ui.MouseButtonRight { r.game.CancelTool(ctx) } } case *ui.MouseButtonUpEvent: if _, ok := r.drag.IsDragging(); ok { r.game.Terrain.Center = r.isometric.TileInt(r.isometric.Center()) r.drag.Cancel() } case *ui.MouseMoveEvent: pos := e.Pos() if pos.In(r.interactBounds) { hover := r.isometric.ViewToTileInt(pos) r.hover = &hover } else { r.hover = nil } if _, ok := r.drag.IsDragging(); ok { delta, _ := r.drag.Move(pos) r.isometric.Pan(delta.Invert()) } if r.hover != nil { if e.MouseWheel < 0 { r.isometric.ZoomOut(r.hover.ToF32()) } else if e.MouseWheel > 0 { r.isometric.ZoomIn(r.hover.ToF32()) } } case *ui.MouseLeaveEvent: r.hover = nil case *ui.KeyDownEvent: switch e.Key { case ui.KeyPadPlus: r.isometric.ZoomIn(r.isometric.Center()) case ui.KeyMinus: r.isometric.ZoomOut(r.isometric.Center()) case ui.KeyPadMinus: r.isometric.ZoomOut(r.isometric.Center()) case ui.KeyW: r.isometric.PanTile(geom.PtF32(-1, -1)) case ui.KeyA: r.isometric.PanTile(geom.PtF32(-1, 1)) case ui.KeyS: r.isometric.PanTile(geom.PtF32(1, 1)) case ui.KeyD: r.isometric.PanTile(geom.PtF32(1, -1)) } } tool := r.game.Tool().Type() if r.hover != nil { if tool != "none" { ctx.Renderer().SetMouseCursor(ui.MouseCursorPointer) } if tool == "plant-flower" { terrain := r.game.Terrain temp := func() string { temp := terrain.Temp.Value(r.hover.X, r.hover.Y) switch { case temp < .3: return "very cold" case temp < .4: return "cold" case temp > .7: return "very hot" case temp > .6: return "hot" default: return "moderate" } }() humid := func() string { humid := terrain.Humid.Value(r.hover.X, r.hover.Y) switch { case humid < .3: return " and very arid" case humid < .4: return " and arid" case humid > .7: return " and very damp" case humid > .6: return " and damp" default: return "" } }() ctx.ShowTooltip(fmt.Sprintf("It is %s%s over here", temp, humid)) } } return false } func (r *terrainRenderer) Render(ctx ui.Context) { zoom := r.isometric.Zoom() terrain := r.game.Terrain toTileTexture := func(tile geom.Point) ui.Texture { temp := terrain.Temp.Value(tile.X, tile.Y) if temp < .35 { return ctx.Textures().ScaledByName("tile-snow", zoom) } if temp > .65 { return ctx.Textures().ScaledByName("tile-dirt", zoom) } return ctx.Textures().ScaledByName("tile-grass", zoom) } 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().ScaledByName(textName, zoom) } 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().ScaledByName(desc.IconTemplate.Variant(variantToInt(variant)), zoom) } 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 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) } }) r.isometric.EnumerateInt(func(tile geom.Point, view geom.PointF32) { text := toItemTexture(tile.X, tile.Y) if text == nil { return } 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) }) }