109 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			109 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | 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] | ||
|  | } | ||
|  | 
 | ||
|  | type DoorToRemove struct { | ||
|  | 	Entity   ecs.Entity | ||
|  | 	Position *components.Position | ||
|  | } | ||
|  | 
 | ||
|  | func NewDestroyableSystem(world *ecs.World, gridcontainer *grid.GridContainer) 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, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return system | ||
|  | } | ||
|  | 
 | ||
|  | func (system *DestroyableSystem) Update() error { | ||
|  | 	playerobserver := observers.GetPlayerObserver(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 playerobserver.Entities { | ||
|  | 			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.Image = door.GetNext() | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	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.DrawImageOptions{} | ||
|  | 	query := system.Selector.Query(system.World) | ||
|  | 
 | ||
|  | 	for query.Next() { | ||
|  | 		pos, render, _ := query.Get() | ||
|  | 
 | ||
|  | 		op.GeoM.Reset() | ||
|  | 		op.GeoM.Translate(float64(pos.X), float64(pos.Y)) | ||
|  | 		screen.DrawImage(render.Image, op) | ||
|  | 	} | ||
|  | } |