From 96f25d15a94944bdc159f9c215090bcf139d1e44 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 7 Jun 2022 08:10:19 -0600 Subject: [PATCH 1/4] Settings code fix --- BaseClasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index 4b3986bf..1d90ed37 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2893,7 +2893,7 @@ class Settings(object): args.bombbag[p] = True if settings[8] & 0x2 else False args.shufflelinks[p] = True if settings[8] & 0x1 else False if len(settings) > 9: - args.restrict_boss_items[p] = r(rb_mode)[(settings[9] & 0x80) >> 6] + args.restrict_boss_items[p] = r(rb_mode)[(settings[9] & 0xC0) >> 6] args.algorithm = r(algo_mode)[(settings[9] & 0x38) >> 3] args.shufflebosses[p] = r(boss_mode)[(settings[9] & 0x07)] From e9433e56c076fa6d0a3b1aeb1e48a17a27cd6d79 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 7 Jun 2022 08:12:13 -0600 Subject: [PATCH 2/4] Missing player --- Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 836a164e..1c9d82a7 100644 --- a/Rom.py +++ b/Rom.py @@ -1274,7 +1274,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest if world.open_pyramid[player] or world.goal[player] == 'trinity': rom.initial_sram.pre_open_pyramid_hole() - if world.crystals_needed_for_gt == 0: + if world.crystals_needed_for_gt[player] == 0: rom.initial_sram.pre_open_ganons_tower() rom.write_byte(0xF5D73, 0xF0) # bees are catchable rom.write_byte(0xF5F10, 0xF0) # bees are catchable From 25905fe3f38a09511d2bf25e22f58a829d3d7ffc Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 14 Jun 2022 13:22:24 -0600 Subject: [PATCH 3/4] Capitalization fixes Highlight part of the text in red (experimental) --- Items.py | 78 ++++++++++++++++++++++++------------------------- Main.py | 2 +- RELEASENOTES.md | 5 ++++ Rom.py | 49 ++++++++++++++++++++----------- Text.py | 37 +++++++++++++++++++++-- 5 files changed, 112 insertions(+), 59 deletions(-) diff --git a/Items.py b/Items.py index 06e1dcec..90b518cb 100644 --- a/Items.py +++ b/Items.py @@ -23,51 +23,51 @@ def ItemFactory(items, player): # Format: Name: (Advancement, Priority, Type, ItemCode, BasePrice, Pedestal Hint Text, Pedestal Credit Text, Sick Kid Credit Text, Zora Credit Text, Witch Credit Text, Flute Boy Credit Text, Hint Text) -item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'the Bow'), - 'Progressive Bow': (True, False, None, 0x64, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), - 'Progressive Bow (Alt)': (True, False, None, 0x65, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), - 'Book of Mudora': (True, False, None, 0x1D, 150, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'), - 'Hammer': (True, False, None, 0x09, 250, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the Hammer'), +item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'the bow'), + 'Progressive Bow': (True, False, None, 0x64, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a bow'), + 'Progressive Bow (Alt)': (True, False, None, 0x65, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a bow'), + 'Book of Mudora': (True, False, None, 0x1D, 150, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the book'), + 'Hammer': (True, False, None, 0x09, 250, 'Stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the hammer'), 'Hookshot': (True, False, None, 0x0A, 250, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'), - 'Magic Mirror': (True, False, None, 0x1A, 250, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the Mirror'), - 'Ocarina': (True, False, None, 0x14, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'), - 'Pegasus Boots': (True, False, None, 0x4B, 250, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the Boots'), + 'Magic Mirror': (True, False, None, 0x1A, 250, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the mirror'), + 'Ocarina': (True, False, None, 0x14, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the flute'), + 'Pegasus Boots': (True, False, None, 0x4B, 250, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the boots'), 'Power Glove': (True, False, None, 0x1B, 100, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the Glove'), - 'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the Cape'), - 'Mushroom': (True, False, None, 0x29, 50, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the Mushroom'), - 'Shovel': (True, False, None, 0x13, 50, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the Shovel'), - 'Lamp': (True, False, None, 0x12, 150, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the Lamp'), - 'Magic Powder': (True, False, None, 0x0D, 50, 'you can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the Powder'), + 'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the cape'), + 'Mushroom': (True, False, None, 0x29, 50, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the mushroom'), + 'Shovel': (True, False, None, 0x13, 50, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the shovel'), + 'Lamp': (True, False, None, 0x12, 150, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the lamp'), + 'Magic Powder': (True, False, None, 0x0D, 50, 'you can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the powder'), 'Moon Pearl': (True, False, None, 0x1F, 200, ' Bunny Link\n be\n gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the Moon Pearl'), - 'Cane of Somaria': (True, False, None, 0x15, 250, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the red Cane'), + 'Cane of Somaria': (True, False, None, 0x15, 250, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the red cane'), 'Fire Rod': (True, False, None, 0x07, 250, 'I\'m the hot\nrod. I make\nthings burn!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the Fire Rod'), - 'Flippers': (True, False, None, 0x1E, 250, 'fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the Flippers'), + 'Flippers': (True, False, None, 0x1E, 250, 'Fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the flippers'), 'Ice Rod': (True, False, None, 0x08, 250, 'I\'m the cold\nrod. I make\nthings freeze!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the Ice Rod'), 'Titans Mitts': (True, False, None, 0x1C, 200, 'Now you can\nlift heavy\nstuff!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the Mitts'), 'Bombos': (True, False, None, 0x0F, 100, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'medallion boy melts room again', 'Bombos'), 'Ether': (True, False, None, 0x10, 100, 'This magic\ncoin freezes\neverything!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-coin', 'medallion boy sees floor again', 'Ether'), 'Quake': (True, False, None, 0x11, 100, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'medallion boy shakes dirt again', 'Quake'), - 'Bottle': (True, False, None, 0x16, 50, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'bottle boy has terrarium again', 'a Bottle'), - 'Bottle (Red Potion)': (True, False, None, 0x2B, 70, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a Bottle'), - 'Bottle (Green Potion)': (True, False, None, 0x2C, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a Bottle'), - 'Bottle (Blue Potion)': (True, False, None, 0x2D, 80, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a Bottle'), - 'Bottle (Fairy)': (True, False, None, 0x3D, 70, 'Save me and I will revive you', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'bottle boy has friend again', 'a Bottle'), - 'Bottle (Bee)': (True, False, None, 0x3C, 50, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a Bottle'), - 'Bottle (Good Bee)': (True, False, None, 0x48, 60, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has beetor again', 'a Bottle'), + 'Bottle': (True, False, None, 0x16, 50, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'bottle boy has terrarium again', 'a bottle'), + 'Bottle (Red Potion)': (True, False, None, 0x2B, 70, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a bottle'), + 'Bottle (Green Potion)': (True, False, None, 0x2C, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a bottle'), + 'Bottle (Blue Potion)': (True, False, None, 0x2D, 80, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a bottle'), + 'Bottle (Fairy)': (True, False, None, 0x3D, 70, 'Save me and I will revive you', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'bottle boy has friend again', 'a bottle'), + 'Bottle (Bee)': (True, False, None, 0x3C, 50, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bottle'), + 'Bottle (Good Bee)': (True, False, None, 0x48, 60, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has beetor again', 'a bottle'), 'Master Sword': (True, False, 'Sword', 0x50, 100, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'), 'Tempered Sword': (True, False, 'Sword', 0x02, 150, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'), 'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the small sword'), 'Sword and Shield': (True, False, 'Sword', 0x00, 'An uncle\nsword rests\nhere!', 'the sword and shield', 'sword and shield-wielding kid', 'training set for sale', 'fungus for training set', 'sword and shield boy fights again', 'the small sword and shield'), 'Golden Sword': (True, False, 'Sword', 0x03, 200, 'The butter\nsword rests\nhere!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'), - 'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'a better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a sword'), - 'Progressive Glove': (True, False, None, 0x61, 150, 'a way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'), + 'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'A better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a sword'), + 'Progressive Glove': (True, False, None, 0x61, 150, 'A way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'), 'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the silver arrows'), 'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x01, 0x08], 999, None, None, None, None, None, None, None), 'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02, 0x09], 999, None, None, None, None, None, None, None), 'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03, 0x0a], 999, None, None, None, None, None, None, None), 'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'), - 'Power Star': (True, False, None, 0x6B, 100, 'a small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), - 'Triforce Piece': (True, False, None, 0x6C, 100, 'a small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce Piece'), + 'Power Star': (True, False, None, 0x6B, 100, 'A small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), + 'Triforce Piece': (True, False, None, 0x6C, 100, 'A small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'), 'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x06, 0x01], 999, None, None, None, None, None, None, None), 'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x06, 0x02], 999, None, None, None, None, None, None, None), 'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x06, 0x03], 999, None, None, None, None, None, None, None), @@ -75,10 +75,10 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x06, 0x05], 999, None, None, None, None, None, None, None), 'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06, 0x06], 999, None, None, None, None, None, None, None), 'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x06, 0x07], 999, None, None, None, None, None, None, None), - 'Single Arrow': (False, False, None, 0x43, 3, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), + 'Single Arrow': (False, False, None, 0x43, 3, 'A lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), 'Arrows (10)': (False, False, None, 0x44, 30, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'), - 'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), - 'Arrow Upgrade (+5)': (False, False, None, 0x53, 100, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (+5)': (False, False, None, 0x53, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), 'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), 'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'), @@ -86,17 +86,17 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Chicken': (False, False, None, 0x5A, 999, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), 'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), 'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), - 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), - 'Bomb Upgrade (+5)': (False, False, None, 0x51, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), + 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), + 'Bomb Upgrade (+5)': (False, False, None, 0x51, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), 'Blue Mail': (False, True, None, 0x22, 50, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the blue mail'), 'Red Mail': (False, True, None, 0x23, 100, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the red mail'), - 'Progressive Armor': (False, True, None, 0x60, 50, 'time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), + 'Progressive Armor': (False, True, None, 0x60, 50, 'Time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), 'Blue Boomerang': (True, False, None, 0x0C, 50, 'No matter what\nyou do, blue\nreturns to you', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the blue boomerang'), 'Red Boomerang': (True, False, None, 0x2A, 50, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the red boomerang'), 'Blue Shield': (False, True, None, 0x04, 50, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a blue shield'), 'Red Shield': (False, True, None, 0x05, 500, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'a red shield'), 'Mirror Shield': (True, False, None, 0x06, 200, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the Mirror Shield'), - 'Progressive Shield': (True, False, None, 0x5F, 50, 'have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'), + 'Progressive Shield': (True, False, None, 0x5F, 50, 'Have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'), 'Bug Catching Net': (True, False, None, 0x21, 50, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the bug net'), 'Cane of Byrna': (True, False, None, 0x18, 50, 'Use this to\nbecome\ninvincible!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the blue Cane'), 'Boss Heart Container': (False, True, None, 0x3E, 40, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), @@ -108,12 +108,12 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Rupees (50)': (False, False, None, 0x41, 25, 'A rupee pile!\nOkay?', 'and the rupee pile', 'the well-off kid', 'life lesson for sale', 'buying okay drugs', 'destitute boy has dinner again', 'fifty rupees'), 'Rupees (100)': (False, False, None, 0x40, 50, 'A rupee stash!\nHell yeah!', 'and the rupee stash', 'the kind-of-rich kid', 'life lesson for sale', 'buying good drugs', 'affluent boy goes drinking again', 'one hundred rupees'), 'Rupees (300)': (False, False, None, 0x46, 150, 'A rupee hoard!\nHell yeah!', 'and the rupee hoard', 'the really-rich kid', 'life lesson for sale', 'buying the best drugs', 'fat-cat boy is rich again', 'three hundred rupees'), - 'Rupoor': (False, False, None, 0x59, 0, 'a debt collector', 'and the toll-booth', 'the toll-booth kid', 'double loss for sale', 'witch stole your rupees', 'affluent boy steals rupees', 'a rupoor'), - 'Red Clock': (False, True, None, 0x5B, 0, 'a waste of time', 'the ruby clock', 'the ruby-time kid', 'red time for sale', 'for ruby time', 'moment boy travels time again', 'a red clock'), - 'Blue Clock': (False, True, None, 0x5C, 50, 'a bit of time', 'the sapphire clock', 'sapphire-time kid', 'blue time for sale', 'for sapphire time', 'moment boy time travels again', 'a blue clock'), - 'Green Clock': (False, True, None, 0x5D, 200, 'a lot of time', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'), - 'Single RNG': (False, True, None, 0x62, 300, 'something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'), - 'Multi RNG': (False, True, None, 0x63, 100, 'something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'), + 'Rupoor': (False, False, None, 0x59, 0, 'A debt collector', 'and the toll-booth', 'the toll-booth kid', 'double loss for sale', 'witch stole your rupees', 'affluent boy steals rupees', 'a rupoor'), + 'Red Clock': (False, True, None, 0x5B, 0, 'A waste of time', 'the ruby clock', 'the ruby-time kid', 'red time for sale', 'for ruby time', 'moment boy travels time again', 'a red clock'), + 'Blue Clock': (False, True, None, 0x5C, 50, 'A bit of time', 'the sapphire clock', 'sapphire-time kid', 'blue time for sale', 'for sapphire time', 'moment boy time travels again', 'a blue clock'), + 'Green Clock': (False, True, None, 0x5D, 200, 'A lot of time', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'), + 'Single RNG': (False, True, None, 0x62, 300, 'Something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'), + 'Multi RNG': (False, True, None, 0x63, 100, 'Something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'), 'Magic Upgrade (1/2)': (True, False, None, 0x4E, 50, 'Your magic\npower has been\ndoubled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Half Magic'), # can be required to beat mothula in an open seed in very very rare circumstance 'Magic Upgrade (1/4)': (True, False, None, 0x4F, 100, 'Your magic\npower has been\nquadrupled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Quarter Magic'), # can be required to beat mothula in an open seed in very very rare circumstance 'Small Key (Eastern Palace)': (False, False, 'SmallKey', 0xA2, 40, 'A small key to Armos Knights', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Eastern Palace'), diff --git a/Main.py b/Main.py index f3dcb0ee..174a2568 100644 --- a/Main.py +++ b/Main.py @@ -31,7 +31,7 @@ from Utils import output_path, parse_player_names from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config from source.tools.BPS import create_bps_from_data -__version__ = '1.0.2.4-v' +__version__ = '1.0.2.5-v' from source.classes.BabelFish import BabelFish diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 31d1e10e..f120a32c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -157,6 +157,11 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o #### Volatile +* 1.0.2.5 + * Some textual changes for hints (capitalization standardization) + * Item will be highlighted in red if experimental is on + * Bug with 0 GT crystals not opening GT + * Settings code fix * 1.0.2.4 * Updated tourney winners (included Doors Async League winners) * Fixed a couple issues with dungeon counters and the DungeonCompletion field for autotracking diff --git a/Rom.py b/Rom.py index 1c9d82a7..f4bdd02c 100644 --- a/Rom.py +++ b/Rom.py @@ -15,7 +15,7 @@ try: except ImportError: raise Exception('Could not load BPS module') -from BaseClasses import ShopType, Region, Location, Door, DoorType, RegionType, LocationType +from BaseClasses import ShopType, Region, Location, Door, DoorType, RegionType, LocationType, Item from DoorShuffle import compass_data, DROptions, boss_indicator, dungeon_portals from Dungeons import dungeon_music_addresses, dungeon_table from Regions import location_table, shop_to_location_table, retro_shops @@ -1975,6 +1975,8 @@ def write_strings(rom, world, player, team): else: if isinstance(dest, Region) and dest.type == RegionType.Dungeon and dest.dungeon: hint = dest.dungeon.name + elif isinstance(dest, Item) and world.experimental[player]: + hint = f'{{C:RED}}{dest.hint_text}{{C:WHITE}}' if dest.hint_text else 'something' else: hint = dest.hint_text if dest.hint_text else "something" if dest.player != player: @@ -1994,7 +1996,7 @@ def write_strings(rom, world, player, team): all_entrances = [entrance for entrance in world.get_entrances() if entrance.player == player] random.shuffle(all_entrances) - #First we take care of the one inconvenient dungeon in the appropriately simple shuffles. + # First we take care of the one inconvenient dungeon in the appropriately simple shuffles. entrances_to_hint = {} entrances_to_hint.update(InconvenientDungeonEntrances) if world.shuffle_ganon: @@ -2009,7 +2011,7 @@ def write_strings(rom, world, player, team): tt[hint_locations.pop(0)] = this_hint entrances_to_hint = {} break - #Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones. + # Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones. entrances_to_hint.update(InconvenientOtherEntrances) if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: hint_count = 0 @@ -2027,7 +2029,7 @@ def write_strings(rom, world, player, team): else: break - #Next we handle hints for randomly selected other entrances, curating the selection intelligently based on shuffle. + # Next we handle hints for randomly selected other entrances, curating the selection intelligently based on shuffle. if world.shuffle[player] not in ['simple', 'restricted', 'restricted_legacy']: entrances_to_hint.update(ConnectorEntrances) entrances_to_hint.update(DungeonEntrances) @@ -2082,7 +2084,7 @@ def write_strings(rom, world, player, team): else: second_item = hint_text(world.get_location('Swamp Palace - West Chest', player).item) first_item = hint_text(world.get_location('Swamp Palace - Big Key Chest', player).item) - this_hint = ('The westmost chests in Swamp Palace contain ' + first_item + ' and ' + second_item + '.') + this_hint = f'The westmost chests in Swamp Palace contain {first_item} and {second_item}.' tt[hint_locations.pop(0)] = this_hint elif location == 'Mire Left': if random.randint(0, 1) == 0: @@ -2091,34 +2093,43 @@ def write_strings(rom, world, player, team): else: second_item = hint_text(world.get_location('Misery Mire - Compass Chest', player).item) first_item = hint_text(world.get_location('Misery Mire - Big Key Chest', player).item) - this_hint = ('The westmost chests in Misery Mire contain ' + first_item + ' and ' + second_item + '.') + this_hint = f'The westmost chests in Misery Mire contain {first_item} and {second_item}.' tt[hint_locations.pop(0)] = this_hint elif location == 'Tower of Hera - Big Key Chest': - this_hint = 'Waiting in the Tower of Hera basement leads to ' + hint_text(world.get_location(location, player).item) + '.' + item = hint_text(world.get_location(location, player).item) + this_hint = f'Waiting in the Tower of Hera basement leads to {item}.' tt[hint_locations.pop(0)] = this_hint elif location == 'Ganons Tower - Big Chest': - this_hint = 'The big chest in Ganon\'s Tower contains ' + hint_text(world.get_location(location, player).item) + '.' + item = hint_text(world.get_location(location, player).item) + this_hint = f'The big chest in Ganon\'s Tower contains {item}.' tt[hint_locations.pop(0)] = this_hint elif location == 'Thieves\' Town - Big Chest': - this_hint = 'The big chest in Thieves\' Town contains ' + hint_text(world.get_location(location, player).item) + '.' + item = hint_text(world.get_location(location, player).item) + this_hint = f'The big chest in Thieves\' Town contains {item}.' tt[hint_locations.pop(0)] = this_hint elif location == 'Ice Palace - Big Chest': - this_hint = 'The big chest in Ice Palace contains ' + hint_text(world.get_location(location, player).item) + '.' + item = hint_text(world.get_location(location, player).item) + this_hint = f'The big chest in Ice Palace contains {item}.' tt[hint_locations.pop(0)] = this_hint elif location == 'Eastern Palace - Big Key Chest': - this_hint = 'The antifairy guarded chest in Eastern Palace contains ' + hint_text(world.get_location(location, player).item) + '.' + item = hint_text(world.get_location(location, player).item) + this_hint = f'The antifairy guarded chest in Eastern Palace contains {item}.' tt[hint_locations.pop(0)] = this_hint elif location == 'Sahasrahla': - this_hint = 'Sahasrahla seeks a green pendant for ' + hint_text(world.get_location(location, player).item) + '.' + item = hint_text(world.get_location(location, player).item) + this_hint = f'Sahasrahla seeks a green pendant for {item}.' tt[hint_locations.pop(0)] = this_hint elif location == 'Graveyard Cave': - this_hint = 'The cave north of the graveyard contains ' + hint_text(world.get_location(location, player).item) + '.' + item = hint_text(world.get_location(location, player).item) + this_hint = f'The cave north of the graveyard contains {item}.' tt[hint_locations.pop(0)] = this_hint else: - this_hint = location + ' contains ' + hint_text(world.get_location(location, player).item) + '.' + this_hint = f'{location} contains {hint_text(world.get_location(location, player).item)}.' tt[hint_locations.pop(0)] = this_hint - # Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well. + # Lastly we write hints to show where certain interesting items are. + # It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless + # of how many exist. This supports many settings well. items_to_hint = RelevantItems.copy() if world.keyshuffle[player]: items_to_hint.extend(SmallKeys) @@ -2132,7 +2143,10 @@ def write_strings(rom, world, player, team): this_location = world.find_items_not_key_only(this_item, player) random.shuffle(this_location) if this_location: - this_hint = this_location[0].item.hint_text + ' can be found ' + hint_text(this_location[0]) + '.' + item_name = this_location[0].item.hint_text + item_name = item_name[0].upper() + item_name[1:] + item_format = f'{{C:RED}}{item_name}{{C:WHITE}}' if world.experimental[player] else item_name + this_hint = f'{item_format} can be found {hint_text(this_location[0])}.' tt[hint_locations.pop(0)] = this_hint hint_count -= 1 @@ -2186,7 +2200,8 @@ def write_strings(rom, world, player, team): elif hint_type == 'path': if item_count == 1: the_item = text_for_item(next(iter(choice_set)), world, player, team) - hint_candidates.append((hint_type, f'{name} conceals {the_item}')) + item_format = f'{{C:RED}}{the_item}{{C:WHITE}}' if world.experimental[player] else the_item + hint_candidates.append((hint_type, f'{name} conceals only {item_format}')) else: hint_candidates.append((hint_type, f'{name} conceals {item_count} {item_type} items')) district_hints = min(len(hint_candidates), len(hint_locations)) diff --git a/Text.py b/Text.py index 07b076f9..4da86b4c 100644 --- a/Text.py +++ b/Text.py @@ -1,6 +1,7 @@ # -*- coding: UTF-8 -*- from collections import OrderedDict import logging +import re text_addresses = {'Pedestal': (0x180300, 256), 'Triforce': (0x180400, 256), @@ -624,6 +625,12 @@ class MultiByteCoreTextMapper(object): "{IBOX}": [0x6B, 0x02, 0x77, 0x07, 0x7A, 0x03], "{C:GREEN}": [0x77, 0x07], "{C:YELLOW}": [0x77, 0x02], + "{C:WHITE}": [0x77, 0x06], + "{C:INV_WHITE}": [0x77, 0x16], + "{C:INV_YELLOW}": [0x77, 0x12], + "{C:INV_GREEN}": [0x77, 0x17], + "{C:RED}": [0x77, 0x01], + "{C:INV_RED}": [0x77, 0x11], } @classmethod @@ -637,7 +644,9 @@ class MultiByteCoreTextMapper(object): while lines: linespace = wrap line = lines.pop(0) - if line.startswith('{'): + + match = re.search('^\{[A-Z0-9_:]+\}$', line) + if match: if line == '{PAGEBREAK}': if lineindex % 3 != 0: # insert a wait for keypress, unless we just did so @@ -654,10 +663,27 @@ class MultiByteCoreTextMapper(object): pending_space = False while words: word = words.pop(0) + + match = re.search('^(\{[A-Z0-9_:]+\}).*', word) + if match: + start_command = match.group(1) + outbuf.extend(cls.special_commands[start_command]) + word = word.replace(start_command, '') + + match = re.search('(\{[A-Z0-9_:]+\})\.?$', word) + if match: + end_command = match.group(1) + word = word.replace(end_command, '') + period = word.endswith('.') + else: + end_command, period = None, False + # sanity check: if the word we have is more than 19 characters, # we take as much as we can still fit and push the rest back for later if cls.wordlen(word) > wrap: (word_first, word_rest) = cls.splitword(word, linespace) + if end_command: + word_rest = (word_rest[:-1] + end_command + '.') if period else (word_rest + end_command) words.insert(0, word_rest) lines.insert(0, ' '.join(words)) @@ -670,9 +696,16 @@ class MultiByteCoreTextMapper(object): if cls.wordlen(word) < linespace: pending_space = True linespace -= cls.wordlen(word) + 1 if pending_space else 0 - outbuf.extend(RawMBTextMapper.convert(word)) + word_to_map = word[:-1] if period else word + outbuf.extend(RawMBTextMapper.convert(word_to_map)) + if end_command: + outbuf.extend(cls.special_commands[end_command]) + if period: + outbuf.extend(RawMBTextMapper.convert('.')) else: # ran out of space, push word and lines back and continue with next line + if end_command: + word = (word[:-1] + end_command + '.') if period else (word + end_command) words.insert(0, word) lines.insert(0, ' '.join(words)) break From 32aebb2ad141a11efca033b45206b1551c87e599 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 15 Jun 2022 16:55:20 -0600 Subject: [PATCH 4/4] Pottery fix for item counting because of shop conflict --- RELEASENOTES.md | 1 + Rom.py | 2 +- data/base2current.bps | Bin 93021 -> 93041 bytes 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f120a32c..a27c05d0 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -162,6 +162,7 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o * Item will be highlighted in red if experimental is on * Bug with 0 GT crystals not opening GT * Settings code fix + * Fix for pottery not counting items in certain caves that share a supertile with shops * 1.0.2.4 * Updated tourney winners (included Doors Async League winners) * Fixed a couple issues with dungeon counters and the DungeonCompletion field for autotracking diff --git a/Rom.py b/Rom.py index f4bdd02c..c64c7491 100644 --- a/Rom.py +++ b/Rom.py @@ -37,7 +37,7 @@ from source.dungeon.RoomList import Room0127 JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '01166fb16b38b49ef79acc9993dc4f02' +RANDOMIZERBASEHASH = 'e33204b6023f07025eba16874308f57d' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index bd6417a47ef665eabc70d7f6055e50cdcb4562a4..fc22d9c3956780075f6752188bd52fca2ae6c189 100644 GIT binary patch delta 3217 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 LP#1RGTsixHG@5?( delta 3280 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 fWKz-Q^6O6{O4j?rcrZGc