9
9
from argparse import Namespace
10
10
from collections import ChainMap , Counter , deque
11
11
from enum import IntEnum , IntFlag
12
- from typing import Any , Callable , Dict , Iterable , Iterator , List , NamedTuple , Optional , Set , Tuple , TypedDict , Union
12
+ from typing import Any , Callable , Dict , Iterable , Iterator , List , NamedTuple , Optional , Set , Tuple , TypedDict , Union , \
13
+ Type , ClassVar
13
14
14
15
import NetUtils
15
16
import Options
@@ -788,6 +789,44 @@ def remove(self, item: Item):
788
789
self .stale [item .player ] = True
789
790
790
791
792
+ class Entrance :
793
+ access_rule : Callable [[CollectionState ], bool ] = staticmethod (lambda state : True )
794
+ hide_path : bool = False
795
+ player : int
796
+ name : str
797
+ parent_region : Optional [Region ]
798
+ connected_region : Optional [Region ] = None
799
+ # LttP specific, TODO: should make a LttPEntrance
800
+ addresses = None
801
+ target = None
802
+
803
+ def __init__ (self , player : int , name : str = '' , parent : Region = None ):
804
+ self .name = name
805
+ self .parent_region = parent
806
+ self .player = player
807
+
808
+ def can_reach (self , state : CollectionState ) -> bool :
809
+ if self .parent_region .can_reach (state ) and self .access_rule (state ):
810
+ if not self .hide_path and not self in state .path :
811
+ state .path [self ] = (self .name , state .path .get (self .parent_region , (self .parent_region .name , None )))
812
+ return True
813
+
814
+ return False
815
+
816
+ def connect (self , region : Region , addresses : Any = None , target : Any = None ) -> None :
817
+ self .connected_region = region
818
+ self .target = target
819
+ self .addresses = addresses
820
+ region .entrances .append (self )
821
+
822
+ def __repr__ (self ):
823
+ return self .__str__ ()
824
+
825
+ def __str__ (self ):
826
+ world = self .parent_region .multiworld if self .parent_region else None
827
+ return world .get_name_string_for_object (self ) if world else f'{ self .name } (Player { self .player } )'
828
+
829
+
791
830
class Region :
792
831
name : str
793
832
_hint_text : str
@@ -796,6 +835,7 @@ class Region:
796
835
entrances : List [Entrance ]
797
836
exits : List [Entrance ]
798
837
locations : List [Location ]
838
+ entrance_type : ClassVar [Type [Entrance ]] = Entrance
799
839
800
840
def __init__ (self , name : str , player : int , multiworld : MultiWorld , hint : Optional [str ] = None ):
801
841
self .name = name
@@ -823,20 +863,48 @@ def can_reach_private(self, state: CollectionState) -> bool:
823
863
def hint_text (self ) -> str :
824
864
return self ._hint_text if self ._hint_text else self .name
825
865
826
- def get_connecting_entrance (self , is_main_entrance : typing . Callable [[Entrance ], bool ]) -> Entrance :
866
+ def get_connecting_entrance (self , is_main_entrance : Callable [[Entrance ], bool ]) -> Entrance :
827
867
for entrance in self .entrances :
828
868
if is_main_entrance (entrance ):
829
869
return entrance
830
870
for entrance in self .entrances : # BFS might be better here, trying DFS for now.
831
871
return entrance .parent_region .get_connecting_entrance (is_main_entrance )
832
872
833
- def add_locations (self , locations : Dict [str , Optional [int ]], location_type : Optional [typing .Type [Location ]] = None ) -> None :
834
- """Adds locations to the Region object, where location_type is your Location class and locations is a dict of
835
- location names to address."""
873
+ def add_locations (self , locations : Dict [str , Optional [int ]],
874
+ location_type : Optional [Type [Location ]] = None ) -> None :
875
+ """
876
+ Adds locations to the Region object, where location_type is your Location class and locations is a dict of
877
+ location names to address.
878
+
879
+ :param locations: dictionary of locations to be created and added to this Region `{name: ID}`
880
+ :param location_type: Location class to be used to create the locations with"""
836
881
if location_type is None :
837
882
location_type = Location
838
883
for location , address in locations .items ():
839
884
self .locations .append (location_type (self .player , location , address , self ))
885
+
886
+ def connect (self , connecting_region : Region , name : Optional [str ] = None ,
887
+ rule : Optional [Callable [[CollectionState ], bool ]] = None ) -> None :
888
+ """
889
+ Connects this Region to another Region, placing the provided rule on the connection.
890
+
891
+ :param connecting_region: Region object to connect to path is `self -> exiting_region`
892
+ :param name: name of the connection being created
893
+ :param rule: callable to determine access of this connection to go from self to the exiting_region"""
894
+ exit_ = self .create_exit (name if name else f"{ self .name } -> { connecting_region .name } " )
895
+ if rule :
896
+ exit_ .access_rule = rule
897
+ exit_ .connect (connecting_region )
898
+
899
+ def create_exit (self , name : str ) -> Entrance :
900
+ """
901
+ Creates and returns an Entrance object as an exit of this region.
902
+
903
+ :param name: name of the Entrance being created
904
+ """
905
+ exit_ = self .entrance_type (self .player , name , self )
906
+ self .exits .append (exit_ )
907
+ return exit_
840
908
841
909
def add_exits (self , exits : Union [Iterable [str ], Dict [str , Optional [str ]]],
842
910
rules : Dict [str , Callable [[CollectionState ], bool ]] = None ) -> None :
@@ -850,11 +918,9 @@ def add_exits(self, exits: Union[Iterable[str], Dict[str, Optional[str]]],
850
918
if not isinstance (exits , Dict ):
851
919
exits = dict .fromkeys (exits )
852
920
for connecting_region , name in exits .items ():
853
- entrance = Entrance (self .player , name if name else f"{ self .name } -> { connecting_region } " , self )
854
- if rules and connecting_region in rules :
855
- entrance .access_rule = rules [connecting_region ]
856
- self .exits .append (entrance )
857
- entrance .connect (self .multiworld .get_region (connecting_region , self .player ))
921
+ self .connect (self .multiworld .get_region (connecting_region , self .player ),
922
+ name ,
923
+ rules [connecting_region ] if rules and connecting_region in rules else None )
858
924
859
925
def __repr__ (self ):
860
926
return self .__str__ ()
@@ -863,44 +929,6 @@ def __str__(self):
863
929
return self .multiworld .get_name_string_for_object (self ) if self .multiworld else f'{ self .name } (Player { self .player } )'
864
930
865
931
866
- class Entrance :
867
- access_rule : Callable [[CollectionState ], bool ] = staticmethod (lambda state : True )
868
- hide_path : bool = False
869
- player : int
870
- name : str
871
- parent_region : Optional [Region ]
872
- connected_region : Optional [Region ] = None
873
- # LttP specific, TODO: should make a LttPEntrance
874
- addresses = None
875
- target = None
876
-
877
- def __init__ (self , player : int , name : str = '' , parent : Region = None ):
878
- self .name = name
879
- self .parent_region = parent
880
- self .player = player
881
-
882
- def can_reach (self , state : CollectionState ) -> bool :
883
- if self .parent_region .can_reach (state ) and self .access_rule (state ):
884
- if not self .hide_path and not self in state .path :
885
- state .path [self ] = (self .name , state .path .get (self .parent_region , (self .parent_region .name , None )))
886
- return True
887
-
888
- return False
889
-
890
- def connect (self , region : Region , addresses : Any = None , target : Any = None ) -> None :
891
- self .connected_region = region
892
- self .target = target
893
- self .addresses = addresses
894
- region .entrances .append (self )
895
-
896
- def __repr__ (self ):
897
- return self .__str__ ()
898
-
899
- def __str__ (self ):
900
- world = self .parent_region .multiworld if self .parent_region else None
901
- return world .get_name_string_for_object (self ) if world else f'{ self .name } (Player { self .player } )'
902
-
903
-
904
932
class LocationProgressType (IntEnum ):
905
933
DEFAULT = 1
906
934
PRIORITY = 2
0 commit comments