package tins2020 import ( "fmt" "github.com/veandco/go-sdl2/sdl" ) type terrainRenderer struct { game *Game terrain *Map hover *Point project projection drag Drageable } type Drageable struct { start *Point dragged bool } func (d *Drageable) Cancel() { d.start = nil } func (d *Drageable) IsDragging() bool { return d.start != nil } func (d *Drageable) HasDragged() bool { return d.dragged } func (d *Drageable) Move(p Point) Point { delta := p.Sub(*d.start) d.start = &p d.dragged = true return delta } func (d *Drageable) Start(p Point) { d.start = &p d.dragged = false } func NewTerrainRenderer(game *Game) Control { return &terrainRenderer{game: game, terrain: game.Terrain, project: newProjection()} } func (r *terrainRenderer) Arrange(ctx *Context, _ Rectangle) { r.project.update(ctx.Renderer) } func (r *terrainRenderer) Init(ctx *Context) error { r.project.update(ctx.Renderer) return nil } func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) { switch e := event.(type) { case *sdl.MouseButtonEvent: if r.project.windowInteractRect.IsPointInside(e.X, e.Y) { switch e.Button { case sdl.BUTTON_LEFT: down := e.Type == sdl.MOUSEBUTTONDOWN if down && !r.drag.IsDragging() { r.drag.Start(Pt(e.X, e.Y)) } else if !down && r.drag.IsDragging() { r.drag.Cancel() } if e.Type == sdl.MOUSEBUTTONUP && !r.drag.HasDragged() { pos := r.project.screenToMapInt(e.X, e.Y) r.game.UserClickedTile(pos) } case sdl.BUTTON_RIGHT: if e.Type == sdl.MOUSEBUTTONDOWN { r.game.CancelTool() } } } 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) } } } } func (r *terrainRenderer) Render(ctx *Context) { toTileTexture := func(x, y int32) *Texture { temp := r.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 := r.terrain.Variant.Value(x, y) _, ok := r.terrain.Flowers[Pt(x, y)] if ok { return variantToTexture("flower-anemone-%d", variant) } temp := r.terrain.Temp.Value(x, y) humid := r.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 := r.terrain.PlaceX.Value(x, y), r.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)) }) // gfx.RectangleColor(ctx.Renderer, r.project.windowRect.X, r.project.windowRect.Y, r.project.windowRect.X+r.project.windowRect.W, r.project.windowRect.Y+r.project.windowRect.H, sdl.Color{R: 255, A: 255}) // for y := int32(-40); y < 40; y++ { // for x := int32(-40); x < 40; x++ { // pos := r.project.mapToScreen(x, y) // ctx.Fonts.Font("debug").RenderCopy(ctx.Renderer, fmt.Sprintf("(%d, %d)", x, y), pos, sdl.Color{R: 255, A: 255}) // } // } }