diff --git a/worlds/dark_souls_3/Items.py b/worlds/dark_souls_3/Items.py index e7ba2ecf..140d3ba6 100644 --- a/worlds/dark_souls_3/Items.py +++ b/worlds/dark_souls_3/Items.py @@ -1,10 +1,18 @@ +import sys + from BaseClasses import Item -from worlds.dark_souls_3.data.items_data import item_tables +from worlds.dark_souls_3.data.items_data import item_tables, dlc_shields_table, dlc_weapons_upgrade_10_table, \ + dlc_weapons_upgrade_5_table, dlc_goods_table, dlc_spells_table, dlc_armor_table, dlc_ring_table, dlc_misc_table, dlc_goods_2_table class DarkSouls3Item(Item): game: str = "Dark Souls III" + dlc_set = {**dlc_shields_table, **dlc_weapons_upgrade_10_table, **dlc_weapons_upgrade_5_table, + **dlc_goods_table, **dlc_spells_table, **dlc_armor_table, **dlc_ring_table, **dlc_misc_table} + + dlc_progressive = {**dlc_goods_2_table} + @staticmethod def get_name_to_id() -> dict: base_id = 100000 @@ -12,6 +20,17 @@ class DarkSouls3Item(Item): output = {} for i, table in enumerate(item_tables): + if len(table) > table_offset: + raise Exception("An item table has {} entries, that is more than {} entries (table #{})".format(len(table), table_offset, i)) output.update({name: id for id, name in enumerate(table, base_id + (table_offset * i))}) return output + + @staticmethod + def is_dlc_item(name) -> bool: + return name in DarkSouls3Item.dlc_set + + @staticmethod + def is_dlc_progressive(name) -> bool: + return name in DarkSouls3Item.dlc_progressive + diff --git a/worlds/dark_souls_3/Locations.py b/worlds/dark_souls_3/Locations.py index 0ba84e36..a63a951c 100644 --- a/worlds/dark_souls_3/Locations.py +++ b/worlds/dark_souls_3/Locations.py @@ -1,5 +1,8 @@ +import sys + from BaseClasses import Location -from worlds.dark_souls_3.data.locations_data import location_tables +from worlds.dark_souls_3.data.locations_data import location_tables, painted_world_table, dreg_heap_table, \ + ringed_city_table class DarkSouls3Location(Location): @@ -12,6 +15,8 @@ class DarkSouls3Location(Location): output = {} for i, table in enumerate(location_tables): + if len(table) > table_offset: + raise Exception("A location table has {} entries, that is more than {} entries (table #{})".format(len(table), table_offset, i)) output.update({name: id for id, name in enumerate(table, base_id + (table_offset * i))}) return output diff --git a/worlds/dark_souls_3/Options.py b/worlds/dark_souls_3/Options.py index 54c43143..dc9510a7 100644 --- a/worlds/dark_souls_3/Options.py +++ b/worlds/dark_souls_3/Options.py @@ -1,5 +1,5 @@ import typing -from Options import Toggle, Option, DeathLink +from Options import Toggle, Option, Range, Choice, DeathLink class AutoEquipOption(Toggle): @@ -29,10 +29,57 @@ class NoEquipLoadOption(Toggle): display_name = "No Equip load" -class RandomizeWeaponsLevelOption(Toggle): - """Enable this option to upgrade 33% ( based on the probability chance ) of the pool of weapons to a random value - between +1 and +5/+10""" +class RandomizeWeaponsLevelOption(Choice): + """Enable this option to upgrade a percentage of the pool of weapons to a random value between the minimum and + maximum levels defined. + all: All weapons are eligible, both basic and epic + basic: Only weapons that can be upgraded to +10 + epic: Only weapons that can be upgraded to +5""" display_name = "Randomize weapons level" + option_none = 0 + option_all = 1 + option_basic = 2 + option_epic = 3 + + +class RandomizeWeaponsLevelPercentageOption(Range): + """The percentage of weapons in the pool to be upgraded if randomize weapons level is toggled""" + display_name = "Percentage of randomized weapons" + range_start = 1 + range_end = 100 + default = 33 + + +class MinLevelsIn5WeaponPoolOption(Range): + """The minimum upgraded value of a weapon in the pool of weapons that can only reach +5""" + display_name = "Minimum level of +5 weapons" + range_start = 1 + range_end = 5 + default = 1 + + +class MaxLevelsIn5WeaponPoolOption(Range): + """The maximum upgraded value of a weapon in the pool of weapons that can only reach +5""" + display_name = "Maximum level of +5 weapons" + range_start = 1 + range_end = 5 + default = 5 + + +class MinLevelsIn10WeaponPoolOption(Range): + """The minimum upgraded value of a weapon in the pool of weapons that can reach +10""" + display_name = "Minimum level of +10 weapons" + range_start = 1 + range_end = 10 + default = 1 + + +class MaxLevelsIn10WeaponPoolOption(Range): + """The maximum upgraded value of a weapon in the pool of weapons that can reach +10""" + display_name = "Maximum level of +10 weapons" + range_start = 1 + range_end = 10 + default = 10 class LateBasinOfVowsOption(Toggle): @@ -41,14 +88,31 @@ class LateBasinOfVowsOption(Toggle): display_name = "Late Basin of Vows" +class EnableProgressiveLocationsOption(Toggle): + """Randomize upgrade materials such as the titanite shards, the estus shards and the consumables""" + display_name = "Randomize materials, Estus shards and consumables (+196 checks/items)" + + +class EnableDLCOption(Toggle): + """To use this option, you must own both the ASHES OF ARIANDEL and the RINGED CITY DLC""" + display_name = "Add the DLC Items and Locations to the pool (+81 checks/items)" + + dark_souls_options: typing.Dict[str, type(Option)] = { "auto_equip": AutoEquipOption, "lock_equip": LockEquipOption, "no_weapon_requirements": NoWeaponRequirementsOption, "randomize_weapons_level": RandomizeWeaponsLevelOption, + "randomize_weapons_percentage": RandomizeWeaponsLevelPercentageOption, + "min_levels_in_5": MinLevelsIn5WeaponPoolOption, + "max_levels_in_5": MaxLevelsIn5WeaponPoolOption, + "min_levels_in_10": MinLevelsIn10WeaponPoolOption, + "max_levels_in_10": MaxLevelsIn10WeaponPoolOption, "late_basin_of_vows": LateBasinOfVowsOption, "no_spell_requirements": NoSpellRequirementsOption, "no_equip_load": NoEquipLoadOption, "death_link": DeathLink, + "enable_progressive_locations": EnableProgressiveLocationsOption, + "enable_dlc": EnableDLCOption, } diff --git a/worlds/dark_souls_3/__init__.py b/worlds/dark_souls_3/__init__.py index 6d16e562..d08cd3ee 100644 --- a/worlds/dark_souls_3/__init__.py +++ b/worlds/dark_souls_3/__init__.py @@ -1,18 +1,18 @@ # world/dark_souls_3/__init__.py -import json -import os from typing import Dict from .Items import DarkSouls3Item from .Locations import DarkSouls3Location from .Options import dark_souls_options -from .data.items_data import weapons_upgrade_5_table, weapons_upgrade_10_table, item_dictionary, key_items_list +from .data.items_data import weapons_upgrade_5_table, weapons_upgrade_10_table, item_dictionary, key_items_list, \ + dlc_weapons_upgrade_5_table, dlc_weapons_upgrade_10_table from .data.locations_data import location_dictionary, fire_link_shrine_table, \ high_wall_of_lothric, \ undead_settlement_table, road_of_sacrifice_table, consumed_king_garden_table, cathedral_of_the_deep_table, \ farron_keep_table, catacombs_of_carthus_table, smouldering_lake_table, irithyll_of_the_boreal_valley_table, \ irithyll_dungeon_table, profaned_capital_table, anor_londo_table, lothric_castle_table, grand_archives_table, \ - untended_graves_table, archdragon_peak_table, firelink_shrine_bell_tower_table, progressive_locations + untended_graves_table, archdragon_peak_table, firelink_shrine_bell_tower_table, progressive_locations, \ + progressive_locations_2, progressive_locations_3, painted_world_table, dreg_heap_table, ringed_city_table, dlc_progressive_locations from ..AutoWorld import World, WebWorld from BaseClasses import MultiWorld, Region, Item, Entrance, Tutorial, ItemClassification from ..generic.Rules import set_rule, add_item_rule @@ -52,9 +52,9 @@ class DarkSouls3World(World): option_definitions = dark_souls_options topology_present: bool = True web = DarkSouls3Web() - data_version = 4 + data_version = 5 base_id = 100000 - required_client_version = (0, 3, 6) + required_client_version = (0, 3, 7) item_name_to_id = DarkSouls3Item.get_name_to_id() location_name_to_id = DarkSouls3Location.get_name_to_id() @@ -77,7 +77,15 @@ class DarkSouls3World(World): return DarkSouls3Item(name, item_classification, data, self.player) def create_regions(self): - menu_region = self.create_region("Menu", progressive_locations) + + if self.multiworld.enable_progressive_locations[self.player].value and self.multiworld.enable_dlc[self.player].value: + menu_region = self.create_region("Menu", {**progressive_locations, **progressive_locations_2, + **progressive_locations_3, **dlc_progressive_locations}) + elif self.multiworld.enable_progressive_locations[self.player].value: + menu_region = self.create_region("Menu", {**progressive_locations, **progressive_locations_2, + **progressive_locations_3}) + else: + menu_region = self.create_region("Menu", None) # Create all Vanilla regions of Dark Souls III firelink_shrine_region = self.create_region("Firelink Shrine", fire_link_shrine_table) @@ -101,6 +109,11 @@ class DarkSouls3World(World): untended_graves_region = self.create_region("Untended Graves", untended_graves_table) archdragon_peak_region = self.create_region("Archdragon Peak", archdragon_peak_table) kiln_of_the_first_flame_region = self.create_region("Kiln Of The First Flame", None) + # DLC Down here + if self.multiworld.enable_dlc[self.player]: + painted_world_of_ariandel_region = self.create_region("Painted World of Ariandel", painted_world_table) + dreg_heap_region = self.create_region("Dreg Heap", dreg_heap_table) + ringed_city_region = self.create_region("Ringed City", ringed_city_table) # Create the entrance to connect those regions menu_region.exits.append(Entrance(self.player, "New Game", menu_region)) @@ -112,7 +125,8 @@ class DarkSouls3World(World): firelink_shrine_region.exits.append(Entrance(self.player, "Goto Bell Tower", firelink_shrine_region)) self.multiworld.get_entrance("Goto High Wall of Lothric", self.player).connect(high_wall_of_lothric_region) - self.multiworld.get_entrance("Goto Kiln Of The First Flame", self.player).connect(kiln_of_the_first_flame_region) + self.multiworld.get_entrance("Goto Kiln Of The First Flame", self.player).connect( + kiln_of_the_first_flame_region) self.multiworld.get_entrance("Goto Bell Tower", self.player).connect(firelink_shrine_bell_tower_region) high_wall_of_lothric_region.exits.append(Entrance(self.player, "Goto Undead Settlement", high_wall_of_lothric_region)) @@ -133,7 +147,7 @@ class DarkSouls3World(World): catacombs_of_carthus_region)) catacombs_of_carthus_region.exits.append(Entrance(self.player, "Goto Smouldering Lake", catacombs_of_carthus_region)) - self.multiworld.get_entrance("Goto Irithyll of the boreal", self.player).\ + self.multiworld.get_entrance("Goto Irithyll of the boreal", self.player). \ connect(irithyll_of_the_boreal_valley_region) self.multiworld.get_entrance("Goto Smouldering Lake", self.player).connect(smouldering_lake_region) irithyll_of_the_boreal_valley_region.exits.append(Entrance(self.player, "Goto Irithyll dungeon", @@ -153,6 +167,16 @@ class DarkSouls3World(World): consumed_king_garden_region.exits.append(Entrance(self.player, "Goto Untended Graves", consumed_king_garden_region)) self.multiworld.get_entrance("Goto Untended Graves", self.player).connect(untended_graves_region) + # DLC Connectors Below + if self.multiworld.enable_dlc[self.player]: + cathedral_of_the_deep_region.exits.append(Entrance(self.player, "Goto Painted World of Ariandel", + cathedral_of_the_deep_region)) + self.multiworld.get_entrance("Goto Painted World of Ariandel", self.player).connect(painted_world_of_ariandel_region) + painted_world_of_ariandel_region.exits.append(Entrance(self.player, "Goto Dreg Heap", + painted_world_of_ariandel_region)) + self.multiworld.get_entrance("Goto Dreg Heap", self.player).connect(dreg_heap_region) + dreg_heap_region.exits.append(Entrance(self.player, "Goto Ringed City", dreg_heap_region)) + self.multiworld.get_entrance("Goto Ringed City", self.player).connect(ringed_city_region) # For each region, add the associated locations retrieved from the corresponding location_table def create_region(self, region_name, location_table) -> Region: @@ -169,8 +193,18 @@ class DarkSouls3World(World): def create_items(self): for name, address in self.item_name_to_id.items(): # Specific items will be included in the item pool under certain conditions. See generate_basic - if name != "Basin of Vows": - self.multiworld.itempool += [self.create_item(name)] + if name == "Basin of Vows": + continue + # Do not add progressive_items ( containing "#" ) to the itempool if the option is disabled + if (not self.multiworld.enable_progressive_locations[self.player]) and "#" in name: + continue + # Do not add DLC items if the option is disabled + if (not self.multiworld.enable_dlc[self.player]) and DarkSouls3Item.is_dlc_item(name): + continue + # Do not add DLC Progressives if both options are disabled + if ((not self.multiworld.enable_progressive_locations[self.player]) or (not self.multiworld.enable_dlc[self.player])) and DarkSouls3Item.is_dlc_progressive(name): + continue + self.multiworld.itempool += [self.create_item(name)] def generate_early(self): pass @@ -194,15 +228,23 @@ class DarkSouls3World(World): lambda state: state.has("Grand Archives Key", self.player)) set_rule(self.multiworld.get_entrance("Goto Kiln Of The First Flame", self.player), lambda state: state.has("Cinders of a Lord - Abyss Watcher", self.player) and - state.has("Cinders of a Lord - Yhorm the Giant", self.player) and - state.has("Cinders of a Lord - Aldrich", self.player) and - state.has("Cinders of a Lord - Lothric Prince", self.player)) + state.has("Cinders of a Lord - Yhorm the Giant", self.player) and + state.has("Cinders of a Lord - Aldrich", self.player) and + state.has("Cinders of a Lord - Lothric Prince", self.player)) + # DLC Access Rules Below + if self.multiworld.enable_dlc[self.player]: + set_rule(self.multiworld.get_entrance("Goto Painted World of Ariandel", self.player), + lambda state: state.has("Contraption Key", self.player)) + set_rule(self.multiworld.get_entrance("Goto Ringed City", self.player), + lambda state: state.has("Small Envoy Banner", self.player)) # Define the access rules to some specific locations set_rule(self.multiworld.get_location("HWL: Soul of the Dancer", self.player), lambda state: state.has("Basin of Vows", self.player)) set_rule(self.multiworld.get_location("HWL: Greirat's Ashes", self.player), lambda state: state.has("Cell Key", self.player)) + set_rule(self.multiworld.get_location("HWL: Blue Tearstone Ring", self.player), + lambda state: state.has("Cell Key", self.player)) set_rule(self.multiworld.get_location("ID: Bellowing Dragoncrest Ring", self.player), lambda state: state.has("Jailbreaker's Key", self.player)) set_rule(self.multiworld.get_location("ID: Prisoner Chief's Ashes", self.player), @@ -242,17 +284,38 @@ class DarkSouls3World(World): # Depending on the specified option, modify items hexadecimal value to add an upgrade level item_dictionary_copy = item_dictionary.copy() - if self.multiworld.randomize_weapons_level[self.player]: - # Randomize some weapons upgrades - for name in weapons_upgrade_5_table.keys(): - if self.multiworld.random.randint(0, 100) < 33: - value = self.multiworld.random.randint(1, 5) - item_dictionary_copy[name] += value + if self.multiworld.randomize_weapons_level[self.player] > 0: + # if the user made an error and set a min higher than the max we default to the max + max_5 = self.multiworld.max_levels_in_5[self.player] + min_5 = min(self.multiworld.min_levels_in_5[self.player], max_5) + max_10 = self.multiworld.max_levels_in_10[self.player] + min_10 = min(self.multiworld.min_levels_in_10[self.player], max_10) + weapons_percentage = self.multiworld.randomize_weapons_percentage[self.player] - for name in weapons_upgrade_10_table.keys(): - if self.multiworld.random.randint(0, 100) < 33: - value = self.multiworld.random.randint(1, 10) - item_dictionary_copy[name] += value + # Randomize some weapons upgrades + if self.multiworld.randomize_weapons_level[self.player] in [1, 3]: # Options are either all or +5 + for name in weapons_upgrade_5_table.keys(): + if self.multiworld.per_slot_randoms[self.player].randint(1, 100) < weapons_percentage: + value = self.multiworld.per_slot_randoms[self.player].randint(min_5, max_5) + item_dictionary_copy[name] += value + + if self.multiworld.randomize_weapons_level[self.player] in [1, 2]: # Options are either all or +10 + for name in weapons_upgrade_10_table.keys(): + if self.multiworld.per_slot_randoms[self.player].randint(1, 100) < weapons_percentage: + value = self.multiworld.per_slot_randoms[self.player].randint(min_10, max_10) + item_dictionary_copy[name] += value + + if self.multiworld.randomize_weapons_level[self.player] in [1, 3]: + for name in dlc_weapons_upgrade_5_table.keys(): + if self.multiworld.per_slot_randoms[self.player].randint(1, 100) < weapons_percentage: + value = self.multiworld.per_slot_randoms[self.player].randint(min_5, max_5) + item_dictionary_copy[name] += value + + if self.multiworld.randomize_weapons_level[self.player] in [1, 2]: + for name in dlc_weapons_upgrade_10_table.keys(): + if self.multiworld.per_slot_randoms[self.player].randint(1, 100) < weapons_percentage: + value = self.multiworld.per_slot_randoms[self.player].randint(min_10, max_10) + item_dictionary_copy[name] += value # Create the mandatory lists to generate the player's output file items_id = [] @@ -281,6 +344,7 @@ class DarkSouls3World(World): "death_link": self.multiworld.death_link[self.player].value, "no_spell_requirements": self.multiworld.no_spell_requirements[self.player].value, "no_equip_load": self.multiworld.no_equip_load[self.player].value, + "enable_dlc": self.multiworld.enable_dlc[self.player].value }, "seed": self.multiworld.seed_name, # to verify the server's multiworld "slot": self.multiworld.player_name[self.player], # to connect to server diff --git a/worlds/dark_souls_3/data/items_data.py b/worlds/dark_souls_3/data/items_data.py index c3f2c682..0ecd0434 100644 --- a/worlds/dark_souls_3/data/items_data.py +++ b/worlds/dark_souls_3/data/items_data.py @@ -13,7 +13,6 @@ weapons_upgrade_5_table = { "Izalith Staff": 0x00C96A80, "Fume Ultra Greatsword": 0x0060E4B0, "Black Knight Sword": 0x005F5E10, - "Yorshka's Spear": 0x008C3A70, "Smough's Great Hammer": 0x007E30B0, "Dragonslayer Greatbow": 0x00CF8500, @@ -25,7 +24,6 @@ weapons_upgrade_5_table = { "Dragonslayer Spear": 0x008CAFA0, "Caitha's Chime": 0x00CA06C0, "Sunlight Straight Sword": 0x00203230, - "Firelink Greatsword": 0x0060BDA0, "Hollowslayer Greatsword": 0x00604870, "Arstor's Spear": 0x008BEC50, @@ -37,7 +35,6 @@ weapons_upgrade_5_table = { "Wolnir's Holy Sword": 0x005FFA50, "Demon's Greataxe": 0x006CA480, "Demon's Fist": 0x00A84DF0, - "Old King's Great Hammer": 0x007CF830, "Greatsword of Judgment": 0x005E2590, "Profaned Greatsword": 0x005E4CA0, @@ -55,6 +52,29 @@ weapons_upgrade_5_table = { "Irithyll Rapier": 0x002E8A10 } +dlc_weapons_upgrade_5_table = { + "Friede's Great Scythe": 0x009B55A0, + "Rose of Ariandel": 0x00B82C70, + "Demon's Scar": 0x003F04D0, # Assigned to "RC: Church Guardian Shiv" + "Frayed Blade": 0x004D35A0, # Assigned to "RC: Ritual Spear Fragment" + "Gael's Greatsword": 0x00227C20, # Assigned to "RC: Violet Wrappings" + "Repeating Crossbow": 0x00D885B0, # Assigned to "RC: Blood of the Dark Souls" + "Onyx Blade": 0x00222E00, # VILHELM FIGHT + "Earth Seeker": 0x006D8EE0, + "Quakestone Hammer": 0x007ECCF0, + "Millwood Greatbow": 0x00D85EA0, + "Valorheart": 0x00F646E0, + "Aquamarine Dagger": 0x00116520, + "Ringed Knight Straight Sword": 0x00225510, + "Ledo's Great Hammer": 0x007EF400, # INVADER FIGHT + "Ringed Knight Spear": 0x008CFDC0, + "Crucifix of the Mad King": 0x008D4BE0, + "Sacred Chime of Filianore": 0x00CCECF0, + "Preacher's Right Arm": 0x00CD1400, + "White Birch Bow": 0x00D77440, + "Ringed Knight Paired Greatswords": 0x00F69500 +} + weapons_upgrade_10_table = { "Broken Straight Sword": 0x001EF9B0, "Deep Battle Axe": 0x0006AFA54, @@ -73,7 +93,6 @@ weapons_upgrade_10_table = { "Red Hilted Halberd": 0x009AB960, "Saint's Talisman": 0x00CACA10, "Large Club": 0x007AFC60, - "Brigand Twindaggers": 0x00F50E60, "Butcher Knife": 0x006BE130, "Brigand Axe": 0x006B1DE0, @@ -104,9 +123,24 @@ weapons_upgrade_10_table = { "Drakeblood Greatsword": 0x00609690, "Greatlance": 0x008A8CC0, "Sniper Crossbow": 0x00D83790, - "Claw": 0x00A7D8C0, "Drang Twinspears": 0x00F5AAA0, + "Pyromancy Flame": 0x00CC77C0 #given/dropped by Cornyx +} + +dlc_weapons_upgrade_10_table = { + "Follower Sabre": 0x003EDDC0, + "Millwood Battle Axe": 0x006D67D0, + "Follower Javelin": 0x008CD6B0, + "Crow Talons": 0x00A89C10, + "Pyromancer's Parting Flame": 0x00CC9ED0, + "Crow Quills": 0x00F66DF0, + "Follower Torch": 0x015F1AD0, + "Murky Hand Scythe": 0x00118C30, + "Herald Curved Greatsword": 0x006159E0, + "Lothric War Banner": 0x008D24D0, + "Splitleaf Greatsword": 0x009B2E90, # SHOP ITEM + "Murky Longstaff": 0x00CCC5E0, } shields_table = { @@ -132,7 +166,15 @@ shields_table = { "Golden Wing Crest Shield": 0x0143CAA0, "Ancient Dragon Greatshield": 0x013599D0, "Spirit Tree Crest Shield": 0x014466E0, - "Blessed Red and White Shield": 0x01343FB9, + "Blessed Red and White Shield": 0x01343FB9 +} + +dlc_shields_table = { + "Followers Shield": 0x0135C0E0, + "Ethereal Oak Shield": 0x01450320, + "Giant Door Shield": 0x00F5F8C0, + "Dragonhead Shield": 0x0135E7F0, + "Dragonhead Greatshield": 0x01452A30 } goods_table = { @@ -167,7 +209,55 @@ goods_table = { **{"Soul of a Great Champion #"+str(i): 0x400001A4 for i in range(1, 3)}, **{"Soul of a Champion #"+str(i): 0x400001A3 for i in range(1, 5)}, **{"Soul of a Venerable Old Hand #"+str(i): 0x400001A2 for i in range(1, 5)}, - **{"Soul of a Crestfallen Knight #"+str(i): 0x40000199 for i in range(1, 11)}, + **{"Soul of a Crestfallen Knight #"+str(i): 0x40000199 for i in range(1, 11)} +} + +goods_2_table = { # Added by Br00ty + "HWL: Gold Pine Resin #": 0x4000014B, + "US: Charcoal Pine Resin #": 0x4000014A, + "FK: Gold Pine Bundle #": 0x40000155, + "CC: Carthus Rouge #": 0x4000014F, + "ID: Pale Pine Resin #": 0x40000150, + **{"Ember #"+str(i): 0x400001F4 for i in range(1, 45)}, + **{"Titanite Shard #"+str(i): 0x400003E8 for i in range(11, 16)}, + **{"Large Titanite Shard #"+str(i): 0x400003E9 for i in range(11, 16)}, + **{"Titanite Scale #" + str(i): 0x400003FC for i in range(1, 25)} +} + +goods_3_table = { # Added by Br00ty + **{"Fading Soul #" + str(i): 0x40000190 for i in range(1, 4)}, + **{"Ring of Sacrifice #"+str(i): 0x20004EF2 for i in range(1, 5)}, + **{"Homeward Bone #"+str(i): 0x4000015E for i in range(1, 17)}, + **{"Green Blossom #"+str(i): 0x40000104 for i in range(1, 7)}, + **{"Human Pine Resin #"+str(i): 0x4000014E for i in range(1, 3)}, + **{"Charcoal Pine Bundle #"+str(i): 0x40000154 for i in range(1, 3)}, + **{"Rotten Pine Resin #"+str(i): 0x40000157 for i in range(1, 3)}, + **{"Alluring Skull #"+str(i): 0x40000126 for i in range(1, 9)}, + **{"Rusted Coin #"+str(i): 0x400001C7 for i in range(1, 3)}, + **{"Rusted Gold Coin #"+str(i): 0x400001C9 for i in range(1, 3)}, + **{"Titanite Chunk #"+str(i): 0x400003EA for i in range(1, 17)}, + **{"Twinkling Titanite #"+str(i): 0x40000406 for i in range(1, 8)} +} + +dlc_goods_table = { + "Soul of Sister Friede": 0x400002E8, + "Soul of the Demon Prince": 0x400002EA, + "Soul of Darkeater Midir": 0x400002EB, + "Soul of Slave Knight Gael": 0x400002E9 +} + +dlc_goods_2_table = { #71 + **{"Large Soul of an Unknown Traveler $"+str(i): 0x40000194 for i in range(1, 10)}, + **{"Soul of a Weary Warrior $"+str(i): 0x40000197 for i in range(1, 6)}, + **{"Large Soul of a Weary Warrior $"+str(i): 0x40000198 for i in range(1, 7)}, + **{"Soul of a Crestfallen Knight $"+str(i): 0x40000199 for i in range(1, 7)}, + **{"Large Soul of a Crestfallen Knight $"+str(i): 0x4000019A for i in range(1, 4)}, + **{"Homeward Bone $"+str(i): 0x4000015E for i in range(1, 7)}, + **{"Large Titanite Shard $"+str(i): 0x400003E9 for i in range(1, 4)}, + **{"Titanite Chunk $"+str(i): 0x400003EA for i in range(1, 16)}, + **{"Twinkling Titanite $"+str(i): 0x40000406 for i in range(1, 6)}, + **{"Rusted Coin $"+str(i): 0x400001C7 for i in range(1, 4)}, + **{"Ember $"+str(i): 0x400001F4 for i in range(1, 11)} } armor_table = { @@ -265,6 +355,69 @@ armor_table = { "Outrider Knight Armor": 0x1328BB28, "Outrider Knight Gauntlets": 0x1328BF10, "Outrider Knight Leggings": 0x1328C2F8, + + "Cornyx's Wrap": 0x11946370, + "Cornyx's Garb": 0x11945F88, + "Cornyx's Skirt": 0x11946758 +} + +dlc_armor_table = { + "Slave Knight Hood": 0x134EDCE0, + "Slave Knight Armor": 0x134EE0C8, + "Slave Knight Gauntlets": 0x134EE4B0, + "Slave Knight Leggings": 0x134EE898, + "Vilhelm's Helm": 0x11312D00, + "Vilhelm's Armor": 0x113130E8, + "Vilhelm's Gauntlets": 0x113134D0, + "Vilhelm's Leggings": 0x113138B8, + #"Millwood Knight Helm": 0x139B2820, # SHOP ITEM + #"Millwood Knight Armor": 0x139B2C08, # SHOP ITEM + #"Millwood Knight Gauntlets": 0x139B2FF0, # SHOP ITEM + #"Millwood Knight Leggings": 0x139B33D8, # SHOP ITEM + + "Shira's Crown": 0x11C22260, + "Shira's Armor": 0x11C22648, + "Shira's Gloves": 0x11C22A30, + "Shira's Trousers": 0x11C22E18, + #"Lapp's Helm": 0x11E84800, # SHOP ITEM + #"Lapp's Armor": 0x11E84BE8, # SHOP ITEM + #"Lapp's Gauntlets": 0x11E84FD0, # SHOP ITEM + #"Lapp's Leggings": 0x11E853B8, # SHOP ITEM + #"Ringed Knight Hood": 0x13C8EEE0, # RANDOM ENEMY DROP + #"Ringed Knight Armor": 0x13C8F2C8, # RANDOM ENEMY DROP + #"Ringed Knight Gauntlets": 0x13C8F6B0, # RANDOM ENEMY DROP + #"Ringed Knight Leggings": 0x13C8FA98, # RANDOM ENEMY DROP + #"Harald Legion Armor": 0x13D83508, # RANDOM ENEMY DROP + #"Harald Legion Gauntlets": 0x13D838F0, # RANDOM ENEMY DROP + #"Harald Legion Leggings": 0x13D83CD8, # RANDOM ENEMY DROP + "Iron Dragonslayer Helm": 0x1405F7E0, + "Iron Dragonslayer Armor": 0x1405FBC8, + "Iron Dragonslayer Gauntlets": 0x1405FFB0, + "Iron Dragonslayer Leggings": 0x14060398, + + "Ruin Sentinel Helm": 0x14CC5520, + "Ruin Sentinel Armor": 0x14CC5908, + "Ruin Sentinel Gauntlets": 0x14CC5CF0, + "Ruin Sentinel Leggings": 0x14CC60D8, + "Desert Pyromancer Hood": 0x14DB9760, + "Desert Pyromancer Garb": 0x14DB9B48, + "Desert Pyromancer Gloves": 0x14DB9F30, + "Desert Pyromancer Skirt": 0x14DBA318, + + #"Follower Helm": 0x137CA3A0, # RANDOM ENEMY DROP + #"Follower Armor": 0x137CA788, # RANDOM ENEMY DROP + #"Follower Gloves": 0x137CAB70, # RANDOM ENEMY DROP + #"Follower Boots": 0x137CAF58, # RANDOM ENEMY DROP + #"Ordained Hood": 0x135E1F20, # SHOP ITEM + #"Ordained Dress": 0x135E2308, # SHOP ITEM + #"Ordained Trousers": 0x135E2AD8, # SHOP ITEM + "Black Witch Veil": 0x14FA1BE0, + "Black Witch Hat": 0x14EAD9A0, + "Black Witch Garb": 0x14EADD88, + "Black Witch Wrappings": 0x14EAE170, + "Black Witch Trousers": 0x14EAE558, + "White Preacher Head": 0x14153A20, + "Antiquated Plain Garb": 0x11B2E408 } rings_table = { @@ -314,6 +467,12 @@ rings_table = { "Dragonscale Ring": 0x2000515E, "Knight Slayer's Ring": 0x20005000, "Magic Stoneplate Ring": 0x20004E66, + "Blue Tearstone Ring": 0x20004ED4 #given/dropped by Greirat +} + +dlc_ring_table = { + "Havel's Ring": 0x20004E34, + "Chillbite Ring": 0x20005208 } spells_table = { @@ -335,7 +494,21 @@ spells_table = { "Divine Pillars of Light": 0x4038C340, "Great Magic Barrier": 0x40365628, "Great Magic Shield": 0x40144F38, - "Crystal Scroll": 0x40000856, + "Crystal Scroll": 0x40000856 +} + +dlc_spells_table = { + #"Boulder Heave": 0x40282170, # KILN STRAY DEMON + #"Seething Chaos": 0x402896A0, # KILN DEMON PRINCES + #"Old Moonlight": 0x4014FF00, # KILN MIDIR + "Frozen Weapon": 0x401408E8, + "Snap Freeze": 0x401A90C8, + "Great Soul Dregs": 0x401879A0, + "Flame Fan": 0x40258190, + "Lightning Arrow": 0x40358B08, + "Way of White Corona": 0x403642A0, + "Projected Heal": 0x40364688, + "Floating Chaos": 0x40257DA8 } misc_items_table = { @@ -347,7 +520,7 @@ misc_items_table = { "Braille Divine Tome of Carim": 0x40000847, # Shop "Great Swamp Pyromancy Tome": 0x4000084F, # Shop "Farron Coal ": 0x40000837, # Shop - "Paladin's Ashes": 0x4000083D, #Shop + "Paladin's Ashes": 0x4000083D, # Shop "Deep Braille Divine Tome": 0x40000860, # Shop "Small Doll": 0x400007D5, "Golden Scroll": 0x4000085C, @@ -388,6 +561,12 @@ misc_items_table = { "Orbeck's Ashes": 0x40000840 } +dlc_misc_table = { + "Captains Ashes": 0x4000086A, + "Contraption Key": 0x4000086B, # Needed for Painted World + "Small Envoy Banner": 0x4000086C # Needed to get to Ringed City from Dreg Heap +} + key_items_list = { "Small Lothric Banner", "Basin of Vows", @@ -405,8 +584,17 @@ key_items_list = { "Prisoner Chief's Ashes", "Old Cell Key", "Jailer's Key Ring", + "Contraption Key", + "Small Envoy Banner" } -item_tables = [weapons_upgrade_5_table, weapons_upgrade_10_table, shields_table, armor_table, rings_table, spells_table, misc_items_table, goods_table] +item_tables = [weapons_upgrade_5_table, weapons_upgrade_10_table, shields_table, + armor_table, rings_table, spells_table, misc_items_table, goods_table, goods_2_table, goods_3_table, + dlc_weapons_upgrade_5_table, dlc_weapons_upgrade_10_table, dlc_shields_table, dlc_goods_table, + dlc_armor_table, dlc_spells_table, dlc_ring_table, dlc_misc_table, dlc_goods_2_table] + +item_dictionary = {**weapons_upgrade_5_table, **weapons_upgrade_10_table, **shields_table, + **armor_table, **rings_table, **spells_table, **misc_items_table, **goods_table, **goods_2_table, + **goods_3_table, **dlc_weapons_upgrade_5_table, **dlc_weapons_upgrade_10_table, **dlc_shields_table, + **dlc_goods_table, **dlc_armor_table, **dlc_spells_table, **dlc_ring_table, **dlc_misc_table, **dlc_goods_2_table} -item_dictionary = {**weapons_upgrade_5_table, **weapons_upgrade_10_table, **shields_table, **armor_table, **rings_table, **spells_table, **misc_items_table, **goods_table} diff --git a/worlds/dark_souls_3/data/locations_data.py b/worlds/dark_souls_3/data/locations_data.py index 654c7f09..5a859169 100644 --- a/worlds/dark_souls_3/data/locations_data.py +++ b/worlds/dark_souls_3/data/locations_data.py @@ -42,6 +42,7 @@ high_wall_of_lothric = { "HWL: Soul of the Dancer": 0x400002CA, "HWL: Way of Blue Covenant": 0x2000274C, "HWL: Greirat's Ashes": 0x4000083F, + "HWL: Blue Tearstone Ring": 0x20004ED4 #given/dropped by Greirat } undead_settlement_table = { @@ -91,7 +92,11 @@ undead_settlement_table = { "US: Warrior of Sunlight Covenant": 0x20002738, "US: Blessed Red and White Shield": 0x01343FB9, "US: Irina's Ashes": 0x40000843, - "US: Cornyx's Ashes": 0x40000841 + "US: Cornyx's Ashes": 0x40000841, + "US: Cornyx's Wrap": 0x11946370, + "US: Cornyx's Garb": 0x11945F88, + "US: Cornyx's Skirt": 0x11946758, + "US: Pyromancy Flame": 0x00CC77C0 #given/dropped by Cornyx } road_of_sacrifice_table = { @@ -437,6 +442,101 @@ archdragon_peak_table = { "AP: Havel's Greatshield": 0x013376F0, } +painted_world_table = { # DLC + "PW: Follower Javelin": 0x008CD6B0, + "PW: Frozen Weapon": 0x401408E8, + "PW: Millwood Greatbow": 0x00D85EA0, + "PW: Captains Ashes": 0x4000086A, + "PW: Millwood Battle Axe": 0x006D67D0, + "PW: Ethereal Oak Shield": 0x01450320, + "PW: Crow Quills": 0x00F66DF0, + "PW: Slave Knight Hood": 0x134EDCE0, + "PW: Slave Knight Armor": 0x134EE0C8, + "PW: Slave Knight Gauntlets": 0x134EE4B0, + "PW: Slave Knight Leggings": 0x134EE898, + "PW: Way of White Corona": 0x403642A0, + "PW: Crow Talons": 0x00A89C10, + "PW: Quakestone Hammer": 0x007ECCF0, + "PW: Earth Seeker": 0x006D8EE0, + "PW: Follower Torch": 0x015F1AD0, + "PW: Follower Shield": 0x0135C0E0, + "PW: Follower Sabre": 0x003EDDC0, + "PW: Snap Freeze": 0x401A90C8, + "PW: Floating Chaos": 0x40257DA8, + "PW: Pyromancer's Parting Flame": 0x00CC9ED0, + "PW: Vilhelm's Helm": 0x11312D00, + "PW: Vilhelm's Armor": 0x113130E8, + "PW: Vilhelm's Gauntlets": 0x113134D0, + "PW: Vilhelm's Leggings": 0x113138B8, + "PW: Vilhelm's Leggings": 0x113138B8, + "PW: Valorheart": 0x00F646E0, # GRAVETENDER FIGHT + "PW: Champions Bones": 0x40000869, # GRAVETENDER FIGHT + "PW: Onyx Blade": 0x00222E00, # VILHELM FIGHT + "PW: Soul of Sister Friede": 0x400002E8, + "PW: Titanite Slab": 0x400003EB, + "PW: Chillbite Ring": 0x20005208, + "PW: Contraption Key": 0x4000086B # VILHELM FIGHT/NEEDED TO PROGRESS THROUGH PW +} + +dreg_heap_table = { # DLC + "DH: Loincloth": 0x11B2EBD8, + "DH: Aquamarine Dagger": 0x00116520, + "DH: Murky Hand Scythe": 0x00118C30, + "DH: Murky Longstaff": 0x00CCC5E0, + "DH: Great Soul Dregs": 0x401879A0, + "DH: Lothric War Banner": 0x00CCC5E0, + "DH: Projected Heal": 0x40364688, + "DH: Desert Pyromancer Hood": 0x14DB9760, + "DH: Desert Pyromancer Garb": 0x14DB9B48, + "DH: Desert Pyromancer Gloves": 0x14DB9F30, + "DH: Desert Pyromancer Skirt": 0x14DBA318, + "DH: Giant Door Shield": 0x00F5F8C0, + "DH: Herald Curved Greatsword": 0x006159E0, + "DH: Flame Fan": 0x40258190, + "DH: Soul of the Demon Prince": 0x400002EA, + "DH: Small Envoy Banner": 0x4000086C # NEEDED TO TRAVEL TO RINGED CITY +} + +ringed_city_table = { # DLC + "RC: Ruin Sentinel Helm": 0x14CC5520, + "RC: Ruin Sentinel Armor": 0x14CC5908, + "RC: Ruin Sentinel Gauntlets": 0x14CC5CF0, + "RC: Ruin Sentinel Leggings": 0x14CC60D8, + "RC: Black Witch Veil": 0x14FA1BE0, + "RC: Black Witch Hat": 0x14EAD9A0, + "RC: Black Witch Garb": 0x14EADD88, + "RC: Black Witch Wrappings": 0x14EAE170, + "RC: Black Witch Trousers": 0x14EAE558, + "RC: White Preacher Head": 0x14153A20, + "RC: Havel's Ring": 0x20004E34, + "RC: Ringed Knight Spear": 0x008CFDC0, + "RC: Dragonhead Shield": 0x0135E7F0, + "RC: Ringed Knight Straight Sword": 0x00225510, + "RC: Preacher's Right Arm": 0x00CD1400, + "RC: White Birch Bow": 0x00D77440, + "RC: Church Guardian Shiv": 0x4000013B, # Assigned to "Demon's Scar" + "RC: Dragonhead Greatshield": 0x01452A30, + "RC: Ringed Knight Paired Greatswords": 0x00F69500, + "RC: Shira's Crown": 0x11C22260, + "RC: Shira's Armor": 0x11C22648, + "RC: Shira's Gloves": 0x11C22A30, + "RC: Shira's Trousers": 0x11C22E18, + "RC: Titanite Slab": 0x400003EB, # SHIRA DROP + "RC: Crucifix of the Mad King": 0x008D4BE0, # SHIRA DROP + "RC: Sacred Chime of Filianore": 0x00CCECF0, # SHIRA DROP + "RC: Iron Dragonslayer Helm": 0x1405F7E0, + "RC: Iron Dragonslayer Armor": 0x1405FBC8, + "RC: Iron Dragonslayer Gauntlets": 0x1405FFB0, + "RC: Iron Dragonslayer Leggings": 0x14060398, + "RC: Lightning Arrow": 0x40358B08, + "RC: Ritual Spear Fragment": 0x4000028A, # Assigned to "Frayed Blade" + "RC: Antiquated Plain Garb": 0x11B2E408, + "RC: Violet Wrappings": 0x11B2E7F0, # Assigned to "Gael's Greatsword" + "RC: Soul of Darkeater Midir": 0x400002EB, + "RC: Soul of Slave Knight Gael": 0x400002E9, + "RC: Blood of the Dark Souls": 0x4000086E, # Assigned to "Repeating Crossbow" +} + progressive_locations = { # Upgrade materials **{"Titanite Shard #"+str(i): 0x400003E8 for i in range(1, 11)}, @@ -456,15 +556,60 @@ progressive_locations = { **{"Soul of a Deserted Corpse #" + str(i): 0x40000191 for i in range(1, 6)}, **{"Large Soul of a Deserted Corpse #" + str(i): 0x40000192 for i in range(1, 6)}, **{"Soul of an Unknown Traveler #" + str(i): 0x40000193 for i in range(1, 6)}, - **{"Large Soul of an Unknown Traveler #" + str(i): 0x40000194 for i in range(1, 6)}, + **{"Large Soul of an Unknown Traveler #" + str(i): 0x40000194 for i in range(1, 6)} +} + +progressive_locations_2 = { + ##Added by Br00ty + "HWL: Gold Pine Resin #": 0x4000014B, + "US: Charcoal Pine Resin #": 0x4000014A, + "FK: Gold Pine Bundle #": 0x40000155, + "CC: Carthus Rouge #": 0x4000014F, + "ID: Pale Pine Resin #": 0x40000150, + **{"Titanite Scale #" + str(i): 0x400003FC for i in range(1, 27)}, + **{"Fading Soul #" + str(i): 0x40000190 for i in range(1, 4)}, + **{"Ring of Sacrifice #"+str(i): 0x20004EF2 for i in range(1, 5)}, + **{"Homeward Bone #"+str(i): 0x4000015E for i in range(1, 17)}, + **{"Ember #"+str(i): 0x400001F4 for i in range(1, 46)}, +} + +progressive_locations_3 = { + **{"Green Blossom #" + str(i): 0x40000104 for i in range(1, 7)}, + **{"Human Pine Resin #" + str(i): 0x4000014E for i in range(1, 3)}, + **{"Charcoal Pine Bundle #" + str(i): 0x40000154 for i in range(1, 3)}, + **{"Rotten Pine Resin #" + str(i): 0x40000157 for i in range(1, 3)}, + **{"Pale Tongue #" + str(i): 0x40000175 for i in range(1, 3)}, + **{"Alluring Skull #" + str(i): 0x40000126 for i in range(1, 3)}, + **{"Undead Hunter Charm #" + str(i): 0x40000128 for i in range(1, 3)}, + **{"Duel Charm #" + str(i): 0x40000130 for i in range(1, 3)}, + **{"Rusted Coin #" + str(i): 0x400001C7 for i in range(1, 3)}, + **{"Rusted Gold Coin #" + str(i): 0x400001C9 for i in range(1, 4)}, + **{"Titanite Chunk #"+str(i): 0x400003EA for i in range(1, 17)}, + **{"Twinkling Titanite #"+str(i): 0x40000406 for i in range(1, 8)} +} + +dlc_progressive_locations = { #71 + **{"Large Soul of an Unknown Traveler $"+str(i): 0x40000194 for i in range(1, 10)}, + **{"Soul of a Weary Warrior $"+str(i): 0x40000197 for i in range(1, 6)}, + **{"Large Soul of a Weary Warrior $"+str(i): 0x40000198 for i in range(1, 7)}, + **{"Soul of a Crestfallen Knight $"+str(i): 0x40000199 for i in range(1, 7)}, + **{"Large Soul of a Crestfallen Knight $"+str(i): 0x4000019A for i in range(1, 4)}, + **{"Homeward Bone $"+str(i): 0x4000015E for i in range(1, 7)}, + **{"Large Titanite Shard $"+str(i): 0x400003E9 for i in range(1, 4)}, + **{"Titanite Chunk $"+str(i): 0x400003EA for i in range(1, 16)}, + **{"Twinkling Titanite $"+str(i): 0x40000406 for i in range(1, 6)}, + **{"Rusted Coin $"+str(i): 0x400001C7 for i in range(1, 4)}, + **{"Ember $"+str(i): 0x400001F4 for i in range(1, 11)} } location_tables = [fire_link_shrine_table, firelink_shrine_bell_tower_table, high_wall_of_lothric, undead_settlement_table, road_of_sacrifice_table, cathedral_of_the_deep_table, farron_keep_table, catacombs_of_carthus_table, smouldering_lake_table, irithyll_of_the_boreal_valley_table, irithyll_dungeon_table, profaned_capital_table, anor_londo_table, lothric_castle_table, consumed_king_garden_table, - grand_archives_table, untended_graves_table, archdragon_peak_table, progressive_locations] + grand_archives_table, untended_graves_table, archdragon_peak_table, progressive_locations, progressive_locations_2, progressive_locations_3, + painted_world_table, dreg_heap_table, ringed_city_table, dlc_progressive_locations] location_dictionary = {**fire_link_shrine_table, **firelink_shrine_bell_tower_table, **high_wall_of_lothric, **undead_settlement_table, **road_of_sacrifice_table, **cathedral_of_the_deep_table, **farron_keep_table, **catacombs_of_carthus_table, **smouldering_lake_table, **irithyll_of_the_boreal_valley_table, **irithyll_dungeon_table, **profaned_capital_table, **anor_londo_table, **lothric_castle_table, **consumed_king_garden_table, - **grand_archives_table, **untended_graves_table, **archdragon_peak_table, **progressive_locations} + **grand_archives_table, **untended_graves_table, **archdragon_peak_table, **progressive_locations, **progressive_locations_2, **progressive_locations_3, + **painted_world_table, **dreg_heap_table, **ringed_city_table, **dlc_progressive_locations} diff --git a/worlds/dark_souls_3/docs/en_Dark Souls III.md b/worlds/dark_souls_3/docs/en_Dark Souls III.md index 2effa5f1..3ad8236c 100644 --- a/worlds/dark_souls_3/docs/en_Dark Souls III.md +++ b/worlds/dark_souls_3/docs/en_Dark Souls III.md @@ -7,19 +7,20 @@ config file. ## What does randomization do to this game? -In Dark Souls III, all unique items you can earn from a static corpse, a chest or the death of a Boss/NPC are randomized. -This exclude the upgrade materials such as the titanite shards, the estus shards and the consumables which remain at -the same location. I also added an option available from the settings page to randomize the level of the generated -weapons(from +0 to +10/+5) +In Dark Souls III, all unique items you can earn from a static corpse, a chest or the death of a Boss/NPC are +randomized. +An option is available from the settings page to also randomize the upgrade materials, the Estus shards and the +consumables. +Another option is available to randomize the level of the generated weapons(from +0 to +10/+5) -To beat the game you need to collect the 4 "Cinders of a Lord" randomized in the multiworld +To beat the game you need to collect the 4 "Cinders of a Lord" randomized in the multiworld and kill the final boss "Soul of Cinder" ## What Dark Souls III items can appear in other players' worlds? -Every unique items from Dark Souls III can appear in other player's worlds, such as a piece of armor, an upgraded weapon +Every unique item from Dark Souls III can appear in other player's worlds, such as a piece of armor, an upgraded weapon, or a key item. ## What does another world's item look like in Dark Souls III? -In Dark Souls III, items which need to be sent to other worlds appear as a Prism Stone. \ No newline at end of file +In Dark Souls III, items which need to be sent to other worlds appear as a Prism Stone. diff --git a/worlds/dark_souls_3/docs/setup_en.md b/worlds/dark_souls_3/docs/setup_en.md index b4705ad5..8e1af8e9 100644 --- a/worlds/dark_souls_3/docs/setup_en.md +++ b/worlds/dark_souls_3/docs/setup_en.md @@ -5,6 +5,10 @@ - [Dark Souls III](https://store.steampowered.com/app/374320/DARK_SOULS_III/) - [Dark Souls III AP Client](https://github.com/Marechal-L/Dark-Souls-III-Archipelago-client/releases) +## Optional Software + +- [Dark Souls III Maptracker Pack](https://github.com/Br00ty/DS3_AP_Maptracker/releases/latest), for use with [Poptracker](https://github.com/black-sliver/PopTracker/releases) + ## General Concept The Dark Souls III AP Client is a dinput8.dll triggered when launching Dark Souls III. This .dll file will launch a command