Add Rogue Legacy to Archipelago (#180)
This commit is contained in:
parent
41fdafa3fb
commit
f06e565441
|
@ -155,4 +155,7 @@ Archipelago.zip
|
|||
|
||||
#minecraft server stuff
|
||||
jdk*/
|
||||
minecraft*/
|
||||
minecraft*/
|
||||
|
||||
#pyenv
|
||||
.python-version
|
||||
|
|
|
@ -14,6 +14,7 @@ Currently, the following games are supported:
|
|||
* Super Metroid
|
||||
* Secret of Evermore
|
||||
* Final Fantasy
|
||||
* Rogue Legacy
|
||||
|
||||
For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
|
||||
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Rogue Legacy (PC)
|
||||
|
||||
## Where is the settings page?
|
||||
The player settings page for this game is located <a href="../player-settings">here</a>. It contains all the options
|
||||
you need to configure and export a config file.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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 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
|
||||
good for business!
|
|
@ -0,0 +1,47 @@
|
|||
# Rogue Legacy Randomizer Setup Guide
|
||||
|
||||
## Required Software
|
||||
|
||||
- [Rogue Legacy Randomizer](https://github.com/ThePhar/RogueLegacyRandomizer/releases)
|
||||
|
||||
## Configuring your YAML file
|
||||
|
||||
### What is a YAML file and why do I need one?
|
||||
Your YAML file contains a set of configuration options which provide the generator with information about how
|
||||
it should generate your game. Each player of a multiworld will provide their own YAML file. This setup allows
|
||||
each player to enjoy an experience customized for their taste, and different players in the same multiworld
|
||||
can all have different options.
|
||||
|
||||
### Where do I get a YAML file?
|
||||
you can customize your settings by visiting the <a href="/games/Rogue Legacy/player-settings">rogue legacy settings page here</a>.
|
||||
|
||||
### Connect to the MultiServer
|
||||
Once in game, press the start button and the AP connection screen should appear. You will fill out the hostname, port,
|
||||
slot name, and password (if applicable). You should only need to fill out hostname, port, and password if the server
|
||||
provides an alternative one to the default values.
|
||||
|
||||
### Play the game
|
||||
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
|
||||
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!**
|
|
@ -342,5 +342,24 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"gameTitle": "Rogue Legacy",
|
||||
"tutorials": [
|
||||
{
|
||||
"name": "Multiworld Setup Guide",
|
||||
"description": "A guide to setting up the Rogue Legacy Randomizer software on your computer. This guide covers single-player, multiworld, and related software.",
|
||||
"files": [
|
||||
{
|
||||
"language": "English",
|
||||
"filename": "legacy/legacy_en.md",
|
||||
"link": "legacy/legacy/en",
|
||||
"authors": [
|
||||
"Phar"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
import typing
|
||||
|
||||
from BaseClasses import Item
|
||||
from .Names import ItemName
|
||||
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
code: typing.Optional[int]
|
||||
progression: bool
|
||||
quantity: int = 1
|
||||
event: bool = False
|
||||
|
||||
|
||||
class LegacyItem(Item):
|
||||
game: str = "Rogue Legacy"
|
||||
|
||||
def __init__(self, name, advancement: bool = False, code: int = None, player: int = None):
|
||||
super(LegacyItem, self).__init__(name, advancement, code, player)
|
||||
|
||||
|
||||
# Separate tables for each type of item.
|
||||
vendors_table = {
|
||||
ItemName.blacksmith: ItemData(90000, True),
|
||||
ItemName.enchantress: ItemData(90001, True),
|
||||
ItemName.architect: ItemData(90002, False),
|
||||
}
|
||||
|
||||
static_classes_table = {
|
||||
ItemName.knight: ItemData(90080, True),
|
||||
ItemName.paladin: ItemData(90081, True),
|
||||
ItemName.mage: ItemData(90082, True),
|
||||
ItemName.archmage: ItemData(90083, True),
|
||||
ItemName.barbarian: ItemData(90084, True),
|
||||
ItemName.barbarian_king: ItemData(90085, True),
|
||||
ItemName.knave: ItemData(90086, True),
|
||||
ItemName.assassin: ItemData(90087, True),
|
||||
ItemName.shinobi: ItemData(90088, True),
|
||||
ItemName.hokage: ItemData(90089, True),
|
||||
ItemName.miner: ItemData(90090, True),
|
||||
ItemName.spelunker: ItemData(90091, True),
|
||||
ItemName.lich: ItemData(90092, True),
|
||||
ItemName.lich_king: ItemData(90093, True),
|
||||
ItemName.spellthief: ItemData(90094, True),
|
||||
ItemName.spellsword: ItemData(90095, True),
|
||||
ItemName.dragon: ItemData(90096, True),
|
||||
ItemName.traitor: ItemData(90097, True),
|
||||
}
|
||||
|
||||
progressive_classes_table = {
|
||||
ItemName.progressive_knight: ItemData(90003, True, 2),
|
||||
ItemName.progressive_mage: ItemData(90004, True, 2),
|
||||
ItemName.progressive_barbarian: ItemData(90005, True, 2),
|
||||
ItemName.progressive_knave: ItemData(90006, True, 2),
|
||||
ItemName.progressive_shinobi: ItemData(90007, True, 2),
|
||||
ItemName.progressive_miner: ItemData(90008, True, 2),
|
||||
ItemName.progressive_lich: ItemData(90009, True, 2),
|
||||
ItemName.progressive_spellthief: ItemData(90010, True, 2),
|
||||
}
|
||||
|
||||
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),
|
||||
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, True),
|
||||
ItemName.silver_blueprints: ItemData(90041, True),
|
||||
ItemName.guardian_blueprints: ItemData(90042, True),
|
||||
ItemName.imperial_blueprints: ItemData(90043, True),
|
||||
ItemName.royal_blueprints: ItemData(90044, True),
|
||||
ItemName.knight_blueprints: ItemData(90045, True),
|
||||
ItemName.ranger_blueprints: ItemData(90046, True),
|
||||
ItemName.sky_blueprints: ItemData(90047, True),
|
||||
ItemName.dragon_blueprints: ItemData(90048, True),
|
||||
ItemName.slayer_blueprints: ItemData(90049, True),
|
||||
ItemName.blood_blueprints: ItemData(90050, True),
|
||||
ItemName.sage_blueprints: ItemData(90051, True),
|
||||
ItemName.retribution_blueprints: ItemData(90052, True),
|
||||
ItemName.holy_blueprints: ItemData(90053, True),
|
||||
ItemName.dark_blueprints: ItemData(90054, True),
|
||||
}
|
||||
|
||||
runes_table = {
|
||||
ItemName.vault_runes: ItemData(90060, True),
|
||||
ItemName.sprint_runes: ItemData(90061, True),
|
||||
ItemName.vampire_runes: ItemData(90062, True),
|
||||
ItemName.sky_runes: ItemData(90063, True),
|
||||
ItemName.siphon_runes: ItemData(90064, True),
|
||||
ItemName.retaliation_runes: ItemData(90065, True),
|
||||
ItemName.bounty_runes: ItemData(90066, True),
|
||||
ItemName.haste_runes: ItemData(90067, True),
|
||||
ItemName.curse_runes: ItemData(90068, True),
|
||||
ItemName.grace_runes: ItemData(90069, True),
|
||||
ItemName.balance_runes: ItemData(90070, True),
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
# Complete item table.
|
||||
item_table = {
|
||||
**vendors_table,
|
||||
**static_classes_table,
|
||||
**progressive_classes_table,
|
||||
**skill_unlocks_table,
|
||||
**blueprints_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}
|
|
@ -0,0 +1,85 @@
|
|||
import typing
|
||||
|
||||
from BaseClasses import Location
|
||||
from .Names import LocationName
|
||||
|
||||
|
||||
class LegacyLocation(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,
|
||||
|
||||
# Boss Rewards
|
||||
LocationName.boss_khindr: 91100,
|
||||
LocationName.boss_alexander: 91102,
|
||||
LocationName.boss_leon: 91104,
|
||||
LocationName.boss_herodotus: 91106,
|
||||
|
||||
# Special Rooms
|
||||
LocationName.special_jukebox: 91200,
|
||||
|
||||
# Special Locations
|
||||
LocationName.castle: None,
|
||||
LocationName.garden: None,
|
||||
LocationName.tower: None,
|
||||
LocationName.dungeon: None,
|
||||
LocationName.fountain: None,
|
||||
}
|
||||
|
||||
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)},
|
||||
}
|
||||
|
||||
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)},
|
||||
}
|
||||
|
||||
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()}
|
|
@ -0,0 +1,95 @@
|
|||
# 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"
|
||||
|
||||
# Blueprint Definitions
|
||||
squire_blueprints = "Squire Armor Blueprints"
|
||||
silver_blueprints = "Silver Armor Blueprints"
|
||||
guardian_blueprints = "Guardian Armor Blueprints"
|
||||
imperial_blueprints = "Imperial Armor Blueprints"
|
||||
royal_blueprints = "Royal Armor Blueprints"
|
||||
knight_blueprints = "Knight Armor Blueprints"
|
||||
ranger_blueprints = "Ranger Armor Blueprints"
|
||||
sky_blueprints = "Sky Armor Blueprints"
|
||||
dragon_blueprints = "Dragon Armor Blueprints"
|
||||
slayer_blueprints = "Slayer Armor Blueprints"
|
||||
blood_blueprints = "Blood Armor Blueprints"
|
||||
sage_blueprints = "Sage Armor Blueprints"
|
||||
retribution_blueprints = "Retribution Armor Blueprints"
|
||||
holy_blueprints = "Holy Armor Blueprints"
|
||||
dark_blueprints = "Dark Armor 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_khindr = "Defeat Khindr"
|
||||
boss_alexander = "Defeat Alexander"
|
||||
boss_leon = "Defeat Ponce de Leon"
|
||||
boss_herodotus = "Defeat Herodotus"
|
||||
boss_fountain = "Defeat The Fountain"
|
|
@ -0,0 +1,52 @@
|
|||
# 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_khindr = "Khindr's Boss Chest"
|
||||
boss_alexander = "Alexander's Boss Chest"
|
||||
boss_leon = "Ponce de Leon's Boss Chest"
|
||||
boss_herodotus = "Herodotus's Boss Chest"
|
||||
|
||||
# Special Room Definitions
|
||||
special_jukebox = "Jukebox"
|
||||
|
||||
# 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"
|
|
@ -0,0 +1,128 @@
|
|||
import typing
|
||||
|
||||
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle
|
||||
|
||||
|
||||
class StartingGender(Choice):
|
||||
"""
|
||||
Determines the gender of your initial 'Sir Lee' character.
|
||||
"""
|
||||
displayname = "Starting Gender"
|
||||
option_sir = 0
|
||||
option_lady = 1
|
||||
alias_male = 0
|
||||
alias_female = 1
|
||||
default = 0
|
||||
|
||||
|
||||
class StartingClass(Choice):
|
||||
"""
|
||||
Determines the starting class of your initial 'Sir Lee' character.
|
||||
"""
|
||||
displayname = "Starting Class"
|
||||
option_knight = 0
|
||||
option_mage = 1
|
||||
option_barbarian = 2
|
||||
option_knave = 3
|
||||
default = 0
|
||||
|
||||
|
||||
class NewGamePlus(Choice):
|
||||
"""
|
||||
Puts the castle in new game plus mode which vastly increases enemy level, but increases gold gain by 50%. Not
|
||||
recommended for those inexperienced to Rogue Legacy!
|
||||
"""
|
||||
displayname = "New Game Plus"
|
||||
option_normal = 0
|
||||
option_new_game_plus = 1
|
||||
option_new_game_plus_2 = 2
|
||||
alias_hard = 1
|
||||
alias_brutal = 2
|
||||
default = 0
|
||||
|
||||
|
||||
class FairyChestsPerZone(Range):
|
||||
"""
|
||||
Determines the number of Fairy Chests in a given zone that contain items. After these have been checked, only stat
|
||||
bonuses can be found in Fairy Chests.
|
||||
"""
|
||||
displayname = "Fairy Chests Per Zone"
|
||||
range_start = 5
|
||||
range_end = 15
|
||||
default = 5
|
||||
|
||||
|
||||
class ChestsPerZone(Range):
|
||||
"""
|
||||
Determines the number of Non-Fairy Chests in a given zone that contain items. After these have been checked, only
|
||||
gold or stat bonuses can be found in Chests.
|
||||
"""
|
||||
displayname = "Chests Per Zone"
|
||||
range_start = 15
|
||||
range_end = 30
|
||||
default = 15
|
||||
|
||||
|
||||
class Vendors(Choice):
|
||||
"""
|
||||
Determines where to place the Blacksmith and Enchantress unlocks in logic (or start with them unlocked).
|
||||
"""
|
||||
displayname = "Vendors"
|
||||
option_start_unlocked = 0
|
||||
option_early = 1
|
||||
option_normal = 2
|
||||
option_anywhere = 3
|
||||
default = 1
|
||||
|
||||
|
||||
class DisableCharon(Toggle):
|
||||
"""
|
||||
Prevents Charon from taking your money when you re-enter the castle. Also removes Haggling from the Item Pool.
|
||||
"""
|
||||
displayname = "Disable Charon"
|
||||
|
||||
|
||||
class RequirePurchasing(DefaultOnToggle):
|
||||
"""
|
||||
Determines where you will be required to purchase equipment and runes from the Blacksmith and Enchantress before
|
||||
equipping them. If you disable require purchasing, Manor Renovations are scaled to take this into account.
|
||||
"""
|
||||
displayname = "Require Purchasing"
|
||||
|
||||
|
||||
class GoldGainMultiplier(Choice):
|
||||
"""
|
||||
Adjusts the multiplier for gaining gold from all sources.
|
||||
"""
|
||||
displayname = "Gold Gain Multiplier"
|
||||
option_normal = 0
|
||||
option_quarter = 1
|
||||
option_half = 2
|
||||
option_double = 3
|
||||
option_quadruple = 4
|
||||
default = 0
|
||||
|
||||
|
||||
class NumberOfChildren(Range):
|
||||
"""
|
||||
Determines the number of offspring you can choose from on the lineage screen after a death.
|
||||
"""
|
||||
displayname = "Number of Children"
|
||||
range_start = 1
|
||||
range_end = 5
|
||||
default = 3
|
||||
|
||||
|
||||
legacy_options: typing.Dict[str, type(Option)] = {
|
||||
"starting_gender": StartingGender,
|
||||
"starting_class": StartingClass,
|
||||
"new_game_plus": NewGamePlus,
|
||||
"fairy_chests_per_zone": FairyChestsPerZone,
|
||||
"chests_per_zone": ChestsPerZone,
|
||||
"vendors": Vendors,
|
||||
"disable_charon": DisableCharon,
|
||||
"require_purchasing": RequirePurchasing,
|
||||
"gold_gain_multiplier": GoldGainMultiplier,
|
||||
"number_of_children": NumberOfChildren,
|
||||
"death_link": DeathLink,
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
import typing
|
||||
|
||||
from BaseClasses import MultiWorld, Region, Entrance
|
||||
from .Items import LegacyItem
|
||||
from .Locations import LegacyLocation, diary_location_table, location_table, base_location_table
|
||||
from .Names import LocationName, ItemName
|
||||
|
||||
|
||||
def create_regions(world, player: int):
|
||||
|
||||
locations: typing.List[str] = []
|
||||
|
||||
# Add required locations.
|
||||
locations += [location for location in base_location_table]
|
||||
locations += [location for location in diary_location_table]
|
||||
|
||||
# Add chests per settings.
|
||||
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}"]
|
||||
|
||||
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}"]
|
||||
|
||||
# Set up the regions correctly.
|
||||
world.regions += [
|
||||
create_region(world, player, "Menu", None, [LocationName.outside]),
|
||||
create_region(world, player, LocationName.castle, locations),
|
||||
]
|
||||
|
||||
# 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_khindr, True, None, player))
|
||||
world.get_location(LocationName.garden, player).place_locked_item(LegacyItem(ItemName.boss_alexander, True, None, player))
|
||||
world.get_location(LocationName.tower, player).place_locked_item(LegacyItem(ItemName.boss_leon, True, None, player))
|
||||
world.get_location(LocationName.dungeon, player).place_locked_item(LegacyItem(ItemName.boss_herodotus, True, None, player))
|
||||
world.get_location(LocationName.fountain, player).place_locked_item(LegacyItem(ItemName.boss_fountain, True, None, player))
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
# Shamelessly stolen from the ROR2 definition, lol
|
||||
ret = Region(name, None, 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)
|
||||
ret.locations.append(location)
|
||||
if exits:
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(player, exit, ret))
|
||||
|
||||
return ret
|
|
@ -0,0 +1,131 @@
|
|||
from BaseClasses import MultiWorld
|
||||
from .Names import LocationName, ItemName
|
||||
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 _legacy_has_all_vendors(self, player: int) -> bool:
|
||||
return self.has_all({ItemName.blacksmith, ItemName.enchantress}, player)
|
||||
|
||||
def _legacy_has_stat_upgrades(self, player: int, amount: int) -> bool:
|
||||
count: int = 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)
|
||||
return count >= amount
|
||||
|
||||
|
||||
def set_rules(world: MultiWorld, player: int):
|
||||
# Chests
|
||||
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_khindr, player))
|
||||
set_rule(world.get_location(f"{LocationName.tower} - Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(f"{LocationName.dungeon} - Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_leon, player))
|
||||
|
||||
# Fairy Chests
|
||||
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_khindr, player))
|
||||
set_rule(world.get_location(f"{LocationName.tower} - Fairy Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(f"{LocationName.dungeon} - Fairy Chest {i + 1}", player),
|
||||
lambda state: state.has(ItemName.boss_leon, player))
|
||||
|
||||
# Vendors
|
||||
if world.vendors[player] == "early":
|
||||
set_rule(world.get_location(LocationName.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))
|
||||
elif world.vendors[player] == "anywhere":
|
||||
pass # it can be anywhere, so no rule for this!
|
||||
|
||||
# Diaries
|
||||
for i in range(0, 5):
|
||||
set_rule(world.get_location(f"Diary {i + 6}", player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(f"Diary {i + 11}", player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(f"Diary {i + 16}", player),
|
||||
lambda state: state.has(ItemName.boss_leon, player))
|
||||
set_rule(world.get_location(f"Diary {i + 21}", player),
|
||||
lambda state: state.has(ItemName.boss_herodotus, player))
|
||||
|
||||
# Scale each manor location.
|
||||
set_rule(world.get_location(LocationName.manor_left_wing_window, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_wing_roof, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_wing_window, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_wing_roof, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_base, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_big_base, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_tree1, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_tree2, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_tree, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_upper1, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_upper2, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_windows, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_big_roof, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_far_base, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_far_roof, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_left_extension, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_big_upper, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_big_roof, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_extension, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_high_base, player),
|
||||
lambda state: state.has(ItemName.boss_leon, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_high_upper, player),
|
||||
lambda state: state.has(ItemName.boss_leon, player))
|
||||
set_rule(world.get_location(LocationName.manor_right_high_tower, player),
|
||||
lambda state: state.has(ItemName.boss_leon, player))
|
||||
set_rule(world.get_location(LocationName.manor_observatory_base, player),
|
||||
lambda state: state.has(ItemName.boss_leon, player))
|
||||
set_rule(world.get_location(LocationName.manor_observatory_scope, player),
|
||||
lambda state: state.has(ItemName.boss_leon, player))
|
||||
|
||||
# Standard Zone Progression
|
||||
set_rule(world.get_location(LocationName.garden, player),
|
||||
lambda state: state._legacy_has_stat_upgrades(player, 10) and state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.tower, player),
|
||||
lambda state: state._legacy_has_stat_upgrades(player, 25) and state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.dungeon, player),
|
||||
lambda state: state._legacy_has_stat_upgrades(player, 40) and state.has(ItemName.boss_leon, player))
|
||||
|
||||
# Bosses
|
||||
set_rule(world.get_location(LocationName.boss_khindr, player),
|
||||
lambda state: state.has(ItemName.boss_khindr, player))
|
||||
set_rule(world.get_location(LocationName.boss_alexander, player),
|
||||
lambda state: state.has(ItemName.boss_alexander, player))
|
||||
set_rule(world.get_location(LocationName.boss_leon, player),
|
||||
lambda state: state.has(ItemName.boss_leon, player))
|
||||
set_rule(world.get_location(LocationName.boss_herodotus, player),
|
||||
lambda state: state.has(ItemName.boss_herodotus, player))
|
||||
set_rule(world.get_location(LocationName.fountain, player),
|
||||
lambda state: state._legacy_has_stat_upgrades(player, 50) and state.has(ItemName.boss_herodotus, player))
|
||||
|
||||
world.completion_condition[player] = lambda state: state.has(ItemName.boss_fountain, player)
|
|
@ -0,0 +1,105 @@
|
|||
import typing
|
||||
|
||||
from BaseClasses import Item, MultiWorld
|
||||
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 .Regions import create_regions
|
||||
from .Rules import set_rules
|
||||
from .Names import ItemName
|
||||
from ..AutoWorld import World
|
||||
|
||||
|
||||
class LegacyWorld(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"
|
||||
options = legacy_options
|
||||
topology_present = False
|
||||
data_version = 1
|
||||
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = location_table
|
||||
|
||||
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],
|
||||
"vendors": self.world.vendors[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],
|
||||
"death_link": self.world.death_link[self.player],
|
||||
}
|
||||
|
||||
def _create_items(self, name: str):
|
||||
data = item_table[name]
|
||||
return [self.create_item(name)] * data.quantity
|
||||
|
||||
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 slot_data
|
||||
|
||||
def generate_basic(self):
|
||||
itempool: typing.List[LegacyItem] = []
|
||||
total_required_locations = 61 + (self.world.chests_per_zone[self.player] * 4) + (self.world.fairy_chests_per_zone[self.player] * 4)
|
||||
|
||||
# Fill item pool with all required items
|
||||
for item in {**skill_unlocks_table, **blueprints_table, **runes_table}:
|
||||
# if Haggling, do not add if Disable Charon.
|
||||
if item == ItemName.haggling and self.world.disable_charon[self.player] == 1:
|
||||
continue
|
||||
itempool += self._create_items(item)
|
||||
|
||||
# Add specific classes into the pool. Eventually, will be able to shuffle the starting ones, but until then...
|
||||
itempool += [
|
||||
self.create_item(ItemName.paladin),
|
||||
self.create_item(ItemName.archmage),
|
||||
self.create_item(ItemName.barbarian_king),
|
||||
self.create_item(ItemName.assassin),
|
||||
self.create_item(ItemName.dragon),
|
||||
self.create_item(ItemName.traitor),
|
||||
*self._create_items(ItemName.progressive_shinobi),
|
||||
*self._create_items(ItemName.progressive_miner),
|
||||
*self._create_items(ItemName.progressive_lich),
|
||||
*self._create_items(ItemName.progressive_spellthief),
|
||||
]
|
||||
|
||||
# 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)]
|
||||
|
||||
# Add Arcitect.
|
||||
itempool += [self.create_item(ItemName.architect)]
|
||||
|
||||
# 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 += [self.create_item(item)]
|
||||
|
||||
self.world.itempool += itempool
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player)
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
data = item_table[name]
|
||||
return LegacyItem(name, data.progression, data.code, self.player)
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
Loading…
Reference in New Issue