Skip to content

Commit 641131e

Browse files
committed
terrain
1 parent 18a4299 commit 641131e

10 files changed

Lines changed: 199 additions & 116 deletions

File tree

tests/tuxemon/test_collision_manager.py

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,48 +26,6 @@ def collision_manager(map_manager, npc_manager):
2626
return CollisionManager(map_manager, npc_manager)
2727

2828

29-
def test_get_all_tile_properties(collision_manager, map_manager):
30-
surface_map = {
31-
(0, 0): {"label1": 1.0, "label2": 2.0},
32-
(1, 1): {"label1": 3.0, "label3": 4.0},
33-
}
34-
map_manager.surface_map = surface_map
35-
36-
result = collision_manager.get_all_tile_properties(surface_map, "label1")
37-
assert result == [(0, 0), (1, 1)]
38-
39-
40-
@patch(
41-
"tuxemon.map.collision_manager.SURFACE_KEYS",
42-
["label1", "label2", "label3"],
43-
)
44-
def test_update_tile_property(collision_manager, map_manager):
45-
surface_map = {
46-
(0, 0): {"label1": 1.0, "label2": 2.0},
47-
(1, 1): {"label1": 3.0, "label3": 4.0},
48-
}
49-
map_manager.surface_map = surface_map
50-
51-
collision_manager.update_tile_property("label1", 5.0)
52-
53-
assert map_manager.surface_map[(0, 0)]["label1"] == 5.0
54-
assert map_manager.surface_map[(1, 1)]["label1"] == 5.0
55-
56-
57-
@patch(
58-
"tuxemon.map.collision_manager.SURFACE_KEYS",
59-
["label1", "label2", "label3"],
60-
)
61-
def test_all_tiles_modified(collision_manager, map_manager):
62-
surface_map = {
63-
(0, 0): {"label1": 5.0, "label2": 2.0},
64-
(1, 1): {"label1": 5.0, "label3": 4.0},
65-
}
66-
map_manager.surface_map = surface_map
67-
68-
assert collision_manager.all_tiles_modified("label1", 5.0)
69-
70-
7129
def test_check_collision_zones(collision_manager, map_manager):
7230
collision_map = {
7331
(0, 0): RegionProperties([], [], [], None, "label1"),
@@ -87,7 +45,7 @@ def test_add_collision(collision_manager, map_manager):
8745
region = RegionProperties([], [], [], None, "label1")
8846
map_manager.collision_map = {(0, 0): region}
8947

90-
collision_manager.add_collision(entity, (0.0, 0.0))
48+
collision_manager.add_collision(entity, (0, 0))
9149

9250
assert map_manager.collision_map[(0, 0)].entity is not None
9351

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# SPDX-License-Identifier: GPL-3.0
2+
# Copyright (c) 2014-2026 William Edwards <shadowapex@gmail.com>, Benjamin Bean <superman2k5@gmail.com>
3+
from unittest.mock import MagicMock, patch
4+
5+
import pytest
6+
7+
from tuxemon.map.terrain import TerrainManager
8+
9+
10+
@pytest.fixture
11+
def map_manager():
12+
mgr = MagicMock()
13+
mgr.surface_map = {}
14+
return mgr
15+
16+
17+
@pytest.fixture
18+
def terrain(map_manager):
19+
return TerrainManager(map_manager)
20+
21+
22+
def test_get_all_tile_properties_basic(terrain, map_manager):
23+
map_manager.surface_map = {
24+
(0, 0): {"grass": 1.0},
25+
(1, 1): {"water": 0.5},
26+
(2, 2): {"grass": 2.0},
27+
}
28+
29+
result = terrain.get_all_tile_properties("grass")
30+
assert set(result) == {(0, 0), (2, 2)}
31+
32+
33+
def test_get_all_tile_properties_no_matches(terrain, map_manager):
34+
map_manager.surface_map = {
35+
(0, 0): {"grass": 1.0},
36+
(1, 1): {"grass": 2.0},
37+
}
38+
39+
assert terrain.get_all_tile_properties("lava") == []
40+
41+
42+
def test_get_all_tile_properties_empty_map(terrain, map_manager):
43+
map_manager.surface_map = {}
44+
assert terrain.get_all_tile_properties("grass") == []
45+
46+
47+
@patch("tuxemon.map.terrain.SURFACE_KEYS", ["grass", "water"])
48+
def test_update_tile_property_updates_existing_labels(terrain, map_manager):
49+
map_manager.surface_map = {
50+
(0, 0): {"grass": 1.0},
51+
(1, 1): {"grass": 2.0, "water": 0.5},
52+
}
53+
54+
terrain.update_tile_property("grass", 9.0)
55+
56+
assert map_manager.surface_map[(0, 0)]["grass"] == 9.0
57+
assert map_manager.surface_map[(1, 1)]["grass"] == 9.0
58+
59+
60+
@patch("tuxemon.map.terrain.SURFACE_KEYS", ["grass", "water"])
61+
def test_update_tile_property_does_not_create_new_labels(terrain, map_manager):
62+
map_manager.surface_map = {
63+
(0, 0): {"grass": 1.0},
64+
}
65+
66+
terrain.update_tile_property("water", 5.0)
67+
68+
assert "water" not in map_manager.surface_map[(0, 0)]
69+
70+
71+
@patch("tuxemon.map.terrain.SURFACE_KEYS", ["grass"])
72+
def test_update_tile_property_invalid_label_ignored(terrain, map_manager):
73+
map_manager.surface_map = {
74+
(0, 0): {"grass": 1.0},
75+
}
76+
77+
terrain.update_tile_property("lava", 5.0)
78+
79+
assert map_manager.surface_map[(0, 0)]["grass"] == 1.0
80+
assert "lava" not in map_manager.surface_map[(0, 0)]
81+
82+
83+
@patch("tuxemon.map.terrain.SURFACE_KEYS", ["grass"])
84+
def test_update_tile_property_no_mutation_when_value_same(
85+
terrain, map_manager
86+
):
87+
map_manager.surface_map = {
88+
(0, 0): {"grass": 3.0},
89+
}
90+
91+
terrain.update_tile_property("grass", 3.0)
92+
assert map_manager.surface_map[(0, 0)] == {"grass": 3.0}
93+
94+
95+
def test_all_tiles_modified_true(terrain, map_manager):
96+
map_manager.surface_map = {
97+
(0, 0): {"grass": 5.0},
98+
(1, 1): {"grass": 5.0},
99+
}
100+
101+
assert terrain.all_tiles_modified("grass", 5.0) is True
102+
103+
104+
def test_all_tiles_modified_false(terrain, map_manager):
105+
map_manager.surface_map = {
106+
(0, 0): {"grass": 5.0},
107+
(1, 1): {"grass": 3.0},
108+
}
109+
110+
assert terrain.all_tiles_modified("grass", 5.0) is False
111+
112+
113+
def test_all_tiles_modified_no_tiles(terrain, map_manager):
114+
map_manager.surface_map = {
115+
(0, 0): {"water": 1.0},
116+
}
117+
118+
assert terrain.all_tiles_modified("grass", 5.0) is True
119+
120+
121+
def test_all_tiles_modified_missing_key_on_some_tiles(terrain, map_manager):
122+
map_manager.surface_map = {
123+
(0, 0): {"grass": 5.0},
124+
(1, 1): {"grass": 5.0, "water": 1.0},
125+
(2, 2): {"water": 1.0}, # does not contain "grass"
126+
}
127+
128+
assert terrain.all_tiles_modified("grass", 5.0) is True

tuxemon/base_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from tuxemon.map.collision_manager import CollisionManager
3434
from tuxemon.map.loader import MapLoader
3535
from tuxemon.map.manager import MapManager
36+
from tuxemon.map.terrain import TerrainManager
3637
from tuxemon.map.transition import MapTransition
3738
from tuxemon.map.view import AbstractRenderer, NullRenderer
3839
from tuxemon.menu.alert import AlertManager
@@ -158,6 +159,7 @@ def __init__(self, config: TuxemonConfig, context: DisplayContext) -> None:
158159
self.movement_manager = MovementManager(
159160
self.event_manager, self.input_manager
160161
)
162+
self.terrain_manager = TerrainManager(self.map_manager)
161163
self.collision_manager = CollisionManager(
162164
self.map_manager, self.npc_manager
163165
)

tuxemon/core/conditions/facing_tile.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ def test_with_monster(self, session: Session, target: Monster) -> bool:
4545
tiles = get_coords(player.tile_pos, client.map_manager.map_size)
4646

4747
label = (
48-
client.collision_manager.get_all_tile_properties(
49-
client.map_manager.surface_map, self.facing_tile
50-
)
48+
client.terrain_manager.get_all_tile_properties(self.facing_tile)
5149
if self.facing_tile in SURFACE_KEYS
5250
else client.collision_manager.check_collision_zones(
5351
client.map_manager.collision_map, self.facing_tile

tuxemon/event/actions/update_tile_properties.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ class UpdateTilePropertiesAction(EventAction):
3535
moverate: float
3636

3737
def start(self, session: Session) -> None:
38-
session.client.collision_manager.update_tile_property(
38+
session.client.terrain_manager.update_tile_property(
3939
self.label, self.moverate
4040
)

tuxemon/event/conditions/char_facing_tile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ def test(self, session: Session) -> bool:
5757
# check if the NPC is facing a specific set of tiles
5858
if self.value:
5959
if self.value in SURFACE_KEYS:
60-
label = client.collision_manager.get_all_tile_properties(
61-
client.map_manager.surface_map, self.value
60+
label = client.terrain_manager.get_all_tile_properties(
61+
self.value
6262
)
6363
else:
6464
label = client.collision_manager.check_collision_zones(

tuxemon/event/conditions/char_in.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@ def test(self, session: Session) -> bool:
4141

4242
tiles = []
4343
if self.value in SURFACE_KEYS:
44-
tiles = client.collision_manager.get_all_tile_properties(
45-
client.map_manager.surface_map, self.value
46-
)
44+
tiles = client.terrain_manager.get_all_tile_properties(self.value)
4745
else:
4846
tiles = client.collision_manager.check_collision_zones(
4947
client.map_manager.collision_map, self.value

tuxemon/event/conditions/tile_property_updated.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ class TilePropertyUpdatedCondition(EventCondition):
3333
moverate: float
3434

3535
def test(self, session: Session) -> bool:
36-
return session.client.collision_manager.all_tiles_modified(
36+
return session.client.terrain_manager.all_tiles_modified(
3737
self.label, self.moverate
3838
)

tuxemon/map/collision_manager.py

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from typing import TYPE_CHECKING
99

1010
from tuxemon.map.region import RegionProperties
11-
from tuxemon.platform.const.sizes import SURFACE_KEYS
1211

1312
if TYPE_CHECKING:
1413
from tuxemon.entity.entity import Entity
@@ -36,68 +35,6 @@ def __init__(
3635
self._map_manager = map_manager
3736
self._npc_manager = npc_manager
3837

39-
def get_all_tile_properties(
40-
self,
41-
surface_map: MutableMapping[tuple[int, int], dict[str, float]],
42-
label: str,
43-
) -> list[tuple[int, int]]:
44-
"""
45-
Retrieves the coordinates of all tiles with a specific property.
46-
47-
Parameters:
48-
map: The surface map.
49-
label: The label (SurfaceKeys).
50-
51-
Returns:
52-
A list of coordinates (tuples) of tiles with the specified label.
53-
"""
54-
return [
55-
coords for coords, props in surface_map.items() if label in props
56-
]
57-
58-
def update_tile_property(self, label: str, moverate: float) -> None:
59-
"""
60-
Updates the movement rate property for existing tile entries in the
61-
surface map.
62-
63-
This method modifies the moverate value for tiles that already contain
64-
the specified label, ensuring that no new dictionary entries are created.
65-
If the label is not present in a tile's properties, the tile remains
66-
unchanged. The update process runs efficiently to prevent unnecessary
67-
modifications.
68-
69-
Parameters:
70-
label: The property key to update (e.g., terrain type).
71-
moverate: The new movement rate value to assign.
72-
"""
73-
if label not in SURFACE_KEYS:
74-
return
75-
76-
for coord in self.get_all_tile_properties(
77-
self._map_manager.surface_map, label
78-
):
79-
props = self._map_manager.surface_map.get(coord)
80-
if props and props.get(label) != moverate:
81-
props[label] = moverate
82-
83-
def all_tiles_modified(self, label: str, moverate: float) -> bool:
84-
"""
85-
Checks if all tiles with the specified label have been modified.
86-
87-
Parameters:
88-
label: The property key to check.
89-
moverate: The expected movement rate.
90-
91-
Returns:
92-
True if all tiles have the expected moverate, False otherwise.
93-
"""
94-
return all(
95-
self._map_manager.surface_map[coord].get(label) == moverate
96-
for coord in self.get_all_tile_properties(
97-
self._map_manager.surface_map, label
98-
)
99-
)
100-
10138
def check_collision_zones(
10239
self,
10340
collision_map: MutableMapping[

tuxemon/map/terrain.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# SPDX-License-Identifier: GPL-3.0
2+
# Copyright (c) 2014-2026 William Edwards <shadowapex@gmail.com>, Benjamin Bean <superman2k5@gmail.com>
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING
6+
7+
from tuxemon.platform.const.sizes import SURFACE_KEYS
8+
9+
if TYPE_CHECKING:
10+
from tuxemon.map.manager import MapManager
11+
12+
13+
class TerrainManager:
14+
"""
15+
Manages terrain-related tile properties in the surface map.
16+
17+
This class mirrors the behavior originally embedded in CollisionManager,
18+
including validation against SURFACE_KEYS and the guarantee that no new
19+
surface_map entries or keys are created implicitly.
20+
"""
21+
22+
def __init__(self, map_manager: MapManager):
23+
self.map_manager = map_manager
24+
25+
def get_all_tile_properties(
26+
self,
27+
label: str,
28+
) -> list[tuple[int, int]]:
29+
"""
30+
Return all tile coordinates that contain the given surface label.
31+
"""
32+
return [
33+
coords
34+
for coords, props in self.map_manager.surface_map.items()
35+
if label in props
36+
]
37+
38+
def update_tile_property(self, label: str, moverate: float) -> None:
39+
"""
40+
Update the moverate for all tiles that already contain `label`.
41+
42+
This preserves the original guarantee:
43+
- no new dictionary entries are created
44+
- only existing keys are updated
45+
- invalid labels (not in SURFACE_KEYS) are ignored
46+
"""
47+
if label not in SURFACE_KEYS:
48+
return
49+
50+
for coord in self.get_all_tile_properties(label):
51+
props = self.map_manager.surface_map.get(coord)
52+
if props and props.get(label) != moverate:
53+
props[label] = moverate
54+
55+
def all_tiles_modified(self, label: str, moverate: float) -> bool:
56+
"""
57+
Check whether all tiles containing `label` have the expected moverate.
58+
"""
59+
return all(
60+
self.map_manager.surface_map[coord].get(label) == moverate
61+
for coord in self.get_all_tile_properties(label)
62+
)

0 commit comments

Comments
 (0)