You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
3.2 KiB
99 lines
3.2 KiB
package system |
|
|
|
import ( |
|
"image" |
|
"image/color" |
|
|
|
"code.rocketnine.space/tslocum/commandeuropa/component" |
|
"code.rocketnine.space/tslocum/commandeuropa/world" |
|
"code.rocketnine.space/tslocum/gohan" |
|
"github.com/hajimehoshi/ebiten/v2" |
|
) |
|
|
|
type RenderEnvironment struct { |
|
Once *component.Once |
|
|
|
op *ebiten.DrawImageOptions |
|
initialized bool |
|
} |
|
|
|
func (r *RenderEnvironment) Initialize() { |
|
r.op = &ebiten.DrawImageOptions{} |
|
} |
|
|
|
func (r *RenderEnvironment) Update(e gohan.Entity) error { |
|
return gohan.ErrUnregister |
|
} |
|
|
|
func (r *RenderEnvironment) Draw(e gohan.Entity, screen *ebiten.Image) error { |
|
if !r.initialized { |
|
r.Initialize() |
|
} |
|
|
|
// Clear selection after HandleSelection runs. |
|
world.SelectX, world.SelectY = -1, -1 |
|
|
|
for x := 0.0; x < world.MapSize; x++ { |
|
for y := 0.0; y < world.MapSize; y++ { |
|
i := world.TileIndex(x, y) |
|
t := world.Map[i] |
|
|
|
if t.Sprite == nil { |
|
continue |
|
} |
|
|
|
drawX, drawY := world.LevelCoordinatesToScreen(x*4, y*4) |
|
|
|
// Skip drawing off-screen tiles. |
|
if world.PixelCoordinatesOffScreen(drawX, drawY) { |
|
continue |
|
} |
|
|
|
renderSprite(screen, t.Sprite, x*4, y*4, r.op) |
|
} |
|
} |
|
|
|
highlightX, highlightY := -1.0, -1.0 |
|
if highlightX >= 0 && highlightY >= 0 && highlightX < world.MapSize && highlightY < world.MapSize { |
|
boxX, boxY := world.LevelCoordinatesToScreen(highlightX, highlightY) |
|
right := boxX + world.TileSize*world.CamScale |
|
bottom := boxY + world.TileSize*world.CamScale |
|
|
|
outerColor := color.RGBA{0, 0, 0, 255} |
|
outerSize := 3 * world.CamScale |
|
screen.SubImage(floatRect(boxX, boxY, right, boxY+outerSize)).(*ebiten.Image).Fill(outerColor) |
|
screen.SubImage(floatRect(boxX, bottom-outerSize, right, bottom)).(*ebiten.Image).Fill(outerColor) |
|
screen.SubImage(floatRect(boxX, boxY, boxX+outerSize, bottom)).(*ebiten.Image).Fill(outerColor) |
|
screen.SubImage(floatRect(right-outerSize, boxY, right, bottom)).(*ebiten.Image).Fill(outerColor) |
|
|
|
innerColor := color.RGBA{255, 255, 255, 255} |
|
innerPadding := 1 * world.CamScale |
|
innerSize := 1 * world.CamScale |
|
screen.SubImage(floatRect(boxX+innerPadding, boxY+innerPadding, right-innerPadding, boxY+innerPadding+innerSize)).(*ebiten.Image).Fill(innerColor) |
|
screen.SubImage(floatRect(boxX+innerPadding, bottom-innerPadding-innerSize, right-innerPadding, bottom-innerPadding)).(*ebiten.Image).Fill(innerColor) |
|
screen.SubImage(floatRect(boxX+innerPadding, boxY+innerPadding, boxX+innerPadding+innerSize, bottom-innerPadding)).(*ebiten.Image).Fill(innerColor) |
|
screen.SubImage(floatRect(right-innerPadding-innerSize, boxY+innerPadding, right-innerPadding, bottom-innerPadding)).(*ebiten.Image).Fill(innerColor) |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func renderSprite(target *ebiten.Image, img *ebiten.Image, x float64, y float64, op *ebiten.DrawImageOptions) int { |
|
op.GeoM.Reset() |
|
|
|
// Move to current position. |
|
op.GeoM.Translate(x*4, y*4) |
|
// Translate camera position. |
|
op.GeoM.Translate(float64(-world.CamX), float64(-world.CamY)) |
|
// Zoom. |
|
op.GeoM.Scale(float64(world.CamScale), float64(world.CamScale)) |
|
// Center. |
|
op.GeoM.Translate(float64(world.ScreenWidth/2), float64(world.ScreenHeight/2)) |
|
|
|
target.DrawImage(img, op) |
|
return 1 |
|
} |
|
|
|
func floatRect(x0, y0, x1, y1 float64) image.Rectangle { |
|
return image.Rect(int(x0), int(y0), int(x1), int(y1)) |
|
}
|
|
|