Files
Grinch-AP/worlds/soe/__init__.py

150 lines
5.0 KiB
Python
Raw Normal View History

2021-09-29 09:12:23 +02:00
from .Options import soe_options
from ..AutoWorld import World
from ..generic.Rules import set_rule
from BaseClasses import Region, Location, Entrance, Item
import typing
from . import Logic # load logic mixin
try:
import pyevermizer # from package
except ImportError:
from . import pyevermizer # as part of the source tree
"""
In evermizer:
Items are uniquely defined by a pair of (type, id).
For most items this is their vanilla location (i.e. CHECK_GOURD, number).
Items have `provides`, which give the actual progression
instead of providing multiple events per item, we iterate through them in Logic.py
e.g. Found any weapon
Locations have `requires` and `provides`.
Requirements have to be converted to (access) rules for AP
e.g. Chest locked behind having a weapon
Provides could be events, but instead we iterate through the entire logic in Logic.py
e.g. NPC available after fighting a Boss
Rules are special locations that don't have a physical location
instead of implementing virtual locations and virtual items, we simply use them in Logic.py
e.g. 2DEs+Wheel+Gauge = Rocket
Rules and Locations live on the same logic tree returned by pyevermizer.get_logic()
TODO: for balancing we may want to generate Regions (with Entrances) for some
common rules, place the locations in those Regions and shorten the rules.
"""
GAME_NAME = "Secret of Evermore"
ID_OFF_BASE = 64000
ID_OFFS: typing.Dict[int,int] = {
pyevermizer.CHECK_ALCHEMY: ID_OFF_BASE + 0, # alchemy 64000..64049
pyevermizer.CHECK_BOSS: ID_OFF_BASE + 50, # bosses 64050..6499
pyevermizer.CHECK_GOURD: ID_OFF_BASE + 100, # gourds 64100..64399
pyevermizer.CHECK_NPC: ID_OFF_BASE + 400, # npc 64400..64499
# TODO: sniff 64500..64799
}
def _get_locations():
locs = pyevermizer.get_locations()
for loc in locs:
if loc.type == 3: # TODO: CHECK_GOURD
loc.name = f'{loc.name} #{loc.index}'
return locs
def _get_location_ids():
m = {}
for loc in _get_locations():
m[loc.name] = ID_OFFS[loc.type] + loc.index
m['Done'] = None
return m
def _get_items():
return pyevermizer.get_items()
def _get_item_ids():
m = {}
for item in _get_items():
if item.name in m: continue
m[item.name] = ID_OFFS[item.type] + item.index
m['Victory'] = None
return m
class SoEWorld(World):
"""
TODO: insert game description here
"""
game: str = GAME_NAME
# options = soe_options
topology_present: bool = True
item_name_to_id = _get_item_ids()
location_name_to_id = _get_location_ids()
remote_items: bool = True # False # True only for testing
def generate_basic(self):
print('SoE: generate_basic')
itempool = [item for item in map(lambda item: self.create_item(item), _get_items())]
self.world.itempool += itempool
self.world.get_location('Done', self.player).place_locked_item(self.create_event('Victory'))
def create_regions(self):
# TODO: generate *some* regions from locations' requirements
r = Region('Menu', None, 'Menu', self.player, self.world)
r.exits = [Entrance(self.player, 'New Game', r)]
self.world.regions += [r]
r = Region('Ingame', None, 'Ingame', self.player, self.world)
r.locations = [SoELocation(self.player, loc.name, self.location_name_to_id[loc.name], r)
for loc in _get_locations()]
r.locations.append(SoELocation(self.player, 'Done', None, r))
self.world.regions += [r]
self.world.get_entrance('New Game', self.player).connect(self.world.get_region('Ingame', self.player))
def create_event(self, event: str) -> Item:
progression = True
return SoEItem(event, progression, None, self.player)
def create_item(self, item) -> Item:
# TODO: if item is string: look up item by name
return SoEItem(item.name, item.progression, self.item_name_to_id[item.name], self.player)
def set_rules(self):
print('SoE: set_rules')
self.world.completion_condition[self.player] = lambda state: state.has('Victory', self.player)
# set Done from goal option once we have multiple goals
set_rule(self.world.get_location('Done', self.player),
lambda state: state._soe_has(pyevermizer.P_FINAL_BOSS, self.world, self.player))
set_rule(self.world.get_entrance('New Game', self.player), lambda state: True)
for loc in _get_locations():
set_rule(self.world.get_location(loc.name, self.player), self.make_rule(loc.requires))
def make_rule(self, requires):
def rule(state):
for count, progress in requires:
if not state._soe_has(progress, self.world, self.player, count):
return False
return True
return rule
class SoEItem(Item):
game: str = GAME_NAME
class SoELocation(Location):
game: str = GAME_NAME
def __init__(self, player: int, name: str, address: typing.Optional[int], parent):
super().__init__(player, name, address, parent)
self.event = not address