Blasphemous: Randomizer 2.0 (#1883)

Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
This commit is contained in:
Trevor L
2023-07-05 22:39:26 -06:00
committed by GitHub
parent f2117be7d9
commit 1338d7a968
10 changed files with 10277 additions and 2650 deletions

View File

@@ -1,14 +1,14 @@
from typing import Dict, Set, List, Any
from typing import Dict, List, Set, Any
from collections import Counter
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification
from ..AutoWorld import World, WebWorld
from .Items import base_id, item_table, group_table, tears_set, reliquary_set, skill_set
from .Locations import location_table, shop_set
from .Exits import region_exit_table, exit_lookup_table
from worlds.AutoWorld import World, WebWorld
from .Items import base_id, item_table, group_table, tears_set, reliquary_set, event_table
from .Locations import location_table
from .Rooms import room_table, door_table
from .Rules import rules
from worlds.generic.Rules import set_rule
from worlds.generic.Rules import set_rule, add_rule
from .Options import blasphemous_options
from . import Vanilla
from .Vanilla import unrandomized_dict, junk_locations, thorn_set, skill_dict
class BlasphemousWeb(WebWorld):
@@ -32,7 +32,7 @@ class BlasphemousWorld(World):
game: str = "Blasphemous"
web = BlasphemousWeb()
data_version = 1
data_version = 2
item_name_to_id = {item["name"]: (base_id + index) for index, item in enumerate(item_table)}
location_name_to_id = {loc["name"]: (base_id + index) for index, loc in enumerate(location_table)}
@@ -41,6 +41,14 @@ class BlasphemousWorld(World):
item_name_groups = group_table
option_definitions = blasphemous_options
required_client_version = (0, 4, 2)
def __init__(self, multiworld, player):
super(BlasphemousWorld, self).__init__(multiworld, player)
self.start_room: str = "D17Z01S01"
self.door_connections: Dict[str, str] = {}
def set_rules(self):
rules(self)
@@ -61,102 +69,134 @@ class BlasphemousWorld(World):
return self.multiworld.random.choice(tears_set)
def create_items(self):
placed_items = []
def generate_early(self):
world = self.multiworld
player = self.player
placed_items.extend(Vanilla.unrandomized_dict.values())
if not world.starting_location[player].randomized:
if world.starting_location[player].value == 6 and world.difficulty[player].value < 2:
raise Exception(f"[Blasphemous - '{world.get_player_name(player)}'] {world.starting_location[player]}"
" cannot be chosen if Difficulty is lower than Hard.")
if not self.multiworld.reliquary_shuffle[self.player]:
placed_items.extend(reliquary_set)
elif self.multiworld.reliquary_shuffle[self.player]:
placed_items.append("Tears of Atonement (250)")
placed_items.append("Tears of Atonement (300)")
placed_items.append("Tears of Atonement (500)")
if (world.starting_location[player].value == 0 or world.starting_location[player].value == 6) \
and world.dash_shuffle[player]:
raise Exception(f"[Blasphemous - '{world.get_player_name(player)}'] {world.starting_location[player]}"
" cannot be chosen if Shuffle Dash is enabled.")
if world.starting_location[player].value == 3 and world.wall_climb_shuffle[player]:
raise Exception(f"[Blasphemous - '{world.get_player_name(player)}'] {world.starting_location[player]}"
" cannot be chosen if Shuffle Wall Climb is enabled.")
else:
locations: List[int] = [ 0, 1, 2, 3, 4, 5, 6 ]
invalid: bool = False
if not self.multiworld.cherub_shuffle[self.player]:
for i in range(38):
placed_items.append("Child of Moonlight")
if world.difficulty[player].value < 2:
locations.remove(6)
if not self.multiworld.life_shuffle[self.player]:
for i in range(6):
placed_items.append("Life Upgrade")
if world.dash_shuffle[player]:
locations.remove(0)
if 6 in locations:
locations.remove(6)
if not self.multiworld.fervour_shuffle[self.player]:
for i in range(6):
placed_items.append("Fervour Upgrade")
if world.wall_climb_shuffle[player]:
locations.remove(3)
if not self.multiworld.sword_shuffle[self.player]:
for i in range(7):
placed_items.append("Mea Culpa Upgrade")
if world.starting_location[player].value == 6 and world.difficulty[player].value < 2:
invalid = True
if not self.multiworld.blessing_shuffle[self.player]:
placed_items.extend(Vanilla.blessing_dict.values())
if (world.starting_location[player].value == 0 or world.starting_location[player].value == 6) \
and world.dash_shuffle[player]:
invalid = True
if world.starting_location[player].value == 3 and world.wall_climb_shuffle[player]:
invalid = True
if not self.multiworld.dungeon_shuffle[self.player]:
placed_items.extend(Vanilla.dungeon_dict.values())
if not self.multiworld.tirso_shuffle[self.player]:
placed_items.extend(Vanilla.tirso_dict.values())
if not self.multiworld.miriam_shuffle[self.player]:
placed_items.append("Cantina of the Blue Rose")
if not self.multiworld.redento_shuffle[self.player]:
placed_items.extend(Vanilla.redento_dict.values())
if not self.multiworld.jocinero_shuffle[self.player]:
placed_items.extend(Vanilla.jocinero_dict.values())
if invalid:
world.starting_location[player].value = world.random.choice(locations)
if not self.multiworld.altasgracias_shuffle[self.player]:
placed_items.extend(Vanilla.altasgracias_dict.values())
if not world.dash_shuffle[player]:
world.push_precollected(self.create_item("Dash Ability"))
if not self.multiworld.tentudia_shuffle[self.player]:
placed_items.extend(Vanilla.tentudia_dict.values())
if not world.wall_climb_shuffle[player]:
world.push_precollected(self.create_item("Wall Climb Ability"))
if not self.multiworld.gemino_shuffle[self.player]:
placed_items.extend(Vanilla.gemino_dict.values())
if world.skip_long_quests[player]:
for loc in junk_locations:
world.exclude_locations[player].value.add(loc)
if not self.multiworld.guilt_shuffle[self.player]:
placed_items.append("Weight of True Guilt")
start_rooms: Dict[int, str] = {
0: "D17Z01S01",
1: "D01Z02S01",
2: "D02Z03S09",
3: "D03Z03S11",
4: "D04Z03S01",
5: "D06Z01S09",
6: "D20Z02S09"
}
if not self.multiworld.ossuary_shuffle[self.player]:
placed_items.extend(Vanilla.ossuary_dict.values())
self.start_room = start_rooms[world.starting_location[player].value]
if not self.multiworld.boss_shuffle[self.player]:
placed_items.extend(Vanilla.boss_dict.values())
if not self.multiworld.wound_shuffle[self.player]:
placed_items.extend(Vanilla.wound_dict.values())
def create_items(self):
world = self.multiworld
player = self.player
if not self.multiworld.mask_shuffle[self.player]:
placed_items.extend(Vanilla.mask_dict.values())
removed: int = 0
to_remove: List[str] = [
"Tears of Atonement (250)",
"Tears of Atonement (300)",
"Tears of Atonement (500)",
"Tears of Atonement (500)",
"Tears of Atonement (500)"
]
if not self.multiworld.eye_shuffle[self.player]:
placed_items.extend(Vanilla.eye_dict.values())
skipped_items = []
junk: int = 0
if not self.multiworld.herb_shuffle[self.player]:
placed_items.extend(Vanilla.herb_dict.values())
for item, count in world.start_inventory[player].value.items():
for _ in range(count):
skipped_items.append(item)
junk += 1
if not self.multiworld.church_shuffle[self.player]:
placed_items.extend(Vanilla.church_dict.values())
skipped_items.extend(unrandomized_dict.values())
if not self.multiworld.shop_shuffle[self.player]:
placed_items.extend(Vanilla.shop_dict.values())
if self.multiworld.thorn_shuffle[self.player] == 2:
if world.thorn_shuffle[player] == 2:
for i in range(8):
placed_items.append("Thorn Upgrade")
skipped_items.append("Thorn Upgrade")
if not self.multiworld.candle_shuffle[self.player]:
placed_items.extend(Vanilla.candle_dict.values())
if world.dash_shuffle[player]:
skipped_items.append(to_remove[removed])
removed += 1
elif not world.dash_shuffle[player]:
skipped_items.append("Dash Ability")
if self.multiworld.start_wheel[self.player]:
placed_items.append("The Young Mason's Wheel")
if world.wall_climb_shuffle[player]:
skipped_items.append(to_remove[removed])
removed += 1
elif not world.wall_climb_shuffle[player]:
skipped_items.append("Wall Climb Ability")
if not self.multiworld.skill_randomizer[self.player]:
placed_items.extend(Vanilla.skill_dict.values())
if not world.reliquary_shuffle[player]:
skipped_items.extend(reliquary_set)
elif world.reliquary_shuffle[player]:
for i in range(3):
skipped_items.append(to_remove[removed])
removed += 1
counter = Counter(placed_items)
if not world.boots_of_pleading[player]:
skipped_items.append("Boots of Pleading")
if not world.purified_hand[player]:
skipped_items.append("Purified Hand of the Nun")
if world.start_wheel[player]:
skipped_items.append("The Young Mason's Wheel")
if not world.skill_randomizer[player]:
skipped_items.extend(skill_dict.values())
counter = Counter(skipped_items)
pool = []
@@ -169,95 +209,30 @@ class BlasphemousWorld(World):
for i in range(count):
pool.append(self.create_item(item["name"]))
self.multiworld.itempool += pool
for _ in range(junk):
pool.append(self.create_item(self.get_filler_item_name()))
world.itempool += pool
def pre_fill(self):
self.place_items_from_dict(Vanilla.unrandomized_dict)
world = self.multiworld
player = self.player
if not self.multiworld.cherub_shuffle[self.player]:
self.place_items_from_set(Vanilla.cherub_set, "Child of Moonlight")
self.place_items_from_dict(unrandomized_dict)
if not self.multiworld.life_shuffle[self.player]:
self.place_items_from_set(Vanilla.life_set, "Life Upgrade")
if world.thorn_shuffle[player] == 2:
self.place_items_from_set(thorn_set, "Thorn Upgrade")
if not self.multiworld.fervour_shuffle[self.player]:
self.place_items_from_set(Vanilla.fervour_set, "Fervour Upgrade")
if not self.multiworld.sword_shuffle[self.player]:
self.place_items_from_set(Vanilla.sword_set, "Mea Culpa Upgrade")
if not self.multiworld.blessing_shuffle[self.player]:
self.place_items_from_dict(Vanilla.blessing_dict)
if not self.multiworld.dungeon_shuffle[self.player]:
self.place_items_from_dict(Vanilla.dungeon_dict)
if not self.multiworld.tirso_shuffle[self.player]:
self.place_items_from_dict(Vanilla.tirso_dict)
if not self.multiworld.miriam_shuffle[self.player]:
self.multiworld.get_location("AtTotS: Miriam's gift", self.player)\
.place_locked_item(self.create_item("Cantina of the Blue Rose"))
if not self.multiworld.redento_shuffle[self.player]:
self.place_items_from_dict(Vanilla.redento_dict)
if not self.multiworld.jocinero_shuffle[self.player]:
self.place_items_from_dict(Vanilla.jocinero_dict)
if not self.multiworld.altasgracias_shuffle[self.player]:
self.place_items_from_dict(Vanilla.altasgracias_dict)
if not self.multiworld.tentudia_shuffle[self.player]:
self.place_items_from_dict(Vanilla.tentudia_dict)
if not self.multiworld.gemino_shuffle[self.player]:
self.place_items_from_dict(Vanilla.gemino_dict)
if not self.multiworld.guilt_shuffle[self.player]:
self.multiworld.get_location("GotP: Confessor Dungeon room", self.player)\
.place_locked_item(self.create_item("Weight of True Guilt"))
if not self.multiworld.ossuary_shuffle[self.player]:
self.place_items_from_dict(Vanilla.ossuary_dict)
if not self.multiworld.boss_shuffle[self.player]:
self.place_items_from_dict(Vanilla.boss_dict)
if not self.multiworld.wound_shuffle[self.player]:
self.place_items_from_dict(Vanilla.wound_dict)
if not self.multiworld.mask_shuffle[self.player]:
self.place_items_from_dict(Vanilla.mask_dict)
if not self.multiworld.eye_shuffle[self.player]:
self.place_items_from_dict(Vanilla.eye_dict)
if not self.multiworld.herb_shuffle[self.player]:
self.place_items_from_dict(Vanilla.herb_dict)
if not self.multiworld.church_shuffle[self.player]:
self.place_items_from_dict(Vanilla.church_dict)
if not self.multiworld.shop_shuffle[self.player]:
self.place_items_from_dict(Vanilla.shop_dict)
if self.multiworld.thorn_shuffle[self.player] == 2:
self.place_items_from_set(Vanilla.thorn_set, "Thorn Upgrade")
if not self.multiworld.candle_shuffle[self.player]:
self.place_items_from_dict(Vanilla.candle_dict)
if self.multiworld.start_wheel[self.player]:
self.multiworld.get_location("BotSS: Beginning gift", self.player)\
if world.start_wheel[player]:
world.get_location("Beginning gift", player)\
.place_locked_item(self.create_item("The Young Mason's Wheel"))
if not self.multiworld.skill_randomizer[self.player]:
self.place_items_from_dict(Vanilla.skill_dict)
if not world.skill_randomizer[player]:
self.place_items_from_dict(skill_dict)
if self.multiworld.thorn_shuffle[self.player] == 1:
self.multiworld.local_items[self.player].value.add("Thorn Upgrade")
if world.thorn_shuffle[player] == 1:
world.local_items[player].value.add("Thorn Upgrade")
def place_items_from_set(self, location_set: Set[str], name: str):
@@ -273,133 +248,143 @@ class BlasphemousWorld(World):
def create_regions(self) -> None:
player = self.player
world = self.multiworld
region_table: Dict[str, Region] = {
"menu" : Region("Menu", player, world),
"albero" : Region("Albero", player, world),
"attots" : Region("All the Tears of the Sea", player, world),
"ar" : Region("Archcathedral Rooftops", player, world),
"bottc" : Region("Bridge of the Three Cavalries", player, world),
"botss" : Region("Brotherhood of the Silent Sorrow", player, world),
"coolotcv": Region("Convent of Our Lady of the Charred Visage", player, world),
"dohh" : Region("Deambulatory of His Holiness", player, world),
"dc" : Region("Desecrated Cistern", player, world),
"eos" : Region("Echoes of Salt", player, world),
"ft" : Region("Ferrous Tree", player, world),
"gotp" : Region("Graveyard of the Peaks", player, world),
"ga" : Region("Grievance Ascends", player, world),
"hotd" : Region("Hall of the Dawning", player, world),
"jondo" : Region("Jondo", player, world),
"kottw" : Region("Knot of the Three Words", player, world),
"lotnw" : Region("Library of the Negated Words", player, world),
"md" : Region("Mercy Dreams", player, world),
"mom" : Region("Mother of Mothers", player, world),
"moted" : Region("Mountains of the Endless Dusk", player, world),
"mah" : Region("Mourning and Havoc", player, world),
"potss" : Region("Patio of the Silent Steps", player, world),
"petrous" : Region("Petrous", player, world),
"thl" : Region("The Holy Line", player, world),
"trpots" : Region("The Resting Place of the Sister", player, world),
"tsc" : Region("The Sleeping Canvases", player, world),
"wothp" : Region("Wall of the Holy Prohibitions", player, world),
"wotbc" : Region("Wasteland of the Buried Churches", player, world),
"wotw" : Region("Where Olive Trees Wither", player, world),
"dungeon" : Region("Dungeons", player, world)
}
for rname, reg in region_table.items():
world.regions.append(reg)
for ename, exits in region_exit_table.items():
if ename == rname:
for i in exits:
ent = Entrance(player, i, reg)
reg.exits.append(ent)
for e, r in exit_lookup_table.items():
if i == e:
ent.connect(region_table[r])
for loc in location_table:
id = base_id + location_table.index(loc)
region_table[loc["region"]].locations\
.append(BlasphemousLocation(self.player, loc["name"], id, region_table[loc["region"]]))
victory = Location(self.player, "His Holiness Escribar", None, self.multiworld.get_region("Deambulatory of His Holiness", self.player))
victory.place_locked_item(self.create_event("Victory"))
self.multiworld.get_region("Deambulatory of His Holiness", self.player).locations.append(victory)
menu_region = Region("Menu", player, world)
misc_region = Region("Misc", player, world)
world.regions += [menu_region, misc_region]
if self.multiworld.ending[self.player].value == 1:
for room in room_table:
region = Region(room, player, world)
world.regions.append(region)
menu_region.add_exits({self.start_room: "New Game"})
world.get_region(self.start_room, player).add_exits({"Misc": "Misc"})
for door in door_table:
if door.get("OriginalDoor") is None:
continue
else:
if not door["Id"] in self.door_connections.keys():
self.door_connections[door["Id"]] = door["OriginalDoor"]
self.door_connections[door["OriginalDoor"]] = door["Id"]
parent_region: Region = self.get_room_from_door(door["Id"])
target_region: Region = self.get_room_from_door(door["OriginalDoor"])
parent_region.add_exits({
target_region.name: door["Id"]
}, {
target_region.name: lambda x: door.get("VisibilityFlags") != 1
})
for index, loc in enumerate(location_table):
if not world.boots_of_pleading[player] and loc["name"] == "BotSS: 2nd meeting with Redento":
continue
if not world.purified_hand[player] and loc["name"] == "MoM: Western room ledge":
continue
region: Region = world.get_region(loc["room"], player)
region.add_locations({loc["name"]: base_id + index})
#id = base_id + location_table.index(loc)
#reg.locations.append(BlasphemousLocation(player, loc["name"], id, reg))
for e, r in event_table.items():
region: Region = world.get_region(r, player)
event = BlasphemousLocation(player, e, None, region)
event.show_in_spoiler = False
event.place_locked_item(self.create_event(e))
region.locations.append(event)
for door in door_table:
region: Region = self.get_room_from_door(self.door_connections[door["Id"]])
event = BlasphemousLocation(player, door["Id"], None, region)
event.show_in_spoiler = False
event.place_locked_item(self.create_event(door["Id"]))
add_rule(event, lambda state: state.can_reach(self.get_connected_door(door["Id"])), player)
region.locations.append(event)
victory = Location(player, "His Holiness Escribar", None, world.get_region("D07Z01S03", player))
victory.place_locked_item(self.create_event("Victory"))
world.get_region("D07Z01S03", player).locations.append(victory)
if world.ending[self.player].value == 1:
set_rule(victory, lambda state: state.has("Thorn Upgrade", player, 8))
elif self.multiworld.ending[self.player].value == 2:
set_rule(victory, lambda state: state.has("Thorn Upgrade", player, 8) and \
elif world.ending[self.player].value == 2:
set_rule(victory, lambda state: state.has("Thorn Upgrade", player, 8) and
state.has("Holy Wound of Abnegation", player))
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
world.completion_condition[self.player] = lambda state: state.has("Victory", player)
def get_room_from_door(self, door: str) -> Region:
return self.multiworld.get_region(door.split("[")[0], self.player)
def get_connected_door(self, door: str) -> Entrance:
return self.multiworld.get_entrance(self.door_connections[door], self.player)
def fill_slot_data(self) -> Dict[str, Any]:
slot_data: Dict[str, Any] = {}
locations = []
doors: Dict[str, str] = {}
for loc in self.multiworld.get_filled_locations(self.player):
if loc.name == "His Holiness Escribar":
world = self.multiworld
player = self.player
thorns: bool = True
if world.thorn_shuffle[player].value == 2:
thorns = False
for loc in world.get_filled_locations(player):
if loc.item.code == None:
continue
else:
data = {
"id": self.location_name_to_game_id[loc.name],
"ap_id": loc.address,
"name": loc.item.name,
"player_name": self.multiworld.player_name[loc.item.player]
"player_name": world.player_name[loc.item.player],
"type": int(loc.item.classification)
}
if loc.name in shop_set:
data["type"] = loc.item.classification.name
locations.append(data)
config = {
"versionCreated": "AP",
"general": {
"teleportationAlwaysUnlocked": bool(self.multiworld.prie_dieu_warp[self.player].value),
"skipCutscenes": bool(self.multiworld.skip_cutscenes[self.player].value),
"enablePenitence": bool(self.multiworld.penitence[self.player].value),
"hardMode": False,
"customSeed": 0,
"allowHints": bool(self.multiworld.corpse_hints[self.player].value)
},
"items": {
"type": 1,
"lungDamage": False,
"disableNPCDeath": True,
"startWithWheel": bool(self.multiworld.start_wheel[self.player].value),
"shuffleReliquaries": bool(self.multiworld.reliquary_shuffle[self.player].value)
},
"enemies": {
"type": self.multiworld.enemy_randomizer[self.player].value,
"maintainClass": bool(self.multiworld.enemy_groups[self.player].value),
"areaScaling": bool(self.multiworld.enemy_scaling[self.player].value)
},
"prayers": {
"type": 0,
"removeMirabis": False
},
"doors": {
"type": 0
},
"debug": {
"type": 0
}
"LogicDifficulty": world.difficulty[player].value,
"StartingLocation": world.starting_location[player].value,
"VersionCreated": "AP",
"UnlockTeleportation": bool(world.prie_dieu_warp[player].value),
"AllowHints": bool(world.corpse_hints[player].value),
"AllowPenitence": bool(world.penitence[player].value),
"ShuffleReliquaries": bool(world.reliquary_shuffle[player].value),
"ShuffleBootsOfPleading": bool(world.boots_of_pleading[player].value),
"ShufflePurifiedHand": bool(world.purified_hand[player].value),
"ShuffleDash": bool(world.dash_shuffle[player].value),
"ShuffleWallClimb": bool(world.wall_climb_shuffle[player].value),
"ShuffleSwordSkills": bool(world.skill_randomizer[player].value),
"ShuffleThorns": thorns,
"JunkLongQuests": bool(world.skip_long_quests[player].value),
"StartWithWheel": bool(world.start_wheel[player].value),
"EnemyShuffleType": world.enemy_randomizer[player].value,
"MaintainClass": bool(world.enemy_groups[player].value),
"AreaScaling": bool(world.enemy_scaling[player].value),
"BossShuffleType": 0,
"DoorShuffleType": 0
}
slot_data = {
"locations": locations,
"doors": doors,
"cfg": config,
"ending": self.multiworld.ending[self.player].value,
"death_link": bool(self.multiworld.death_link[self.player].value)
"ending": world.ending[self.player].value,
"death_link": bool(world.death_link[self.player].value)
}
return slot_data