diff --git a/asset/asset.go b/asset/asset.go index b4cda3d..de7f367 100644 --- a/asset/asset.go +++ b/asset/asset.go @@ -29,7 +29,11 @@ var ( ImgCrosshair = LoadImage("image/crosshair.png") - ImgFishTileset = LoadImage("image/cozy-fishing/global.png") + ImgFish = LoadImage("image/cozy-fishing/global.png") + + ImgPeepBody = LoadImage("image/cozy-people/characters/char_all.png") + ImgPeepClothesShirt = LoadImage("image/cozy-people/clothes/basic.png") + ImgPeepClothesPants = LoadImage("image/cozy-people/clothes/pants.png") ) func init() { @@ -127,35 +131,31 @@ func LoadOGG(context *audio.Context, p string, loop bool) *audio.Player { return player } -func FishTileAt(i int) *ebiten.Image { - const tileSize = 16 - const tilesetWidth = 56 - - x, y := i%tilesetWidth, i/tilesetWidth - - r := image.Rect(x*tileSize, y*tileSize, (x+1)*tileSize, (y+1)*tileSize) - - return ImgFishTileset.SubImage(r).(*ebiten.Image) -} - func FishTileXY(x, y int) *ebiten.Image { const tileSize = 16 - const tilesetWidth = 56 r := image.Rect(x*tileSize, y*tileSize, (x+1)*tileSize, (y+1)*tileSize) - - return ImgFishTileset.SubImage(r).(*ebiten.Image) + return ImgFish.SubImage(r).(*ebiten.Image) } func FishImage(i int) *ebiten.Image { const tileSize = 16 - const fishTilesetWidth = 10 - - x, y := i%fishTilesetWidth, i/fishTilesetWidth + const tilesetWidth = 10 + x, y := i%tilesetWidth, i/tilesetWidth x += 46 r := image.Rect(x*tileSize, y*tileSize, (x+1)*tileSize, (y+1)*tileSize) - - return ImgFishTileset.SubImage(r).(*ebiten.Image) + return ImgFish.SubImage(r).(*ebiten.Image) +} + +func PeepImage(tileset *ebiten.Image, i int, frame int) *ebiten.Image { + const tileSize = 32 + const tilesetWidth = 8 + + x, y := frame%tilesetWidth, frame/tilesetWidth + offsetX, offsetY := i*32*8, 0 + + r := image.Rect(offsetX+x*tileSize, offsetY+y*tileSize, offsetX+(x+1)*tileSize, offsetY+(y+1)*tileSize) + return tileset.SubImage(r).(*ebiten.Image) } diff --git a/component/sprite.go b/component/sprite.go index 14410db..9410356 100644 --- a/component/sprite.go +++ b/component/sprite.go @@ -26,4 +26,8 @@ type Sprite struct { OverrideColorScale bool ColorScale float64 + + OffsetX, OffsetY float64 + + Images []*ebiten.Image } diff --git a/entity/creep.go b/entity/creep.go index 5c4091b..07eb3fd 100644 --- a/entity/creep.go +++ b/entity/creep.go @@ -4,6 +4,8 @@ import ( "math/rand" "sync" + "github.com/hajimehoshi/ebiten/v2" + "code.rocketnine.space/tslocum/fishfightback/asset" "code.rocketnine.space/tslocum/fishfightback/component" "code.rocketnine.space/tslocum/fishfightback/level" @@ -21,6 +23,10 @@ func newCreepID() int64 { return newestCreepID } +func randCreepType() int { + return rand.Intn(8) +} + func NewCreep(creepType int, x, y float64) gohan.Entity { creepID := newCreepID() @@ -32,8 +38,18 @@ func NewCreep(creepType int, x, y float64) gohan.Entity { Z: level.LayerCreep, }) + images := []*ebiten.Image{ + asset.PeepImage(asset.ImgPeepBody, randCreepType(), 0), + } + if rand.Intn(3) == 0 { + images = append(images, asset.PeepImage(asset.ImgPeepClothesShirt, randCreepType(), 0)) + } + images = append(images, asset.PeepImage(asset.ImgPeepClothesPants, randCreepType(), 0)) + creep.AddComponent(&component.Sprite{ - Image: asset.FishImage(int(level.FishMackerel)), + Images: images, + OffsetX: -16, + OffsetY: -16, }) creep.AddComponent(&component.Creep{ diff --git a/entity/playerbullet.go b/entity/playerbullet.go index 0c184a5..872c2d0 100644 --- a/entity/playerbullet.go +++ b/entity/playerbullet.go @@ -22,7 +22,9 @@ func NewPlayerBullet(x, y, xSpeed, ySpeed float64) gohan.Entity { }) bullet.AddComponent(&component.Sprite{ - Image: asset.ImgBlackSquare, + Image: asset.ImgBlackSquare, + OffsetX: -2, + OffsetY: -2, }) bullet.AddComponent(&component.PlayerBullet{}) diff --git a/system/movement.go b/system/movement.go index 532c419..50a27ee 100644 --- a/system/movement.go +++ b/system/movement.go @@ -2,7 +2,6 @@ package system import ( "image" - "image/color" "math" "code.rocketnine.space/tslocum/fishfightback/component" @@ -11,8 +10,6 @@ import ( "github.com/hajimehoshi/ebiten/v2" ) -const rewindThreshold = 1 - type MovementSystem struct { Position *component.Position Velocity *component.Velocity @@ -29,25 +26,6 @@ func NewMovementSystem() *MovementSystem { return s } -func drawDebugRect(r image.Rectangle, c color.Color, overrideColorScale bool) gohan.Entity { - rectEntity := gohan.NewEntity() - - rectImg := ebiten.NewImage(r.Dx(), r.Dy()) - rectImg.Fill(c) - - rectEntity.AddComponent(&component.Position{ - X: float64(r.Min.X), - Y: float64(r.Min.Y), - }) - - rectEntity.AddComponent(&component.Sprite{ - Image: rectImg, - OverrideColorScale: overrideColorScale, - }) - - return rectEntity -} - func (s *MovementSystem) Update(e gohan.Entity) error { if !world.World.GameStarted { return nil @@ -128,27 +106,25 @@ func (s *MovementSystem) Update(e gohan.Entity) error { if world.World.NoClip { return nil } - bulletSize := 4.0 - bulletRect := image.Rect(int(position.X), int(position.Y), int(position.X+bulletSize), int(position.Y+bulletSize)) creepBullet := s.CreepBullet playerBullet := s.PlayerBullet - var currentSection = world.World.SectionA - if world.World.SectionB.X != 0 && position.X >= world.World.SectionB.X && position.X < world.World.SectionB.X+world.SectionWidth { - currentSection = world.World.SectionB - } - tx, ty := int((position.X-currentSection.X)/TileWidth), int((position.Y-currentSection.Y)/TileWidth) - offscreen := tx < 0 || ty < 0 || tx >= world.SectionWidth/TileWidth || ty >= world.ScreenHeight/TileWidth - - // Check hazard collisions. if creepBullet != nil { - if offscreen { + playerRect := image.Rect(int(world.World.PlayerX), int(world.World.PlayerY), int(world.World.PlayerX+world.World.PlayerWidth), int(world.World.PlayerY+world.World.PlayerHeight)) + bulletSize := 4.0 + + var currentSection = world.World.SectionA + if world.World.SectionB.X != 0 && position.X >= world.World.SectionB.X && position.X < world.World.SectionB.X+world.SectionWidth { + currentSection = world.World.SectionB + } + tx, ty := int((position.X-currentSection.X)/TileWidth), int((position.Y-currentSection.Y)/TileWidth) + if tx < 0 || ty < 0 || tx >= world.SectionWidth/TileWidth || ty >= world.ScreenHeight/TileWidth { e.Remove() return nil } - playerRect := image.Rect(int(world.World.PlayerX), int(world.World.PlayerY), int(world.World.PlayerX+world.World.PlayerWidth), int(world.World.PlayerY+world.World.PlayerHeight)) + bulletRect := image.Rect(int(position.X), int(position.Y), int(position.X+bulletSize), int(position.Y+bulletSize)) if bulletRect.Overlaps(playerRect) { if !world.World.GodMode { world.World.SetGameOver() @@ -159,29 +135,48 @@ func (s *MovementSystem) Update(e gohan.Entity) error { } if playerBullet != nil { + var offscreen bool + for oy := -2; oy < 5; oy++ { + for ox := -2; ox < 5; ox++ { + var currentSection = world.World.SectionA + if world.World.SectionB.X != 0 && position.X >= world.World.SectionB.X && position.X < world.World.SectionB.X+world.SectionWidth { + currentSection = world.World.SectionB + } + tx, ty := int((position.X+float64(ox)-currentSection.X)/TileWidth), int((position.Y+float64(oy)-currentSection.Y)/TileWidth) + if tx < 0 || ty < 0 || tx >= world.SectionWidth/TileWidth || ty >= world.ScreenHeight/TileWidth { + continue // Offscreen + } + offscreen = false + + // Hit creep. + for offset := 0; offset < 2; offset++ { + if ty+offset >= world.ScreenHeight/TileWidth { + break + } + creepEntity := currentSection.Creeps[ty+offset][tx] + if creepEntity != 0 { + var hitCreep bool + creepEntity.With(func(creep *component.Creep) { + if creep == nil || !creep.Active { + return + } + creep.Health-- + creep.DamageTicks = 6 + hitCreep = true + }) + if hitCreep { + e.Remove() + currentSection.Creeps[ty+offset][tx] = 0 + } + } + } + } + } + if offscreen { e.Remove() return nil } - - // Hit creep. - creepEntity := currentSection.Creeps[ty][tx] - if creepEntity != 0 { - var hitCreep bool - creepEntity.With(func(creep *component.Creep) { - if creep == nil || !creep.Active { - return - } - creep.Health-- - creep.DamageTicks = 6 - hitCreep = true - }) - if hitCreep { - e.Remove() - currentSection.Creeps[ty][tx] = 0 - } - - } } return nil diff --git a/system/render.go b/system/render.go index 7c21220..6dd9c86 100644 --- a/system/render.go +++ b/system/render.go @@ -127,6 +127,11 @@ func (s *RenderSystem) Draw(e gohan.Entity, screen *ebiten.Image) error { colorScale = sprite.ColorScale } - s.renderSprite(position.X, position.Y, 0, 0, sprite.Angle, 1.0, colorScale, 1.0, sprite.HorizontalFlip, sprite.VerticalFlip, sprite.Image, screen) + if len(sprite.Images) == 0 { + s.renderSprite(position.X, position.Y, sprite.OffsetX, sprite.OffsetY, sprite.Angle, 1.0, colorScale, 1.0, sprite.HorizontalFlip, sprite.VerticalFlip, sprite.Image, screen) + } + for _, img := range sprite.Images { + s.renderSprite(position.X, position.Y, sprite.OffsetX, sprite.OffsetY, sprite.Angle, 1.0, colorScale, 1.0, sprite.HorizontalFlip, sprite.VerticalFlip, img, screen) + } return nil } diff --git a/world/section.go b/world/section.go index ef4d3ac..ff395c7 100644 --- a/world/section.go +++ b/world/section.go @@ -179,11 +179,8 @@ func (s *Section) Regenerate(lastShoreDepth int) { } // Generate buildings. - // TODO bag of random buildings - addBuildings := rand.Intn(14) - for j := 0; j < addBuildings; j++ { specialBuilding := rand.Intn(4) == 0 @@ -218,9 +215,10 @@ func (s *Section) Regenerate(lastShoreDepth int) { } // Generate creeps. - for attempt := 0; attempt < 7; attempt++ { - tx, ty := rand.Intn(SectionWidth/16), int(float64(rand.Intn(s.ShoreDepth))) - if !s.tileAvailable(tx, ty, true) { + const numCreeps = 40 // TODO + for attempt := 0; attempt < numCreeps; attempt++ { + tx, ty := rand.Intn(SectionWidth/16), int(float64(rand.Intn(s.ShoreDepth-1))) + if !s.tileAvailable(tx, ty, true) || (ty != 0 && !s.tileAvailable(tx, ty-1, true)) || !s.tileAvailable(tx, ty+1, true) || !s.tileAvailable(tx-1, ty, true) || !s.tileAvailable(tx+1, ty, true) { continue } diff --git a/world/world.go b/world/world.go index f743492..37262d7 100644 --- a/world/world.go +++ b/world/world.go @@ -23,7 +23,12 @@ const ( ScreenHeight = 225 ) -var RailSpeed = 0.4 +const ( + StartingRailSpeed = 0.4 + FishSpeedIncrease = 0.05 +) + +var RailSpeed = StartingRailSpeed var NumberPrinter = message.NewPrinter(language.English) @@ -155,4 +160,6 @@ func SetFish(fish level.FishType) { sprite.Image = asset.FishImage(int(fish)) }) + + RailSpeed = StartingRailSpeed + (FishSpeedIncrease * float64(fish)) }