Update win screen
This commit is contained in:
parent
41275ad3c1
commit
5400251f83
36
creep.go
36
creep.go
|
@ -14,6 +14,7 @@ const (
|
|||
TypeVampire = iota
|
||||
TypeBat
|
||||
TypeGhost
|
||||
TypeSoul
|
||||
TypeTorch
|
||||
)
|
||||
|
||||
|
@ -67,6 +68,10 @@ func newCreep(creepType int, l *Level, p *gamePlayer) *gameCreep {
|
|||
imageAtlas[ImageGhost1],
|
||||
}
|
||||
startingHealth = 1
|
||||
} else if creepType == TypeSoul {
|
||||
sprites = []*ebiten.Image{
|
||||
ojasDungeonSS.Soul1,
|
||||
}
|
||||
} else if creepType == TypeTorch {
|
||||
sprites = []*ebiten.Image{
|
||||
sandstoneSS.TorchTop1,
|
||||
|
@ -114,8 +119,8 @@ func (c *gameCreep) queueNextAction() {
|
|||
func (c *gameCreep) runAway() {
|
||||
c.queueNextAction()
|
||||
|
||||
randMovementA := (rand.Float64() - 0.5) / 8
|
||||
randMovementB := (rand.Float64() - 0.5) / 8
|
||||
randMovementA := ((rand.Float64() - 0.5) * c.moveSpeed()) / 8
|
||||
randMovementB := ((rand.Float64() - 0.5) * c.moveSpeed()) / 8
|
||||
|
||||
c.moveX = c.x - c.player.x
|
||||
if c.moveX < 0 {
|
||||
|
@ -131,9 +136,20 @@ func (c *gameCreep) runAway() {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *gameCreep) moveSpeed() float64 {
|
||||
if c.creepType == TypeSoul {
|
||||
return 0.5 / 4
|
||||
}
|
||||
return 0.4
|
||||
}
|
||||
|
||||
func (c *gameCreep) seekPlayer() {
|
||||
maxSpeed := 0.5 / 9
|
||||
minSpeed := 0.1 / 9
|
||||
maxSpeed := c.moveSpeed() / 9
|
||||
minSpeed := c.moveSpeed() / 5 / 9
|
||||
|
||||
if c.creepType == TypeSoul {
|
||||
maxSpeed *= 5
|
||||
}
|
||||
|
||||
a := angle(c.x, c.y, c.player.x, c.player.y)
|
||||
c.moveX = -math.Cos(a)
|
||||
|
@ -155,14 +171,15 @@ func (c *gameCreep) seekPlayer() {
|
|||
c.moveY *= 0.9
|
||||
}
|
||||
|
||||
c.tick = 0
|
||||
c.nextAction = 1440
|
||||
}
|
||||
|
||||
func (c *gameCreep) doNextAction() {
|
||||
c.queueNextAction()
|
||||
|
||||
randMovementA := (rand.Float64() - 0.5) / 12
|
||||
randMovementB := (rand.Float64() - 0.5) / 12
|
||||
randMovementA := ((rand.Float64() - 0.5) * c.moveSpeed()) / 12
|
||||
randMovementB := ((rand.Float64() - 0.5) * c.moveSpeed()) / 12
|
||||
|
||||
if c.creepType == TypeGhost {
|
||||
c.angle = angle(c.x, c.y, c.player.x, c.player.y)
|
||||
|
@ -181,7 +198,7 @@ func (c *gameCreep) doNextAction() {
|
|||
}
|
||||
|
||||
repelled := c.repelled()
|
||||
if !repelled && rand.Intn(13) == 0 {
|
||||
if !repelled && rand.Intn(13) == 0 && c.creepType != TypeSoul {
|
||||
c.seekPlayer()
|
||||
} else {
|
||||
c.moveX = randMovementA
|
||||
|
@ -201,6 +218,9 @@ func (c *gameCreep) doNextAction() {
|
|||
}
|
||||
|
||||
func (c *gameCreep) repelled() bool {
|
||||
if c.creepType == TypeSoul {
|
||||
return false
|
||||
}
|
||||
repelled := !c.player.garlicUntil.IsZero() || !c.player.holyWaterUntil.IsZero()
|
||||
return repelled
|
||||
}
|
||||
|
@ -274,7 +294,7 @@ func (c *gameCreep) Update() {
|
|||
}
|
||||
|
||||
dx, dy := deltaXY(c.x, c.y, c.player.x, c.player.y)
|
||||
seekDistance := 2.0
|
||||
seekDistance := 3.5
|
||||
if !repelled && dx < seekDistance && dy < seekDistance {
|
||||
c.queueNextAction()
|
||||
c.seekPlayer()
|
||||
|
|
400
game.go
400
game.go
|
@ -81,14 +81,16 @@ type game struct {
|
|||
|
||||
player *gamePlayer
|
||||
|
||||
requiredSouls int
|
||||
spawnedPortal bool
|
||||
|
||||
gameStartTime time.Time
|
||||
|
||||
gameOverTime time.Time
|
||||
gameWon bool
|
||||
|
||||
winScreenBackground *ebiten.Image
|
||||
winScreenSun *ebiten.Image
|
||||
winScreenSunY float64
|
||||
winScreenColorScale float64
|
||||
|
||||
camScale float64
|
||||
camScaleTo float64
|
||||
|
||||
|
@ -135,6 +137,7 @@ func NewGame() (*game, error) {
|
|||
mousePanY: math.MinInt32,
|
||||
activeGamepad: -1,
|
||||
forceColorScale: -1,
|
||||
levelNum: 1,
|
||||
|
||||
op: &ebiten.DrawImageOptions{},
|
||||
}
|
||||
|
@ -151,11 +154,6 @@ func NewGame() (*game, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = g.reset()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
|
@ -207,9 +205,12 @@ func (g *game) newItem(itemType int) *gameItem {
|
|||
}
|
||||
|
||||
func (g *game) nextLevel() error {
|
||||
g.player.soulsRescued = 0
|
||||
|
||||
g.levelNum++
|
||||
if g.levelNum > 13 {
|
||||
log.Fatal("YOU WIN")
|
||||
if g.levelNum > 3 {
|
||||
g.showWinScreen()
|
||||
return nil
|
||||
}
|
||||
return g.generateLevel()
|
||||
}
|
||||
|
@ -230,12 +231,14 @@ func (g *game) generateLevel() error {
|
|||
}
|
||||
|
||||
// Position player.
|
||||
for {
|
||||
g.player.x = float64(rand.Intn(g.level.w))
|
||||
g.player.y = float64(rand.Intn(g.level.h))
|
||||
|
||||
if g.level.isFloor(g.player.x, g.player.y) {
|
||||
break
|
||||
if g.levelNum > 1 {
|
||||
g.player.x, g.player.y = float64(g.level.enterX), float64(g.level.enterY)+1
|
||||
} else {
|
||||
for {
|
||||
g.player.x, g.player.y = float64(rand.Intn(g.level.w)), float64(rand.Intn(g.level.h))
|
||||
if g.level.isFloor(g.player.x, g.player.y) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,6 +339,39 @@ func (g *game) updateCursor() {
|
|||
ebiten.SetCursorShape(ebiten.CursorShapeCrosshair)
|
||||
}
|
||||
|
||||
func (g *game) handlePlayerDeath() {
|
||||
if g.player.health > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
g.player.holyWaters = 0
|
||||
|
||||
g.gameOverTime = time.Now()
|
||||
|
||||
// Play die sound.
|
||||
err := g.playSound(SoundPlayerDie, playerDieVolume)
|
||||
if err != nil {
|
||||
// TODO return err
|
||||
panic(err)
|
||||
}
|
||||
|
||||
g.updateCursor()
|
||||
}
|
||||
|
||||
func (g *game) checkLevelComplete() {
|
||||
if g.player.soulsRescued < g.level.requiredSouls || g.level.exitOpen {
|
||||
return
|
||||
}
|
||||
g.level.exitOpen = true
|
||||
|
||||
g.level.tiles[g.level.exitY][g.level.exitX].sprites = nil
|
||||
g.level.tiles[g.level.exitY][g.level.exitX].AddSprite(sandstoneSS.FloorA)
|
||||
g.level.tiles[g.level.exitY][g.level.exitX].AddSprite(sandstoneSS.DoorOpen)
|
||||
|
||||
// TODO widen doorway
|
||||
// TODO add trigger entity or hardcode check
|
||||
}
|
||||
|
||||
// Update reads current user input and updates the game state.
|
||||
func (g *game) Update() error {
|
||||
gamepadDeadZone := 0.1
|
||||
|
@ -393,7 +429,6 @@ func (g *game) Update() error {
|
|||
|
||||
g.resetExpiredTimers()
|
||||
|
||||
biteThreshold := 0.75
|
||||
liveCreeps := 0
|
||||
for _, c := range g.level.creeps {
|
||||
if c.health == 0 {
|
||||
|
@ -406,11 +441,24 @@ func (g *game) Update() error {
|
|||
continue
|
||||
}
|
||||
|
||||
biteThreshold := 0.75
|
||||
if c.creepType == TypeSoul {
|
||||
biteThreshold = 0.25
|
||||
}
|
||||
|
||||
// TODO can this move into creep?
|
||||
cx, cy := c.Position()
|
||||
dx, dy := deltaXY(g.player.x, g.player.y, cx, cy)
|
||||
if dx <= biteThreshold && dy <= biteThreshold {
|
||||
if !g.godMode && !c.repelled() {
|
||||
if c.creepType == TypeSoul {
|
||||
g.player.soulsRescued++
|
||||
err := g.hurtCreep(c, -1)
|
||||
if err != nil {
|
||||
// TODO
|
||||
panic(err)
|
||||
}
|
||||
g.checkLevelComplete()
|
||||
} else if !g.godMode && !c.repelled() {
|
||||
if g.player.holyWaters > 0 {
|
||||
// TODO g.playSound(SoundItemUseHolyWater, useholyWaterVolume)
|
||||
g.player.holyWaterUntil = time.Now().Add(holyWaterActiveTime)
|
||||
|
@ -431,21 +479,8 @@ func (g *game) Update() error {
|
|||
}
|
||||
|
||||
g.addBloodSplatter(g.player.x, g.player.y)
|
||||
}
|
||||
}
|
||||
|
||||
if g.player.health == 0 {
|
||||
ebiten.SetCursorShape(ebiten.CursorShapeDefault)
|
||||
|
||||
g.player.holyWaters = 0
|
||||
|
||||
g.gameOverTime = time.Now()
|
||||
|
||||
// Play die sound.
|
||||
err := g.playSound(SoundPlayerDie, playerDieVolume)
|
||||
if err != nil {
|
||||
// TODO return err
|
||||
panic(err)
|
||||
g.handlePlayerDeath()
|
||||
}
|
||||
}
|
||||
} else if c.creepType == TypeBat && (dx <= 12 && dy <= 7) && rand.Intn(166) == 6 && time.Since(g.lastBatSound) >= batSoundDelay {
|
||||
|
@ -575,6 +610,7 @@ func (g *game) Update() error {
|
|||
|
||||
// Update boolets.
|
||||
bulletHitThreshold := 0.501
|
||||
bulletSeekThreshold := 2.0
|
||||
removed := 0
|
||||
UPDATEPROJECTILES:
|
||||
for i, p := range g.projectiles {
|
||||
|
@ -601,13 +637,16 @@ UPDATEPROJECTILES:
|
|||
}
|
||||
|
||||
for _, c := range g.level.creeps {
|
||||
if c.health == 0 {
|
||||
if c.health == 0 || c.creepType == TypeSoul {
|
||||
continue
|
||||
}
|
||||
|
||||
cx, cy := c.Position()
|
||||
dx, dy := deltaXY(p.x, p.y, cx, cy)
|
||||
if dx > bulletHitThreshold || dy > bulletHitThreshold {
|
||||
if dx < bulletSeekThreshold && dy < bulletSeekThreshold {
|
||||
c.seekPlayer()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -660,7 +699,7 @@ UPDATEPROJECTILES:
|
|||
}
|
||||
|
||||
// Spawn garlic.
|
||||
if g.tick%(144*45) == 0 {
|
||||
if g.tick%(144*45) == 0 || rand.Intn(666) == 0 {
|
||||
item := g.newItem(itemTypeGarlic)
|
||||
g.level.items = append(g.level.items, item)
|
||||
|
||||
|
@ -670,7 +709,7 @@ UPDATEPROJECTILES:
|
|||
}
|
||||
|
||||
// Spawn holy water.
|
||||
if g.tick%(144*120) == 0 || rand.Intn(660) == 0 { // TODO
|
||||
if g.tick%(144*120) == 0 || rand.Intn(666) == 0 {
|
||||
item := g.newItem(itemTypeHolyWater)
|
||||
g.level.items = append(g.level.items, item)
|
||||
|
||||
|
@ -729,14 +768,7 @@ UPDATEPROJECTILES:
|
|||
}
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyG) {
|
||||
g.godMode = !g.godMode
|
||||
if g.godMode {
|
||||
g.flashMessage("GOD MODE ACTIVATED")
|
||||
} else {
|
||||
g.flashMessage("GOD MODE DEACTIVATED")
|
||||
}
|
||||
}
|
||||
// Read user input.
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyM) {
|
||||
g.muteAudio = !g.muteAudio
|
||||
if g.muteAudio {
|
||||
|
@ -745,88 +777,92 @@ UPDATEPROJECTILES:
|
|||
g.flashMessage("AUDIO UNMUTED")
|
||||
}
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyN) {
|
||||
g.noclipMode = !g.noclipMode
|
||||
if g.noclipMode {
|
||||
g.flashMessage("NOCLIP MODE ACTIVATED")
|
||||
} else {
|
||||
g.flashMessage("NOCLIP MODE DEACTIVATED")
|
||||
}
|
||||
}
|
||||
spawnAmount := 13
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.Key1) {
|
||||
for i := 0; i < spawnAmount; i++ {
|
||||
g.level.addCreep(TypeVampire)
|
||||
}
|
||||
g.flashMessage(fmt.Sprintf("SPAWNED %d VAMPIRES", spawnAmount))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.Key2) {
|
||||
for i := 0; i < spawnAmount; i++ {
|
||||
g.level.addCreep(TypeBat)
|
||||
}
|
||||
g.flashMessage(fmt.Sprintf("SPAWNED %d BATS", spawnAmount))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.Key3) {
|
||||
for i := 0; i < spawnAmount; i++ {
|
||||
g.level.addCreep(TypeGhost)
|
||||
}
|
||||
g.flashMessage(fmt.Sprintf("SPAWNED %d GHOST", spawnAmount))
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.Key7) {
|
||||
g.player.holyWaters++
|
||||
g.flashMessage("SPAWNED HOLY WATER")
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.Key8) {
|
||||
// TODO Add garlic to inventory
|
||||
//g.flashMessage("+ GARLIC")
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyDigit0) {
|
||||
g.fullBrightMode = !g.fullBrightMode
|
||||
if g.fullBrightMode {
|
||||
g.flashMessage("FULLBRIGHT MODE ACTIVATED")
|
||||
} else {
|
||||
g.flashMessage("FULLBRIGHT MODE DEACTIVATED")
|
||||
}
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyShift) && inpututil.IsKeyJustPressed(ebiten.KeyEqual) {
|
||||
g.showWinScreen()
|
||||
g.flashMessage("WARPED TO WIN SCREEN")
|
||||
} else if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyEqual) {
|
||||
err := g.nextLevel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.flashMessage(fmt.Sprintf("WARPED TO LEVEL %d", g.levelNum))
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyV) {
|
||||
g.debugMode = !g.debugMode
|
||||
if g.debugMode {
|
||||
g.flashMessage("DEBUG MODE ACTIVATED")
|
||||
} else {
|
||||
g.flashMessage("DEBUG MODE DEACTIVATED")
|
||||
}
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyP) {
|
||||
if g.cpuProfile == nil {
|
||||
g.flashMessage("CPU PROFILING STARTED")
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) {
|
||||
spawnAmount := 13
|
||||
switch {
|
||||
case inpututil.IsKeyJustPressed(ebiten.KeyE):
|
||||
g.player.x, g.player.y = float64(g.level.exitX), float64(g.level.exitY+1)
|
||||
g.flashMessage("WARPED TO EXIT")
|
||||
case inpututil.IsKeyJustPressed(ebiten.KeyF):
|
||||
g.fullBrightMode = !g.fullBrightMode
|
||||
if g.fullBrightMode {
|
||||
g.flashMessage("FULLBRIGHT MODE ACTIVATED")
|
||||
} else {
|
||||
g.flashMessage("FULLBRIGHT MODE DEACTIVATED")
|
||||
}
|
||||
case inpututil.IsKeyJustPressed(ebiten.KeyG):
|
||||
g.godMode = !g.godMode
|
||||
if g.godMode {
|
||||
g.flashMessage("GOD MODE ACTIVATED")
|
||||
} else {
|
||||
g.flashMessage("GOD MODE DEACTIVATED")
|
||||
}
|
||||
case inpututil.IsKeyJustPressed(ebiten.KeyN):
|
||||
g.noclipMode = !g.noclipMode
|
||||
if g.noclipMode {
|
||||
g.flashMessage("NOCLIP MODE ACTIVATED")
|
||||
} else {
|
||||
g.flashMessage("NOCLIP MODE DEACTIVATED")
|
||||
}
|
||||
case inpututil.IsKeyJustPressed(ebiten.KeyV):
|
||||
g.debugMode = !g.debugMode
|
||||
if g.debugMode {
|
||||
g.flashMessage("DEBUG MODE ACTIVATED")
|
||||
} else {
|
||||
g.flashMessage("DEBUG MODE DEACTIVATED")
|
||||
}
|
||||
case inpututil.IsKeyJustPressed(ebiten.KeyP):
|
||||
if g.cpuProfile == nil {
|
||||
g.flashMessage("CPU PROFILING STARTED")
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.cpuProfile, err = os.Create(path.Join(homeDir, "cartillery.prof"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pprof.StartCPUProfile(g.cpuProfile); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
g.flashMessage("CPU PROFILING STOPPED")
|
||||
|
||||
pprof.StopCPUProfile()
|
||||
g.cpuProfile.Close()
|
||||
g.cpuProfile = nil
|
||||
}
|
||||
case inpututil.IsKeyJustPressed(ebiten.Key1):
|
||||
for i := 0; i < spawnAmount; i++ {
|
||||
g.level.addCreep(TypeVampire)
|
||||
}
|
||||
g.flashMessage(fmt.Sprintf("SPAWNED %d VAMPIRES", spawnAmount))
|
||||
case inpututil.IsKeyJustPressed(ebiten.Key2):
|
||||
for i := 0; i < spawnAmount; i++ {
|
||||
g.level.addCreep(TypeBat)
|
||||
}
|
||||
g.flashMessage(fmt.Sprintf("SPAWNED %d BATS", spawnAmount))
|
||||
case inpututil.IsKeyJustPressed(ebiten.Key3):
|
||||
for i := 0; i < spawnAmount; i++ {
|
||||
g.level.addCreep(TypeGhost)
|
||||
}
|
||||
g.flashMessage(fmt.Sprintf("SPAWNED %d GHOSTS", spawnAmount))
|
||||
case inpututil.IsKeyJustPressed(ebiten.Key7):
|
||||
g.player.holyWaters++
|
||||
g.flashMessage("SPAWNED HOLY WATER")
|
||||
case inpututil.IsKeyJustPressed(ebiten.Key8):
|
||||
// TODO Add garlic to inventory
|
||||
//g.flashMessage("+ GARLIC")
|
||||
case ebiten.IsKeyPressed(ebiten.KeyShift) && inpututil.IsKeyJustPressed(ebiten.KeyEqual):
|
||||
g.showWinScreen()
|
||||
g.flashMessage("WARPED TO WIN SCREEN")
|
||||
case inpututil.IsKeyJustPressed(ebiten.KeyEqual):
|
||||
err := g.nextLevel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.cpuProfile, err = os.Create(path.Join(homeDir, "cartillery.prof"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pprof.StartCPUProfile(g.cpuProfile); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
g.flashMessage("CPU PROFILING STOPPED")
|
||||
|
||||
pprof.StopCPUProfile()
|
||||
g.cpuProfile.Close()
|
||||
g.cpuProfile = nil
|
||||
g.flashMessage(fmt.Sprintf("WARPED TO LEVEL %d", g.levelNum))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -834,12 +870,12 @@ UPDATEPROJECTILES:
|
|||
return nil
|
||||
}
|
||||
|
||||
func (g *game) drawText(target *ebiten.Image, y float64, scale float64, alpha float64, text string) {
|
||||
func (g *game) drawText(target *ebiten.Image, offsetX float64, y float64, scale float64, alpha float64, text string) {
|
||||
g.overlayImg.Clear()
|
||||
ebitenutil.DebugPrint(g.overlayImg, text)
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Scale(scale, scale)
|
||||
g.op.GeoM.Translate(float64(g.w/2)-(float64(len(text))*3*scale), y)
|
||||
g.op.GeoM.Translate(float64(g.w/2)-(float64(len(text))*3*scale)+offsetX, y)
|
||||
g.op.ColorM.Scale(1, 1, 1, alpha)
|
||||
target.DrawImage(g.overlayImg, g.op)
|
||||
g.op.ColorM.Reset()
|
||||
|
@ -850,14 +886,14 @@ func (g *game) Draw(screen *ebiten.Image) {
|
|||
if g.gameStartTime.IsZero() {
|
||||
screen.Fill(colorBlood)
|
||||
|
||||
g.drawText(screen, float64(g.h/2)-350, 16, 1.0, "CAROTID")
|
||||
g.drawText(screen, float64(g.h/2)-100, 16, 1.0, "ARTILLERY")
|
||||
g.drawText(screen, 0, float64(g.h/2)-350, 16, 1.0, "CAROTID")
|
||||
g.drawText(screen, 0, float64(g.h/2)-100, 16, 1.0, "ARTILLERY")
|
||||
|
||||
g.drawText(screen, float64(g.h-210), 4, 1.0, "WASD + MOUSE = OK")
|
||||
g.drawText(screen, float64(g.h-145), 4, 1.0, "FULLSCREEN + GAMEPAD = BEST")
|
||||
g.drawText(screen, 0, float64(g.h-210), 4, 1.0, "WASD + MOUSE = OK")
|
||||
g.drawText(screen, 0, float64(g.h-145), 4, 1.0, "FULLSCREEN + GAMEPAD = BEST")
|
||||
|
||||
if time.Now().UnixMilli()%2000 < 1500 {
|
||||
g.drawText(screen, float64(g.h-80), 4, 1.0, "PRESS ANY KEY OR BUTTON TO START")
|
||||
g.drawText(screen, 0, float64(g.h-80), 4, 1.0, "PRESS ANY KEY OR BUTTON TO START")
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -865,6 +901,18 @@ func (g *game) Draw(screen *ebiten.Image) {
|
|||
|
||||
var drawn int
|
||||
if g.gameOverTime.IsZero() || g.gameWon {
|
||||
if g.gameWon {
|
||||
g.op.GeoM.Reset()
|
||||
g.op.ColorM.Reset()
|
||||
g.op.ColorM.Scale(g.winScreenColorScale, g.winScreenColorScale, g.winScreenColorScale, 1)
|
||||
screen.DrawImage(g.winScreenBackground, g.op)
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Translate(float64(g.w)*0.75, g.winScreenSunY)
|
||||
g.op.ColorM.Reset()
|
||||
g.op.ColorM.Scale(g.winScreenColorScale, g.winScreenColorScale, g.winScreenColorScale, 1)
|
||||
screen.DrawImage(g.winScreenSun, g.op)
|
||||
g.op.ColorM.Reset()
|
||||
}
|
||||
drawn = g.renderLevel(screen)
|
||||
} else {
|
||||
// Draw game over screen.
|
||||
|
@ -882,30 +930,49 @@ func (g *game) Draw(screen *ebiten.Image) {
|
|||
screen.DrawImage(img, g.op)
|
||||
g.op.ColorM.Reset()
|
||||
|
||||
g.drawText(screen, float64(g.h/2)-150, 16, a, "GAME OVER")
|
||||
g.drawText(screen, 0, float64(g.h/2)-150, 16, a, "GAME OVER")
|
||||
|
||||
if time.Since(g.gameOverTime).Milliseconds()%2000 < 1500 {
|
||||
g.drawText(screen, 8, 4, a, "PRESS ENTER OR START TO PLAY AGAIN")
|
||||
g.drawText(screen, 0, 8, 4, a, "PRESS ENTER OR START TO PLAY AGAIN")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if g.gameOverTime.IsZero() {
|
||||
// Draw health.
|
||||
heartSpace := 32
|
||||
heartX := (g.w / 2) - ((heartSpace * g.player.health) / 2) + 8
|
||||
heartX := (g.w / 2) - ((heartSpace * g.player.health) / 2)
|
||||
for i := 0; i < g.player.health; i++ {
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Translate(float64(heartX+(i*heartSpace)), 32)
|
||||
screen.DrawImage(imageAtlas[ImageHeart], g.op)
|
||||
}
|
||||
|
||||
// Draw holy waters.
|
||||
holyWaterSpace := 16
|
||||
holyWaterX := (g.w / 2) - ((holyWaterSpace * g.player.holyWaters) / 2)
|
||||
holyWaterX := (g.w / 2) - ((holyWaterSpace * g.player.holyWaters) / 2) - 8
|
||||
for i := 0; i < g.player.holyWaters; i++ {
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Translate(float64(holyWaterX+(i*holyWaterSpace)), 76)
|
||||
screen.DrawImage(imageAtlas[ImageHolyWater], g.op)
|
||||
}
|
||||
|
||||
scale := 3.0
|
||||
soulsY := 104.0
|
||||
if !g.level.exitOpen {
|
||||
// Draw souls.
|
||||
soulsLabel := fmt.Sprintf("%d", g.level.requiredSouls-g.player.soulsRescued)
|
||||
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Translate(float64(g.w/2)-(float64(len(soulsLabel))*3*scale)-40, soulsY+8)
|
||||
screen.DrawImage(ojasDungeonSS.Soul1, g.op)
|
||||
|
||||
g.drawText(screen, 0, soulsY, scale, 1.0, soulsLabel)
|
||||
} else {
|
||||
// Draw exit message.
|
||||
// TODO flash text
|
||||
g.drawText(screen, 0, soulsY, scale, 1.0, "EXIT OPEN")
|
||||
}
|
||||
}
|
||||
|
||||
flashTime := g.flashMessageUntil.Sub(time.Now())
|
||||
|
@ -914,7 +981,7 @@ func (g *game) Draw(screen *ebiten.Image) {
|
|||
if alpha > 1 {
|
||||
alpha = 1
|
||||
}
|
||||
g.drawText(screen, float64(g.h-40), 2, alpha, g.flashMessageText)
|
||||
g.drawText(screen, 0, float64(g.h-40), 2, alpha, g.flashMessageText)
|
||||
}
|
||||
|
||||
if !g.gameWon {
|
||||
|
@ -923,7 +990,7 @@ func (g *game) Draw(screen *ebiten.Image) {
|
|||
a = 1
|
||||
}
|
||||
scoreLabel := numberPrinter.Sprintf("%d", g.player.score)
|
||||
g.drawText(screen, float64(g.h-150), 8, a, scoreLabel)
|
||||
g.drawText(screen, 0, float64(g.h-150), 8, a, scoreLabel)
|
||||
}
|
||||
|
||||
if !g.debugMode {
|
||||
|
@ -1014,7 +1081,12 @@ func (g *game) renderLevel(screen *ebiten.Image) int {
|
|||
continue
|
||||
}
|
||||
|
||||
drawn += g.renderSprite(c.x, c.y, 0, 0, c.angle, 1.0, g.colorScale(c.x, c.y), 1.0, c.sprites[c.frame], screen)
|
||||
a := 1.0
|
||||
if c.creepType == TypeSoul {
|
||||
a = 0.35
|
||||
}
|
||||
|
||||
drawn += g.renderSprite(c.x, c.y, 0, 0, c.angle, 1.0, g.colorScale(c.x, c.y), a, c.sprites[c.frame], screen)
|
||||
if c.frames > 1 && time.Since(c.lastFrame) >= 75*time.Millisecond {
|
||||
c.frame++
|
||||
if c.frame == c.frames {
|
||||
|
@ -1211,6 +1283,11 @@ func (g *game) hurtCreep(c *gameCreep, damage int) error {
|
|||
|
||||
g.addBloodSplatter(c.x, c.y)
|
||||
|
||||
soul := g.level.addCreep(TypeSoul)
|
||||
soul.x, soul.y = c.x, c.y
|
||||
soul.moveX, soul.moveY = c.moveX/4, c.moveY/4
|
||||
soul.tick, soul.nextAction = c.tick, c.nextAction
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1256,6 +1333,11 @@ func (g *game) showWinScreen() {
|
|||
if !g.gameOverTime.IsZero() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO make it a sunrise instead
|
||||
|
||||
g.forceColorScale = 0.4
|
||||
|
||||
g.gameWon = true
|
||||
g.gameOverTime = time.Now()
|
||||
|
||||
|
@ -1267,14 +1349,54 @@ func (g *game) showWinScreen() {
|
|||
|
||||
g.level = newWinLevel(g.player)
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Second)
|
||||
// TODO move win screen background upward
|
||||
|
||||
g.winScreenBackground = ebiten.NewImage(g.w, g.h)
|
||||
g.winScreenBackground.Fill(colornames.Deepskyblue)
|
||||
|
||||
sunSize := 66
|
||||
g.winScreenSun = ebiten.NewImage(sunSize, sunSize)
|
||||
g.winScreenSun.Fill(color.RGBA{254, 231, 108, 255})
|
||||
|
||||
g.winScreenSunY = float64(g.h/2) + float64(sunSize/2)
|
||||
|
||||
go func() {
|
||||
// Animate sunrise.
|
||||
go func() {
|
||||
time.Sleep(5 * time.Second)
|
||||
for i := 0; i < 144*15; i++ {
|
||||
g.winScreenSunY -= 0.035
|
||||
|
||||
if i > int(144*3.5) {
|
||||
g.forceColorScale += 0.0005
|
||||
if g.forceColorScale > 1 {
|
||||
g.forceColorScale = 1
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
}()
|
||||
|
||||
// Fade in sky.
|
||||
for i := 0.0001; i < 1; i *= 1.005 {
|
||||
g.winScreenColorScale = i
|
||||
|
||||
for i := 1.0; i > 0.001; i *= 0.99 {
|
||||
g.forceColorScale = i
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Fade out win screen.
|
||||
|
||||
for i := 0; i < 144; i++ {
|
||||
g.winScreenColorScale -= 0.01
|
||||
g.forceColorScale -= 0.01
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
|
||||
// Fade in game over screen.
|
||||
|
||||
g.gameWon = false
|
||||
|
||||
defer func() {
|
||||
|
|
8
go.mod
8
go.mod
|
@ -5,16 +5,16 @@ go 1.17
|
|||
require (
|
||||
github.com/Meshiest/go-dungeon v0.0.0-20160809210039-1d1d1e7596b8
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.1
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
||||
golang.org/x/text v0.3.7
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be // indirect
|
||||
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.2 // indirect
|
||||
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.3 // indirect
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect
|
||||
golang.org/x/exp v0.0.0-20211025140241-8418b01e8c3b // indirect
|
||||
golang.org/x/mobile v0.0.0-20211027134744-eb3c0abee20a // indirect
|
||||
golang.org/x/exp v0.0.0-20211029153326-98a2daa683e3 // indirect
|
||||
golang.org/x/mobile v0.0.0-20211029123140-7b8bfe09930c // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
|
||||
)
|
||||
|
|
24
go.sum
24
go.sum
|
@ -106,8 +106,9 @@ github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod
|
|||
github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1 h1:7cJz/zRQV4aJvMSSRqzN2TImoVVMpE0BCY4nrNJaDOM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.2 h1:DV2DcbY3YLuLB9gI9R1GT9TPOo92lUeWveV8ci1sBLk=
|
||||
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ=
|
||||
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.3 h1:vXHLbXL0GpLE9RRUzTeTashvto4mjRF/AAT+0TIHW1M=
|
||||
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.3/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ=
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
|
@ -258,6 +259,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
|
|||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
|
@ -287,16 +289,18 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp v0.0.0-20211025140241-8418b01e8c3b h1:AHIXEGqpPGg/yJCO803Q+UbBiq/vkCe/OPwMSVE7psI=
|
||||
golang.org/x/exp v0.0.0-20211025140241-8418b01e8c3b/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA=
|
||||
golang.org/x/exp v0.0.0-20211029153326-98a2daa683e3 h1:EThhdleNHAYpuHkwXUPv0bjZuNhSMmNrIvCandX6jSU=
|
||||
golang.org/x/exp v0.0.0-20211029153326-98a2daa683e3/go.mod h1:OyI624f2tQ/aU3IMa7GB16Hk54CHURAfHfj6tMqtyhA=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -306,15 +310,16 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
|
|||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5/go.mod h1:c4YKU3ZylDmvbw+H/PSvm42vhdWbuxCzbonauEAP9B8=
|
||||
golang.org/x/mobile v0.0.0-20211027134744-eb3c0abee20a h1:AVqU+D7dTd7kx8xFcaRktB8LbDTIA29vxySTqiZ7A1w=
|
||||
golang.org/x/mobile v0.0.0-20211027134744-eb3c0abee20a/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
||||
golang.org/x/mobile v0.0.0-20211029123140-7b8bfe09930c h1:K3U/yO0T6tX3GbBJxRdmhVD/JAQBtDXynUQW38GUujM=
|
||||
golang.org/x/mobile v0.0.0-20211029123140-7b8bfe09930c/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -332,8 +337,10 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -369,8 +376,10 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
@ -402,6 +411,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
|||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
41
level.go
41
level.go
|
@ -25,6 +25,13 @@ type Level struct {
|
|||
player *gamePlayer
|
||||
|
||||
torches []*gameCreep
|
||||
|
||||
enterX, enterY int
|
||||
exitX, exitY int
|
||||
|
||||
exitOpen bool
|
||||
|
||||
requiredSouls int
|
||||
}
|
||||
|
||||
// Tile returns the tile at the provided coordinates, or nil.
|
||||
|
@ -99,6 +106,13 @@ func NewLevel(levelNum int, p *gamePlayer) (*Level, error) {
|
|||
player: p,
|
||||
}
|
||||
|
||||
l.requiredSouls = 66
|
||||
if levelNum == 2 {
|
||||
l.requiredSouls = 666
|
||||
} else if levelNum == 3 {
|
||||
l.requiredSouls = 6666
|
||||
}
|
||||
|
||||
var err error
|
||||
sandstoneSS, err = LoadEnvironmentSpriteSheet()
|
||||
if err != nil {
|
||||
|
@ -149,6 +163,8 @@ func NewLevel(levelNum int, p *gamePlayer) (*Level, error) {
|
|||
return t.floor
|
||||
}
|
||||
|
||||
var topWalls [][2]int
|
||||
|
||||
// Add walls.
|
||||
for x := 0; x < l.w; x++ {
|
||||
for y := 0; y < l.h; y++ {
|
||||
|
@ -196,6 +212,7 @@ func NewLevel(levelNum int, p *gamePlayer) (*Level, error) {
|
|||
l.torches = append(l.torches, c)
|
||||
} else {
|
||||
neighbor.AddSprite(sandstoneSS.WallTop)
|
||||
topWalls = append(topWalls, [2]int{nx, ny})
|
||||
}
|
||||
case spriteLeft:
|
||||
if spriteBottom {
|
||||
|
@ -218,6 +235,27 @@ func NewLevel(levelNum int, p *gamePlayer) (*Level, error) {
|
|||
}
|
||||
}
|
||||
|
||||
entrance := topWalls[rand.Intn(len(topWalls))]
|
||||
exit := entrance
|
||||
for exit == entrance {
|
||||
exit = topWalls[rand.Intn(len(topWalls))]
|
||||
}
|
||||
|
||||
l.enterX, l.enterY = entrance[0], entrance[1]
|
||||
l.exitX, l.exitY = exit[0], exit[1]
|
||||
|
||||
if levelNum > 1 {
|
||||
l.Tile(l.enterX, l.enterY).sprites = nil
|
||||
l.Tile(l.enterX, l.enterY).AddSprite(sandstoneSS.FloorA)
|
||||
l.Tile(l.enterX, l.enterY).AddSprite(sandstoneSS.DoorClosed)
|
||||
}
|
||||
|
||||
l.Tile(l.exitX, l.exitY).sprites = nil
|
||||
l.Tile(l.exitX, l.exitY).AddSprite(sandstoneSS.FloorA)
|
||||
l.Tile(l.exitX, l.exitY).AddSprite(sandstoneSS.DoorClosed)
|
||||
|
||||
// TODO special door for final exit
|
||||
|
||||
l.bakeLightmap()
|
||||
|
||||
return l, nil
|
||||
|
@ -261,9 +299,10 @@ func (l *Level) bakePartialLightmap(lx, ly int) {
|
|||
}
|
||||
}
|
||||
|
||||
func (l *Level) addCreep(creepType int) {
|
||||
func (l *Level) addCreep(creepType int) *gameCreep {
|
||||
c := newCreep(creepType, l, l.player)
|
||||
l.creeps = append(l.creeps, c)
|
||||
return c
|
||||
}
|
||||
|
||||
func angle(x1, y1, x2, y2 float64) float64 {
|
||||
|
|
8
main.go
8
main.go
|
@ -28,7 +28,15 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = g.reset()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
parseFlags(g)
|
||||
if !g.debugMode {
|
||||
g.gameStartTime = time.Time{}
|
||||
}
|
||||
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Notify(sigc,
|
||||
|
|
|
@ -33,6 +33,8 @@ type EnvironmentSpriteSheet struct {
|
|||
TorchTop8 *ebiten.Image
|
||||
TorchTop9 *ebiten.Image
|
||||
TorchMulti *ebiten.Image
|
||||
DoorOpen *ebiten.Image
|
||||
DoorClosed *ebiten.Image
|
||||
}
|
||||
|
||||
// LoadEnvironmentSpriteSheet loads the embedded EnvironmentSpriteSheet.
|
||||
|
@ -69,6 +71,8 @@ func LoadEnvironmentSpriteSheet() (*EnvironmentSpriteSheet, error) {
|
|||
s.WallTopLeft = dungeonSpriteAt(2, 3)
|
||||
s.WallTopRight = dungeonSpriteAt(0, 3)
|
||||
s.WallPillar = dungeonSpriteAt(8, 4)
|
||||
s.DoorOpen = dungeonSpriteAt(3, 3)
|
||||
s.DoorClosed = dungeonSpriteAt(3, 2)
|
||||
|
||||
// Prop sprites
|
||||
propFile, err := assetsFS.Open("assets/sandstone-dungeon/Tiles-Props-pack.png")
|
||||
|
|
|
@ -27,6 +27,7 @@ type OjasDungeonSpriteSheet struct {
|
|||
Grass81 *ebiten.Image
|
||||
Grass82 *ebiten.Image
|
||||
Grass91 *ebiten.Image
|
||||
Soul1 *ebiten.Image
|
||||
Wall1 *ebiten.Image
|
||||
Vent1 *ebiten.Image
|
||||
Door11 *ebiten.Image
|
||||
|
@ -71,6 +72,7 @@ func LoadOjasDungeonSpriteSheet() (*OjasDungeonSpriteSheet, error) {
|
|||
s.Grass81 = spriteAt(14, 11)
|
||||
s.Grass82 = spriteAt(10, 14)
|
||||
s.Grass91 = spriteAt(10, 15)
|
||||
s.Soul1 = spriteAt(8, 11)
|
||||
s.Wall1 = spriteAt(7, 5)
|
||||
s.Vent1 = spriteAt(9, 13)
|
||||
s.Door11 = spriteAt(3, 6)
|
||||
|
|
103
win.go
103
win.go
|
@ -125,26 +125,65 @@ func newWinLevel(p *gamePlayer) *Level {
|
|||
|
||||
l.bakeLightmap()
|
||||
|
||||
doorX := float64(startX) - 0.4
|
||||
|
||||
p.angle = 0
|
||||
p.x, p.y = float64(startX), float64(startY)
|
||||
p.x, p.y = doorX, float64(startY)
|
||||
|
||||
go func() {
|
||||
// Walk away.
|
||||
for i := 0; i < 144; i++ {
|
||||
p.x += 0.05 * (float64(144-i) / 144)
|
||||
for i := 0; i < 36; i++ {
|
||||
p.x += 0.05
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
for i := 0; i < 288; i++ {
|
||||
p.x += 0.05 * (float64(288-i) / 288)
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
// Turn around.
|
||||
p.angle = math.Pi
|
||||
time.Sleep(time.Second / 2)
|
||||
time.Sleep(time.Millisecond * 1750)
|
||||
|
||||
throwEnd := float64(startX) - 0.4
|
||||
// Throw weapon.
|
||||
weaponSprite := newCreep(TypeTorch, l, p)
|
||||
weaponSprite.x, weaponSprite.y = p.x, p.y-0.25
|
||||
weaponSprite.frames = 1
|
||||
weaponSprite.frame = 0
|
||||
weaponSprite.sprites = []*ebiten.Image{
|
||||
imageAtlas[ImageUzi],
|
||||
}
|
||||
|
||||
p.weapon = nil
|
||||
l.creeps = append(l.creeps, weaponSprite)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < 144*2; i++ {
|
||||
if weaponSprite.x < doorX {
|
||||
for i, c := range l.creeps {
|
||||
if c == weaponSprite {
|
||||
l.creeps = append(l.creeps[:i], l.creeps[i+1:]...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
weaponSprite.x -= 0.05
|
||||
if i < 100 {
|
||||
weaponSprite.y -= 0.005 * (float64(144-i) / 144)
|
||||
} else {
|
||||
weaponSprite.y += 0.01 * (float64(288-i) / 288)
|
||||
}
|
||||
weaponSprite.angle -= .1
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
// Throw torch.
|
||||
torchSprite := newCreep(TypeTorch, l, p)
|
||||
torchSprite.x, torchSprite.y = p.x, p.y
|
||||
torchSprite.x, torchSprite.y = p.x, p.y-0.25
|
||||
torchSprite.frames = 1
|
||||
torchSprite.frame = 0
|
||||
torchSprite.sprites = []*ebiten.Image{
|
||||
|
@ -156,7 +195,7 @@ func newWinLevel(p *gamePlayer) *Level {
|
|||
|
||||
go func() {
|
||||
for i := 0; i < 144*3; i++ {
|
||||
if torchSprite.x < throwEnd {
|
||||
if torchSprite.x < doorX {
|
||||
for i, c := range l.creeps {
|
||||
if c == torchSprite {
|
||||
l.creeps = append(l.creeps[:i], l.creeps[i+1:]...)
|
||||
|
@ -165,51 +204,26 @@ func newWinLevel(p *gamePlayer) *Level {
|
|||
}
|
||||
|
||||
torchSprite.x -= 0.05
|
||||
if i < 100 {
|
||||
torchSprite.y -= 0.005 * (float64(144-i) / 144)
|
||||
} else {
|
||||
torchSprite.y += 0.01 * (float64(288-i) / 288)
|
||||
}
|
||||
|
||||
torchSprite.angle -= .1
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
// Throw weapon.
|
||||
weaponSprite := newCreep(TypeTorch, l, p)
|
||||
weaponSprite.x, weaponSprite.y = p.x, p.y
|
||||
weaponSprite.frames = 1
|
||||
weaponSprite.frame = 0
|
||||
weaponSprite.sprites = []*ebiten.Image{
|
||||
imageAtlas[ImageUzi],
|
||||
}
|
||||
|
||||
p.weapon = nil
|
||||
l.creeps = append(l.creeps, weaponSprite)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < 144*3; i++ {
|
||||
if weaponSprite.x < throwEnd {
|
||||
for i, c := range l.creeps {
|
||||
if c == weaponSprite {
|
||||
l.creeps = append(l.creeps[:i], l.creeps[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
weaponSprite.x -= 0.05
|
||||
weaponSprite.angle -= .1
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
}()
|
||||
|
||||
// Walk away.
|
||||
time.Sleep(time.Second / 2)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
p.angle = 0
|
||||
time.Sleep(time.Second / 2)
|
||||
for i := 0; i < 144; i++ {
|
||||
p.x += 0.05 * (float64(i) / 144)
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
for i := 0; i < 144*12; i++ {
|
||||
for i := 0; i < 144*15; i++ {
|
||||
if p.health > 0 {
|
||||
// Game has restarted.
|
||||
return
|
||||
|
@ -217,13 +231,6 @@ func newWinLevel(p *gamePlayer) *Level {
|
|||
p.x += 0.05
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
|
||||
return
|
||||
t := time.NewTicker(time.Second / 144)
|
||||
for range t.C {
|
||||
p.x += 0.05
|
||||
p.angle += 0.1
|
||||
}
|
||||
}()
|
||||
|
||||
return l
|
||||
|
|
Loading…
Reference in New Issue