diff --git a/Sources/FirebladeECS/FSM.swift b/Sources/FirebladeECS/FSM.swift index c1ea41b..946f5c2 100644 --- a/Sources/FirebladeECS/FSM.swift +++ b/Sources/FirebladeECS/FSM.swift @@ -6,7 +6,7 @@ public typealias ComponentInitializable = Component & EmptyInitializable public protocol ComponentProvider { var identifier: AnyHashable { get } - func getComponent() -> C? where C: Component + func getComponent() -> Component } // MARK: - @@ -24,8 +24,8 @@ extension ComponentInstanceProvider: ComponentProvider { ObjectIdentifier(instance) } - public func getComponent() -> C? where C : Component { - instance as? C + public func getComponent() -> Component { + instance } } @@ -42,8 +42,8 @@ public struct ComponentTypeProvider { } extension ComponentTypeProvider: ComponentProvider { - public func getComponent() -> C? where C: Component { - componentType.init() as? C + public func getComponent() -> Component { + componentType.init() } } @@ -70,8 +70,8 @@ public class ComponentSingletonProvider { } extension ComponentSingletonProvider: ComponentProvider { - public func getComponent() -> C? where C: Component { - instance as? C + public func getComponent() -> Component { + instance } } @@ -96,8 +96,8 @@ extension DynamicComponentProvider: ComponentProvider { ObjectIdentifier(closure) } - public func getComponent() -> C? where C: Component { - closure.closure() as? C + public func getComponent() -> Component { + closure.closure() } } @@ -109,8 +109,15 @@ public class EntityState { public init() { } public func add(_ type: C.Type) -> StateComponentMapping { - StateComponentMapping(creatingState: self, - type: type) + StateComponentMapping(creatingState: self, type: type) + } + + public func get(_ type: C.Type) -> ComponentProvider? { + providers[type.identifier] + } + + public func has(_ type: C.Type) -> Bool { + providers[type.identifier] != nil } } // MARK: - @@ -165,3 +172,66 @@ public class StateComponentMapping { creatingState.providers[componentType.identifier] = provider } } + +// MARK: - + +public class EntityStateMachine { + private var states: [String: EntityState] + + private var currentState: EntityState? + + public var entity: Entity + + public init(entity: Entity) { + self.entity = entity + states = [:] + } + + public func addState(name: String, state: EntityState) -> Self { + states[name] = state + return self + } + + public func createState(name: String) -> EntityState { + let state = EntityState() + states[name] = state + return state + } + + public func changeState(name: String) { + guard let newState = states[name] else { + fatalError("Entity state '\(name)' doesn't exist") + } + + if newState === currentState { + return + } + var toAdd: [ComponentIdentifier: ComponentProvider] + if let currentState = currentState { + toAdd = .init() + for t in newState.providers { + toAdd[t.key] = t.value + } + + for t in currentState.providers { + if let other = toAdd[t.key], let current = currentState.providers[t.key], + current.identifier == other.identifier { + toAdd[t.key] = nil + } else { + entity.remove(t.key) + } + + } + } else { + toAdd = newState.providers + } + + for t in toAdd { + guard let component = toAdd[t.key]?.getComponent() else { + continue + } + entity.assign(component) + } + currentState = newState + } +} diff --git a/Tests/FirebladeECSTests/FSMTests.swift b/Tests/FirebladeECSTests/FSMTests.swift index aeeda68..bfd3833 100644 --- a/Tests/FirebladeECSTests/FSMTests.swift +++ b/Tests/FirebladeECSTests/FSMTests.swift @@ -5,7 +5,7 @@ class ComponentInstanceProviderTests: XCTestCase { func testProviderReturnsTheInstance() { let instance = MockComponent(value: .max) let provider1 = ComponentInstanceProvider(instance: instance) - let providedComponent: MockComponent? = provider1.getComponent() + let providedComponent = provider1.getComponent() as? MockComponent XCTAssertTrue(providedComponent === instance) } @@ -34,14 +34,14 @@ class ComponentInstanceProviderTests: XCTestCase { class ComponentTypeProviderTests: XCTestCase { func testProviderReturnsAnInstanceOfType() { let provider = ComponentTypeProvider(type: MockComponent.self) - let component: MockComponent? = provider.getComponent() + let component = provider.getComponent() as? MockComponent XCTAssertNotNil(component) } func testProviderReturnsNewInstanceEachTime() { let provider = ComponentTypeProvider(type: MockComponent.self) - let component1: MockComponent? = provider.getComponent() - let component2: MockComponent? = provider.getComponent() + let component1 = provider.getComponent() as? MockComponent + let component2 = provider.getComponent() as? MockComponent XCTAssertFalse(component1 === component2) } @@ -78,14 +78,14 @@ class ComponentTypeProviderTests: XCTestCase { class ComponentSingletonProviderTests: XCTestCase { func testProviderReturnsAnInstanceOfType() { let provider = ComponentSingletonProvider(type: MockComponent.self) - let component: MockComponent? = provider.getComponent() + let component = provider.getComponent() as? MockComponent XCTAssertNotNil(component) } func testProviderReturnsSameInstanceEachTime() { let provider = ComponentSingletonProvider(type: MockComponent.self) - let component1: MockComponent? = provider.getComponent() - let component2: MockComponent? = provider.getComponent() + let component1 = provider.getComponent() as? MockComponent + let component2 = provider.getComponent() as? MockComponent XCTAssertTrue(component1 === component2) } @@ -124,7 +124,7 @@ class DynamicComponentProviderTests: XCTestCase { let instance = MockComponent(value: 0) let providerMethod = DynamicComponentProvider.Closure { instance } let provider = DynamicComponentProvider(closure: providerMethod) - let component: MockComponent? = provider.getComponent() + let component = provider.getComponent() as? MockComponent XCTAssertTrue(component === instance) } @@ -153,3 +153,7 @@ class DynamicComponentProviderTests: XCTestCase { } } } + +class EntityStateMachineTests: XCTestCase { + // TODO: +}