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 { 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.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.MouseWheel < 0 { r.project.ZoomOut(ctx, r.hover.ToF32()) } else if e.MouseWheel > 0 { 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) { zoom := r.project.zoom terrain := r.game.Terrain toTileTexture := func(x, y int) ui.Texture { temp := terrain.Temp.Value(x, 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 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().ScaledByName("tile-hover", zoom), 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()) // ctx.Fonts().Text("debug", pos.ToF32(), color.White, fmt.Sprintf("%d, %d", x, y)) }) }