diff --git a/cmd/krampus19/game.go b/cmd/krampus19/game.go index 796642e..ed4e75c 100644 --- a/cmd/krampus19/game.go +++ b/cmd/krampus19/game.go @@ -136,15 +136,11 @@ func (g *game) loadSprites(names ...string) error { func (g *game) loadAssets() error { log.Println("Loading textures...") err := g.loadTextures(map[string]string{ - "basic_tile.png": "basic_tile", - "sunken_brick_tile.png": "sunken_brick_tile", - "water_tile.png": "water_tile", + "entity_brick.png": "brick", + "entity_main_character.png": "main_character", - "main_character.png": "main_character", - "villain_character.png": "villain_character", - - "brick.png": "brick", - "crate.png": "crate", + "tile_lava_brick.png": "lava_brick", + "tile_magma.png": "magma", }) if err != nil { return err @@ -166,10 +162,10 @@ func (g *game) loadAssets() error { log.Printf("Loaded %d fonts.\n", g.ui.Fonts().Len()) log.Println("Loading sprites") - // err = g.loadSprites("dragon") - // if err != nil { - // return err - // } + err = g.loadSprites("brick", "lava_brick", "magma", "main_character") + if err != nil { + return err + } log.Printf("Loaded %d sprites.\n", len(g.ctx.Sprites)) return nil } diff --git a/cmd/krampus19/level.go b/cmd/krampus19/level.go index b3530bf..46512e7 100644 --- a/cmd/krampus19/level.go +++ b/cmd/krampus19/level.go @@ -17,7 +17,6 @@ const ( entityTypeCharacter = '@' entityTypeVillain = 'X' entityTypeBrick = 'B' - entityTypeCrate = 'C' ) func (e entityType) IsValid() bool { @@ -26,7 +25,6 @@ func (e entityType) IsValid() bool { case entityTypeCharacter: case entityTypeVillain: case entityTypeBrick: - case entityTypeCrate: default: return false } @@ -37,14 +35,14 @@ const ( tileInvalid tile = tile(0) tileNothing = '.' tileBasic = '#' - tileWater = '~' + tileMagma = '~' ) func (t tile) IsValid() bool { switch t { case tileNothing: case tileBasic: - case tileWater: + case tileMagma: default: return false } diff --git a/cmd/krampus19/playlevel.go b/cmd/krampus19/playlevel.go index 87891e8..fd8b283 100644 --- a/cmd/krampus19/playlevel.go +++ b/cmd/krampus19/playlevel.go @@ -2,14 +2,12 @@ package main import ( "fmt" - "log" "sort" "time" "opslag.de/schobers/allg5" "opslag.de/schobers/geom" "opslag.de/schobers/krampus19/alui" - "opslag.de/schobers/krampus19/gut" ) type playLevel struct { @@ -20,21 +18,12 @@ type playLevel struct { menu *alui.Menu showMenu bool - name string - offset geom.PointF32 - scale float32 - keysDown keyPressedState + name string + offset geom.PointF32 + scale float32 - level level - player *entity - villain *entity - bricks []*entity - sunken []*entity - steps int - tick time.Duration - ani gut.Animations + state playLevelState } - type keyPressedState map[allg5.Key]bool func (s keyPressedState) CountPressed(keys ...allg5.Key) int { @@ -84,90 +73,6 @@ func (a *entityMoveAnimation) Animate(start, now time.Duration) bool { return true } -func (l *playLevel) idxToPos(i int) geom.PointF32 { return l.level.idxToPos(i).ToF32() } - -func (l *playLevel) isIdle() bool { return l.ani.Idle() } - -func findEntityAt(entities []*entity, pos geom.Point) *entity { - idx := findEntityIdx(entities, pos) - if idx == -1 { - return nil - } - return entities[idx] -} - -func findEntityIdx(entities []*entity, pos geom.Point) int { - for i, e := range entities { - if e.pos == pos { - return i - } - } - return -1 -} - -func (l *playLevel) findEntityAt(pos geom.Point) *entity { - if l.player.pos == pos { - return l.player - } - brick := findEntityAt(l.bricks, pos) - if brick != nil { - return brick - } - return findEntityAt(l.sunken, pos) -} - -func (l *playLevel) checkTile(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool) bool { - return l.checkTileNotFound(pos, check, false) -} - -func (l *playLevel) checkTileNotFound(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool, notFound bool) bool { - idx := l.level.posToIdx(pos) - if idx == -1 { - return notFound - } - return check(pos, idx, l.level.tiles[idx]) -} - -func (l *playLevel) isSolidTile(pos geom.Point, idx int, t tile) bool { - switch t { - case tileBasic: - return true - case tileWater: - return findEntityAt(l.sunken, pos) != nil - } - return false -} - -func (l *playLevel) wouldBrickSink(pos geom.Point, idx int, t tile) bool { - return t == tileWater && findEntityAt(l.sunken, pos) == nil -} - -func (l *playLevel) isObstructed(pos geom.Point, idx int, t tile) bool { - if findEntityAt(l.bricks, pos) != nil { - return true // brick - } - switch l.level.tiles[idx] { - case tileWater: - return false - case tileBasic: - return false - } - return true -} - -func (l *playLevel) canMove(from, dir geom.Point) bool { - to := from.Add(dir) - if !l.checkTile(to, l.isSolidTile) { - return false - } - brick := findEntityAt(l.bricks, to) - if brick != nil { - brickTo := to.Add(dir) - return !l.checkTileNotFound(brickTo, l.isObstructed, true) - } - return true -} - func (l *playLevel) Enter(ctx *Context) error { l.ctx = ctx @@ -176,20 +81,7 @@ func (l *playLevel) Enter(ctx *Context) error { l.menu.Add("Quit to main menu", func() { l.ctx.Navigation.showMainMenu() }) l.menu.Add("Quit to desktop", func() { l.ctx.Navigation.quit() }) - l.keysDown = keyPressedState{} - l.level = l.ctx.Levels[l.name] - l.bricks = nil - l.sunken = nil - for i, e := range l.level.entities { - switch e { - case entityTypeBrick: - l.bricks = append(l.bricks, newEntity(e, l.level.idxToPos(i))) - case entityTypeCharacter: - l.player = newEntity(e, l.level.idxToPos(i)) - case entityTypeVillain: - l.villain = newEntity(e, l.level.idxToPos(i)) - } - } + l.state.Init(l.ctx, l.name) return nil } @@ -201,78 +93,43 @@ func (l *playLevel) posToScreen(p geom.Point) geom.PointF32 { func (l *playLevel) posToScreenF32(p geom.PointF32) geom.PointF32 { pos := p.Add2D(.5, .5) - pos = geom.PtF32(pos.X*148-pos.Y*46, pos.Y*82) - pos = geom.PtF32(pos.X*l.scale, pos.Y*l.scale) - return pos.Add(l.offset) + pos = geom.PtF32(pos.X*100-pos.Y*50, pos.Y*40) + return pos.Mul(l.scale).Add(l.offset) } -// <- 168-> -// <-128-> +// <- 150-> +// <-100-> // // /----/| ^ ^ -// / / / | 72 -// /----/ / 92 v ^ -// |----|/ v v 20 +// / / | | 40 +// /----/ | 140 v ^ +// | | | | | +// | | / | 100 +// |----|/ v v // -// Gap: 20 -// Center: 84,46 -// Offset between horizontal tiles: 148,0 -// Offset between vertical tiles: -46,82 +// Center: 74,19 +// Offset between horizontal tiles: 100,0 +// Offset between vertical tiles: -25,40 func (l *playLevel) Layout(ctx *alui.Context, bounds geom.RectangleF32) { l.scale = bounds.Dy() / 1080 - tilesCenter := geom.PtF32(.5*float32(l.level.width), .5*float32(l.level.height)) - tilesCenter = geom.PtF32(tilesCenter.X*148-tilesCenter.Y*46, tilesCenter.Y*82) + level := l.state.Level() + tilesCenter := geom.PtF32(.5*float32(level.width), .5*float32(level.height)) + tilesCenter = geom.PtF32(tilesCenter.X*100-tilesCenter.Y*25, tilesCenter.Y*40) tilesCenter = geom.PtF32(tilesCenter.X*l.scale, tilesCenter.Y*l.scale) center := bounds.Center() l.offset = geom.PtF32(center.X-tilesCenter.X, center.Y-tilesCenter.Y) - l.ani.Animate(l.ctx.Tick) -} - -func (l *playLevel) tryPlayerMove(dir geom.Point, key allg5.Key) { - if !l.isIdle() { - return - } - - to := l.player.pos.Add(dir) - if !l.canMove(l.player.pos, dir) { - log.Printf("Move is not allowed (tried out move to %s after key '%s' was pressed)", to, gut.KeyToString(key)) - return - } - - l.steps++ - log.Printf("Moving player to %s", to) - l.ani.StartFn(l.ctx.Tick, newEntityMoveAnimation(l.player, to), func() { - log.Printf("Player movement finished") - if l.keysDown[key] && l.keysDown.CountPressed(l.ctx.Settings.Controls.MovementKeys()...) == 1 { - log.Printf("Key %s is still down, moving further", gut.KeyToString(key)) - l.tryPlayerMove(dir, key) - } - }) - - if brick := findEntityAt(l.bricks, to); brick != nil { - log.Printf("Pushing brick at %s", to) - brickTo := to.Add(dir) - l.ani.StartFn(l.ctx.Tick, newEntityMoveAnimation(brick, brickTo), func() { - log.Printf("Brick movement finished") - if l.checkTile(brickTo, l.wouldBrickSink) { - log.Printf("Sinking brick at %s", brickTo) - idx := findEntityIdx(l.bricks, brickTo) - l.bricks = append(l.bricks[:idx], l.bricks[idx+1:]...) - l.sunken = append(l.sunken, brick) - } - }) - } + l.state.Tick(l.ctx.Tick) } func (l *playLevel) Handle(e allg5.Event) { switch e := e.(type) { case *allg5.KeyDownEvent: - l.keysDown[e.KeyCode] = true + l.state.PressKey(e.KeyCode) case *allg5.KeyUpEvent: - l.keysDown[e.KeyCode] = false + l.state.ReleaseKey(e.KeyCode) } if l.showMenu { @@ -294,50 +151,66 @@ func (l *playLevel) Handle(e allg5.Event) { l.showMenu = true l.menu.Activate(0) case l.ctx.Settings.Controls.MoveUp: - l.tryPlayerMove(geom.Pt(0, -1), e.KeyCode) + l.state.TryPlayerMove(geom.Pt(0, -1), e.KeyCode) case l.ctx.Settings.Controls.MoveRight: - l.tryPlayerMove(geom.Pt(1, 0), e.KeyCode) + l.state.TryPlayerMove(geom.Pt(1, 0), e.KeyCode) case l.ctx.Settings.Controls.MoveDown: - l.tryPlayerMove(geom.Pt(0, 1), e.KeyCode) + l.state.TryPlayerMove(geom.Pt(0, 1), e.KeyCode) case l.ctx.Settings.Controls.MoveLeft: - l.tryPlayerMove(geom.Pt(-1, 0), e.KeyCode) + l.state.TryPlayerMove(geom.Pt(-1, 0), e.KeyCode) } } } -func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) { - basicTile := l.ctx.Textures["basic_tile"] - waterTile := l.ctx.Textures["water_tile"] - sunkenBrickTile := l.ctx.Textures["sunken_brick_tile"] +func (l *playLevel) drawSpritePart(name, partName string, pos geom.PointF32) { + l.drawSpritePartOffset(name, partName, pos, geom.PointF32{}) +} - opts := allg5.DrawOptions{Center: true, Scale: allg5.NewUniformScale(l.scale * (168 / float32(basicTile.Width())))} - level := l.level +func (l *playLevel) drawSpritePartOffset(name, partName string, pos, offset geom.PointF32) { + sprite, ok := l.ctx.Sprites[name] + if !ok { + return + } + text, ok := l.ctx.Textures[sprite.texture] + if !ok { + return + } + partText, ok := text.Subs[partName] + if !ok { + return + } + part := sprite.FindPartByName(partName) + scale := l.scale + if part.scale != 0 { + scale *= 1. / part.scale + } + anchor := part.sub.Min.Sub(part.anchor).ToF32().Mul(scale) + scrPos := l.posToScreenF32(pos).Add(anchor).Add(offset.Mul(100 * l.scale)) + left, top := scrPos.X, scrPos.Y + partText.DrawOptions(left, top, allg5.DrawOptions{Scale: allg5.NewUniformScale(scale)}) +} + +func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) { + level := l.state.level for i, t := range level.tiles { pos := geom.Pt(i%level.width, i/level.width) - scrPos := l.posToScreen(pos) switch t { case tileBasic: - basicTile.DrawOptions(scrPos.X, scrPos.Y, opts) - case tileWater: - scrPos := l.posToScreenF32(pos.ToF32().Add2D(0, .2*l.scale)) - if findEntityAt(l.sunken, pos) == nil { - waterTile.DrawOptions(scrPos.X, scrPos.Y, opts) + if l.state.IsNextToMagma(pos) { + l.drawSpritePart("lava_brick", "magma", pos.ToF32()) } else { - sunkenBrickTile.DrawOptions(scrPos.X, scrPos.Y, opts) + l.drawSpritePart("lava_brick", "lava_brick", pos.ToF32()) + } + case tileMagma: + l.drawSpritePart("magma", "magma", pos.ToF32()) + if l.state.IsFilledUp(pos) { + l.drawSpritePartOffset("brick", "brick", pos.ToF32(), geom.PtF32(0, .8)) + l.drawSpritePart("magma", "sunken_overlay", pos.ToF32()) } } } - character := l.ctx.Textures["main_character"] - villain := l.ctx.Textures["villain_character"] - brick := l.ctx.Textures["brick"] - crate := l.ctx.Textures["crate"] - - var entities []*entity - entities = append(entities, l.player) - entities = append(entities, l.villain) - entities = append(entities, l.bricks...) - + entities := l.state.Entities() sort.Slice(entities, func(i, j int) bool { if entities[i].scr.Y == entities[j].scr.Y { return entities[i].scr.X < entities[j].scr.X @@ -346,27 +219,16 @@ func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) { }) for _, e := range entities { - scrPos := l.posToScreenF32(e.scr) switch e.typ { case entityTypeCharacter: - scrPos := l.posToScreenF32(e.scr.Add2D(-.2*l.scale, -.9*l.scale)) - character.DrawOptions(scrPos.X, scrPos.Y, opts) - case entityTypeVillain: - scrPos := l.posToScreenF32(e.scr.Add2D(-.2*l.scale, -.9*l.scale)) - villain.DrawOptions(scrPos.X, scrPos.Y, opts) + l.drawSpritePart("main_character", "main_character", e.scr) case entityTypeBrick: - if findEntityAt(l.bricks, e.pos) == nil { - break - } - scrPos := l.posToScreenF32(e.scr.Add2D(-.2*l.scale, -.7*l.scale)) - brick.DrawOptions(scrPos.X, scrPos.Y, opts) - case entityTypeCrate: - crate.DrawOptions(scrPos.X, scrPos.Y, opts) + l.drawSpritePart("brick", "brick", e.scr) } } font := ctx.Fonts.Get("default") - steps := fmt.Sprintf("STEPS: %d", l.steps) + steps := fmt.Sprintf("STEPS: %d", l.state.Steps()) ctx.Fonts.DrawAlignFont(font, bounds.Min.X, 24, bounds.Max.X, ctx.Palette.Text, allg5.AlignCenter, steps) if l.showMenu { diff --git a/cmd/krampus19/playlevelstate.go b/cmd/krampus19/playlevelstate.go new file mode 100644 index 0000000..7186dba --- /dev/null +++ b/cmd/krampus19/playlevelstate.go @@ -0,0 +1,197 @@ +package main + +import ( + "log" + "time" + + "opslag.de/schobers/allg5" + "opslag.de/schobers/geom" + "opslag.de/schobers/krampus19/gut" +) + +func findEntityAt(entities []*entity, pos geom.Point) *entity { + idx := findEntityIdx(entities, pos) + if idx == -1 { + return nil + } + return entities[idx] +} + +func findEntityIdx(entities []*entity, pos geom.Point) int { + for i, e := range entities { + if e.pos == pos { + return i + } + } + return -1 +} + +type playLevelState struct { + ctx *Context + + level level + player *entity + villain *entity + bricks []*entity + sunken []*entity + steps int + tick time.Duration + ani gut.Animations + keysDown keyPressedState +} + +func (s *playLevelState) Entities() []*entity { + var entities []*entity + entities = append(entities, s.player) + entities = append(entities, s.villain) + entities = append(entities, s.bricks...) + return entities +} + +func (s *playLevelState) IsFilledUp(pos geom.Point) bool { + return findEntityAt(s.sunken, pos) != nil +} + +func (s *playLevelState) IsNextToMagma(pos geom.Point) bool { + return s.checkTile(pos.Add2D(1, 0), s.isMagma) || + s.checkTile(pos.Add2D(-1, 0), s.isMagma) || + s.checkTile(pos.Add2D(0, -1), s.isMagma) || + s.checkTile(pos.Add2D(0, 1), s.isMagma) +} + +func (s *playLevelState) Init(ctx *Context, levelName string) { + s.ctx = ctx + s.level = ctx.Levels[levelName] + s.bricks = nil + s.sunken = nil + for i, e := range s.level.entities { + switch e { + case entityTypeBrick: + s.bricks = append(s.bricks, newEntity(e, s.level.idxToPos(i))) + case entityTypeCharacter: + s.player = newEntity(e, s.level.idxToPos(i)) + case entityTypeVillain: + s.villain = newEntity(e, s.level.idxToPos(i)) + } + } + s.keysDown = keyPressedState{} +} + +func (s *playLevelState) Level() level { return s.level } + +func (s *playLevelState) PressKey(key allg5.Key) { + s.keysDown[key] = true +} + +func (s *playLevelState) ReleaseKey(key allg5.Key) { + s.keysDown[key] = false +} + +func (s *playLevelState) Steps() int { return s.steps } + +func (s *playLevelState) Tick(now time.Duration) { + s.ani.Animate(now) +} + +func (s *playLevelState) TryPlayerMove(dir geom.Point, key allg5.Key) { + if !s.isIdle() { + return + } + + to := s.player.pos.Add(dir) + if !s.canMove(s.player.pos, dir) { + log.Printf("Move is not allowed (tried out move to %s after key '%s' was pressed)", to, gut.KeyToString(key)) + return + } + + s.steps++ + log.Printf("Moving player to %s", to) + s.ani.StartFn(s.ctx.Tick, newEntityMoveAnimation(s.player, to), func() { + log.Printf("Player movement finished") + if s.keysDown[key] && s.keysDown.CountPressed(s.ctx.Settings.Controls.MovementKeys()...) == 1 { + log.Printf("Key %s is still down, moving further", gut.KeyToString(key)) + s.TryPlayerMove(dir, key) + } + }) + + if brick := findEntityAt(s.bricks, to); brick != nil { + log.Printf("Pushing brick at %s", to) + brickTo := to.Add(dir) + s.ani.StartFn(s.ctx.Tick, newEntityMoveAnimation(brick, brickTo), func() { + log.Printf("Brick movement finished") + if s.checkTile(brickTo, s.wouldBrickSink) { + log.Printf("Sinking brick at %s", brickTo) + idx := findEntityIdx(s.bricks, brickTo) + s.bricks = append(s.bricks[:idx], s.bricks[idx+1:]...) + s.sunken = append(s.sunken, brick) + } + }) + } +} + +func (s *playLevelState) canMove(from, dir geom.Point) bool { + to := from.Add(dir) + if !s.checkTile(to, s.isSolidTile) { + return false + } + brick := findEntityAt(s.bricks, to) + if brick != nil { + brickTo := to.Add(dir) + return !s.checkTileNotFound(brickTo, s.isObstructed, true) + } + return true +} + +func (s *playLevelState) checkTile(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool) bool { + return s.checkTileNotFound(pos, check, false) +} + +func (s *playLevelState) checkTileNotFound(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool, notFound bool) bool { + idx := s.level.posToIdx(pos) + if idx == -1 { + return notFound + } + return check(pos, idx, s.level.tiles[idx]) +} + +func (s *playLevelState) findEntityAt(pos geom.Point) *entity { + if s.player.pos == pos { + return s.player + } + brick := findEntityAt(s.bricks, pos) + if brick != nil { + return brick + } + return findEntityAt(s.sunken, pos) +} + +func (s *playLevelState) isIdle() bool { return s.ani.Idle() } + +func (s *playLevelState) isObstructed(pos geom.Point, idx int, t tile) bool { + if findEntityAt(s.bricks, pos) != nil { + return true // brick + } + switch s.level.tiles[idx] { + case tileMagma: + return false + case tileBasic: + return false + } + return true +} + +func (s *playLevelState) isMagma(pos geom.Point, idx int, t tile) bool { return t == tileMagma } + +func (s *playLevelState) isSolidTile(pos geom.Point, idx int, t tile) bool { + switch t { + case tileBasic: + return true + case tileMagma: + return findEntityAt(s.sunken, pos) != nil + } + return false +} + +func (s *playLevelState) wouldBrickSink(pos geom.Point, idx int, t tile) bool { + return t == tileMagma && findEntityAt(s.sunken, pos) == nil +} diff --git a/cmd/krampus19/res/basic_tile.png b/cmd/krampus19/res/basic_tile.png deleted file mode 100644 index 26c4be4..0000000 Binary files a/cmd/krampus19/res/basic_tile.png and /dev/null differ diff --git a/cmd/krampus19/res/brick.png b/cmd/krampus19/res/brick.png deleted file mode 100644 index d6d4a3d..0000000 Binary files a/cmd/krampus19/res/brick.png and /dev/null differ diff --git a/cmd/krampus19/res/crate.png b/cmd/krampus19/res/crate.png deleted file mode 100644 index f38e4f5..0000000 Binary files a/cmd/krampus19/res/crate.png and /dev/null differ diff --git a/cmd/krampus19/res/entity_brick.png b/cmd/krampus19/res/entity_brick.png new file mode 100644 index 0000000..90f324e Binary files /dev/null and b/cmd/krampus19/res/entity_brick.png differ diff --git a/cmd/krampus19/res/main_character.png b/cmd/krampus19/res/entity_main_character.png similarity index 100% rename from cmd/krampus19/res/main_character.png rename to cmd/krampus19/res/entity_main_character.png diff --git a/cmd/krampus19/res/levels/level1.txt b/cmd/krampus19/res/levels/level1.txt index cbf9131..30a10ff 100644 --- a/cmd/krampus19/res/levels/level1.txt +++ b/cmd/krampus19/res/levels/level1.txt @@ -1,11 +1,11 @@ level: ._._._._._._._._._._ -._._#_#_#_._._._._._ -._._#_._#B._._._._._ -._._#_#_#_._._._._._ -._._#_._#B._._._._._ +._#_#_#_#_._._._._._ +._#_#_._#B._._._._._ +._#_#_#_#_._._._._._ +._#_#_._#B._._._._._ ._#@#_#_#_~_~_#_#X._ -._._._._~_~_#_._._._ -._._._._#_#_#_._._._ +._#_#_~_~_~_#_#_#_._ +._#_#_~_#_#_#_#_#_._ ._._._._._._._._._._ :level \ No newline at end of file diff --git a/cmd/krampus19/res/sprites/brick.txt b/cmd/krampus19/res/sprites/brick.txt new file mode 100644 index 0000000..31843e8 --- /dev/null +++ b/cmd/krampus19/res/sprites/brick.txt @@ -0,0 +1,11 @@ +sprite: +texture: brick + +part: +name: brick +sub_texture: 0,0,1020,960 +anchor: 510,780 +scale: 6 +:part + +:sprite \ No newline at end of file diff --git a/cmd/krampus19/res/sprites/dragon.txt b/cmd/krampus19/res/sprites/dragon.txt deleted file mode 100644 index 1f8bce3..0000000 --- a/cmd/krampus19/res/sprites/dragon.txt +++ /dev/null @@ -1,28 +0,0 @@ -sprite: -texture: dragon - -part: -name: body -sub_texture: 0,0,2107,1284 -anchor: 1065,594 -:part - -part: -name: foot -sub_texture: 42,1345,529,856 -anchor: 386,1283 -:part - -part: -name: upper_wing -sub_texture: 2112,39,1080,651 -anchor: 2413,644 -:part - -part: -name: lower_wing -sub_texture: 2170,764,1030,736 -anchor: 2658,799 -:part - -:sprite \ No newline at end of file diff --git a/cmd/krampus19/res/sprites/lava_brick.txt b/cmd/krampus19/res/sprites/lava_brick.txt new file mode 100644 index 0000000..a922418 --- /dev/null +++ b/cmd/krampus19/res/sprites/lava_brick.txt @@ -0,0 +1,18 @@ +sprite: +texture: lava_brick + +part: +name: lava_brick +sub_texture: 0,0,1020,960 +anchor: 510,180 +scale: 6 +:part + +part: +name: magma +sub_texture: 0,960,1020,960 +anchor: 510,1140 +scale: 6 +:part + +:sprite \ No newline at end of file diff --git a/cmd/krampus19/res/sprites/magma.txt b/cmd/krampus19/res/sprites/magma.txt new file mode 100644 index 0000000..2205c43 --- /dev/null +++ b/cmd/krampus19/res/sprites/magma.txt @@ -0,0 +1,18 @@ +sprite: +texture: magma + +part: +name: magma +sub_texture: 0,0,1020,960 +anchor: 510,180 +scale: 6 +:part + +part: +name: sunken_overlay +sub_texture: 0,960,1020,960 +anchor: 510,1140 +scale: 6 +:part + +:sprite \ No newline at end of file diff --git a/cmd/krampus19/res/sprites/main_character.txt b/cmd/krampus19/res/sprites/main_character.txt new file mode 100644 index 0000000..f602196 --- /dev/null +++ b/cmd/krampus19/res/sprites/main_character.txt @@ -0,0 +1,11 @@ +sprite: +texture: main_character + +part: +name: main_character +sub_texture: 0,0,200,400 +anchor: 100,350 +scale: 2 +:part + +:sprite \ No newline at end of file diff --git a/cmd/krampus19/res/sunken_brick_tile.png b/cmd/krampus19/res/sunken_brick_tile.png deleted file mode 100644 index 2744900..0000000 Binary files a/cmd/krampus19/res/sunken_brick_tile.png and /dev/null differ diff --git a/cmd/krampus19/res/tile_lava_brick.png b/cmd/krampus19/res/tile_lava_brick.png new file mode 100644 index 0000000..85ac0b0 Binary files /dev/null and b/cmd/krampus19/res/tile_lava_brick.png differ diff --git a/cmd/krampus19/res/tile_magma.png b/cmd/krampus19/res/tile_magma.png new file mode 100644 index 0000000..e522ea3 Binary files /dev/null and b/cmd/krampus19/res/tile_magma.png differ diff --git a/cmd/krampus19/res/water_tile.png b/cmd/krampus19/res/water_tile.png deleted file mode 100644 index 6a16faa..0000000 Binary files a/cmd/krampus19/res/water_tile.png and /dev/null differ diff --git a/cmd/krampus19/splash.go b/cmd/krampus19/splash.go index 689de16..f39d2a9 100644 --- a/cmd/krampus19/splash.go +++ b/cmd/krampus19/splash.go @@ -1,11 +1,8 @@ package main import ( - "math" "time" - "opslag.de/schobers/allg5" - "opslag.de/schobers/geom" "opslag.de/schobers/krampus19/alui" ) @@ -20,26 +17,5 @@ type splash struct { } func (s *splash) Render(ctx *alui.Context, bounds geom.RectangleF32) { - var dragon = s.ctx.Sprites["dragon"] - - var texture = s.ctx.Textures[dragon.texture] - var body = texture.Subs["body"] - var upperWing = texture.Subs["upper_wing"] - var lowerWing = texture.Subs["lower_wing"] - var foot = texture.Subs["foot"] - - scale := bounds.Dy() / 1600 - - var repeat = 2 * time.Second / time.Millisecond - var tick = float64((s.ctx.Tick / time.Millisecond) % repeat) - flap := .5*float32(math.Cos(tick*2*math.Pi/float64(repeat))) + .5 // [0..1] - upperFlap := flap*.75 + .25 // [.25..1] - lowerFlap := flap*.5 + .5 // [.5..1] - - body.DrawOptions(0, 0, allg5.DrawOptions{Scale: allg5.NewUniformScale(scale)}) - foot.DrawOptions(750*scale, 500*scale, allg5.DrawOptions{Scale: allg5.NewUniformScale(scale)}) - - var upperWingOffset = float32(upperWing.Height()) * scale * (1 - flap) * .8 - upperWing.DrawOptions(750*scale, -100*scale+upperWingOffset, allg5.DrawOptions{Scale: allg5.NewScale(scale, scale*upperFlap)}) - lowerWing.DrawOptions(750*scale, -100*scale+upperWingOffset*.95, allg5.DrawOptions{Scale: allg5.NewScale(scale, scale*lowerFlap)}) + s.atEnd = true } diff --git a/cmd/krampus19/sprite.go b/cmd/krampus19/sprite.go index a54b2bd..d14701c 100644 --- a/cmd/krampus19/sprite.go +++ b/cmd/krampus19/sprite.go @@ -14,10 +14,20 @@ type sprite struct { parts []spritePart } +func (s sprite) FindPartByName(name string) spritePart { + for _, part := range s.parts { + if part.name == name { + return part + } + } + return spritePart{} +} + type spritePart struct { name string sub geom.Rectangle anchor geom.Point + scale float32 } func loadSpriteAsset(r io.Reader) (sprite, error) { @@ -79,7 +89,7 @@ func (c *spriteContext) parsePart(p *lineParser) parseLineFn { for i, s := range s { res[i], err = strconv.Atoi(s) if err != nil { - panic("string does not represent an integer") + panic("string does not represent an integer number") } } return res @@ -88,9 +98,17 @@ func (c *spriteContext) parsePart(p *lineParser) parseLineFn { coords := strings.Split(strings.TrimSpace(s), ",") return mustAtois(coords...) } + mustAtof := func(s string) float32 { + f, err := strconv.ParseFloat(strings.TrimSpace(s), 32) + if err != nil { + panic("string does not represent an floating point number") + } + return float32(f) + } const nameTag = "name:" const subTextureTag = "sub_texture:" const anchorTag = "anchor:" + const scaleTag = "scale:" const partEndTag = ":part" if p.skipSpaceEOF() { return p.emitErr(errUnexpectedEnd) @@ -117,6 +135,10 @@ func (c *spriteContext) parsePart(p *lineParser) parseLineFn { c.part.anchor = geom.Pt(coords[0], coords[1]) p.next() return c.parsePart + case strings.HasPrefix(line, scaleTag): + c.part.scale = mustAtof(line[len(scaleTag):]) + p.next() + return c.parsePart case line == partEndTag: c.sprite.parts = append(c.sprite.parts, *c.part) p.next()