forked from OoTRandomizer/OoT-Randomizer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Location.py
executable file
·158 lines (122 loc) · 4.93 KB
/
Location.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
from LocationList import location_table
from Region import TimeOfDay
from enum import Enum
class Location(object):
def __init__(self, name='', address=None, address2=None, default=None, type='Chest', scene=None, parent=None, filter_tags=None, internal=False):
self.name = name
self.parent_region = parent
self.item = None
self.address = address
self.address2 = address2
self.default = default
self.type = type
self.scene = scene
self.internal = internal
self.staleness_count = 0
self.access_rule = lambda state, **kwargs: True
self.access_rules = []
self.item_rule = lambda location, item: True
self.locked = False
self.price = None
self.minor_only = False
self.world = None
self.disabled = DisableType.ENABLED
self.always = False
self.never = False
if filter_tags is None:
self.filter_tags = None
else:
self.filter_tags = list(filter_tags)
def copy(self, new_region):
new_location = Location(self.name, self.address, self.address2, self.default, self.type, self.scene, new_region, self.filter_tags)
new_location.world = new_region.world
if self.item:
new_location.item = self.item.copy(new_region.world)
new_location.item.location = new_location
new_location.access_rule = self.access_rule
new_location.access_rules = list(self.access_rules)
new_location.item_rule = self.item_rule
new_location.locked = self.locked
new_location.internal = self.internal
new_location.minor_only = self.minor_only
new_location.disabled = self.disabled
new_location.always = self.always
new_location.never = self.never
return new_location
def add_rule(self, lambda_rule):
if self.always:
self.set_rule(lambda_rule)
self.always = False
return
if self.never:
return
self.access_rules.append(lambda_rule)
self.access_rule = lambda state, **kwargs: all(rule(state, **kwargs) for rule in self.access_rules)
def set_rule(self, lambda_rule):
self.access_rule = lambda_rule
self.access_rules = [lambda_rule]
def can_fill(self, state, item, check_access=True):
if self.minor_only and item.majoritem:
return False
return (
not self.is_disabled() and
self.can_fill_fast(item) and
(not check_access or state.search.spot_access(self, 'either')))
def can_fill_fast(self, item, manual=False):
return (self.parent_region.can_fill(item, manual) and self.item_rule(self, item))
def is_disabled(self):
return (self.disabled == DisableType.DISABLED) or \
(self.disabled == DisableType.PENDING and self.locked)
# Can the player see what's placed at this location without collecting it?
# Used to reduce JSON spoiler noise
def has_preview(self):
if self.type in ('Collectable', 'BossHeart', 'GS Token', 'Shop'):
return True
if self.type == 'Chest':
return self.scene == 0x10 or self.world.correct_chest_sizes # Treasure Chest Game Prize or CSMC
if self.type == 'NPC':
return self.scene in (0x4B, 0x51, 0x57) # Bombchu Bowling, Hyrule Field (OoT), Lake Hylia (RL/FA)
return False
def has_item(self):
return self.item is not None
def has_no_item(self):
return self.item is None
def has_progression_item(self):
return self.item is not None and self.item.advancement
def __str__(self):
return str(self.__unicode__())
def __unicode__(self):
return '%s' % self.name
def LocationFactory(locations, world=None):
ret = []
singleton = False
if isinstance(locations, str):
locations = [locations]
singleton = True
for location in locations:
if location in location_table:
match_location = location
else:
match_location = next(filter(lambda k: k.lower() == location.lower(), location_table), None)
if match_location:
type, scene, default, addresses, filter_tags = location_table[match_location]
if addresses is None:
addresses = (None, None)
address, address2 = addresses
ret.append(Location(match_location, address, address2, default, type, scene, filter_tags=filter_tags))
else:
raise KeyError('Unknown Location: %s', location)
if singleton:
return ret[0]
return ret
def LocationIterator(predicate=lambda loc: True):
for location_name in location_table:
location = LocationFactory(location_name)
if predicate(location):
yield location
def IsLocation(name):
return name in location_table
class DisableType(Enum):
ENABLED = 0
PENDING = 1
DISABLED = 2