package tins2020 import ( "fmt" "github.com/veandco/go-sdl2/sdl" ) type terrainRenderer struct { game *Game hover *Point project projection drag Drageable } func NewTerrainRenderer(game *Game) Control { return &terrainRenderer{game: game, project: newProjection()} } func (r *terrainRenderer) Arrange(ctx *Context, _ Rectangle) { r.project.update(ctx.Renderer) } func (r *terrainRenderer) Init(ctx *Context) error { r.game.CenterChanged().RegisterItf(func(state interface{}) { center := state.(Point) r.project.center = center.ToPtF() r.project.update(ctx.Renderer) }) r.project.update(ctx.Renderer) return nil } func isControlKeyDown() bool { state := sdl.GetKeyboardState() return state[sdl.SCANCODE_LCTRL] == 1 || state[sdl.SCANCODE_RCTRL] == 1 } func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) bool { switch e := event.(type) { case *sdl.MouseButtonEvent: if r.project.windowInteractRect.IsPointInside(e.X, e.Y) { if e.Type == sdl.MOUSEBUTTONDOWN { if e.Button == sdl.BUTTON_MIDDLE || (e.Button == sdl.BUTTON_LEFT && isControlKeyDown()) { if !r.drag.IsDragging() { r.drag.Start(Pt(e.X, e.Y)) } } if e.Button == sdl.BUTTON_LEFT { pos := r.project.screenToMapInt(e.X, e.Y) r.game.UserClickedTile(pos) } if e.Button == sdl.BUTTON_RIGHT { if e.Type == sdl.MOUSEBUTTONDOWN { r.game.CancelTool() } } } if e.Type == sdl.MOUSEBUTTONUP { if r.drag.IsDragging() { r.game.Terrain.Center = mapToTile(r.project.center) r.drag.Cancel() } } } case *sdl.MouseMotionEvent: if r.project.windowInteractRect.IsPointInside(e.X, e.Y) { hover := r.project.screenToMapInt(e.X, e.Y) r.hover = &hover } else { r.hover = nil } if r.drag.IsDragging() { delta := r.drag.Move(Pt(e.X, e.Y)) r.project.center = r.project.center.Sub(r.project.screenToMapRel(delta.X, delta.Y)) r.project.update(ctx.Renderer) } case *sdl.MouseWheelEvent: if r.hover != nil { zoom := r.project.zoom if e.Y < 0 && r.project.zoom > .25 { zoom *= .5 } else if e.Y > 0 && r.project.zoom < 2 { zoom *= 2 } if zoom != r.project.zoom { hover := r.hover.ToPtF() r.project.center = hover.Sub(hover.Sub(r.project.center).Mul(r.project.zoom / zoom)) r.project.zoom = zoom r.project.update(ctx.Renderer) } } case *sdl.WindowEvent: if e.Event == sdl.WINDOWEVENT_LEAVE { r.hover = nil r.project.update(ctx.Renderer) } } return false } func (r *terrainRenderer) Render(ctx *Context) { terrain := r.game.Terrain toTileTexture := func(x, y int32) *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) *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) *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 int32) *Texture { variant := terrain.Variant.Value(x, y) flower, ok := terrain.Flowers[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 int32, pos Point) { text := toTileTexture(x, y) rect := r.project.screenToTileRect(pos) text.CopyResize(ctx.Renderer, rect) if r.hover != nil && x == r.hover.X && y == r.hover.Y { ctx.Textures.Texture("tile-hover").CopyResize(ctx.Renderer, rect) } }) r.project.visibleTiles(func(x, y int32, pos 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)) text.CopyResize(ctx.Renderer, r.project.screenToTileRect(pos)) }) }