Add Portal class with target zone and coordinates
Portals are non-portable Things that exist in zones and define transitions to other zones via target coordinates.
This commit is contained in:
parent
621c42b833
commit
303ce2c89e
2 changed files with 121 additions and 0 deletions
25
src/mudlib/portal.py
Normal file
25
src/mudlib/portal.py
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
"""Portal — a transition point between zones."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from mudlib.thing import Thing
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Portal(Thing):
|
||||||
|
"""A portal connecting zones.
|
||||||
|
|
||||||
|
Portals are non-portable Things that exist in zones and define
|
||||||
|
transitions to other zones via target coordinates.
|
||||||
|
"""
|
||||||
|
|
||||||
|
target_zone: str = ""
|
||||||
|
target_x: int = 0
|
||||||
|
target_y: int = 0
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
"""Force portals to be non-portable."""
|
||||||
|
self.portable = False
|
||||||
|
super().__post_init__()
|
||||||
96
tests/test_portal.py
Normal file
96
tests/test_portal.py
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
"""Tests for the Portal class."""
|
||||||
|
|
||||||
|
from mudlib.object import Object
|
||||||
|
from mudlib.portal import Portal
|
||||||
|
from mudlib.thing import Thing
|
||||||
|
from mudlib.zone import Zone
|
||||||
|
|
||||||
|
# --- construction ---
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_creation_minimal():
|
||||||
|
"""Portal can be created with just a name."""
|
||||||
|
p = Portal(name="portal")
|
||||||
|
assert p.name == "portal"
|
||||||
|
assert p.location is None
|
||||||
|
assert p.target_zone == ""
|
||||||
|
assert p.target_x == 0
|
||||||
|
assert p.target_y == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_creation_with_target():
|
||||||
|
"""Portal can be created with target zone and coordinates."""
|
||||||
|
p = Portal(name="gateway", target_zone="dungeon", target_x=5, target_y=10)
|
||||||
|
assert p.target_zone == "dungeon"
|
||||||
|
assert p.target_x == 5
|
||||||
|
assert p.target_y == 10
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_is_thing_subclass():
|
||||||
|
"""Portal inherits from Thing."""
|
||||||
|
p = Portal(name="portal")
|
||||||
|
assert isinstance(p, Thing)
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_is_object_subclass():
|
||||||
|
"""Portal inherits from Object (via Thing)."""
|
||||||
|
p = Portal(name="portal")
|
||||||
|
assert isinstance(p, Object)
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_always_non_portable():
|
||||||
|
"""Portal is always non-portable (cannot be picked up)."""
|
||||||
|
p = Portal(name="portal")
|
||||||
|
assert p.portable is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_forced_non_portable():
|
||||||
|
"""Portal forces portable=False even if explicitly set True."""
|
||||||
|
# Even if we try to make it portable, it should be forced to False
|
||||||
|
p = Portal(name="portal", portable=True)
|
||||||
|
assert p.portable is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_inherits_description():
|
||||||
|
"""Portal can have a description (from Thing)."""
|
||||||
|
p = Portal(name="gateway", description="a shimmering portal")
|
||||||
|
assert p.description == "a shimmering portal"
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_inherits_aliases():
|
||||||
|
"""Portal can have aliases (from Thing)."""
|
||||||
|
p = Portal(name="gateway", aliases=["portal", "gate"])
|
||||||
|
assert p.aliases == ["portal", "gate"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_in_zone():
|
||||||
|
"""Portal can exist in a zone with coordinates."""
|
||||||
|
terrain = [["." for _ in range(10)] for _ in range(10)]
|
||||||
|
zone = Zone(name="test", width=10, height=10, terrain=terrain)
|
||||||
|
p = Portal(
|
||||||
|
name="gateway",
|
||||||
|
location=zone,
|
||||||
|
x=3,
|
||||||
|
y=7,
|
||||||
|
target_zone="dungeon",
|
||||||
|
target_x=5,
|
||||||
|
target_y=5,
|
||||||
|
)
|
||||||
|
assert p.location is zone
|
||||||
|
assert p.x == 3
|
||||||
|
assert p.y == 7
|
||||||
|
assert p in zone.contents
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_rejects_contents():
|
||||||
|
"""Portal cannot accept other objects (uses Thing's default behavior)."""
|
||||||
|
p = Portal(name="portal")
|
||||||
|
obj = Object(name="rock")
|
||||||
|
assert p.can_accept(obj) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_rejects_things():
|
||||||
|
"""Portal cannot accept things."""
|
||||||
|
p = Portal(name="portal")
|
||||||
|
thing = Thing(name="sword")
|
||||||
|
assert p.can_accept(thing) is False
|
||||||
Loading…
Reference in a new issue