Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

State restoration using Codable #6

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion Source/Engine/Actor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Copyright © 2019 Nick Lockwood. All rights reserved.
//

public protocol Actor {
public protocol Actor: Codable {
var radius: Double { get }
var position: Vector { get set }
var isDead: Bool { get }
Expand Down Expand Up @@ -41,6 +41,10 @@ public extension Actor {
return rect.intersection(with: door.rect)
}

func intersection(with pushwall: Pushwall) -> Vector? {
return rect.intersection(with: pushwall.rect)
}

func intersection(with world: World) -> Vector? {
if let intersection = intersection(with: world.map) {
return intersection
Expand All @@ -50,6 +54,11 @@ public extension Actor {
return intersection
}
}
for pushwall in world.pushwalls where pushwall.position != position {
if let intersection = intersection(with: pushwall) {
return intersection
}
}
return nil
}

Expand All @@ -67,4 +76,20 @@ public extension Actor {
attempts -= 1
}
}

func isStuck(in world: World) -> Bool {
// If outside map
if position.x < 1 || position.x > world.map.size.x - 1 ||
position.y < 1 || position.y > world.map.size.y - 1 {
return true
}
// If stuck in a wall
if world.map[Int(position.x), Int(position.y)].isWall {
return true
}
// If stuck in pushwall
return world.pushwalls.contains(where: {
abs(position.x - $0.position.x) < 0.6 && abs(position.y - $0.position.y) < 0.6
})
}
}
2 changes: 1 addition & 1 deletion Source/Engine/Animation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Copyright © 2019 Nick Lockwood. All rights reserved.
//

public struct Animation {
public struct Animation: Codable {
public let frames: [Texture]
public let duration: Double
public var time: Double = 0
Expand Down
9 changes: 4 additions & 5 deletions Source/Engine/Color.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Copyright © 2019 Nick Lockwood. All rights reserved.
//

public struct Color {
public struct Color: Codable {
public var r, g, b, a: UInt8

public init(r: UInt8, g: UInt8, b: UInt8, a: UInt8 = 255) {
Expand All @@ -25,8 +25,7 @@ public extension Color {
static let clear = Color(r: 0, g: 0, b: 0, a: 0)
static let black = Color(r: 0, g: 0, b: 0)
static let white = Color(r: 255, g: 255, b: 255)
static let gray = Color(r: 192, g: 192, b: 192)
static let red = Color(r: 255, g: 0, b: 0)
static let green = Color(r: 0, g: 255, b: 0)
static let blue = Color(r: 0, g: 0, b: 255)
static let red = Color(r: 217, g: 87, b: 99)
static let green = Color(r: 153, g: 229, b: 80)
static let yellow = Color(r: 251, g: 242, b: 54)
}
15 changes: 11 additions & 4 deletions Source/Engine/Door.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
// Copyright © 2019 Nick Lockwood. All rights reserved.
//

public enum DoorState {
public enum DoorState: Int, Codable {
case closed
case opening
case open
case closing
}

public struct Door {
public struct Door: Codable {
public let duration: Double = 0.5
public let closeDelay: Double = 3
public let position: Vector
Expand All @@ -37,7 +37,8 @@ public struct Door {
public extension Door {
var rect: Rect {
let position = self.position + direction * (offset - 0.5)
return Rect(min: position, max: position + direction)
let depth = direction.orthogonal * 0.1
return Rect(min: position + depth, max: position + direction - depth)
}

var offset: Double {
Expand Down Expand Up @@ -70,8 +71,13 @@ public extension Door {
mutating func update(in world: inout World) {
switch state {
case .closed:
if world.player.intersection(with: self) != nil {
if world.player.intersection(with: self) != nil ||
world.monsters.contains(where: { monster in
monster.isDead == false &&
monster.intersection(with: self) != nil
}) {
state = .opening
world.playSound(.doorSlide, at: position)
time = 0
}
case .opening:
Expand All @@ -82,6 +88,7 @@ public extension Door {
case .open:
if time >= closeDelay {
state = .closing
world.playSound(.doorSlide, at: position)
time = 0
}
case .closing:
Expand Down
4 changes: 2 additions & 2 deletions Source/Engine/Effect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
// Copyright © 2019 Nick Lockwood. All rights reserved.
//

public enum EffectType {
public enum EffectType: Int, Codable {
case fadeIn
case fadeOut
case fizzleOut
}

public struct Effect {
public struct Effect: Codable {
public let type: EffectType
public let color: Color
public let duration: Double
Expand Down
12 changes: 12 additions & 0 deletions Source/Engine/Font.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// Font.swift
// Engine
//
// Created by Nick Lockwood on 21/04/2020.
// Copyright © 2020 Nick Lockwood. All rights reserved.
//

public struct Font: Decodable {
public let texture: Texture
public let characters: [String]
}
100 changes: 100 additions & 0 deletions Source/Engine/Game.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// Game.swift
// Engine
//
// Created by Nick Lockwood on 07/10/2019.
// Copyright © 2019 Nick Lockwood. All rights reserved.
//

public protocol GameDelegate: AnyObject {
func playSound(_ sound: Sound)
func clearSounds()
}

public enum GameState {
case title
case starting
case playing
}

public struct SavedGame: Codable {
let world: World?
}

public struct Game {
public weak var delegate: GameDelegate?
public let levels: [Tilemap]
public private(set) var world: World
public private(set) var state: GameState
public private(set) var transition: Effect?
public let font: Font
public var titleText = "TAP TO START"

public init(levels: [Tilemap], font: Font) {
self.state = .title
self.levels = levels
self.world = World(map: levels[0])
self.font = font
}
}

public extension Game {
var hud: HUD {
return HUD(player: world.player, font: font)
}

func save() -> SavedGame {
switch state {
case .playing:
return SavedGame(world: world)
default:
return SavedGame(world: nil)
}
}

mutating func load(_ savedGame: SavedGame) {
guard let world = savedGame.world else {
self.state = .title
return
}
self.state = .playing
self.world = world
}

mutating func update(timeStep: Double, input: Input) {
guard let delegate = delegate else {
return
}

// Update transition
if var effect = transition {
effect.time += timeStep
transition = effect
}

// Update state
switch state {
case .title:
if input.isFiring {
transition = Effect(type: .fadeOut, color: .black, duration: 0.5)
state = .starting
}
case .starting:
if transition?.isCompleted == true {
transition = Effect(type: .fadeIn, color: .black, duration: 0.5)
state = .playing
}
case .playing:
if let action = world.update(timeStep: timeStep, input: input) {
switch action {
case .loadLevel(let index):
let index = index % levels.count
world.setLevel(levels[index])
delegate.clearSounds()
case .playSounds(let sounds):
sounds.forEach(delegate.playSound)
}
}
}
}
}
33 changes: 33 additions & 0 deletions Source/Engine/HUD.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// HUD.swift
// Engine
//
// Created by Nick Lockwood on 19/04/2020.
// Copyright © 2020 Nick Lockwood. All rights reserved.
//

public struct HUD {
public let healthString: String
public let healthTint: Color
public let ammoString: String
public let playerWeapon: Texture
public let weaponIcon: Texture
public let font: Font

public init(player: Player, font: Font) {
let health = Int(max(0, player.health))
switch health {
case ...10:
self.healthTint = .red
case 10 ... 30:
self.healthTint = .yellow
default:
self.healthTint = .green
}
self.healthString = String(health)
self.ammoString = String(Int(max(0, min(99, player.ammo))))
self.playerWeapon = player.animation.texture
self.weaponIcon = player.weapon.attributes.hudIcon
self.font = font
}
}
Loading