alchemyst/engine/ecs/systems/level_render_system.go
2023-08-02 00:09:54 +03:00

161 lines
3.9 KiB
Go

package systems
import (
"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/engine/fov"
"lab.zaar.be/thefish/alchemyst-go/engine/gamestate"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/ui/mainwindow"
"sort"
"time"
)
var fovRecompute = true
var redraw = false
type LevelRenderSystem struct {
Viewport *mainwindow.ViewPort
Controller *ecs.Controller
state *gamestate.GameState
layer *mainwindow.Layer
fov fov.Fov
animateTiles *time.Ticker
TorchRadius int
}
func NewLevelRenderSystem(
state *gamestate.GameState,
controller *ecs.Controller,
vp *mainwindow.ViewPort,
layer *mainwindow.Layer,
fov fov.Fov,
) LevelRenderSystem {
trs := LevelRenderSystem{
Viewport: vp,
Controller: controller,
layer: layer,
state: state,
fov: fov,
}
trs.TorchRadius = 12 //fixme move to sight component
trs.animateTiles = time.NewTicker(time.Second / 12)
return trs
}
// fixme add to screens/game Exit()
func (trs LevelRenderSystem) Close() {
trs.animateTiles.Stop()
trs.animateTiles = nil //zero pointer to ticker
}
func (trs LevelRenderSystem) Listen() {
for {
select {
case <-trs.state.FovRecompute:
fovRecompute = true
case <-trs.state.Redraw:
redraw = true
case <-trs.animateTiles.C:
redraw = true
}
}
}
type priorityRenderable struct {
types.Appearance
types.Coords
Priority int //with bigger are rendered last
}
type prioritySorter []priorityRenderable
func (a prioritySorter) Len() int { return len(a) }
func (a prioritySorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a prioritySorter) Less(i, j int) bool { return a[i].Priority < a[j].Priority }
func (trs LevelRenderSystem) Process() {
playerCoords := trs.state.Controller.GetComponent(trs.state.Player, ecs.CoordsComponent).(types.Coords)
trs.Viewport.Move(trs.state, playerCoords)
if fovRecompute {
trs.layer.ClearRect(trs.Viewport.Rect)
trs.fov.ComputeFov(trs.state.Level, playerCoords, trs.TorchRadius)
fovRecompute = false
redraw = true
}
if redraw {
//terrain
for y := 0; y < trs.Viewport.H; y++ {
for x := 0; x < trs.Viewport.W; x++ {
mapCoords := types.Coords{X: trs.Viewport.CameraCoords.X + x, Y: trs.Viewport.CameraCoords.Y + y}
if trs.state.Level.InBounds(mapCoords) {
tile := trs.state.Level.GetTile(mapCoords)
if tile.Explored || tile.Visible {
trs.layer.PutToBase(
x+trs.Viewport.X,
y+trs.Viewport.Y,
tile.GetChar(),
tile.GetRawColor(),
tile.GetRawBgColor(),
)
}
}
}
}
//mobs
entToRender := make([]priorityRenderable, 0)
for e := range trs.Controller.GetEntities() {
if trs.Controller.HasComponent(e, ecs.CoordsComponent) &&
trs.Controller.HasComponent(e, ecs.AppearanceComponent) {
pos := trs.Controller.GetComponent(e, ecs.CoordsComponent).(types.Coords)
appearance := trs.Controller.GetComponent(e, ecs.AppearanceComponent).(types.Appearance)
//fixme fov check
if !trs.state.Level.GetTile(pos).Visible {
continue
}
vpc, err := trs.Viewport.ToVPCoords(pos)
if err != nil {
continue //we cant see it? no problem.
}
//Костыль для приоритета отрисовки
p := 0
if trs.Controller.HasComponent(e, ecs.CarriedComponent) {
p = 10
}
if trs.Controller.HasComponent(e, ecs.MoveableComponent) {
p = 100
}
entToRender = append(entToRender, priorityRenderable{
Appearance: appearance,
Coords: vpc,
Priority: p,
})
}
}
sort.Sort(prioritySorter(entToRender))
for _, ree := range entToRender {
trs.layer.WithRawColor(ree.Appearance.ColorSet.Fg.GetColor()).
Put(ree.X, ree.Y, ree.Appearance.Glyph.GetGlyph())
}
entToRender = nil
redraw = false
}
}
func (trs LevelRenderSystem) SystemType() string {
return ecs.TerrainRenderSystem
}