Rogue Legacy: World folder clean up and generation improvements. (#1148)
* Minor cleanup and renaming of some files/functions. * Rename `LegacyWorld` and `LegacyWeb` to RLWorld and RLWeb. * Undo accidental change to comment. * Undo accidental change to comment. * Restructure Items.py format and combine all tables into one. * Restructure Locations.py format and combine all tables into one. * Split boss event items into separate boss entries. * Remove definitions folder. * Reformatted __init__.py for Rogue Legacy. * Allow fairy chests to be disabled. * Add working prefill logic for early vendors. * Re-introduce Early Architect setting. * Revamped rules and regions and can now generate games. * Fix normal vendors breaking everything. * Fix early vendor logic and add fairy chest logic to require Dragons or Enchantress + runes. * Fix issue with duplicate items being created. * Move event placement into __init__.py and fix duplicate Vendors. * Tweak weights and spacing. * Update documentation and include bug report link. * Fix relative link for template file. * Increase amount of chest locations in `location_table`. * Correct a refactor rename gone wrong. * Remove unused reference in imports. * Tweak mistake in boss name in place_events. * English is hard. * Tweak some lines in __init__.py to use `.settings()` method. * Add unique id tests for Rogue Legacy. IDs are mixed around, so let's try to avoid accidentally using the same identifier twice. * Fix typo in doc. * Simplify `fill_slot_data`. * Change prefix on `_place_events` to maintain convention. * Remove items that are **not** progression from rules.
This commit is contained in:
parent
09d8c4b912
commit
1cad51b1af
|
@ -0,0 +1,23 @@
|
|||
from typing import Dict
|
||||
|
||||
from . import RLTestBase
|
||||
from worlds.rogue_legacy.Items import RLItemData, item_table
|
||||
from worlds.rogue_legacy.Locations import RLLocationData, location_table
|
||||
|
||||
|
||||
class UniqueTest(RLTestBase):
|
||||
@staticmethod
|
||||
def test_item_ids_are_all_unique():
|
||||
item_ids: Dict[int, str] = {}
|
||||
for name, data in item_table.items():
|
||||
assert data.code not in item_ids.keys(), f"'{name}': {data.code}, is not unique. " \
|
||||
f"'{item_ids[data.code]}' also has this identifier."
|
||||
item_ids[data.code] = name
|
||||
|
||||
@staticmethod
|
||||
def test_location_ids_are_all_unique():
|
||||
location_ids: Dict[int, str] = {}
|
||||
for name, data in location_table.items():
|
||||
assert data.code not in location_ids.keys(), f"'{name}': {data.code}, is not unique. " \
|
||||
f"'{location_ids[data.code]}' also has this identifier."
|
||||
location_ids[data.code] = name
|
|
@ -0,0 +1,5 @@
|
|||
from test.worlds.test_base import WorldTestBase
|
||||
|
||||
|
||||
class RLTestBase(WorldTestBase):
|
||||
game = "Rogue Legacy"
|
|
@ -1,136 +1,115 @@
|
|||
import typing
|
||||
from typing import Dict, NamedTuple, Optional
|
||||
|
||||
from BaseClasses import Item
|
||||
from .Names import ItemName
|
||||
from BaseClasses import Item, ItemClassification
|
||||
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
code: typing.Optional[int]
|
||||
progression: bool
|
||||
quantity: int = 1
|
||||
event: bool = False
|
||||
|
||||
|
||||
class LegacyItem(Item):
|
||||
class RLItem(Item):
|
||||
game: str = "Rogue Legacy"
|
||||
|
||||
|
||||
# Separate tables for each type of item.
|
||||
vendors_table = {
|
||||
ItemName.blacksmith: ItemData(90000, True),
|
||||
ItemName.enchantress: ItemData(90001, True),
|
||||
ItemName.architect: ItemData(90002, False),
|
||||
class RLItemData(NamedTuple):
|
||||
category: str
|
||||
code: Optional[int] = None
|
||||
classification: ItemClassification = ItemClassification.filler
|
||||
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] = {}
|
||||
for name, data in item_table.items():
|
||||
if data.category == category:
|
||||
item_dict.setdefault(name, data)
|
||||
|
||||
return item_dict
|
||||
|
||||
|
||||
item_table: Dict[str, RLItemData] = {
|
||||
# Vendors
|
||||
"Blacksmith": RLItemData("Vendors", 90_000, ItemClassification.useful),
|
||||
"Enchantress": RLItemData("Vendors", 90_001, ItemClassification.progression),
|
||||
"Architect": RLItemData("Vendors", 90_002, ItemClassification.useful),
|
||||
|
||||
# Classes
|
||||
"Progressive Knights": RLItemData("Classes", 90_003, ItemClassification.useful, 2),
|
||||
"Progressive Mages": RLItemData("Classes", 90_004, ItemClassification.useful, 2),
|
||||
"Progressive Barbarians": RLItemData("Classes", 90_005, ItemClassification.useful, 2),
|
||||
"Progressive Knaves": RLItemData("Classes", 90_006, ItemClassification.useful, 2),
|
||||
"Progressive Shinobis": RLItemData("Classes", 90_007, ItemClassification.useful, 2),
|
||||
"Progressive Miners": RLItemData("Classes", 90_008, ItemClassification.useful, 2),
|
||||
"Progressive Liches": RLItemData("Classes", 90_009, ItemClassification.useful, 2),
|
||||
"Progressive Spellthieves": RLItemData("Classes", 90_010, ItemClassification.useful, 2),
|
||||
"Dragons": RLItemData("Classes", 90_096, ItemClassification.progression),
|
||||
"Traitors": RLItemData("Classes", 90_097, ItemClassification.useful),
|
||||
|
||||
# Skills
|
||||
"Health Up": RLItemData("Skills", 90_013, ItemClassification.progression_skip_balancing, 15),
|
||||
"Mana Up": RLItemData("Skills", 90_014, ItemClassification.progression_skip_balancing, 15),
|
||||
"Attack Up": RLItemData("Skills", 90_015, ItemClassification.progression_skip_balancing, 15),
|
||||
"Magic Damage Up": RLItemData("Skills", 90_016, ItemClassification.progression_skip_balancing, 15),
|
||||
"Armor Up": RLItemData("Skills", 90_017, ItemClassification.useful, 15),
|
||||
"Equip Up": RLItemData("Skills", 90_018, ItemClassification.useful, 5),
|
||||
"Crit Chance Up": RLItemData("Skills", 90_019, ItemClassification.useful, 5),
|
||||
"Crit Damage Up": RLItemData("Skills", 90_020, ItemClassification.useful, 5),
|
||||
"Down Strike Up": RLItemData("Skills", 90_021),
|
||||
"Gold Gain Up": RLItemData("Skills", 90_022),
|
||||
"Potion Efficiency Up": RLItemData("Skills", 90_023),
|
||||
"Invulnerability Time Up": RLItemData("Skills", 90_024),
|
||||
"Mana Cost Down": RLItemData("Skills", 90_025),
|
||||
"Death Defiance": RLItemData("Skills", 90_026, ItemClassification.useful),
|
||||
"Haggling": RLItemData("Skills", 90_027, ItemClassification.useful),
|
||||
"Randomize Children": RLItemData("Skills", 90_028, ItemClassification.useful),
|
||||
|
||||
# Blueprints
|
||||
"Progressive Blueprints": RLItemData("Blueprints", 90_055, ItemClassification.useful, 15),
|
||||
"Squire Blueprints": RLItemData("Blueprints", 90_040, ItemClassification.useful),
|
||||
"Silver Blueprints": RLItemData("Blueprints", 90_041, ItemClassification.useful),
|
||||
"Guardian Blueprints": RLItemData("Blueprints", 90_042, ItemClassification.useful),
|
||||
"Imperial Blueprints": RLItemData("Blueprints", 90_043, ItemClassification.useful),
|
||||
"Royal Blueprints": RLItemData("Blueprints", 90_044, ItemClassification.useful),
|
||||
"Knight Blueprints": RLItemData("Blueprints", 90_045, ItemClassification.useful),
|
||||
"Ranger Blueprints": RLItemData("Blueprints", 90_046, ItemClassification.useful),
|
||||
"Sky Blueprints": RLItemData("Blueprints", 90_047, ItemClassification.useful),
|
||||
"Dragon Blueprints": RLItemData("Blueprints", 90_048, ItemClassification.useful),
|
||||
"Slayer Blueprints": RLItemData("Blueprints", 90_049, ItemClassification.useful),
|
||||
"Blood Blueprints": RLItemData("Blueprints", 90_050, ItemClassification.useful),
|
||||
"Sage Blueprints": RLItemData("Blueprints", 90_051, ItemClassification.useful),
|
||||
"Retribution Blueprints": RLItemData("Blueprints", 90_052, ItemClassification.useful),
|
||||
"Holy Blueprints": RLItemData("Blueprints", 90_053, ItemClassification.useful),
|
||||
"Dark Blueprints": RLItemData("Blueprints", 90_054, ItemClassification.useful),
|
||||
|
||||
# Runes
|
||||
"Vault Runes": RLItemData("Runes", 90_060, ItemClassification.progression),
|
||||
"Sprint Runes": RLItemData("Runes", 90_061, ItemClassification.progression),
|
||||
"Vampire Runes": RLItemData("Runes", 90_062, ItemClassification.useful),
|
||||
"Sky Runes": RLItemData("Runes", 90_063, ItemClassification.progression),
|
||||
"Siphon Runes": RLItemData("Runes", 90_064, ItemClassification.useful),
|
||||
"Retaliation Runes": RLItemData("Runes", 90_065),
|
||||
"Bounty Runes": RLItemData("Runes", 90_066),
|
||||
"Haste Runes": RLItemData("Runes", 90_067),
|
||||
"Curse Runes": RLItemData("Runes", 90_068),
|
||||
"Grace Runes": RLItemData("Runes", 90_069),
|
||||
"Balance Runes": RLItemData("Runes", 90_070, ItemClassification.useful),
|
||||
|
||||
# Junk
|
||||
"Triple Stat Increase": RLItemData("Filler", 90_030, weight=6),
|
||||
"1000 Gold": RLItemData("Filler", 90_031, weight=3),
|
||||
"3000 Gold": RLItemData("Filler", 90_032, weight=2),
|
||||
"5000 Gold": RLItemData("Filler", 90_033, weight=1),
|
||||
}
|
||||
|
||||
static_classes_table = {
|
||||
ItemName.knight: ItemData(90080, False),
|
||||
ItemName.paladin: ItemData(90081, False),
|
||||
ItemName.mage: ItemData(90082, False),
|
||||
ItemName.archmage: ItemData(90083, False),
|
||||
ItemName.barbarian: ItemData(90084, False),
|
||||
ItemName.barbarian_king: ItemData(90085, False),
|
||||
ItemName.knave: ItemData(90086, False),
|
||||
ItemName.assassin: ItemData(90087, False),
|
||||
ItemName.shinobi: ItemData(90088, False),
|
||||
ItemName.hokage: ItemData(90089, False),
|
||||
ItemName.miner: ItemData(90090, False),
|
||||
ItemName.spelunker: ItemData(90091, False),
|
||||
ItemName.lich: ItemData(90092, False),
|
||||
ItemName.lich_king: ItemData(90093, False),
|
||||
ItemName.spellthief: ItemData(90094, False),
|
||||
ItemName.spellsword: ItemData(90095, False),
|
||||
ItemName.dragon: ItemData(90096, False),
|
||||
ItemName.traitor: ItemData(90097, False),
|
||||
event_item_table: Dict[str, RLItemData] = {
|
||||
"Defeat Khidr": RLItemData("Event", classification=ItemClassification.progression),
|
||||
"Defeat Alexander": RLItemData("Event", classification=ItemClassification.progression),
|
||||
"Defeat Ponce de Leon": RLItemData("Event", classification=ItemClassification.progression),
|
||||
"Defeat Herodotus": RLItemData("Event", classification=ItemClassification.progression),
|
||||
"Defeat Neo Khidr": RLItemData("Event", classification=ItemClassification.progression),
|
||||
"Defeat Alexander IV": RLItemData("Event", classification=ItemClassification.progression),
|
||||
"Defeat Ponce de Freon": RLItemData("Event", classification=ItemClassification.progression),
|
||||
"Defeat Astrodotus": RLItemData("Event", classification=ItemClassification.progression),
|
||||
"Defeat The Fountain": RLItemData("Event", classification=ItemClassification.progression),
|
||||
}
|
||||
|
||||
progressive_classes_table = {
|
||||
ItemName.progressive_knight: ItemData(90003, False, 2),
|
||||
ItemName.progressive_mage: ItemData(90004, False, 2),
|
||||
ItemName.progressive_barbarian: ItemData(90005, False, 2),
|
||||
ItemName.progressive_knave: ItemData(90006, False, 2),
|
||||
ItemName.progressive_shinobi: ItemData(90007, False, 2),
|
||||
ItemName.progressive_miner: ItemData(90008, False, 2),
|
||||
ItemName.progressive_lich: ItemData(90009, False, 2),
|
||||
ItemName.progressive_spellthief: ItemData(90010, False, 2),
|
||||
}
|
||||
|
||||
configurable_skill_unlocks_table = {
|
||||
ItemName.health: ItemData(90013, True, 15),
|
||||
ItemName.mana: ItemData(90014, True, 15),
|
||||
ItemName.attack: ItemData(90015, True, 15),
|
||||
ItemName.magic_damage: ItemData(90016, True, 15),
|
||||
ItemName.armor: ItemData(90017, True, 10),
|
||||
ItemName.equip: ItemData(90018, True, 10),
|
||||
ItemName.crit_chance: ItemData(90019, False, 5),
|
||||
ItemName.crit_damage: ItemData(90020, False, 5),
|
||||
}
|
||||
|
||||
skill_unlocks_table = {
|
||||
ItemName.down_strike: ItemData(90021, False),
|
||||
ItemName.gold_gain: ItemData(90022, False),
|
||||
ItemName.potion_efficiency: ItemData(90023, False),
|
||||
ItemName.invulnerability_time: ItemData(90024, False),
|
||||
ItemName.mana_cost_down: ItemData(90025, False),
|
||||
ItemName.death_defiance: ItemData(90026, False),
|
||||
ItemName.haggling: ItemData(90027, False),
|
||||
ItemName.random_children: ItemData(90028, False),
|
||||
}
|
||||
|
||||
blueprints_table = {
|
||||
ItemName.squire_blueprints: ItemData(90040, False),
|
||||
ItemName.silver_blueprints: ItemData(90041, False),
|
||||
ItemName.guardian_blueprints: ItemData(90042, False),
|
||||
ItemName.imperial_blueprints: ItemData(90043, False),
|
||||
ItemName.royal_blueprints: ItemData(90044, False),
|
||||
ItemName.knight_blueprints: ItemData(90045, False),
|
||||
ItemName.ranger_blueprints: ItemData(90046, False),
|
||||
ItemName.sky_blueprints: ItemData(90047, False),
|
||||
ItemName.dragon_blueprints: ItemData(90048, False),
|
||||
ItemName.slayer_blueprints: ItemData(90049, False),
|
||||
ItemName.blood_blueprints: ItemData(90050, False),
|
||||
ItemName.sage_blueprints: ItemData(90051, False),
|
||||
ItemName.retribution_blueprints: ItemData(90052, False),
|
||||
ItemName.holy_blueprints: ItemData(90053, False),
|
||||
ItemName.dark_blueprints: ItemData(90054, False),
|
||||
}
|
||||
|
||||
progressive_blueprint_table = {
|
||||
ItemName.progressive_blueprints: ItemData(90055, False),
|
||||
}
|
||||
|
||||
runes_table = {
|
||||
ItemName.vault_runes: ItemData(90060, False),
|
||||
ItemName.sprint_runes: ItemData(90061, False),
|
||||
ItemName.vampire_runes: ItemData(90062, False),
|
||||
ItemName.sky_runes: ItemData(90063, False),
|
||||
ItemName.siphon_runes: ItemData(90064, False),
|
||||
ItemName.retaliation_runes: ItemData(90065, False),
|
||||
ItemName.bounty_runes: ItemData(90066, False),
|
||||
ItemName.haste_runes: ItemData(90067, False),
|
||||
ItemName.curse_runes: ItemData(90068, False),
|
||||
ItemName.grace_runes: ItemData(90069, False),
|
||||
ItemName.balance_runes: ItemData(90070, False),
|
||||
}
|
||||
|
||||
misc_items_table = {
|
||||
ItemName.trip_stat_increase: ItemData(90030, False),
|
||||
ItemName.gold_1000: ItemData(90031, False),
|
||||
ItemName.gold_3000: ItemData(90032, False),
|
||||
ItemName.gold_5000: ItemData(90033, False),
|
||||
# ItemName.rage_trap: ItemData(90034, False),
|
||||
}
|
||||
|
||||
# Complete item table.
|
||||
item_table = {
|
||||
**vendors_table,
|
||||
**static_classes_table,
|
||||
**progressive_classes_table,
|
||||
**configurable_skill_unlocks_table,
|
||||
**skill_unlocks_table,
|
||||
**blueprints_table,
|
||||
**progressive_blueprint_table,
|
||||
**runes_table,
|
||||
**misc_items_table,
|
||||
}
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code}
|
||||
|
|
|
@ -1,90 +1,98 @@
|
|||
import typing
|
||||
from typing import Dict, NamedTuple, Optional
|
||||
|
||||
from BaseClasses import Location
|
||||
from .Names import LocationName
|
||||
|
||||
|
||||
class LegacyLocation(Location):
|
||||
class RLLocation(Location):
|
||||
game: str = "Rogue Legacy"
|
||||
|
||||
|
||||
base_location_table = {
|
||||
# Manor Renovations
|
||||
LocationName.manor_ground_base: 91000,
|
||||
LocationName.manor_main_base: 91001,
|
||||
LocationName.manor_main_bottom_window: 91002,
|
||||
LocationName.manor_main_top_window: 91003,
|
||||
LocationName.manor_main_roof: 91004,
|
||||
LocationName.manor_left_wing_base: 91005,
|
||||
LocationName.manor_left_wing_window: 91006,
|
||||
LocationName.manor_left_wing_roof: 91007,
|
||||
LocationName.manor_left_big_base: 91008,
|
||||
LocationName.manor_left_big_upper1: 91009,
|
||||
LocationName.manor_left_big_upper2: 91010,
|
||||
LocationName.manor_left_big_windows: 91011,
|
||||
LocationName.manor_left_big_roof: 91012,
|
||||
LocationName.manor_left_far_base: 91013,
|
||||
LocationName.manor_left_far_roof: 91014,
|
||||
LocationName.manor_left_extension: 91015,
|
||||
LocationName.manor_left_tree1: 91016,
|
||||
LocationName.manor_left_tree2: 91017,
|
||||
LocationName.manor_right_wing_base: 91018,
|
||||
LocationName.manor_right_wing_window: 91019,
|
||||
LocationName.manor_right_wing_roof: 91020,
|
||||
LocationName.manor_right_big_base: 91021,
|
||||
LocationName.manor_right_big_upper: 91022,
|
||||
LocationName.manor_right_big_roof: 91023,
|
||||
LocationName.manor_right_high_base: 91024,
|
||||
LocationName.manor_right_high_upper: 91025,
|
||||
LocationName.manor_right_high_tower: 91026,
|
||||
LocationName.manor_right_extension: 91027,
|
||||
LocationName.manor_right_tree: 91028,
|
||||
LocationName.manor_observatory_base: 91029,
|
||||
LocationName.manor_observatory_scope: 91030,
|
||||
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] = {}
|
||||
for name, data in location_table.items():
|
||||
if data.category == category:
|
||||
location_dict.setdefault(name, data)
|
||||
|
||||
return location_dict
|
||||
|
||||
|
||||
location_table: Dict[str, RLLocationData] = {
|
||||
# Manor Renovation
|
||||
"Manor - Ground Road": RLLocationData("Manor", 91_000),
|
||||
"Manor - Main Base": RLLocationData("Manor", 91_001),
|
||||
"Manor - Main Bottom Window": RLLocationData("Manor", 91_002),
|
||||
"Manor - Main Top Window": RLLocationData("Manor", 91_003),
|
||||
"Manor - Main Rooftop": RLLocationData("Manor", 91_004),
|
||||
"Manor - Left Wing Base": RLLocationData("Manor", 91_005),
|
||||
"Manor - Left Wing Window": RLLocationData("Manor", 91_006),
|
||||
"Manor - Left Wing Rooftop": RLLocationData("Manor", 91_007),
|
||||
"Manor - Left Big Base": RLLocationData("Manor", 91_008),
|
||||
"Manor - Left Big Upper 1": RLLocationData("Manor", 91_009),
|
||||
"Manor - Left Big Upper 2": RLLocationData("Manor", 91_010),
|
||||
"Manor - Left Big Windows": RLLocationData("Manor", 91_011),
|
||||
"Manor - Left Big Rooftop": RLLocationData("Manor", 91_012),
|
||||
"Manor - Left Far Base": RLLocationData("Manor", 91_013),
|
||||
"Manor - Left Far Roof": RLLocationData("Manor", 91_014),
|
||||
"Manor - Left Extension": RLLocationData("Manor", 91_015),
|
||||
"Manor - Left Tree 1": RLLocationData("Manor", 91_016),
|
||||
"Manor - Left Tree 2": RLLocationData("Manor", 91_017),
|
||||
"Manor - Right Wing Base": RLLocationData("Manor", 91_018),
|
||||
"Manor - Right Wing Window": RLLocationData("Manor", 91_019),
|
||||
"Manor - Right Wing Rooftop": RLLocationData("Manor", 91_020),
|
||||
"Manor - Right Big Base": RLLocationData("Manor", 91_021),
|
||||
"Manor - Right Big Upper": RLLocationData("Manor", 91_022),
|
||||
"Manor - Right Big Rooftop": RLLocationData("Manor", 91_023),
|
||||
"Manor - Right High Base": RLLocationData("Manor", 91_024),
|
||||
"Manor - Right High Upper": RLLocationData("Manor", 91_025),
|
||||
"Manor - Right High Tower": RLLocationData("Manor", 91_026),
|
||||
"Manor - Right Extension": RLLocationData("Manor", 91_027),
|
||||
"Manor - Right Tree": RLLocationData("Manor", 91_028),
|
||||
"Manor - Observatory Base": RLLocationData("Manor", 91_029),
|
||||
"Manor - Observatory Telescope": RLLocationData("Manor", 91_030),
|
||||
|
||||
# Boss Rewards
|
||||
LocationName.boss_castle: 91100,
|
||||
LocationName.boss_forest: 91102,
|
||||
LocationName.boss_tower: 91104,
|
||||
LocationName.boss_dungeon: 91106,
|
||||
|
||||
# Special Rooms
|
||||
LocationName.special_jukebox: 91200,
|
||||
LocationName.special_painting: 91201,
|
||||
LocationName.special_cheapskate: 91202,
|
||||
LocationName.special_carnival: 91203,
|
||||
"Castle Hamson Boss Reward": RLLocationData("Boss", 91_100),
|
||||
"Forest Abkhazia Boss Reward": RLLocationData("Boss", 91_102),
|
||||
"The Maya Boss Reward": RLLocationData("Boss", 91_104),
|
||||
"Land of Darkness Boss Reward": RLLocationData("Boss", 91_106),
|
||||
|
||||
# Special Locations
|
||||
LocationName.castle: None,
|
||||
LocationName.garden: None,
|
||||
LocationName.tower: None,
|
||||
LocationName.dungeon: None,
|
||||
LocationName.fountain: None,
|
||||
"Jukebox": RLLocationData("Special", 91_200),
|
||||
"Painting": RLLocationData("Special", 91_201),
|
||||
"Cheapskate Elf's Game": RLLocationData("Special", 91_202),
|
||||
"Carnival": RLLocationData("Special", 91_203),
|
||||
|
||||
# Diaries
|
||||
**{f"Diary {i+1}": RLLocationData("Diary", 91_300 + i) for i in range(0, 25)},
|
||||
|
||||
# Chests
|
||||
**{f"Castle Hamson - Chest {i+1}": RLLocationData("Chests", 91_600 + i) for i in range(0, 50)},
|
||||
**{f"Forest Abkhazia - Chest {i+1}": RLLocationData("Chests", 91_700 + i) for i in range(0, 50)},
|
||||
**{f"The Maya - Chest {i+1}": RLLocationData("Chests", 91_800 + i) for i in range(0, 50)},
|
||||
**{f"Land of Darkness - Chest {i+1}": RLLocationData("Chests", 91_900 + i) for i in range(0, 50)},
|
||||
**{f"Chest {i+1}": RLLocationData("Chests", 92_000 + i) for i in range(0, 200)},
|
||||
|
||||
# Fairy Chests
|
||||
**{f"Castle Hamson - Fairy Chest {i+1}": RLLocationData("Fairies", 91_400 + i) for i in range(0, 15)},
|
||||
**{f"Forest Abkhazia - Fairy Chest {i+1}": RLLocationData("Fairies", 91_450 + i) for i in range(0, 15)},
|
||||
**{f"The Maya - Fairy Chest {i+1}": RLLocationData("Fairies", 91_500 + i) for i in range(0, 15)},
|
||||
**{f"Land of Darkness - Fairy Chest {i+1}": RLLocationData("Fairies", 91_550 + i) for i in range(0, 15)},
|
||||
**{f"Fairy Chest {i+1}": RLLocationData("Fairies", 92_200 + i) for i in range(0, 60)},
|
||||
}
|
||||
|
||||
diary_location_table = {f"{LocationName.diary} {i + 1}": i + 91300 for i in range(0, 25)}
|
||||
|
||||
fairy_chest_location_table = {
|
||||
**{f"{LocationName.castle} - Fairy Chest {i + 1}": i + 91400 for i in range(0, 50)},
|
||||
**{f"{LocationName.garden} - Fairy Chest {i + 1}": i + 91450 for i in range(0, 50)},
|
||||
**{f"{LocationName.tower} - Fairy Chest {i + 1}": i + 91500 for i in range(0, 50)},
|
||||
**{f"{LocationName.dungeon} - Fairy Chest {i + 1}": i + 91550 for i in range(0, 50)},
|
||||
**{f"Fairy Chest {i + 1}": i + 92200 for i in range(0, 60)},
|
||||
event_location_table: Dict[str, RLLocationData] = {
|
||||
"Castle Hamson Boss Room": RLLocationData("Event"),
|
||||
"Forest Abkhazia Boss Room": RLLocationData("Event"),
|
||||
"The Maya Boss Room": RLLocationData("Event"),
|
||||
"Land of Darkness Boss Room": RLLocationData("Event"),
|
||||
"Fountain Room": RLLocationData("Event"),
|
||||
}
|
||||
|
||||
chest_location_table = {
|
||||
**{f"{LocationName.castle} - Chest {i + 1}": i + 91600 for i in range(0, 100)},
|
||||
**{f"{LocationName.garden} - Chest {i + 1}": i + 91700 for i in range(0, 100)},
|
||||
**{f"{LocationName.tower} - Chest {i + 1}": i + 91800 for i in range(0, 100)},
|
||||
**{f"{LocationName.dungeon} - Chest {i + 1}": i + 91900 for i in range(0, 100)},
|
||||
**{f"Chest {i + 1}": i + 92000 for i in range(0, 120)},
|
||||
}
|
||||
|
||||
location_table = {
|
||||
**base_location_table,
|
||||
**diary_location_table,
|
||||
**fairy_chest_location_table,
|
||||
**chest_location_table,
|
||||
}
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {id: name for name, _ in location_table.items()}
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
# Vendor Definitions
|
||||
blacksmith = "Blacksmith"
|
||||
enchantress = "Enchantress"
|
||||
architect = "Architect"
|
||||
|
||||
# Progressive Class Definitions
|
||||
progressive_knight = "Progressive Knights"
|
||||
progressive_mage = "Progressive Mages"
|
||||
progressive_barbarian = "Progressive Barbarians"
|
||||
progressive_knave = "Progressive Knaves"
|
||||
progressive_shinobi = "Progressive Shinobis"
|
||||
progressive_miner = "Progressive Miners"
|
||||
progressive_lich = "Progressive Liches"
|
||||
progressive_spellthief = "Progressive Spellthieves"
|
||||
|
||||
# Static Class Definitions
|
||||
knight = "Knights"
|
||||
paladin = "Paladins"
|
||||
mage = "Mages"
|
||||
archmage = "Archmages"
|
||||
barbarian = "Barbarians"
|
||||
barbarian_king = "Barbarian Kings"
|
||||
knave = "Knaves"
|
||||
assassin = "Assassins"
|
||||
shinobi = "Shinobis"
|
||||
hokage = "Hokages"
|
||||
miner = "Miners"
|
||||
spelunker = "Spelunkers"
|
||||
lich = "Lichs"
|
||||
lich_king = "Lich Kings"
|
||||
spellthief = "Spellthieves"
|
||||
spellsword = "Spellswords"
|
||||
dragon = "Dragons"
|
||||
traitor = "Traitors"
|
||||
|
||||
# Skill Unlock Definitions
|
||||
health = "Health Up"
|
||||
mana = "Mana Up"
|
||||
attack = "Attack Up"
|
||||
magic_damage = "Magic Damage Up"
|
||||
armor = "Armor Up"
|
||||
equip = "Equip Up"
|
||||
crit_chance = "Crit Chance Up"
|
||||
crit_damage = "Crit Damage Up"
|
||||
down_strike = "Down Strike Up"
|
||||
gold_gain = "Gold Gain Up"
|
||||
potion_efficiency = "Potion Efficiency Up"
|
||||
invulnerability_time = "Invulnerability Time Up"
|
||||
mana_cost_down = "Mana Cost Down"
|
||||
death_defiance = "Death Defiance"
|
||||
haggling = "Haggling"
|
||||
random_children = "Randomize Children"
|
||||
|
||||
# Misc. Definitions
|
||||
trip_stat_increase = "Triple Stat Increase"
|
||||
gold_1000 = "1000 Gold"
|
||||
gold_3000 = "3000 Gold"
|
||||
gold_5000 = "5000 Gold"
|
||||
rage_trap = "Rage Trap"
|
||||
|
||||
# Blueprint Definitions
|
||||
progressive_blueprints = "Progressive Blueprints"
|
||||
squire_blueprints = "Squire Blueprints"
|
||||
silver_blueprints = "Silver Blueprints"
|
||||
guardian_blueprints = "Guardian Blueprints"
|
||||
imperial_blueprints = "Imperial Blueprints"
|
||||
royal_blueprints = "Royal Blueprints"
|
||||
knight_blueprints = "Knight Blueprints"
|
||||
ranger_blueprints = "Ranger Blueprints"
|
||||
sky_blueprints = "Sky Blueprints"
|
||||
dragon_blueprints = "Dragon Blueprints"
|
||||
slayer_blueprints = "Slayer Blueprints"
|
||||
blood_blueprints = "Blood Blueprints"
|
||||
sage_blueprints = "Sage Blueprints"
|
||||
retribution_blueprints = "Retribution Blueprints"
|
||||
holy_blueprints = "Holy Blueprints"
|
||||
dark_blueprints = "Dark Blueprints"
|
||||
|
||||
# Rune Definitions
|
||||
vault_runes = "Vault Runes"
|
||||
sprint_runes = "Sprint Runes"
|
||||
vampire_runes = "Vampire Runes"
|
||||
sky_runes = "Sky Runes"
|
||||
siphon_runes = "Siphon Runes"
|
||||
retaliation_runes = "Retaliation Runes"
|
||||
bounty_runes = "Bounty Runes"
|
||||
haste_runes = "Haste Runes"
|
||||
curse_runes = "Curse Runes"
|
||||
grace_runes = "Grace Runes"
|
||||
balance_runes = "Balance Runes"
|
||||
|
||||
# Event Definitions
|
||||
boss_castle = "Defeat Castle Hamson Boss"
|
||||
boss_forest = "Defeat Forest Abkhazia Boss"
|
||||
boss_tower = "Defeat The Maya Boss"
|
||||
boss_dungeon = "Defeat The Land of Darkness Boss"
|
||||
boss_fountain = "Defeat The Fountain"
|
|
@ -1,55 +0,0 @@
|
|||
# Manor Piece Definitions
|
||||
manor_ground_base = "Manor Renovation - Ground Road"
|
||||
manor_main_base = "Manor Renovation - Main Base"
|
||||
manor_main_bottom_window = "Manor Renovation - Main Bottom Window"
|
||||
manor_main_top_window = "Manor Renovation - Main Top Window"
|
||||
manor_main_roof = "Manor Renovation - Main Rooftop"
|
||||
manor_left_wing_base = "Manor Renovation - Left Wing Base"
|
||||
manor_left_wing_window = "Manor Renovation - Left Wing Window"
|
||||
manor_left_wing_roof = "Manor Renovation - Left Wing Rooftop"
|
||||
manor_left_big_base = "Manor Renovation - Left Big Base"
|
||||
manor_left_big_upper1 = "Manor Renovation - Left Big Upper 1"
|
||||
manor_left_big_upper2 = "Manor Renovation - Left Big Upper 2"
|
||||
manor_left_big_windows = "Manor Renovation - Left Big Windows"
|
||||
manor_left_big_roof = "Manor Renovation - Left Big Rooftop"
|
||||
manor_left_far_base = "Manor Renovation - Left Far Base"
|
||||
manor_left_far_roof = "Manor Renovation - Left Far Roof"
|
||||
manor_left_extension = "Manor Renovation - Left Extension"
|
||||
manor_left_tree1 = "Manor Renovation - Left Tree 1"
|
||||
manor_left_tree2 = "Manor Renovation - Left Tree 2"
|
||||
manor_right_wing_base = "Manor Renovation - Right Wing Base"
|
||||
manor_right_wing_window = "Manor Renovation - Right Wing Window"
|
||||
manor_right_wing_roof = "Manor Renovation - Right Wing Rooftop"
|
||||
manor_right_big_base = "Manor Renovation - Right Big Base"
|
||||
manor_right_big_upper = "Manor Renovation - Right Big Upper"
|
||||
manor_right_big_roof = "Manor Renovation - Right Big Rooftop"
|
||||
manor_right_high_base = "Manor Renovation - Right High Base"
|
||||
manor_right_high_upper = "Manor Renovation - Right High Upper"
|
||||
manor_right_high_tower = "Manor Renovation - Right High Tower"
|
||||
manor_right_extension = "Manor Renovation - Right Extension"
|
||||
manor_right_tree = "Manor Renovation - Right Tree"
|
||||
manor_observatory_base = "Manor Renovation - Observatory Base"
|
||||
manor_observatory_scope = "Manor Renovation - Observatory Telescope"
|
||||
|
||||
# Boss Chest Definitions
|
||||
boss_castle = "Castle Hamson Boss"
|
||||
boss_forest = "Forest Abkhazia Boss"
|
||||
boss_tower = "The Maya Boss"
|
||||
boss_dungeon = "The Land of Darkness Boss"
|
||||
|
||||
# Special Room Definitions
|
||||
special_jukebox = "Jukebox"
|
||||
special_painting = "Painting"
|
||||
special_cheapskate = "Cheapskate Elf's Game"
|
||||
special_carnival = "Carnival"
|
||||
|
||||
# Shorthand Definitions
|
||||
diary = "Diary"
|
||||
|
||||
# Region Definitions
|
||||
outside = "Outside Castle Hamson"
|
||||
castle = "Castle Hamson"
|
||||
garden = "Forest Abkhazia"
|
||||
tower = "The Maya"
|
||||
dungeon = "The Land of Darkness"
|
||||
fountain = "Fountain Room"
|
|
@ -1,6 +1,6 @@
|
|||
import typing
|
||||
from typing import Dict
|
||||
|
||||
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList, OptionSet
|
||||
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionSet
|
||||
|
||||
|
||||
class StartingGender(Choice):
|
||||
|
@ -63,9 +63,9 @@ class FairyChestsPerZone(Range):
|
|||
bonuses can be found in Fairy Chests.
|
||||
"""
|
||||
display_name = "Fairy Chests Per Zone"
|
||||
range_start = 5
|
||||
range_start = 0
|
||||
range_end = 15
|
||||
default = 5
|
||||
default = 1
|
||||
|
||||
|
||||
class ChestsPerZone(Range):
|
||||
|
@ -74,9 +74,9 @@ class ChestsPerZone(Range):
|
|||
gold or stat bonuses can be found in Chests.
|
||||
"""
|
||||
display_name = "Chests Per Zone"
|
||||
range_start = 15
|
||||
range_end = 30
|
||||
default = 15
|
||||
range_start = 20
|
||||
range_end = 50
|
||||
default = 20
|
||||
|
||||
|
||||
class UniversalFairyChests(Toggle):
|
||||
|
@ -111,8 +111,10 @@ class Architect(Choice):
|
|||
"""
|
||||
display_name = "Architect"
|
||||
option_start_unlocked = 0
|
||||
option_normal = 2
|
||||
option_early = 1
|
||||
option_anywhere = 2
|
||||
option_disabled = 3
|
||||
alias_normal = 2
|
||||
default = 2
|
||||
|
||||
|
||||
|
@ -173,7 +175,7 @@ class NumberOfChildren(Range):
|
|||
default = 3
|
||||
|
||||
|
||||
class AdditionalNames(OptionList):
|
||||
class AdditionalNames(OptionSet):
|
||||
"""
|
||||
Set of additional names your potential offspring can have. If Allow Default Names is disabled, this is the only list
|
||||
of names your children can have. The first value will also be your initial character's name depending on Starting
|
||||
|
@ -337,7 +339,8 @@ class AvailableClasses(OptionSet):
|
|||
default = {"Knight", "Mage", "Barbarian", "Knave", "Shinobi", "Miner", "Spellthief", "Lich", "Dragon", "Traitor"}
|
||||
valid_keys = {"Knight", "Mage", "Barbarian", "Knave", "Shinobi", "Miner", "Spellthief", "Lich", "Dragon", "Traitor"}
|
||||
|
||||
legacy_options: typing.Dict[str, type(Option)] = {
|
||||
|
||||
rl_options: Dict[str, type(Option)] = {
|
||||
"starting_gender": StartingGender,
|
||||
"starting_class": StartingClass,
|
||||
"available_classes": AvailableClasses,
|
||||
|
|
|
@ -1,72 +1,125 @@
|
|||
import typing
|
||||
from typing import Dict, List, NamedTuple, Optional
|
||||
|
||||
from BaseClasses import MultiWorld, Region, RegionType, Entrance, ItemClassification
|
||||
from .Items import LegacyItem
|
||||
from .Locations import LegacyLocation, diary_location_table, location_table, base_location_table
|
||||
from .Names import LocationName, ItemName
|
||||
|
||||
prog = ItemClassification.progression
|
||||
from BaseClasses import MultiWorld, Region, RegionType, Entrance
|
||||
from .Items import RLItem
|
||||
from .Locations import RLLocation, location_table, get_locations_by_category
|
||||
|
||||
|
||||
def create_regions(world, player: int):
|
||||
class RLRegionData(NamedTuple):
|
||||
locations: Optional[List[str]]
|
||||
exits: Optional[List[str]]
|
||||
|
||||
locations: typing.List[str] = []
|
||||
|
||||
# Add required locations.
|
||||
locations += [location for location in base_location_table]
|
||||
locations += [location for location in diary_location_table]
|
||||
def create_regions(world: 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),
|
||||
}
|
||||
|
||||
# Add chests per settings.
|
||||
if world.universal_fairy_chests[player]:
|
||||
fairies = int(world.fairy_chests_per_zone[player]) * 4
|
||||
for i in range(0, fairies):
|
||||
locations += [f"Fairy Chest {i + 1}"]
|
||||
else:
|
||||
fairies = int(world.fairy_chests_per_zone[player])
|
||||
for i in range(0, fairies):
|
||||
locations += [f"{LocationName.castle} - Fairy Chest {i + 1}"]
|
||||
locations += [f"{LocationName.garden} - Fairy Chest {i + 1}"]
|
||||
locations += [f"{LocationName.tower} - Fairy Chest {i + 1}"]
|
||||
locations += [f"{LocationName.dungeon} - Fairy Chest {i + 1}"]
|
||||
# Diaries
|
||||
for diary in range(0, 25):
|
||||
region: str
|
||||
if 0 <= diary < 6:
|
||||
region = "Castle Hamson"
|
||||
elif 6 <= diary < 12:
|
||||
region = "Forest Abkhazia"
|
||||
elif 12 <= diary < 18:
|
||||
region = "The Maya"
|
||||
elif 18 <= diary < 24:
|
||||
region = "Land of Darkness"
|
||||
else:
|
||||
region = "The Fountain Room"
|
||||
|
||||
if world.universal_chests[player]:
|
||||
chests = int(world.chests_per_zone[player]) * 4
|
||||
for i in range(0, chests):
|
||||
locations += [f"Chest {i + 1}"]
|
||||
else:
|
||||
chests = int(world.chests_per_zone[player])
|
||||
for i in range(0, chests):
|
||||
locations += [f"{LocationName.castle} - Chest {i + 1}"]
|
||||
locations += [f"{LocationName.garden} - Chest {i + 1}"]
|
||||
locations += [f"{LocationName.tower} - Chest {i + 1}"]
|
||||
locations += [f"{LocationName.dungeon} - Chest {i + 1}"]
|
||||
regions[region].locations.append(f"Diary {diary + 1}")
|
||||
|
||||
# Manor & Special
|
||||
for manor in get_locations_by_category("Manor").keys():
|
||||
regions["The Manor"].locations.append(manor)
|
||||
for special in get_locations_by_category("Special").keys():
|
||||
regions["Castle Hamson"].locations.append(special)
|
||||
|
||||
# Boss Rewards
|
||||
regions["Castle Hamson"].locations.append("Castle Hamson Boss Reward")
|
||||
regions["Forest Abkhazia"].locations.append("Forest Abkhazia Boss Reward")
|
||||
regions["The Maya"].locations.append("The Maya Boss Reward")
|
||||
regions["Land of Darkness"].locations.append("Land of Darkness Boss Reward")
|
||||
|
||||
# Events
|
||||
regions["Castle Hamson"].locations.append("Castle Hamson Boss Room")
|
||||
regions["Forest Abkhazia"].locations.append("Forest Abkhazia Boss Room")
|
||||
regions["The Maya"].locations.append("The Maya Boss Room")
|
||||
regions["Land of Darkness"].locations.append("Land of Darkness Boss Room")
|
||||
regions["The Fountain Room"].locations.append("Fountain Room")
|
||||
|
||||
# Chests
|
||||
chests = int(world.chests_per_zone[player])
|
||||
for i in range(0, chests):
|
||||
if world.universal_chests[player]:
|
||||
regions["Castle Hamson"].locations.append(f"Chest {i + 1}")
|
||||
regions["Forest Abkhazia"].locations.append(f"Chest {i + 1 + chests}")
|
||||
regions["The Maya"].locations.append(f"Chest {i + 1 + (chests * 2)}")
|
||||
regions["Land of Darkness"].locations.append(f"Chest {i + 1 + (chests * 3)}")
|
||||
else:
|
||||
regions["Castle Hamson"].locations.append(f"Castle Hamson - Chest {i + 1}")
|
||||
regions["Forest Abkhazia"].locations.append(f"Forest Abkhazia - Chest {i + 1}")
|
||||
regions["The Maya"].locations.append(f"The Maya - Chest {i + 1}")
|
||||
regions["Land of Darkness"].locations.append(f"Land of Darkness - Chest {i + 1}")
|
||||
|
||||
# Fairy Chests
|
||||
chests = int(world.fairy_chests_per_zone[player])
|
||||
for i in range(0, chests):
|
||||
if world.universal_fairy_chests[player]:
|
||||
regions["Castle Hamson"].locations.append(f"Fairy Chest {i + 1}")
|
||||
regions["Forest Abkhazia"].locations.append(f"Fairy Chest {i + 1 + chests}")
|
||||
regions["The Maya"].locations.append(f"Fairy Chest {i + 1 + (chests * 2)}")
|
||||
regions["Land of Darkness"].locations.append(f"Fairy Chest {i + 1 + (chests * 3)}")
|
||||
else:
|
||||
regions["Castle Hamson"].locations.append(f"Castle Hamson - Fairy Chest {i + 1}")
|
||||
regions["Forest Abkhazia"].locations.append(f"Forest Abkhazia - Fairy Chest {i + 1}")
|
||||
regions["The Maya"].locations.append(f"The Maya - Fairy Chest {i + 1}")
|
||||
regions["Land of Darkness"].locations.append(f"Land of Darkness - Fairy Chest {i + 1}")
|
||||
|
||||
# Set up the regions correctly.
|
||||
world.regions += [
|
||||
create_region(world, player, "Menu", None, [LocationName.outside]),
|
||||
create_region(world, player, LocationName.castle, locations),
|
||||
]
|
||||
for name, data in regions.items():
|
||||
world.regions.append(create_region(world, player, name, data.locations, data.exits))
|
||||
|
||||
# Connect entrances and set up events.
|
||||
world.get_entrance(LocationName.outside, player).connect(world.get_region(LocationName.castle, player))
|
||||
world.get_location(LocationName.castle, player).place_locked_item(LegacyItem(ItemName.boss_castle, prog, None, player))
|
||||
world.get_location(LocationName.garden, player).place_locked_item(LegacyItem(ItemName.boss_forest, prog, None, player))
|
||||
world.get_location(LocationName.tower, player).place_locked_item(LegacyItem(ItemName.boss_tower, prog, None, player))
|
||||
world.get_location(LocationName.dungeon, player).place_locked_item(LegacyItem(ItemName.boss_dungeon, prog, None, player))
|
||||
world.get_location(LocationName.fountain, player).place_locked_item(LegacyItem(ItemName.boss_fountain, prog, None, player))
|
||||
world.get_entrance("Castle Hamson", player).connect(world.get_region("Castle Hamson", player))
|
||||
world.get_entrance("The Manor", player).connect(world.get_region("The Manor", player))
|
||||
world.get_entrance("Forest Abkhazia", player).connect(world.get_region("Forest Abkhazia", player))
|
||||
world.get_entrance("The Maya", player).connect(world.get_region("The Maya", player))
|
||||
world.get_entrance("Land of Darkness", player).connect(world.get_region("Land of Darkness", player))
|
||||
world.get_entrance("The Fountain Room", player).connect(world.get_region("The Fountain Room", player))
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
# Shamelessly stolen from the ROR2 definition, lol
|
||||
ret = Region(name, RegionType.Generic, name, player)
|
||||
ret.world = world
|
||||
if locations:
|
||||
for location in locations:
|
||||
loc_id = location_table.get(location, 0)
|
||||
location = LegacyLocation(player, location, loc_id, ret)
|
||||
for loc_name in locations:
|
||||
loc_data = location_table.get(loc_name)
|
||||
location = RLLocation(player, loc_name, loc_data.code if loc_data else None, ret)
|
||||
|
||||
# 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:
|
||||
ret.exits.append(Entrance(player, exit, ret))
|
||||
|
||||
entrance = Entrance(player, exit, ret)
|
||||
ret.exits.append(entrance)
|
||||
return ret
|
||||
|
||||
|
||||
|
|
|
@ -1,177 +1,86 @@
|
|||
from BaseClasses import MultiWorld
|
||||
from .Names import LocationName, ItemName
|
||||
from BaseClasses import MultiWorld, CollectionState
|
||||
|
||||
from ..AutoWorld import LogicMixin
|
||||
from ..generic.Rules import set_rule
|
||||
|
||||
|
||||
class LegacyLogic(LogicMixin):
|
||||
def _legacy_has_any_vendors(self, player: int) -> bool:
|
||||
return self.has_any({ItemName.blacksmith, ItemName.enchantress}, player)
|
||||
def has_any_vendors(self: CollectionState, player: int) -> bool:
|
||||
return self.has_any({"Blacksmith", "Enchantress"}, player)
|
||||
|
||||
def _legacy_has_all_vendors(self, player: int) -> bool:
|
||||
return self.has_all({ItemName.blacksmith, ItemName.enchantress}, player)
|
||||
def has_all_vendors(self: CollectionState, player: int) -> bool:
|
||||
return self.has_all({"Blacksmith", "Enchantress"}, player)
|
||||
|
||||
def _legacy_has_stat_upgrades(self, player: int, amount: int) -> bool:
|
||||
return self._legacy_stat_upgrade_count(player) >= amount
|
||||
def has_stat_upgrades(self, player: int, amount: int) -> bool:
|
||||
return self.stat_upgrade_count(player) >= amount
|
||||
|
||||
def _legacy_total_stat_upgrades_count(self, player: int) -> int:
|
||||
def total_stat_upgrades_count(self, player: int) -> int:
|
||||
return int(self.world.health_pool[player]) + \
|
||||
int(self.world.mana_pool[player]) + \
|
||||
int(self.world.attack_pool[player]) + \
|
||||
int(self.world.magic_damage_pool[player]) + \
|
||||
int(self.world.armor_pool[player]) + \
|
||||
int(self.world.equip_pool[player])
|
||||
int(self.world.magic_damage_pool[player])
|
||||
|
||||
def _legacy_stat_upgrade_count(self, player: int) -> int:
|
||||
return self.item_count(ItemName.health, player) + self.item_count(ItemName.mana, player) + \
|
||||
self.item_count(ItemName.attack, player) + self.item_count(ItemName.magic_damage, player) + \
|
||||
self.item_count(ItemName.armor, player) + self.item_count(ItemName.equip, 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 set_rules(world: MultiWorld, player: int):
|
||||
# Check for duplicate names.
|
||||
if len(set(world.additional_lady_names[player].value)) != len(world.additional_lady_names[player].value):
|
||||
raise Exception(f"Duplicate values are not allowed in additional_lady_names.")
|
||||
if len(set(world.additional_sir_names[player].value)) != len(world.additional_sir_names[player].value):
|
||||
raise Exception(f"Duplicate values are not allowed in additional_sir_names.")
|
||||
|
||||
if not world.allow_default_names[player]:
|
||||
# Check for quantity.
|
||||
name_count = len(world.additional_lady_names[player].value)
|
||||
if name_count < int(world.number_of_children[player]):
|
||||
raise Exception(f"allow_default_names is off, but not enough names are defined in additional_lady_names. Expected {int(world.number_of_children[player])}, Got {name_count}")
|
||||
|
||||
name_count = len(world.additional_sir_names[player].value)
|
||||
if name_count < int(world.number_of_children[player]):
|
||||
raise Exception(f"allow_default_names is off, but not enough names are defined in additional_sir_names. Expected {int(world.number_of_children[player])}, Got {name_count}")
|
||||
|
||||
# Chests
|
||||
if world.universal_chests[player]:
|
||||
for i in range(0, world.chests_per_zone[player]):
|
||||
set_rule(world.get_location(f"Chest {i + 1 + (world.chests_per_zone[player] * 1)}", player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(f"Chest {i + 1 + (world.chests_per_zone[player] * 2)}", player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(f"Chest {i + 1 + (world.chests_per_zone[player] * 3)}", player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
else:
|
||||
for i in range(0, world.chests_per_zone[player]):
|
||||
set_rule(world.get_location(f"{LocationName.garden} - Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(f"{LocationName.tower} - Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(f"{LocationName.dungeon} - Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
|
||||
# Fairy Chests
|
||||
if world.universal_fairy_chests[player]:
|
||||
for i in range(0, world.fairy_chests_per_zone[player]):
|
||||
set_rule(world.get_location(f"Fairy Chest {i + 1 + (world.fairy_chests_per_zone[player] * 1)}", player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(f"Fairy Chest {i + 1 + (world.fairy_chests_per_zone[player] * 2)}", player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(f"Fairy Chest {i + 1 + (world.fairy_chests_per_zone[player] * 3)}", player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
else:
|
||||
for i in range(0, world.fairy_chests_per_zone[player]):
|
||||
set_rule(world.get_location(f"{LocationName.garden} - Fairy Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(f"{LocationName.tower} - Fairy Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(f"{LocationName.dungeon} - Fairy Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
|
||||
# Vendors
|
||||
if world.vendors[player] == "early":
|
||||
set_rule(world.get_location(LocationName.boss_castle, player),
|
||||
lambda state: state._legacy_has_all_vendors(player))
|
||||
elif world.vendors[player] == "normal":
|
||||
set_rule(world.get_location(LocationName.garden, player),
|
||||
lambda state: state._legacy_has_any_vendors(player))
|
||||
|
||||
# Diaries
|
||||
for i in range(0, 5):
|
||||
set_rule(world.get_location(f"Diary {i + 6}", player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(f"Diary {i + 11}", player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(f"Diary {i + 16}", player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
set_rule(world.get_location(f"Diary {i + 21}", player),
|
||||
lambda state: state.has(ItemName.boss_dungeon, player))
|
||||
if world.vendors[player] == "normal":
|
||||
set_rule(world.get_location("Forest Abkhazia Boss Reward", player),
|
||||
lambda state: state.has_all_vendors(player))
|
||||
|
||||
# Scale each manor location.
|
||||
set_rule(world.get_location(LocationName.manor_left_wing_window, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_wing_roof, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_wing_window, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_wing_roof, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_base, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_big_base, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_tree1, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_tree2, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_tree, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_upper1, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_upper2, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_windows, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_roof, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_far_base, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_far_roof, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_extension, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_big_upper, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_big_roof, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_extension, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_high_base, player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_high_upper, player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_high_tower, player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
set_rule(world.get_location(LocationName.manor_observatory_base, player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
set_rule(world.get_location(LocationName.manor_observatory_scope, player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
manor_rules = {
|
||||
"Defeat Khidr" if world.khidr[player] == "vanilla" else "Defeat Neo Khidr": [
|
||||
"Manor - Left Wing Window",
|
||||
"Manor - Left Wing Rooftop",
|
||||
"Manor - Right Wing Window",
|
||||
"Manor - Right Wing Rooftop",
|
||||
"Manor - Left Big Base",
|
||||
"Manor - Right Big Base",
|
||||
"Manor - Left Tree 1",
|
||||
"Manor - Left Tree 2",
|
||||
"Manor - Right Tree",
|
||||
],
|
||||
"Defeat Alexander" if world.alexander[player] == "vanilla" else "Defeat Alexander IV": [
|
||||
"Manor - Left Big Upper 1",
|
||||
"Manor - Left Big Upper 2",
|
||||
"Manor - Left Big Windows",
|
||||
"Manor - Left Big Rooftop",
|
||||
"Manor - Left Far Base",
|
||||
"Manor - Left Far Roof",
|
||||
"Manor - Left Extension",
|
||||
"Manor - Right Big Upper",
|
||||
"Manor - Right Big Rooftop",
|
||||
"Manor - Right Extension",
|
||||
],
|
||||
"Defeat Ponce de Leon" if world.leon[player] == "vanilla" else "Defeat Ponce de Freon": [
|
||||
"Manor - Right High Base",
|
||||
"Manor - Right High Upper",
|
||||
"Manor - Right High Tower",
|
||||
"Manor - Observatory Base",
|
||||
"Manor - Observatory Telescope",
|
||||
]
|
||||
}
|
||||
|
||||
for event, locations in manor_rules.items():
|
||||
for location in locations:
|
||||
set_rule(world.get_location(location, player), lambda state: state.has(event, player))
|
||||
|
||||
# Standard Zone Progression
|
||||
set_rule(world.get_location(LocationName.garden, player),
|
||||
lambda state: state._legacy_has_stat_upgrades(player, 0.125 * state._legacy_total_stat_upgrades_count(player)) and state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.tower, player),
|
||||
lambda state: state._legacy_has_stat_upgrades(player, 0.3125 * state._legacy_total_stat_upgrades_count(player)) and state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.dungeon, player),
|
||||
lambda state: state._legacy_has_stat_upgrades(player, 0.5 * state._legacy_total_stat_upgrades_count(player)) and state.has(ItemName.boss_tower, player))
|
||||
world.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)))
|
||||
world.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)))
|
||||
world.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)))
|
||||
world.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)))
|
||||
|
||||
# Bosses
|
||||
set_rule(world.get_location(LocationName.boss_castle, player),
|
||||
lambda state: state.has(ItemName.boss_castle, player))
|
||||
set_rule(world.get_location(LocationName.boss_forest, player),
|
||||
lambda state: state.has(ItemName.boss_forest, player))
|
||||
set_rule(world.get_location(LocationName.boss_tower, player),
|
||||
lambda state: state.has(ItemName.boss_tower, player))
|
||||
set_rule(world.get_location(LocationName.boss_dungeon, player),
|
||||
lambda state: state.has(ItemName.boss_dungeon, player))
|
||||
set_rule(world.get_location(LocationName.fountain, player),
|
||||
lambda state: state._legacy_has_stat_upgrades(player, 0.625 * state._legacy_total_stat_upgrades_count(player))
|
||||
and state.has(ItemName.boss_castle, player)
|
||||
and state.has(ItemName.boss_forest, player)
|
||||
and state.has(ItemName.boss_tower, player)
|
||||
and state.has(ItemName.boss_dungeon, player))
|
||||
|
||||
world.completion_condition[player] = lambda state: state.has(ItemName.boss_fountain, player)
|
||||
world.completion_condition[player] = lambda state: state.has("Defeat The Fountain", player)
|
||||
|
|
|
@ -36,4 +36,3 @@ traits = [
|
|||
"Glaucoma",
|
||||
"Adopted",
|
||||
]
|
||||
|
||||
|
|
|
@ -1,176 +1,265 @@
|
|||
import typing
|
||||
from typing import List
|
||||
|
||||
from BaseClasses import Item, ItemClassification, Tutorial
|
||||
from .Items import LegacyItem, ItemData, item_table, vendors_table, static_classes_table, progressive_classes_table, \
|
||||
skill_unlocks_table, blueprints_table, runes_table, misc_items_table
|
||||
from .Locations import LegacyLocation, location_table, base_location_table
|
||||
from .Options import legacy_options
|
||||
from BaseClasses import Tutorial
|
||||
from .Items import RLItem, RLItemData, event_item_table, item_table, get_items_by_category
|
||||
from .Locations import RLLocation, location_table
|
||||
from .Options import rl_options
|
||||
from .Regions import create_regions
|
||||
from .Rules import set_rules
|
||||
from .Names import ItemName
|
||||
from ..AutoWorld import World, WebWorld
|
||||
|
||||
|
||||
class LegacyWeb(WebWorld):
|
||||
class RLWeb(WebWorld):
|
||||
theme = "stone"
|
||||
tutorials = [Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
"A guide to setting up the Rogue Legacy Randomizer software on your computer. This guide covers single-player, multiworld, and related software.",
|
||||
"A guide to setting up the Rogue Legacy Randomizer software on your computer. This guide covers single-player, "
|
||||
"multiworld, and related software.",
|
||||
"English",
|
||||
"rogue-legacy_en.md",
|
||||
"rogue-legacy/en",
|
||||
["Phar"]
|
||||
)]
|
||||
bug_report_page = "https://github.com/ThePhar/RogueLegacyRandomizer/issues/new?assignees=&labels=bug&template=" \
|
||||
"report-an-issue---.md&title=%5BIssue%5D"
|
||||
|
||||
|
||||
class LegacyWorld(World):
|
||||
class RLWorld(World):
|
||||
"""
|
||||
Rogue Legacy is a genealogical rogue-"LITE" where anyone can be a hero. Each time you die, your child will succeed
|
||||
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"
|
||||
option_definitions = legacy_options
|
||||
topology_present = False
|
||||
data_version = 3
|
||||
required_client_version = (0, 2, 3)
|
||||
web = LegacyWeb()
|
||||
option_definitions = rl_options
|
||||
topology_present = True
|
||||
data_version = 4
|
||||
required_client_version = (0, 3, 5)
|
||||
web = RLWeb()
|
||||
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = location_table
|
||||
location_name_to_id = {name: data.code for name, data in location_table.items()}
|
||||
|
||||
def _get_slot_data(self):
|
||||
return {
|
||||
"starting_gender": self.world.starting_gender[self.player],
|
||||
"starting_class": self.world.starting_class[self.player],
|
||||
"new_game_plus": self.world.new_game_plus[self.player],
|
||||
"fairy_chests_per_zone": self.world.fairy_chests_per_zone[self.player],
|
||||
"chests_per_zone": self.world.chests_per_zone[self.player],
|
||||
"universal_fairy_chests": self.world.universal_fairy_chests[self.player],
|
||||
"universal_chests": self.world.universal_chests[self.player],
|
||||
"vendors": self.world.vendors[self.player],
|
||||
"architect_fee": self.world.architect_fee[self.player],
|
||||
"disable_charon": self.world.disable_charon[self.player],
|
||||
"require_purchasing": self.world.require_purchasing[self.player],
|
||||
"gold_gain_multiplier": self.world.gold_gain_multiplier[self.player],
|
||||
"number_of_children": self.world.number_of_children[self.player],
|
||||
"khidr": self.world.khidr[self.player],
|
||||
"alexander": self.world.alexander[self.player],
|
||||
"leon": self.world.leon[self.player],
|
||||
"herodotus": self.world.herodotus[self.player],
|
||||
"allow_default_names": self.world.allow_default_names[self.player],
|
||||
"additional_sir_names": self.world.additional_sir_names[self.player],
|
||||
"additional_lady_names": self.world.additional_lady_names[self.player],
|
||||
"death_link": self.world.death_link[self.player],
|
||||
}
|
||||
item_pool: List[RLItem] = []
|
||||
prefill_items: List[RLItem] = []
|
||||
|
||||
def _create_items(self, name: str):
|
||||
data = item_table[name]
|
||||
return [self.create_item(name) for _ in range(data.quantity)]
|
||||
def setting(self, name: str):
|
||||
return getattr(self.world, name)[self.player]
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_slot_data()
|
||||
for option_name in legacy_options:
|
||||
option = getattr(self.world, option_name)[self.player]
|
||||
slot_data[option_name] = option.value
|
||||
return {option_name: self.setting(option_name).value for option_name in rl_options}
|
||||
|
||||
return slot_data
|
||||
def generate_early(self):
|
||||
self.prefill_items = []
|
||||
|
||||
# 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")):
|
||||
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}")
|
||||
|
||||
if additional_sir_names < int(self.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}")
|
||||
|
||||
if self.setting("vendors") == "early":
|
||||
self.prefill_items += [self.create_item("Blacksmith"), self.create_item("Enchantress")]
|
||||
|
||||
if self.setting("architect") == "early":
|
||||
self.prefill_items += [self.create_item("Architect")]
|
||||
|
||||
def generate_basic(self):
|
||||
itempool: typing.List[LegacyItem] = []
|
||||
total_required_locations = 64 + (self.world.chests_per_zone[self.player] * 4) + (self.world.fairy_chests_per_zone[self.player] * 4)
|
||||
self.item_pool = []
|
||||
total_locations = 64 + (self.setting("chests_per_zone") * 4) + (self.setting("fairy_chests_per_zone") * 4)
|
||||
|
||||
# Fill item pool with all required items
|
||||
for item in {**skill_unlocks_table, **runes_table}:
|
||||
# if Haggling, do not add if Disable Charon.
|
||||
if item == ItemName.haggling and self.world.disable_charon[self.player] == 1:
|
||||
# Add items to item pool. Anything with a "continue" will not be added to the item pool.
|
||||
for name, data in item_table.items():
|
||||
quantity = data.max_quantity
|
||||
|
||||
# Architect
|
||||
if name == "Architect":
|
||||
if self.setting("architect") == "disabled" or self.setting("architect") == "early":
|
||||
continue
|
||||
if self.setting("architect") == "start_unlocked":
|
||||
self.world.push_precollected(self.create_item(name))
|
||||
continue
|
||||
|
||||
# Blacksmith and Enchantress
|
||||
if name == "Blacksmith" or name == "Enchantress":
|
||||
if self.setting("vendors") == "start_unlocked":
|
||||
self.world.push_precollected(self.create_item(name))
|
||||
continue
|
||||
if self.setting("vendors") == "early":
|
||||
continue
|
||||
|
||||
# Haggling
|
||||
if name == "Haggling" and self.setting("disable_charon"):
|
||||
continue
|
||||
itempool += self._create_items(item)
|
||||
|
||||
# Blueprints
|
||||
if self.world.progressive_blueprints[self.player]:
|
||||
itempool += [self.create_item(ItemName.progressive_blueprints) for _ in range(15)]
|
||||
else:
|
||||
for item in blueprints_table:
|
||||
itempool += self._create_items(item)
|
||||
# Blueprints
|
||||
if data.category == "Blueprints":
|
||||
# No progressive blueprints if progressive_blueprints are disabled.
|
||||
if name == "Progressive Blueprints" and not self.setting("progressive_blueprints"):
|
||||
continue
|
||||
# No distinct blueprints if progressive_blueprints are enabled.
|
||||
elif name != "Progressive Blueprints" and self.setting("progressive_blueprints"):
|
||||
continue
|
||||
|
||||
# Check Pool settings to add a certain amount of these items.
|
||||
itempool += [self.create_item(ItemName.health) for _ in range(self.world.health_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.mana) for _ in range(self.world.mana_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.attack) for _ in range(self.world.attack_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.magic_damage) for _ in range(self.world.magic_damage_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.armor) for _ in range(self.world.armor_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.equip) for _ in range(self.world.equip_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.crit_chance) for _ in range(self.world.crit_chance_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.crit_damage) for _ in range(self.world.crit_damage_pool[self.player])]
|
||||
# Classes
|
||||
if data.category == "Classes":
|
||||
if name == "Progressive Knights":
|
||||
if "Knight" not in self.setting("available_classes"):
|
||||
continue
|
||||
|
||||
classes = self.world.available_classes[self.player]
|
||||
if "Dragon" in classes:
|
||||
itempool.append(self.create_item(ItemName.dragon))
|
||||
if "Traitor" in classes:
|
||||
itempool.append(self.create_item(ItemName.traitor))
|
||||
if self.world.starting_class[self.player] == "knight":
|
||||
itempool.append(self.create_item(ItemName.progressive_knight))
|
||||
elif "Knight" in classes:
|
||||
itempool.extend(self._create_items(ItemName.progressive_knight))
|
||||
if self.world.starting_class[self.player] == "mage":
|
||||
itempool.append(self.create_item(ItemName.progressive_mage))
|
||||
elif "Mage" in classes:
|
||||
itempool.extend(self._create_items(ItemName.progressive_mage))
|
||||
if self.world.starting_class[self.player] == "barbarian":
|
||||
itempool.append(self.create_item(ItemName.progressive_barbarian))
|
||||
elif "Barbarian" in classes:
|
||||
itempool.extend(self._create_items(ItemName.progressive_barbarian))
|
||||
if self.world.starting_class[self.player] == "knave":
|
||||
itempool.append(self.create_item(ItemName.progressive_knave))
|
||||
elif "Knave" in classes:
|
||||
itempool.extend(self._create_items(ItemName.progressive_knave))
|
||||
if self.world.starting_class[self.player] == "shinobi":
|
||||
itempool.append(self.create_item(ItemName.progressive_shinobi))
|
||||
elif "Shinobi" in classes:
|
||||
itempool.extend(self._create_items(ItemName.progressive_shinobi))
|
||||
if self.world.starting_class[self.player] == "miner":
|
||||
itempool.append(self.create_item(ItemName.progressive_miner))
|
||||
elif "Miner" in classes:
|
||||
itempool.extend(self._create_items(ItemName.progressive_miner))
|
||||
if self.world.starting_class[self.player] == "lich":
|
||||
itempool.append(self.create_item(ItemName.progressive_lich))
|
||||
elif "Lich" in classes:
|
||||
itempool.extend(self._create_items(ItemName.progressive_lich))
|
||||
if self.world.starting_class[self.player] == "spellthief":
|
||||
itempool.append(self.create_item(ItemName.progressive_spellthief))
|
||||
elif "Spellthief" in classes:
|
||||
itempool.extend(self._create_items(ItemName.progressive_spellthief))
|
||||
if self.setting("starting_class") == "knight":
|
||||
quantity = 1
|
||||
if name == "Progressive Mages":
|
||||
if "Mage" not in self.setting("available_classes"):
|
||||
continue
|
||||
|
||||
# Check if we need to start with these vendors or put them in the pool.
|
||||
if self.world.vendors[self.player] == "start_unlocked":
|
||||
self.world.push_precollected(self.world.create_item(ItemName.blacksmith, self.player))
|
||||
self.world.push_precollected(self.world.create_item(ItemName.enchantress, self.player))
|
||||
else:
|
||||
itempool += [self.create_item(ItemName.blacksmith), self.create_item(ItemName.enchantress)]
|
||||
if self.setting("starting_class") == "mage":
|
||||
quantity = 1
|
||||
if name == "Progressive Barbarians":
|
||||
if "Barbarian" not in self.setting("available_classes"):
|
||||
continue
|
||||
|
||||
# Add Architect.
|
||||
if self.world.architect[self.player] == "start_unlocked":
|
||||
self.world.push_precollected(self.world.create_item(ItemName.architect, self.player))
|
||||
elif self.world.architect[self.player] != "disabled":
|
||||
itempool.append(self.create_item(ItemName.architect))
|
||||
if self.setting("starting_class") == "barbarian":
|
||||
quantity = 1
|
||||
if name == "Progressive Knaves":
|
||||
if "Knave" not in self.setting("available_classes"):
|
||||
continue
|
||||
|
||||
# Fill item pool with the remaining
|
||||
for _ in range(len(itempool), total_required_locations):
|
||||
item = self.world.random.choice(list(misc_items_table.keys()))
|
||||
itempool.append(self.create_item(item))
|
||||
if self.setting("starting_class") == "knave":
|
||||
quantity = 1
|
||||
if name == "Progressive Miners":
|
||||
if "Miner" not in self.setting("available_classes"):
|
||||
continue
|
||||
|
||||
self.world.itempool += itempool
|
||||
if self.setting("starting_class") == "miner":
|
||||
quantity = 1
|
||||
if name == "Progressive Shinobis":
|
||||
if "Shinobi" not in self.setting("available_classes"):
|
||||
continue
|
||||
|
||||
if self.setting("starting_class") == "shinobi":
|
||||
quantity = 1
|
||||
if name == "Progressive Liches":
|
||||
if "Lich" not in self.setting("available_classes"):
|
||||
continue
|
||||
|
||||
if self.setting("starting_class") == "lich":
|
||||
quantity = 1
|
||||
if name == "Progressive Spellthieves":
|
||||
if "Spellthief" not in self.setting("available_classes"):
|
||||
continue
|
||||
|
||||
if self.setting("starting_class") == "spellthief":
|
||||
quantity = 1
|
||||
if name == "Dragons":
|
||||
if "Dragon" not in self.setting("available_classes"):
|
||||
continue
|
||||
if name == "Traitors":
|
||||
if "Traitor" not in self.setting("available_classes"):
|
||||
continue
|
||||
|
||||
# Skills
|
||||
if name == "Health Up":
|
||||
quantity = self.setting("health_pool")
|
||||
elif name == "Mana Up":
|
||||
quantity = self.setting("mana_pool")
|
||||
elif name == "Attack Up":
|
||||
quantity = self.setting("attack_pool")
|
||||
elif name == "Magic Damage Up":
|
||||
quantity = self.setting("magic_damage_pool")
|
||||
elif name == "Armor Up":
|
||||
quantity = self.setting("armor_pool")
|
||||
elif name == "Equip Up":
|
||||
quantity = self.setting("equip_pool")
|
||||
elif name == "Crit Chance Up":
|
||||
quantity = self.setting("crit_chance_pool")
|
||||
elif name == "Crit Damage Up":
|
||||
quantity = self.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)]
|
||||
|
||||
# Fill any empty locations with filler items.
|
||||
while len(self.item_pool) + len(self.prefill_items) < total_locations:
|
||||
self.item_pool.append(self.create_item(self.get_filler_item_name()))
|
||||
|
||||
self.world.itempool += self.item_pool
|
||||
|
||||
def pre_fill(self) -> None:
|
||||
reachable = [loc for loc in self.world.get_reachable_locations(player=self.player) if not loc.item]
|
||||
self.world.random.shuffle(reachable)
|
||||
items = self.prefill_items.copy()
|
||||
for item in items:
|
||||
reachable.pop().place_locked_item(item)
|
||||
|
||||
def get_pre_fill_items(self) -> List[RLItem]:
|
||||
return self.prefill_items
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.world.random.choice(list(misc_items_table.keys()))
|
||||
fillers = get_items_by_category("Filler")
|
||||
weights = [data.weight for data in fillers.values()]
|
||||
return self.world.random.choices([filler for filler in fillers.keys()], weights, k=1)[0]
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player)
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
def create_item(self, name: str) -> RLItem:
|
||||
data = item_table[name]
|
||||
return LegacyItem(name, ItemClassification.progression if data.progression else ItemClassification.filler, data.code, self.player)
|
||||
return RLItem(name, data.classification, data.code, self.player)
|
||||
|
||||
def create_event(self, name: str) -> RLItem:
|
||||
data = event_item_table[name]
|
||||
return RLItem(name, data.classification, data.code, self.player)
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player)
|
||||
self._place_events()
|
||||
|
||||
def _place_events(self):
|
||||
# Fountain
|
||||
self.world.get_location("Fountain Room", self.player).place_locked_item(
|
||||
self.create_event("Defeat The Fountain"))
|
||||
|
||||
# Khidr / Neo Khidr
|
||||
if self.setting("khidr") == "vanilla":
|
||||
self.world.get_location("Castle Hamson Boss Room", self.player).place_locked_item(
|
||||
self.create_event("Defeat Khidr"))
|
||||
else:
|
||||
self.world.get_location("Castle Hamson Boss Room", self.player).place_locked_item(
|
||||
self.create_event("Defeat Neo Khidr"))
|
||||
|
||||
# Alexander / Alexander IV
|
||||
if self.setting("alexander") == "vanilla":
|
||||
self.world.get_location("Forest Abkhazia Boss Room", self.player).place_locked_item(
|
||||
self.create_event("Defeat Alexander"))
|
||||
else:
|
||||
self.world.get_location("Forest Abkhazia Boss Room", self.player).place_locked_item(
|
||||
self.create_event("Defeat Alexander IV"))
|
||||
|
||||
# Ponce de Leon / Ponce de Freon
|
||||
if self.setting("leon") == "vanilla":
|
||||
self.world.get_location("The Maya Boss Room", self.player).place_locked_item(
|
||||
self.create_event("Defeat Ponce de Leon"))
|
||||
else:
|
||||
self.world.get_location("The Maya Boss Room", self.player).place_locked_item(
|
||||
self.create_event("Defeat Ponce de Freon"))
|
||||
|
||||
# Herodotus / Astrodotus
|
||||
if self.setting("herodotus") == "vanilla":
|
||||
self.world.get_location("Land of Darkness Boss Room", self.player).place_locked_item(
|
||||
self.create_event("Defeat Herodotus"))
|
||||
else:
|
||||
self.world.get_location("Land of Darkness Boss Room", self.player).place_locked_item(
|
||||
self.create_event("Defeat Astrodotus"))
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
## Where is the settings page?
|
||||
|
||||
The [player settings page for this game](../player-settings) is located contains all the options you need to configure
|
||||
and export a config file.
|
||||
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
|
||||
[template yaml here](../../../static/generated/configs/Rogue%20Legacy.yaml).
|
||||
|
||||
## What does randomization do to this game?
|
||||
|
||||
You are not able to buy skill upgrades in the manor upgrade screen, and instead, need to find them in order to level up
|
||||
your character to make fighting the 5 bosses easier.
|
||||
Rogue Legacy Randomizer takes all the classes, skills, runes, and blueprints and spreads them out into chests, the manor
|
||||
upgrade screen, bosses, and some special individual locations. The goal is to become powerful enough to defeat the four
|
||||
zone bosses and then defeat The Fountain.
|
||||
|
||||
## What items and locations get shuffled?
|
||||
|
||||
|
@ -16,6 +18,8 @@ All the skill upgrades, class upgrades, runes packs, and equipment packs are shu
|
|||
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.
|
||||
|
||||
Some additional locations that can contain items are the Jukebox, the Portraits, and the mini-game rewards.
|
||||
|
||||
## Which items can be in another player's world?
|
||||
|
||||
Any of the items which can be shuffled may also be placed into another player's world. It is possible to choose to limit
|
||||
|
@ -25,3 +29,8 @@ certain items to your own world.
|
|||
|
||||
When the player receives an item, your character will hold the item above their head and display it to the world. It's
|
||||
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).
|
|
@ -28,25 +28,8 @@ 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!
|
||||
|
||||
## Manual Installation
|
||||
## Recommended Installation Instructions
|
||||
|
||||
In order to run Rogue Legacy Randomizer you will need to have Rogue Legacy installed on your local machine. Extract the
|
||||
Randomizer release into a desired folder **outside** of your Rogue Legacy install. Copy the following files from your
|
||||
Rogue Legacy install into the main directory of your Rogue Legacy Randomizer install:
|
||||
|
||||
- DS2DEngine.dll
|
||||
- InputSystem.dll
|
||||
- Nuclex.Input.dll
|
||||
- SpriteSystem.dll
|
||||
- Tweener.dll
|
||||
|
||||
And copy the directory from your Rogue Legacy install as well into the main directory of your Rogue Legacy Randomizer
|
||||
install:
|
||||
|
||||
- Content/
|
||||
|
||||
Then copy the contents of the CustomContent directory in your Rogue Legacy Randomizer into the newly copied Content
|
||||
directory and overwrite all files.
|
||||
|
||||
**BE SURE YOU ARE REPLACING THE COPIED FILES IN YOUR ROGUE LEGACY RANDOMIZER DIRECTORY AND NOT REPLACING YOUR ROGUE
|
||||
LEGACY FILES!**
|
||||
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.
|
||||
|
|
Loading…
Reference in New Issue