diff --git a/ease.go b/ease.go new file mode 100644 index 0000000..b531fbc --- /dev/null +++ b/ease.go @@ -0,0 +1,21 @@ +package main + +import ( + "math" +) + +func easeInOutCubic(x float32) float32 { + if x < 0.5 { + return 4 * x * x * x + } else { + return 1 - float32(math.Pow(float64(-2*x+2), 3))/2 + } +} + +func lerp(v0, v1, t float32) float32 { + return (1-t)*v0 + t*v1 +} + +func easeInOutSine(x float32) float32 { + return -float32(math.Cos(math.Pi*float64(x))-1) / 2 +} diff --git a/go.mod b/go.mod index 4a29b8c..7fe4ecd 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module danmaku -go 1.20 +go 1.23 require github.com/gen2brain/raylib-go/raylib v0.0.0-20230719211022-1083eace2049 diff --git a/main.go b/main.go index 2bd9be0..5157ded 100644 --- a/main.go +++ b/main.go @@ -8,15 +8,16 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" ) -// diz para um inimigo como ele deve se mover -type movementPattern func(*enemy) rl.Vector2 - // diz para um inimigo em que condições atirar. acionado pelo movementPattern type shootingPattern func(*enemy) // diz para uma bala como ela deve se mover. type bulletMovementPattern func(*bullet) rl.Vector2 +type duration float32 + +const second duration = 1 + type hazard interface { // inimigos e projéteis Pos() rl.Vector2 } @@ -28,6 +29,7 @@ type game struct { frame float32 enemies []*enemy waves []*wave + walls []plane currentWave int bullets []*bullet score int @@ -35,6 +37,11 @@ type game struct { backgroundColor rl.Color } +type plane struct { + normal rl.Vector2 + pos rl.Vector2 +} + type bullet struct { pos rl.Vector2 speed rl.Vector2 @@ -63,10 +70,42 @@ type enemy struct { } type wave struct { + duration *timer enemies []*enemy entrance movementPattern } +type timer struct { + ttl duration + time float32 + start float64 +} + +func (t *timer) tick(g *game) { + t.time += rl.GetFrameTime() * g.gameSpeed +} + +func (t *timer) isTimeout() bool { + return t.time >= float32(t.ttl) +} + +func (t *timer) reset() { + t.start = float64(t.time) + t.time = 0 +} + +func (t *timer) unit() float32 { + return t.time / float32(t.ttl) +} + +func newTimer(duration duration) *timer { + return &timer{ + time: 0, + ttl: duration, + start: rl.GetTime(), + } +} + func (g game) insideArena(v rl.Vector2) bool { return v.X >= 0 && v.Y >= 0 && v.Y <= float32(g.arenaHeight) && v.X <= float32(g.arenaWidth) @@ -89,7 +128,7 @@ func (b *bullet) update(g *game, index int) { } func bulletExplosion(g *game, rate, amount int, bulletSpeed, size float32) shootingPattern { - t := newTimer(second) + t := newTimer(second * duration(rate)) return func(e *enemy) { t.tick(g) @@ -140,51 +179,17 @@ func ShootAtPlayer(g *game, p *player, rate int, } } -type timer struct { - time, ttl float32 - start float64 -} - -func (t *timer) tick(g *game) { - t.time += rl.GetFrameTime() * g.gameSpeed -} - -func (t *timer) isTimeout() bool { - return t.time >= t.ttl -} - -func (t *timer) reset() { - t.start = float64(t.time) - t.time = 0 -} - -func newTimer(duration float32) *timer { - return &timer { - time: 0, - ttl: duration, - start: rl.GetTime(), - } -} - -var second float32 = 1 - func burstShootAtPlayer(g *game, p *player, rate float32, bulletMoveSpeed float32) shootingPattern { flag := true off := newTimer(second) - on := newTimer(second*rate) + on := newTimer(duration(float32(second) * rate)) return func(e *enemy) { - fmt.Printf("\r %f %f", rl.GetTime(), rl.GetFrameTime()) - off.tick(g) if off.isTimeout() { flag = !flag - fmt.Println() - fmt.Println(off) off.reset() - fmt.Println(off) - fmt.Println() } if !flag { @@ -226,86 +231,59 @@ func shootStraightDown(g *game) shootingPattern { } } -func shootStill() movementPattern { - return func(e *enemy) rl.Vector2 { - e.shoot(e) - return rl.Vector2{X: 0, Y: 0} - } -} +// func detectWallCollision(planes []plane, initialPos, direction rl.Vector2) float32 { +// p := initialPos +// w := direction +// var closest = math.Inf(1) +// for _, plane := range planes { +// c := plane.pos +// n := plane.normal +// denominator := rl.Vector2DotProduct(w, n) +// if denominator == 0 { +// continue +// } +// t := rl.Vector2DotProduct(rl.Vector2Subtract(p, c), n) / denominator +// closest = min(float64(t), closest) +// } +// return float32(-closest) +// } -func horizonalPattern(g *game) movementPattern { - direction := rl.Vector2{X: 4, Y: 0} - - return func(e *enemy) rl.Vector2 { - e.shoot(e) - - result := rl.Vector2Add(direction, e.pos) - if !g.insideArena(result) { - direction = rl.Vector2Negate(direction) - } - return direction - } -} - -func sineHorizonalPattern(g *game) movementPattern { - direction := rl.Vector2{X: 4, Y: 0} - - return func(e *enemy) rl.Vector2 { - if e.shoot != nil { - e.shoot(e) - } - - - result := rl.Vector2Add(direction, e.pos) - if !g.insideArena(result) { - direction = rl.Vector2Negate(direction) - } - - sine := rl.Vector2{ - X: 0, - Y: float32(math.Sin(float64(g.frame*0.04))*2), - } - - return rl.Vector2Add(sine, direction) - } -} - -func foobarPattern(g *game) movementPattern { - - pos := rl.Vector2{} - state := 0 - wait := 0 - - return func(e *enemy) rl.Vector2 { - - switch state { - case 0: // init - pos.X = e.pos.X - state = 1 - return pos - case 1: // descer - pos.Y += 1 - if pos.Y >= 100 { state = 2; wait = 50 } - return pos - case 2: // atirar por um tempo - e.shoot(e) - wait -= 1 - if wait <= 0 { state = 3; wait = 60 } - return pos - case 3: // wait - wait -= 1 - if wait <= 0 { state = 4 } - return pos - case 4: // retornar - pos.Y -= 3 - if pos.Y - e.hitBoxRadius < 0 { - e.health = 0 - } - return pos - } - panic(state) - } -} +// func foobarPattern(g *game) movementPattern { +// +// pos := rl.Vector2{} +// state := 0 +// wait := 0 +// +// return func(e *enemy) rl.Vector2 { +// +// switch state { +// case 0: // init +// pos.X = e.pos.X +// state = 1 +// return pos +// case 1: // descer +// pos.Y += 1 +// if pos.Y >= 100 { state = 2; wait = 50 } +// return pos +// case 2: // atirar por um tempo +// e.shoot(e) +// wait -= 1 +// if wait <= 0 { state = 3; wait = 60 } +// return pos +// case 3: // wait +// wait -= 1 +// if wait <= 0 { state = 4 } +// return pos +// case 4: // retornar +// pos.Y -= 3 +// if pos.Y - e.hitBoxRadius < 0 { +// e.health = 0 +// } +// return pos +// } +// panic(state) +// } +// } func (e *enemy) checkHit(g *game) (bool, *bullet, int) { for index, bullet := range g.bullets { @@ -337,7 +315,8 @@ func (e *enemy) update(g *game) { enemyColor := rl.Blue if e.move != nil { - e.pos = rl.Vector2Add(rl.Vector2Scale(e.move(e), g.gameSpeed), e.pos) + // e.pos = rl.Vector2Add(rl.Vector2Scale(e.move(e), g.gameSpeed), e.pos) + e.move(e) // e.pos = e.move(e) } @@ -349,129 +328,9 @@ func (e *enemy) update(g *game) { g.backgroundColor = rl.NewColor(20, 20, 20, 255) } - rl.DrawCircleV(e.pos, e.hitBoxRadius, enemyColor) } -func (p *player) move(g *game) { - - var moveSpeed float32 - if p.focusMode { - moveSpeed = p.moveSpeed * p.focusSpeedDecrease - } else { - moveSpeed = p.moveSpeed - } - - p.speed = rl.Vector2{X: 0, Y: 0} - - if rl.IsKeyDown(rl.KeyW) { - p.speed.Y -= 1 - } - if rl.IsKeyDown(rl.KeyS) { - p.speed.Y += 1 - } - if rl.IsKeyDown(rl.KeyA) { - p.speed.X -= 1 - } - if rl.IsKeyDown(rl.KeyD) { - p.speed.X += 1 - } - - if !(p.speed.X == 0 && p.speed.Y == 0) { - // jogador se move mais rapido na diagonal caso o contrario - p.speed = rl.Vector2Normalize(p.speed) - p.speed = rl.Vector2Scale(p.speed, moveSpeed) - } - - result := rl.Vector2Add(p.pos, p.speed) - - if result.Y-p.hitBoxRadius < 0 || - result.Y+p.hitBoxRadius > float32(g.arenaHeight) { - p.speed.Y = 0 - } - if result.X-p.hitBoxRadius < 0 || - result.X+p.hitBoxRadius > float32(g.arenaWidth) { - p.speed.X = 0 - } - - p.pos = rl.Vector2Add(p.pos, p.speed) -} - -func (p *player) shoot(g *game) { - if p.focusMode { - return - } - - if int(g.frame)%3 != 0 { - return - } - - if rl.IsMouseButtonDown(rl.MouseLeftButton) { - mouse := rl.GetMousePosition() - direction := rl.Vector2Subtract(mouse, p.pos) - direction = rl.Vector2Add(direction, p.speed) - direction = rl.Vector2Normalize(direction) - direction = rl.Vector2Scale(direction, p.bulletMoveSpeed) - - g.bullets = append(g.bullets, &bullet{ - pos: p.pos, - size: p.bulletSize, - speed: direction, - dmg: 1, - enemy: false, - }) - } -} - -func (p *player) checkHit(g *game) { - for _, bullet := range g.bullets { - if !bullet.enemy { - continue - } - distance := rl.Vector2Distance(p.pos, bullet.pos) - bullet.size - // fmt.Println(distance) - - if distance < p.hitBoxRadius { - // fmt.Println("hit!") - } - } -} - -func (p *player) checkFocus() { - // raylib não entende keybindings customizadas através do xmodmap? - if rl.IsKeyDown(rl.KeyLeftShift) || rl.IsKeyDown(rl.KeyRightShift) { - p.focusMode = true - } else { - p.focusMode = false - } -} - -func (p *player) update(g *game) { - - p.checkFocus() - - p.move(g) - - p.shoot(g) - - p.checkHit(g) - - // hitbox - rl.DrawCircleV(p.pos, p.hitBoxRadius, rl.Red) - - if p.focusMode { - return - } - // mira - mouse := rl.GetMousePosition() - inverted := rl.Vector2Subtract(p.pos, mouse) - inverted = rl.Vector2Negate(inverted) - inverted = rl.Vector2Normalize(inverted) - inverted = rl.Vector2Scale(inverted, 2000) - rl.DrawLineV(p.pos, rl.Vector2Add(mouse, inverted), rl.NewColor(255, 0, 0, 100)) - -} - func (g *game) fullClear() bool { for _, e := range g.waves[g.currentWave].enemies { if e.health != 0 { @@ -481,12 +340,41 @@ func (g *game) fullClear() bool { return true } +func (g *game) waveTimeout() bool { + currentWave := g.waves[g.currentWave] + if currentWave.duration == nil { + return false + } + if currentWave.duration.isTimeout() { + return true + } + currentWave.duration.tick(g) + return false +} + +func (g *game) wavesOver() bool { + return g.currentWave == len(g.waves) +} + +func (g *game) nextWave() bool { + g.currentWave++ + if g.wavesOver() { + return false + } + g.addEnemy(g.waves[g.currentWave].enemies...) + return true +} + +func (g *game) addEnemy(e ...*enemy) { + g.enemies = append(g.enemies, e...) +} + func main() { state := &game{ arenaWidth: 450, arenaHeight: 700, - interfaceWidth: 300, + interfaceWidth: 400, gameSpeed: 1, backgroundColor: rl.NewColor(0, 0, 0, 100), } @@ -502,6 +390,26 @@ func main() { hitBoxRadius: 5, } + var arena = []plane{ + { + normal: rl.Vector2{X: 0, Y: -1}, + pos: rl.Vector2{X: 1, Y: 0}, + }, + { + normal: rl.Vector2{X: -1, Y: 0}, + pos: rl.Vector2{X: float32(state.arenaWidth), Y: 0}, + }, + { + normal: rl.Vector2{X: 0, Y: 1}, + pos: rl.Vector2{X: 0, Y: float32(state.arenaHeight)}, + }, + { + normal: rl.Vector2{X: 1, Y: 0}, + pos: rl.Vector2{X: 0, Y: 1}, + }, + } + state.walls = arena + // rl.SetTraceLog(rl.LogWarning | rl.LogDebug) rl.SetConfigFlags(rl.FlagMsaa4xHint) @@ -517,26 +425,65 @@ func main() { // state.arenaHeight, // ) + // descentAndSine := jjjjjj + state.waves = []*wave{ { + duration: newTimer(second * 10), enemies: []*enemy{ { - pos: rl.Vector2{X: 100, Y: 100}, + pos: rl.Vector2{X: 100, Y: -20}, health: 100, hitBoxRadius: 20, - move: sineHorizonalPattern(state), - shoot: burstShootAtPlayer(state, &player, 0.1, 4), + move: statePipeline{ + { + duration: 1.1, + move: sineDescentMove(state, -20, 200, 1), + }, + { + move: sineHorizonalPattern(state, 0), + }, + }.MovementPattern(state), + shoot: burstShootAtPlayer(state, &player, 0.1, 4), + }, + { + pos: rl.Vector2{X: 200, Y: -20}, + health: 100, + hitBoxRadius: 20, + move: statePipeline{ + { + duration: 5, + move: shootStill(), + }, + { + duration: 1.1, + move: descentMove(state, -20, 200, 1), + }, + { + move: sineHorizonalPattern(state, 1), + }, + }.MovementPattern(state), + shoot: burstShootAtPlayer(state, &player, 0.1, 4), }, }, }, { + duration: newTimer(second * 20), enemies: []*enemy{ { pos: rl.Vector2{X: 100, Y: 100}, health: 100, hitBoxRadius: 20, - move: horizonalPattern(state), - shoot: bulletExplosion(state, targetFPS, 20, 2, 11), + move: statePipeline{ + { + duration: 1.1, + move: sineDescentMove(state, -20, 200, 1), + }, + { + move: sineHorizonalPattern(state, 0), + }, + }.MovementPattern(state), + shoot: bulletExplosion(state, 1, 40, 2, 11), }, }, }, @@ -546,22 +493,45 @@ func main() { pos: rl.Vector2{X: 200, Y: 200}, health: 100, hitBoxRadius: 20, - move: horizonalPattern(state), - shoot: bulletExplosion(state, targetFPS, 20, 2, 11), + move: statePipeline{ + { + duration: 1.1, + move: sineDescentMove(state, -20, 200, 1), + }, + { + move: sineHorizonalPattern(state, 0), + }, + }.MovementPattern(state), + shoot: bulletExplosion(state, 1, 20, 2, 11), }, { - pos: rl.Vector2{X: 100, Y: 100}, - health: 100, + pos: rl.Vector2{X: 100, Y: 100}, health: 100, hitBoxRadius: 20, - move: horizonalPattern(state), - shoot: burstShootAtPlayer(state, &player, 0.2, 4), + move: statePipeline{ + { + duration: 1.1, + move: sineDescentMove(state, -20, 200, 1), + }, + { + move: sineHorizonalPattern(state, 0), + }, + }.MovementPattern(state), + shoot: burstShootAtPlayer(state, &player, 0.2, 4), }, { pos: rl.Vector2{X: 50, Y: 250}, health: 100, hitBoxRadius: 20, - move: horizonalPattern(state), - shoot: bulletExplosion(state, targetFPS, 20, 2, 11), + move: statePipeline{ + { + duration: 1.1, + move: sineDescentMove(state, -20, 200, 1), + }, + { + move: sineHorizonalPattern(state, 0), + }, + }.MovementPattern(state), + shoot: bulletExplosion(state, 1, 20, 2, 11), // shoot: shootStraightDown(state), }, }, @@ -569,6 +539,7 @@ func main() { } currectScore := 0 + state.addEnemy(state.waves[0].enemies...) for ; !rl.WindowShouldClose(); state.frame += state.gameSpeed { rl.BeginDrawing() @@ -579,16 +550,16 @@ func main() { player.update(state) - if state.fullClear() { - state.currentWave++ + if state.fullClear() || state.waveTimeout() { + if !state.nextWave() { + break + } } - if state.currentWave >= len(state.waves) { - break - } - state.enemies = state.waves[state.currentWave].enemies + enemiesTotalLifeRemaining := 0 for i := 0; i < len(state.enemies); i++ { state.enemies[i].update(state) + enemiesTotalLifeRemaining += state.enemies[i].health } for i := 0; i < len(state.bullets); i++ { @@ -606,7 +577,6 @@ func main() { state.gameSpeed = 1 } - // rl.EndTextureMode() // { // shaders @@ -635,17 +605,34 @@ func main() { { // UI currectScore += (state.score - currectScore) / 11 - rl.DrawText(strconv.Itoa(currectScore), state.arenaWidth, 0, 50, rl.White) - rl.DrawText( - strconv.Itoa(len(state.bullets)), state.arenaWidth, 50, 50, rl.White, + fmt.Sprint("score: ", strconv.Itoa(currectScore)), + state.arenaWidth, 0, 50, rl.White, ) rl.DrawText( - fmt.Sprintf("%f", state.frame), state.arenaWidth, 100, 50, rl.White, + fmt.Sprint("bullets: ", strconv.Itoa(len(state.bullets))), + state.arenaWidth, 50, 50, rl.White, + ) + + rl.DrawText( + fmt.Sprintf("wave: %d/%d", state.currentWave, len(state.waves)), + state.arenaWidth, 100, 50, rl.White, + ) + + rl.DrawText( + fmt.Sprintf("t: %f", rl.GetTime()), state.arenaWidth, 150, 50, rl.White, ) rl.DrawText( - fmt.Sprintf("%d", int(state.frame)), state.arenaWidth, 150, 50, rl.White, + fmt.Sprintf("sec/f: %.5f", rl.GetFrameTime()), state.arenaWidth, 200, 50, rl.White, + ) + rl.DrawText( + fmt.Sprintf("total life: %d", enemiesTotalLifeRemaining), + state.arenaWidth, 250, 50, rl.White, + ) + rl.DrawText( + fmt.Sprintf("speed: %f", state.gameSpeed), + state.arenaWidth, 300, 50, rl.White, ) rl.DrawText("danmaku babe bullet shoot shoot", 20, 20, 20, rl.DarkGray) rl.DrawLine(18, 42, state.arenaWidth-18, 42, rl.Black) diff --git a/movementPatterns.go b/movementPatterns.go new file mode 100644 index 0000000..adfc584 --- /dev/null +++ b/movementPatterns.go @@ -0,0 +1,138 @@ +package main + +import ( + "math" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +type movementPattern func(*enemy) *enemy + +type patternDuration struct { + duration duration + move movementPattern +} + +type statePipeline []patternDuration + +func shootStill() movementPattern { + return func(e *enemy) *enemy { + if e.shoot != nil { + e.shoot(e) + } + return e + } +} + +func (sp statePipeline) MovementPattern(g *game) movementPattern { + state := -1 + t := newTimer(0) + return func(e *enemy) *enemy { + if !t.isTimeout() { + t.tick(g) + } else if state < len(sp)-1 { + state++ + t = newTimer(sp[state].duration) + } + sp[state].move(e) + return e + } +} + +func horizonalPattern(g *game) movementPattern { + direction := 1 + + return func(e *enemy) *enemy { + if e.shoot != nil { + e.shoot(e) + } + + delta := rl.Vector2{ + X: 400, + Y: 0, + } + + if e.pos.X < e.hitBoxRadius || e.pos.X > float32(g.arenaWidth-int32(e.hitBoxRadius)) { + direction = -direction + } + + e.pos = rl.Vector2Add( + e.pos, + rl.Vector2Scale( + rl.Vector2{X: float32(direction) * delta.X, Y: delta.Y}, + rl.GetFrameTime()*g.gameSpeed, + ), + ) + return e + } +} + +func descentMove(g *game, y0, y1 float32, d duration) movementPattern { + t := newTimer(d) + return func(e *enemy) *enemy { + + if e.shoot != nil { + e.shoot(e) + } + + if !t.isTimeout() { + t.tick(g) + } else { + return e + } + + e.pos.Y = lerp(y0, y1, easeInOutSine(t.unit())) + return e + } +} + +func sineDescentMove(g *game, y0, y1 float32, d duration) movementPattern { + t := newTimer(d) + return func(e *enemy) *enemy { + + if e.shoot != nil { + e.shoot(e) + } + + if !t.isTimeout() { + t.tick(g) + } else { + return e + } + + e.pos.Y = lerp(y0, y1, easeInOutSine(t.unit())) + e.pos.X += float32(math.Sin(rl.GetTime() * 5 * float64(g.gameSpeed))) + + return e + } +} + +func sineHorizonalPattern(g *game, offset float64) movementPattern { + direction := 1 + + return func(e *enemy) *enemy { + if e.shoot != nil { + e.shoot(e) + } + + sine := math.Sin(rl.GetTime()*5*float64(g.gameSpeed)+offset) * 150 + + delta := rl.Vector2{ + X: 400, + Y: float32(sine), + } + + if e.pos.X < e.hitBoxRadius || e.pos.X > float32(g.arenaWidth-int32(e.hitBoxRadius)) { + direction = -direction + } + + e.pos = rl.Vector2Add( + e.pos, + rl.Vector2Scale( + rl.Vector2{X: float32(direction) * delta.X, Y: delta.Y}, + rl.GetFrameTime()*g.gameSpeed, + ), + ) + return e + } +} diff --git a/player.go b/player.go new file mode 100644 index 0000000..472e154 --- /dev/null +++ b/player.go @@ -0,0 +1,117 @@ +package main + +import ( + rl "github.com/gen2brain/raylib-go/raylib" +) + +func (p *player) move(g *game) { + + var moveSpeed float32 + if p.focusMode { + moveSpeed = p.moveSpeed * p.focusSpeedDecrease + } else { + moveSpeed = p.moveSpeed + } + + p.speed = rl.Vector2{X: 0, Y: 0} + + if rl.IsKeyDown(rl.KeyW) { + p.speed.Y -= 1 + } + if rl.IsKeyDown(rl.KeyS) { + p.speed.Y += 1 + } + if rl.IsKeyDown(rl.KeyA) { + p.speed.X -= 1 + } + if rl.IsKeyDown(rl.KeyD) { + p.speed.X += 1 + } + + if !(p.speed.X == 0 && p.speed.Y == 0) { + // jogador se move mais rapido na diagonal caso o contrario + p.speed = rl.Vector2Normalize(p.speed) + p.speed = rl.Vector2Scale(p.speed, moveSpeed) + } + + result := rl.Vector2Add(p.pos, p.speed) + + if result.Y-p.hitBoxRadius < 0 || result.Y+p.hitBoxRadius > float32(g.arenaHeight) { + p.speed.Y = 0 + } + if result.X-p.hitBoxRadius < 0 || result.X+p.hitBoxRadius > float32(g.arenaWidth) { + p.speed.X = 0 + } + + p.pos = rl.Vector2Add(p.pos, p.speed) +} + +func (p *player) shoot(g *game) { + if p.focusMode { + return + } + + if int(g.frame)%3 != 0 { + return + } + + if rl.IsMouseButtonDown(rl.MouseLeftButton) { + mouse := rl.GetMousePosition() + direction := rl.Vector2Subtract(mouse, p.pos) + direction = rl.Vector2Add(direction, p.speed) + direction = rl.Vector2Normalize(direction) + direction = rl.Vector2Scale(direction, p.bulletMoveSpeed) + + g.bullets = append(g.bullets, &bullet{ + pos: p.pos, + size: p.bulletSize, + speed: direction, + dmg: 1, + enemy: false, + }) + } +} + +func (p *player) checkHit(g *game) { + for _, bullet := range g.bullets { + if !bullet.enemy { + continue + } + distance := rl.Vector2Distance(p.pos, bullet.pos) - bullet.size + + if distance < p.hitBoxRadius { + // fmt.Println("hit!") + } + } +} + +func (p *player) checkFocus() { + // raylib não entende keybindings customizadas através do xmodmap? + p.focusMode = rl.IsKeyDown(rl.KeyLeftShift) || rl.IsKeyDown(rl.KeyRightShift) +} + +func (p *player) update(g *game) { + + p.checkFocus() + + p.move(g) + + p.shoot(g) + + p.checkHit(g) + + // hitbox + rl.DrawCircleV(p.pos, p.hitBoxRadius, rl.Red) + + if p.focusMode { + return + } + // mira + mouse := rl.GetMousePosition() + inverted := rl.Vector2Subtract(p.pos, mouse) + inverted = rl.Vector2Negate(inverted) + inverted = rl.Vector2Normalize(inverted) + inverted = rl.Vector2Scale(inverted, 2000) + rl.DrawLineV(p.pos, rl.Vector2Add(mouse, inverted), rl.NewColor(255, 0, 0, 100)) + +}