forked from OoTRandomizer/OoT-Randomizer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Location.py
executable file
·148 lines (111 loc) · 4.78 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
from LocationList import location_table
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):
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.spot_type = 'Location'
self.recursion_count = { 'child': 0, 'adult': 0 }
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
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.spot_type = self.spot_type
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.minor_only = self.minor_only
new_location.disabled = self.disabled
return new_location
def add_rule(self, lambda_rule):
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.playthrough.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))
# tod is passed explicitly only when we want to test for it
def can_reach(self, state, age=None, tod=None):
if self.is_disabled():
return False
return self.access_rule(state, spot=self, age=age, tod=tod) and state.can_reach(self.parent_region, age=age, tod=tod)
def can_reach_simple(self, state, age=None):
# todo: raw evaluation of access_rule? requires nonrecursive tod checks in state
# and GS Token and Gossip Stone Fairy have special checks as well
return self.access_rule(state, age=age, spot=self)
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 # Treasure Chest Game Prize
if self.type == 'NPC':
return self.scene in (0x4B, 0x51, 0x57) # Bombchu Bowling, Hyrule Field (OoT), Lake Hylia (RL/FA)
return False
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:
type, scene, default, addresses, filter_tags = location_table[location]
if addresses is None:
addresses = (None, None)
address, address2 = addresses
ret.append(Location(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