From aa77cedd28d829ecc189807a11d64243a86a5575 Mon Sep 17 00:00:00 2001 From: zheng-ze Date: Fri, 22 Mar 2024 23:05:41 +0800 Subject: [PATCH 1/7] Add systems to GameWorld --- TowerForge/TowerForge/GameWorld.swift | 8 ++++++++ .../Events/Implemented Events/DamageEvent.swift | 9 ++++----- .../TowerForge/LevelManager/SystemManager.swift | 16 +++++++++++++++- .../LevelManager/Systems/RemoveSystem.swift | 1 - .../TowerForge/LevelManager/TFEntity.swift | 2 +- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/TowerForge/TowerForge/GameWorld.swift b/TowerForge/TowerForge/GameWorld.swift index d80eb64c..9649b102 100644 --- a/TowerForge/TowerForge/GameWorld.swift +++ b/TowerForge/TowerForge/GameWorld.swift @@ -26,6 +26,7 @@ class GameWorld { grid = Grid(entityManager: entityManager) renderer = Renderer(target: self, scene: scene) + self.setUpSystems() self.setUpSelectionNode() } @@ -81,6 +82,13 @@ class GameWorld { // TODO: Handle any separation logic here. } + private func setUpSystems() { + systemManager.add(system: HealthSystem(entityManager: entityManager, eventManager: eventManager)) + systemManager.add(system: MovementSystem(entityManager: entityManager, eventManager: eventManager)) + systemManager.add(system: RemoveSystem(entityManager: entityManager, eventManager: eventManager)) + systemManager.add(system: SpawnSystem(entityManager: entityManager, eventManager: eventManager)) + } + private func setUpSelectionNode() { selectionNode.delegate = grid scene?.addChild(selectionNode) diff --git a/TowerForge/TowerForge/LevelManager/Events/Implemented Events/DamageEvent.swift b/TowerForge/TowerForge/LevelManager/Events/Implemented Events/DamageEvent.swift index fdb4e0a6..35c31f9e 100644 --- a/TowerForge/TowerForge/LevelManager/Events/Implemented Events/DamageEvent.swift +++ b/TowerForge/TowerForge/LevelManager/Events/Implemented Events/DamageEvent.swift @@ -23,13 +23,12 @@ struct DamageEvent: TFEvent { guard let healthSystem = target.system(ofType: HealthSystem.self) else { return nil } - let healthIsZero = healthSystem.modifyHealth(for: entityId, with: damage) + let healthIsZero = healthSystem.modifyHealth(for: entityId, with: -damage) + var eventOutput = EventOutput() if healthIsZero { - var newEventOutput = EventOutput() - newEventOutput.add(RemoveEvent(on: entityId, at: timestamp)) - return newEventOutput + eventOutput.add(RemoveEvent(on: entityId, at: timestamp)) } - return EventOutput() + return eventOutput } } diff --git a/TowerForge/TowerForge/LevelManager/SystemManager.swift b/TowerForge/TowerForge/LevelManager/SystemManager.swift index ab19cfde..6c413d84 100644 --- a/TowerForge/TowerForge/LevelManager/SystemManager.swift +++ b/TowerForge/TowerForge/LevelManager/SystemManager.swift @@ -8,11 +8,25 @@ import Foundation class SystemManager { + var systems: [String: TFSystem] = [:] func system(ofType type: T.Type) -> T? { - nil + systems[String(describing: T.self)] as? T } func update(_ deltaTime: TimeInterval) { + for system in systems.values where system.isActive { + system.update(within: deltaTime) + } + } + + func add(system: T) { + guard systems[String(describing: T.self)] == nil else { + return + } + systems[String(describing: T.self)] = system + } + func remove(ofType type: T.Type) { + systems.removeValue(forKey: String(describing: type.self)) } } diff --git a/TowerForge/TowerForge/LevelManager/Systems/RemoveSystem.swift b/TowerForge/TowerForge/LevelManager/Systems/RemoveSystem.swift index 8e299b08..17007d1f 100644 --- a/TowerForge/TowerForge/LevelManager/Systems/RemoveSystem.swift +++ b/TowerForge/TowerForge/LevelManager/Systems/RemoveSystem.swift @@ -15,5 +15,4 @@ class RemoveSystem: TFSystem { func handleRemove(for entityId: UUID) { entityManager?.removeEntity(with: entityId) } - } diff --git a/TowerForge/TowerForge/LevelManager/TFEntity.swift b/TowerForge/TowerForge/LevelManager/TFEntity.swift index 4e7bc4f7..6ca64142 100644 --- a/TowerForge/TowerForge/LevelManager/TFEntity.swift +++ b/TowerForge/TowerForge/LevelManager/TFEntity.swift @@ -39,7 +39,7 @@ class TFEntity: Collidable { } func removeComponent(ofType type: T.Type) { - guard let componentToBeRemoved = component(ofType: T.self) else { + guard let componentToBeRemoved = component(ofType: type.self) else { return } componentToBeRemoved.willRemoveFromEntity() From 6cc23b9c6b1d5e75bce6e59baf424a02509d274f Mon Sep 17 00:00:00 2001 From: zheng-ze Date: Fri, 22 Mar 2024 23:07:23 +0800 Subject: [PATCH 2/7] Fix bug where damage is still being done when entities are of the same team. --- TowerForge/TowerForge/Grid.swift | 3 +- .../Entities/Base Entities/BaseTower.swift | 4 +-- .../Entities/Base Entities/BaseUnit.swift | 29 ++----------------- 3 files changed, 6 insertions(+), 30 deletions(-) diff --git a/TowerForge/TowerForge/Grid.swift b/TowerForge/TowerForge/Grid.swift index 8a41d484..043adad1 100644 --- a/TowerForge/TowerForge/Grid.swift +++ b/TowerForge/TowerForge/Grid.swift @@ -20,7 +20,8 @@ class Grid: UnitSelectionNodeDelegate { func unitSelectionNodeDidSpawn(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: entityManager) + let unit = UnitGenerator.spawn(ofType: type, at: snapPosition, player: Player.ownPlayer, + entityManager: entityManager) entityManager.add(unit) } diff --git a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift index d2a13767..a742ca88 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift @@ -34,11 +34,11 @@ class BaseTower: TFEntity { } override func collide(with damageComponent: DamageComponent) -> TFEvent? { - guard self.hasComponent(ofType: HealthComponent.self) else { + guard let healthComponent = self.component(ofType: HealthComponent.self) else { return nil } // No call to super here as super is done on collide with Collidable above. - return DamageEvent(on: self.id, at: Date().timeIntervalSince1970, with: damageComponent.attackPower) + return damageComponent.damage(healthComponent) } private func createHealthComponent(maxHealth: CGFloat, entityManager: EntityManager) { diff --git a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseUnit.swift b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseUnit.swift index 7866cea4..fc9d22ff 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseUnit.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseUnit.swift @@ -7,31 +7,6 @@ import Foundation -enum UnitType { - case melee - case soldier - static let possibleUnits = [melee, soldier] - - var cost: Int { - switch self { - case .melee: - return MeleeUnit.cost - case .soldier: - return SoldierUnit.cost - } - } - - // TODO: A better way to do this - var title: String { - switch self { - case .melee: - return "melee" - case .soldier: - return "soldier" - } - } -} - class BaseUnit: TFEntity { init(textureNames: [String], size: CGSize, @@ -62,11 +37,11 @@ class BaseUnit: TFEntity { } override func collide(with damageComponent: DamageComponent) -> TFEvent? { - guard self.hasComponent(ofType: HealthComponent.self) else { + guard let healthComponent = self.component(ofType: HealthComponent.self) else { return nil } // No call to super here as super is done on collide with Collidable above. - return DamageEvent(on: self.id, at: Date().timeIntervalSince1970, with: damageComponent.attackPower) + return damageComponent.damage(healthComponent) } private func createHealthComponent(maxHealth: CGFloat, entityManager: EntityManager) { From a6d49ceae72985a10a4574c386c1ae49c928b8a1 Mon Sep 17 00:00:00 2001 From: zheng-ze Date: Fri, 22 Mar 2024 23:17:19 +0800 Subject: [PATCH 3/7] Remove entity manager from components and shift updates to systems --- TowerForge/TowerForge/GameWorld.swift | 1 - TowerForge/TowerForge/Grid.swift | 5 +- .../LevelManager/Components/AiComponent.swift | 3 +- .../GameComponents/DamageComponent.swift | 4 +- .../GameComponents/HealthComponent.swift | 15 +-- .../GameComponents/ShootingComponent.swift | 95 +++++++++---------- .../LevelManager/Entities/Arrow.swift | 5 +- .../LevelManager/Entities/ArrowTower.swift | 3 +- .../Entities/Base Entities/BaseTower.swift | 9 +- .../Entities/Base Entities/BaseUnit.swift | 9 +- .../LevelManager/Entities/MeleeUnit.swift | 6 +- .../LevelManager/Entities/SoldierUnit.swift | 9 +- .../LevelManager/EntityManager.swift | 9 -- .../Generators/UnitGenerator.swift | 5 +- .../LevelManager/Systems/HealthSystem.swift | 2 +- .../LevelManager/Systems/MovementSystem.swift | 2 +- .../LevelManager/Systems/RemoveSystem.swift | 2 +- .../LevelManager/Systems/SpawnSystem.swift | 2 +- .../TowerForge/Protocols/Spawnable.swift | 2 +- 19 files changed, 75 insertions(+), 113 deletions(-) diff --git a/TowerForge/TowerForge/GameWorld.swift b/TowerForge/TowerForge/GameWorld.swift index 9649b102..b63f3c8b 100644 --- a/TowerForge/TowerForge/GameWorld.swift +++ b/TowerForge/TowerForge/GameWorld.swift @@ -37,7 +37,6 @@ class GameWorld { systemManager.update(deltaTime) eventManager.executeEvents(in: self) - entityManager.update(deltaTime) renderer?.render() } diff --git a/TowerForge/TowerForge/Grid.swift b/TowerForge/TowerForge/Grid.swift index 043adad1..b82c36af 100644 --- a/TowerForge/TowerForge/Grid.swift +++ b/TowerForge/TowerForge/Grid.swift @@ -18,10 +18,9 @@ class Grid: UnitSelectionNodeDelegate { self.noOfRows = DEFAULT_NO_OF_ROWS } - func unitSelectionNodeDidSpawn(ofType type: T.Type, position: CGPoint) { + func unitSelectionNodeDidSpawn(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: entityManager) + let unit = UnitGenerator.spawn(ofType: type, at: snapPosition, player: Player.ownPlayer) entityManager.add(unit) } diff --git a/TowerForge/TowerForge/LevelManager/Components/AiComponent.swift b/TowerForge/TowerForge/LevelManager/Components/AiComponent.swift index 0f33ee96..89d29d77 100644 --- a/TowerForge/TowerForge/LevelManager/Components/AiComponent.swift +++ b/TowerForge/TowerForge/LevelManager/Components/AiComponent.swift @@ -20,8 +20,7 @@ class AiComponent: TFComponent { guard let homeComponent = entity?.component(ofType: HomeComponent.self), let chosenUnit = chosenUnit else { return } - let unit = UnitGenerator.spawn(ofType: chosenUnit, at: CGPoint(x: 0, y: 10), player: .oppositePlayer, - entityManager: entityManager) + let unit = UnitGenerator.spawn(ofType: chosenUnit, at: CGPoint(x: 0, y: 10), player: .oppositePlayer) entityManager.add(unit) } } diff --git a/TowerForge/TowerForge/LevelManager/Components/GameComponents/DamageComponent.swift b/TowerForge/TowerForge/LevelManager/Components/GameComponents/DamageComponent.swift index cc5ac189..9edd8f56 100644 --- a/TowerForge/TowerForge/LevelManager/Components/GameComponents/DamageComponent.swift +++ b/TowerForge/TowerForge/LevelManager/Components/GameComponents/DamageComponent.swift @@ -12,14 +12,12 @@ import SpriteKit class DamageComponent: TFComponent { private let attackRate: TimeInterval private var lastAttackTime = TimeInterval(0) - private let entityManager: EntityManager private let temporary: Bool let attackPower: CGFloat - init(attackRate: TimeInterval, attackPower: CGFloat, temporary: Bool, entityManager: EntityManager) { + init(attackRate: TimeInterval, attackPower: CGFloat, temporary: Bool) { self.attackRate = attackRate self.attackPower = attackPower - self.entityManager = entityManager self.temporary = temporary super.init() } diff --git a/TowerForge/TowerForge/LevelManager/Components/GameComponents/HealthComponent.swift b/TowerForge/TowerForge/LevelManager/Components/GameComponents/HealthComponent.swift index 614af0ce..355e9d71 100644 --- a/TowerForge/TowerForge/LevelManager/Components/GameComponents/HealthComponent.swift +++ b/TowerForge/TowerForge/LevelManager/Components/GameComponents/HealthComponent.swift @@ -10,29 +10,16 @@ import Foundation class HealthComponent: TFComponent { var currentHealth: CGFloat var maxHealth: CGFloat - private let entityManager: EntityManager var isZero: Bool { currentHealth.isZero } - init(maxHealth: CGFloat, entityManager: EntityManager) { + init(maxHealth: CGFloat) { self.currentHealth = maxHealth self.maxHealth = maxHealth - self.entityManager = entityManager super.init() } - override func update(deltaTime: TimeInterval) { - if self.currentHealth <= 0 { - guard let entity = entity, - let spriteComponent = entity.component(ofType: SpriteComponent.self) else { - return - } - // Need to remove the entity entirely if the health goes to zero or less - self.entityManager.removeEntity(with: entity.id) - } - } - /// Adjusts health by the specified amount. func adjustHealth(amount: CGFloat) { if amount < 0 { diff --git a/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift b/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift index a3440b98..375f176a 100644 --- a/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift +++ b/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift @@ -12,61 +12,58 @@ class ShootingComponent: TFComponent { var fireRate: TimeInterval // Delay between shots var range: CGFloat private var lastShotTime = TimeInterval(0) - private let entityManager: EntityManager let attackPower: CGFloat - init(fireRate: TimeInterval, range: CGFloat, entityManager: EntityManager, attackPower: CGFloat) { + init(fireRate: TimeInterval, range: CGFloat, attackPower: CGFloat) { self.fireRate = fireRate self.range = range - self.entityManager = entityManager self.attackPower = attackPower super.init() } - override func update(deltaTime: TimeInterval) { - super.update(deltaTime: deltaTime) - - // Required components for the current Melee - guard let entity = entity, - let spriteComponent = entity.component(ofType: SpriteComponent.self), - let positionComponent = entity.component(ofType: PositionComponent.self) else { - return - } - - // Loop opposite team's entities - for entity in entityManager.entities { - guard let playerComponent = entity.component(ofType: PlayerComponent.self) else { - return - } - if playerComponent.player == .ownPlayer { - return - } - // Get opposite team's components - guard let oppositeSpriteComponent = entity.component(ofType: SpriteComponent.self), - let oppositeHealthComponent = entity.component(ofType: HealthComponent.self), - let oppositePositionComponent = entity.component(ofType: PositionComponent.self) else { - return - } - - // Get the horizontal distance - let distanceBetween = (positionComponent.position.x - oppositePositionComponent.position.x) - - // Check if within range - if distanceBetween < range { - // TODO : Change the hard coded velocity value - let arrow = Arrow(position: positionComponent.position, - velocity: playerComponent.player.getDirectionVelocity(), - attackRate: 1.0, - entityManager: entityManager) - guard let arrowSpriteComponent = arrow.component(ofType: SpriteComponent.self) else { - return - } - - // Check if can attack - if CACurrentMediaTime() - lastShotTime > fireRate { - lastShotTime = CACurrentMediaTime() - } - } - } - } +// override func update(deltaTime: TimeInterval) { +// super.update(deltaTime: deltaTime) +// +// // Required components for the current Melee +// guard let entity = entity, +// let spriteComponent = entity.component(ofType: SpriteComponent.self), +// let positionComponent = entity.component(ofType: PositionComponent.self) else { +// return +// } +// +// // Loop opposite team's entities +// for entity in entityManager.entities { +// guard let playerComponent = entity.component(ofType: PlayerComponent.self) else { +// return +// } +// if playerComponent.player == .ownPlayer { +// return +// } +// // Get opposite team's components +// guard let oppositeSpriteComponent = entity.component(ofType: SpriteComponent.self), +// let oppositeHealthComponent = entity.component(ofType: HealthComponent.self), +// let oppositePositionComponent = entity.component(ofType: PositionComponent.self) else { +// return +// } +// +// // Get the horizontal distance +// let distanceBetween = (positionComponent.position.x - oppositePositionComponent.position.x) +// +// // Check if within range +// if distanceBetween < range { +// // TODO : Change the hard coded velocity value +// let arrow = Arrow(position: positionComponent.position, +// velocity: playerComponent.player.getDirectionVelocity(), +// attackRate: 1.0, +// entityManager: entityManager) +// guard let arrowSpriteComponent = arrow.component(ofType: SpriteComponent.self) else { +// return +// } +// +// // Check if can attack +// if CACurrentMediaTime() - lastShotTime > fireRate { +// lastShotTime = CACurrentMediaTime() +// } +// } +// } } diff --git a/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift b/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift index 05b8e3b4..6fdad36a 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift @@ -13,7 +13,7 @@ class Arrow: BaseProjectile { static let key = "arrow" static let damage = 5.0 - init(position: CGPoint, velocity: CGVector, attackRate: TimeInterval, entityManager: EntityManager) { + init(position: CGPoint, velocity: CGVector, attackRate: TimeInterval) { super.init(textureNames: Arrow.textureNames, size: Arrow.size, key: Arrow.key, @@ -21,8 +21,7 @@ class Arrow: BaseProjectile { velocity: velocity) self.addComponent(DamageComponent(attackRate: attackRate, attackPower: Arrow.damage, - temporary: true, - entityManager: entityManager)) + temporary: true)) } override func collide(with other: any Collidable) -> TFEvent? { diff --git a/TowerForge/TowerForge/LevelManager/Entities/ArrowTower.swift b/TowerForge/TowerForge/LevelManager/Entities/ArrowTower.swift index e96bb595..99bb73a7 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/ArrowTower.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/ArrowTower.swift @@ -20,10 +20,9 @@ class ArrowTower: BaseTower { size: ArrowTower.size, key: ArrowTower.key, position: position, - maxHealth: ArrowTower.maxHealth, entityManager: entityManager) + maxHealth: ArrowTower.maxHealth) self.addComponent(ShootingComponent(fireRate: ArrowTower.fireRate, range: 1.0, - entityManager: entityManager, attackPower: ArrowTower.damage)) } } diff --git a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift index a742ca88..bd05a6b9 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift @@ -12,11 +12,10 @@ class BaseTower: TFEntity { size: CGSize, key: String, position: CGPoint, - maxHealth: CGFloat, - entityManager: EntityManager) { + maxHealth: CGFloat) { super.init() - createHealthComponent(maxHealth: maxHealth, entityManager: entityManager) + createHealthComponent(maxHealth: maxHealth) createSpriteComponent(textureNames: textureNames, size: size, key: key, position: position) createPositionComponent(position: position) } @@ -41,8 +40,8 @@ class BaseTower: TFEntity { return damageComponent.damage(healthComponent) } - private func createHealthComponent(maxHealth: CGFloat, entityManager: EntityManager) { - let healthComponent = HealthComponent(maxHealth: maxHealth, entityManager: entityManager) + private func createHealthComponent(maxHealth: CGFloat) { + let healthComponent = HealthComponent(maxHealth: maxHealth) self.addComponent(healthComponent) } private func createPositionComponent(position: CGPoint) { diff --git a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseUnit.swift b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseUnit.swift index fc9d22ff..b7bb2290 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseUnit.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseUnit.swift @@ -13,11 +13,10 @@ class BaseUnit: TFEntity { key: String, position: CGPoint, maxHealth: CGFloat, - entityManager: EntityManager, velocity: CGVector, team: Team) { super.init() - createHealthComponent(maxHealth: maxHealth, entityManager: entityManager) + createHealthComponent(maxHealth: maxHealth) createSpriteComponent(textureNames: textureNames, size: size, key: key, position: position) createMovableComponent(position: position, velocity: velocity) createPositionComponent(position: position) @@ -44,10 +43,11 @@ class BaseUnit: TFEntity { return damageComponent.damage(healthComponent) } - private func createHealthComponent(maxHealth: CGFloat, entityManager: EntityManager) { - let healthComponent = HealthComponent(maxHealth: maxHealth, entityManager: entityManager) + private func createHealthComponent(maxHealth: CGFloat) { + let healthComponent = HealthComponent(maxHealth: maxHealth) self.addComponent(healthComponent) } + private func createPositionComponent(position: CGPoint) { let positionComponent = PositionComponent(position: position) self.addComponent(positionComponent) @@ -66,6 +66,7 @@ class BaseUnit: TFEntity { let movableComponent = MovableComponent(position: position, velocity: velocity) self.addComponent(movableComponent) } + private func createPlayerComponent(team: Team) { let playerComponent = PlayerComponent(player: team.player) self.addComponent(playerComponent) diff --git a/TowerForge/TowerForge/LevelManager/Entities/MeleeUnit.swift b/TowerForge/TowerForge/LevelManager/Entities/MeleeUnit.swift index 5e0f60a8..3318514e 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/MeleeUnit.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/MeleeUnit.swift @@ -18,19 +18,17 @@ class MeleeUnit: BaseUnit, Spawnable { static let attackRate = 1.0 static let velocity = CGVector(dx: 10.0, dy: 0.0) - required init(position: CGPoint, entityManager: EntityManager, team: Team) { + required init(position: CGPoint, team: Team) { super.init(textureNames: MeleeUnit.textureNames, size: MeleeUnit.size, key: MeleeUnit.key, position: position, maxHealth: MeleeUnit.maxHealth, - entityManager: entityManager, velocity: MeleeUnit.velocity, team: team) self.addComponent(DamageComponent(attackRate: MeleeUnit.attackRate, attackPower: MeleeUnit.damage, - temporary: false, - entityManager: entityManager)) + temporary: false)) } override func collide(with other: any Collidable) -> (any TFEvent)? { diff --git a/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift b/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift index 451c6404..f758319c 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift @@ -16,22 +16,19 @@ class SoldierUnit: BaseUnit, Spawnable { static let damage = 10.0 static var cost = 5 static let attackRate = 10.0 - static let velocity = CGVector(dx: 10.0, dy: 0.0) + static let velocity = CGVector(dx: -10.0, dy: 0.0) - required init(position: CGPoint, entityManager: EntityManager, team: Team) { + required init(position: CGPoint, team: Team) { super.init(textureNames: SoldierUnit.textureNames, size: SoldierUnit.size, key: SoldierUnit.key, position: position, maxHealth: SoldierUnit.maxHealth, - entityManager: entityManager, velocity: SoldierUnit.velocity, team: team) self.addComponent(ShootingComponent(fireRate: SoldierUnit.attackRate, range: 1.0, - entityManager: entityManager, - attackPower: 10.0 - )) + attackPower: 10.0)) } } diff --git a/TowerForge/TowerForge/LevelManager/EntityManager.swift b/TowerForge/TowerForge/LevelManager/EntityManager.swift index a5ba14b9..7c6cff99 100644 --- a/TowerForge/TowerForge/LevelManager/EntityManager.swift +++ b/TowerForge/TowerForge/LevelManager/EntityManager.swift @@ -41,13 +41,4 @@ class EntityManager { func components(ofType type: T.Type) -> [T] { entities.compactMap { $0.component(ofType: type) } } - - // TODO update to be changed to systems - func update(_ deltaTime: TimeInterval) { - for entity in entities { - for component in entity.components.values { - component.update(deltaTime: deltaTime) - } - } - } } diff --git a/TowerForge/TowerForge/LevelManager/Generators/UnitGenerator.swift b/TowerForge/TowerForge/LevelManager/Generators/UnitGenerator.swift index e3239257..1ee190d0 100644 --- a/TowerForge/TowerForge/LevelManager/Generators/UnitGenerator.swift +++ b/TowerForge/TowerForge/LevelManager/Generators/UnitGenerator.swift @@ -9,9 +9,8 @@ import Foundation import SpriteKit class UnitGenerator { - static func spawn(ofType type: T.Type, at position: CGPoint, - player: Player, entityManager: EntityManager) -> T { - let unit = type.init(position: position, entityManager: entityManager, team: Team(player: player)) + static func spawn(ofType type: T.Type, at position: CGPoint, player: Player) -> T { + let unit = type.init(position: position, team: Team(player: player)) return unit } } diff --git a/TowerForge/TowerForge/LevelManager/Systems/HealthSystem.swift b/TowerForge/TowerForge/LevelManager/Systems/HealthSystem.swift index ec9df187..73913223 100644 --- a/TowerForge/TowerForge/LevelManager/Systems/HealthSystem.swift +++ b/TowerForge/TowerForge/LevelManager/Systems/HealthSystem.swift @@ -1,7 +1,7 @@ import Foundation class HealthSystem: TFSystem { - var isActive = false + var isActive = true weak var entityManager: EntityManager? weak var eventManager: EventManager? diff --git a/TowerForge/TowerForge/LevelManager/Systems/MovementSystem.swift b/TowerForge/TowerForge/LevelManager/Systems/MovementSystem.swift index 3de4ab49..424eb340 100644 --- a/TowerForge/TowerForge/LevelManager/Systems/MovementSystem.swift +++ b/TowerForge/TowerForge/LevelManager/Systems/MovementSystem.swift @@ -1,7 +1,7 @@ import Foundation class MovementSystem: TFSystem { - var isActive = false + var isActive = true weak var entityManager: EntityManager? weak var eventManager: EventManager? diff --git a/TowerForge/TowerForge/LevelManager/Systems/RemoveSystem.swift b/TowerForge/TowerForge/LevelManager/Systems/RemoveSystem.swift index 17007d1f..c8aac08f 100644 --- a/TowerForge/TowerForge/LevelManager/Systems/RemoveSystem.swift +++ b/TowerForge/TowerForge/LevelManager/Systems/RemoveSystem.swift @@ -1,7 +1,7 @@ import Foundation class RemoveSystem: TFSystem { - var isActive = false + var isActive = true weak var entityManager: EntityManager? weak var eventManager: EventManager? diff --git a/TowerForge/TowerForge/LevelManager/Systems/SpawnSystem.swift b/TowerForge/TowerForge/LevelManager/Systems/SpawnSystem.swift index 6f2a4810..c535b4f7 100644 --- a/TowerForge/TowerForge/LevelManager/Systems/SpawnSystem.swift +++ b/TowerForge/TowerForge/LevelManager/Systems/SpawnSystem.swift @@ -1,7 +1,7 @@ import Foundation class SpawnSystem: TFSystem { - var isActive = false + var isActive = true weak var entityManager: EntityManager? weak var eventManager: EventManager? diff --git a/TowerForge/TowerForge/Protocols/Spawnable.swift b/TowerForge/TowerForge/Protocols/Spawnable.swift index 3b570f3c..c9aca28d 100644 --- a/TowerForge/TowerForge/Protocols/Spawnable.swift +++ b/TowerForge/TowerForge/Protocols/Spawnable.swift @@ -8,7 +8,7 @@ import Foundation protocol Spawnable { - init(position: CGPoint, entityManager: EntityManager, team: Team) + init(position: CGPoint, team: Team) static var cost: Int { get set } static var title: String { get } } From ee73ef374b5d9476b0107ca43f8d3df17e3651d2 Mon Sep 17 00:00:00 2001 From: zheng-ze Date: Sat, 23 Mar 2024 00:58:09 +0800 Subject: [PATCH 4/7] Change existing Spawnable to PlayerSpawnable. Add new Spawnable Protocol, --- .../LevelManager/Components/AiComponent.swift | 4 ++-- .../LevelManager/Entities/MeleeUnit.swift | 2 +- .../LevelManager/Entities/SoldierUnit.swift | 14 ++++++++------ .../LevelManager/Entities/SpawnableEntities.swift | 3 +-- .../LevelManager/Systems/SpawnSystem.swift | 1 - TowerForge/TowerForge/Nodes/UnitNode.swift | 4 ++-- .../TowerForge/Nodes/UnitSelectionNode.swift | 4 ++-- .../TowerForge/Protocols/PlayerSpawnable.swift | 13 +++++++++++++ TowerForge/TowerForge/Protocols/Spawnable.swift | 4 +--- 9 files changed, 30 insertions(+), 19 deletions(-) create mode 100644 TowerForge/TowerForge/Protocols/PlayerSpawnable.swift diff --git a/TowerForge/TowerForge/LevelManager/Components/AiComponent.swift b/TowerForge/TowerForge/LevelManager/Components/AiComponent.swift index 89d29d77..edbc0838 100644 --- a/TowerForge/TowerForge/LevelManager/Components/AiComponent.swift +++ b/TowerForge/TowerForge/LevelManager/Components/AiComponent.swift @@ -9,10 +9,10 @@ import Foundation class AiComponent: TFComponent { private var entityManager: EntityManager - private var chosenUnit: (BaseUnit & Spawnable).Type? + private var chosenUnit: (TFEntity & PlayerSpawnable).Type? init(entityManager: EntityManager) { self.entityManager = entityManager - self.chosenUnit = SpawnableEntities.possibleUnits.randomElement() + self.chosenUnit = SpawnableEntities.playerSpawnableEntities.randomElement() super.init() } diff --git a/TowerForge/TowerForge/LevelManager/Entities/MeleeUnit.swift b/TowerForge/TowerForge/LevelManager/Entities/MeleeUnit.swift index 3318514e..43a97a59 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/MeleeUnit.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/MeleeUnit.swift @@ -7,7 +7,7 @@ import Foundation -class MeleeUnit: BaseUnit, Spawnable { +class MeleeUnit: BaseUnit, PlayerSpawnable { static let title: String = "melee" static let textureNames = ["melee-1", "melee-2"] static let size = CGSize(width: 100, height: 100) diff --git a/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift b/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift index f758319c..08c226ae 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift @@ -7,7 +7,7 @@ import Foundation -class SoldierUnit: BaseUnit, Spawnable { +class SoldierUnit: BaseUnit, PlayerSpawnable { static let title: String = "soldier" static let textureNames = ["Shooter-1", "Shooter-2"] static let size = CGSize(width: 100, height: 100) @@ -15,8 +15,10 @@ class SoldierUnit: BaseUnit, Spawnable { static let maxHealth = 100.0 static let damage = 10.0 static var cost = 5 - static let attackRate = 10.0 - static let velocity = CGVector(dx: -10.0, dy: 0.0) + static let attackRate = 1.0 + static let velocity = CGVector(dx: 10.0, dy: 0.0) + static let range = 400.0 + static let attackPower = 10.0 required init(position: CGPoint, team: Team) { super.init(textureNames: SoldierUnit.textureNames, @@ -25,10 +27,10 @@ class SoldierUnit: BaseUnit, Spawnable { position: position, maxHealth: SoldierUnit.maxHealth, velocity: SoldierUnit.velocity, - team: team) + team: Team(player: .oppositePlayer)) self.addComponent(ShootingComponent(fireRate: SoldierUnit.attackRate, - range: 1.0, - attackPower: 10.0)) + range: SoldierUnit.range, + attackPower: SoldierUnit.attackPower)) } } diff --git a/TowerForge/TowerForge/LevelManager/Entities/SpawnableEntities.swift b/TowerForge/TowerForge/LevelManager/Entities/SpawnableEntities.swift index 05e3f77b..064dbca9 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/SpawnableEntities.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/SpawnableEntities.swift @@ -8,6 +8,5 @@ import Foundation struct SpawnableEntities { - static let possibleUnits: [(BaseUnit & Spawnable).Type] = [MeleeUnit.self, - SoldierUnit.self] + static let playerSpawnableEntities: [(TFEntity & PlayerSpawnable).Type] = [MeleeUnit.self, SoldierUnit.self] } diff --git a/TowerForge/TowerForge/LevelManager/Systems/SpawnSystem.swift b/TowerForge/TowerForge/LevelManager/Systems/SpawnSystem.swift index c535b4f7..aade6440 100644 --- a/TowerForge/TowerForge/LevelManager/Systems/SpawnSystem.swift +++ b/TowerForge/TowerForge/LevelManager/Systems/SpawnSystem.swift @@ -15,5 +15,4 @@ class SpawnSystem: TFSystem { func handleSpawn(with entity: TFEntity) { entityManager?.add(entity) } - } diff --git a/TowerForge/TowerForge/Nodes/UnitNode.swift b/TowerForge/TowerForge/Nodes/UnitNode.swift index 92b3cdcd..83375004 100644 --- a/TowerForge/TowerForge/Nodes/UnitNode.swift +++ b/TowerForge/TowerForge/Nodes/UnitNode.swift @@ -13,7 +13,7 @@ protocol UnitNodeDelegate: AnyObject { } class UnitNode: TFSpriteNode { - let type: (BaseUnit & Spawnable).Type + let type: (TFEntity & PlayerSpawnable).Type weak var delegate: UnitNodeDelegate? var purchasable = true var teamController: TeamController? @@ -21,7 +21,7 @@ class UnitNode: TFSpriteNode { var unitCostLabel: SKLabelNode! var backgroundNode: SKSpriteNode! - init(ofType type: T.Type) { + init(ofType type: T.Type) { self.type = type super.init(imageName: type.title, height: 200.0, width: 140.0) diff --git a/TowerForge/TowerForge/Nodes/UnitSelectionNode.swift b/TowerForge/TowerForge/Nodes/UnitSelectionNode.swift index 04e85e31..47a1d486 100644 --- a/TowerForge/TowerForge/Nodes/UnitSelectionNode.swift +++ b/TowerForge/TowerForge/Nodes/UnitSelectionNode.swift @@ -9,7 +9,7 @@ import Foundation import UIKit protocol UnitSelectionNodeDelegate: AnyObject { - func unitSelectionNodeDidSpawn(ofType type: T.Type, position: CGPoint) + func unitSelectionNodeDidSpawn(ofType type: T.Type, position: CGPoint) } class UnitSelectionNode: TFSpriteNode, UnitNodeDelegate { @@ -26,7 +26,7 @@ class UnitSelectionNode: TFSpriteNode, UnitNodeDelegate { super.init(textures: nil, height: 200.0, width: 100.0) isUserInteractionEnabled = true - let possibleUnits: [(BaseUnit & Spawnable).Type] = SpawnableEntities.possibleUnits + let possibleUnits: [(TFEntity & PlayerSpawnable).Type] = SpawnableEntities.playerSpawnableEntities for type in possibleUnits { let unitNode = UnitNode(ofType: type) unitNodes.append(unitNode) diff --git a/TowerForge/TowerForge/Protocols/PlayerSpawnable.swift b/TowerForge/TowerForge/Protocols/PlayerSpawnable.swift new file mode 100644 index 00000000..3ba891a6 --- /dev/null +++ b/TowerForge/TowerForge/Protocols/PlayerSpawnable.swift @@ -0,0 +1,13 @@ +// +// Spawnable.swift +// TowerForge +// +// Created by Vanessa Mae on 19/03/24. +// + +import Foundation + +protocol PlayerSpawnable: Spawnable { + static var cost: Int { get set } + static var title: String { get } +} diff --git a/TowerForge/TowerForge/Protocols/Spawnable.swift b/TowerForge/TowerForge/Protocols/Spawnable.swift index c9aca28d..c402e665 100644 --- a/TowerForge/TowerForge/Protocols/Spawnable.swift +++ b/TowerForge/TowerForge/Protocols/Spawnable.swift @@ -2,13 +2,11 @@ // Spawnable.swift // TowerForge // -// Created by Vanessa Mae on 19/03/24. +// Created by Zheng Ze on 23/3/24. // import Foundation protocol Spawnable { init(position: CGPoint, team: Team) - static var cost: Int { get set } - static var title: String { get } } From ff1351316644cad82a634b95bb58796e65584ab5 Mon Sep 17 00:00:00 2001 From: zheng-ze Date: Sat, 23 Mar 2024 00:58:40 +0800 Subject: [PATCH 5/7] Update SpawnEvent to use new Spawnable Protocol --- .../Events/Implemented Events/SpawnEvent.swift | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/TowerForge/TowerForge/LevelManager/Events/Implemented Events/SpawnEvent.swift b/TowerForge/TowerForge/LevelManager/Events/Implemented Events/SpawnEvent.swift index e4ada9a6..dad7730c 100644 --- a/TowerForge/TowerForge/LevelManager/Events/Implemented Events/SpawnEvent.swift +++ b/TowerForge/TowerForge/LevelManager/Events/Implemented Events/SpawnEvent.swift @@ -3,23 +3,18 @@ import Foundation struct SpawnEvent: TFEvent { let timestamp: TimeInterval let entityId: UUID + let entity: TFEntity - private let position: CGPoint - private let velocity: CGVector - - init(timestamp: TimeInterval, entityId: UUID, position: CGPoint, velocity: CGVector) { + init(ofType type: T.Type, timestamp: TimeInterval, position: CGPoint, team: Team) { + self.entity = type.init(position: position, team: team) self.timestamp = timestamp - self.entityId = entityId - self.position = position - self.velocity = velocity + self.entityId = entity.id } func execute(in target: any EventTarget) -> EventOutput? { guard let spawnSystem = target.system(ofType: SpawnSystem.self) else { return nil } - var entity = TFEntity() - entity.addComponent(MovableComponent(position: position, velocity: velocity)) spawnSystem.handleSpawn(with: entity) return EventOutput() } From fd0251f8a8f2b5cf1635bbba6439ac81268781f1 Mon Sep 17 00:00:00 2001 From: zheng-ze Date: Sat, 23 Mar 2024 01:00:47 +0800 Subject: [PATCH 6/7] Add ShootingSystem to update ShootingComponents. Update ShootingComponent to use new SpawnEvent. --- .../TowerForge.xcodeproj/project.pbxproj | 16 +++-- TowerForge/TowerForge/GameWorld.swift | 1 + .../GameComponents/DamageComponent.swift | 12 ++-- .../GameComponents/ShootingComponent.swift | 69 +++++++------------ .../LevelManager/Entities/Arrow.swift | 12 ++-- .../LevelManager/Entities/ArrowTower.swift | 9 ++- .../Base Entities/BaseProjectile.swift | 8 ++- .../Entities/Base Entities/BaseTower.swift | 9 ++- .../LevelManager/Entities/SoldierUnit.swift | 2 +- .../LevelManager/Events/EventManager.swift | 2 +- .../LevelManager/Systems/ShootingSystem.swift | 40 +++++++++++ 11 files changed, 116 insertions(+), 64 deletions(-) create mode 100644 TowerForge/TowerForge/LevelManager/Systems/ShootingSystem.swift diff --git a/TowerForge/TowerForge.xcodeproj/project.pbxproj b/TowerForge/TowerForge.xcodeproj/project.pbxproj index c541f042..d4b2925b 100644 --- a/TowerForge/TowerForge.xcodeproj/project.pbxproj +++ b/TowerForge/TowerForge.xcodeproj/project.pbxproj @@ -34,9 +34,11 @@ 3CE951512BAC8955008B2785 /* Renderable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951502BAC8955008B2785 /* Renderable.swift */; }; 3CE951562BACA0CF008B2785 /* Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951552BACA0CF008B2785 /* Collidable.swift */; }; 3CE951582BAD724D008B2785 /* TFContact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951572BAD724D008B2785 /* TFContact.swift */; }; + 3CE9515F2BADE2C5008B2785 /* ShootingSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE9515E2BADE2C5008B2785 /* ShootingSystem.swift */; }; + 3CE951612BADE881008B2785 /* Spawnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951602BADE881008B2785 /* Spawnable.swift */; }; 5200624E2BA8D597000DBA30 /* AiComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5200624D2BA8D597000DBA30 /* AiComponent.swift */; }; 520062522BA8DA09000DBA30 /* UnitGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520062512BA8DA09000DBA30 /* UnitGenerator.swift */; }; - 520062562BA8E026000DBA30 /* Spawnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520062552BA8E026000DBA30 /* Spawnable.swift */; }; + 520062562BA8E026000DBA30 /* PlayerSpawnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520062552BA8E026000DBA30 /* PlayerSpawnable.swift */; }; 520062582BA8ED73000DBA30 /* HomeComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520062572BA8ED73000DBA30 /* HomeComponent.swift */; }; 52578B822BA61AAF00B4D76C /* PositionComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52578B812BA61AAF00B4D76C /* PositionComponent.swift */; }; 52578B872BA6209700B4D76C /* DamageComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52578B862BA6209700B4D76C /* DamageComponent.swift */; }; @@ -123,9 +125,11 @@ 3CE951502BAC8955008B2785 /* Renderable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Renderable.swift; sourceTree = ""; }; 3CE951552BACA0CF008B2785 /* Collidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Collidable.swift; sourceTree = ""; }; 3CE951572BAD724D008B2785 /* TFContact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFContact.swift; sourceTree = ""; }; + 3CE9515E2BADE2C5008B2785 /* ShootingSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShootingSystem.swift; sourceTree = ""; }; + 3CE951602BADE881008B2785 /* Spawnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Spawnable.swift; sourceTree = ""; }; 5200624D2BA8D597000DBA30 /* AiComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AiComponent.swift; sourceTree = ""; }; 520062512BA8DA09000DBA30 /* UnitGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitGenerator.swift; sourceTree = ""; }; - 520062552BA8E026000DBA30 /* Spawnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Spawnable.swift; sourceTree = ""; }; + 520062552BA8E026000DBA30 /* PlayerSpawnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSpawnable.swift; sourceTree = ""; }; 520062572BA8ED73000DBA30 /* HomeComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeComponent.swift; sourceTree = ""; }; 52578B812BA61AAF00B4D76C /* PositionComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionComponent.swift; sourceTree = ""; }; 52578B862BA6209700B4D76C /* DamageComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamageComponent.swift; sourceTree = ""; }; @@ -230,6 +234,7 @@ 3C769A712BA58DE700F454F9 /* MovementSystem.swift */, 3C769A732BA591BD00F454F9 /* SpawnSystem.swift */, BA443D3C2BAD9557009F0FFB /* RemoveSystem.swift */, + 3CE9515E2BADE2C5008B2785 /* ShootingSystem.swift */, ); path = Systems; sourceTree = ""; @@ -432,7 +437,8 @@ isa = PBXGroup; children = ( 52DF5FE82BA33F9700135367 /* Animatable.swift */, - 520062552BA8E026000DBA30 /* Spawnable.swift */, + 520062552BA8E026000DBA30 /* PlayerSpawnable.swift */, + 3CE951602BADE881008B2785 /* Spawnable.swift */, ); path = Protocols; sourceTree = ""; @@ -638,6 +644,7 @@ 52578B822BA61AAF00B4D76C /* PositionComponent.swift in Sources */, 3C9955A32BA47DBB00D33FA5 /* BaseUnit.swift in Sources */, 3C9955BA2BA5637200D33FA5 /* DamageEvent.swift in Sources */, + 3CE9515F2BADE2C5008B2785 /* ShootingSystem.swift in Sources */, 3C9955CA2BA5888F00D33FA5 /* SpawnEvent.swift in Sources */, 3CE951582BAD724D008B2785 /* TFContact.swift in Sources */, 5295A2132BAAEA16005018A8 /* UnitNode.swift in Sources */, @@ -660,6 +667,7 @@ 3CE951562BACA0CF008B2785 /* Collidable.swift in Sources */, 52DF5FE62BA33AF300135367 /* TFSpriteNode.swift in Sources */, 3C9955B42BA4B12000D33FA5 /* ArrowTower.swift in Sources */, + 3CE951612BADE881008B2785 /* Spawnable.swift in Sources */, 52DF5FE92BA33F9700135367 /* Animatable.swift in Sources */, 3C9955AF2BA48FD200D33FA5 /* MeleeUnit.swift in Sources */, 5295A2022BA9FBD9005018A8 /* SceneManagerDelegate.swift in Sources */, @@ -676,7 +684,7 @@ 52DF5FED2BA34D0300135367 /* TFComponent.swift in Sources */, 527E3A242BA613F000FE1628 /* PlayerComponent.swift in Sources */, 3C9955C52BA585DD00D33FA5 /* HealthSystem.swift in Sources */, - 520062562BA8E026000DBA30 /* Spawnable.swift in Sources */, + 520062562BA8E026000DBA30 /* PlayerSpawnable.swift in Sources */, 52DF5FFB2BA3601400135367 /* HealthComponent.swift in Sources */, 3C9955BC2BA563A800D33FA5 /* TFEvent.swift in Sources */, BA443D3F2BAD9774009F0FFB /* RemoveEvent.swift in Sources */, diff --git a/TowerForge/TowerForge/GameWorld.swift b/TowerForge/TowerForge/GameWorld.swift index b63f3c8b..75a04f4c 100644 --- a/TowerForge/TowerForge/GameWorld.swift +++ b/TowerForge/TowerForge/GameWorld.swift @@ -86,6 +86,7 @@ class GameWorld { systemManager.add(system: MovementSystem(entityManager: entityManager, eventManager: eventManager)) systemManager.add(system: RemoveSystem(entityManager: entityManager, eventManager: eventManager)) systemManager.add(system: SpawnSystem(entityManager: entityManager, eventManager: eventManager)) + systemManager.add(system: ShootingSystem(entityManager: entityManager, eventManager: eventManager)) } private func setUpSelectionNode() { diff --git a/TowerForge/TowerForge/LevelManager/Components/GameComponents/DamageComponent.swift b/TowerForge/TowerForge/LevelManager/Components/GameComponents/DamageComponent.swift index 9edd8f56..a3294f87 100644 --- a/TowerForge/TowerForge/LevelManager/Components/GameComponents/DamageComponent.swift +++ b/TowerForge/TowerForge/LevelManager/Components/GameComponents/DamageComponent.swift @@ -6,7 +6,6 @@ // import Foundation -import CoreGraphics import SpriteKit class DamageComponent: TFComponent { @@ -26,8 +25,8 @@ class DamageComponent: TFComponent { CACurrentMediaTime() - lastAttackTime >= attackRate } - func damage(_ healthComponent: HealthComponent) -> DamageEvent? { - guard canDamage, let entityId = healthComponent.entity?.id else { + func damage(_ healthComponent: HealthComponent) -> TFEvent? { + guard canDamage, let enemyId = healthComponent.entity?.id, let id = entity?.id else { return nil } @@ -38,6 +37,11 @@ class DamageComponent: TFComponent { } lastAttackTime = CACurrentMediaTime() - return DamageEvent(on: entityId, at: lastAttackTime, with: attackPower) + let event = DamageEvent(on: enemyId, at: lastAttackTime, with: attackPower) + + if temporary { + return event.concurrentlyWith(RemoveEvent(on: id, at: lastAttackTime)) + } + return event } } diff --git a/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift b/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift index 375f176a..1f4439a0 100644 --- a/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift +++ b/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift @@ -21,49 +21,28 @@ class ShootingComponent: TFComponent { super.init() } -// override func update(deltaTime: TimeInterval) { -// super.update(deltaTime: deltaTime) -// -// // Required components for the current Melee -// guard let entity = entity, -// let spriteComponent = entity.component(ofType: SpriteComponent.self), -// let positionComponent = entity.component(ofType: PositionComponent.self) else { -// return -// } -// -// // Loop opposite team's entities -// for entity in entityManager.entities { -// guard let playerComponent = entity.component(ofType: PlayerComponent.self) else { -// return -// } -// if playerComponent.player == .ownPlayer { -// return -// } -// // Get opposite team's components -// guard let oppositeSpriteComponent = entity.component(ofType: SpriteComponent.self), -// let oppositeHealthComponent = entity.component(ofType: HealthComponent.self), -// let oppositePositionComponent = entity.component(ofType: PositionComponent.self) else { -// return -// } -// -// // Get the horizontal distance -// let distanceBetween = (positionComponent.position.x - oppositePositionComponent.position.x) -// -// // Check if within range -// if distanceBetween < range { -// // TODO : Change the hard coded velocity value -// let arrow = Arrow(position: positionComponent.position, -// velocity: playerComponent.player.getDirectionVelocity(), -// attackRate: 1.0, -// entityManager: entityManager) -// guard let arrowSpriteComponent = arrow.component(ofType: SpriteComponent.self) else { -// return -// } -// -// // Check if can attack -// if CACurrentMediaTime() - lastShotTime > fireRate { -// lastShotTime = CACurrentMediaTime() -// } -// } -// } + var canShoot: Bool { + CACurrentMediaTime() - lastShotTime >= fireRate + } + + func shoot(_ healthComponent: HealthComponent) -> SpawnEvent? { + guard canShoot, let entityA = self.entity, let entityB = healthComponent.entity else { + return nil + } + + guard let playerA = entityA.component(ofType: PlayerComponent.self)?.player, + let playerB = entityB.component(ofType: PlayerComponent.self)?.player, + playerA != playerB else { + return nil + } + + guard let positionA = entityA.component(ofType: PositionComponent.self)?.position, + let positionB = entityB.component(ofType: PositionComponent.self)?.position, + abs(positionA.x - positionB.x) <= range else { + return nil + } + + lastShotTime = CACurrentMediaTime() + return SpawnEvent(ofType: Arrow.self, timestamp: lastShotTime, position: positionA, team: Team(player: playerA)) + } } diff --git a/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift b/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift index 6fdad36a..5ce69c43 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift @@ -6,20 +6,24 @@ // import Foundation +import SpriteKit -class Arrow: BaseProjectile { +class Arrow: BaseProjectile, Spawnable { static let textureNames: [String] = [] static let size = CGSize(width: 10, height: 10) static let key = "arrow" static let damage = 5.0 + static let attackRate = 1.0 + static let velocity = CGVector(dx: 100, dy: 0) - init(position: CGPoint, velocity: CGVector, attackRate: TimeInterval) { + required init(position: CGPoint, team: Team) { super.init(textureNames: Arrow.textureNames, size: Arrow.size, key: Arrow.key, position: position, - velocity: velocity) - self.addComponent(DamageComponent(attackRate: attackRate, + team: team, + velocity: Arrow.velocity) + self.addComponent(DamageComponent(attackRate: Arrow.attackRate, attackPower: Arrow.damage, temporary: true)) } diff --git a/TowerForge/TowerForge/LevelManager/Entities/ArrowTower.swift b/TowerForge/TowerForge/LevelManager/Entities/ArrowTower.swift index 99bb73a7..069c9910 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/ArrowTower.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/ArrowTower.swift @@ -7,20 +7,23 @@ import Foundation -class ArrowTower: BaseTower { +class ArrowTower: BaseTower, PlayerSpawnable { + static let title: String = "arrowTower" static let textureNames = ["LightHouse-1"] static let size = CGSize(width: 100, height: 100) static let key = "arrowTower" static let maxHealth = 200.0 static let damage = 10.0 + static var cost = 10 static let fireRate = 1.0 - init(position: CGPoint, entityManager: EntityManager) { + required init(position: CGPoint, team: Team) { super.init(textureNames: ArrowTower.textureNames, size: ArrowTower.size, key: ArrowTower.key, position: position, - maxHealth: ArrowTower.maxHealth) + maxHealth: ArrowTower.maxHealth, + team: team) self.addComponent(ShootingComponent(fireRate: ArrowTower.fireRate, range: 1.0, attackPower: ArrowTower.damage)) diff --git a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseProjectile.swift b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseProjectile.swift index 27ec05cd..64700c08 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseProjectile.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseProjectile.swift @@ -8,12 +8,13 @@ import Foundation class BaseProjectile: TFEntity { - init(textureNames: [String], size: CGSize, key: String, position: CGPoint, velocity: CGVector = .zero) { + init(textureNames: [String], size: CGSize, key: String, position: CGPoint, team: Team, velocity: CGVector = .zero) { super.init() createSpriteComponent(textureNames: textureNames, size: size, key: key, position: position) createMovableComponent(position: position, velocity: velocity) createPositionComponent(position: position) + createPlayerComponent(team: team) } private func createSpriteComponent(textureNames: [String], size: CGSize, key: String, position: CGPoint) { @@ -34,4 +35,9 @@ class BaseProjectile: TFEntity { let movableComponent = MovableComponent(position: position, velocity: velocity) self.addComponent(movableComponent) } + + private func createPlayerComponent(team: Team) { + let playerComponent = PlayerComponent(player: team.player) + self.addComponent(playerComponent) + } } diff --git a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift index bd05a6b9..5d8b785b 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/Base Entities/BaseTower.swift @@ -12,12 +12,14 @@ class BaseTower: TFEntity { size: CGSize, key: String, position: CGPoint, - maxHealth: CGFloat) { + maxHealth: CGFloat, + team: Team) { super.init() createHealthComponent(maxHealth: maxHealth) createSpriteComponent(textureNames: textureNames, size: size, key: key, position: position) createPositionComponent(position: position) + createPlayerComponent(team: team) } override func collide(with other: any Collidable) -> TFEvent? { @@ -57,4 +59,9 @@ class BaseTower: TFEntity { animatableKey: key) self.addComponent(spriteComponent) } + + private func createPlayerComponent(team: Team) { + let playerComponent = PlayerComponent(player: team.player) + self.addComponent(playerComponent) + } } diff --git a/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift b/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift index 08c226ae..6849e359 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/SoldierUnit.swift @@ -27,7 +27,7 @@ class SoldierUnit: BaseUnit, PlayerSpawnable { position: position, maxHealth: SoldierUnit.maxHealth, velocity: SoldierUnit.velocity, - team: Team(player: .oppositePlayer)) + team: team) self.addComponent(ShootingComponent(fireRate: SoldierUnit.attackRate, range: SoldierUnit.range, diff --git a/TowerForge/TowerForge/LevelManager/Events/EventManager.swift b/TowerForge/TowerForge/LevelManager/Events/EventManager.swift index e7cc799c..7410e532 100644 --- a/TowerForge/TowerForge/LevelManager/Events/EventManager.swift +++ b/TowerForge/TowerForge/LevelManager/Events/EventManager.swift @@ -21,7 +21,7 @@ class EventManager { func executeEvents(in target: EventTarget) { while !eventQueue.isEmpty { let currentEvent = eventQueue.removeFirst() - + print(currentEvent) if let output = currentEvent.execute(in: target) { output.events.forEach { eventQueue.append($0) } } diff --git a/TowerForge/TowerForge/LevelManager/Systems/ShootingSystem.swift b/TowerForge/TowerForge/LevelManager/Systems/ShootingSystem.swift new file mode 100644 index 00000000..006ebddc --- /dev/null +++ b/TowerForge/TowerForge/LevelManager/Systems/ShootingSystem.swift @@ -0,0 +1,40 @@ +// +// ShootingSystem.swift +// TowerForge +// +// Created by Zheng Ze on 23/3/24. +// + +import Foundation + +class ShootingSystem: TFSystem { + var isActive = true + weak var entityManager: EntityManager? + weak var eventManager: EventManager? + + init(entityManager: EntityManager, eventManager: EventManager) { + self.entityManager = entityManager + self.eventManager = eventManager + } + + func update(within time: CGFloat) { + guard let entityManager = entityManager, let eventManager = eventManager else { + return + } + let shootingComponents = entityManager.components(ofType: ShootingComponent.self).filter({ $0.canShoot }) + let targetableHealthComponents = entityManager.components(ofType: HealthComponent.self).filter({ + $0.entity?.hasComponent(ofType: PositionComponent.self) ?? false + }) + + for shootingComponent in shootingComponents { + for healthComponent in targetableHealthComponents { + guard let event = shootingComponent.shoot(healthComponent) else { + continue + } + + eventManager.add(event) + break + } + } + } +} From da00d11271d210eb9a9b9bbfdaccf27f1e52cdce Mon Sep 17 00:00:00 2001 From: zheng-ze Date: Sat, 23 Mar 2024 02:30:13 +0800 Subject: [PATCH 7/7] Add AiSystem to update AiComponent and HomeComponent --- .../TowerForge.xcodeproj/project.pbxproj | 12 ++++--- TowerForge/TowerForge/GameWorld.swift | 1 + .../GameComponents/ShootingComponent.swift | 4 +-- .../Entities/{Arrow.swift => Bullet.swift} | 20 +++++------ .../LevelManager/Systems/AiSystem.swift | 35 +++++++++++++++++++ 5 files changed, 56 insertions(+), 16 deletions(-) rename TowerForge/TowerForge/LevelManager/Entities/{Arrow.swift => Bullet.swift} (71%) create mode 100644 TowerForge/TowerForge/LevelManager/Systems/AiSystem.swift diff --git a/TowerForge/TowerForge.xcodeproj/project.pbxproj b/TowerForge/TowerForge.xcodeproj/project.pbxproj index 66ae1286..87ccf2ad 100644 --- a/TowerForge/TowerForge.xcodeproj/project.pbxproj +++ b/TowerForge/TowerForge.xcodeproj/project.pbxproj @@ -14,7 +14,7 @@ 3C9955A52BA47DC600D33FA5 /* BaseProjectile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9955A42BA47DC600D33FA5 /* BaseProjectile.swift */; }; 3C9955AD2BA483B100D33FA5 /* TFSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9955AC2BA483B100D33FA5 /* TFSystem.swift */; }; 3C9955AF2BA48FD200D33FA5 /* MeleeUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9955AE2BA48FD200D33FA5 /* MeleeUnit.swift */; }; - 3C9955B12BA4ACA100D33FA5 /* Arrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9955B02BA4ACA100D33FA5 /* Arrow.swift */; }; + 3C9955B12BA4ACA100D33FA5 /* Bullet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9955B02BA4ACA100D33FA5 /* Bullet.swift */; }; 3C9955B42BA4B12000D33FA5 /* ArrowTower.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9955B32BA4B12000D33FA5 /* ArrowTower.swift */; }; 3C9955BA2BA5637200D33FA5 /* DamageEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9955B92BA5637200D33FA5 /* DamageEvent.swift */; }; 3C9955BC2BA563A800D33FA5 /* TFEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9955BB2BA563A800D33FA5 /* TFEvent.swift */; }; @@ -36,6 +36,7 @@ 3CE951582BAD724D008B2785 /* TFContact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951572BAD724D008B2785 /* TFContact.swift */; }; 3CE9515F2BADE2C5008B2785 /* ShootingSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE9515E2BADE2C5008B2785 /* ShootingSystem.swift */; }; 3CE951612BADE881008B2785 /* Spawnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951602BADE881008B2785 /* Spawnable.swift */; }; + 3CE951632BAE037C008B2785 /* AiSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951622BAE037C008B2785 /* AiSystem.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 */; }; @@ -106,7 +107,7 @@ 3C9955A42BA47DC600D33FA5 /* BaseProjectile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseProjectile.swift; sourceTree = ""; }; 3C9955AC2BA483B100D33FA5 /* TFSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFSystem.swift; sourceTree = ""; }; 3C9955AE2BA48FD200D33FA5 /* MeleeUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeleeUnit.swift; sourceTree = ""; }; - 3C9955B02BA4ACA100D33FA5 /* Arrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arrow.swift; sourceTree = ""; }; + 3C9955B02BA4ACA100D33FA5 /* Bullet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bullet.swift; sourceTree = ""; }; 3C9955B32BA4B12000D33FA5 /* ArrowTower.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowTower.swift; sourceTree = ""; }; 3C9955B92BA5637200D33FA5 /* DamageEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamageEvent.swift; sourceTree = ""; }; 3C9955BB2BA563A800D33FA5 /* TFEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFEvent.swift; sourceTree = ""; }; @@ -128,6 +129,7 @@ 3CE951572BAD724D008B2785 /* TFContact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFContact.swift; sourceTree = ""; }; 3CE9515E2BADE2C5008B2785 /* ShootingSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShootingSystem.swift; sourceTree = ""; }; 3CE951602BADE881008B2785 /* Spawnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Spawnable.swift; sourceTree = ""; }; + 3CE951622BAE037C008B2785 /* AiSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AiSystem.swift; sourceTree = ""; }; 5200624D2BA8D597000DBA30 /* AiComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AiComponent.swift; sourceTree = ""; }; 520062512BA8DA09000DBA30 /* UnitGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitGenerator.swift; sourceTree = ""; }; 520062552BA8E026000DBA30 /* PlayerSpawnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSpawnable.swift; sourceTree = ""; }; @@ -207,7 +209,7 @@ children = ( 3CE9514C2BAC8668008B2785 /* Base Entities */, 3C9955AE2BA48FD200D33FA5 /* MeleeUnit.swift */, - 3C9955B02BA4ACA100D33FA5 /* Arrow.swift */, + 3C9955B02BA4ACA100D33FA5 /* Bullet.swift */, 3C9955B32BA4B12000D33FA5 /* ArrowTower.swift */, 52578B8B2BA627B200B4D76C /* Team.swift */, 529F91872BA6D7A7009551D9 /* SoldierUnit.swift */, @@ -237,6 +239,7 @@ 3C769A732BA591BD00F454F9 /* SpawnSystem.swift */, BA443D3C2BAD9557009F0FFB /* RemoveSystem.swift */, 3CE9515E2BADE2C5008B2785 /* ShootingSystem.swift */, + 3CE951622BAE037C008B2785 /* AiSystem.swift */, ); path = Systems; sourceTree = ""; @@ -648,6 +651,7 @@ 52578B822BA61AAF00B4D76C /* PositionComponent.swift in Sources */, 3C9955A32BA47DBB00D33FA5 /* BaseUnit.swift in Sources */, 3C9955BA2BA5637200D33FA5 /* DamageEvent.swift in Sources */, + 3CE951632BAE037C008B2785 /* AiSystem.swift in Sources */, 3CE9515F2BADE2C5008B2785 /* ShootingSystem.swift in Sources */, 3C9955CA2BA5888F00D33FA5 /* SpawnEvent.swift in Sources */, 3CE951582BAD724D008B2785 /* TFContact.swift in Sources */, @@ -679,7 +683,7 @@ 520062582BA8ED73000DBA30 /* HomeComponent.swift in Sources */, 52DF5FA82BA32B2300135367 /* AppDelegate.swift in Sources */, 3C769A742BA591BD00F454F9 /* SpawnSystem.swift in Sources */, - 3C9955B12BA4ACA100D33FA5 /* Arrow.swift in Sources */, + 3C9955B12BA4ACA100D33FA5 /* Bullet.swift in Sources */, 3CE9514F2BAC8936008B2785 /* Renderer.swift in Sources */, 3C769A722BA58DE700F454F9 /* MovementSystem.swift in Sources */, 52DF5FEB2BA3400C00135367 /* TFAnimatableNode.swift in Sources */, diff --git a/TowerForge/TowerForge/GameWorld.swift b/TowerForge/TowerForge/GameWorld.swift index 9c6357d6..23e99e74 100644 --- a/TowerForge/TowerForge/GameWorld.swift +++ b/TowerForge/TowerForge/GameWorld.swift @@ -97,6 +97,7 @@ 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: AiSystem(entityManager: entityManager, eventManager: eventManager)) } private func setUpSelectionNode() { diff --git a/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift b/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift index e707d098..ab6beb3f 100644 --- a/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift +++ b/TowerForge/TowerForge/LevelManager/Components/GameComponents/ShootingComponent.swift @@ -38,11 +38,11 @@ class ShootingComponent: TFComponent { guard let positionA = entityA.component(ofType: PositionComponent.self)?.position, let positionB = entityB.component(ofType: PositionComponent.self)?.position, - abs(positionA.x - positionB.x) <= range else { + abs(positionA.x - positionB.x) <= range, abs(positionA.y - positionB.y) <= 50 else { return nil } lastShotTime = CACurrentMediaTime() - return SpawnEvent(ofType: Arrow.self, timestamp: lastShotTime, position: positionA, player: playerA) + return SpawnEvent(ofType: Bullet.self, timestamp: lastShotTime, position: positionA, player: playerA) } } diff --git a/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift b/TowerForge/TowerForge/LevelManager/Entities/Bullet.swift similarity index 71% rename from TowerForge/TowerForge/LevelManager/Entities/Arrow.swift rename to TowerForge/TowerForge/LevelManager/Entities/Bullet.swift index 61ea2883..bf400ed7 100644 --- a/TowerForge/TowerForge/LevelManager/Entities/Arrow.swift +++ b/TowerForge/TowerForge/LevelManager/Entities/Bullet.swift @@ -1,5 +1,5 @@ // -// Arrow.swift +// Bullet.swift // TowerForge // // Created by Zheng Ze on 16/3/24. @@ -8,23 +8,23 @@ import Foundation import SpriteKit -class Arrow: BaseProjectile, Spawnable { - static let textureNames: [String] = [] +class Bullet: BaseProjectile, Spawnable { + static let textureNames: [String] = ["bullet"] static let size = CGSize(width: 10, height: 10) - static let key = "arrow" + static let key = "bullet" static let damage = 5.0 static let attackRate = 1.0 static let velocity = CGVector(dx: 100, dy: 0) required init(position: CGPoint, player: Player) { - super.init(textureNames: Arrow.textureNames, - size: Arrow.size, - key: Arrow.key, + super.init(textureNames: Bullet.textureNames, + size: Bullet.size, + key: Bullet.key, position: position, player: player, - velocity: Arrow.velocity) - self.addComponent(DamageComponent(attackRate: Arrow.attackRate, - attackPower: Arrow.damage, + velocity: Bullet.velocity) + self.addComponent(DamageComponent(attackRate: Bullet.attackRate, + attackPower: Bullet.damage, temporary: true)) } diff --git a/TowerForge/TowerForge/LevelManager/Systems/AiSystem.swift b/TowerForge/TowerForge/LevelManager/Systems/AiSystem.swift new file mode 100644 index 00000000..ef76f05b --- /dev/null +++ b/TowerForge/TowerForge/LevelManager/Systems/AiSystem.swift @@ -0,0 +1,35 @@ +// +// AiSystem.swift +// TowerForge +// +// Created by Zheng Ze on 23/3/24. +// + +import Foundation + +class AiSystem: TFSystem { + var isActive = true + weak var entityManager: EntityManager? + weak var eventManager: EventManager? + + init(entityManager: EntityManager, eventManager: EventManager) { + self.entityManager = entityManager + self.eventManager = eventManager + } + + func update(within time: CGFloat) { + guard let entityManager = entityManager, let eventManager = eventManager else { + return + } + + let homeComponents = entityManager.components(ofType: HomeComponent.self) + for homeComponent in homeComponents { + homeComponent.update(deltaTime: time) + } + + let aiComponents = entityManager.components(ofType: AiComponent.self) + for aiComponent in aiComponents { + aiComponent.update(deltaTime: time) + } + } +}