fireblade-ecs/Sources/FirebladeECS/Entity+Component.swift

156 lines
6.7 KiB
Swift

//
// Entity+Component.swift
// FirebladeECS
//
// Created by Christian Treffs on 22.10.17.
//
extension Entity {
@inlinable
public func get<C>() -> C? where C: Component {
nexus.get(safe: identifier)
}
@inlinable
public func get<A>(component compType: A.Type = A.self) -> A? where A: Component {
nexus.get(safe: identifier)
}
@inlinable
public func get<A, B>(components _: A.Type, _: B.Type) -> (A?, B?) where A: Component, B: Component {
let compA: A? = get(component: A.self)
let compB: B? = get(component: B.self)
return (compA, compB)
}
// swiftlint:disable large_tuple
@inlinable
public func get<A, B, C>(components _: A.Type, _: B.Type, _: C.Type) -> (A?, B?, C?) where A: Component, B: Component, C: Component {
let compA: A? = get(component: A.self)
let compB: B? = get(component: B.self)
let compC: C? = get(component: C.self)
return (compA, compB, compC)
}
/// Get or set component instance by type via subscript.
///
/// **Behavior:**
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
/// the new instance will be assigned to this entity.
/// - If `Comp` is already assinged to this entity nothing happens.
/// - If `Comp` is set to `nil` and an instance of `Comp` is assigned to this entity,
/// `Comp` will be removed from this entity.
@inlinable
public subscript<Comp>(_ componentType: Comp.Type) -> Comp? where Comp: Component {
get { self.get(component: componentType) }
nonmutating set {
guard let newComponent = newValue else {
self.remove(Comp.self)
return
}
if self.get(component: componentType) === newComponent {
return
}
self.assign(newComponent)
}
}
/// Get the value of a component using the key Path to the property in the component.
///
/// A `Comp` instance must be assigned to this entity!
/// - Parameter componentKeyPath: The `KeyPath` to the property of the given component.
@inlinable
public func get<Comp, Value>(valueAt componentKeyPath: KeyPath<Comp, Value>) -> Value where Comp: Component {
self.get(component: Comp.self)![keyPath: componentKeyPath]
}
/// Get the value of a component using the key Path to the property in the component.
///
/// A `Comp` instance must be assigned to this entity!
/// - Parameter componentKeyPath: The `KeyPath` to the property of the given component.
@inlinable
public func get<Comp, Value>(valueAt componentKeyPath: KeyPath<Comp, Value?>) -> Value? where Comp: Component {
self.get(component: Comp.self)![keyPath: componentKeyPath]
}
/// Get the value of a component using the key Path to the property in the component.
@inlinable
public subscript<Comp, Value>(_ componentKeyPath: KeyPath<Comp, Value>) -> Value where Comp: Component {
self.get(valueAt: componentKeyPath)
}
/// Get the value of a component using the key Path to the property in the component.
@inlinable
public subscript<Comp, Value>(_ componentKeyPath: KeyPath<Comp, Value?>) -> Value? where Comp: Component {
self.get(valueAt: componentKeyPath)
}
/// Set the value of a component using the key path to the property in the component.
///
/// **Behavior:**
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
/// a new instance of `Comp` will be default initialized and `newValue` will be set at the given keyPath.
///
/// - Parameters:
/// - newValue: The value to set.
/// - componentKeyPath: The `ReferenceWritableKeyPath` to the property of the given component.
/// - Returns: Returns true if an action was performed, false otherwise.
@inlinable
@discardableResult
public func set<Comp, Value>(value newValue: Value, for componentKeyPath: ReferenceWritableKeyPath<Comp, Value>) -> Bool where Comp: Component & DefaultInitializable {
guard has(Comp.self) else {
let newInstance = Comp()
newInstance[keyPath: componentKeyPath] = newValue
return nexus.assign(component: newInstance, entityId: identifier)
}
get(component: Comp.self)![keyPath: componentKeyPath] = newValue
return true
}
/// Set the value of a component using the key path to the property in the component.
///
/// **Behavior:**
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
/// a new instance of `Comp` will be default initialized and `newValue` will be set at the given keyPath.
///
/// - Parameters:
/// - newValue: The value to set.
/// - componentKeyPath: The `ReferenceWritableKeyPath` to the property of the given component.
/// - Returns: Returns true if an action was performed, false otherwise.
@inlinable
@discardableResult
public func set<Comp, Value>(value newValue: Value?, for componentKeyPath: ReferenceWritableKeyPath<Comp, Value?>) -> Bool where Comp: Component & DefaultInitializable {
guard has(Comp.self) else {
let newInstance = Comp()
newInstance[keyPath: componentKeyPath] = newValue
return nexus.assign(component: newInstance, entityId: identifier)
}
get(component: Comp.self)![keyPath: componentKeyPath] = newValue
return true
}
/// Set the value of a component using the key path to the property in the component.
///
/// **Behavior:**
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
/// a new instance of `Comp` will be default initialized and `newValue` will be set at the given keyPath.
@inlinable
public subscript<Comp, Value>(_ componentKeyPath: ReferenceWritableKeyPath<Comp, Value>) -> Value where Comp: Component & DefaultInitializable {
get { self.get(valueAt: componentKeyPath) }
nonmutating set { self.set(value: newValue, for: componentKeyPath) }
}
/// Set the value of a component using the key path to the property in the component.
///
/// **Behavior:**
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
/// a new instance of `Comp` will be default initialized and `newValue` will be set at the given keyPath.
@inlinable
public subscript<Comp, Value>(_ componentKeyPath: ReferenceWritableKeyPath<Comp, Value?>) -> Value? where Comp: Component & DefaultInitializable {
get { self.get(valueAt: componentKeyPath) }
nonmutating set { self.set(value: newValue, for: componentKeyPath) }
}
}