diff --git a/CHANGELOG.md b/CHANGELOG.md index e556603..aa44ea5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Relation components can now be copied between distinct keys in the same entity, instead of the assignment being silently ignored. + https://github.com/HexDecimal/python-tcod-ecs/pull/59 + ## [5.5.1] - 2026-10-13 Special thanks to Anton Soloviev for critical bugfixes. diff --git a/tcod/ecs/entity.py b/tcod/ecs/entity.py index 28e38c2..e674a00 100644 --- a/tcod/ecs/entity.py +++ b/tcod/ecs/entity.py @@ -1058,7 +1058,11 @@ def __setitem__(self, __key: ComponentKey[T], __values: Mapping[Entity, T], /) - ..versionadded:: 4.2.0 """ - if isinstance(__values, EntityComponentRelationMapping) and __values.entity is self.entity: + if ( + isinstance(__values, EntityComponentRelationMapping) + and __values.entity is self.entity + and __values.key == __key + ): return mapping: EntityComponentRelationMapping[T] = self[__key] mapping.clear() diff --git a/tests/test_relations.py b/tests/test_relations.py index 4374056..0b4ac64 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -177,6 +177,35 @@ def test_relation_components() -> None: assert not set(world.Q.all_of(relations=[(int, world[1])])) +@pytest.mark.parametrize("source_key", [int, ("fire", int)]) +def test_component_relation_copy_between_keys(source_key: type[int] | tuple[str, type[int]]) -> None: + world = tcod.ecs.Registry() + mage, target, old_target = world["mage"], world["target"], world["old_target"] + destination_key = ("poison", int) + source_value = 5 + mage.relation_components[source_key][target] = source_value + mage.relation_components[destination_key][old_target] = 9 + + mage.relation_components[destination_key] = mage.relation_components[source_key] + + assert dict(mage.relation_components[destination_key]) == {target: source_value} + assert dict(mage.relation_components[source_key]) == {target: source_value} + assert set(world.Q.all_of(relations=[(destination_key, target)])) == {mage} + assert set(world.Q.all_of(relations=[(destination_key, old_target)])) == set() + + +def test_component_relation_copy_from_another_entity() -> None: + world = tcod.ecs.Registry() + first, second, target = world["first"], world["second"], world["target"] + value = 5 + first.relation_components[int][target] = value + + second.relation_components[int] = first.relation_components[int] + + assert dict(second.relation_components[int]) == {target: value} + assert dict(first.relation_components[int]) == {target: value} + + def test_conditional_relations() -> None: world = tcod.ecs.Registry() world["A"].relation_tag[ChildOf] = world["B"]