koota 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -398,7 +398,7 @@ world.set(Time, { current: performance.now() })
398
398
 
399
399
  ### Select traits on queries for updates
400
400
 
401
- Query filters entity results and `select` is used to choose what traits are fetched for `updateEach` and `useStore`. This can be useful if your query is wider than the data you want to modify.
401
+ Query filters entity results and `select` is used to choose what traits are fetched for `updateEach` and `useStores`. This can be useful if your query is wider than the data you want to modify.
402
402
 
403
403
  ```js
404
404
  // The query finds all entities with Position, Velocity and Mass
@@ -414,11 +414,11 @@ world.query(Position, Velocity, Mass)
414
414
 
415
415
  ### Modifying trait stores directly
416
416
 
417
- For performance-critical operations, you can modify trait stores directly using the `useStore` hook. This approach bypasses some of the safety checks and event triggers, so use it with caution. All stores are structure of arrays for performance purposes.
417
+ For performance-critical operations, you can modify trait stores directly using the `useStores` hook. This approach bypasses some of the safety checks and event triggers, so use it with caution. All stores are structure of arrays for performance purposes.
418
418
 
419
419
  ```js
420
420
  // Returns the SoA stores
421
- world.query(Position, Velocity).useStore(([position, velocity], entities) => {
421
+ world.query(Position, Velocity).useStores(([position, velocity], entities) => {
422
422
  // Write our own loop over the stores
423
423
  for (let i = 0; i < entities.length; i++) {
424
424
  // Get the entity ID to use as the array index
@@ -748,7 +748,7 @@ const Attacker = trait<Pick<AttackerSchema, keyof AttackerSchema>>({
748
748
 
749
749
  #### Accessing the store directly
750
750
 
751
- The store can be accessed with `getStore`, but this low-level access is risky as it bypasses Koota's guard rails. However, this can be useful for debugging where direct introspection of the store is needed. For direct store mutations, use the [`useStore` API](#modifying-trait-stores-direclty) instead.
751
+ The store can be accessed with `getStore`, but this low-level access is risky as it bypasses Koota's guard rails. However, this can be useful for debugging where direct introspection of the store is needed. For direct store mutations, use the [`useStores` API](#modifying-trait-stores-direclty) instead.
752
752
 
753
753
  ```js
754
754
  // Returns SoA or AoS depending on the trait
@@ -913,6 +913,40 @@ return (
913
913
  )
914
914
  ```
915
915
 
916
+ ### `useTag`
917
+
918
+ Observes an entity, or world, for a tag and reactively updates when it is added or removed. Returns `true` when the tag is present or `false` when absent. Use this instead of `useTrait` for tags. For tracking the presence of non-tag traits, use `useHas`.
919
+
920
+ ```js
921
+ const IsActive = trait()
922
+
923
+ function ActiveIndicator({ entity }) {
924
+ // Returns true if the entity has the tag, false otherwise
925
+ const isActive = useTag(entity, IsActive)
926
+
927
+ if (!isActive) return null
928
+
929
+ return <div>🟢 Active</div>
930
+ }
931
+ ```
932
+
933
+ ### `useHas`
934
+
935
+ Observes an entity, or world, for any trait and reactively updates when it is added or removed. Returns `true` when the trait is present or `false` when absent. Unlike `useTrait`, this only tracks presence and not the trait's value.
936
+
937
+ ```js
938
+ const Health = trait({ amount: 100 })
939
+
940
+ function HealthIndicator({ entity }) {
941
+ // Returns true if the entity has the trait, false otherwise
942
+ const hasHealth = useHas(entity, Health)
943
+
944
+ if (!hasHealth) return null
945
+
946
+ return <div>❤️ Has Health</div>
947
+ }
948
+ ```
949
+
916
950
  ### `useTraitEffect`
917
951
 
918
952
  Subscribes a callback to a trait on an entity. This callback fires as an effect whenever it is added, removed or changes value without rerendering.
@@ -40,6 +40,18 @@ function unpackEntity(entity) {
40
40
  entityId: entity & ENTITY_ID_MASK
41
41
  };
42
42
  }
43
+ var getEntityId = (
44
+ /* @inline @pure */
45
+ (entity) => entity & ENTITY_ID_MASK
46
+ );
47
+ var getEntityWorldId = (
48
+ /* @inline @pure */
49
+ (entity) => entity >>> WORLD_ID_SHIFT
50
+ );
51
+ var getEntityGeneration = (
52
+ /* @inline @pure */
53
+ (entity) => entity >>> GENERATION_SHIFT & GENERATION_MASK
54
+ );
43
55
  var incrementGeneration = (entity) => entity & ~(GENERATION_MASK << GENERATION_SHIFT) | // Clear current generation bits
44
56
  ((entity >>> GENERATION_SHIFT & GENERATION_MASK) + 1 & GENERATION_MASK) << GENERATION_SHIFT;
45
57
 
@@ -139,25 +151,11 @@ function createChanged() {
139
151
  }
140
152
  function setChanged(world, entity, trait2) {
141
153
  const ctx = world[$internal];
142
- let result_hasTrait_0_$f;
143
- const ctx_0_$f = world[$internal];
144
- const data_0_$f = ctx_0_$f.traitData.get(trait2);
145
- if (!data_0_$f) {
146
- result_hasTrait_0_$f = false;
147
- } else {
148
- const {
149
- generationId,
150
- bitflag
151
- } = data_0_$f;
152
- const eid_0_$f = entity & ENTITY_ID_MASK;
153
- const mask_0_$f = ctx_0_$f.entityMasks[generationId][eid_0_$f];
154
- result_hasTrait_0_$f = (mask_0_$f & bitflag) === bitflag;
155
- }
156
- if (!result_hasTrait_0_$f) return;
154
+ if (!hasTrait(world, entity, trait2)) return;
157
155
  if (!ctx.traitData.has(trait2)) registerTrait(world, trait2);
158
156
  const data = ctx.traitData.get(trait2);
159
157
  for (const changedMask of ctx.changedMasks.values()) {
160
- const eid = entity & ENTITY_ID_MASK;
158
+ const eid = getEntityId(entity);
161
159
  const data2 = ctx.traitData.get(trait2);
162
160
  const {
163
161
  generationId,
@@ -350,21 +348,7 @@ function addTrait(world, entity, ...traits) {
350
348
  } else {
351
349
  trait2 = traits[i];
352
350
  }
353
- let result_hasTrait_0_$f;
354
- const ctx_0_$f = world[$internal];
355
- const data_0_$f = ctx_0_$f.traitData.get(trait2);
356
- if (!data_0_$f) {
357
- result_hasTrait_0_$f = false;
358
- } else {
359
- const {
360
- generationId: generationId2,
361
- bitflag: bitflag2
362
- } = data_0_$f;
363
- const eid_0_$f = entity & ENTITY_ID_MASK;
364
- const mask_0_$f = ctx_0_$f.entityMasks[generationId2][eid_0_$f];
365
- result_hasTrait_0_$f = (mask_0_$f & bitflag2) === bitflag2;
366
- }
367
- if (result_hasTrait_0_$f) continue;
351
+ if (hasTrait(world, entity, trait2)) continue;
368
352
  const traitCtx = trait2[$internal];
369
353
  if (!ctx.traitData.has(trait2)) registerTrait(world, trait2);
370
354
  const data = ctx.traitData.get(trait2);
@@ -373,7 +357,7 @@ function addTrait(world, entity, ...traits) {
373
357
  bitflag,
374
358
  queries
375
359
  } = data;
376
- const eid = entity & ENTITY_ID_MASK;
360
+ const eid = getEntityId(entity);
377
361
  ctx.entityMasks[generationId][eid] |= bitflag;
378
362
  for (const dirtyMask of ctx.dirtyMasks.values()) {
379
363
  if (!dirtyMask[generationId]) dirtyMask[generationId] = [];
@@ -429,21 +413,7 @@ function removeTrait(world, entity, ...traits) {
429
413
  for (let i = 0; i < traits.length; i++) {
430
414
  const trait2 = traits[i];
431
415
  const traitCtx = trait2[$internal];
432
- let result_hasTrait_3_$f;
433
- const ctx_3_$f = world[$internal];
434
- const data_3_$f = ctx_3_$f.traitData.get(trait2);
435
- if (!data_3_$f) {
436
- result_hasTrait_3_$f = false;
437
- } else {
438
- const {
439
- generationId: generationId2,
440
- bitflag: bitflag2
441
- } = data_3_$f;
442
- const eid_3_$f = entity & ENTITY_ID_MASK;
443
- const mask_3_$f = ctx_3_$f.entityMasks[generationId2][eid_3_$f];
444
- result_hasTrait_3_$f = (mask_3_$f & bitflag2) === bitflag2;
445
- }
446
- if (!result_hasTrait_3_$f) continue;
416
+ if (!hasTrait(world, entity, trait2)) continue;
447
417
  const data = ctx.traitData.get(trait2);
448
418
  const {
449
419
  generationId,
@@ -453,7 +423,7 @@ function removeTrait(world, entity, ...traits) {
453
423
  for (const sub of data.removeSubscriptions) {
454
424
  sub(entity);
455
425
  }
456
- const eid = entity & ENTITY_ID_MASK;
426
+ const eid = getEntityId(entity);
457
427
  ctx.entityMasks[generationId][eid] &= ~bitflag;
458
428
  for (const dirtyMask of ctx.dirtyMasks.values()) {
459
429
  dirtyMask[generationId][eid] |= bitflag;
@@ -472,7 +442,9 @@ function removeTrait(world, entity, ...traits) {
472
442
  ctx.relationTargetEntities.delete(entity);
473
443
  }
474
444
  const target = traitCtx.pairTarget;
475
- removeTrait(world, entity, Pair(Wildcard, target));
445
+ if (!hasRelationToTarget(world, entity, target)) {
446
+ removeTrait(world, entity, Pair(Wildcard, target));
447
+ }
476
448
  const relation2 = traitCtx.relation;
477
449
  const otherTargets = getRelationTargets(world, relation2, entity);
478
450
  if (otherTargets.length === 0) {
@@ -488,6 +460,18 @@ function removeTrait(world, entity, ...traits) {
488
460
  }
489
461
  }
490
462
  }
463
+ function hasTrait(world, entity, trait2) {
464
+ const ctx = world[$internal];
465
+ const data = ctx.traitData.get(trait2);
466
+ if (!data) return false;
467
+ const {
468
+ generationId,
469
+ bitflag
470
+ } = data;
471
+ const eid = getEntityId(entity);
472
+ const mask = ctx.entityMasks[generationId][eid];
473
+ return (mask & bitflag) === bitflag;
474
+ }
491
475
  function getStore(world, trait2) {
492
476
  const ctx = world[$internal];
493
477
  const data = ctx.traitData.get(trait2);
@@ -495,34 +479,18 @@ function getStore(world, trait2) {
495
479
  }
496
480
  function setTrait(world, entity, trait2, value, triggerChanged = true) {
497
481
  const ctx = trait2[$internal];
498
- const ctx_7_$f = world[$internal];
499
- const data_7_$f = ctx_7_$f.traitData.get(trait2);
500
- const store = data_7_$f.store;
501
- const index = entity & ENTITY_ID_MASK;
482
+ const store = getStore(world, trait2);
483
+ const index = getEntityId(entity);
502
484
  value instanceof Function && (value = value(ctx.get(index, store)));
503
485
  ctx.set(index, store, value);
504
486
  triggerChanged && setChanged(world, entity, trait2);
505
487
  }
506
488
  function getTrait(world, entity, trait2) {
507
- let result_hasTrait_9_$f;
508
- const ctx_9_$f = world[$internal];
509
- const data_9_$f = ctx_9_$f.traitData.get(trait2);
510
- if (!data_9_$f) {
511
- result_hasTrait_9_$f = false;
512
- } else {
513
- const {
514
- generationId,
515
- bitflag
516
- } = data_9_$f;
517
- const eid_9_$f = entity & ENTITY_ID_MASK;
518
- const mask_9_$f = ctx_9_$f.entityMasks[generationId][eid_9_$f];
519
- result_hasTrait_9_$f = (mask_9_$f & bitflag) === bitflag;
520
- }
521
- const result = result_hasTrait_9_$f;
489
+ const result = hasTrait(world, entity, trait2);
522
490
  if (!result) return void 0;
523
491
  const traitCtx = trait2[$internal];
524
- const store = data_9_$f.store;
525
- return traitCtx.get(entity & ENTITY_ID_MASK, store);
492
+ const store = getStore(world, trait2);
493
+ return traitCtx.get(getEntityId(entity), store);
526
494
  }
527
495
 
528
496
  // ../core/src/relation/relation.ts
@@ -568,6 +536,18 @@ var getRelationTargets = (world, relation2, entity) => {
568
536
  }
569
537
  return targets;
570
538
  };
539
+ var hasRelationToTarget = (world, entity, target) => {
540
+ const ctx = world[$internal];
541
+ const traits = ctx.entityTraits.get(entity);
542
+ if (!traits) return false;
543
+ for (const trait2 of traits) {
544
+ const traitCtx = trait2[$internal];
545
+ if (traitCtx.isPairTrait && traitCtx.pairTarget === target && traitCtx.relation !== Wildcard) {
546
+ return true;
547
+ }
548
+ }
549
+ return false;
550
+ };
571
551
  var Pair = (relation2, target) => {
572
552
  if (relation2 === void 0) throw Error("Relation is undefined");
573
553
  if (target === void 0) throw Error("Relation target is undefined");
@@ -673,28 +653,11 @@ function createQueryResult(world, entities, query) {
673
653
  const atomicSnapshots = [];
674
654
  const trackedIndices = [];
675
655
  const untrackedIndices = [];
676
- for (let i_0_$f = 0; i_0_$f < traits.length; i_0_$f++) {
677
- const trait_0_$f = traits[i_0_$f];
678
- const hasTracked_0_$f = world[$internal].trackedTraits.has(trait_0_$f);
679
- const hasChanged_0_$f = query.hasChangedModifiers && query.changedTraits.has(trait_0_$f);
680
- if (hasTracked_0_$f || hasChanged_0_$f) {
681
- trackedIndices.push(i_0_$f);
682
- } else {
683
- untrackedIndices.push(i_0_$f);
684
- }
685
- }
656
+ getTrackedTraits(traits, world, query, trackedIndices, untrackedIndices);
686
657
  for (let i = 0; i < entities.length; i++) {
687
658
  const entity = entities[i];
688
- const eid = entity & ENTITY_ID_MASK;
689
- for (let j_2_$f = 0; j_2_$f < traits.length; j_2_$f++) {
690
- const trait_2_$f = traits[j_2_$f];
691
- const ctx_2_$f = trait_2_$f[$internal];
692
- const value_2_$f = ctx_2_$f.get(eid, stores[j_2_$f]);
693
- state[j_2_$f] = value_2_$f;
694
- atomicSnapshots[j_2_$f] = ctx_2_$f.type === "aos" ? {
695
- ...value_2_$f
696
- } : null;
697
- }
659
+ const eid = getEntityId(entity);
660
+ createSnapshotsWithAtomic(eid, traits, stores, state, atomicSnapshots);
698
661
  callback(state, entity, i);
699
662
  if (!world.has(entity)) continue;
700
663
  for (let j = 0; j < trackedIndices.length; j++) {
@@ -731,16 +694,8 @@ function createQueryResult(world, entities, query) {
731
694
  const atomicSnapshots = [];
732
695
  for (let i = 0; i < entities.length; i++) {
733
696
  const entity = entities[i];
734
- const eid = entity & ENTITY_ID_MASK;
735
- for (let j_4_$f = 0; j_4_$f < traits.length; j_4_$f++) {
736
- const trait_4_$f = traits[j_4_$f];
737
- const ctx_4_$f = trait_4_$f[$internal];
738
- const value_4_$f = ctx_4_$f.get(eid, stores[j_4_$f]);
739
- state[j_4_$f] = value_4_$f;
740
- atomicSnapshots[j_4_$f] = ctx_4_$f.type === "aos" ? {
741
- ...value_4_$f
742
- } : null;
743
- }
697
+ const eid = getEntityId(entity);
698
+ createSnapshotsWithAtomic(eid, traits, stores, state, atomicSnapshots);
744
699
  callback(state, entity, i);
745
700
  if (!world.has(entity)) continue;
746
701
  for (let j = 0; j < traits.length; j++) {
@@ -766,13 +721,8 @@ function createQueryResult(world, entities, query) {
766
721
  } else if (options.changeDetection === "never") {
767
722
  for (let i = 0; i < entities.length; i++) {
768
723
  const entity = entities[i];
769
- const eid = entity & ENTITY_ID_MASK;
770
- for (let i_6_$f = 0; i_6_$f < traits.length; i_6_$f++) {
771
- const trait_6_$f = traits[i_6_$f];
772
- const ctx_6_$f = trait_6_$f[$internal];
773
- const value_6_$f = ctx_6_$f.get(eid, stores[i_6_$f]);
774
- state[i_6_$f] = value_6_$f;
775
- }
724
+ const eid = getEntityId(entity);
725
+ createSnapshots(eid, traits, stores, state);
776
726
  callback(state, entity, i);
777
727
  if (!world.has(entity)) continue;
778
728
  for (let j = 0; j < traits.length; j++) {
@@ -791,44 +741,62 @@ function createQueryResult(world, entities, query) {
791
741
  select(...params) {
792
742
  traits.length = 0;
793
743
  stores.length = 0;
794
- for (let i_7_$f = 0; i_7_$f < params.length; i_7_$f++) {
795
- const param_7_$f = params[i_7_$f];
796
- if (isModifier(param_7_$f)) {
797
- if (param_7_$f.type === "not") {
798
- continue;
799
- } else {
800
- const modifierTraits_7_$f = param_7_$f.traits;
801
- for (const trait_7_$f of modifierTraits_7_$f) {
802
- if (trait_7_$f[$internal].isTag) {
803
- continue;
804
- } else {
805
- traits.push(trait_7_$f);
806
- const ctx_8_$f = world[$internal];
807
- const data_8_$f = ctx_8_$f.traitData.get(trait_7_$f);
808
- stores.push(data_8_$f.store);
809
- }
810
- }
811
- }
812
- } else {
813
- if (param_7_$f[$internal].isTag) {
814
- continue;
815
- } else {
816
- traits.push(param_7_$f);
817
- const ctx_9_$f = world[$internal];
818
- const data_9_$f = ctx_9_$f.traitData.get(param_7_$f);
819
- stores.push(data_9_$f.store);
820
- }
821
- }
822
- }
744
+ getQueryStores(params, traits, stores, world);
823
745
  return results;
824
746
  },
825
- sort(callback = (a, b) => (a & ENTITY_ID_MASK) - (b & ENTITY_ID_MASK)) {
747
+ sort(callback = (a, b) => getEntityId(a) - getEntityId(b)) {
826
748
  Array.prototype.sort.call(entities, callback);
827
749
  return results;
828
750
  }
829
751
  });
830
752
  return results;
831
753
  }
754
+ function getTrackedTraits(traits, world, query, trackedIndices, untrackedIndices) {
755
+ for (let i = 0; i < traits.length; i++) {
756
+ const trait2 = traits[i];
757
+ const hasTracked = world[$internal].trackedTraits.has(trait2);
758
+ const hasChanged = query.hasChangedModifiers && query.changedTraits.has(trait2);
759
+ if (hasTracked || hasChanged) trackedIndices.push(i);
760
+ else untrackedIndices.push(i);
761
+ }
762
+ }
763
+ function createSnapshots(entityId, traits, stores, state) {
764
+ for (let i = 0; i < traits.length; i++) {
765
+ const trait2 = traits[i];
766
+ const ctx = trait2[$internal];
767
+ const value = ctx.get(entityId, stores[i]);
768
+ state[i] = value;
769
+ }
770
+ }
771
+ function createSnapshotsWithAtomic(entityId, traits, stores, state, atomicSnapshots) {
772
+ for (let j = 0; j < traits.length; j++) {
773
+ const trait2 = traits[j];
774
+ const ctx = trait2[$internal];
775
+ const value = ctx.get(entityId, stores[j]);
776
+ state[j] = value;
777
+ atomicSnapshots[j] = ctx.type === "aos" ? {
778
+ ...value
779
+ } : null;
780
+ }
781
+ }
782
+ function getQueryStores(params, traits, stores, world) {
783
+ for (let i = 0; i < params.length; i++) {
784
+ const param = params[i];
785
+ if (isModifier(param)) {
786
+ if (param.type === "not") continue;
787
+ const modifierTraits = param.traits;
788
+ for (const trait2 of modifierTraits) {
789
+ if (trait2[$internal].isTag) continue;
790
+ traits.push(trait2);
791
+ stores.push(getStore(world, trait2));
792
+ }
793
+ } else {
794
+ if (param[$internal].isTag) continue;
795
+ traits.push(param);
796
+ stores.push(getStore(world, param));
797
+ }
798
+ }
799
+ }
832
800
  function createEmptyQueryResult() {
833
801
  const results = Object.assign([], {
834
802
  updateEach: () => results,
@@ -869,7 +837,7 @@ var allocateEntity = (index) => {
869
837
  if (index.aliveCount < index.dense.length) {
870
838
  const recycledEntity = incrementGeneration(index.dense[index.aliveCount]);
871
839
  index.dense[index.aliveCount] = recycledEntity;
872
- index.sparse[recycledEntity & ENTITY_ID_MASK] = index.aliveCount;
840
+ index.sparse[getEntityId(recycledEntity)] = index.aliveCount;
873
841
  index.aliveCount++;
874
842
  return recycledEntity;
875
843
  }
@@ -881,92 +849,69 @@ var allocateEntity = (index) => {
881
849
  return entity;
882
850
  };
883
851
  var releaseEntity = (index, entity) => {
884
- const id = entity & ENTITY_ID_MASK;
852
+ const id = getEntityId(entity);
885
853
  const denseIndex = index.sparse[id];
886
854
  if (denseIndex === void 0 || denseIndex >= index.aliveCount) return;
887
855
  const lastIndex = index.aliveCount - 1;
888
856
  const lastEntity = index.dense[lastIndex];
889
- const lastId = lastEntity & ENTITY_ID_MASK;
857
+ const lastId = getEntityId(lastEntity);
890
858
  index.sparse[lastId] = denseIndex;
891
859
  index.dense[denseIndex] = lastEntity;
892
860
  index.sparse[id] = lastIndex;
893
861
  index.dense[lastIndex] = entity;
894
862
  index.aliveCount--;
895
863
  };
864
+ var isEntityAlive = (
865
+ /* @inline @pure */
866
+ (index, entity) => {
867
+ const denseIndex = index.sparse[getEntityId(entity)];
868
+ if (denseIndex === void 0 || denseIndex >= index.aliveCount) return false;
869
+ const storedEntity = index.dense[denseIndex];
870
+ return getEntityGeneration(entity) === getEntityGeneration(storedEntity) && getEntityWorldId(entity) === index.worldId;
871
+ }
872
+ );
896
873
  var getAliveEntities = (index) => {
897
874
  return index.dense.slice(0, index.aliveCount);
898
875
  };
899
876
 
900
877
  // ../core/src/entity/entity-methods-patch.ts
901
878
  Number.prototype.add = function(...traits) {
902
- const worldId_0_$f = this >>> WORLD_ID_SHIFT;
903
- return addTrait(universe.worlds[worldId_0_$f], this, ...traits);
879
+ return addTrait(getEntityWorld(this), this, ...traits);
904
880
  };
905
881
  Number.prototype.remove = function(...traits) {
906
- const worldId_1_$f = this >>> WORLD_ID_SHIFT;
907
- return removeTrait(universe.worlds[worldId_1_$f], this, ...traits);
882
+ return removeTrait(getEntityWorld(this), this, ...traits);
908
883
  };
909
884
  Number.prototype.has = function(trait2) {
910
- let result_hasTrait_2_$f;
911
- const worldId_3_$f = this >>> WORLD_ID_SHIFT;
912
- const ctx_2_$f = universe.worlds[worldId_3_$f][$internal];
913
- const data_2_$f = ctx_2_$f.traitData.get(trait2);
914
- if (!data_2_$f) {
915
- result_hasTrait_2_$f = false;
916
- } else {
917
- const {
918
- generationId,
919
- bitflag
920
- } = data_2_$f;
921
- const eid_2_$f = this & ENTITY_ID_MASK;
922
- const mask_2_$f = ctx_2_$f.entityMasks[generationId][eid_2_$f];
923
- result_hasTrait_2_$f = (mask_2_$f & bitflag) === bitflag;
924
- }
925
- return result_hasTrait_2_$f;
885
+ return hasTrait(getEntityWorld(this), this, trait2);
926
886
  };
927
887
  Number.prototype.destroy = function() {
928
- const worldId_4_$f = this >>> WORLD_ID_SHIFT;
929
- return destroyEntity(universe.worlds[worldId_4_$f], this);
888
+ return destroyEntity(getEntityWorld(this), this);
930
889
  };
931
890
  Number.prototype.changed = function(trait2) {
932
- const worldId_5_$f = this >>> WORLD_ID_SHIFT;
933
- return setChanged(universe.worlds[worldId_5_$f], this, trait2);
891
+ return setChanged(getEntityWorld(this), this, trait2);
934
892
  };
935
893
  Number.prototype.get = function(trait2) {
936
- const worldId_6_$f = this >>> WORLD_ID_SHIFT;
937
- return getTrait(universe.worlds[worldId_6_$f], this, trait2);
894
+ return getTrait(getEntityWorld(this), this, trait2);
938
895
  };
939
896
  Number.prototype.set = function(trait2, value, triggerChanged = true) {
940
- const worldId_7_$f = this >>> WORLD_ID_SHIFT;
941
- setTrait(universe.worlds[worldId_7_$f], this, trait2, value, triggerChanged);
897
+ setTrait(getEntityWorld(this), this, trait2, value, triggerChanged);
942
898
  };
943
899
  Number.prototype.targetsFor = function(relation2) {
944
- const worldId_8_$f = this >>> WORLD_ID_SHIFT;
945
- return getRelationTargets(universe.worlds[worldId_8_$f], relation2, this);
900
+ return getRelationTargets(getEntityWorld(this), relation2, this);
946
901
  };
947
902
  Number.prototype.targetFor = function(relation2) {
948
- const worldId_9_$f = this >>> WORLD_ID_SHIFT;
949
- return getRelationTargets(universe.worlds[worldId_9_$f], relation2, this)[0];
903
+ return getRelationTargets(getEntityWorld(this), relation2, this)[0];
950
904
  };
951
905
  Number.prototype.id = function() {
952
- return this & ENTITY_ID_MASK;
906
+ return getEntityId(this);
953
907
  };
954
908
  Number.prototype.generation = function() {
955
- return this >>> GENERATION_SHIFT & GENERATION_MASK;
909
+ return getEntityGeneration(this);
956
910
  };
957
911
  Number.prototype.isAlive = function() {
958
- const worldId_12_$f = this >>> WORLD_ID_SHIFT;
959
- const world = universe.worlds[worldId_12_$f];
912
+ const world = getEntityWorld(this);
960
913
  const entityIndex = world[$internal].entityIndex;
961
- let result_isEntityAlive_13_$f;
962
- const denseIndex_13_$f = entityIndex.sparse[this & ENTITY_ID_MASK];
963
- if (denseIndex_13_$f === void 0 || denseIndex_13_$f >= entityIndex.aliveCount) {
964
- result_isEntityAlive_13_$f = false;
965
- } else {
966
- const storedEntity_13_$f = entityIndex.dense[denseIndex_13_$f];
967
- result_isEntityAlive_13_$f = (this >>> GENERATION_SHIFT & GENERATION_MASK) === (storedEntity_13_$f >>> GENERATION_SHIFT & GENERATION_MASK) && this >>> WORLD_ID_SHIFT === entityIndex.worldId;
968
- }
969
- return result_isEntityAlive_13_$f;
914
+ return isEntityAlive(entityIndex, this);
970
915
  };
971
916
 
972
917
  // ../core/src/entity/entity.ts
@@ -976,7 +921,7 @@ function createEntity(world, ...traits) {
976
921
  for (const query of ctx.notQueries) {
977
922
  const match = query.check(world, entity);
978
923
  if (match) query.add(entity);
979
- query.resetTrackingBitmasks(entity & ENTITY_ID_MASK);
924
+ query.resetTrackingBitmasks(getEntityId(entity));
980
925
  }
981
926
  ctx.entityTraits.set(entity, /* @__PURE__ */ new Set());
982
927
  addTrait(world, entity, ...traits);
@@ -1023,12 +968,16 @@ function destroyEntity(world, entity) {
1023
968
  const allQuery = ctx.queriesHashMap.get("");
1024
969
  if (allQuery) allQuery.remove(world, currentEntity);
1025
970
  ctx.entityTraits.delete(entity);
1026
- const eid = currentEntity & ENTITY_ID_MASK;
971
+ const eid = getEntityId(currentEntity);
1027
972
  for (let i = 0; i < ctx.entityMasks.length; i++) {
1028
973
  ctx.entityMasks[i][eid] = 0;
1029
974
  }
1030
975
  }
1031
976
  }
977
+ function getEntityWorld(entity) {
978
+ const worldId = getEntityWorldId(entity);
979
+ return universe.worlds[worldId];
980
+ }
1032
981
 
1033
982
  // ../core/src/world/world.ts
1034
983
  var World = class {
@@ -1093,29 +1042,7 @@ var World = class {
1093
1042
  return createEntity(this, ...traits);
1094
1043
  }
1095
1044
  has(target) {
1096
- let result_isEntityAlive_0_$f;
1097
- const denseIndex_0_$f = this[$internal].entityIndex.sparse[target & ENTITY_ID_MASK];
1098
- if (denseIndex_0_$f === void 0 || denseIndex_0_$f >= this[$internal].entityIndex.aliveCount) {
1099
- result_isEntityAlive_0_$f = false;
1100
- } else {
1101
- const storedEntity_0_$f = this[$internal].entityIndex.dense[denseIndex_0_$f];
1102
- result_isEntityAlive_0_$f = (target >>> GENERATION_SHIFT & GENERATION_MASK) === (storedEntity_0_$f >>> GENERATION_SHIFT & GENERATION_MASK) && target >>> WORLD_ID_SHIFT === this[$internal].entityIndex.worldId;
1103
- }
1104
- let result_hasTrait_1_$f;
1105
- const ctx_1_$f = this[$internal];
1106
- const data_1_$f = ctx_1_$f.traitData.get(target);
1107
- if (!data_1_$f) {
1108
- result_hasTrait_1_$f = false;
1109
- } else {
1110
- const {
1111
- generationId,
1112
- bitflag
1113
- } = data_1_$f;
1114
- const eid_1_$f = this[$internal].worldEntity & ENTITY_ID_MASK;
1115
- const mask_1_$f = ctx_1_$f.entityMasks[generationId][eid_1_$f];
1116
- result_hasTrait_1_$f = (mask_1_$f & bitflag) === bitflag;
1117
- }
1118
- return typeof target === "number" ? result_isEntityAlive_0_$f : result_hasTrait_1_$f;
1045
+ return typeof target === "number" ? isEntityAlive(this[$internal].entityIndex, target) : hasTrait(this, this[$internal].worldEntity, target);
1119
1046
  }
1120
1047
  add(...traits) {
1121
1048
  addTrait(this, this[$internal].worldEntity, ...traits);
@@ -1160,6 +1087,10 @@ var World = class {
1160
1087
  ctx.changedMasks.clear();
1161
1088
  ctx.trackedTraits.clear();
1162
1089
  ctx.worldEntity = createEntity(this, IsExcluded);
1090
+ for (const [hash, parameters] of universe.cachedQueries) {
1091
+ const query = createQuery(this, parameters);
1092
+ ctx.queriesHashMap.set(hash, query);
1093
+ }
1163
1094
  for (const sub of ctx.resetSubscriptions) {
1164
1095
  sub(this);
1165
1096
  }
@@ -1314,7 +1245,7 @@ function checkQuery(world, query, entity, event) {
1314
1245
  generations
1315
1246
  } = query;
1316
1247
  const ctx = world[$internal];
1317
- const eid = entity & ENTITY_ID_MASK;
1248
+ const eid = getEntityId(entity);
1318
1249
  if (query.traitData.all.length === 0) return false;
1319
1250
  for (let i = 0; i < generations.length; i++) {
1320
1251
  const generationId = generations[i];
@@ -1536,7 +1467,7 @@ function createQuery(world, parameters) {
1536
1467
  const changedMask = ctx.changedMasks.get(id);
1537
1468
  for (const entity of ctx.entityIndex.dense) {
1538
1469
  let allTraitsMatch = true;
1539
- const eid = entity & ENTITY_ID_MASK;
1470
+ const eid = getEntityId(entity);
1540
1471
  for (const trait2 of traits) {
1541
1472
  const {
1542
1473
  generationId,
@@ -1576,35 +1507,7 @@ function createQuery(world, parameters) {
1576
1507
  }
1577
1508
  }
1578
1509
  }
1579
- for (let i_2_$f = 0; i_2_$f < parameters.length; i_2_$f++) {
1580
- const param_2_$f = parameters[i_2_$f];
1581
- if (isModifier(param_2_$f)) {
1582
- if (param_2_$f.type === "not") {
1583
- continue;
1584
- } else {
1585
- const modifierTraits_2_$f = param_2_$f.traits;
1586
- for (const trait_2_$f of modifierTraits_2_$f) {
1587
- if (trait_2_$f[$internal].isTag) {
1588
- continue;
1589
- } else {
1590
- query.resultTraits.push(trait_2_$f);
1591
- const ctx_3_$f = world[$internal];
1592
- const data_3_$f = ctx_3_$f.traitData.get(trait_2_$f);
1593
- query.resultStores.push(data_3_$f.store);
1594
- }
1595
- }
1596
- }
1597
- } else {
1598
- if (param_2_$f[$internal].isTag) {
1599
- continue;
1600
- } else {
1601
- query.resultTraits.push(param_2_$f);
1602
- const ctx_4_$f = world[$internal];
1603
- const data_4_$f = ctx_4_$f.traitData.get(param_2_$f);
1604
- query.resultStores.push(data_4_$f.store);
1605
- }
1606
- }
1607
- }
1510
+ getQueryStores(parameters, query.resultTraits, query.resultStores, world);
1608
1511
  return query;
1609
1512
  }
1610
1513