package systems import ( "log/slog" "openquell/components" . "openquell/components" . "openquell/config" "openquell/grid" "openquell/observers" "github.com/hajimehoshi/ebiten/v2" "github.com/mlange-42/arche/ecs" "github.com/mlange-42/arche/generic" ) type DestroyableSystem struct { World *ecs.World Selector *generic.Filter3[Position, Renderable, Destroyable] GridContainer *grid.GridContainer SolidMapper generic.Map4[ // needed for replacement components.Position, components.Renderable, components.Tilish, components.Solid] Cellsize int } type DoorToRemove struct { Entity ecs.Entity Position *components.Position } func NewDestroyableSystem(world *ecs.World, gridcontainer *grid.GridContainer, cellsize int) System { solidmapper := generic.NewMap4[ components.Position, components.Renderable, components.Tilish, components.Solid](world) system := &DestroyableSystem{ Selector: generic.NewFilter3[Position, Renderable, Destroyable](), World: world, GridContainer: gridcontainer, SolidMapper: solidmapper, Cellsize: cellsize, } return system } func (system *DestroyableSystem) Update() error { observer := observers.GetGameObserver(system.World) posID := ecs.ComponentID[components.Position](system.World) veloID := ecs.ComponentID[components.Velocity](system.World) query := system.Selector.Query(system.World) EntitiestoRemove := []*DoorToRemove{} for query.Next() { doorposition, renderable, door := query.Get() for _, player := range observer.GetPlayers() { if !system.World.Alive(player) { continue } playerposition := (*Position)(system.World.Get(player, posID)) playervelocity := (*Velocity)(system.World.Get(player, veloID)) ok, newpos := doorposition.Intersects(playerposition, playervelocity) if ok { // player bumped into hidden wall, activate it, snap in player slog.Debug("bump not die", "originalpos", playerposition) playervelocity.Change(Stop) playerposition.Set(newpos) if door.Activated { // player bumps into the door a second time, now hide it EntitiestoRemove = append(EntitiestoRemove, &DoorToRemove{Entity: query.Entity(), Position: doorposition}) } else { slog.Debug("activating destroyable", "doorpos", doorposition) door.Activated = true renderable.Damaged = 1 } } } } for _, door := range EntitiestoRemove { slog.Debug("hiding destroyable", "doorpos", door.Position.Point()) // remove door entity system.World.RemoveEntity(door.Entity) } return nil } func (system *DestroyableSystem) Draw(screen *ebiten.Image) { // write transients (these are no tiles!) op := &ebiten.DrawRectShaderOptions{} query := system.Selector.Query(system.World) for query.Next() { pos, render, _ := query.Get() op.GeoM.Reset() op.Uniforms = map[string]any{ "Damaged": render.Damaged, } op.Images[0] = render.Image op.Images[1] = render.DamageImage op.GeoM.Translate(float64(pos.X), float64(pos.Y)) screen.DrawRectShader(system.Cellsize, system.Cellsize, render.Shader, op) } }