Real-time strategy (RTS) video game
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

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))
}