diff --git a/star-dash/star-dash.xcodeproj/project.pbxproj b/star-dash/star-dash.xcodeproj/project.pbxproj index f4c4cca6..4e120e06 100644 --- a/star-dash/star-dash.xcodeproj/project.pbxproj +++ b/star-dash/star-dash.xcodeproj/project.pbxproj @@ -8,8 +8,20 @@ /* Begin PBXBuildFile section */ 142D9F8F2BA15FBB005FE9E0 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 142D9F8E2BA15FBB005FE9E0 /* .swiftlint.yml */; }; + 143AA38C2BA4D3E3009C28E7 /* JumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 143AA38B2BA4D3E3009C28E7 /* JumpEvent.swift */; }; + 143AA38F2BA4D61A009C28E7 /* CGVector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 143AA38E2BA4D61A009C28E7 /* CGVector.swift */; }; + 143AA3912BA4D7AC009C28E7 /* MonsterDeathEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 143AA3902BA4D7AC009C28E7 /* MonsterDeathEvent.swift */; }; + 143AA3932BA4DBE7009C28E7 /* PlayerMonsterContactEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 143AA3922BA4DBE7009C28E7 /* PlayerMonsterContactEvent.swift */; }; + 143AA3952BA4DF1C009C28E7 /* MonsterAttackPlayerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 143AA3942BA4DF1C009C28E7 /* MonsterAttackPlayerEvent.swift */; }; + 143AA3972BA4E0D9009C28E7 /* PickupCollectibleEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 143AA3962BA4E0D9009C28E7 /* PickupCollectibleEvent.swift */; }; 145F2C802BA203B400457549 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 145F2C7F2BA203B400457549 /* Event.swift */; }; 145F2C842BA22CA300457549 /* EventModifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 145F2C832BA22CA300457549 /* EventModifiable.swift */; }; + 1471B0A02BA6A70600878B14 /* HealthSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1471B09F2BA6A70600878B14 /* HealthSystem.swift */; }; + 1471B0A22BA6AA2200878B14 /* RespawnEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1471B0A12BA6AA2200878B14 /* RespawnEvent.swift */; }; + 1471B0A42BA6AAF200878B14 /* PlayerDeathEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1471B0A32BA6AAF200878B14 /* PlayerDeathEvent.swift */; }; + 1471B0A62BA6AC2D00878B14 /* PlayerAttackMonsterEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1471B0A52BA6AC2D00878B14 /* PlayerAttackMonsterEvent.swift */; }; + 1471B0AD2BA6AE4E00878B14 /* UseGrappleHookEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1471B0AC2BA6AE4E00878B14 /* UseGrappleHookEvent.swift */; }; + 14D14B732BA5A3CD00386C3B /* RemoveEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14D14B722BA5A3CD00386C3B /* RemoveEvent.swift */; }; 14E2478E2BA22FCE0071FFC0 /* MoveEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14E2478D2BA22FCE0071FFC0 /* MoveEvent.swift */; }; 14E247932BA2CA920071FFC0 /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = 14E247922BA2CA920071FFC0 /* DequeModule */; }; 14E247952BA2CB480071FFC0 /* EventManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14E247942BA2CB480071FFC0 /* EventManager.swift */; }; @@ -102,8 +114,20 @@ /* Begin PBXFileReference section */ 142D9F8E2BA15FBB005FE9E0 /* .swiftlint.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; + 143AA38B2BA4D3E3009C28E7 /* JumpEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpEvent.swift; sourceTree = ""; }; + 143AA38E2BA4D61A009C28E7 /* CGVector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGVector.swift; sourceTree = ""; }; + 143AA3902BA4D7AC009C28E7 /* MonsterDeathEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonsterDeathEvent.swift; sourceTree = ""; }; + 143AA3922BA4DBE7009C28E7 /* PlayerMonsterContactEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerMonsterContactEvent.swift; sourceTree = ""; }; + 143AA3942BA4DF1C009C28E7 /* MonsterAttackPlayerEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonsterAttackPlayerEvent.swift; sourceTree = ""; }; + 143AA3962BA4E0D9009C28E7 /* PickupCollectibleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickupCollectibleEvent.swift; sourceTree = ""; }; 145F2C7F2BA203B400457549 /* Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; 145F2C832BA22CA300457549 /* EventModifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventModifiable.swift; sourceTree = ""; }; + 1471B09F2BA6A70600878B14 /* HealthSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthSystem.swift; sourceTree = ""; }; + 1471B0A12BA6AA2200878B14 /* RespawnEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RespawnEvent.swift; sourceTree = ""; }; + 1471B0A32BA6AAF200878B14 /* PlayerDeathEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerDeathEvent.swift; sourceTree = ""; }; + 1471B0A52BA6AC2D00878B14 /* PlayerAttackMonsterEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerAttackMonsterEvent.swift; sourceTree = ""; }; + 1471B0AC2BA6AE4E00878B14 /* UseGrappleHookEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseGrappleHookEvent.swift; sourceTree = ""; }; + 14D14B722BA5A3CD00386C3B /* RemoveEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveEvent.swift; sourceTree = ""; }; 14E2478D2BA22FCE0071FFC0 /* MoveEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveEvent.swift; sourceTree = ""; }; 14E247942BA2CB480071FFC0 /* EventManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventManager.swift; sourceTree = ""; }; 461148902BA1CDBF0073E7E1 /* SystemManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemManager.swift; sourceTree = ""; }; @@ -193,22 +217,81 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 143AA38D2BA4D600009C28E7 /* Extensions */ = { + isa = PBXGroup; + children = ( + 143AA38E2BA4D61A009C28E7 /* CGVector.swift */, + ); + path = Extensions; + sourceTree = ""; + }; 145F2C7E2BA2039700457549 /* Events */ = { isa = PBXGroup; children = ( 145F2C7F2BA203B400457549 /* Event.swift */, 14E247942BA2CB480071FFC0 /* EventManager.swift */, 145F2C832BA22CA300457549 /* EventModifiable.swift */, - 14E2478D2BA22FCE0071FFC0 /* MoveEvent.swift */, + 1471B0AB2BA6AE4400878B14 /* ToolEvents */, + 14D14B742BA5A72800386C3B /* PlayerEvents */, + 14D14B782BA5ABAC00386C3B /* MonsterEvents */, + 14D14B772BA5AB0700386C3B /* ContactEvents */, + 14D14B792BA5AC1900386C3B /* CommonEvents */, ); path = Events; sourceTree = ""; }; + 1471B0AB2BA6AE4400878B14 /* ToolEvents */ = { + isa = PBXGroup; + children = ( + 1471B0AC2BA6AE4E00878B14 /* UseGrappleHookEvent.swift */, + ); + path = ToolEvents; + sourceTree = ""; + }; + 14D14B742BA5A72800386C3B /* PlayerEvents */ = { + isa = PBXGroup; + children = ( + 143AA3962BA4E0D9009C28E7 /* PickupCollectibleEvent.swift */, + 1471B0A12BA6AA2200878B14 /* RespawnEvent.swift */, + 1471B0A32BA6AAF200878B14 /* PlayerDeathEvent.swift */, + ); + path = PlayerEvents; + sourceTree = ""; + }; + 14D14B772BA5AB0700386C3B /* ContactEvents */ = { + isa = PBXGroup; + children = ( + 143AA3922BA4DBE7009C28E7 /* PlayerMonsterContactEvent.swift */, + ); + path = ContactEvents; + sourceTree = ""; + }; + 14D14B782BA5ABAC00386C3B /* MonsterEvents */ = { + isa = PBXGroup; + children = ( + 143AA3942BA4DF1C009C28E7 /* MonsterAttackPlayerEvent.swift */, + 1471B0A52BA6AC2D00878B14 /* PlayerAttackMonsterEvent.swift */, + 143AA3902BA4D7AC009C28E7 /* MonsterDeathEvent.swift */, + ); + path = MonsterEvents; + sourceTree = ""; + }; + 14D14B792BA5AC1900386C3B /* CommonEvents */ = { + isa = PBXGroup; + children = ( + 14E2478D2BA22FCE0071FFC0 /* MoveEvent.swift */, + 143AA38B2BA4D3E3009C28E7 /* JumpEvent.swift */, + 14D14B722BA5A3CD00386C3B /* RemoveEvent.swift */, + ); + path = CommonEvents; + sourceTree = ""; + }; 4611488F2BA1CD9E0073E7E1 /* Systems */ = { isa = PBXGroup; children = ( 461148902BA1CDBF0073E7E1 /* SystemManager.swift */, 461148922BA1D04B0073E7E1 /* System.swift */, + 1471B09F2BA6A70600878B14 /* HealthSystem.swift */, 461148952BA1D53D0073E7E1 /* PositionSystem.swift */, 461148972BA1E41F0073E7E1 /* PhysicsSystem.swift */, ); @@ -276,6 +359,7 @@ 4E86605D2BA095CC0035530D /* Constants */, 4E630F352B9F91C20008F887 /* Enums */, 145F2C7E2BA2039700457549 /* Events */, + 143AA38D2BA4D600009C28E7 /* Extensions */, 4E630EF52B9F7E070008F887 /* AppDelegate.swift */, 4E630EF72B9F7E070008F887 /* SceneDelegate.swift */, 4E630EF92B9F7E070008F887 /* ViewController.swift */, @@ -574,9 +658,13 @@ E6B1DC902BA34A4800473563 /* SDPhysicsEngine in Sources */, 4E630F272B9F7E770008F887 /* Entity.swift in Sources */, 4E630F2C2B9F7F460008F887 /* Component.swift in Sources */, + 143AA38F2BA4D61A009C28E7 /* CGVector.swift in Sources */, 14E2478E2BA22FCE0071FFC0 /* MoveEvent.swift in Sources */, + 14D14B732BA5A3CD00386C3B /* RemoveEvent.swift in Sources */, 4E8660622BA0964A0035530D /* Obstacle.swift in Sources */, + 1471B0A02BA6A70600878B14 /* HealthSystem.swift in Sources */, 461148932BA1D04B0073E7E1 /* System.swift in Sources */, + 1471B0A62BA6AC2D00878B14 /* PlayerAttackMonsterEvent.swift in Sources */, 4E8660642BA096600035530D /* Tool.swift in Sources */, 4E630F392B9F943B0008F887 /* EntityManager.swift in Sources */, 4E630F302B9F83DE0008F887 /* SpriteComponent.swift in Sources */, @@ -584,6 +672,8 @@ 4E86605F2BA095E30035530D /* Collectible.swift in Sources */, E64361122BA4C2CD003850FD /* ObjectModule.swift in Sources */, 4E8660662BA097D40035530D /* PhysicsConstants.swift in Sources */, + 143AA3952BA4DF1C009C28E7 /* MonsterAttackPlayerEvent.swift in Sources */, + 143AA3932BA4DBE7009C28E7 /* PlayerMonsterContactEvent.swift in Sources */, 46D418162BA5CBD60091A38B /* Collidable.swift in Sources */, 461148912BA1CDBF0073E7E1 /* SystemManager.swift in Sources */, 145F2C842BA22CA300457549 /* EventModifiable.swift in Sources */, @@ -591,8 +681,10 @@ E6A011172BA5F4AD006904D9 /* EntitySyncInterface.swift in Sources */, E64361132BA4C2CD003850FD /* PhysicsModule.swift in Sources */, 4E630F322B9F887C0008F887 /* PhysicsComponent.swift in Sources */, + 1471B0AD2BA6AE4E00878B14 /* UseGrappleHookEvent.swift in Sources */, 4E630EF62B9F7E070008F887 /* AppDelegate.swift in Sources */, 461148962BA1D53D0073E7E1 /* PositionSystem.swift in Sources */, + 143AA38C2BA4D3E3009C28E7 /* JumpEvent.swift in Sources */, 4E630F2A2B9F7EF60008F887 /* PositionComponent.swift in Sources */, 46D418132BA5C9930091A38B /* Floor.swift in Sources */, E64361152BA4C2CD003850FD /* GameBridge.swift in Sources */, @@ -604,18 +696,21 @@ 46D418222BA5D4E60091A38B /* Obstacle+Collidable.swift in Sources */, E6A745162BA057040080C1BE /* MTKRenderer.swift in Sources */, E6A745182BA057040080C1BE /* Renderer.swift in Sources */, + 143AA3972BA4E0D9009C28E7 /* PickupCollectibleEvent.swift in Sources */, E6A745192BA057040080C1BE /* GameScene.swift in Sources */, 46D418242BA5D5280091A38B /* Tool+Collidable.swift in Sources */, E64361142BA4C2CD003850FD /* CreationModule.swift in Sources */, E6A745162BA057040080C1BE /* MTKRenderer.swift in Sources */, E6A745182BA057040080C1BE /* Renderer.swift in Sources */, E6A745172BA057040080C1BE /* ControlView.swift in Sources */, + 1471B0A42BA6AAF200878B14 /* PlayerDeathEvent.swift in Sources */, 46D418202BA5D3420091A38B /* Collectible+Collidable.swift in Sources */, E6A7451D2BA0CAD90080C1BE /* JoystickView.swift in Sources */, E6B550A12BA15E9C00DC7396 /* OverlayView.swift in Sources */, 46D418262BA5D6500091A38B /* Wall+Collidable.swift in Sources */, 4E630F342B9F8FC00008F887 /* Player.swift in Sources */, 4E630F372B9F91DE0008F887 /* PlayerSprite.swift in Sources */, + 143AA3912BA4D7AC009C28E7 /* MonsterDeathEvent.swift in Sources */, E64361102BA4C2CD003850FD /* SpriteModule.swift in Sources */, 46B8C09A2BA328D900498705 /* GameEngine.swift in Sources */, 461148982BA1E41F0073E7E1 /* PhysicsSystem.swift in Sources */, @@ -623,6 +718,7 @@ 4E630F2E2B9F81850008F887 /* HealthComponent.swift in Sources */, 4E86605C2BA095460035530D /* Monster.swift in Sources */, 145F2C802BA203B400457549 /* Event.swift in Sources */, + 1471B0A22BA6AA2200878B14 /* RespawnEvent.swift in Sources */, 46D418112BA5C88B0091A38B /* Wall.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/star-dash/star-dash/Events/CommonEvents/JumpEvent.swift b/star-dash/star-dash/Events/CommonEvents/JumpEvent.swift new file mode 100644 index 00000000..30aba083 --- /dev/null +++ b/star-dash/star-dash/Events/CommonEvents/JumpEvent.swift @@ -0,0 +1,31 @@ +// +// JumpEvent.swift +// star-dash +// +// Created by Jason Qiu on 16/3/24. +// + +import Foundation + +class JumpEvent: Event { + let timestamp: Date + let entityId: EntityId + + let jumpImpulse: CGVector + + init(on entityId: EntityId, by jumpImpulse: CGVector) { + timestamp = Date.now + self.entityId = entityId + self.jumpImpulse = jumpImpulse + } + + func execute(on target: EventModifiable) { + guard let physicsSystem = target.system(ofType: PhysicsSystem.self) else { + return + } + guard !physicsSystem.isJumping(entityId) else { + return + } + physicsSystem.applyImpulse(to: entityId, impulse: jumpImpulse) + } +} diff --git a/star-dash/star-dash/Events/CommonEvents/MoveEvent.swift b/star-dash/star-dash/Events/CommonEvents/MoveEvent.swift new file mode 100644 index 00000000..5396a804 --- /dev/null +++ b/star-dash/star-dash/Events/CommonEvents/MoveEvent.swift @@ -0,0 +1,28 @@ +// +// MoveEvent.swift +// star-dash +// +// Created by Jason Qiu on 14/3/24. +// + +import Foundation + +class MoveEvent: Event { + let timestamp: Date + let entityId: EntityId + + let destination: CGPoint + + init(on entityId: EntityId, to destination: CGPoint) { + timestamp = Date.now + self.entityId = entityId + self.destination = destination + } + + func execute(on target: EventModifiable) { + guard let positionSystem = target.system(ofType: PositionSystem.self) else { + return + } + positionSystem.move(entityId: entityId, to: destination) + } +} diff --git a/star-dash/star-dash/Events/CommonEvents/RemoveEvent.swift b/star-dash/star-dash/Events/CommonEvents/RemoveEvent.swift new file mode 100644 index 00000000..0c22fd21 --- /dev/null +++ b/star-dash/star-dash/Events/CommonEvents/RemoveEvent.swift @@ -0,0 +1,25 @@ +// +// RemoveEvent.swift +// star-dash +// +// Created by Jason Qiu on 16/3/24. +// + +import Foundation + +class RemoveEvent: Event { + let timestamp: Date + let entityId: EntityId + + init(on entityId: EntityId) { + timestamp = Date.now + self.entityId = entityId + } + + func execute(on target: EventModifiable) { + guard let entity = target.entity(with: entityId) else { + return + } + target.remove(entity: entity) + } +} diff --git a/star-dash/star-dash/Events/ContactEvents/PlayerMonsterContactEvent.swift b/star-dash/star-dash/Events/ContactEvents/PlayerMonsterContactEvent.swift new file mode 100644 index 00000000..97f89642 --- /dev/null +++ b/star-dash/star-dash/Events/ContactEvents/PlayerMonsterContactEvent.swift @@ -0,0 +1,26 @@ +// +// PlayerMonsterContactEvent.swift +// star-dash +// +// Created by Jason Qiu on 16/3/24. +// + +import Foundation + +class PlayerMonsterContactEvent: Event { + let timestamp: Date + let entityId: EntityId + + let monsterEntityId: EntityId + + init(from playerEntityId: EntityId, on monsterEntityId: EntityId) { + self.timestamp = Date.now + entityId = playerEntityId + self.monsterEntityId = monsterEntityId + } + + func execute(on target: EventModifiable) { + // TODO: Determine if player is directly on top of monster. + // Trigger PlayerAttackMonsterEvent and MonsterAttackPlayerEvent accordingly + } +} diff --git a/star-dash/star-dash/Events/EventModifiable.swift b/star-dash/star-dash/Events/EventModifiable.swift index 844297e0..b1436340 100644 --- a/star-dash/star-dash/Events/EventModifiable.swift +++ b/star-dash/star-dash/Events/EventModifiable.swift @@ -7,5 +7,11 @@ import Foundation -/// EventModifiable represents objects that can be modified by events. -protocol EventModifiable { } +/// EventModifiable represents instances with entities that can be modified by events through systems. +protocol EventModifiable { + func entity(with entityId: EntityId) -> Entity? + func system(ofType type: T.Type) -> T? + func add(entity: Entity) + func add(event: Event) + func remove(entity: Entity) +} diff --git a/star-dash/star-dash/Events/MonsterEvents/MonsterAttackPlayerEvent.swift b/star-dash/star-dash/Events/MonsterEvents/MonsterAttackPlayerEvent.swift new file mode 100644 index 00000000..ea7addf9 --- /dev/null +++ b/star-dash/star-dash/Events/MonsterEvents/MonsterAttackPlayerEvent.swift @@ -0,0 +1,31 @@ +// +// MonsterAttackPlayerEvent.swift +// star-dash +// +// Created by Jason Qiu on 16/3/24. +// + +import Foundation + +class MonsterAttackPlayerEvent: Event { + private static let playerHealthDecrement = 20 + + let timestamp: Date + let entityId: EntityId + + init(on entityId: EntityId) { + timestamp = Date.now + self.entityId = entityId + } + + func execute(on target: EventModifiable) { + guard let healthSystem = target.system(ofType: HealthSystem.self) else { + return + } + healthSystem.decreaseHealth(of: entityId, by: MonsterAttackPlayerEvent.playerHealthDecrement) + + if !healthSystem.hasHealth(for: entityId) { + target.add(event: PlayerDeathEvent(on: entityId)) + } + } +} diff --git a/star-dash/star-dash/Events/MonsterEvents/MonsterDeathEvent.swift b/star-dash/star-dash/Events/MonsterEvents/MonsterDeathEvent.swift new file mode 100644 index 00000000..7d4171ef --- /dev/null +++ b/star-dash/star-dash/Events/MonsterEvents/MonsterDeathEvent.swift @@ -0,0 +1,22 @@ +// +// MonsterDeathEvent.swift +// star-dash +// +// Created by Jason Qiu on 16/3/24. +// + +import Foundation + +class MonsterDeathEvent: Event { + let timestamp: Date + let entityId: EntityId + + init(on entityId: EntityId) { + self.timestamp = Date.now + self.entityId = entityId + } + + func execute(on target: EventModifiable) { + target.add(event: RemoveEvent(on: entityId)) + } +} diff --git a/star-dash/star-dash/Events/MonsterEvents/PlayerAttackMonsterEvent.swift b/star-dash/star-dash/Events/MonsterEvents/PlayerAttackMonsterEvent.swift new file mode 100644 index 00000000..b5900604 --- /dev/null +++ b/star-dash/star-dash/Events/MonsterEvents/PlayerAttackMonsterEvent.swift @@ -0,0 +1,31 @@ +// +// PlayerAttackMonsterEvent.swift +// star-dash +// +// Created by Jason Qiu on 17/3/24. +// + +import Foundation + +class PlayerAttackMonsterEvent: Event { + private static let monsterHealthDecrement = 200 + + let timestamp: Date + let entityId: EntityId + + init(on entityId: EntityId) { + timestamp = Date.now + self.entityId = entityId + } + + func execute(on target: EventModifiable) { + guard let healthSystem = target.system(ofType: HealthSystem.self) else { + return + } + healthSystem.decreaseHealth(of: entityId, by: PlayerAttackMonsterEvent.monsterHealthDecrement) + + if !healthSystem.hasHealth(for: entityId) { + target.add(event: MonsterDeathEvent(on: entityId)) + } + } +} diff --git a/star-dash/star-dash/Events/MoveEvent.swift b/star-dash/star-dash/Events/MoveEvent.swift deleted file mode 100644 index 475009e8..00000000 --- a/star-dash/star-dash/Events/MoveEvent.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// MoveEvent.swift -// star-dash -// -// Created by Jason Qiu on 14/3/24. -// - -import Foundation - -class MoveEvent: Event { - var entityId: EntityId - var timestamp: Date - - var displacement: CGVector - - init(entityId: EntityId, displacement: CGVector, timestamp: Date = Date.now) { - self.entityId = entityId - self.timestamp = timestamp - self.displacement = displacement - } - - func execute(on target: EventModifiable) { - // TODO: Use PositionSystem from target to modify entity of entityId - } -} diff --git a/star-dash/star-dash/Events/PlayerEvents/PickupCollectibleEvent.swift b/star-dash/star-dash/Events/PlayerEvents/PickupCollectibleEvent.swift new file mode 100644 index 00000000..5641c7d0 --- /dev/null +++ b/star-dash/star-dash/Events/PlayerEvents/PickupCollectibleEvent.swift @@ -0,0 +1,26 @@ +// +// PickupCollectibleEvent.swift +// star-dash +// +// Created by Jason Qiu on 16/3/24. +// + +import Foundation + +class PickupCollectibleEvent: Event { + let timestamp: Date + let entityId: EntityId + + let collectibleEntityId: EntityId + + init(by entityId: EntityId, collectibleEntityId: EntityId) { + timestamp = Date.now + self.entityId = entityId + self.collectibleEntityId = collectibleEntityId + } + + func execute(on target: EventModifiable) { + // TODO: Add player score + target.add(event: RemoveEvent(on: collectibleEntityId)) + } +} diff --git a/star-dash/star-dash/Events/PlayerEvents/PlayerDeathEvent.swift b/star-dash/star-dash/Events/PlayerEvents/PlayerDeathEvent.swift new file mode 100644 index 00000000..25eece90 --- /dev/null +++ b/star-dash/star-dash/Events/PlayerEvents/PlayerDeathEvent.swift @@ -0,0 +1,23 @@ +// +// PlayerDeathEvent.swift +// star-dash +// +// Created by Jason Qiu on 17/3/24. +// + +import Foundation + +class PlayerDeathEvent: Event { + let timestamp: Date + let entityId: EntityId + + init(on entityId: EntityId) { + timestamp = Date.now + self.entityId = entityId + } + + func execute(on target: EventModifiable) { + // TODO: Where should player respawn to? + // target.add(event: RespawnEvent(on: entityId, to: )) + } +} diff --git a/star-dash/star-dash/Events/PlayerEvents/RespawnEvent.swift b/star-dash/star-dash/Events/PlayerEvents/RespawnEvent.swift new file mode 100644 index 00000000..929f26d8 --- /dev/null +++ b/star-dash/star-dash/Events/PlayerEvents/RespawnEvent.swift @@ -0,0 +1,25 @@ +// +// RespawnEvent.swift +// star-dash +// +// Created by Jason Qiu on 17/3/24. +// + +import Foundation + +class RespawnEvent: Event { + let timestamp: Date + let entityId: EntityId + + let newPosition: CGPoint + + init(on entityId: EntityId, to newPosition: CGPoint) { + timestamp = Date.now + self.entityId = entityId + self.newPosition = newPosition + } + + func execute(on target: EventModifiable) { + target.add(event: MoveEvent(on: entityId, to: newPosition)) + } +} diff --git a/star-dash/star-dash/Events/ToolEvents/UseGrappleHookEvent.swift b/star-dash/star-dash/Events/ToolEvents/UseGrappleHookEvent.swift new file mode 100644 index 00000000..8766c9bb --- /dev/null +++ b/star-dash/star-dash/Events/ToolEvents/UseGrappleHookEvent.swift @@ -0,0 +1,22 @@ +// +// UseGrappleHookEvent.swift +// star-dash +// +// Created by Jason Qiu on 17/3/24. +// + +import Foundation + +class UseGrappleHookEvent: Event { + let timestamp: Date + let entityId: EntityId + + init(from playerEntityId: EntityId) { + self.timestamp = Date.now + entityId = playerEntityId + } + + func execute(on target: EventModifiable) { + // TODO: Shoot graple hook from player position + } +} diff --git a/star-dash/star-dash/Extensions/CGVector.swift b/star-dash/star-dash/Extensions/CGVector.swift new file mode 100644 index 00000000..2d3143c1 --- /dev/null +++ b/star-dash/star-dash/Extensions/CGVector.swift @@ -0,0 +1,43 @@ +// +// CGVector.swift +// star-dash +// +// Created by Jason Qiu on 16/3/24. +// + +import Foundation + +// MARK: Operators +extension CGVector { + static prefix func - (vector: CGVector) -> CGVector { + CGVector(dx: -vector.dx, dy: -vector.dy) + } + + static func + (left: CGVector, right: CGVector) -> CGVector { + CGVector(dx: left.dx + right.dx, dy: left.dy + right.dy) + } + + static func - (left: CGVector, right: CGVector) -> CGVector { + left + (-right) + } + + static func * (left: CGVector, scalar: CGFloat) -> CGVector { + CGVector(dx: left.dx * scalar, dy: left.dy * scalar) + } + + static func * (scalar: CGFloat, right: CGVector) -> CGVector { + right * scalar + } + + static func / (left: CGVector, scalar: CGFloat) -> CGVector { + left * (1 / scalar) + } + + static func += (left: inout CGVector, right: CGVector) { + left = CGVector(dx: left.dx + right.dx, dy: left.dy + right.dy) + } + + static func -= (left: inout CGVector, right: CGVector) { + left = CGVector(dx: left.dx - right.dx, dy: left.dy - right.dy) + } +} diff --git a/star-dash/star-dash/GameEngine/Entities/EntityManager.swift b/star-dash/star-dash/GameEngine/Entities/EntityManager.swift index 7baff5ae..0573a015 100644 --- a/star-dash/star-dash/GameEngine/Entities/EntityManager.swift +++ b/star-dash/star-dash/GameEngine/Entities/EntityManager.swift @@ -43,6 +43,21 @@ class EntityManager { self.entityComponentMap[entity.id] = Set() } + func remove(entity: Entity) { + entityMap[entity.id] = nil + guard let componentIds = entityComponentMap[entity.id] else { + return + } + for componentId in componentIds { + componentMap[componentId] = nil + } + entityComponentMap[entity.id] = nil + } + + func entity(with entityId: EntityId) -> Entity? { + entityMap[entityId] + } + func component(ofType type: T.Type, of entityId: EntityId) -> T? { guard let components = entityComponentMap[entityId] else { return nil diff --git a/star-dash/star-dash/GameEngine/GameEngine.swift b/star-dash/star-dash/GameEngine/GameEngine.swift index 9c79367a..31350e77 100644 --- a/star-dash/star-dash/GameEngine/GameEngine.swift +++ b/star-dash/star-dash/GameEngine/GameEngine.swift @@ -37,7 +37,25 @@ class GameEngine { } extension GameEngine: EventModifiable { - // TODO: functions of event modifiable + func entity(with entityId: EntityId) -> Entity? { + entityManager.entity(with: entityId) + } + + func system(ofType type: T.Type) -> T? { + systemManager.system(ofType: type) + } + + func add(entity: Entity) { + entityManager.add(entity: entity) + } + + func add(event: Event) { + eventManager.add(event: event) + } + + func remove(entity: Entity) { + entityManager.remove(entity: entity) + } } extension GameEngine: EntitySyncInterface { diff --git a/star-dash/star-dash/GameEngine/Systems/HealthSystem.swift b/star-dash/star-dash/GameEngine/Systems/HealthSystem.swift new file mode 100644 index 00000000..a0e90e18 --- /dev/null +++ b/star-dash/star-dash/GameEngine/Systems/HealthSystem.swift @@ -0,0 +1,48 @@ +// +// HealthSystem.swift +// star-dash +// +// Created by Jason Qiu on 17/3/24. +// + +import Foundation + +class HealthSystem: System { + var isActive: Bool + var dispatcher: EventModifiable? + var entityManager: EntityManager + + init(_ entityManager: EntityManager, dispatcher: EventModifiable? = nil) { + self.isActive = true + self.entityManager = entityManager + self.dispatcher = dispatcher + } + + func hasHealth(for entityId: EntityId) -> Bool { + guard let healthComponent = getHealthComponent(of: entityId) else { + return false + } + + return healthComponent.health > 0 + } + + func increaseHealth(of entityId: EntityId, by increment: Int) { + guard let healthComponent = getHealthComponent(of: entityId) else { + return + } + + healthComponent.health += increment + } + + func decreaseHealth(of entityId: EntityId, by decrement: Int) { + guard let healthComponent = getHealthComponent(of: entityId) else { + return + } + + healthComponent.health -= decrement + } + + private func getHealthComponent(of entityId: EntityId) -> HealthComponent? { + entityManager.component(ofType: HealthComponent.self, of: entityId) + } +} diff --git a/star-dash/star-dash/GameEngine/Systems/PhysicsSystem.swift b/star-dash/star-dash/GameEngine/Systems/PhysicsSystem.swift index 7f81a089..0df8d50b 100644 --- a/star-dash/star-dash/GameEngine/Systems/PhysicsSystem.swift +++ b/star-dash/star-dash/GameEngine/Systems/PhysicsSystem.swift @@ -47,10 +47,15 @@ class PhysicsSystem: System { return } - let newForce = CGVector(dx: physicsComponent.force.dx + newForce.dx, - dy: physicsComponent.force.dy + newForce.dy) + physicsComponent.force += newForce + } + + func applyImpulse(to entityId: EntityId, impulse: CGVector) { + guard let physicsComponent = getPhysicsComponent(of: entityId) else { + return + } - physicsComponent.force = newForce + physicsComponent.velocity += (impulse / physicsComponent.mass) } private func getPhysicsComponent(of entityId: EntityId) -> PhysicsComponent? { diff --git a/star-dash/star-dash/GameEngine/Systems/SystemManager.swift b/star-dash/star-dash/GameEngine/Systems/SystemManager.swift index 716245e7..8c630fa2 100644 --- a/star-dash/star-dash/GameEngine/Systems/SystemManager.swift +++ b/star-dash/star-dash/GameEngine/Systems/SystemManager.swift @@ -19,4 +19,8 @@ class SystemManager { system.update(by: deltaTime) } } + + func system(ofType type: T.Type) -> T? { + systems.first(where: { $0 is T }) as? T + } }