package game import ( "ducky/assets" "ducky/elements" "ducky/fonts" "ducky/gamedata" "fmt" "image/color" "math" "math/rand" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/audio" "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/text/v2" ) const ( screenWidth = 640 screenHeight = 480 duckyWidth = 32 duckyHeight = duckyWidth spotResetDuration = 120 duckySpottedTextOffsetX = 120 duckySpottedTextOffsetY = 32 duckySpottedTextSize = 30 toastCount = 120 ) var ( audioContext = audio.NewContext(assets.SampleRate) ) type Game struct { initialized bool reducky *elements.Ducky ducky *elements.Ducky spotlight *elements.Spotlight cycle int mousepos gamedata.Coordinates darkness *ebiten.Image offscreen *ebiten.Image reduckycol int reduckyrow int spottedticks int spotted bool toast bool toastcounter int disabledarkness bool sliders []*elements.Slider reachcount int musicInitialized bool audioplayer *audio.Player } func (g *Game) Update() error { g.HandleInput() if !g.initialized { g.Initialize() } g.UpdateDuck() g.UpdateDetection() //g.UpdateSliders() g.cycle++ return nil } func (g *Game) Draw(screen *ebiten.Image) { screen.Clear() g.darkness.Clear() if g.initialized { //g.darkness.Fill(color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff}) g.darkness.Fill(color.Black) g.ducky.Draw() g.reducky.Draw() op := &ebiten.DrawImageOptions{} for x := 0; x < screenWidth/duckyWidth; x++ { for y := 0; y < screenHeight/duckyWidth; y++ { op.GeoM.Reset() op.GeoM.Translate(float64(x)*duckyWidth, float64(y)*duckyHeight) if !(x == g.reduckycol && y == g.reduckyrow) { screen.DrawImage(g.ducky.Sprite, op) } } } op = &ebiten.DrawImageOptions{} op.GeoM.Translate(float64(g.reduckycol)*duckyWidth, float64(g.reduckyrow)*duckyHeight) screen.DrawImage(g.reducky.Sprite, op) op = &ebiten.DrawImageOptions{} op.GeoM.Translate(-duckyWidth/2, -duckyHeight/2) op.GeoM.Translate(g.mousepos.X, g.mousepos.Y) //screen.DrawImage(assets.ImageBank[assets.Orb], op) g.spotlight.Draw() g.offscreen.Clear() op = &ebiten.DrawImageOptions{} spotlight_w := float64(g.spotlight.Sprite.Bounds().Dx()) spotlight_h := float64(g.spotlight.Sprite.Bounds().Dy()) op.GeoM.Translate(-spotlight_w/2, -spotlight_h/2) op.GeoM.Translate(g.mousepos.X, g.mousepos.Y) //g.offscreen.DrawImage(assets.ImageBank[assets.Orb], op) g.offscreen.DrawImage(g.spotlight.Sprite, op) op.GeoM.Reset() op.Blend = ebiten.BlendXor g.offscreen.DrawImage(g.darkness, op) if !g.disabledarkness { op = &ebiten.DrawImageOptions{} if g.toast { op.GeoM.Translate(-screenWidth/2, -screenHeight/2) op.GeoM.Rotate(float64(g.toastcounter) / (math.Pi * 2)) op.GeoM.Translate(screenWidth/2, screenHeight/2) } screen.DrawImage(g.offscreen, op) } if g.spotted && !g.toast { font := &text.GoTextFace{ Source: fonts.DuckyFont.Karen, Size: duckySpottedTextSize, } top := &text.DrawOptions{} top.GeoM.Translate(screenWidth/2-duckySpottedTextOffsetX, screenHeight/2-duckySpottedTextOffsetY) if g.cycle%30 < 15 { text.Draw(screen, "DUCKY SPOTTED", font, top) } } for _, s := range g.sliders { s.Draw() op = &ebiten.DrawImageOptions{} op.GeoM.Translate(s.GetPosition().X, s.GetPosition().Y) screen.DrawImage(s.Sprite, op) } } } func (g *Game) Layout(outsideWidth, outsideHeight int) (screenwidth, screenheight int) { return screenWidth, screenHeight } func (g *Game) Initialize() { assets.LoadImages() g.offscreen = ebiten.NewImage(screenWidth, screenHeight) g.darkness = ebiten.NewImage(screenWidth, screenHeight) g.spotlight = elements.NewSpotlight(assets.ImageBank[assets.Orb]) g.ducky = elements.NewDucky(assets.ImageBank[assets.Ducky]) g.reducky = elements.NewDucky(assets.ImageBank[assets.ReDucky]) for i := 0; i < 5; i++ { slider := elements.NewSlider() g.sliders = append(g.sliders, slider) } g.initialized = true if !g.musicInitialized { //s := audio.NewInfiniteLoop(assets.SoundBank[assets.MainLoop], assets.SoundBank[assets.MainLoop].Length()) s := audio.NewInfiniteLoop(assets.SoundBankOgg[assets.MainLoopOgg], assets.SoundBankOgg[assets.MainLoopOgg].Length()) g.audioplayer, _ = audioContext.NewPlayer(s) g.audioplayer.Play() g.musicInitialized = true } g.Reset() } func (g *Game) UpdateDuck() { g.ducky.Update() g.reducky.Update() x, y := ebiten.CursorPosition() g.mousepos.X = float64(x) g.mousepos.Y = float64(y) } func (g *Game) Reset() { g.reduckycol = rand.Intn(screenWidth / duckyHeight) g.reduckyrow = rand.Intn(screenHeight / duckyWidth) for i, slider := range g.sliders { var yoffset float64 if i%2 == 0 { yoffset = screenHeight } else { yoffset = -screenHeight } slider.SetPosition(gamedata.Coordinates{X: float64(i) * screenWidth / 5, Y: yoffset}) slider.SetTargetPosition(gamedata.Coordinates{X: float64(i) * screenWidth / 5, Y: 0}) slider.Reset() } //fmt.Printf("Ducky Position: %d, %d\n", g.reduckycol, g.reduckyrow) g.spottedticks = 0 g.toast = false g.reachcount = 0 g.toastcounter = 0 } func (g *Game) HandleInput() { if inpututil.IsKeyJustPressed(ebiten.KeyR) { g.Reset() } g.disabledarkness = ebiten.IsKeyPressed(ebiten.KeySpace) } func (g *Game) UpdateDetection() { x, y := ebiten.CursorPosition() if g.reduckycol*duckyWidth <= x && x <= (g.reduckycol+1)*duckyWidth && g.reduckyrow*duckyHeight <= y && y <= (g.reduckyrow+1)*duckyHeight && !g.toast { g.spottedticks++ g.spotted = true fmt.Println("ducky spotted!") } else { g.spotted = false } if g.spottedticks > spotResetDuration { g.toast = true } if g.toast { g.UpdateSliders() g.toastcounter++ } if g.toastcounter > toastCount { g.Reset() } } func (g *Game) UpdateSliders() { for _, s := range g.sliders { s.Update() } }