Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions src/Data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
from .DataValidation import DataValidation, ValidationError
from .Helpers import load_data_file as helpers_load_data_file

from .hooks.Data import \
after_load_game_file, \
after_load_item_file, after_load_location_file, \
after_load_region_file, after_load_category_file, \
after_load_option_file, after_load_meta_file
from .manual.Hooks import DataHooks

# blatantly copied from the minecraft ap world because why not
def load_data_file(*args) -> dict:
Expand Down Expand Up @@ -35,6 +31,9 @@ def load(self):

return contents

hooks = {
"data": DataHooks()
}

game_table = ManualFile('game.json', dict).load() #dict
item_table = convert_to_list(ManualFile('items.json', list).load(), 'data') #list
Expand All @@ -49,13 +48,13 @@ def load(self):
category_table.pop('$schema', '')

# hooks
game_table = after_load_game_file(game_table)
item_table = after_load_item_file(item_table)
location_table = after_load_location_file(location_table)
region_table = after_load_region_file(region_table)
category_table = after_load_category_file(category_table)
option_table = after_load_option_file(option_table)
meta_table = after_load_meta_file(meta_table)
game_table = hooks["data"].call('after_load_game_file', game_table) or game_table
item_table = hooks["data"].call('after_load_item_file', item_table) or item_table
location_table = hooks["data"].call('after_load_location_file', location_table) or location_table
region_table = hooks["data"].call('after_load_region_file', region_table) or region_table
category_table = hooks["data"].call('after_load_category_file', category_table) or category_table
option_table = hooks["data"].call('after_load_option_file', option_table) or option_table
meta_table = hooks["data"].call('after_load_meta_file', meta_table) or meta_table

# seed all of the tables for validation
DataValidation.game_table = game_table
Expand Down
14 changes: 9 additions & 5 deletions src/Helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
from BaseClasses import MultiWorld, Item
from typing import Optional, List, TYPE_CHECKING
from worlds.AutoWorld import World
from .hooks.Helpers import before_is_category_enabled, before_is_item_enabled, before_is_location_enabled
from .manual.Hooks import HelpersHooks

from typing import Union

if TYPE_CHECKING:
from .Items import ManualItem
from .Locations import ManualLocation

hooks = {
"helpers": HelpersHooks()
}

# blatantly copied from the minecraft ap world because why not
def load_data_file(*args) -> dict:
fname = os.path.join("data", *args)
Expand Down Expand Up @@ -58,7 +62,7 @@ def clamp(value, min, max):
def is_category_enabled(multiworld: MultiWorld, player: int, category_name: str) -> bool:
from .Data import category_table
"""Check if a category has been disabled by a yaml option."""
hook_result = before_is_category_enabled(multiworld, player, category_name)
hook_result = hooks["helpers"].before_is_category_enabled(multiworld, player, category_name) or None
if hook_result is not None:
return hook_result

Expand Down Expand Up @@ -88,7 +92,7 @@ def is_item_name_enabled(multiworld: MultiWorld, player: int, item_name: str) ->

def is_item_enabled(multiworld: MultiWorld, player: int, item: "ManualItem") -> bool:
"""Check if an item has been disabled by a yaml option."""
hook_result = before_is_item_enabled(multiworld, player, item)
hook_result = hooks["helpers"].before_is_item_enabled(multiworld, player, item) or None
if hook_result is not None:
return hook_result

Expand All @@ -104,7 +108,7 @@ def is_location_name_enabled(multiworld: MultiWorld, player: int, location_name:

def is_location_enabled(multiworld: MultiWorld, player: int, location: "ManualLocation") -> bool:
"""Check if a location has been disabled by a yaml option."""
hook_result = before_is_location_enabled(multiworld, player, location)
hook_result = hooks["helpers"].before_is_location_enabled(multiworld, player, location) or None
if hook_result is not None:
return hook_result

Expand Down Expand Up @@ -212,4 +216,4 @@ def format_to_valid_identifier(input: str) -> str:
input = input.strip()
if input[:1].isdigit():
input = "_" + input
return input.replace(" ", "_")
return input.replace(" ", "_")
14 changes: 9 additions & 5 deletions src/Options.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
from Options import PerGameCommonOptions, FreeText, Toggle, DefaultOnToggle, Choice, TextChoice, Range, NamedRange, DeathLink, \
OptionGroup, StartInventoryPool, Visibility, item_and_loc_options, Option
from .hooks.Options import before_options_defined, after_options_defined, before_option_groups_created, after_option_groups_created
from .Data import category_table, game_table, option_table
from .Helpers import convert_to_long_string, format_to_valid_identifier
from .Locations import victory_names
from .Items import item_table
from .Game import starting_items

from .manual.Hooks import OptionsHooks

from dataclasses import make_dataclass
from typing import List
import logging

hooks = {
"options": OptionsHooks()
}

class FillerTrapPercent(Range):
"""How many fillers will be replaced with traps. 0 means no additional traps, 100 means all fillers are traps."""
Expand Down Expand Up @@ -58,7 +62,7 @@ def addOptionToGroup(option_name: str, group: str):
# Manual's default options
######################

manual_options = before_options_defined({})
manual_options = hooks["options"].call('before_options_defined', {}) or {}
manual_options["start_inventory_from_pool"] = StartInventoryPool

if len(victory_names) > 1:
Expand Down Expand Up @@ -227,7 +231,7 @@ def addOptionToGroup(option_name: str, group: str):

def make_options_group() -> list[OptionGroup]:
global manual_option_groups
manual_option_groups = before_option_groups_created(manual_option_groups)
manual_option_groups = hooks["options"].call('before_option_groups_created', manual_option_groups) or manual_option_groups
option_groups: List[OptionGroup] = []

# For some reason, unless they are added manually, the base item and loc option don't get grouped as they should
Expand All @@ -243,7 +247,7 @@ def make_options_group() -> list[OptionGroup]:

option_groups.append(OptionGroup('Item & Location Options', base_item_loc_group, True))

return after_option_groups_created(option_groups)
return hooks["options"].call('after_option_groups_created', option_groups) or option_groups

manual_options_data = make_dataclass('ManualOptionsClass', manual_options.items(), bases=(PerGameCommonOptions,))
after_options_defined(manual_options_data)
hooks["options"].call('after_options_defined', manual_options_data)
8 changes: 6 additions & 2 deletions src/Rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from enum import IntEnum
from worlds.generic.Rules import set_rule, add_rule
from .Regions import regionMap
from .hooks import Rules
from .manual.Hooks import RulesHooks

from BaseClasses import MultiWorld, CollectionState
from .Helpers import clamp, is_item_enabled, get_items_with_value, is_option_enabled
Expand All @@ -16,6 +16,10 @@
if TYPE_CHECKING:
from . import ManualWorld

hooks = {
"rules": RulesHooks()
}

class LogicErrorSource(IntEnum):
INFIX_TO_POSTFIX = 1 # includes more closing parentheses than opening (but not the opposite)
EVALUATE_POSTFIX = 2 # includes missing pipes and missing value on either side of AND/OR
Expand Down Expand Up @@ -125,7 +129,7 @@ def findAndRecursivelyExecuteFunctions(requires_list: str, recursionDepth: int =
func = globals().get(func_name)

if func is None:
func = getattr(Rules, func_name, None)
func = getattr(hooks["rules"], func_name, None)

if not callable(func):
raise ValueError(f"Invalid function `{func_name}` in {area}.")
Expand Down
52 changes: 24 additions & 28 deletions src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,12 @@
from Options import PerGameCommonOptions
from worlds.AutoWorld import World, WebWorld

from .hooks.World import \
hook_get_filler_item_name, before_create_regions, after_create_regions, \
before_create_items_starting, before_create_items_filler, after_create_items, \
before_create_item, after_create_item, \
before_set_rules, after_set_rules, \
before_generate_basic, after_generate_basic, \
before_fill_slot_data, after_fill_slot_data, before_write_spoiler, \
before_extend_hint_information, after_extend_hint_information
from .hooks.Data import hook_interpret_slot_data
from .manual.Hooks import DataHooks, WorldHooks

hooks = {
"data": DataHooks(),
"world": WorldHooks()
}

class ManualWorld(World):
__doc__ = world_description
Expand Down Expand Up @@ -71,7 +68,7 @@ class ManualWorld(World):
ut_can_gen_without_yaml = True

def get_filler_item_name(self) -> str:
return hook_get_filler_item_name(self, self.multiworld, self.player) or self.filler_item_name
return hooks["world"].call('hook_get_filler_item_name', self, self.multiworld, self.player) or self.filler_item_name

def interpret_slot_data(self, slot_data: dict[str, any]):
#this is called by tools like UT
Expand All @@ -82,16 +79,15 @@ def interpret_slot_data(self, slot_data: dict[str, any]):
getattr(self.options, key).value = value
regen = True

regen = hook_interpret_slot_data(self, self.player, slot_data) or regen
regen = hooks["data"].call('hook_interpret_slot_data', self, self.player, slot_data) or regen
return regen

@classmethod
def stage_assert_generate(cls, multiworld) -> None:
runGenerationDataValidation()


def create_regions(self):
before_create_regions(self, self.multiworld, self.player)
hooks["world"].call('before_create_regions', self, self.multiworld, self.player)

create_regions(self, self.multiworld, self.player)

Expand All @@ -104,7 +100,7 @@ def create_regions(self):
location_game_complete.place_locked_item(
ManualItem("__Victory__", ItemClassification.progression, None, player=self.player))

after_create_regions(self, self.multiworld, self.player)
hooks["world"].call('after_create_regions', self, self.multiworld, self.player)

def create_items(self):
# Generate item pool
Expand Down Expand Up @@ -159,7 +155,7 @@ def create_items(self):
raise Exception(f"Item {name}'s 'local_early' has an invalid value of '{item['local_early']}'. \nA boolean or an integer was expected.")


pool = before_create_items_starting(pool, self, self.multiworld, self.player)
pool = hooks["world"].call('before_create_items_starting', pool, self, self.multiworld, self.player) or pool

items_started = []

Expand Down Expand Up @@ -200,16 +196,16 @@ def create_items(self):

self.start_inventory = {i.name: items_started.count(i) for i in items_started}

pool = before_create_items_filler(pool, self, self.multiworld, self.player)
pool = hooks["world"].call('before_create_items_filler', pool, self, self.multiworld, self.player) or pool
pool = self.adjust_filler_items(pool, traps)
pool = after_create_items(pool, self, self.multiworld, self.player)
pool = hooks["world"].call('after_create_items', pool, self, self.multiworld, self.player) or pool

# need to put all of the items in the pool so we can have a full state for placement
# then will remove specific item placements below from the overall pool
self.multiworld.itempool += pool

def create_item(self, name: str) -> Item:
name = before_create_item(name, self, self.multiworld, self.player)
name = hooks["world"].call('before_create_item', name, self, self.multiworld, self.player) or name

item = self.item_name_to_item[name]
classification = ItemClassification.filler
Expand All @@ -228,19 +224,19 @@ def create_item(self, name: str) -> Item:
item_object = ManualItem(name, classification,
self.item_name_to_id[name], player=self.player)

item_object = after_create_item(item_object, self, self.multiworld, self.player)
item_object = hooks["world"].call('after_create_item', item_object, self, self.multiworld, self.player) or item_object

return item_object

def set_rules(self):
before_set_rules(self, self.multiworld, self.player)
hooks["world"].call('before_set_rules', self, self.multiworld, self.player)

set_rules(self, self.multiworld, self.player)

after_set_rules(self, self.multiworld, self.player)
hooks["world"].call('after_set_rules', self, self.multiworld, self.player)

def generate_basic(self):
before_generate_basic(self, self.multiworld, self.player)
hooks["world"].call('before_generate_basic', self, self.multiworld, self.player)

# Handle item forbidding
manual_locations_with_forbid = {location['name']: location for location in location_name_to_location.values() if "dont_place_item" in location or "dont_place_item_category" in location}
Expand Down Expand Up @@ -307,7 +303,7 @@ def generate_basic(self):
self.multiworld.itempool.remove(item_to_place)


after_generate_basic(self, self.multiworld, self.player)
hooks["world"].call('after_generate_basic', self, self.multiworld, self.player)

# Enable this in Meta.json to generate a diagram of your manual. Only works on 0.4.4+
if enable_region_diagram:
Expand All @@ -319,7 +315,7 @@ def pre_fill(self):
runPreFillDataValidation(self, self.multiworld)

def fill_slot_data(self):
slot_data = before_fill_slot_data({}, self, self.multiworld, self.player)
slot_data = hooks["world"].call('before_fill_slot_data', {}, self, self.multiworld, self.player) or {}

# slot_data["DeathLink"] = bool(self.multiworld.death_link[self.player].value)
common_options = set(PerGameCommonOptions.type_hints.keys())
Expand All @@ -328,7 +324,7 @@ def fill_slot_data(self):
continue
slot_data[option_key] = get_option_value(self.multiworld, self.player, option_key)

slot_data = after_fill_slot_data(slot_data, self, self.multiworld, self.player)
slot_data = hooks["world"].call('after_fill_slot_data', slot_data, self, self.multiworld, self.player) or slot_data

return slot_data

Expand All @@ -339,10 +335,10 @@ def generate_output(self, output_directory: str):
f.write(b64encode(bytes(json.dumps(data), 'utf-8')))

def write_spoiler(self, spoiler_handle):
before_write_spoiler(self, self.multiworld, spoiler_handle)
hooks["world"].call('before_write_spoiler', self, self.multiworld, spoiler_handle)

def extend_hint_information(self, hint_data: dict[int, dict[int, str]]) -> None:
before_extend_hint_information(hint_data, self, self.multiworld, self.player)
hooks["world"].call('before_extend_hint_information', hint_data, self, self.multiworld, self.player)

for location in self.multiworld.get_locations(self.player):
if not location.address:
Expand All @@ -352,7 +348,7 @@ def extend_hint_information(self, hint_data: dict[int, dict[int, str]]) -> None:
hint_data.update({self.player: {}})
hint_data[self.player][location.address] = self.location_name_to_location[location.name]["hint_entrance"]

after_extend_hint_information(hint_data, self, self.multiworld, self.player)
hooks["world"].call('after_extend_hint_information', hint_data, self, self.multiworld, self.player)

###
# Non-standard AP world methods
Expand Down
43 changes: 0 additions & 43 deletions src/hooks/Data.py

This file was deleted.

Loading