LCOV - code coverage report
Current view: top level - lib/src - entity.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 35 62 56.5 %
Date: 2022-01-24 13:22:55 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:equatable/equatable.dart';
       2             : 
       3             : import 'component.dart';
       4             : import 'extensions/extensions.dart';
       5             : import 'system.dart';
       6             : 
       7             : /// A container that holds components essentially.
       8             : class Entity with EquatableMixin {
       9           1 :   Entity(this.guid, this.system);
      10             : 
      11             :   final String guid;
      12             : 
      13             :   /// The system where this entity is contained.
      14             :   final EntitySystem system;
      15             : 
      16             :   // Holding all components map through their type.
      17             :   final components = <Type, Component>{}.bs;
      18             : 
      19             :   /// Used internally for verifying Entity has not been destroyed before
      20             :   /// mutating any values.
      21             :   final isDestroyed = false.bs;
      22             : 
      23             :   /// Returns a matching component from type T.
      24           1 :   T get<T extends Component>() {
      25           3 :     var component = components()[T];
      26             :     return component as T;
      27             :   }
      28             : 
      29             :   /// Adds component to the entity.
      30           1 :   Entity operator +(Component component) {
      31           2 :     assert(!isDestroyed(), 'Tried adding component to destroyed entity: ${toJson()}');
      32           3 :     final copy = Map<Type, Component>.from(components());
      33             :     // Create new list and then add.
      34           3 :     copy[component.runtimeType] = component..ref = this;
      35           2 :     components(copy);
      36             :     // Call onAdded.
      37           1 :     component.onAdded();
      38             :     return this;
      39             :   }
      40             : 
      41             :   /// For cascade
      42           0 :   void set(Component component) {
      43           0 :     var _ = this + component;
      44             :   }
      45             : 
      46             :   /// Removes component from the entity.
      47           0 :   Entity operator -(Type t) {
      48           0 :     assert(isDestroyed(), 'Tried removing component from destroyed entity: ${toJson()}');
      49           0 :     var component = components()[t];
      50             :     if (component != null) {
      51             :       // Call onRemoved.
      52           0 :       component.onRemoved();
      53           0 :       final copy = Map<Type, Component>.from(components());
      54             :       // Created copy and removed this component.
      55           0 :       copy.remove(t);
      56             :       // Set value of the subject.
      57           0 :       components.add(copy);
      58             :     }
      59             : 
      60             :     return this;
      61             :   }
      62             : 
      63             :   /// For cascade
      64           0 :   void remove<T extends Component>() {
      65           0 :     var _ = this - T;
      66             :   }
      67             : 
      68             :   /// If entity has a component of type T
      69           0 :   bool has<T extends Component>() {
      70           0 :     return components().containsKey(T);
      71             :   }
      72             : 
      73             :   /// Checks if entity has a component of the type of the given object.
      74           1 :   bool hasComponent(Type type) {
      75           3 :     return components().containsKey(type);
      76             :   }
      77             : 
      78             :   /// Destroy an entity which will lead to following steps:
      79             :   /// 1. Remove all components
      80             :   /// 2. Remove from the system
      81             :   /// 3. Set `isDestroyed` to `true`
      82           0 :   void destroy() {
      83           0 :     for (var comp in components().values.toList()) {
      84           0 :       var _ = this - comp.runtimeType;
      85             :     }
      86           0 :     system.destroyed(this);
      87             :     // Add to sink that we are destroyed.
      88           0 :     isDestroyed(true);
      89           0 :     isDestroyed.close();
      90             :   }
      91             : 
      92             :   /// Compares the component list between entities and returns a list of
      93             :   /// types of components that "a" has that "b" does not.
      94             :   ///
      95             :   /// E.g.
      96             :   ///
      97             :   /// final compared = a.componentDiff(b);
      98           1 :   Set<Type> componentDiff(Entity e) {
      99           4 :     final ecomps = e.components().keys.toSet();
     100           5 :     return components().keys.toSet().difference(ecomps);
     101             :   }
     102             : 
     103           0 :   Map<String, dynamic> toJson() {
     104           0 :     return system.entityToJson(this);
     105             :   }
     106             : 
     107           0 :   static Entity fromJson(Map<String, dynamic> json, EntitySystem system) {
     108           0 :     return system.createFromJson(json);
     109             :   }
     110             : 
     111           1 :   @override
     112           3 :   List<Object> get props => [guid, components];
     113             : }
     114             : 
     115             : /// A class that describe a way to filter entities.
     116             : ///
     117             : /// There are 3 properties, each with it's own functionality.
     118             : /// all: The entity MUST contain all of these components.
     119             : /// any: The entity MUST contain at *least* one of these components.
     120             : /// inverse: Flips the logic, so MUST becomes MUST NOT.
     121             : ///
     122             : /// var matcher = EntityMatcher(all: [CountComponent, PriceComponent])
     123             : /// This matcher would match any entity that has both the CountComponent AND the
     124             : /// Price Component.
     125             : ///
     126             : /// var matcher = EntityMatcher(all: [CartComponent], any: [PriceComponent, CouponComponent])
     127             : /// This matcher would match any entity that has the CartComponent, and has at least one between
     128             : /// the PriceComponent and CouponComponent.
     129             : ///
     130             : /// var matcher = EntityMatcher(all: [DiscontinuedComponent, PriceComponent], reverse: true)
     131             : /// This matcher would match any entity that DOES NOT have the DiscontinuedComponent and PriceComponent.
     132             : ///
     133             : /// var matcher = EntityMatcher(any: [DiscontinuedComponent, OutOfStockComponent, DisabledComponent], reverse: true)
     134             : /// This matcher would match any entity that DOES NOT have any one of these components.
     135             : class EntityMatcher extends Equatable {
     136           1 :   const EntityMatcher({this.all = const {}, this.any = const {}, this.reverse = false});
     137             : 
     138             :   final Set<Type> all;
     139             :   final Set<Type> any;
     140             :   final bool reverse;
     141             : 
     142           0 :   bool contains(Type type) {
     143           0 :     return all.contains(type) || any.contains(type);
     144             :   }
     145             : 
     146           1 :   bool matches(Entity entity) {
     147           4 :     if (any.isEmpty && all.isEmpty) {
     148             :       return true;
     149             :     }
     150             : 
     151           1 :     final anyMatched = matchesAny(entity);
     152           1 :     final allMatched = matchesAll(entity);
     153             : 
     154             :     return anyMatched && allMatched;
     155             :   }
     156             : 
     157           1 :   bool matchesAll(Entity entity) {
     158           2 :     if (all.isEmpty) return true;
     159             :     // If reverse, we want to make sure we contain NONE of the components
     160             :     // in all.
     161           1 :     if (reverse) {
     162           2 :       for (final t in all) {
     163           1 :         if (entity.hasComponent(t)) {
     164             :           return false;
     165             :         }
     166             :       }
     167             :     } else {
     168           2 :       for (final t in all) {
     169           1 :         if (!entity.hasComponent(t)) {
     170             :           return false;
     171             :         }
     172             :       }
     173             :     }
     174             : 
     175             :     return true;
     176             :   }
     177             : 
     178           1 :   bool matchesAny(Entity entity) {
     179           2 :     if (any.isEmpty) return true;
     180             :     // If reversed, we match if it doesn't contain any of the any components
     181           1 :     if (reverse) {
     182           2 :       for (var t in any) {
     183           2 :         if (entity.hasComponent(t) == false) {
     184             :           return true;
     185             :         }
     186             :       }
     187             :     } else {
     188           2 :       for (var t in any) {
     189           1 :         if (entity.hasComponent(t)) {
     190             :           return true;
     191             :         }
     192             :       }
     193             :     }
     194             : 
     195             :     return false;
     196             :   }
     197             : 
     198           0 :   @override
     199           0 :   List<Object?> get props => [all, any, reverse];
     200             : }

Generated by: LCOV version 1.15