Blasphemous: Implement new game (#1446)

Adds @BrandenEK's Blasphemous Randomizer as a new Archipelago game.
This commit is contained in:
Trevor L 2023-02-23 23:33:09 -07:00 committed by GitHub
parent 1d6ab13015
commit 7c68e91d4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 4640 additions and 0 deletions

135
worlds/blasphemous/Exits.py Normal file
View File

@ -0,0 +1,135 @@
from typing import List, Dict
region_exit_table: Dict[str, List[str]] = {
"menu" : ["New Game"],
"albero" : ["To The Holy Line",
"To Desecrated Cistern",
"To Wasteland of the Buried Churches",
"To Dungeons"],
"attots" : ["To Mother of Mothers"],
"ar" : ["To Mother of Mothers",
"To Wall of the Holy Prohibitions",
"To Deambulatory of His Holiness"],
"bottc" : ["To Wasteland of the Buried Churches",
"To Ferrous Tree"],
"botss" : ["To The Holy Line",
"To Mountains of the Endless Dusk"],
"coolotcv" : ["To Graveyard of the Peaks",
"To Wall of the Holy Prohibitions"],
"dohh" : ["To Archcathedral Rooftops"],
"dc" : ["To Albero",
"To Mercy Dreams",
"To Mountains of the Endless Dusk",
"To Echoes of Salt",
"To Grievance Ascends"],
"eos" : ["To Jondo",
"To Mountains of the Endless Dusk",
"To Desecrated Cistern",
"To The Resting Place of the Sister",
"To Mourning and Havoc"],
"ft" : ["To Bridge of the Three Cavalries",
"To Hall of the Dawning",
"To Patio of the Silent Steps"],
"gotp" : ["To Where Olive Trees Wither",
"To Convent of Our Lady of the Charred Visage"],
"ga" : ["To Jondo",
"To Desecrated Cistern"],
"hotd" : ["To Ferrous Tree"],
"jondo" : ["To Mountains of the Endless Dusk",
"To Grievance Ascends"],
"kottw" : ["To Mother of Mothers"],
"lotnw" : ["To Mother of Mothers",
"To The Sleeping Canvases"],
"md" : ["To Wasteland of the Buried Churches",
"To Desecrated Cistern",
"To The Sleeping Canvases"],
"mom" : ["To Patio of the Silent Steps",
"To Archcathedral Rooftops",
"To Knot of the Three Words",
"To Library of the Negated Words",
"To All the Tears of the Sea"],
"moted" : ["To Brotherhood of the Silent Sorrow",
"To Jondo",
"To Desecrated Cistern"],
"mah" : ["To Echoes of Salt",
"To Mother of Mothers"],
"potss" : ["To Ferrous Tree",
"To Mother of Mothers",
"To Wall of the Holy Prohibitions"],
"petrous" : ["To The Holy Line"],
"thl" : ["To Brotherhood of the Silent Sorrow",
"To Petrous",
"To Albero"],
"trpots" : ["To Echoes of Salt"],
"tsc" : ["To Library of the Negated Words",
"To Mercy Dreams"],
"wothp" : ["To Archcathedral Rooftops",
"To Convent of Our Lady of the Charred Visage"],
"wotbc" : ["To Albero",
"To Where Olive Trees Wither",
"To Mercy Dreams"],
"wotw" : ["To Wasteland of the Buried Churches",
"To Graveyard of the Peaks"]
}
exit_lookup_table: Dict[str, str] = {
"New Game": "botss",
"To Albero": "albero",
"To All the Tears of the Sea": "attots",
"To Archcathedral Rooftops": "ar",
"To Bridge of the Three Cavalries": "bottc",
"To Brotherhood of the Silent Sorrow": "botss",
"To Convent of Our Lady of the Charred Visage": "coolotcv",
"To Deambulatory of His Holiness": "dohh",
"To Desecrated Cistern": "dc",
"To Echoes of Salt": "eos",
"To Ferrous Tree": "ft",
"To Graveyard of the Peaks": "gotp",
"To Grievance Ascends": "ga",
"To Hall of the Dawning": "hotd",
"To Jondo": "jondo",
"To Knot of the Three Words": "kottw",
"To Library of the Negated Words": "lotnw",
"To Mercy Dreams": "md",
"To Mother of Mothers": "mom",
"To Mountains of the Endless Dusk": "moted",
"To Mourning and Havoc": "mah",
"To Patio of the Silent Steps": "potss",
"To Petrous": "petrous",
"To The Holy Line": "thl",
"To The Resting Place of the Sister": "trpots",
"To The Sleeping Canvases": "tsc",
"To Wall of the Holy Prohibitions": "wothp",
"To Wasteland of the Buried Churches": "wotbc",
"To Where Olive Trees Wither": "wotw",
"To Dungeons": "dungeon"
}

754
worlds/blasphemous/Items.py Normal file
View File

@ -0,0 +1,754 @@
from BaseClasses import ItemClassification
from typing import TypedDict, Dict, List, Set
class ItemDict(TypedDict):
name: str
count: int
classification: ItemClassification
base_id = 1909000
item_table: List[ItemDict] = [
# Rosary Beads
{'name': "Dove Skull",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Ember of the Holy Cremation",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Silver Grape",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Uvula of Proclamation",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Hollow Pearl",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Knot of Hair",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Painted Wood Bead",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Piece of a Golden Mask",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Moss Preserved in Glass",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Frozen Olive",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Quirce's Scorched Bead",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Wicker Knot",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Perpetva's Protection",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Thorned Symbol",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Piece of a Tombstone",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Sphere of the Sacred Smoke",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Bead of Red Wax",
'count': 3,
'classification': ItemClassification.progression},
{'name': "Little Toe made of Limestone",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Big Toe made of Limestone",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Fourth Toe made of Limestone",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Bead of Blue Wax",
'count': 3,
'classification': ItemClassification.progression},
{'name': "Pelican Effigy",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Drop of Coagulated Ink",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Amber Eye",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Muted Bell",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Consecrated Amethyst",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Embers of a Broken Star",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Scaly Coin",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Seashell of the Inverted Spiral",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Calcified Eye of Erudition",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Weight of True Guilt",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Reliquary of the Fervent Heart",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Reliquary of the Suffering Heart",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Reliquary of the Sorrowful Heart",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Token of Appreciation",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Cloistered Ruby",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Bead of Gold Thread",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Cloistered Sapphire",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Fire Enclosed in Enamel",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Light of the Lady of the Lamp",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Scale of Burnished Alabaster",
'count': 1,
'classification': ItemClassification.useful},
{'name': "The Young Mason's Wheel",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Crown of Gnawed Iron",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Crimson Heart of a Miura",
'count': 1,
'classification': ItemClassification.useful},
# Prayers
{'name': "Seguiriya to your Eyes like Stars",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Debla of the Lights",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Saeta Dolorosa",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Campanillero to the Sons of the Aurora",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Lorquiana",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Zarabanda of the Safe Haven",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Taranto to my Sister",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Solea of Excommunication",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Tiento to your Thorned Hairs",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Cante Jondo of the Three Sisters",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Verdiales of the Forsaken Hamlet",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Romance to the Crimson Mist",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Zambra to the Resplendent Crown",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Aubade of the Nameless Guardian",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Cantina of the Blue Rose",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Mirabras of the Return to Port",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Tirana of the Celestial Bastion",
'count': 1,
'classification': ItemClassification.progression},
# Relics
{'name': "Blood Perpetuated in Sand",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Incorrupt Hand of the Fraternal Master",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Nail Uprooted from Dirt",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Shroud of Dreamt Sins",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Linen of Golden Thread",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Silvered Lung of Dolphos",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Three Gnarled Tongues",
'count': 1,
'classification': ItemClassification.progression},
# Mea Culpa Hearts
{'name': "Smoking Heart of Incense",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Heart of the Virtuous Pain",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Heart of Saltpeter Blood",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Heart of Oils",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Heart of Cerulean Incense",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Heart of the Holy Purge",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Molten Heart of Boiling Blood",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Heart of the Single Tone",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Heart of the Unnamed Minstrel",
'count': 1,
'classification': ItemClassification.useful},
{'name': "Brilliant Heart of Dawn",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Apodictic Heart of Mea Culpa",
'count': 1,
'classification': ItemClassification.progression},
# Quest Items
{'name': "Cord of the True Burying",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Mark of the First Refuge",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Mark of the Second Refuge",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Mark of the Third Refuge",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Tentudia's Carnal Remains",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Remains of Tentudia's Hair",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Tentudia's Skeletal Remains",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Melted Golden Coins",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Torn Bridal Ribbon",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Black Grieving Veil",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Egg of Deformity",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Hatched Egg of Deformity",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Bouquet of Rosemary",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Incense Garlic",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Thorn Upgrade",
'count': 8,
'classification': ItemClassification.progression},
{'name': "Olive Seeds",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Holy Wound of Attrition",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Holy Wound of Contrition",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Holy Wound of Compunction",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Empty Bile Vessel",
'count': 8,
'classification': ItemClassification.progression},
{'name': "Knot of Rosary Rope",
'count': 6,
'classification': ItemClassification.progression},
{'name': "Golden Thimble Filled with Burning Oil",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Key to the Chamber of the Eldest Brother",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Empty Golden Thimble",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Deformed Mask of Orestes",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Mirrored Mask of Dolphos",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Embossed Mask of Crescente",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Dried Clove",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Sooty Garlic",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Bouquet of Thyme",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Linen Cloth",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Severed Hand",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Dried Flowers bathed in Tears",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Key of the Secular",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Key of the Scribe",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Key of the Inquisitor",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Key of the High Peaks",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Chalice of Inverted Verses",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Quicksilver",
'count': 5,
'classification': ItemClassification.useful},
{'name': "Petrified Bell",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Verses Spun from Gold",
'count': 4,
'classification': ItemClassification.progression},
{'name': "Severed Right Eye of the Traitor",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Broken Left Eye of the Traitor",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Incomplete Scapular",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Key Grown from Twisted Wood",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Holy Wound of Abnegation",
'count': 1,
'classification': ItemClassification.progression},
# Skills
{'name': "Combo Skill",
'count': 3,
'classification': ItemClassification.useful},
{'name': "Charged Skill",
'count': 3,
'classification': ItemClassification.progression},
{'name': "Ranged Skill",
'count': 3,
'classification': ItemClassification.progression},
{'name': "Dive Skill",
'count': 3,
'classification': ItemClassification.progression},
{'name': "Lunge Skill",
'count': 3,
'classification': ItemClassification.useful},
# Other
{'name': "Parietal bone of Lasser, the Inquisitor",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Jaw of Ashgan, the Inquisitor",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Cervical vertebra of Zicher, the Brewmaster",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Clavicle of Dalhuisen, the Schoolchild",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Sternum of Vitas, the Performer",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Ribs of Sabnock, the Guardian",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Vertebra of John, the Gambler",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Scapula of Carlos, the Executioner",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Humerus of McMittens, the Nurse",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Ulna of Koke, the Troubadour",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Radius of Helzer, the Poet",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Frontal of Martinus, the Ropemaker",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Metacarpus of Hodges, the Blacksmith",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Phalanx of Arthur, the Sailor",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Phalanx of Miriam, the Counsellor",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Phalanx of Brannon, the Gravedigger",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Coxal of June, the Prostitute",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Sacrum of the Dark Warlock",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Coccyx of Daniel, the Possessed",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Femur of Karpow, the Bounty Hunter",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Kneecap of Sebastien, the Puppeteer",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Tibia of Alsahli, the Mystic",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Fibula of Rysp, the Ranger",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Temporal of Joel, the Thief",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Metatarsus of Rikusyo, the Traveller",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Phalanx of Zeth, the Prisoner",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Phalanx of William, the Sceptic",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Phalanx of Aralcarim, the Archivist",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Occipital of Tequila, the Metalsmith",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Maxilla of Tarradax, the Cleric",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Nasal bone of Charles, the Artist",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Hyoid bone of Senex, the Beggar",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Vertebra of Lindquist, the Forger",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Trapezium of Jeremiah, the Hangman",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Trapezoid of Yeager, the Jeweller",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Capitate of Barock, the Herald",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Hamate of Vukelich, the Copyist",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Pisiform of Hernandez, the Explorer",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Triquetral of Luca, the Tailor",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Lunate of Keiya, the Butcher",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Scaphoid of Fierce, the Leper",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Anklebone of Weston, the Pilgrim",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Calcaneum of Persian, the Bandit",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Navicular of Kahnnyhoo, the Murderer",
'count': 1,
'classification': ItemClassification.progression},
{'name': "Child of Moonlight",
'count': 38,
'classification': ItemClassification.progression},
{'name': "Life Upgrade",
'count': 6,
'classification': ItemClassification.progression},
{'name': "Fervour Upgrade",
'count': 6,
'classification': ItemClassification.progression},
{'name': "Mea Culpa Upgrade",
'count': 7,
'classification': ItemClassification.progression},
{'name': "Tears of Atonement (250)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (300)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (500)",
'count': 3,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (625)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (750)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (1000)",
'count': 4,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (1250)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (1500)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (1750)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (2000)",
'count': 2,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (2100)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (2500)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (2600)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (3000)",
'count': 2,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (4300)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (5000)",
'count': 4,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (5500)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (9000)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (10000)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (11250)",
'count': 1,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (18000)",
'count': 5,
'classification': ItemClassification.filler},
{'name': "Tears of Atonement (30000)",
'count': 1,
'classification': ItemClassification.filler}
]
group_table: Dict[str, Set[str]] = {
"wounds" : ["Holy Wound of Attrition",
"Holy Wound of Contrition",
"Holy Wound of Compunction"],
"masks" : ["Deformed Mask of Orestes",
"Mirrored Mask of Dolphos",
"Embossed Mask of Crescente"],
"tirso" : ["Bouquet of Rosemary",
"Incense Garlic",
"Olive Seeds",
"Dried Clove",
"Sooty Garlic",
"Bouquet of Thyme"],
"tentudia": ["Tentudia's Carnal Remains",
"Remains of Tentudia's Hair",
"Tentudia's Skeletal Remains"],
"egg" : ["Melted Golden Coins",
"Torn Bridal Ribbon",
"Black Grieving Veil"],
"bones" : ["Parietal bone of Lasser, the Inquisitor",
"Jaw of Ashgan, the Inquisitor",
"Cervical vertebra of Zicher, the Brewmaster",
"Clavicle of Dalhuisen, the Schoolchild",
"Sternum of Vitas, the Performer",
"Ribs of Sabnock, the Guardian",
"Vertebra of John, the Gambler",
"Scapula of Carlos, the Executioner",
"Humerus of McMittens, the Nurse",
"Ulna of Koke, the Troubadour",
"Radius of Helzer, the Poet",
"Frontal of Martinus, the Ropemaker",
"Metacarpus of Hodges, the Blacksmith",
"Phalanx of Arthur, the Sailor",
"Phalanx of Miriam, the Counsellor",
"Phalanx of Brannon, the Gravedigger",
"Coxal of June, the Prostitute",
"Sacrum of the Dark Warlock",
"Coccyx of Daniel, the Possessed",
"Femur of Karpow, the Bounty Hunter",
"Kneecap of Sebastien, the Puppeteer",
"Tibia of Alsahli, the Mystic",
"Fibula of Rysp, the Ranger",
"Temporal of Joel, the Thief",
"Metatarsus of Rikusyo, the Traveller",
"Phalanx of Zeth, the Prisoner",
"Phalanx of William, the Sceptic",
"Phalanx of Aralcarim, the Archivist",
"Occipital of Tequila, the Metalsmith",
"Maxilla of Tarradax, the Cleric",
"Nasal bone of Charles, the Artist",
"Hyoid bone of Senex, the Beggar",
"Vertebra of Lindquist, the Forger",
"Trapezium of Jeremiah, the Hangman",
"Trapezoid of Yeager, the Jeweller",
"Capitate of Barock, the Herald",
"Hamate of Vukelich, the Copyist",
"Pisiform of Hernandez, the Explorer",
"Triquetral of Luca, the Tailor",
"Lunate of Keiya, the Butcher",
"Scaphoid of Fierce, the Leper",
"Anklebone of Weston, the Pilgrim",
"Calcaneum of Persian, the Bandit",
"Navicular of Kahnnyhoo, the Murderer"],
"power" : ["Life Upgrade",
"Fervour Upgrade",
"Empty Bile Vessel",
"Quicksilver"],
"prayer" : ["Seguiriya to your Eyes like Stars",
"Debla of the Lights",
"Saeta Dolorosa",
"Campanillero to the Sons of the Aurora",
"Lorquiana",
"Zarabanda of the Safe Haven",
"Taranto to my Sister",
"Solea of Excommunication",
"Tiento to your Thorned Hairs",
"Cante Jondo of the Three Sisters",
"Verdiales of the Forsaken Hamlet",
"Romance to the Crimson Mist",
"Zambra to the Resplendent Crown",
"Cantina of the Blue Rose",
"Mirabras of the Return to Port"]
}
tears_set: Set[str] = [
"Tears of Atonement (500)",
"Tears of Atonement (625)",
"Tears of Atonement (750)",
"Tears of Atonement (1000)",
"Tears of Atonement (1250)",
"Tears of Atonement (1500)",
"Tears of Atonement (1750)",
"Tears of Atonement (2000)",
"Tears of Atonement (2100)",
"Tears of Atonement (2500)",
"Tears of Atonement (2600)",
"Tears of Atonement (3000)",
"Tears of Atonement (4300)",
"Tears of Atonement (5000)",
"Tears of Atonement (5500)",
"Tears of Atonement (9000)",
"Tears of Atonement (10000)",
"Tears of Atonement (11250)",
"Tears of Atonement (18000)",
"Tears of Atonement (30000)"
]
reliquary_set: Set[str] = [
"Reliquary of the Fervent Heart",
"Reliquary of the Suffering Heart",
"Reliquary of the Sorrowful Heart"
]
skill_set: Set[str] = [
"Combo Skill",
"Charged Skill",
"Ranged Skill",
"Dive Skill",
"Lunge Skill"
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,257 @@
from Options import Choice, Toggle, DefaultOnToggle, DeathLink
class PrieDieuWarp(DefaultOnToggle):
"""Automatically unlocks the ability to warp between Prie Dieu shrines."""
display_name = "Unlock Fast Travel"
class SkipCutscenes(DefaultOnToggle):
"""Automatically skips most cutscenes."""
display_name = "Auto Skip Cutscenes"
class CorpseHints(DefaultOnToggle):
"""Changes the 34 corpses in game to give various hints about item locations."""
display_name = "Corpse Hints"
class Difficulty(Choice):
"""Adjusts the logic required to defeat bosses.
Impossible: Removes all logic requirements for bosses. Good luck."""
display_name = "Difficulty"
option_easy = 0
option_normal = 1
option_hard = 2
option_impossible = 3
default = 1
class Penitence(Toggle):
"""Allows one of the three Penitences to be chosen at the beginning of the game."""
display_name = "Penitence"
class ExpertLogic(Toggle):
"""Expands the logic used by the randomizer to allow for some difficult and/or lesser known tricks."""
display_name = "Expert Logic"
class Ending(Choice):
"""Choose which ending is required to complete the game."""
display_name = "Ending"
option_any_ending = 0
option_ending_b = 1
option_ending_c = 2
default = 0
class ThornShuffle(Choice):
"""Shuffles the Thorn given by Deogracias and all Thorn upgrades into the item pool."""
display_name = "Shuffle Thorn"
option_anywhere = 0
option_local_only = 1
option_vanilla = 2
default = 0
class ReliquaryShuffle(DefaultOnToggle):
"""Adds the True Torment exclusive Reliquary rosary beads into the item pool."""
display_name = "Shuffle Penitence Rewards"
class CherubShuffle(DefaultOnToggle):
"""Shuffles Children of Moonlight into the item pool."""
display_name = "Shuffle Children of Moonlight"
class LifeShuffle(DefaultOnToggle):
"""Shuffles life upgrades from the Lady of the Six Sorrows into the item pool."""
display_name = "Shuffle Life Upgrades"
class FervourShuffle(DefaultOnToggle):
"""Shuffles fervour upgrades from the Oil of the Pilgrims into the item pool."""
display_name = "Shuffle Fervour Upgrades"
class SwordShuffle(DefaultOnToggle):
"""Shuffles Mea Culpa upgrades from the Mea Culpa Altars into the item pool."""
display_name = "Shuffle Mea Culpa Upgrades"
class BlessingShuffle(DefaultOnToggle):
"""Shuffles blessings from the Lake of Silent Pilgrims into the item pool."""
display_name = "Shuffle Blessings"
class DungeonShuffle(DefaultOnToggle):
"""Shuffles rewards from completing Confessor Dungeons into the item pool."""
display_name = "Shuffle Dungeon Rewards"
class TirsoShuffle(DefaultOnToggle):
"""Shuffles rewards from delivering herbs to Tirso into the item pool."""
display_name = "Shuffle Tirso's Rewards"
class MiriamShuffle(DefaultOnToggle):
"""Shuffles the prayer given by Miriam into the item pool."""
display_name = "Shuffle Miriram's Reward"
class RedentoShuffle(DefaultOnToggle):
"""Shuffles rewards from assisting Redento into the item pool."""
display_name = "Shuffle Redento's Rewards"
class JocineroShuffle(DefaultOnToggle):
"""Shuffles rewards from rescuing 20 and 38 Children of Moonlight into the item pool."""
display_name = "Shuffle Jocinero's Rewards"
class AltasgraciasShuffle(DefaultOnToggle):
"""Shuffles the reward given by Altasgracias and the item left behind by them into the item pool."""
display_name = "Shuffle Altasgracias' Rewards"
class TentudiaShuffle(DefaultOnToggle):
"""Shuffles the rewards from delivering Tentudia's remains to Lvdovico into the item pool."""
display_name = "Shuffle Lvdovico's Rewards"
class GeminoShuffle(DefaultOnToggle):
"""Shuffles the rewards from Gemino's quest and the hidden tomb into the item pool."""
display_name = "Shuffle Gemino's Rewards"
class GuiltShuffle(DefaultOnToggle):
"""Shuffles the Weight of True Guilt into the item pool."""
display_name = "Shuffle Immaculate Bead"
class OssuaryShuffle(DefaultOnToggle):
"""Shuffles the rewards from delivering bones to the Ossuary into the item pool."""
display_name = "Shuffle Ossuary Rewards"
class BossShuffle(DefaultOnToggle):
"""Shuffles the Tears of Atonement from defeating bosses into the item pool."""
display_name = "Shuffle Boss Tears"
class WoundShuffle(DefaultOnToggle):
"""Shuffles the Holy Wounds required to pass the Bridge of the Three Cavalries into the item pool."""
display_name = "Shuffle Holy Wounds"
class MaskShuffle(DefaultOnToggle):
"""Shuffles the masks required to use the elevator in Archcathedral Rooftops into the item pool."""
display_name = "Shuffle Masks"
class EyeShuffle(DefaultOnToggle):
"""Shuffles the Eyes of the Traitor from defeating Isidora and Sierpes into the item pool."""
display_name = "Shuffle Traitor's Eyes"
class HerbShuffle(DefaultOnToggle):
"""Shuffles the herbs required for Tirso's quest into the item pool."""
display_name = "Shuffle Herbs"
class ChurchShuffle(DefaultOnToggle):
"""Shuffles the rewards from donating 5,000 and 50,000 Tears of Atonement to the Church in Albero into the item pool."""
display_name = "Shuffle Donation Rewards"
class ShopShuffle(DefaultOnToggle):
"""Shuffles the items sold in Candelaria's shops into the item pool."""
display_name = "Shuffle Shop Items"
class CandleShuffle(DefaultOnToggle):
"""Shuffles the Beads of Wax and their upgrades into the item pool."""
display_name = "Shuffle Candles"
class StartWheel(Toggle):
"""Changes the beginning gift to The Young Mason's Wheel."""
display_name = "Start with Wheel"
class SkillRando(Toggle):
"""Randomizes the abilities from the skill tree into the item pool."""
display_name = "Skill Randomizer"
class EnemyRando(Choice):
"""Randomizes the enemies that appear in each room.
Shuffled: Enemies will be shuffled amongst each other, but can only appear as many times as they do in a standard game.
Randomized: Every enemy is completely random, and can appear any number of times.
Some enemies will never be randomized."""
display_name = "Enemy Randomizer"
option_disabled = 0
option_shuffled = 1
option_randomized = 2
default = 0
class EnemyGroups(DefaultOnToggle):
"""Randomized enemies will chosen from sets of specific groups.
(Weak, normal, large, flying)
Has no effect if Enemy Randomizer is disabled."""
display_name = "Enemy Groups"
class EnemyScaling(DefaultOnToggle):
"""Randomized enemies will have their stats increased or decreased depending on the area they appear in.
Has no effect if Enemy Randomizer is disabled."""
display_name = "Enemy Scaling"
class BlasphemousDeathLink(DeathLink):
"""When you die, everyone dies. The reverse is also true.
Note that Guilt Fragments will not appear when killed by Death Link."""
blasphemous_options = {
"prie_dieu_warp": PrieDieuWarp,
"skip_cutscenes": SkipCutscenes,
"corpse_hints": CorpseHints,
"difficulty": Difficulty,
"penitence": Penitence,
"expert_logic": ExpertLogic,
"ending": Ending,
"thorn_shuffle" : ThornShuffle,
"reliquary_shuffle": ReliquaryShuffle,
"cherub_shuffle" : CherubShuffle,
"life_shuffle" : LifeShuffle,
"fervour_shuffle" : FervourShuffle,
"sword_shuffle" : SwordShuffle,
"blessing_shuffle" : BlessingShuffle,
"dungeon_shuffle" : DungeonShuffle,
"tirso_shuffle" : TirsoShuffle,
"miriam_shuffle" : MiriamShuffle,
"redento_shuffle" : RedentoShuffle,
"jocinero_shuffle" : JocineroShuffle,
"altasgracias_shuffle" : AltasgraciasShuffle,
"tentudia_shuffle" : TentudiaShuffle,
"gemino_shuffle" : GeminoShuffle,
"guilt_shuffle" : GuiltShuffle,
"ossuary_shuffle" : OssuaryShuffle,
"boss_shuffle" : BossShuffle,
"wound_shuffle" : WoundShuffle,
"mask_shuffle" : MaskShuffle,
"eye_shuffle": EyeShuffle,
"herb_shuffle" : HerbShuffle,
"church_shuffle" : ChurchShuffle,
"shop_shuffle" : ShopShuffle,
"candle_shuffle" : CandleShuffle,
"start_wheel": StartWheel,
"skill_randomizer": SkillRando,
"enemy_randomizer": EnemyRando,
"enemy_groups": EnemyGroups,
"enemy_scaling": EnemyScaling,
"death_link": BlasphemousDeathLink
}

1455
worlds/blasphemous/Rules.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
from typing import Set, Dict
unrandomized_dict: Dict[str, str] = {
"CoOLotCV: Fountain of burning oil": "Golden Thimble Filled with Burning Oil",
"MotED: Egg hatching": "Hatched Egg of Deformity",
"BotSS: Crisanta's gift": "Holy Wound of Abnegation",
"DC: Chalice room": "Chalice of Inverted Verses"
}
cherub_set: Set[str] = [
"Albero: Child of Moonlight",
"AR: Upper west shaft Child of Moonlight",
"BotSS: Starting room Child of Moonlight",
"DC: Child of Moonlight, above water",
"DC: Upper east Child of Moonlight",
"DC: Child of Moonlight, miasma room",
"DC: Child of Moonlight, behind pillar",
"DC: Top of elevator Child of Moonlight",
"DC: Elevator shaft Child of Moonlight",
"GotP: Shop cave Child of Moonlight",
"GotP: Elevator shaft Child of Moonlight",
"GotP: West shaft Child of Moonlight",
"GotP: Center shaft Child of Moonlight",
"GA: Miasma room Child of Moonlight",
"GA: Blood bridge Child of Moonlight",
"GA: Lower east Child of Moonlight",
"Jondo: Upper east Child of Moonlight",
"Jondo: Spike tunnel Child of Moonlight",
"Jondo: Upper west Child of Moonlight",
"LotNW: Platform room Child of Moonlight",
"LotNW: Lowest west Child of Moonlight",
"LotNW: Elevator Child of Moonlight",
"MD: Second area Child of Moonlight",
"MD: Cave Child of Moonlight",
"MoM: Lower west Child of Moonlight",
"MoM: Upper center Child of Moonlight",
"MotED: Child of Moonlight, above chasm",
"PotSS: First area Child of Moonlight",
"PotSS: Third area Child of Moonlight",
"THL: Child of Moonlight",
"WotHP: Upper east room, top bronze cell",
"WotHP: Upper west room, top silver cell",
"WotHP: Lower east room, bottom silver cell",
"WotHP: Outside Child of Moonlight",
"WotBC: Outside Child of Moonlight",
"WotBC: Cliffside Child of Moonlight",
"WOTW: Underground Child of Moonlight",
"WOTW: Upper east Child of Moonlight",
]
life_set: Set[str] = [
"AR: Lady of the Six Sorrows",
"CoOLotCV: Lady of the Six Sorrows",
"DC: Lady of the Six Sorrows, from MD",
"DC: Lady of the Six Sorrows, elevator shaft",
"GotP: Lady of the Six Sorrows",
"LotNW: Lady of the Six Sorrows"
]
fervour_set: Set[str] = [
"DC: Oil of the Pilgrims",
"GotP: Oil of the Pilgrims",
"GA: Oil of the Pilgrims",
"LotNW: Oil of the Pilgrims",
"MoM: Oil of the Pilgrims",
"WotHP: Oil of the Pilgrims"
]
sword_set: Set[str] = [
"Albero: Mea Culpa altar",
"AR: Mea Culpa altar",
"BotSS: Mea Culpa altar",
"CoOLotCV: Mea Culpa altar",
"DC: Mea Culpa altar",
"LotNW: Mea Culpa altar",
"MoM: Mea Culpa altar"
]
blessing_dict: Dict[str, str] = {
"Albero: Bless Severed Hand": "Incorrupt Hand of the Fraternal Master",
"Albero: Bless Linen Cloth": "Shroud of Dreamt Sins",
"Albero: Bless Hatched Egg": "Three Gnarled Tongues"
}
dungeon_dict: Dict[str, str] = {
"Confessor Dungeon 1 extra": "Tears of Atonement (1000)",
"Confessor Dungeon 2 extra": "Heart of the Single Tone",
"Confessor Dungeon 3 extra": "Tears of Atonement (3000)",
"Confessor Dungeon 4 extra": "Embers of a Broken Star",
"Confessor Dungeon 5 extra": "Tears of Atonement (5000)",
"Confessor Dungeon 6 extra": "Scaly Coin",
"Confessor Dungeon 7 extra": "Seashell of the Inverted Spiral"
}
tirso_dict: Dict[str, str] = {
"Albero: Tirso's 1st reward": "Linen Cloth",
"Albero: Tirso's 2nd reward": "Tears of Atonement (500)",
"Albero: Tirso's 3rd reward": "Tears of Atonement (1000)",
"Albero: Tirso's 4th reward": "Tears of Atonement (2000)",
"Albero: Tirso's 5th reward": "Tears of Atonement (5000)",
"Albero: Tirso's 6th reward": "Tears of Atonement (10000)",
"Albero: Tirso's final reward": "Knot of Rosary Rope"
}
redento_dict: Dict[str, str] = {
"MoM: Redento's treasure": "Nail Uprooted from Dirt",
"MoM: Final meeting with Redento": "Knot of Rosary Rope",
"MotED: 1st meeting with Redento": "Fourth Toe made of Limestone",
"PotSS: 4th meeting with Redento": "Big Toe made of Limestone",
"WotBC: 3rd meeting with Redento": "Little Toe made of Limestone"
}
jocinero_dict: Dict[str, str] = {
"TSC: Jocinero's 1st reward": "Linen of Golden Thread",
"TSC: Jocinero's final reward": "Campanillero to the Sons of the Aurora"
}
altasgracias_dict: Dict[str, str] = {
"GA: Altasgracias' gift": "Egg of Deformity",
"GA: Empty giant egg": "Knot of Hair"
}
tentudia_dict: Dict[str, str] = {
"Albero: Lvdovico's 1st reward": "Tears of Atonement (500)",
"Albero: Lvdovico's 2nd reward": "Tears of Atonement (1000)",
"Albero: Lvdovico's 3rd reward": "Debla of the Lights"
}
gemino_dict: Dict[str, str] = {
"WOTW: Gift for the tomb": "Dried Flowers bathed in Tears",
"WOTW: Underground tomb": "Saeta Dolorosa",
"WOTW: Gemino's gift": "Empty Golden Thimble",
"WOTW: Gemino's reward": "Frozen Olive"
}
ossuary_dict: Dict[str, str] = {
"Ossuary: 1st reward": "Tears of Atonement (250)",
"Ossuary: 2nd reward": "Tears of Atonement (500)",
"Ossuary: 3rd reward": "Tears of Atonement (750)",
"Ossuary: 4th reward": "Tears of Atonement (1000)",
"Ossuary: 5th reward": "Tears of Atonement (1250)",
"Ossuary: 6th reward": "Tears of Atonement (1500)",
"Ossuary: 7th reward": "Tears of Atonement (1750)",
"Ossuary: 8th reward": "Tears of Atonement (2000)",
"Ossuary: 9th reward": "Tears of Atonement (2500)",
"Ossuary: 10th reward": "Tears of Atonement (3000)",
"Ossuary: 11th reward": "Tears of Atonement (5000)",
}
boss_dict: Dict[str, str] = {
"BotTC: Esdras, of the Anointed Legion": "Tears of Atonement (4300)",
"BotSS: Warden of the Silent Sorrow": "Tears of Atonement (300)",
"CoOLotCV: Our Lady of the Charred Visage": "Tears of Atonement (2600)",
"HotD: Laudes, the First of the Amanecidas": "Tears of Atonement (30000)",
"GotP: Amanecida of the Bejeweled Arrow": "Tears of Atonement (18000)",
"GA: Tres Angustias": "Tears of Atonement (2100)",
"MD: Ten Piedad": "Tears of Atonement (625)",
"MoM: Melquiades, The Exhumed Archbishop": "Tears of Atonement (5500)",
"MotED: Amanecida of the Golden Blades": "Tears of Atonement (18000)",
"MaH: Sierpes": "Tears of Atonement (5000)",
"PotSS: Amanecida of the Chiselled Steel": "Tears of Atonement (18000)",
"TSC: Exposito, Scion of Abjuration": "Tears of Atonement (9000)",
"WotHP: Quirce, Returned By The Flames": "Tears of Atonement (11250)",
"WotHP: Amanecida of the Molten Thorn": "Tears of Atonement (18000)"
}
wound_dict: Dict[str, str] = {
"CoOLotCV: Visage of Compunction": "Holy Wound of Compunction",
"GA: Visage of Contrition": "Holy Wound of Contrition",
"MD: Visage of Attrition": "Holy Wound of Attrition"
}
mask_dict: Dict[str, str] = {
"CoOLotCV: Mask room": "Mirrored Mask of Dolphos",
"LotNW: Mask room": "Embossed Mask of Crescente",
"MoM: Mask room": "Deformed Mask of Orestes"
}
eye_dict: Dict[str, str] = {
"Ossuary: Isidora, Voice of the Dead": "Severed Right Eye of the Traitor",
"MaH: Sierpes' eye": "Broken Left Eye of the Traitor"
}
herb_dict: Dict[str, str] = {
"Albero: Gate of Travel room": "Bouquet of Thyme",
"Jondo: Lower east bell trap": "Bouquet of Rosemary",
"MotED: Blood platform alcove": "Dried Clove",
"PotSS: Third area lower ledge": "Olive Seeds",
"TSC: Painting ladder ledge": "Sooty Garlic",
"WOTW: Entrance to tomb": "Incense Garlic"
}
church_dict: Dict[str, str] = {
"Albero: Donate 5000 Tears": "Token of Appreciation",
"Albero: Donate 50000 Tears": "Cloistered Ruby"
}
shop_dict: Dict[str, str] = {
"GotP: Shop item 1": "Torn Bridal Ribbon",
"GotP: Shop item 2": "Calcified Eye of Erudition",
"GotP: Shop item 3": "Ember of the Holy Cremation",
"MD: Shop item 1": "Key to the Chamber of the Eldest Brother",
"MD: Shop item 2": "Hollow Pearl",
"MD: Shop item 3": "Moss Preserved in Glass",
"TSC: Shop item 1": "Wicker Knot",
"TSC: Shop item 2": "Empty Bile Vessel",
"TSC: Shop item 3": "Key of the Inquisitor"
}
thorn_set: Set[str] = {
"THL: Deogracias' gift",
"Confessor Dungeon 1 main",
"Confessor Dungeon 2 main",
"Confessor Dungeon 3 main",
"Confessor Dungeon 4 main",
"Confessor Dungeon 5 main",
"Confessor Dungeon 6 main",
"Confessor Dungeon 7 main",
}
candle_dict: Dict[str, str] = {
"CoOLotCV: Red candle": "Bead of Red Wax",
"LotNW: Red candle": "Bead of Red Wax",
"MD: Red candle": "Bead of Red Wax",
"BotSS: Blue candle": "Bead of Blue Wax",
"CoOLotCV: Blue candle": "Bead of Blue Wax",
"MD: Blue candle": "Bead of Blue Wax"
}
skill_dict: Dict[str, str] = {
"Skill 1, Tier 1": "Combo Skill",
"Skill 1, Tier 2": "Combo Skill",
"Skill 1, Tier 3": "Combo Skill",
"Skill 2, Tier 1": "Charged Skill",
"Skill 2, Tier 2": "Charged Skill",
"Skill 2, Tier 3": "Charged Skill",
"Skill 3, Tier 1": "Ranged Skill",
"Skill 3, Tier 2": "Ranged Skill",
"Skill 3, Tier 3": "Ranged Skill",
"Skill 4, Tier 1": "Dive Skill",
"Skill 4, Tier 2": "Dive Skill",
"Skill 4, Tier 3": "Dive Skill",
"Skill 5, Tier 1": "Lunge Skill",
"Skill 5, Tier 2": "Lunge Skill",
"Skill 5, Tier 3": "Lunge Skill",
}

View File

@ -0,0 +1,413 @@
from typing import Dict, 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 .Rules import rules
from worlds.generic.Rules import set_rule
from .Options import blasphemous_options
from . import Vanilla
class BlasphemousWeb(WebWorld):
theme = "stone"
tutorials = [Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Blasphemous randomizer connected to an Archipelago Multiworld",
"English",
"setup_en.md",
"setup/en",
["TRPG"]
)]
class BlasphemousWorld(World):
"""
Blasphemous is a challenging Metroidvania set in the cursed land of Cvstodia. Play as the Penitent One, trapped
in an endless cycle of death and rebirth, and free the world from it's terrible fate in your quest to break
your eternal damnation!
"""
game: str = "Blasphemous"
web = BlasphemousWeb()
data_version: 1
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)}
location_name_to_game_id = {loc["name"]: loc["game_id"] for loc in location_table}
item_name_groups = group_table
option_definitions = blasphemous_options
def set_rules(self):
rules(self)
def create_item(self, name: str) -> "BlasphemousItem":
item_id: int = self.item_name_to_id[name]
id = item_id - base_id
return BlasphemousItem(name, item_table[id]["classification"], item_id, player=self.player)
def create_event(self, event: str):
return BlasphemousItem(event, ItemClassification.progression_skip_balancing, None, self.player)
def get_filler_item_name(self) -> str:
return self.multiworld.random.choice(tears_set)
def generate_basic(self):
placed_items = []
placed_items.extend(Vanilla.unrandomized_dict.values())
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 not self.multiworld.cherub_shuffle[self.player]:
for i in range(38):
placed_items.append("Child of Moonlight")
if not self.multiworld.life_shuffle[self.player]:
for i in range(6):
placed_items.append("Life Upgrade")
if not self.multiworld.fervour_shuffle[self.player]:
for i in range(6):
placed_items.append("Fervour Upgrade")
if not self.multiworld.sword_shuffle[self.player]:
for i in range(7):
placed_items.append("Mea Culpa Upgrade")
if not self.multiworld.blessing_shuffle[self.player]:
placed_items.extend(Vanilla.blessing_dict.values())
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 not self.multiworld.altasgracias_shuffle[self.player]:
placed_items.extend(Vanilla.altasgracias_dict.values())
if not self.multiworld.tentudia_shuffle[self.player]:
placed_items.extend(Vanilla.tentudia_dict.values())
if not self.multiworld.gemino_shuffle[self.player]:
placed_items.extend(Vanilla.gemino_dict.values())
if not self.multiworld.guilt_shuffle[self.player]:
placed_items.append("Weight of True Guilt")
if not self.multiworld.ossuary_shuffle[self.player]:
placed_items.extend(Vanilla.ossuary_dict.values())
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())
if not self.multiworld.mask_shuffle[self.player]:
placed_items.extend(Vanilla.mask_dict.values())
if not self.multiworld.eye_shuffle[self.player]:
placed_items.extend(Vanilla.eye_dict.values())
if not self.multiworld.herb_shuffle[self.player]:
placed_items.extend(Vanilla.herb_dict.values())
if not self.multiworld.church_shuffle[self.player]:
placed_items.extend(Vanilla.church_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:
for i in range(8):
placed_items.append("Thorn Upgrade")
if not self.multiworld.candle_shuffle[self.player]:
placed_items.extend(Vanilla.candle_dict.values())
if self.multiworld.start_wheel[self.player]:
placed_items.append("The Young Mason's Wheel")
if not self.multiworld.skill_randomizer[self.player]:
placed_items.extend(Vanilla.skill_dict.values())
counter = Counter(placed_items)
pool = []
for item in item_table:
count = item["count"] - counter[item["name"]]
if count <= 0:
continue
else:
for i in range(count):
pool.append(self.create_item(item["name"]))
self.multiworld.itempool += pool
def pre_fill(self):
self.place_items_from_dict(Vanilla.unrandomized_dict)
if not self.multiworld.cherub_shuffle[self.player]:
self.place_items_from_set(Vanilla.cherub_set, "Child of Moonlight")
if not self.multiworld.life_shuffle[self.player]:
self.place_items_from_set(Vanilla.life_set, "Life 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)\
.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 self.multiworld.thorn_shuffle[self.player] == 1:
self.multiworld.local_items[self.player].value.add("Thorn Upgrade")
def place_items_from_set(self, location_set: Set[str], name: str):
for loc in location_set:
self.multiworld.get_location(loc, self.player)\
.place_locked_item(self.create_item(name))
def place_items_from_dict(self, option_dict: Dict[str, str]):
for loc, item in option_dict.items():
self.multiworld.get_location(loc, self.player)\
.place_locked_item(self.create_item(item))
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)
if self.multiworld.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 \
state.has("Holy Wound of Abnegation", player))
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
def fill_slot_data(self) -> Dict[str, Any]:
slot_data: Dict[str, Any] = {}
locations = []
for loc in self.multiworld.get_filled_locations(self.player):
if loc.name == "His Holiness Escribar":
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]
}
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
}
}
slot_data = {
"locations": locations,
"cfg": config,
"ending": self.multiworld.ending[self.player].value,
"death_link": bool(self.multiworld.death_link[self.player].value)
}
return slot_data
class BlasphemousItem(Item):
game: str = "Blasphemous"
class BlasphemousLocation(Location):
game: str = "Blasphemous"

View File

@ -0,0 +1,64 @@
# Blasphemous
## Where is the settings page?
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a config file.
## What does randomization do to this game?
All items that appear on the ground are randomized, and there are options to randomize more of the game, such as stat upgrades, enemies, skills, and more.
In addition, there are other changes to the game that make it better optimized for a randomizer:
- Some items and enemies are never randomized.
- Teleportation between Prie Dieus can be unlocked from the beginning.
- New save files are created in True Torment mode (NG+), but enemies and bosses will use their regular attack & defense values. A penitence can be chosen if the option is enabled.
- Save files can not be ascended - the randomizer is meant to be completed in a single playthrough.
- The Ossuary will give a reward for every four bones collected.
- Side quests have been modified so that the items received from them cannot be missed.
- The Apodictic Heart of Mea Culpa can be unequipped.
- Dying with the Immaculate Bead is unnecessary, it is automatically upgraded to the Weight of True Guilt.
- If the option is enabled, the 34 corpses in game will have their messages changed to give hints about certain items and locations. The Shroud of Dreamt Sins is not required to hear them.
## What has been changed about the side quests?
Tirso:
- Tirso's helpers will never die. Herbs can be given to him at any time.
Gemino:
- Gemino will never freeze. The thimble can be given to him at any time.
Viridiana:
- Viridiana will never die. The player can ask for her assistance at all 5 boss fights, and she will still appear at the rooftops.
Redento:
- No changes.
Cleofas:
- The choice to end Socorro's suffering has been removed.
- Cleofas will not jump off the rooftops, even after talking to him without the Cord of the True Burying.
Crisanta / Ending C:
- The Incomplete Scapular will not skip the fight with Esdras. Instead, it is required to open the door to the church in Brotherhood of the Silent Sorrow.
- Perpetva's item from The Resting Place of the Sister is always accessible, even after defeating Esdras.
- Crisanta's gift in Brotherhood of the Silent Sorrow will always be the Holy Wound of Abnegation.
- When fighting Crisanta, it is no longer required to have the Apodictic Heart of Mea Culpa equipped to continue with Ending C, it just needs to be in the player's inventory.
## Which items and enemies are never randomized?
Items:
- Golden Thimble Filled with Burning Oil - from the fountain of burning oil in Convent of Our Lady of the Charred Visage
- Hatched Egg of Deformity - from the tree in Mountains of the Endless Dusk
- Chalice of Inverted Verses - from the statue in Desecrated Cistern
- Holy Wound of Abnegation - given by Crisanta in Brotherhood of the Silent Sorrow
Enemies:
- The Charging Knell in Mountains of the Endless Dusk
- The bell ringer in lower east Jondo
- The first Phalaris, Lionheart, and Sleepless Tomb in their respective areas (Chalice of Inverted Verses quest)
In addition, any enemies that appear in some kind of arena (such as the Confessor Dungeons, or the bridges in Archcathedral Rooftops or Grievance Ascends) will not be randomized to prevent softlocking.
## What does another world's item look like in Blasphemous?
Items retain their original appearance. You won't know if an item is for another player until you collect it. The only exception to this is the shops, where items that belong to other players are represented by the Archipelago logo.

View File

@ -0,0 +1,21 @@
# Blasphemous Multiworld Setup Guide
## Required Software
- Blasphemous from: [Steam](https://store.steampowered.com/app/774361/Blasphemous/)
- Blasphemous Modding API from: [GitHub](https://github.com/BrandenEK/Blasphemous-Modding-API)
- Blasphemous Randomizer from: [GitHub](https://github.com/BrandenEK/Blasphemous-Randomizer)
- Blasphemous Multiworld from: [GitHub](https://github.com/BrandenEK/Blasphemous-Multiworld)
## Instructions (Windows)
1. Download the [Modding API](https://github.com/BrandenEK/Blasphemous-Modding-API/releases), and follow the [installation instructions](https://github.com/BrandenEK/Blasphemous-Modding-API#installation) on the GitHub page.
2. After the Modding API has been installed, download the [Randomizer](https://github.com/BrandenEK/Blasphemous-Randomizer/releases) and [Multiworld](https://github.com/BrandenEK/Blasphemous-Multiworld/releases) archives, and extract the contents of both into the `Modding` folder.
3. Start Blasphemous. To verfy that the mods are working, look for a version number for both the Randomizer and Multiworld on the title screen.
## Connecting
To connect to an Archipelago server, open the in-game console by pressing backslash `\` and use the command `multiworld connect [address:port] [name] [password]`. The port and password are both optional - if no port is provided then the default port of 38281 is used.
**Make sure to connect to the server before attempting to start a new save file.**