package gg import ( "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" "vultras.su/core/gods/maps" //"fmt" "time" "slices" "sync" ) const ( MaxVertices = 1 << 16 ) type GraphicsLibrary = ebiten.GraphicsLibrary type RunOptions = ebiten.RunGameOptions // The type represents order of drawing. // Higher values are drawn later. type Layer float64 func (l Layer) GetLayer() Layer { return l } // Window configuration type. type WindowConfig struct { DebugInfo ebiten.DebugInfo Options *RunOptions // The title of the window. Title string // Width and height of the window // in pixels. Width, Height int // Optional settings with // self describing names. FixedSize, Fullscreen, VSync bool } type Objects struct { store []Objecter } // The main structure that represents current state of [game] engine. type Engine struct { wcfg *WindowConfig // The main holder for objects. // Uses the map structure to quickly // delete and create new objects. Objects *Objects // The main camera to display in window. // If is set to nil then the engine will panic. Camera *Camera drawLastTime time.Time drawdt Duration // Frame delta time. dt Duration lastTime time.Time // Temporary stuff keys, prevKeys []Key buttons MouseButtonMap wheel Vector cursorPos Vector outerEvents, handleEvents EventChan wg sync.WaitGroup //bufs [LayerBufSize]*Image vertices map[Layer] []ebiten.Vertex //vindices []uint16 // Draw frame. dframe uint // Frame. frame uint runes []rune } type engine Engine // Get currently pressed keys. func (e *Engine) Keys() []Key { return e.keys } func (e *Engine) GraphicsLibrary() GraphicsLibrary { return e.wcfg.DebugInfo.GraphicsLibrary } // Returns currently pressed buttons. func (e *Engine) MouseButtons() []MouseButton { ret := make([]MouseButton, len(e.buttons)) i := 0 for v := range e.buttons { ret[i] = v i++ } slices.Sort(ret) return ret } // Returns new empty Engine. func NewEngine( cfg *WindowConfig, ) *Engine { /*w := Float(cfg.Width) h := Float(cfg.Height)*/ ret := &Engine{} ret.wcfg = cfg ret.Camera = ret.NewCamera() ret.outerEvents = make(EventChan) ret.handleEvents = make(EventChan) ret.Objects = &Objects{} ret.buttons = MouseButtonMap{} return ret } // Get the real window size in the current context. func (c *Engine) RealWinSize() Vector { var w, h int if c.wcfg.Fullscreen { w, h = ebiten.ScreenSizeInFullscreen() } else { w, h = c.wcfg.Width, c.wcfg.Height } return V( Float(w), Float(h), ) } func (c *Engine) AbsWinSize() Vector { return c.RealWinSize().Div(c.Camera.Scale) } func (e *Engine) EventInput() EventChan { return e.outerEvents } // Add new object considering what // interfaces it implements. func (e *Engine) Spawn(b Objecter) error { /*if e.Objects.Has(b) { return ObjectExistErr }*/ b.Start(&Context{Engine: e}) obj := b.GetObject() obj.input = make(chan *Context) go func() { for c := range obj.input { switch c.typ{ case updateContext : b.Update(c) case resolveContext : b.Resolve(c) } e.wg.Done() } }() e.Objects.store = append(e.Objects.store, b) return nil } // Delete object from Engine. func (e *Engine) Del(b Objecter) error { /*if !e.Objects.Has(b) { return ObjectNotExistErr } b.Delete(&Context{Engine: e}) e.Objects.Del(b)*/ return nil } var ( allButtons = []MouseButton{ MouseButton0, MouseButton1, MouseButton2, MouseButton3, MouseButton4, } ) func (e *Engine) IsPressed(k Key) bool { keys := e.Keys() for _, v := range keys { if v == k { return true } } return false } func (e *Engine) IsButtoned(b MouseButton) bool { _, ok := e.buttons[b] return ok } func (e *Engine) Wheel() Vector { return e.wheel } func (e *Engine) cursorPosition() Vector { x, y := ebiten.CursorPosition() return V(Float(x), Float(y)) } func (e *Engine) CursorPosition() Vector { return e.cursorPos } func (e *Engine) AbsCursorPosition() Vector { return e.CursorPosition().Apply(e.Camera.AbsMatrix()) } func (e *Engine) Runes() []rune { return e.runes } func (e *engine) UpdateEvents() []any { eng := (*Engine)(e) e.prevKeys = e.keys e.keys = inpututil. AppendPressedKeys(e.keys[:0]) events := []any{} btns := e.buttons for _, btn := range allButtons { if inpututil.IsMouseButtonJustPressed(btn) { btns[btn] = struct{}{} events = append(events, &MouseButtonDown{ MouseButton: btn, }) } else if inpututil.IsMouseButtonJustReleased(btn) { delete(btns, btn) events = append(events, &MouseButtonUp{ MouseButton: btn, }) } } x, y := ebiten.Wheel() e.wheel = V(x, y) if !(e.wheel.Eq(ZV)) { events = append(events, &WheelChange{ Offset: e.wheel, }) } keyDiff := diffEm(e.prevKeys, e.keys) for _, key := range keyDiff { var event any if eng.IsPressed(key) { event = &KeyDown{ Key: key, } } else { event = &KeyUp{ Key: key, } } events = append(events, event) } realPos := eng.cursorPosition() if !realPos.Eq(e.cursorPos) { absM := eng.Camera.AbsMatrix() absPrevPos := e.cursorPos.Apply(absM) absPos := realPos.Apply(absM) events = append(events, &MouseMove{ Real: realPos.Sub(e.cursorPos), Abs: absPos.Sub(absPrevPos), }) e.cursorPos = realPos } return events } func (e *engine) Update() error { eng := (*Engine)(e) e.dt = time.Since(e.lastTime) e.runes = ebiten.AppendInputChars(e.runes[:0]) //fmt.Println("runes:", e.runes) // Buffering the context for faster. // Providing the events to the objects. // Maybe should think of the better way, // but for it is simple enough. events := e.UpdateEvents() c := &Context{ Engine: eng, typ: updateContext, Events: events, } for _, object := range e.Objects.store { e.wg.Add(1) object.Input() <- c } e.wg.Wait() e.Resolve() e.lastTime = time.Now() e.frame++ return nil } func (e *engine) Resolve() { eng := (*Engine)(e) type resHold struct{ Resolver Resolver input chan *Context } colliders := map[CollisionType] []Collider{} resolvers := []resHold{} for _, object := range e.Objects.store { if object.IsResolvable() { interests := object.GetCollisionInterest() for _, interest := range interests { _, ok := colliders[interest] if !ok { colliders[interest] = []Collider{} } } resolvers = append(resolvers, resHold{ object, object.Input(), }) } } for _, object := range e.Objects.store { if object.IsCollidable() { typ := object.CollisionType() _, ok := colliders[typ] if !ok { continue } colliders[typ] = append(colliders[typ], object) } } for _, resolver := range resolvers { interests := resolver.Resolver.GetCollisionInterest() cols := []Collision{} for _, interest := range interests { cols = append(cols, GetCollisions(resolver.Resolver, colliders[interest])...) } if len(cols) > 0 { c := &Context{ typ: resolveContext, Collisions: cols, Engine: eng, } e.wg.Add(1) resolver.input <- c } } e.wg.Wait() } var ( fullPageIndexes = func() [MaxVertices]uint16 { ret := [MaxVertices]uint16{} for i:=0 ; i