tilemap-parser 3.1.20__tar.gz → 3.1.21__tar.gz

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.
Files changed (36) hide show
  1. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/PKG-INFO +1 -1
  2. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/pyproject.toml +1 -1
  3. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/runtime/object_collision.py +20 -0
  4. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser.egg-info/PKG-INFO +1 -1
  5. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/tests/test_object_collision.py +72 -18
  6. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/tests/test_tile_collision.py +2 -2
  7. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/LICENSE +0 -0
  8. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/README.md +0 -0
  9. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/setup.cfg +0 -0
  10. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/__init__.py +0 -0
  11. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/parser/__init__.py +0 -0
  12. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/parser/animation.py +0 -0
  13. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/parser/collision.py +0 -0
  14. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/parser/collision_loader.py +0 -0
  15. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/parser/map_parse.py +0 -0
  16. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/parser/node_parse.py +0 -0
  17. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/parser/particle.py +0 -0
  18. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/runtime/__init__.py +0 -0
  19. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/runtime/animation_player.py +0 -0
  20. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/runtime/area_node.py +0 -0
  21. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/runtime/collision_cache.py +0 -0
  22. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/runtime/map_loader.py +0 -0
  23. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/runtime/particles.py +0 -0
  24. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/runtime/renderer.py +0 -0
  25. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/runtime/tile_collision.py +0 -0
  26. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/utils/__init__.py +0 -0
  27. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser/utils/geometry.py +0 -0
  28. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser.egg-info/SOURCES.txt +0 -0
  29. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser.egg-info/dependency_links.txt +0 -0
  30. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser.egg-info/requires.txt +0 -0
  31. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/src/tilemap_parser.egg-info/top_level.txt +0 -0
  32. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/tests/test_collision.py +0 -0
  33. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/tests/test_geometry.py +0 -0
  34. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/tests/test_map_loader.py +0 -0
  35. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/tests/test_object_surfaces.py +0 -0
  36. {tilemap_parser-3.1.20 → tilemap_parser-3.1.21}/tests/test_render_scale.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-parser
3
- Version: 3.1.20
3
+ Version: 3.1.21
4
4
  Summary: Standalone parser/loader for tilemap-editor JSON maps, sprite animations, and collision detection runtime.
5
5
  Author: tilemap parser contributors
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tilemap-parser"
7
- version = "3.1.20"
7
+ version = "3.1.21"
8
8
  description = "Standalone parser/loader for tilemap-editor JSON maps, sprite animations, and collision detection runtime."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -455,3 +455,23 @@ class ObjectCollisionManager:
455
455
  if hit is not None:
456
456
  hits.append(hit)
457
457
  return hits
458
+
459
+ def check_object_first(self, obj: ICollidableObject) -> Optional[CollisionHit]:
460
+ """
461
+ Check one object against all others and return the first collision hit.
462
+
463
+ The queried object does not need to be managed. If it is managed,
464
+ comparison with itself is skipped by identity.
465
+
466
+ Candidate iteration follows insertion order among the spatially relevant
467
+ managed objects.
468
+ """
469
+ objects, grid = self._build_spatial_index()
470
+ for index in sorted(self._candidate_indices(obj, grid)):
471
+ other = objects[index]
472
+ if other is obj:
473
+ continue
474
+ hit = check_collision(obj, other)
475
+ if hit is not None:
476
+ return hit
477
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-parser
3
- Version: 3.1.20
3
+ Version: 3.1.21
4
4
  Summary: Standalone parser/loader for tilemap-editor JSON maps, sprite animations, and collision detection runtime.
5
5
  Author: tilemap parser contributors
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -8,32 +8,33 @@ Covers:
8
8
  - ObjectCollisionManager (add/remove, queries, duplicates, warnings)
9
9
  """
10
10
 
11
+ import sys
11
12
  import warnings
12
- import pytest
13
13
  from pathlib import Path
14
- import sys
14
+
15
+ import pytest
15
16
 
16
17
  sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
17
18
 
19
+ import tilemap_parser.runtime.object_collision as object_collision_runtime
18
20
  from tilemap_parser.parser.collision import (
19
21
  CapsuleShape,
20
22
  CircleShape,
21
23
  CollisionPolygon,
22
24
  RectangleShape,
23
25
  )
24
- import tilemap_parser.runtime.object_collision as object_collision_runtime
25
26
  from tilemap_parser.runtime.object_collision import (
26
27
  CollisionHit,
27
28
  ObjectCollisionManager,
28
- should_collide,
29
29
  check_collision,
30
+ should_collide,
30
31
  )
31
32
 
32
-
33
33
  # ---------------------------------------------------------------------------
34
34
  # Helpers — concrete objects implementing ICollidableObject
35
35
  # ---------------------------------------------------------------------------
36
36
 
37
+
37
38
  class _DummyObject:
38
39
  """Minimal collidable object for tests."""
39
40
 
@@ -68,6 +69,7 @@ def _make_poly(x, y, vertices, collision_layer=1, collision_mask=0xFFFFFFFF):
68
69
  # should_collide
69
70
  # ---------------------------------------------------------------------------
70
71
 
72
+
71
73
  class TestShouldCollide:
72
74
  def test_default_layers_collide(self):
73
75
  a = _make_rect(0, 0, 10, 10)
@@ -94,6 +96,7 @@ class TestShouldCollide:
94
96
  x = 0.0
95
97
  y = 0.0
96
98
  collision_shape = RectangleShape(width=10, height=10)
99
+
97
100
  assert should_collide(BareObject(), BareObject()) is True
98
101
 
99
102
 
@@ -101,6 +104,7 @@ class TestShouldCollide:
101
104
  # check_collision
102
105
  # ---------------------------------------------------------------------------
103
106
 
107
+
104
108
  class TestCheckCollisionCircleCircle:
105
109
  def test_colliding(self):
106
110
  a = _make_circle(0, 0, 10)
@@ -237,6 +241,7 @@ class TestCheckCollisionPolygon:
237
241
  # ObjectCollisionManager
238
242
  # ---------------------------------------------------------------------------
239
243
 
244
+
240
245
  class TestManagerAddRemove:
241
246
  def test_add_and_remove(self):
242
247
  mgr = ObjectCollisionManager()
@@ -439,10 +444,44 @@ class TestManagerCheckObject:
439
444
  assert hits[0].object_b is managed
440
445
 
441
446
 
447
+ class TestManagerCheckObjectFirst:
448
+ def test_returns_first_hit(self):
449
+ mgr = ObjectCollisionManager()
450
+ query = _make_circle(0, 0, 10)
451
+ first = _make_circle(15, 0, 10)
452
+ second = _make_circle(18, 0, 10)
453
+ far = _make_circle(100, 0, 10)
454
+ mgr.add_object(first)
455
+ mgr.add_object(second)
456
+ mgr.add_object(far)
457
+
458
+ hit = mgr.check_object_first(query)
459
+
460
+ assert hit is not None
461
+ assert hit.object_a is query
462
+ assert hit.object_b is first
463
+
464
+ def test_returns_none_when_no_hit(self):
465
+ mgr = ObjectCollisionManager()
466
+ query = _make_rect(0, 0, 10, 10)
467
+ far = _make_rect(100, 0, 10, 10)
468
+ mgr.add_object(far)
469
+
470
+ assert mgr.check_object_first(query) is None
471
+
472
+ def test_skips_self(self):
473
+ mgr = ObjectCollisionManager()
474
+ obj = _make_rect(0, 0, 10, 10)
475
+ mgr.add_object(obj)
476
+
477
+ assert mgr.check_object_first(obj) is None
478
+
479
+
442
480
  # ---------------------------------------------------------------------------
443
481
  # CollisionHit helpers
444
482
  # ---------------------------------------------------------------------------
445
483
 
484
+
446
485
  class TestCollisionHitHelpers:
447
486
  def test_resolve_separates(self):
448
487
  a = _make_circle(0, 0, 10)
@@ -486,8 +525,10 @@ class TestCollisionHitHelpers:
486
525
  def test_slide_head_on(self):
487
526
  """Directly into surface → zero tangential motion."""
488
527
  hit = CollisionHit(
489
- object_a=None, object_b=None,
490
- normal=(1.0, 0.0), depth=5.0,
528
+ object_a=None,
529
+ object_b=None,
530
+ normal=(1.0, 0.0),
531
+ depth=5.0,
491
532
  )
492
533
  sx, sy = hit.slide_velocity(10.0, 0.0)
493
534
  assert sx == 0.0
@@ -496,8 +537,10 @@ class TestCollisionHitHelpers:
496
537
  def test_slide_angled(self):
497
538
  """Diagonal into surface → only perpendicular component removed."""
498
539
  hit = CollisionHit(
499
- object_a=None, object_b=None,
500
- normal=(1.0, 0.0), depth=5.0,
540
+ object_a=None,
541
+ object_b=None,
542
+ normal=(1.0, 0.0),
543
+ depth=5.0,
501
544
  )
502
545
  sx, sy = hit.slide_velocity(10.0, 5.0)
503
546
  assert sx == 0.0 # x component fully removed
@@ -506,8 +549,10 @@ class TestCollisionHitHelpers:
506
549
  def test_slide_parallel(self):
507
550
  """Velocity along the surface → unchanged."""
508
551
  hit = CollisionHit(
509
- object_a=None, object_b=None,
510
- normal=(0.0, 1.0), depth=5.0,
552
+ object_a=None,
553
+ object_b=None,
554
+ normal=(0.0, 1.0),
555
+ depth=5.0,
511
556
  )
512
557
  sx, sy = hit.slide_velocity(10.0, 0.0)
513
558
  assert sx == 10.0
@@ -516,8 +561,10 @@ class TestCollisionHitHelpers:
516
561
  def test_slide_away(self):
517
562
  """Velocity pointing away from surface → unchanged."""
518
563
  hit = CollisionHit(
519
- object_a=None, object_b=None,
520
- normal=(1.0, 0.0), depth=5.0,
564
+ object_a=None,
565
+ object_b=None,
566
+ normal=(1.0, 0.0),
567
+ depth=5.0,
521
568
  )
522
569
  sx, sy = hit.slide_velocity(-10.0, 0.0)
523
570
  assert sx == -10.0
@@ -526,8 +573,10 @@ class TestCollisionHitHelpers:
526
573
  def test_slide_zero(self):
527
574
  """Zero velocity → zero."""
528
575
  hit = CollisionHit(
529
- object_a=None, object_b=None,
530
- normal=(1.0, 0.0), depth=5.0,
576
+ object_a=None,
577
+ object_b=None,
578
+ normal=(1.0, 0.0),
579
+ depth=5.0,
531
580
  )
532
581
  sx, sy = hit.slide_velocity(0.0, 0.0)
533
582
  assert sx == 0.0
@@ -536,10 +585,13 @@ class TestCollisionHitHelpers:
536
585
  def test_slide_diagonal_normal(self):
537
586
  """Normal at 45°, velocity into surface → slide along surface."""
538
587
  import math
588
+
539
589
  n = (1.0 / math.sqrt(2), 1.0 / math.sqrt(2))
540
590
  hit = CollisionHit(
541
- object_a=None, object_b=None,
542
- normal=n, depth=5.0,
591
+ object_a=None,
592
+ object_b=None,
593
+ normal=n,
594
+ depth=5.0,
543
595
  )
544
596
  sx, sy = hit.slide_velocity(1.0, 0.0)
545
597
  # Dot = 1/sqrt(2) ≈ 0.707
@@ -552,6 +604,7 @@ class TestCollisionHitHelpers:
552
604
  # Capsule collision
553
605
  # ---------------------------------------------------------------------------
554
606
 
607
+
555
608
  def _make_capsule(
556
609
  x: float = 0.0,
557
610
  y: float = 0.0,
@@ -563,7 +616,8 @@ def _make_capsule(
563
616
  collision_mask: int = 0xFFFFFFFF,
564
617
  ) -> _DummyObject:
565
618
  return _DummyObject(
566
- x=x, y=y,
619
+ x=x,
620
+ y=y,
567
621
  shape=CapsuleShape(radius=radius, height=height, offset=(ox, oy)),
568
622
  collision_layer=collision_layer,
569
623
  collision_mask=collision_mask,
@@ -142,9 +142,9 @@ class TestShapeBounds:
142
142
  sprite = MockSprite(x=100, y=200, shape=CapsuleShape(radius=8, height=48, offset=(0, 0)))
143
143
  left, top, right, bottom = get_shape_bounds(sprite)
144
144
  assert left == 92
145
- assert top == 200
145
+ assert top == 192
146
146
  assert right == 108
147
- assert bottom == 264
147
+ assert bottom == 256
148
148
 
149
149
 
150
150
  # ===========================================================================
File without changes