Skip to content

Commit

Permalink
Add cost checking for player initiated spawns.
Browse files Browse the repository at this point in the history
  • Loading branch information
zheng-ze committed Mar 23, 2024
1 parent 660648f commit 6b5c66a
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 32 deletions.
4 changes: 4 additions & 0 deletions TowerForge/TowerForge.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
3CE951632BAE037C008B2785 /* AiSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951622BAE037C008B2785 /* AiSystem.swift */; };
3CE951652BAE0A04008B2785 /* HomeSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951642BAE0A04008B2785 /* HomeSystem.swift */; };
3CE951672BAEAB0E008B2785 /* ContactSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951662BAEAB0E008B2785 /* ContactSystem.swift */; };
3CE951692BAEB719008B2785 /* RequestSpawnEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951682BAEB719008B2785 /* RequestSpawnEvent.swift */; };
5200624E2BA8D597000DBA30 /* AiComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5200624D2BA8D597000DBA30 /* AiComponent.swift */; };
520062522BA8DA09000DBA30 /* UnitGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520062512BA8DA09000DBA30 /* UnitGenerator.swift */; };
520062562BA8E026000DBA30 /* PlayerSpawnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520062552BA8E026000DBA30 /* PlayerSpawnable.swift */; };
Expand Down Expand Up @@ -134,6 +135,7 @@
3CE951622BAE037C008B2785 /* AiSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AiSystem.swift; sourceTree = "<group>"; };
3CE951642BAE0A04008B2785 /* HomeSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeSystem.swift; sourceTree = "<group>"; };
3CE951662BAEAB0E008B2785 /* ContactSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactSystem.swift; sourceTree = "<group>"; };
3CE951682BAEB719008B2785 /* RequestSpawnEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestSpawnEvent.swift; sourceTree = "<group>"; };
5200624D2BA8D597000DBA30 /* AiComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AiComponent.swift; sourceTree = "<group>"; };
520062512BA8DA09000DBA30 /* UnitGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitGenerator.swift; sourceTree = "<group>"; };
520062552BA8E026000DBA30 /* PlayerSpawnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSpawnable.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -258,6 +260,7 @@
3C9955CB2BA5889800D33FA5 /* MoveEvent.swift */,
3C9955C72BA5865C00D33FA5 /* ConcurrentEvent.swift */,
BA443D3E2BAD9774009F0FFB /* RemoveEvent.swift */,
3CE951682BAEB719008B2785 /* RequestSpawnEvent.swift */,
);
path = "Implemented Events";
sourceTree = "<group>";
Expand Down Expand Up @@ -705,6 +708,7 @@
BA443D3F2BAD9774009F0FFB /* RemoveEvent.swift in Sources */,
5295A2072BAA02FD005018A8 /* TFButton.swift in Sources */,
3CE951672BAEAB0E008B2785 /* ContactSystem.swift in Sources */,
3CE951692BAEB719008B2785 /* RequestSpawnEvent.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
8 changes: 6 additions & 2 deletions TowerForge/TowerForge/GameWorld.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class GameWorld {
systemManager = SystemManager()
eventManager = EventManager()
selectionNode = UnitSelectionNode()
grid = Grid(entityManager: entityManager, screenSize: screenSize)
grid = Grid(eventManager: eventManager, screenSize: screenSize)
if let scene = self.scene {
grid.generateTileMap(scene: scene)
}
Expand All @@ -42,6 +42,7 @@ class GameWorld {
func spawnUnit(at location: CGPoint) {
selectionNode.unitNodeDidSpawn(location)
}

func setupTeam() {
let ownTeam = Team(player: .ownPlayer)
let oppositeTeam = Team(player: .oppositePlayer)
Expand All @@ -63,9 +64,12 @@ class GameWorld {
systemManager.add(system: RemoveSystem(entityManager: entityManager, eventManager: eventManager))
systemManager.add(system: SpawnSystem(entityManager: entityManager, eventManager: eventManager))
systemManager.add(system: ShootingSystem(entityManager: entityManager, eventManager: eventManager))
systemManager.add(system: HomeSystem(entityManager: entityManager))
systemManager.add(system: HomeSystem(entityManager: entityManager, eventManager: eventManager))
systemManager.add(system: AiSystem(entityManager: entityManager, eventManager: eventManager))
systemManager.add(system: ContactSystem(entityManager: entityManager, eventManager: eventManager))

// Temporary until we have different gamemodes
systemManager.system(ofType: AiSystem.self)?.aiPlayers.append(.oppositePlayer)
}

private func setUpSelectionNode() {
Expand Down
10 changes: 5 additions & 5 deletions TowerForge/TowerForge/Grid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ import SpriteKit
class Grid: UnitSelectionNodeDelegate {
let DEFAULT_NO_OF_ROWS = 8

private var entityManager: EntityManager
private var eventManager: EventManager
private var noOfRows: Int
private var width: CGFloat
private var height: CGFloat

init(entityManager: EntityManager, screenSize: CGRect) {
self.entityManager = entityManager
init(eventManager: EventManager, screenSize: CGRect) {
self.eventManager = eventManager
self.noOfRows = DEFAULT_NO_OF_ROWS
self.width = screenSize.width
self.height = screenSize.height
}

func unitSelectionNodeDidSpawn<T: TFEntity & PlayerSpawnable>(ofType type: T.Type, position: CGPoint) {
let snapPosition = CGPoint(x: position.x, y: snapYPosition(yPosition: position.y))
let unit = UnitGenerator.spawn(ofType: type, at: snapPosition, player: Player.ownPlayer)
entityManager.add(unit)
eventManager.add(RequestSpawnEvent(ofType: type, timestamp: CACurrentMediaTime(),
position: snapPosition, player: .ownPlayer))
}

func generateTileMap(scene: SKScene) {
Expand Down
42 changes: 26 additions & 16 deletions TowerForge/TowerForge/LevelManager/Components/AiComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,38 @@ import Foundation
import UIKit

class AiComponent: TFComponent {
private var chosenUnit: (TFEntity & PlayerSpawnable).Type?
private var spawnableEntities: [(TFEntity & PlayerSpawnable).Type]
private(set) var delayUnitNextSpawn: TimeInterval

override init() {
self.chosenUnit = SpawnableEntities.playerSpawnableEntities.randomElement()
super.init()
var chosenUnit: (TFEntity & PlayerSpawnable).Type? {
spawnableEntities.randomElement()
}

func spawn() -> TFEvent? {
guard let homeComponent = entity?.component(ofType: HomeComponent.self),
let chosenUnit = chosenUnit else {
return nil
}
var spawnLocation: CGPoint {
let randomY = CGFloat.random(in: 0...UIScreen.main.bounds.height)
return CGPoint(x: UIScreen.main.bounds.width, y: randomY)
}

init(spawnableEntities: [(TFEntity & PlayerSpawnable).Type] = SpawnableEntities.playerSpawnableEntities) {
self.spawnableEntities = spawnableEntities
self.delayUnitNextSpawn = AiComponent.getNextInterval()
super.init()
}

override func update(deltaTime: TimeInterval) {
delayUnitNextSpawn -= deltaTime
}

guard homeComponent.points >= chosenUnit.cost else {
return nil
func spawn() -> Bool {
guard delayUnitNextSpawn <= 0 else {
return false
}
homeComponent.decreasePoints(chosenUnit.cost)

// Re-randomize the unit
self.chosenUnit = SpawnableEntities.playerSpawnableEntities.randomElement()
return SpawnEvent(ofType: chosenUnit, timestamp: CACurrentMediaTime(),
position: CGPoint(x: UIScreen.main.bounds.width, y: randomY), player: .oppositePlayer)
delayUnitNextSpawn = AiComponent.getNextInterval()
return true
}

private static func getNextInterval() -> TimeInterval {
Double.random(in: 1...10)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ struct MoveEvent: TFEvent {
return nil
}
movementSystem.handleMovement(for: entityId, with: displacement)
return EventOutput()
return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ struct RemoveEvent: TFEvent {
}

removeSystem.handleRemove(for: entityId)
return EventOutput()
return nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// RequestSpawnEvent.swift
// TowerForge
//
// Created by Zheng Ze on 23/3/24.
//

import Foundation

struct RequestSpawnEvent: TFEvent {
let timestamp: TimeInterval
let position: CGPoint
let entityType: (PlayerSpawnable & TFEntity).Type
let player: Player

var entityId: UUID {
fatalError("entityId is not to be used here")
}

init<T: TFEntity & PlayerSpawnable>(ofType type: T.Type, timestamp: TimeInterval,
position: CGPoint, player: Player) {
self.timestamp = timestamp
self.position = position
self.entityType = type
self.player = player
}

func execute(in target: any EventTarget) -> EventOutput? {
guard let homeSystem = target.system(ofType: HomeSystem.self) else {
return nil
}
homeSystem.attemptSpawn(at: position, ofType: entityType, for: player)
return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ struct SpawnEvent: TFEvent {
return nil
}
spawnSystem.handleSpawn(with: entity)
return EventOutput()
return nil
}
}
35 changes: 30 additions & 5 deletions TowerForge/TowerForge/LevelManager/Systems/AiSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
// Created by Zheng Ze on 23/3/24.
//

import Foundation
import SpriteKit

class AiSystem: TFSystem {
var isActive = true
weak var entityManager: EntityManager?
weak var eventManager: EventManager?
var aiPlayers: [Player] = []

init(entityManager: EntityManager, eventManager: EventManager) {
self.entityManager = entityManager
Expand All @@ -22,12 +23,36 @@ class AiSystem: TFSystem {
return
}

let aiComponents = entityManager.components(ofType: AiComponent.self)
for aiComponent in aiComponents {
guard let event = aiComponent.spawn() else {
var aiComponents: [Player: AiComponent] = [:]
for aiComponent in entityManager.components(ofType: AiComponent.self) {
guard let player = aiComponent.entity?.component(ofType: PlayerComponent.self)?.player else {
continue
}
eventManager.add(event)
aiComponents[player] = aiComponent
aiComponent.update(deltaTime: time)
}

var event: TFEvent?

for aiPlayer in aiPlayers {
guard let aiComponent = aiComponents[aiPlayer], aiComponent.spawn(),
let unitType = aiComponent.chosenUnit else {
continue
}

let newEvent = RequestSpawnEvent(ofType: unitType, timestamp: CACurrentMediaTime(),
position: aiComponent.spawnLocation, player: aiPlayer)

guard var event = event else {
event = newEvent
continue
}
event = event.concurrentlyWith(newEvent)
}

guard let event = event else {
return
}
eventManager.add(event)
}
}
28 changes: 27 additions & 1 deletion TowerForge/TowerForge/LevelManager/Systems/HomeSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
//

import Foundation
import SpriteKit

class HomeSystem: TFSystem {
var isActive = true
weak var entityManager: EntityManager?
weak var eventManager: EventManager?

init(entityManager: EntityManager) {
init(entityManager: EntityManager, eventManager: EventManager) {
self.entityManager = entityManager
self.eventManager = eventManager
}

func update(within time: CGFloat) {
Expand All @@ -26,4 +28,28 @@ class HomeSystem: TFSystem {
homeComponent.update(deltaTime: time)
}
}

func attemptSpawn<T: TFEntity & PlayerSpawnable>(at position: CGPoint, ofType type: T.Type, for player: Player) {
guard let entityManager = entityManager else {
return
}

// Get HomeComponent for the player
let playerHomeComponentArr = entityManager.components(ofType: HomeComponent.self).filter(({
$0.entity?.component(ofType: PlayerComponent.self)?.player == player
}))
guard !playerHomeComponentArr.isEmpty else {
return
}
let playerHomeComponent = playerHomeComponentArr[0]

// Check if they have enough points to spawn
guard playerHomeComponent.points >= type.cost else {
return
}

playerHomeComponent.decreasePoints(type.cost)
let spawnEvent = SpawnEvent(ofType: type, timestamp: CACurrentMediaTime(), position: position, player: player)
eventManager?.add(spawnEvent)
}
}

0 comments on commit 6b5c66a

Please sign in to comment.