diff --git a/creep.go b/creep.go new file mode 100644 index 0000000..0d2da28 --- /dev/null +++ b/creep.go @@ -0,0 +1,46 @@ +package main + +import ( + "math/rand" + "sync" + + "github.com/hajimehoshi/ebiten/v2" +) + +type creep struct { + x, y float64 + sprite *ebiten.Image + + moveX, moveY float64 + + tick int + nextAction int + + sync.Mutex +} + +func (c *creep) doNextAction() { + c.moveX = (rand.Float64() - 0.5) / 10 + c.moveY = (rand.Float64() - 0.5) / 10 + + c.nextAction = 400 + rand.Intn(1000) +} + +func (c *creep) Update() { + c.Lock() + defer c.Unlock() + + c.tick++ + if c.tick >= c.nextAction { + c.doNextAction() + c.tick = 0 + } + + c.x, c.y = c.x+c.moveX, c.y+c.moveY +} + +func (c *creep) Position() (float64, float64) { + c.Lock() + defer c.Unlock() + return c.x, c.y +} diff --git a/game.go b/game.go index 50f8482..eca6fe5 100644 --- a/game.go +++ b/game.go @@ -31,11 +31,6 @@ type projectile struct { color color.Color } -type creep struct { - x, y float64 - sprite *ebiten.Image -} - // game is an isometric demo game. type game struct { w, h int @@ -144,13 +139,22 @@ func NewGame() (*game, error) { vampireImage := ebiten.NewImageFromImage(img) + addedCreeps := make(map[string]bool) for i := 0; i < 1000; i++ { c := &creep{ x: float64(1 + rand.Intn(64)), y: float64(1 + rand.Intn(64)), sprite: vampireImage, } + + addedCreep := fmt.Sprintf("%0.0f-%0.0f", c.x, c.y) + if addedCreeps[addedCreep] { + // Already added a creep here + continue + } + g.creeps = append(g.creeps, c) + addedCreeps[addedCreep] = true } ebiten.SetCursorShape(ebiten.CursorShapeCrosshair) @@ -165,6 +169,10 @@ func (g *game) Update() error { return nil } + for _, c := range g.creeps { + c.Update() + } + // Update target zoom level. var scrollY float64 if ebiten.IsKeyPressed(ebiten.KeyC) || ebiten.IsKeyPressed(ebiten.KeyPageDown) { @@ -229,8 +237,9 @@ func (g *game) Update() error { p.x += math.Cos(p.angle) * p.speed p.y += math.Sin(p.angle) * p.speed - for _, creep := range g.creeps { - dx, dy := p.x-creep.x, p.y-creep.y + for _, c := range g.creeps { + cx, cy := c.Position() + dx, dy := p.x-cx, p.y-cy if dx < 0 { dx *= -1 } @@ -238,8 +247,11 @@ func (g *game) Update() error { dy *= -1 } if dx <= bulletHitThreshold && dy <= bulletHitThreshold { - creep.x = 1000 - creep.y = 1000 + // Kill creep + g.addBloodSplatter(cx, cy) + c.x = 1000 + c.y = 1000 + // Remove projectile g.projectiles = append(g.projectiles[:i-removed], g.projectiles[i-removed+1:]...) removed++ break @@ -284,8 +296,36 @@ func (g *game) screenCoordinatesToLevel(x, y float64) (float64, float64) { return ((x - px) * g.camScale) + float64(g.w/2.0), ((y + py) * g.camScale) + float64(g.h/2.0) } +func (g *game) addBloodSplatter(x, y float64) { + splatterSprite := ebiten.NewImage(32, 32) + + for y := 8; y < 20; y++ { + if rand.Intn(2) != 0 { + continue + } + + for x := 12; x < 20; x++ { + if rand.Intn(5) != 0 { + continue + } + + splatterSprite.Set(x, y, colornames.Red) + } + } + + t := g.currentLevel.Tile(int(x), int(y)) + if t != nil { + t.AddSprite(splatterSprite) + } +} + // Draw draws the game on the screen. func (g *game) Draw(screen *ebiten.Image) { + if g.player.health <= 0 { + screen.Fill(colornames.Red) + return + } + // Render level. drawn := g.renderLevel(screen) @@ -372,7 +412,20 @@ func (g *game) renderLevel(screen *ebiten.Image) int { } } + biteThreshold := 0.5 for _, c := range g.creeps { + cx, cy := c.Position() + dx, dy := g.player.x-cx, g.player.y-cy + if dx < 0 { + dx *= -1 + } + if dy < 0 { + dy *= -1 + } + if dx <= biteThreshold && dy <= biteThreshold { + g.player.health-- + } + drawn += g.renderSprite(c.x, c.y, 0, 0, 0, c.sprite, screen) } diff --git a/go.mod b/go.mod index 1f1f1ab..100ae5f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module code.rocketnine.space/tslocum/carotidartillery go 1.17 require ( - github.com/hajimehoshi/ebiten/v2 v2.3.0-alpha.0.20211002105934-2fd97767ff1d + github.com/hajimehoshi/ebiten/v2 v2.3.0-alpha.0.20211005153847-3f5d1762bb36 golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d ) diff --git a/go.sum b/go.sum index 9a98ddf..4575e95 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs= -github.com/hajimehoshi/ebiten/v2 v2.3.0-alpha.0.20211002105934-2fd97767ff1d h1:nDvPnkrJU0l6dYoTvboO2/vz+G0yfJIu+qjsim/yGns= -github.com/hajimehoshi/ebiten/v2 v2.3.0-alpha.0.20211002105934-2fd97767ff1d/go.mod h1:olKl/qqhMBBAm2oI7Zy292nCtE+nitlmYKNF3UpbFn0= +github.com/hajimehoshi/ebiten/v2 v2.3.0-alpha.0.20211005153847-3f5d1762bb36 h1:zp4kNpTpsWJ9OtUrbPkA9PCLUKk1Y1WHjpMLadVpwOI= +github.com/hajimehoshi/ebiten/v2 v2.3.0-alpha.0.20211005153847-3f5d1762bb36/go.mod h1:olKl/qqhMBBAm2oI7Zy292nCtE+nitlmYKNF3UpbFn0= github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE= github.com/hajimehoshi/go-mp3 v0.3.2 h1:xSYNE2F3lxtOu9BRjCWHHceg7S91IHfXfXp5+LYQI7s= github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= diff --git a/level.go b/level.go index b87e4a9..5aad8e5 100644 --- a/level.go +++ b/level.go @@ -77,7 +77,7 @@ func NewLevel() (*Level, error) { case x == l.w-1: t.AddSprite(sandstoneSS.WallRight) case val < 275: - t.AddSprite(sandstoneSS.FloorB) + //t.AddSprite(sandstoneSS.FloorB) case val < 500: t.AddSprite(sandstoneSS.FloorC) } diff --git a/main.go b/main.go index ae1c07e..d52a2ac 100644 --- a/main.go +++ b/main.go @@ -2,14 +2,18 @@ package main import ( "log" + "math/rand" "os" "os/signal" "syscall" + "time" "github.com/hajimehoshi/ebiten/v2" ) func main() { + rand.Seed(time.Now().UnixNano()) + ebiten.SetWindowTitle("Carotid Artillery") ebiten.SetWindowResizable(true) ebiten.SetFullscreen(true) diff --git a/player.go b/player.go index af81354..29ec125 100644 --- a/player.go +++ b/player.go @@ -13,6 +13,8 @@ type gamePlayer struct { angle float64 weapon *playerWeapon + + health int } func NewPlayer() (*gamePlayer, error) { @@ -32,6 +34,7 @@ func NewPlayer() (*gamePlayer, error) { sprite: uziSprite, cooldown: 100 * time.Millisecond, }, + health: 70, } return p, nil }