Rogue Legacy: More refactoring and clean up. ()

* Rogue Legacy: More refactoring and clean up.

* Also marked Blacksmith as Progression as it's used in a rule.

* Remove extra newline.

* Prevent divide by zero type error.

* Scratch last commit, got the math mixed in my head.

* Clarified name of rule regarding percentage of stat upgrades.

* Move early vendors/architect creation into `create_items` logic.

* Rename parameter in `create_region`.

* Rename local var in `create_region`.

* Removed accidental links in Markdown docs.

* Refactor `create_region` signature and caller.

* Remove redundant if-else.

* Revert change to if-else, and moved item_pool to function instead of obj var.

* Rename LegacyLogic to RLLogic.

* Remove LogicMixin for rules.
This commit is contained in:
Zach Parks 2022-12-06 20:49:55 -06:00 committed by GitHub
parent 2cc03d003a
commit 82444229be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 156 additions and 191 deletions

View File

@ -14,10 +14,6 @@ class RLItemData(NamedTuple):
max_quantity: int = 1
weight: int = 1
@property
def is_event_item(self):
return self.code is None
def get_items_by_category(category: str) -> Dict[str, RLItemData]:
item_dict: Dict[str, RLItemData] = {}
@ -30,7 +26,7 @@ def get_items_by_category(category: str) -> Dict[str, RLItemData]:
item_table: Dict[str, RLItemData] = {
# Vendors
"Blacksmith": RLItemData("Vendors", 90_000, ItemClassification.useful),
"Blacksmith": RLItemData("Vendors", 90_000, ItemClassification.progression),
"Enchantress": RLItemData("Vendors", 90_001, ItemClassification.progression),
"Architect": RLItemData("Vendors", 90_002, ItemClassification.useful),

View File

@ -11,10 +11,6 @@ class RLLocationData(NamedTuple):
category: str
code: Optional[int] = None
@property
def is_event_location(self):
return self.code is None
def get_locations_by_category(category: str) -> Dict[str, RLLocationData]:
location_dict: Dict[str, RLLocationData] = {}

View File

@ -1,31 +1,27 @@
from typing import Dict, List, NamedTuple, Optional
from BaseClasses import MultiWorld, Region, RegionType, Entrance
from .Items import RLItem
from .Locations import RLLocation, location_table, get_locations_by_category
class RLRegionData(NamedTuple):
locations: Optional[List[str]]
exits: Optional[List[str]]
region_exits: Optional[List[str]]
def create_regions(multiworld: MultiWorld, player: int):
regions: Dict[str, RLRegionData] = {
"Menu": RLRegionData(None, ["Castle Hamson"]),
"The Manor": RLRegionData([], []),
"Castle Hamson": RLRegionData([], ["Forest Abkhazia",
"The Maya",
"Land of Darkness",
"The Fountain Room",
"The Manor"]),
"Forest Abkhazia": RLRegionData([], []),
"The Maya": RLRegionData([], []),
"Land of Darkness": RLRegionData([], []),
"The Fountain Room": RLRegionData([], None),
"Menu": RLRegionData(None, ["Castle Hamson"]),
"The Manor": RLRegionData([], []),
"Castle Hamson": RLRegionData([], ["Forest Abkhazia", "The Maya", "Land of Darkness",
"The Fountain Room", "The Manor"]),
"Forest Abkhazia": RLRegionData([], []),
"The Maya": RLRegionData([], []),
"Land of Darkness": RLRegionData([], []),
"The Fountain Room": RLRegionData([], None),
}
# Diaries
# Artificially stagger diary spheres for progression.
for diary in range(0, 25):
region: str
if 0 <= diary < 6:
@ -38,7 +34,6 @@ def create_regions(multiworld: MultiWorld, player: int):
region = "Land of Darkness"
else:
region = "The Fountain Room"
regions[region].locations.append(f"Diary {diary + 1}")
# Manor & Special
@ -90,7 +85,7 @@ def create_regions(multiworld: MultiWorld, player: int):
# Set up the regions correctly.
for name, data in regions.items():
multiworld.regions.append(create_region(multiworld, player, name, data.locations, data.exits))
multiworld.regions.append(create_region(multiworld, player, name, data))
multiworld.get_entrance("Castle Hamson", player).connect(multiworld.get_region("Castle Hamson", player))
multiworld.get_entrance("The Manor", player).connect(multiworld.get_region("The Manor", player))
@ -100,24 +95,17 @@ def create_regions(multiworld: MultiWorld, player: int):
multiworld.get_entrance("The Fountain Room", player).connect(multiworld.get_region("The Fountain Room", player))
def create_region(multiworld: MultiWorld, player: int, name: str, locations=None, exits=None):
ret = Region(name, RegionType.Generic, name, player)
ret.multiworld = multiworld
if locations:
for loc_name in locations:
def create_region(multiworld: MultiWorld, player: int, name: str, data: RLRegionData):
region = Region(name, RegionType.Generic, name, player, multiworld)
if data.locations:
for loc_name in data.locations:
loc_data = location_table.get(loc_name)
location = RLLocation(player, loc_name, loc_data.code if loc_data else None, ret)
location = RLLocation(player, loc_name, loc_data.code if loc_data else None, region)
region.locations.append(location)
# Special rule handling for fairy chests.
if "Fairy" in loc_name:
location.access_rule = lambda state: state.has("Dragons", player) or (
state.has("Enchantress", player) and (
state.has("Vault Runes", player) or
state.has("Sprint Runes", player) or
state.has("Sky Runes", player)))
ret.locations.append(location)
if exits:
for exit in exits:
entrance = Entrance(player, exit, ret)
ret.exits.append(entrance)
return ret
if data.region_exits:
for exit in data.region_exits:
entrance = Entrance(player, exit, region)
region.exits.append(entrance)
return region

View File

@ -1,37 +1,61 @@
from BaseClasses import MultiWorld, CollectionState
from ..AutoWorld import LogicMixin
from ..generic.Rules import set_rule
class LegacyLogic(LogicMixin):
def has_any_vendors(self: CollectionState, player: int) -> bool:
return self.has_any({"Blacksmith", "Enchantress"}, player)
def get_upgrade_total(multiworld: MultiWorld, player: int) -> int:
return int(multiworld.health_pool[player]) + int(multiworld.mana_pool[player]) + \
int(multiworld.attack_pool[player]) + int(multiworld.magic_damage_pool[player])
def has_all_vendors(self: CollectionState, player: int) -> bool:
return self.has_all({"Blacksmith", "Enchantress"}, player)
def has_stat_upgrades(self, player: int, amount: int) -> bool:
return self.stat_upgrade_count(player) >= amount
def get_upgrade_count(state: CollectionState, player: int) -> int:
return state.item_count("Health Up", player) + state.item_count("Mana Up", player) + \
state.item_count("Attack Up", player) + state.item_count("Magic Damage Up", player)
def total_stat_upgrades_count(self, player: int) -> int:
return int(self.multiworld.health_pool[player]) + \
int(self.multiworld.mana_pool[player]) + \
int(self.multiworld.attack_pool[player]) + \
int(self.multiworld.magic_damage_pool[player])
def stat_upgrade_count(self: CollectionState, player: int) -> int:
return self.item_count("Health Up", player) + self.item_count("Mana Up", player) + \
self.item_count("Attack Up", player) + self.item_count("Magic Damage Up", player)
def has_vendors(state: CollectionState, player: int) -> bool:
return state.has_all({"Blacksmith", "Enchantress"}, player)
def has_upgrade_amount(state: CollectionState, player: int, amount: int) -> bool:
return get_upgrade_count(state, player) >= amount
def has_upgrades_percentage(state: CollectionState, player: int, percentage: float) -> bool:
return has_upgrade_amount(state, player, get_upgrade_total(state.multiworld, player) * (round(percentage) // 100))
def has_movement_rune(state: CollectionState, player: int) -> bool:
return state.has("Vault Runes", player) or state.has("Sprint Runes", player) or state.has("Sky Runes", player)
def has_fairy_progression(state: CollectionState, player: int) -> bool:
return state.has("Dragons", player) or (state.has("Enchantress", player) and has_movement_rune(state, player))
def has_defeated_castle(state: CollectionState, player: int) -> bool:
return state.has("Defeat Khidr", player) or state.has("Defeat Neo Khidr", player)
def has_defeated_forest(state: CollectionState, player: int) -> bool:
return state.has("Defeat Alexander", player) or state.has("Defeat Alexander IV", player)
def has_defeated_tower(state: CollectionState, player: int) -> bool:
return state.has("Defeat Ponce de Leon", player) or state.has("Defeat Ponce de Freon", player)
def has_defeated_dungeon(state: CollectionState, player: int) -> bool:
return state.has("Defeat Herodotus", player) or state.has("Defeat Astrodotus", player)
def set_rules(multiworld: MultiWorld, player: int):
# Vendors
# If 'vendors' are 'normal', then expect it to show up in the first half(ish) of the spheres.
if multiworld.vendors[player] == "normal":
set_rule(multiworld.get_location("Forest Abkhazia Boss Reward", player),
lambda state: state.has_all_vendors(player))
lambda state: has_vendors(state, player))
# Scale each manor location.
# Gate each manor location so everything isn't dumped into sphere 1.
manor_rules = {
"Defeat Khidr" if multiworld.khidr[player] == "vanilla" else "Defeat Neo Khidr": [
"Manor - Left Wing Window",
@ -65,22 +89,27 @@ def set_rules(multiworld: MultiWorld, player: int):
]
}
# Set rules for manor locations.
for event, locations in manor_rules.items():
for location in locations:
set_rule(multiworld.get_location(location, player), lambda state: state.has(event, player))
# Standard Zone Progression
multiworld.get_entrance("Forest Abkhazia", player).access_rule = \
(lambda state: state.has_stat_upgrades(player, 0.125 * state.total_stat_upgrades_count(player)) and
(state.has("Defeat Khidr", player) or state.has("Defeat Neo Khidr", player)))
multiworld.get_entrance("The Maya", player).access_rule = \
(lambda state: state.has_stat_upgrades(player, 0.25 * state.total_stat_upgrades_count(player)) and
(state.has("Defeat Alexander", player) or state.has("Defeat Alexander IV", player)))
multiworld.get_entrance("Land of Darkness", player).access_rule = \
(lambda state: state.has_stat_upgrades(player, 0.375 * state.total_stat_upgrades_count(player)) and
(state.has("Defeat Ponce de Leon", player) or state.has("Defeat Ponce de Freon", player)))
multiworld.get_entrance("The Fountain Room", player).access_rule = \
(lambda state: state.has_stat_upgrades(player, 0.5 * state.total_stat_upgrades_count(player)) and
(state.has("Defeat Herodotus", player) or state.has("Defeat Astrodotus", player)))
# Set rules for fairy chests to decrease headache of expectation to find non-movement fairy chests.
for fairy_location in [location for location in multiworld.get_locations(player) if "Fairy" in location.name]:
set_rule(fairy_location, lambda state: has_fairy_progression(state, player))
# Region rules.
multiworld.get_entrance("Forest Abkhazia", player).access_rule = \
lambda state: has_upgrades_percentage(state, player, 12.5) and has_defeated_castle(state, player)
multiworld.get_entrance("The Maya", player).access_rule = \
lambda state: has_upgrades_percentage(state, player, 25) and has_defeated_forest(state, player)
multiworld.get_entrance("Land of Darkness", player).access_rule = \
lambda state: has_upgrades_percentage(state, player, 37.5) and has_defeated_tower(state, player)
multiworld.get_entrance("The Fountain Room", player).access_rule = \
lambda state: has_upgrades_percentage(state, player, 50) and has_defeated_dungeon(state, player)
# Win condition.
multiworld.completion_condition[player] = lambda state: state.has("Defeat The Fountain", player)

View File

@ -1,38 +0,0 @@
traits = [
"Color Blind",
"Gay",
"Near-Sighted",
"Far-Sighted",
"Dyslexia",
"Gigantism",
"Dwarfism",
"Baldness",
"Endomorph",
"Ectomorph",
"Alzheimers",
"Dextrocardia",
"Coprolalia",
"ADHD",
"O.C.D.",
"Hypergonadism",
"Muscle Wk.",
"Stereo Blind",
"I.B.S.",
"Vertigo",
"Tunnel Vision",
"Ambilevous",
"P.A.D.",
"Alektorophobia",
"Hypochondriac",
"Dementia",
"Flexible",
"Eid. Mem.",
"Nostalgic",
"C.I.P.",
"Savant",
"The One",
"Clumsy",
"EHS",
"Glaucoma",
"Adopted",
]

View File

@ -30,7 +30,7 @@ class RLWorld(World):
you. Every child is unique. One child might be colorblind, another might have vertigo-- they could even be a dwarf.
But that's OK, because no one is perfect, and you don't have to be to succeed.
"""
game: str = "Rogue Legacy"
game = "Rogue Legacy"
option_definitions = rl_options
topology_present = True
data_version = 4
@ -40,158 +40,154 @@ class RLWorld(World):
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = {name: data.code for name, data in location_table.items()}
item_pool: List[RLItem] = []
def setting(self, name: str):
# TODO: Replace calls to this function with "options-dict", once that PR is completed and merged.
def get_setting(self, name: str):
return getattr(self.multiworld, name)[self.player]
def fill_slot_data(self) -> dict:
return {option_name: self.setting(option_name).value for option_name in rl_options}
return {option_name: self.get_setting(option_name).value for option_name in rl_options}
def generate_early(self):
# Check validation of names.
additional_lady_names = len(self.setting("additional_lady_names").value)
additional_sir_names = len(self.setting("additional_sir_names").value)
if not self.setting("allow_default_names"):
# Check for max_quantity.
if additional_lady_names < int(self.setting("number_of_children")):
additional_lady_names = len(self.get_setting("additional_lady_names").value)
additional_sir_names = len(self.get_setting("additional_sir_names").value)
if not self.get_setting("allow_default_names"):
if additional_lady_names < int(self.get_setting("number_of_children")):
raise Exception(
f"allow_default_names is off, but not enough names are defined in additional_lady_names. "
f"Expected {int(self.setting('number_of_children'))}, Got {additional_lady_names}")
f"Expected {int(self.get_setting('number_of_children'))}, Got {additional_lady_names}")
if additional_sir_names < int(self.setting("number_of_children")):
if additional_sir_names < int(self.get_setting("number_of_children")):
raise Exception(
f"allow_default_names is off, but not enough names are defined in additional_sir_names. "
f"Expected {int(self.setting('number_of_children'))}, Got {additional_sir_names}")
f"Expected {int(self.get_setting('number_of_children'))}, Got {additional_sir_names}")
if self.setting("vendors") == "early":
self.multiworld.local_early_items[self.player]["Blacksmith"] = 1
self.multiworld.local_early_items[self.player]["Enchantress"] = 1
if self.setting("architect") == "early":
self.multiworld.local_early_items[self.player]["Architect"] = 1
def generate_basic(self):
self.item_pool = []
total_locations = 64 + (self.setting("chests_per_zone") * 4) + (self.setting("fairy_chests_per_zone") * 4)
# Add items to item pool. Anything with a "continue" will not be added to the item pool.
def create_items(self):
item_pool: List[RLItem] = []
total_locations = len(self.multiworld.get_unfilled_locations(self.player))
for name, data in item_table.items():
quantity = data.max_quantity
# Architect
if name == "Architect":
if self.setting("architect") == "disabled":
if self.get_setting("architect") == "disabled":
continue
if self.setting("architect") == "start_unlocked":
if self.get_setting("architect") == "start_unlocked":
self.multiworld.push_precollected(self.create_item(name))
continue
if self.get_setting("architect") == "early":
self.multiworld.local_early_items[self.player]["Architect"] = 1
continue
# Blacksmith and Enchantress
if name == "Blacksmith" or name == "Enchantress":
if self.setting("vendors") == "start_unlocked":
if self.get_setting("vendors") == "start_unlocked":
self.multiworld.push_precollected(self.create_item(name))
continue
if self.get_setting("vendors") == "early":
self.multiworld.local_early_items[self.player]["Blacksmith"] = 1
self.multiworld.local_early_items[self.player]["Enchantress"] = 1
continue
# Haggling
if name == "Haggling" and self.setting("disable_charon"):
if name == "Haggling" and self.get_setting("disable_charon"):
continue
# Blueprints
if data.category == "Blueprints":
# No progressive blueprints if progressive_blueprints are disabled.
if name == "Progressive Blueprints" and not self.setting("progressive_blueprints"):
if name == "Progressive Blueprints" and not self.get_setting("progressive_blueprints"):
continue
# No distinct blueprints if progressive_blueprints are enabled.
elif name != "Progressive Blueprints" and self.setting("progressive_blueprints"):
elif name != "Progressive Blueprints" and self.get_setting("progressive_blueprints"):
continue
# Classes
if data.category == "Classes":
if name == "Progressive Knights":
if "Knight" not in self.setting("available_classes"):
if "Knight" not in self.get_setting("available_classes"):
continue
if self.setting("starting_class") == "knight":
if self.get_setting("starting_class") == "knight":
quantity = 1
if name == "Progressive Mages":
if "Mage" not in self.setting("available_classes"):
if "Mage" not in self.get_setting("available_classes"):
continue
if self.setting("starting_class") == "mage":
if self.get_setting("starting_class") == "mage":
quantity = 1
if name == "Progressive Barbarians":
if "Barbarian" not in self.setting("available_classes"):
if "Barbarian" not in self.get_setting("available_classes"):
continue
if self.setting("starting_class") == "barbarian":
if self.get_setting("starting_class") == "barbarian":
quantity = 1
if name == "Progressive Knaves":
if "Knave" not in self.setting("available_classes"):
if "Knave" not in self.get_setting("available_classes"):
continue
if self.setting("starting_class") == "knave":
if self.get_setting("starting_class") == "knave":
quantity = 1
if name == "Progressive Miners":
if "Miner" not in self.setting("available_classes"):
if "Miner" not in self.get_setting("available_classes"):
continue
if self.setting("starting_class") == "miner":
if self.get_setting("starting_class") == "miner":
quantity = 1
if name == "Progressive Shinobis":
if "Shinobi" not in self.setting("available_classes"):
if "Shinobi" not in self.get_setting("available_classes"):
continue
if self.setting("starting_class") == "shinobi":
if self.get_setting("starting_class") == "shinobi":
quantity = 1
if name == "Progressive Liches":
if "Lich" not in self.setting("available_classes"):
if "Lich" not in self.get_setting("available_classes"):
continue
if self.setting("starting_class") == "lich":
if self.get_setting("starting_class") == "lich":
quantity = 1
if name == "Progressive Spellthieves":
if "Spellthief" not in self.setting("available_classes"):
if "Spellthief" not in self.get_setting("available_classes"):
continue
if self.setting("starting_class") == "spellthief":
if self.get_setting("starting_class") == "spellthief":
quantity = 1
if name == "Dragons":
if "Dragon" not in self.setting("available_classes"):
if "Dragon" not in self.get_setting("available_classes"):
continue
if name == "Traitors":
if "Traitor" not in self.setting("available_classes"):
if "Traitor" not in self.get_setting("available_classes"):
continue
# Skills
if name == "Health Up":
quantity = self.setting("health_pool")
quantity = self.get_setting("health_pool")
elif name == "Mana Up":
quantity = self.setting("mana_pool")
quantity = self.get_setting("mana_pool")
elif name == "Attack Up":
quantity = self.setting("attack_pool")
quantity = self.get_setting("attack_pool")
elif name == "Magic Damage Up":
quantity = self.setting("magic_damage_pool")
quantity = self.get_setting("magic_damage_pool")
elif name == "Armor Up":
quantity = self.setting("armor_pool")
quantity = self.get_setting("armor_pool")
elif name == "Equip Up":
quantity = self.setting("equip_pool")
quantity = self.get_setting("equip_pool")
elif name == "Crit Chance Up":
quantity = self.setting("crit_chance_pool")
quantity = self.get_setting("crit_chance_pool")
elif name == "Crit Damage Up":
quantity = self.setting("crit_damage_pool")
quantity = self.get_setting("crit_damage_pool")
# Ignore filler, it will be added in a later stage.
if data.category == "Filler":
continue
self.item_pool += [self.create_item(name) for _ in range(0, quantity)]
item_pool += [self.create_item(name) for _ in range(0, quantity)]
# Fill any empty locations with filler items.
while len(self.item_pool) < total_locations:
self.item_pool.append(self.create_item(self.get_filler_item_name()))
while len(item_pool) < total_locations:
item_pool.append(self.create_item(self.get_filler_item_name()))
self.multiworld.itempool += self.item_pool
self.multiworld.itempool += item_pool
def get_filler_item_name(self) -> str:
fillers = get_items_by_category("Filler")
@ -219,7 +215,7 @@ class RLWorld(World):
self.create_event("Defeat The Fountain"))
# Khidr / Neo Khidr
if self.setting("khidr") == "vanilla":
if self.get_setting("khidr") == "vanilla":
self.multiworld.get_location("Castle Hamson Boss Room", self.player).place_locked_item(
self.create_event("Defeat Khidr"))
else:
@ -227,7 +223,7 @@ class RLWorld(World):
self.create_event("Defeat Neo Khidr"))
# Alexander / Alexander IV
if self.setting("alexander") == "vanilla":
if self.get_setting("alexander") == "vanilla":
self.multiworld.get_location("Forest Abkhazia Boss Room", self.player).place_locked_item(
self.create_event("Defeat Alexander"))
else:
@ -235,7 +231,7 @@ class RLWorld(World):
self.create_event("Defeat Alexander IV"))
# Ponce de Leon / Ponce de Freon
if self.setting("leon") == "vanilla":
if self.get_setting("leon") == "vanilla":
self.multiworld.get_location("The Maya Boss Room", self.player).place_locked_item(
self.create_event("Defeat Ponce de Leon"))
else:
@ -243,7 +239,7 @@ class RLWorld(World):
self.create_event("Defeat Ponce de Freon"))
# Herodotus / Astrodotus
if self.setting("herodotus") == "vanilla":
if self.get_setting("herodotus") == "vanilla":
self.multiworld.get_location("Land of Darkness Boss Room", self.player).place_locked_item(
self.create_event("Defeat Herodotus"))
else:

View File

@ -3,7 +3,7 @@
## Where is the settings page?
The [player settings page for this game](../player-settings) contains most of the options you need to
configure and export a config file. Some settings can only be made via YAML, but an explaination can be found in the
configure and export a config file. Some settings can only be made in YAML, but an explanation can be found in the
[template yaml here](../../../static/generated/configs/Rogue%20Legacy.yaml).
## What does randomization do to this game?
@ -13,7 +13,6 @@ upgrade screen, bosses, and some special individual locations. The goal is to be
zone bosses and then defeat The Fountain.
## What items and locations get shuffled?
All the skill upgrades, class upgrades, runes packs, and equipment packs are shuffled in the manor upgrade screen, diary
checks, chests and fairy chests, and boss rewards. Skill upgrades are also grouped in packs of 5 to make the finding of
stats less of a chore. Runes and Equipment are also grouped together.
@ -24,7 +23,6 @@ Some additional locations that can contain items are the Jukebox, the Portraits,
Any of the items which can be shuffled may also be placed into another player's world. It is possible to choose to limit
certain items to your own world.
## When the player receives an item, what happens?
When the player receives an item, your character will hold the item above their head and display it to the world. It's
@ -33,4 +31,4 @@ good for business!
## What do I do if I encounter a bug with the game?
Please reach out to Phar#4444 on Discord or you can drop a bug report on the
[GitHub page for Rogue Legacy Randomizer](https://github.com/ThePhar/RogueLegacyRandomizer/issues/new?assignees=&labels=bug&template=report-an-issue---.md&title=%5BIssue%5D).
[GitHub page for Rogue Legacy Randomizer](https://github.com/ThePhar/RogueLegacyRandomizer/issues/new?assignees=&labels=bug&template=report-an-issue---.md&title=%5BIssue%5D).

View File

@ -2,8 +2,14 @@
## Required Software
- Rogue Legacy Randomizer from
the [Rogue Legacy Randomizer Releases Page](https://github.com/ThePhar/RogueLegacyRandomizer/releases)
- Rogue Legacy Randomizer from the
[Rogue Legacy Randomizer Releases Page](https://github.com/ThePhar/RogueLegacyRandomizer/releases)
## Recommended Installation Instructions
Please read the README file on the
[Rogue Legacy Randomizer GitHub](https://github.com/ThePhar/RogueLegacyRandomizer/blob/master/README.md) page for
up-to-date installation instructions.
## Configuring your YAML file
@ -27,9 +33,3 @@ provides an alternative one to the default values.
Once you have entered the required values, you go to Connect and then select Confirm on the "Ready to Start" screen. Now
you're off to start your legacy!
## Recommended Installation Instructions
Please read the README file on the
[Rogue Legacy Randomizer GitHub](https://github.com/ThePhar/RogueLegacyRandomizer/blob/master/README.md) page for up to
date installation instructions.