diff --git a/WebHostLib/static/assets/gameInfo/Super Mario 64 EX.md b/WebHostLib/static/assets/gameInfo/Super Mario 64 EX.md new file mode 100644 index 00000000..73014101 --- /dev/null +++ b/WebHostLib/static/assets/gameInfo/Super Mario 64 EX.md @@ -0,0 +1,28 @@ +# Super Mario 64 EX + +## Where is the settings page? + +The player settings page for this game contains all the options you need to configure and export a config file. Player +settings page link: [SM64EX Player Settings Page](../player-settings). + +## What does randomization do to this game? +All 120 Stars, the 3 Cap Switches, the Cellar and Secound Floor Key are now Location Checks and may contain Items for different games as well +as different Items from within SM64. + + +## What is the goal of SM64EX when randomized? +As in most Mario Games, save the Princess! + +## Which items can be in another player's world? +Any of the 120 Stars, and the two Caste Keys. Additionally, Cap Switches are also considered "Items" and the "!"-Boxes will only be active +when someone collects the corresponding Cap Switch Item. + +## What does another world's item look like in SM64EX? +The Items are visually unchanged, though after collecting a Message will pop up to inform you what you collected, +and who will receive it. + +## When the player receives an item, what happens? +When you receive an Item, a Message will pop up to inform you where you received the Item from, +and which one it is. + +NOTE: The Secret Star count in the Menu is broken. \ No newline at end of file diff --git a/WebHostLib/static/assets/tutorial/sm64ex/setup_en.md b/WebHostLib/static/assets/tutorial/sm64ex/setup_en.md new file mode 100644 index 00000000..8a1d487e --- /dev/null +++ b/WebHostLib/static/assets/tutorial/sm64ex/setup_en.md @@ -0,0 +1,79 @@ +# Super Mario 64 EX MultiWorld Setup Guide + +## Required Software + +- Super Mario 64 US Rom (Japanese may work also. Europe and Shindou not supported) +- Either of [sm64pclauncher](https://github.com/N00byKing/sm64pclauncher/actions/workflows/ci.yml?query=branch%3Aarchipelago+event%3Apush) or +- Cloning and building [sm64ex](https://github.com/N00byKing/sm64ex) manually. + +NOTE: The above linked sm64pclauncher is a special version designed to work with the Archipelago build of sm64ex. +You can use other sm64-port based builds with it, but you can't use a different launcher with the Archipelago build of sm64ex. + +## Installation and Game Start Procedures + +# Installation via sm64pclauncher (For Windows) + +First, install [MSYS](https://www.msys2.org/) as described on the page. +Then follow the steps below + +1. Go to the page linked for sm64pclauncher, and press on the topmost entry +3. Scroll down, and download the zip file +4. Unpack the zip file in an empty folder +5. Run the Launcher and press build. +6. Set the location where you installed MSYS when prompted. Check the "Install Dependencies" Checkbox +7. Set the Repo link to `https://github.com/N00byKing/sm64ex` and the Branch to `archipelago` (Top two boxes). You can choose the folder (Secound Box) at will, as long as it does not exist yet +8. Point the Launcher to your Super Mario 64 US/JP Rom, and set the Region correspondingly +9. Set Build Options. Recommended: `-jn` where `n` is the Number of CPU Cores, to build faster. +10. SM64EX will now be compiled. The Launcher will appear to have crashed, but this is not likely the case. Best wait a bit, but there may be a problem if it takes longer than 10 Minutes + +After it's done, the Build list should have another entry titled with what you named the folder in step 7. + +NOTE: For some reason first start of the game always crashes the launcher. Just restart it. +If it still crashes, recheck if you typed the launch options correctly (Described in "Joining a MultiWorld Game") + +# Manual Compilation (Linux/Windows) + +Dependencies for Linux: `sdl2 glew cmake python make`. +Dependencies for Windows: `mingw-w64-x86_64-gcc mingw-w64-x86_64-glew mingw-w64-x86_64-SDL2 git make python3 cmake` +SM64EX will link `jsoncpp` dynamic if installed. If not, it will compile and link statically. + +1. Clone `https://github.com/N00byKing/sm64ex` recursively +2. Enter `sm64ex` and copy your Rom to `baserom.REGION.z64` where `REGION` is either `us` or `jp` respectively. +3. Compile with `make`. For faster compilation set the parameter `-jn` where `n` is the Number of CPU Cores. + +The Compiled binary will be in `build/REGION_pc/`. + +# Joining a MultiWorld Game + +To join, set the following launch options: `--sm64ap_name YourName --sm64ap_ip ServerIP:Port`. +Optionally, add `--sm64ap_passwd "YourPassword"` if the room you are using requires a password. All parameters without quotation marks. +The Name in this case is the one specified in your generated .yaml file. +In case you are using the Archipelago Website, the IP should be `archipelago.gg` and Port `38281`. + +If everything worked out, you will see a textbox informing you the connection has been established after the story intro. + +## Installation Troubleshooting + +Start the game from the command line to view helpful messages regarding SM64EX. + +### Game doesn't start after compiling + +Most likely you forgot to set the launch options. `--sm64ap_name YourName` and `--sm64ap_ip ServerIP:Port` are required for startup. + +## Game Troubleshooting + +### Known Issues + +When using a US Rom, the In-Game messages are missing some letters: `J Q V X Z` and `?`. +The Japanese Version should have no problem displaying these. + +### What happens if I lose connection? + +SM64EX tries to reconnect a few times, so be patient. +Should the problem still be there after about a minute or two, just save and restart the game. + +### How do I update the Game to a new Build? + +When manually compiling just pull in changes and run `make` again. Sometimes it helps to run `make clean` before. + +When using the Launcher follow the normal build steps, but when choosing a folder name use the same as before. Then continue as normal. \ No newline at end of file diff --git a/WebHostLib/static/assets/tutorial/tutorials.json b/WebHostLib/static/assets/tutorial/tutorials.json index 589d2215..ddd594da 100644 --- a/WebHostLib/static/assets/tutorial/tutorials.json +++ b/WebHostLib/static/assets/tutorial/tutorials.json @@ -442,6 +442,25 @@ } ] }, + { + "gameTitle": "Super Mario 64 EX", + "tutorials": [ + { + "name": "Multiworld Setup Guide", + "description": "A guide to setting up SM64EX for MultiWorld.", + "files": [ + { + "language": "English", + "filename": "sm64ex/setup_en.md", + "link": "sm64ex/setup/en", + "authors": [ + "N00byKing" + ] + } + ] + } + ] + }, { "gameTitle": "VVVVVV", "tutorials": [ diff --git a/worlds/sm64ex/Items.py b/worlds/sm64ex/Items.py new file mode 100644 index 00000000..ef71ea92 --- /dev/null +++ b/worlds/sm64ex/Items.py @@ -0,0 +1,13 @@ +from BaseClasses import Item + +class SM64Item(Item): + game: str = "Super Mario 64" + +item_table = { + "Star": 3626000, + "Cellar Key": 3626178, + "Second Floor Key": 3626179, + "Wing Cap": 3626180, + "Metal Cap": 3626181, + "Vanish Cap": 3626182 +} \ No newline at end of file diff --git a/worlds/sm64ex/Locations.py b/worlds/sm64ex/Locations.py new file mode 100644 index 00000000..dac323f6 --- /dev/null +++ b/worlds/sm64ex/Locations.py @@ -0,0 +1,211 @@ +from BaseClasses import Location + +class SM64Location(Location): + game: str = "Super Mario 64" + +#Bob-omb Battlefield +locBoB_table = { + "Big Bob-Omb on the Summit": 3626000, + "Footrace with Koopa The Quick": 3626001, + "Shoot to the Island in the Sky": 3626002, + "Find the 8 Red Coins": 3626003, + "Mario Wings to the Sky": 3626004, + "Behind Chain Chomps Gate": 3626005 +} + +#Whomp's Fortress +locWhomp_table = { + "Chip Off Whomp's Block": 3626007, + "To the Top of the Fortress": 3626008, + "Shoot into the Wild Blue": 3626009, + "Red Coins on the Floating Isle": 3626010, + "Fall onto the Caged Island": 3626011, + "Blast Away the Wall": 3626012 +} + +#Jolly Roger Bay +locJRB_table = { + "Plunder in the Sunken Ship": 3626014, + "Can the Eel Come Out to Play?": 3626015, + "Treasure of the Ocean Cave": 3626016, + "Red Coins on the Ship Afloat": 3626017, + "Blast to the Stone Pillar": 3626018, + "JRB: Through the Jet Stream": 3626019 # Prefix due to duplicate name +} + + +#Cool, Cool Mountain +locCCM_table = { + "Slip Slidin' Away": 3626021, + "Li'l Penguin Lost": 3626022, + "Big Penguin Race": 3626023, + "Frosty Slide for 8 Red Coins": 3626024, + "Snowman's Lost His Head": 3626025, + "Wall Kicks Will Work": 3626026 +} + +#Big Boo's Haunt +locBBH_table = { + "Go on a Ghost Hunt": 3626028, + "Ride Big Boo's Merry-Go-Round": 3626029, + "Secret of the Haunted Books": 3626030, + "Seek the 8 Red Coins": 3626031, + "Big Boo's Balcony": 3626032, + "Eye to Eye in the Secret Room": 3626033 +} + +#Hazy Maze Cave +locHMC_table = { + "Swimming Beast in the Cavern": 3626035, + "Elevate for 8 Red Coins": 3626036, + "Metal-Head Mario Can Move!": 3626037, + "Navigating the Toxic Maze": 3626038, + "A-Maze-Ing Emergency Exit": 3626039, + "Watch for Rolling Rocks": 3626040 +} + +#Lethal Lava Land +locLLL_table = { + "Boil the Big Bully": 3626042, + "Bully the Bullies": 3626043, + "8-Coin Puzzle with 15 Pieces": 3626044, + "Red-Hot Log Rolling": 3626045, + "Hot-Foot-It into the Volcano": 3626046, + "Elevator Tour in the Volcano": 3626047 +} + +#Shifting Sand Land +locSSL_table = { + "In the Talons of the Big Bird": 3626049, + "Shining Atop the Pyramid": 3626050, + "Inside the Ancient Pyramid": 3626051, + "Stand Tall on the Four Pillars": 3626052, + "Free Flying for 8 Red Coins": 3626053, + "Pyramid Puzzle": 3626054 +} + +#Dire, Dire Docks +locDDD_table = { + "Board Bowser's Sub": 3626056, + "Chests in the Current": 3626057, + "Pole-Jumping for Red Coins": 3626058, + "DDD: Through the Jet Stream": 3626059, # Prefix due to duplicate name + "The Manta Ray's Reward": 3626060, + "Collect the Caps...": 3626061 +} + +#Snowman's Land +locSL_table = { + "Snowman's Big Head": 3626063, + "Chill with the Bully": 3626064, + "In the Deep Freeze": 3626065, + "Whirl from the Freezing Pond": 3626066, + "Shell Shreddin' for Red Coins": 3626067, + "Into the Igloo": 3626068 +} + +#Wet-Dry World +locWDW_table = { + "Shocking Arrow Lifts!": 3626070, + "Top o' the Town": 3626071, + "Secrets in the Shallows & Sky": 3626072, + "Express Elevator--Hurry Up!": 3626073, + "Go to Town for Red Coins": 3626074, + "Quick Race Through Downtown!": 3626075 +} + +#Tall, Tall Mountain +locTTM_table = { + "Scale the Mountain": 3626077, + "Mystery of the Monkey Cage": 3626078, + "Scary 'Shrooms, Red Coins": 3626079, + "Mysterious Mountainside": 3626080, + "Breathtaking View from Bridge": 3626081, + "Blast to the Lonely Mushroom": 3626082 +} + +#Tiny-Huge Island +locTHI_table = { + "Pluck the Piranha Flower": 3626084, + "The Tip Top of the Huge Island": 3626085, + "Rematch with Koopa the Quick": 3626086, + "Five Itty Bitty Secrets": 3626087, + "Wiggler's Red Coins": 3626088, + "Make Wiggler Squirm": 3626089 +} + +#Tick Tock Clock +locTTC_table = { + "Roll into the Cage": 3626091, + "The Pit and the Pendulums": 3626092, + "Get a Hand": 3626093, + "Stomp on the Thwomp": 3626094, + "Timed Jumps on Moving Bars": 3626095, + "Stop Time for Red Coins": 3626096 +} + +#Rainbow Ride +locRR_table = { + "Cruiser Crossing the Rainbow": 3626098, + "The Big House in the Sky": 3626099, + "Coins Amassed in a Maze": 3626100, + "Swingin' in the Breeze": 3626101, + "Tricky Triangles!": 3626102, + "Somewhere Over the Rainbow": 3626103 +} + +loc100Coin_table = { + "BoB 100 Coins": 3626006, + "Whomp 100 Coins": 3626013, + "JRB 100 Coins": 3626020, + "CCM 100 Coins": 3626027, + "BBH 100 Coins": 3626034, + "HMC 100 Coins": 3626041, + "LLL 100 Coins": 3626048, + "SSL 100 Coins": 3626055, + "DDD 100 Coins": 3626062, + "SL 100 Coins": 3626069, + "WDW 100 Coins": 3626076, + "TTM 100 Coins": 3626083, + "THI 100 Coins": 3626090, + "TTC 100 Coins": 3626097, + "RR 100 Coins": 3626104 +} + +#Secret Stars and Stages +locSS_table = { + "Bowser in the Dark World Red Coins": 3626105, + "Bowser in the Fire Sea Red Coins": 3626112, + "Bowser in the Sky Red Coins": 3626119, + "The Princess's Secret Slide Box": 3626126, + "The Princess's Secret Slide Fast": 3626127, + "Cavern of the Metal Cap Red Coins": 3626133, + "Tower of the Wing Cap Red Coins": 3626140, + "Vanish Cap Under the Moat Red Coins": 3626147, + "Wing Mario Over the Rainbow Red Coins": 3626154, + "The Secret Aquarium": 3626161, + "Toad (Cellar)": 3626168, + "Toad (Second Floor)": 3626169, + "Toad (Third Floor)": 3626170, + "MIPS 1": 3626171, + "MIPS 2": 3626172 +} + +#Keys +locKey_table = { + "Cellar Key": 3626178, + "Second Floor Key": 3626179 +} + +#Caps +locCap_table = { + "Wing Cap Switch": 3626180, + "Metal Cap Switch": 3626181, + "Vanish Cap Switch": 3626182 +} + +# Correspond to 3626000 + course index * 7 + star index, then secret stars, then keys, then 100 Coin Stars +location_table = {**locBoB_table,**locWhomp_table,**locJRB_table,**locCCM_table,**locBBH_table, \ + **locHMC_table,**locLLL_table,**locSSL_table,**locDDD_table,**locSL_table, \ + **locWDW_table,**locTTM_table,**locTHI_table,**locTTC_table,**locRR_table, \ + **loc100Coin_table,**locSS_table,**locKey_table,**locCap_table} \ No newline at end of file diff --git a/worlds/sm64ex/Options.py b/worlds/sm64ex/Options.py new file mode 100644 index 00000000..d54f57f3 --- /dev/null +++ b/worlds/sm64ex/Options.py @@ -0,0 +1,22 @@ +import typing +from Options import Option, DefaultOnToggle, Range + +class EnableCoinStars(DefaultOnToggle): + """Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything""" + displayname = "Enable 100 Coin Stars" + +class StrictCapRequirements(DefaultOnToggle): + """If disabled, Stars that expect special caps may have to be acquired without the caps""" + displayname = "Strict Cap Requirements" + +class StarsToFinish(Range): + """How many stars are required at the infinite stairs""" + range_start = 50 + range_end = 100 + default = 70 + +sm64_options: typing.Dict[str,type(Option)] = { + "EnableCoinStars": EnableCoinStars, + "StrictCapRequirements": StrictCapRequirements, + "StarsToFinish": StarsToFinish +} \ No newline at end of file diff --git a/worlds/sm64ex/Regions.py b/worlds/sm64ex/Regions.py new file mode 100644 index 00000000..43a74b4c --- /dev/null +++ b/worlds/sm64ex/Regions.py @@ -0,0 +1,140 @@ +import typing +from BaseClasses import MultiWorld, Region, Entrance, Location, RegionType +from .Locations import SM64Location, location_table,locBoB_table,locWhomp_table,locJRB_table,locCCM_table,locBBH_table, \ + locHMC_table,locLLL_table,locSSL_table,locDDD_table,locSL_table, \ + locWDW_table,locTTM_table,locTHI_table,locTTC_table,locRR_table, \ + locSS_table, locKey_table, locCap_table + +def create_regions(world: MultiWorld, player: int): + + regSS = Region("Menu", RegionType.Generic, "Castle Area", player, world) + locSS_names = [name for name, id in locSS_table.items()] + locSS_names += [name for name, id in locKey_table.items()] + locSS_names += [name for name, id in locCap_table.items()] + regSS.locations += [SM64Location(player, loc_name, location_table[loc_name], regSS) for loc_name in locSS_names] + world.regions.append(regSS) + + regBoB = Region("Bob-omb Battlefield", RegionType.Generic, "Bob-omb Battlefield", player, world) + locBoB_names = [name for name, id in locBoB_table.items()] + regBoB.locations += [SM64Location(player, loc_name, location_table[loc_name], regBoB) for loc_name in locBoB_names] + if (world.EnableCoinStars[player].value): + regBoB.locations.append(SM64Location(player, "BoB 100 Coins", location_table["BoB 100 Coins"], regBoB)) + world.regions.append(regBoB) + + regWhomp = Region("Whomp's Fortress", RegionType.Generic, "Whomp's Fortress", player, world) + locWhomp_names = [name for name, id in locWhomp_table.items()] + regWhomp.locations += [SM64Location(player, loc_name, location_table[loc_name], regWhomp) for loc_name in locWhomp_names] + if (world.EnableCoinStars[player].value): + regWhomp.locations.append(SM64Location(player, "Whomp 100 Coins", location_table["Whomp 100 Coins"], regWhomp)) + world.regions.append(regWhomp) + + regJRB = Region("Jolly Roger Bay", RegionType.Generic, "Jolly Roger Bay", player, world) + locJRB_names = [name for name, id in locJRB_table.items()] + regJRB.locations += [SM64Location(player, loc_name, location_table[loc_name], regJRB) for loc_name in locJRB_names] + if (world.EnableCoinStars[player].value): + regJRB.locations.append(SM64Location(player, "JRB 100 Coins", location_table["JRB 100 Coins"], regJRB)) + world.regions.append(regJRB) + + regCCM = Region("Cool, Cool Mountain", RegionType.Generic, "Cool, Cool Mountain", player, world) + locCCM_names = [name for name, id in locCCM_table.items()] + regCCM.locations += [SM64Location(player, loc_name, location_table[loc_name], regCCM) for loc_name in locCCM_names] + if (world.EnableCoinStars[player].value): + regCCM.locations.append(SM64Location(player, "CCM 100 Coins", location_table["CCM 100 Coins"], regCCM)) + world.regions.append(regCCM) + + regBBH = Region("Big Boo's Haunt", RegionType.Generic, "Big Boo's Haunt", player, world) + locBBH_names = [name for name, id in locBBH_table.items()] + regBBH.locations += [SM64Location(player, loc_name, location_table[loc_name], regBBH) for loc_name in locBBH_names] + if (world.EnableCoinStars[player].value): + regBBH.locations.append(SM64Location(player, "BBH 100 Coins", location_table["BBH 100 Coins"], regBBH)) + world.regions.append(regBBH) + + regCellar = Region("Cellar", RegionType.Generic, "Cellar", player, world) + world.regions.append(regCellar) + + regHMC = Region("Hazy Maze Cave", RegionType.Generic, "Hazy Maze Cave", player, world) + locHMC_names = [name for name, id in locHMC_table.items()] + regHMC.locations += [SM64Location(player, loc_name, location_table[loc_name], regHMC) for loc_name in locHMC_names] + if (world.EnableCoinStars[player].value): + regHMC.locations.append(SM64Location(player, "HMC 100 Coins", location_table["HMC 100 Coins"], regHMC)) + world.regions.append(regHMC) + + regLLL = Region("Lethal Lava Land", RegionType.Generic, "Lethal Lava Land", player, world) + locLLL_names = [name for name, id in locLLL_table.items()] + regLLL.locations += [SM64Location(player, loc_name, location_table[loc_name], regLLL) for loc_name in locLLL_names] + if (world.EnableCoinStars[player].value): + regLLL.locations.append(SM64Location(player, "LLL 100 Coins", location_table["LLL 100 Coins"], regLLL)) + world.regions.append(regLLL) + + regSSL = Region("Shifting Sand Land", RegionType.Generic, "Shifting Sand Land", player, world) + locSSL_names = [name for name, id in locSSL_table.items()] + regSSL.locations += [SM64Location(player, loc_name, location_table[loc_name], regSSL) for loc_name in locSSL_names] + if (world.EnableCoinStars[player].value): + regSSL.locations.append(SM64Location(player, "SSL 100 Coins", location_table["SSL 100 Coins"], regSSL)) + world.regions.append(regSSL) + + regDDD = Region("Dire, Dire Docks", RegionType.Generic, "Dire, Dire Docks", player, world) + locDDD_names = [name for name, id in locDDD_table.items()] + regDDD.locations += [SM64Location(player, loc_name, location_table[loc_name], regDDD) for loc_name in locDDD_names] + if (world.EnableCoinStars[player].value): + regDDD.locations.append(SM64Location(player, "DDD 100 Coins", location_table["DDD 100 Coins"], regDDD)) + world.regions.append(regDDD) + + regFloor2 = Region("Second Floor", RegionType.Generic, "Second Floor", player, world) + world.regions.append(regFloor2) + + regSL = Region("Snowman's Land", RegionType.Generic, "Snowman's Land", player, world) + locSL_names = [name for name, id in locSL_table.items()] + regSL.locations += [SM64Location(player, loc_name, location_table[loc_name], regSL) for loc_name in locSL_names] + if (world.EnableCoinStars[player].value): + regSL.locations.append(SM64Location(player, "SL 100 Coins", location_table["SL 100 Coins"], regSL)) + world.regions.append(regSL) + + regWDW = Region("Wet-Dry World", RegionType.Generic, "Wet-Dry World", player, world) + locWDW_names = [name for name, id in locWDW_table.items()] + regWDW.locations += [SM64Location(player, loc_name, location_table[loc_name], regWDW) for loc_name in locWDW_names] + if (world.EnableCoinStars[player].value): + regWDW.locations.append(SM64Location(player, "WDW 100 Coins", location_table["WDW 100 Coins"], regWDW)) + world.regions.append(regWDW) + + regTTM = Region("Tall, Tall Mountain", RegionType.Generic, "Tall, Tall Mountain", player, world) + locTTM_names = [name for name, id in locTTM_table.items()] + regTTM.locations += [SM64Location(player, loc_name, location_table[loc_name], regTTM) for loc_name in locTTM_names] + if (world.EnableCoinStars[player].value): + regTTM.locations.append(SM64Location(player, "TTM 100 Coins", location_table["TTM 100 Coins"], regTTM)) + world.regions.append(regTTM) + + regTHI = Region("Tiny-Huge Island", RegionType.Generic, "Tiny-Huge Island", player, world) + locTHI_names = [name for name, id in locTHI_table.items()] + regTHI.locations += [SM64Location(player, loc_name, location_table[loc_name], regTHI) for loc_name in locTHI_names] + if (world.EnableCoinStars[player].value): + regTHI.locations.append(SM64Location(player, "THI 100 Coins", location_table["THI 100 Coins"], regTHI)) + world.regions.append(regTHI) + + regFloor3 = Region("Third Floor", RegionType.Generic, "Third Floor", player, world) + world.regions.append(regFloor3) + + regTTC = Region("Tick Tock Clock", RegionType.Generic, "Tick Tock Clock", player, world) + locTTC_names = [name for name, id in locTTC_table.items()] + regTTC.locations += [SM64Location(player, loc_name, location_table[loc_name], regTTC) for loc_name in locTTC_names] + if (world.EnableCoinStars[player].value): + regTTC.locations.append(SM64Location(player, "TTC 100 Coins", location_table["TTC 100 Coins"], regTTC)) + world.regions.append(regTTC) + + regRR = Region("Rainbow Ride", RegionType.Generic, "Rainbow Ride", player, world) + locRR_names = [name for name, id in locRR_table.items()] + regRR.locations += [SM64Location(player, loc_name, location_table[loc_name], regRR) for loc_name in locRR_names] + if (world.EnableCoinStars[player].value): + regRR.locations.append(SM64Location(player, "RR 100 Coins", location_table["RR 100 Coins"], regRR)) + world.regions.append(regRR) + + +def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule): + sourceRegion = world.get_region(source, player) + targetRegion = world.get_region(target, player) + + connection = Entrance(player,'', sourceRegion) + connection.access_rule = rule + + sourceRegion.exits.append(connection) + connection.connect(targetRegion) \ No newline at end of file diff --git a/worlds/sm64ex/Rules.py b/worlds/sm64ex/Rules.py new file mode 100644 index 00000000..6a3becd4 --- /dev/null +++ b/worlds/sm64ex/Rules.py @@ -0,0 +1,89 @@ +import typing +from ..generic.Rules import add_rule +from .Regions import connect_regions + +def set_rules(world,player): + connect_regions(world, player, "Menu", "Bob-omb Battlefield", lambda state: True) + connect_regions(world, player, "Menu", "Whomp's Fortress", lambda state: state.has("Star", player, 1)) + connect_regions(world, player, "Menu", "Jolly Roger Bay", lambda state: state.has("Star", player, 3)) + connect_regions(world, player, "Menu", "Cool, Cool Mountain", lambda state: state.has("Star", player, 3)) + connect_regions(world, player, "Menu", "Big Boo's Haunt", lambda state: state.has("Star", player, 12)) + + connect_regions(world, player, "Menu", "Cellar", lambda state: state.has("Cellar Key", player)) + connect_regions(world, player, "Cellar", "Menu", lambda state: True) + + connect_regions(world, player, "Cellar", "Hazy Maze Cave", lambda state: True) + connect_regions(world, player, "Cellar", "Lethal Lava Land", lambda state: True) + connect_regions(world, player, "Cellar", "Shifting Sand Land", lambda state: True) + connect_regions(world, player, "Cellar", "Dire, Dire Docks", lambda state: state.has("Star", player, 30)) + + connect_regions(world, player, "Menu", "Second Floor", lambda state: state.has("Second Floor Key", player)) + connect_regions(world, player, "Second Floor", "Menu", lambda state: True) + + connect_regions(world, player, "Second Floor", "Snowman's Land", lambda state: True) + connect_regions(world, player, "Second Floor", "Wet-Dry World", lambda state: True) + connect_regions(world, player, "Second Floor", "Tall, Tall Mountain", lambda state: True) + connect_regions(world, player, "Second Floor", "Tiny-Huge Island", lambda state: True) + + connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Star", player, 50)) + connect_regions(world, player, "Third Floor", "Second Floor", lambda state: True) + + connect_regions(world, player, "Third Floor", "Tick Tock Clock", lambda state: True) + connect_regions(world, player, "Third Floor", "Rainbow Ride", lambda state: True) + + connect_regions(world, player, "Bob-omb Battlefield", "Menu", lambda state: True) + connect_regions(world, player, "Whomp's Fortress", "Menu", lambda state: True) + connect_regions(world, player, "Jolly Roger Bay", "Menu", lambda state: True) + connect_regions(world, player, "Cool, Cool Mountain", "Menu", lambda state: True) + connect_regions(world, player, "Big Boo's Haunt", "Menu", lambda state: True) + connect_regions(world, player, "Hazy Maze Cave", "Cellar", lambda state: True) + connect_regions(world, player, "Lethal Lava Land", "Cellar", lambda state: True) + connect_regions(world, player, "Shifting Sand Land", "Cellar", lambda state: True) + connect_regions(world, player, "Dire, Dire Docks", "Cellar", lambda state: True) + connect_regions(world, player, "Snowman's Land", "Second Floor", lambda state: True) + connect_regions(world, player, "Wet-Dry World", "Second Floor", lambda state: True) + connect_regions(world, player, "Tall, Tall Mountain", "Second Floor", lambda state: True) + connect_regions(world, player, "Tiny-Huge Island", "Second Floor", lambda state: True) + connect_regions(world, player, "Tick Tock Clock", "Second Floor", lambda state: True) + connect_regions(world, player, "Rainbow Ride", "Second Floor", lambda state: True) + + #Special Rules for some Locations + add_rule(world.get_location("Wing Cap Switch", player), lambda state: state.has("Star", player, 10)) + add_rule(world.get_location("Metal Cap Switch", player), lambda state: state.can_reach("Cellar", 'Region', player)) + add_rule(world.get_location("Vanish Cap Switch", player), lambda state: state.can_reach("Cellar", 'Region', player)) + + add_rule(world.get_location("Eye to Eye in the Secret Room", player), lambda state: state.has("Vanish Cap", player)) + add_rule(world.get_location("Collect the Caps...", player), lambda state: state.has("Metal Cap", player) and + state.has("Vanish Cap", player)) + add_rule(world.get_location("Into the Igloo", player), lambda state: state.has("Vanish Cap", player)) + add_rule(world.get_location("Quick Race Through Downtown!", player), lambda state: state.has("Vanish Cap", player)) + if (world.StrictCapRequirements[player].value): + add_rule(world.get_location("Mario Wings to the Sky", player), lambda state: state.has("Wing Cap", player)) + add_rule(world.get_location("Metal-Head Mario Can Move!", player), lambda state: state.has("Metal Cap", player)) + add_rule(world.get_location("JRB: Through the Jet Stream", player), lambda state: state.has("Metal Cap", player)) + add_rule(world.get_location("Free Flying for 8 Red Coins", player), lambda state: state.has("Wing Cap", player)) + add_rule(world.get_location("DDD: Through the Jet Stream", player), lambda state: state.has("Metal Cap", player)) + add_rule(world.get_location("Vanish Cap Under the Moat Red Coins", player), lambda state: state.has("Vanish Cap", player)) + + #Rules for Secret Stars + add_rule(world.get_location("Bowser in the Dark World Red Coins", player), lambda state: state.has("Star", player, 8)) + add_rule(world.get_location("Bowser in the Fire Sea Red Coins", player), lambda state: state.can_reach("Cellar",'Region',player) and state.has("Star", player, 30)) + add_rule(world.get_location("Bowser in the Sky Red Coins", player), lambda state: state.can_reach("Third Floor",'Region',player) and state.has("Star", player, world.StarsToFinish[player].value)) + add_rule(world.get_location("The Princess's Secret Slide Box", player), lambda state: state.has("Star", player, 1)) + add_rule(world.get_location("The Princess's Secret Slide Fast", player), lambda state: state.has("Star", player, 1)) + add_rule(world.get_location("Cavern of the Metal Cap Red Coins", player), lambda state: state.can_reach("Metal Cap Switch", 'Location', player) and state.has("Metal Cap", player)) + add_rule(world.get_location("Tower of the Wing Cap Red Coins", player), lambda state: state.can_reach("Wing Cap Switch", 'Location', player)) + add_rule(world.get_location("Vanish Cap Under the Moat Red Coins", player), lambda state: state.can_reach("Vanish Cap Switch", 'Location', player)) + add_rule(world.get_location("Wing Mario Over the Rainbow Red Coins", player), lambda state: state.can_reach("Third Floor", 'Region', player) and state.has("Wing Cap", player)) + add_rule(world.get_location("The Secret Aquarium", player), lambda state: state.can_reach("Jolly Roger Bay", 'Region', player)) + add_rule(world.get_location("Toad (Cellar)", player), lambda state: state.can_reach("Cellar",'Region',player)) + add_rule(world.get_location("Toad (Second Floor)", player), lambda state: state.can_reach("Second Floor",'Region',player)) + add_rule(world.get_location("Toad (Third Floor)", player), lambda state: state.can_reach("Third Floor",'Region',player)) + add_rule(world.get_location("MIPS 1", player), lambda state: state.can_reach("Cellar",'Region',player) and state.has("Star", player, 15)) + add_rule(world.get_location("MIPS 2", player), lambda state: state.can_reach("Cellar",'Region',player) and state.has("Star", player, 50)) + + #Rules for Keys + add_rule(world.get_location("Cellar Key", player), lambda state: state.has("Star", player, 8)) + add_rule(world.get_location("Second Floor Key", player), lambda state: state.can_reach("Cellar", 'Region', player) and state.has("Star", player, 30)) + + world.completion_condition[player] = lambda state: state.can_reach("Third Floor",'Region',player) and state.has("Star", player, world.StarsToFinish[player].value) diff --git a/worlds/sm64ex/__init__.py b/worlds/sm64ex/__init__.py new file mode 100644 index 00000000..0cc18718 --- /dev/null +++ b/worlds/sm64ex/__init__.py @@ -0,0 +1,59 @@ +import string +from .Items import item_table, SM64Item +from .Locations import location_table, SM64Location +from .Options import sm64_options +from .Rules import set_rules +from .Regions import create_regions +from BaseClasses import Region, RegionType, Entrance, Item, MultiWorld +from ..AutoWorld import World + +client_version = 1 + +class SM64World(World): + """ + dude do be jumping + """ + + game: str = "Super Mario 64" + topology_present = False + + item_name_to_id = item_table + location_name_to_id = location_table + + data_version = 1 + forced_auto_forfeit = False + + options = sm64_options + + def create_regions(self): + create_regions(self.world,self.player) + + + def set_rules(self): + set_rules(self.world,self.player) + + def create_item(self, name: str) -> Item: + item_id = item_table[name] + item = SM64Item(name, True, item_id, self.player) + return item + + def generate_basic(self): + staritem = self.create_item("Star") + if (self.world.EnableCoinStars[self.player].value): + self.world.itempool += [staritem for i in range(0,120)] + else: + self.world.itempool += [staritem for i in range(0,105)] + + key1 = self.create_item("Cellar Key") + key2 = self.create_item("Second Floor Key") + self.world.itempool += [key1,key2] + + wingcap = self.create_item("Wing Cap") + metalcap = self.create_item("Metal Cap") + vanishcap = self.create_item("Vanish Cap") + self.world.itempool += [wingcap,metalcap,vanishcap] + + def fill_slot_data(self): + return { + "StarsToFinish": self.world.StarsToFinish[self.player].value + }