Sonic Adventure 2: Battle Implementation (#501)

This commit is contained in:
PoryGone 2022-05-14 06:00:49 -04:00 committed by GitHub
parent 00f5975a3c
commit dc10421531
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 2050 additions and 0 deletions

View File

@ -24,6 +24,7 @@ Currently, the following games are supported:
* ArchipIDLE
* Hollow Knight
* The Witness
* Sonic Adventure 2: Battle
For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled

76
worlds/sa2b/Items.py Normal file
View File

@ -0,0 +1,76 @@
import typing
from BaseClasses import Item
from .Names import ItemName
class ItemData(typing.NamedTuple):
code: typing.Optional[int]
progression: bool
quantity: int = 1
event: bool = False
class SA2BItem(Item):
game: str = "Sonic Adventure 2: Battle"
def __init__(self, name, advancement: bool = False, code: int = None, player: int = None):
super(SA2BItem, self).__init__(name, advancement, code, player)
if self.name == ItemName.sonic_light_shoes or self.name == ItemName.shadow_air_shoes:
self.pedestal_credit_text = "and the Soap Shoes"
# Separate tables for each type of item.
emblems_table = {
ItemName.emblem: ItemData(0xFF0000, True),
}
upgrades_table = {
ItemName.sonic_gloves: ItemData(0xFF0001, False),
ItemName.sonic_light_shoes: ItemData(0xFF0002, True),
ItemName.sonic_ancient_light: ItemData(0xFF0003, False),
ItemName.sonic_bounce_bracelet: ItemData(0xFF0004, True),
ItemName.sonic_flame_ring: ItemData(0xFF0005, True),
ItemName.sonic_mystic_melody: ItemData(0xFF0006, True),
ItemName.tails_laser_blaster: ItemData(0xFF0007, False),
ItemName.tails_booster: ItemData(0xFF0008, True),
ItemName.tails_mystic_melody: ItemData(0xFF0009, True),
ItemName.tails_bazooka: ItemData(0xFF000A, True),
ItemName.knuckles_mystic_melody: ItemData(0xFF000B, True),
ItemName.knuckles_shovel_claws: ItemData(0xFF000C, True),
ItemName.knuckles_air_necklace: ItemData(0xFF000D, True),
ItemName.knuckles_hammer_gloves: ItemData(0xFF000E, True),
ItemName.knuckles_sunglasses: ItemData(0xFF000F, True),
ItemName.shadow_flame_ring: ItemData(0xFF0010, True),
ItemName.shadow_air_shoes: ItemData(0xFF0011, True),
ItemName.shadow_ancient_light: ItemData(0xFF0012, False),
ItemName.shadow_mystic_melody: ItemData(0xFF0013, True),
ItemName.eggman_laser_blaster: ItemData(0xFF0014, False),
ItemName.eggman_mystic_melody: ItemData(0xFF0015, True),
ItemName.eggman_jet_engine: ItemData(0xFF0016, True),
ItemName.eggman_large_cannon: ItemData(0xFF0017, True),
ItemName.eggman_protective_armor: ItemData(0xFF0018, False),
ItemName.rouge_mystic_melody: ItemData(0xFF0019, True),
ItemName.rouge_pick_nails: ItemData(0xFF001A, True),
ItemName.rouge_treasure_scope: ItemData(0xFF001B, True),
ItemName.rouge_iron_boots: ItemData(0xFF001C, True),
}
event_table = {
ItemName.maria: ItemData(0xFF001D, True),
}
# Complete item table.
item_table = {
**emblems_table,
**upgrades_table,
**event_table,
}
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code}

277
worlds/sa2b/Locations.py Normal file
View File

@ -0,0 +1,277 @@
import typing
from BaseClasses import Location
from .Names import LocationName
class SA2BLocation(Location):
game: str = "Sonic Adventure 2: Battle"
first_mission_location_table = {
LocationName.city_escape_1: 0xFF0000,
LocationName.wild_canyon_1: 0xFF0001,
LocationName.prison_lane_1: 0xFF0002,
LocationName.metal_harbor_1: 0xFF0003,
LocationName.green_forest_1: 0xFF0004,
LocationName.pumpkin_hill_1: 0xFF0005,
LocationName.mission_street_1: 0xFF0006,
LocationName.aquatic_mine_1: 0xFF0007,
LocationName.route_101_1: 0xFF0008,
LocationName.hidden_base_1: 0xFF0009,
LocationName.pyramid_cave_1: 0xFF000A,
LocationName.death_chamber_1: 0xFF000B,
LocationName.eternal_engine_1: 0xFF000C,
LocationName.meteor_herd_1: 0xFF000D,
LocationName.crazy_gadget_1: 0xFF000E,
LocationName.final_rush_1: 0xFF000F,
LocationName.iron_gate_1: 0xFF0010,
LocationName.dry_lagoon_1: 0xFF0011,
LocationName.sand_ocean_1: 0xFF0012,
LocationName.radical_highway_1: 0xFF0013,
LocationName.egg_quarters_1: 0xFF0014,
LocationName.lost_colony_1: 0xFF0015,
LocationName.weapons_bed_1: 0xFF0016,
LocationName.security_hall_1: 0xFF0017,
LocationName.white_jungle_1: 0xFF0018,
LocationName.route_280_1: 0xFF0019,
LocationName.sky_rail_1: 0xFF001A,
LocationName.mad_space_1: 0xFF001B,
LocationName.cosmic_wall_1: 0xFF001C,
LocationName.final_chase_1: 0xFF001D,
LocationName.cannon_core_1: 0xFF001E,
}
second_mission_location_table = {
LocationName.city_escape_2: 0xFF0020,
LocationName.wild_canyon_2: 0xFF0021,
LocationName.prison_lane_2: 0xFF0022,
LocationName.metal_harbor_2: 0xFF0023,
LocationName.green_forest_2: 0xFF0024,
LocationName.pumpkin_hill_2: 0xFF0025,
LocationName.mission_street_2: 0xFF0026,
LocationName.aquatic_mine_2: 0xFF0027,
LocationName.route_101_2: 0xFF0028,
LocationName.hidden_base_2: 0xFF0029,
LocationName.pyramid_cave_2: 0xFF002A,
LocationName.death_chamber_2: 0xFF002B,
LocationName.eternal_engine_2: 0xFF002C,
LocationName.meteor_herd_2: 0xFF002D,
LocationName.crazy_gadget_2: 0xFF002E,
LocationName.final_rush_2: 0xFF002F,
LocationName.iron_gate_2: 0xFF0030,
LocationName.dry_lagoon_2: 0xFF0031,
LocationName.sand_ocean_2: 0xFF0032,
LocationName.radical_highway_2: 0xFF0033,
LocationName.egg_quarters_2: 0xFF0034,
LocationName.lost_colony_2: 0xFF0035,
LocationName.weapons_bed_2: 0xFF0036,
LocationName.security_hall_2: 0xFF0037,
LocationName.white_jungle_2: 0xFF0038,
LocationName.route_280_2: 0xFF0039,
LocationName.sky_rail_2: 0xFF003A,
LocationName.mad_space_2: 0xFF003B,
LocationName.cosmic_wall_2: 0xFF003C,
LocationName.final_chase_2: 0xFF003D,
LocationName.cannon_core_2: 0xFF003E,
}
third_mission_location_table = {
LocationName.city_escape_3: 0xFF0040,
LocationName.wild_canyon_3: 0xFF0041,
LocationName.prison_lane_3: 0xFF0042,
LocationName.metal_harbor_3: 0xFF0043,
LocationName.green_forest_3: 0xFF0044,
LocationName.pumpkin_hill_3: 0xFF0045,
LocationName.mission_street_3: 0xFF0046,
LocationName.aquatic_mine_3: 0xFF0047,
LocationName.route_101_3: 0xFF0048,
LocationName.hidden_base_3: 0xFF0049,
LocationName.pyramid_cave_3: 0xFF004A,
LocationName.death_chamber_3: 0xFF004B,
LocationName.eternal_engine_3: 0xFF004C,
LocationName.meteor_herd_3: 0xFF004D,
LocationName.crazy_gadget_3: 0xFF004E,
LocationName.final_rush_3: 0xFF004F,
LocationName.iron_gate_3: 0xFF0050,
LocationName.dry_lagoon_3: 0xFF0051,
LocationName.sand_ocean_3: 0xFF0052,
LocationName.radical_highway_3: 0xFF0053,
LocationName.egg_quarters_3: 0xFF0054,
LocationName.lost_colony_3: 0xFF0055,
LocationName.weapons_bed_3: 0xFF0056,
LocationName.security_hall_3: 0xFF0057,
LocationName.white_jungle_3: 0xFF0058,
LocationName.route_280_3: 0xFF0059,
LocationName.sky_rail_3: 0xFF005A,
LocationName.mad_space_3: 0xFF005B,
LocationName.cosmic_wall_3: 0xFF005C,
LocationName.final_chase_3: 0xFF005D,
LocationName.cannon_core_3: 0xFF005E,
}
fourth_mission_location_table = {
LocationName.city_escape_4: 0xFF0060,
LocationName.wild_canyon_4: 0xFF0061,
LocationName.prison_lane_4: 0xFF0062,
LocationName.metal_harbor_4: 0xFF0063,
LocationName.green_forest_4: 0xFF0064,
LocationName.pumpkin_hill_4: 0xFF0065,
LocationName.mission_street_4: 0xFF0066,
LocationName.aquatic_mine_4: 0xFF0067,
LocationName.route_101_4: 0xFF0068,
LocationName.hidden_base_4: 0xFF0069,
LocationName.pyramid_cave_4: 0xFF006A,
LocationName.death_chamber_4: 0xFF006B,
LocationName.eternal_engine_4: 0xFF006C,
LocationName.meteor_herd_4: 0xFF006D,
LocationName.crazy_gadget_4: 0xFF006E,
LocationName.final_rush_4: 0xFF006F,
LocationName.iron_gate_4: 0xFF0070,
LocationName.dry_lagoon_4: 0xFF0071,
LocationName.sand_ocean_4: 0xFF0072,
LocationName.radical_highway_4: 0xFF0073,
LocationName.egg_quarters_4: 0xFF0074,
LocationName.lost_colony_4: 0xFF0075,
LocationName.weapons_bed_4: 0xFF0076,
LocationName.security_hall_4: 0xFF0077,
LocationName.white_jungle_4: 0xFF0078,
LocationName.route_280_4: 0xFF0079,
LocationName.sky_rail_4: 0xFF007A,
LocationName.mad_space_4: 0xFF007B,
LocationName.cosmic_wall_4: 0xFF007C,
LocationName.final_chase_4: 0xFF007D,
LocationName.cannon_core_4: 0xFF007E,
}
fifth_mission_location_table = {
LocationName.city_escape_5: 0xFF0080,
LocationName.wild_canyon_5: 0xFF0081,
LocationName.prison_lane_5: 0xFF0082,
LocationName.metal_harbor_5: 0xFF0083,
LocationName.green_forest_5: 0xFF0084,
LocationName.pumpkin_hill_5: 0xFF0085,
LocationName.mission_street_5: 0xFF0086,
LocationName.aquatic_mine_5: 0xFF0087,
LocationName.route_101_5: 0xFF0088,
LocationName.hidden_base_5: 0xFF0089,
LocationName.pyramid_cave_5: 0xFF008A,
LocationName.death_chamber_5: 0xFF008B,
LocationName.eternal_engine_5: 0xFF008C,
LocationName.meteor_herd_5: 0xFF008D,
LocationName.crazy_gadget_5: 0xFF008E,
LocationName.final_rush_5: 0xFF008F,
LocationName.iron_gate_5: 0xFF0090,
LocationName.dry_lagoon_5: 0xFF0091,
LocationName.sand_ocean_5: 0xFF0092,
LocationName.radical_highway_5: 0xFF0093,
LocationName.egg_quarters_5: 0xFF0094,
LocationName.lost_colony_5: 0xFF0095,
LocationName.weapons_bed_5: 0xFF0096,
LocationName.security_hall_5: 0xFF0097,
LocationName.white_jungle_5: 0xFF0098,
LocationName.route_280_5: 0xFF0099,
LocationName.sky_rail_5: 0xFF009A,
LocationName.mad_space_5: 0xFF009B,
LocationName.cosmic_wall_5: 0xFF009C,
LocationName.final_chase_5: 0xFF009D,
LocationName.cannon_core_5: 0xFF009E,
}
upgrade_location_table = {
LocationName.city_escape_upgrade: 0xFF00A0,
LocationName.wild_canyon_upgrade: 0xFF00A1,
LocationName.prison_lane_upgrade: 0xFF00A2,
LocationName.metal_harbor_upgrade: 0xFF00A3,
LocationName.green_forest_upgrade: 0xFF00A4,
LocationName.pumpkin_hill_upgrade: 0xFF00A5,
LocationName.mission_street_upgrade: 0xFF00A6,
LocationName.aquatic_mine_upgrade: 0xFF00A7,
LocationName.hidden_base_upgrade: 0xFF00A9,
LocationName.pyramid_cave_upgrade: 0xFF00AA,
LocationName.death_chamber_upgrade: 0xFF00AB,
LocationName.eternal_engine_upgrade: 0xFF00AC,
LocationName.meteor_herd_upgrade: 0xFF00AD,
LocationName.crazy_gadget_upgrade: 0xFF00AE,
LocationName.final_rush_upgrade: 0xFF00AF,
LocationName.iron_gate_upgrade: 0xFF00B0,
LocationName.dry_lagoon_upgrade: 0xFF00B1,
LocationName.sand_ocean_upgrade: 0xFF00B2,
LocationName.radical_highway_upgrade: 0xFF00B3,
LocationName.egg_quarters_upgrade: 0xFF00B4,
LocationName.lost_colony_upgrade: 0xFF00B5,
LocationName.weapons_bed_upgrade: 0xFF00B6,
LocationName.security_hall_upgrade: 0xFF00B7,
LocationName.white_jungle_upgrade: 0xFF00B8,
LocationName.sky_rail_upgrade: 0xFF00BA,
LocationName.mad_space_upgrade: 0xFF00BB,
LocationName.cosmic_wall_upgrade: 0xFF00BC,
LocationName.final_chase_upgrade: 0xFF00BD,
}
chao_garden_location_table = {
LocationName.chao_beginner_race: 0xFF00C0,
LocationName.chao_jewel_race: 0xFF00C1,
LocationName.chao_challenge_race: 0xFF00C2,
LocationName.chao_hero_race: 0xFF00C3,
LocationName.chao_dark_race: 0xFF00C4,
LocationName.chao_beginner_karate: 0xFF00C5,
LocationName.chao_standard_karate: 0xFF00C6,
LocationName.chao_expert_karate: 0xFF00C7,
LocationName.chao_super_karate: 0xFF00C8,
}
other_location_table = {
# LocationName.green_hill: 0xFF001F,
LocationName.biolizard: 0xFF003F,
}
all_locations = {
**first_mission_location_table,
**second_mission_location_table,
**third_mission_location_table,
**fourth_mission_location_table,
**fifth_mission_location_table,
**upgrade_location_table,
**chao_garden_location_table,
**other_location_table,
}
location_table = {}
def setup_locations(world, player: int):
location_table = {**first_mission_location_table}
if world.IncludeMissions[player].value >= 2:
location_table.update({**second_mission_location_table})
if world.IncludeMissions[player].value >= 3:
location_table.update({**third_mission_location_table})
if world.IncludeMissions[player].value >= 4:
location_table.update({**fourth_mission_location_table})
if world.IncludeMissions[player].value >= 5:
location_table.update({**fifth_mission_location_table})
location_table.update({**upgrade_location_table})
# location_table.update(**chao_garden_location_table})
location_table.update({**other_location_table})
return location_table
lookup_id_to_name: typing.Dict[int, str] = {id: name for name, _ in all_locations.items()}

View File

@ -0,0 +1,39 @@
# Emblem Definition
emblem = "Emblem"
# Upgrade Definitions
sonic_gloves = "Sonic - Magic Glove"
sonic_light_shoes = "Sonic - Light Shoes"
sonic_ancient_light = "Sonic - Ancient Light"
sonic_bounce_bracelet = "Sonic - Bounce Bracelet"
sonic_flame_ring = "Sonic - Flame Ring"
sonic_mystic_melody = "Sonic - Mystic Melody"
tails_laser_blaster = "Tails - Laser Blaster"
tails_booster = "Tails - Booster"
tails_mystic_melody = "Tails - Mystic Melody"
tails_bazooka = "Tails - Bazooka"
knuckles_mystic_melody = "Knuckles - Mystic Melody"
knuckles_shovel_claws = "Knuckles - Shovel Claws"
knuckles_air_necklace = "Knuckles - Air Necklace"
knuckles_hammer_gloves = "Knuckles - Hammer Gloves"
knuckles_sunglasses = "Knuckles - Sunglasses"
shadow_flame_ring = "Shadow - Flame Ring"
shadow_air_shoes = "Shadow - Air Shoes"
shadow_ancient_light = "Shadow - Ancient Light"
shadow_mystic_melody = "Shadow - Mystic Melody"
eggman_laser_blaster = "Eggman - Laser Blaster"
eggman_mystic_melody = "Eggman - Mystic Melody"
eggman_jet_engine = "Eggman - Jet Engine"
eggman_large_cannon = "Eggman - Large Cannon"
eggman_protective_armor = "Eggman - Protective Armor"
rouge_mystic_melody = "Rouge - Mystic Melody"
rouge_pick_nails = "Rouge - Pick Nails"
rouge_treasure_scope = "Rouge - Treasure Scope"
rouge_iron_boots = "Rouge - Iron Boots"
maria = "What Maria Wanted"

View File

@ -0,0 +1,264 @@
# Sonic Mission Definitions
city_escape_1 = "City Escape - 1"
city_escape_2 = "City Escape - 2"
city_escape_3 = "City Escape - 3"
city_escape_4 = "City Escape - 4"
city_escape_5 = "City Escape - 5"
city_escape_upgrade = "City Escape - Upgrade"
metal_harbor_1 = "Metal Harbor - 1"
metal_harbor_2 = "Metal Harbor - 2"
metal_harbor_3 = "Metal Harbor - 3"
metal_harbor_4 = "Metal Harbor - 4"
metal_harbor_5 = "Metal Harbor - 5"
metal_harbor_upgrade = "Metal Harbor - Upgrade"
green_forest_1 = "Green Forest - 1"
green_forest_2 = "Green Forest - 2"
green_forest_3 = "Green Forest - 3"
green_forest_4 = "Green Forest - 4"
green_forest_5 = "Green Forest - 5"
green_forest_upgrade = "Green Forest - Upgrade"
pyramid_cave_1 = "Pyramid Cave - 1"
pyramid_cave_2 = "Pyramid Cave - 2"
pyramid_cave_3 = "Pyramid Cave - 3"
pyramid_cave_4 = "Pyramid Cave - 4"
pyramid_cave_5 = "Pyramid Cave - 5"
pyramid_cave_upgrade = "Pyramid Cave - Upgrade"
crazy_gadget_1 = "Crazy Gadget - 1"
crazy_gadget_2 = "Crazy Gadget - 2"
crazy_gadget_3 = "Crazy Gadget - 3"
crazy_gadget_4 = "Crazy Gadget - 4"
crazy_gadget_5 = "Crazy Gadget - 5"
crazy_gadget_upgrade = "Crazy Gadget - Upgrade"
final_rush_1 = "Final Rush - 1"
final_rush_2 = "Final Rush - 2"
final_rush_3 = "Final Rush - 3"
final_rush_4 = "Final Rush - 4"
final_rush_5 = "Final Rush - 5"
final_rush_upgrade = "Final Rush - Upgrade"
# Tails Mission Definitions
prison_lane_1 = "Prison Lane - 1"
prison_lane_2 = "Prison Lane - 2"
prison_lane_3 = "Prison Lane - 3"
prison_lane_4 = "Prison Lane - 4"
prison_lane_5 = "Prison Lane - 5"
prison_lane_upgrade = "Prison Lane - Upgrade"
mission_street_1 = "Mission Street - 1"
mission_street_2 = "Mission Street - 2"
mission_street_3 = "Mission Street - 3"
mission_street_4 = "Mission Street - 4"
mission_street_5 = "Mission Street - 5"
mission_street_upgrade = "Mission Street - Upgrade"
route_101_1 = "Route 101 - 1"
route_101_2 = "Route 101 - 2"
route_101_3 = "Route 101 - 3"
route_101_4 = "Route 101 - 4"
route_101_5 = "Route 101 - 5"
hidden_base_1 = "Hidden Base - 1"
hidden_base_2 = "Hidden Base - 2"
hidden_base_3 = "Hidden Base - 3"
hidden_base_4 = "Hidden Base - 4"
hidden_base_5 = "Hidden Base - 5"
hidden_base_upgrade = "Hidden Base - Upgrade"
eternal_engine_1 = "Eternal Engine - 1"
eternal_engine_2 = "Eternal Engine - 2"
eternal_engine_3 = "Eternal Engine - 3"
eternal_engine_4 = "Eternal Engine - 4"
eternal_engine_5 = "Eternal Engine - 5"
eternal_engine_upgrade = "Eternal Engine - Upgrade"
# Knuckles Mission Definitions
wild_canyon_1 = "Wild Canyon - 1"
wild_canyon_2 = "Wild Canyon - 2"
wild_canyon_3 = "Wild Canyon - 3"
wild_canyon_4 = "Wild Canyon - 4"
wild_canyon_5 = "Wild Canyon - 5"
wild_canyon_upgrade = "Wild Canyon - Upgrade"
pumpkin_hill_1 = "Pumpkin Hill - 1"
pumpkin_hill_2 = "Pumpkin Hill - 2"
pumpkin_hill_3 = "Pumpkin Hill - 3"
pumpkin_hill_4 = "Pumpkin Hill - 4"
pumpkin_hill_5 = "Pumpkin Hill - 5"
pumpkin_hill_upgrade = "Pumpkin Hill - Upgrade"
aquatic_mine_1 = "Aquatic Mine - 1"
aquatic_mine_2 = "Aquatic Mine - 2"
aquatic_mine_3 = "Aquatic Mine - 3"
aquatic_mine_4 = "Aquatic Mine - 4"
aquatic_mine_5 = "Aquatic Mine - 5"
aquatic_mine_upgrade = "Aquatic Mine - Upgrade"
death_chamber_1 = "Death Chamber - 1"
death_chamber_2 = "Death Chamber - 2"
death_chamber_3 = "Death Chamber - 3"
death_chamber_4 = "Death Chamber - 4"
death_chamber_5 = "Death Chamber - 5"
death_chamber_upgrade = "Death Chamber - Upgrade"
meteor_herd_1 = "Meteor Herd - 1"
meteor_herd_2 = "Meteor Herd - 2"
meteor_herd_3 = "Meteor Herd - 3"
meteor_herd_4 = "Meteor Herd - 4"
meteor_herd_5 = "Meteor Herd - 5"
meteor_herd_upgrade = "Meteor Herd - Upgrade"
# Shadow Mission Definitions
radical_highway_1 = "Radical Highway - 1"
radical_highway_2 = "Radical Highway - 2"
radical_highway_3 = "Radical Highway - 3"
radical_highway_4 = "Radical Highway - 4"
radical_highway_5 = "Radical Highway - 5"
radical_highway_upgrade = "Radical Highway - Upgrade"
white_jungle_1 = "White Jungle - 1"
white_jungle_2 = "White Jungle - 2"
white_jungle_3 = "White Jungle - 3"
white_jungle_4 = "White Jungle - 4"
white_jungle_5 = "White Jungle - 5"
white_jungle_upgrade = "White Jungle - Upgrade"
sky_rail_1 = "Sky Rail - 1"
sky_rail_2 = "Sky Rail - 2"
sky_rail_3 = "Sky Rail - 3"
sky_rail_4 = "Sky Rail - 4"
sky_rail_5 = "Sky Rail - 5"
sky_rail_upgrade = "Sky Rail - Upgrade"
final_chase_1 = "Final Chase - 1"
final_chase_2 = "Final Chase - 2"
final_chase_3 = "Final Chase - 3"
final_chase_4 = "Final Chase - 4"
final_chase_5 = "Final Chase - 5"
final_chase_upgrade = "Final Chase - Upgrade"
# Eggman Mission Definitions
iron_gate_1 = "Iron Gate - 1"
iron_gate_2 = "Iron Gate - 2"
iron_gate_3 = "Iron Gate - 3"
iron_gate_4 = "Iron Gate - 4"
iron_gate_5 = "Iron Gate - 5"
iron_gate_upgrade = "Iron Gate - Upgrade"
sand_ocean_1 = "Sand Ocean - 1"
sand_ocean_2 = "Sand Ocean - 2"
sand_ocean_3 = "Sand Ocean - 3"
sand_ocean_4 = "Sand Ocean - 4"
sand_ocean_5 = "Sand Ocean - 5"
sand_ocean_upgrade = "Sand Ocean - Upgrade"
lost_colony_1 = "Lost Colony - 1"
lost_colony_2 = "Lost Colony - 2"
lost_colony_3 = "Lost Colony - 3"
lost_colony_4 = "Lost Colony - 4"
lost_colony_5 = "Lost Colony - 5"
lost_colony_upgrade = "Lost Colony - Upgrade"
weapons_bed_1 = "Weapons Bed - 1"
weapons_bed_2 = "Weapons Bed - 2"
weapons_bed_3 = "Weapons Bed - 3"
weapons_bed_4 = "Weapons Bed - 4"
weapons_bed_5 = "Weapons Bed - 5"
weapons_bed_upgrade = "Weapons Bed - Upgrade"
cosmic_wall_1 = "Cosmic Wall - 1"
cosmic_wall_2 = "Cosmic Wall - 2"
cosmic_wall_3 = "Cosmic Wall - 3"
cosmic_wall_4 = "Cosmic Wall - 4"
cosmic_wall_5 = "Cosmic Wall - 5"
cosmic_wall_upgrade = "Cosmic Wall - Upgrade"
# Rouge Mission Definitions
dry_lagoon_1 = "Dry Lagoon - 1"
dry_lagoon_2 = "Dry Lagoon - 2"
dry_lagoon_3 = "Dry Lagoon - 3"
dry_lagoon_4 = "Dry Lagoon - 4"
dry_lagoon_5 = "Dry Lagoon - 5"
dry_lagoon_upgrade = "Dry Lagoon - Upgrade"
egg_quarters_1 = "Egg Quarters - 1"
egg_quarters_2 = "Egg Quarters - 2"
egg_quarters_3 = "Egg Quarters - 3"
egg_quarters_4 = "Egg Quarters - 4"
egg_quarters_5 = "Egg Quarters - 5"
egg_quarters_upgrade = "Egg Quarters - Upgrade"
security_hall_1 = "Security Hall - 1"
security_hall_2 = "Security Hall - 2"
security_hall_3 = "Security Hall - 3"
security_hall_4 = "Security Hall - 4"
security_hall_5 = "Security Hall - 5"
security_hall_upgrade = "Security Hall - Upgrade"
route_280_1 = "Route 280 - 1"
route_280_2 = "Route 280 - 2"
route_280_3 = "Route 280 - 3"
route_280_4 = "Route 280 - 4"
route_280_5 = "Route 280 - 5"
mad_space_1 = "Mad Space - 1"
mad_space_2 = "Mad Space - 2"
mad_space_3 = "Mad Space - 3"
mad_space_4 = "Mad Space - 4"
mad_space_5 = "Mad Space - 5"
mad_space_upgrade = "Mad Space - Upgrade"
# Final Mission Definitions
cannon_core_1 = "Cannon Core - 1"
cannon_core_2 = "Cannon Core - 2"
cannon_core_3 = "Cannon Core - 3"
cannon_core_4 = "Cannon Core - 4"
cannon_core_5 = "Cannon Core - 5"
# Chao Garden Definitions
chao_beginner_race = "Chao Garden - Beginner Race"
chao_jewel_race = "Chao Garden - Jewel Race"
chao_challenge_race = "Chao Garden - Challenge Race"
chao_hero_race = "Chao Garden - Hero Race"
chao_dark_race = "Chao Garden - Dark Race"
chao_beginner_karate = "Chao Garden - Beginner Karate"
chao_standard_karate = "Chao Garden - Standard Karate"
chao_expert_karate = "Chao Garden - Expert Karate"
chao_super_karate = "Chao Garden - Super Karate"
# Other Definitions
green_hill = "Green Hill"
biolizard = "Biolizard"
# Region Definitions
menu_region = "Menu"
gate_0_region = "Gate 0"
gate_1_region = "Gate 1"
gate_2_region = "Gate 2"
gate_3_region = "Gate 3"
gate_4_region = "Gate 4"
gate_5_region = "Gate 5"
city_escape_region = "City Escape"
metal_harbor_region = "Metal Harbor"
green_forest_region = "Green Forest"
pyramid_cave_region = "Pyramid Cave"
crazy_gadget_region = "Crazy Gadget"
final_rush_region = "Final Rush"
prison_lane_region = "Prison Lane"
mission_street_region = "Mission Street"
route_101_region = "Route 101"
hidden_base_region = "Hidden Base"
eternal_engine_region = "Eternal Engine"
wild_canyon_region = "Wild Canyon"
pumpkin_hill_region = "Pumpkin Hill"
aquatic_mine_region = "Aquatic Mine"
death_chamber_region = "Death Chamber"
meteor_herd_region = "Meteor Herd"
radical_highway_region = "Radical Highway"
white_jungle_region = "White Jungle"
sky_rail_region = "Sky Rail"
final_chase_region = "Final Chase"
iron_gate_region = "Iron Gate"
sand_ocean_region = "Sand Ocean"
lost_colony_region = "Lost Colony"
weapons_bed_region = "Weapons Bed"
cosmic_wall_region = "Cosmic Wall"
dry_lagoon_region = "Dry Lagoon"
egg_quarters_region = "Egg Quarters"
security_hall_region = "Security Hall"
route_280_region = "Route 280"
mad_space_region = "Mad Space"
cannon_core_region = "Cannon Core"
biolizard_region = "Biolizard"
chao_garden_region = "Chao Garden"

77
worlds/sa2b/Options.py Normal file
View File

@ -0,0 +1,77 @@
import typing
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList
class IncludeMissions(Range):
"""
Allows logic to place items in a range of Missions for each level
Each mission setting includes lower settings
1: Base Story Missions
2: 100 Ring Missions
3: Lost Chao Missions
4: Timer Missions
5: Hard Mode Missions
"""
display_name = "Include Missions"
range_start = 1
range_end = 5
default = 2
class EmblemPercentageForCannonsCore(Range):
"""
Allows logic to gate the final mission behind a number of Emblems
"""
display_name = "Emblem Percentage for Cannons Core"
range_start = 0
range_end = 75
default = 50
class NumberOfLevelGates(Range):
"""
Allows logic to gate some levels behind emblem requirements
"""
display_name = "Number of Level Gates"
range_start = 0
range_end = 5
default = 3
class LevelGateDistribution(Choice):
"""
Determines how levels are distributed between level gate regions
Early: Earlier regions will have more levels than later regions
Even: Levels will be evenly distributed between all regions
Late: Later regions will have more levels than earlier regions
"""
display_name = "Level Gate Distribution"
option_early = 0
option_even = 1
option_late = 2
default = 1
class MusicShuffle(Choice):
"""
What type of Music Shuffle is used
Off: No music is shuffled.
Levels: Level music is shuffled.
Full: Level, Menu, and Additional music is shuffled.
"""
display_name = "Music Shuffle Type"
option_none = 0
option_levels = 1
option_full = 2
default = 0
sa2b_options: typing.Dict[str, type(Option)] = {
"DeathLink": DeathLink,
"MusicShuffle": MusicShuffle,
"IncludeMissions": IncludeMissions,
"EmblemPercentageForCannonsCore": EmblemPercentageForCannonsCore,
"NumberOfLevelGates": NumberOfLevelGates,
"LevelGateDistribution": LevelGateDistribution,
}

579
worlds/sa2b/Regions.py Normal file
View File

@ -0,0 +1,579 @@
import typing
from BaseClasses import MultiWorld, Region, Entrance
from .Items import SA2BItem
from .Locations import SA2BLocation
from .Names import LocationName, ItemName
class LevelGate:
gate_levels: typing.List[int]
gate_emblem_count: int
def __init__(self, emblems):
self.gate_emblem_count = emblems
self.gate_levels = list()
shuffleable_regions = [
LocationName.city_escape_region,
LocationName.wild_canyon_region,
LocationName.prison_lane_region,
LocationName.metal_harbor_region,
LocationName.green_forest_region,
LocationName.pumpkin_hill_region,
LocationName.mission_street_region,
LocationName.aquatic_mine_region,
LocationName.route_101_region,
LocationName.hidden_base_region,
LocationName.pyramid_cave_region,
LocationName.death_chamber_region,
LocationName.eternal_engine_region,
LocationName.meteor_herd_region,
LocationName.crazy_gadget_region,
LocationName.final_rush_region,
LocationName.iron_gate_region,
LocationName.dry_lagoon_region,
LocationName.sand_ocean_region,
LocationName.radical_highway_region,
LocationName.egg_quarters_region,
LocationName.lost_colony_region,
LocationName.weapons_bed_region,
LocationName.security_hall_region,
LocationName.white_jungle_region,
LocationName.route_280_region,
LocationName.sky_rail_region,
LocationName.mad_space_region,
LocationName.cosmic_wall_region,
LocationName.final_chase_region,
]
gate_0_blacklist_regions = [
LocationName.hidden_base_region,
LocationName.eternal_engine_region,
LocationName.crazy_gadget_region,
LocationName.security_hall_region,
LocationName.cosmic_wall_region,
]
gate_0_whitelist_regions = [
LocationName.city_escape_region,
LocationName.wild_canyon_region,
LocationName.prison_lane_region,
LocationName.metal_harbor_region,
LocationName.green_forest_region,
LocationName.pumpkin_hill_region,
LocationName.mission_street_region,
LocationName.aquatic_mine_region,
LocationName.route_101_region,
LocationName.pyramid_cave_region,
LocationName.death_chamber_region,
LocationName.meteor_herd_region,
LocationName.final_rush_region,
LocationName.iron_gate_region,
LocationName.dry_lagoon_region,
LocationName.sand_ocean_region,
LocationName.radical_highway_region,
LocationName.egg_quarters_region,
LocationName.lost_colony_region,
LocationName.weapons_bed_region,
LocationName.white_jungle_region,
LocationName.route_280_region,
LocationName.sky_rail_region,
LocationName.mad_space_region,
LocationName.final_chase_region,
]
def create_regions(world, player: int, active_locations):
menu_region = create_region(world, player, active_locations, 'Menu', None, None)
gate_0_region = create_region(world, player, active_locations, 'Gate 0', None, None)
gate_1_region = create_region(world, player, active_locations, 'Gate 1', None, None)
gate_2_region = create_region(world, player, active_locations, 'Gate 2', None, None)
gate_3_region = create_region(world, player, active_locations, 'Gate 3', None, None)
gate_4_region = create_region(world, player, active_locations, 'Gate 4', None, None)
gate_5_region = create_region(world, player, active_locations, 'Gate 5', None, None)
city_escape_region_locations = [
LocationName.city_escape_1,
LocationName.city_escape_2,
LocationName.city_escape_3,
LocationName.city_escape_4,
LocationName.city_escape_5,
LocationName.city_escape_upgrade,
]
city_escape_region = create_region(world, player, active_locations, LocationName.city_escape_region,
city_escape_region_locations, None)
metal_harbor_region_locations = [
LocationName.metal_harbor_1,
LocationName.metal_harbor_2,
LocationName.metal_harbor_3,
LocationName.metal_harbor_4,
LocationName.metal_harbor_5,
LocationName.metal_harbor_upgrade,
]
metal_harbor_region = create_region(world, player, active_locations, LocationName.metal_harbor_region,
metal_harbor_region_locations, None)
green_forest_region_locations = [
LocationName.green_forest_1,
LocationName.green_forest_2,
LocationName.green_forest_3,
LocationName.green_forest_4,
LocationName.green_forest_5,
LocationName.green_forest_upgrade,
]
green_forest_region = create_region(world, player, active_locations, LocationName.green_forest_region,
green_forest_region_locations, None)
pyramid_cave_region_locations = [
LocationName.pyramid_cave_1,
LocationName.pyramid_cave_2,
LocationName.pyramid_cave_3,
LocationName.pyramid_cave_4,
LocationName.pyramid_cave_5,
LocationName.pyramid_cave_upgrade,
]
pyramid_cave_region = create_region(world, player, active_locations, LocationName.pyramid_cave_region,
pyramid_cave_region_locations, None)
crazy_gadget_region_locations = [
LocationName.crazy_gadget_1,
LocationName.crazy_gadget_2,
LocationName.crazy_gadget_3,
LocationName.crazy_gadget_4,
LocationName.crazy_gadget_5,
LocationName.crazy_gadget_upgrade,
]
crazy_gadget_region = create_region(world, player, active_locations, LocationName.crazy_gadget_region,
crazy_gadget_region_locations, None)
final_rush_region_locations = [
LocationName.final_rush_1,
LocationName.final_rush_2,
LocationName.final_rush_3,
LocationName.final_rush_4,
LocationName.final_rush_5,
LocationName.final_rush_upgrade,
]
final_rush_region = create_region(world, player, active_locations, LocationName.final_rush_region,
final_rush_region_locations, None)
prison_lane_region_locations = [
LocationName.prison_lane_1,
LocationName.prison_lane_2,
LocationName.prison_lane_3,
LocationName.prison_lane_4,
LocationName.prison_lane_5,
LocationName.prison_lane_upgrade,
]
prison_lane_region = create_region(world, player, active_locations, LocationName.prison_lane_region,
prison_lane_region_locations, None)
mission_street_region_locations = [
LocationName.mission_street_1,
LocationName.mission_street_2,
LocationName.mission_street_3,
LocationName.mission_street_4,
LocationName.mission_street_5,
LocationName.mission_street_upgrade,
]
mission_street_region = create_region(world, player, active_locations, LocationName.mission_street_region,
mission_street_region_locations, None)
route_101_region_locations = [
LocationName.route_101_1,
LocationName.route_101_2,
LocationName.route_101_3,
LocationName.route_101_4,
LocationName.route_101_5,
]
route_101_region = create_region(world, player, active_locations, LocationName.route_101_region,
route_101_region_locations, None)
hidden_base_region_locations = [
LocationName.hidden_base_1,
LocationName.hidden_base_2,
LocationName.hidden_base_3,
LocationName.hidden_base_4,
LocationName.hidden_base_5,
LocationName.hidden_base_upgrade,
]
hidden_base_region = create_region(world, player, active_locations, LocationName.hidden_base_region,
hidden_base_region_locations, None)
eternal_engine_region_locations = [
LocationName.eternal_engine_1,
LocationName.eternal_engine_2,
LocationName.eternal_engine_3,
LocationName.eternal_engine_4,
LocationName.eternal_engine_5,
LocationName.eternal_engine_upgrade,
]
eternal_engine_region = create_region(world, player, active_locations, LocationName.eternal_engine_region,
eternal_engine_region_locations, None)
wild_canyon_region_locations = [
LocationName.wild_canyon_1,
LocationName.wild_canyon_2,
LocationName.wild_canyon_3,
LocationName.wild_canyon_4,
LocationName.wild_canyon_5,
LocationName.wild_canyon_upgrade,
]
wild_canyon_region = create_region(world, player, active_locations, LocationName.wild_canyon_region,
wild_canyon_region_locations, None)
pumpkin_hill_region_locations = [
LocationName.pumpkin_hill_1,
LocationName.pumpkin_hill_2,
LocationName.pumpkin_hill_3,
LocationName.pumpkin_hill_4,
LocationName.pumpkin_hill_5,
LocationName.pumpkin_hill_upgrade,
]
pumpkin_hill_region = create_region(world, player, active_locations, LocationName.pumpkin_hill_region,
pumpkin_hill_region_locations, None)
aquatic_mine_region_locations = [
LocationName.aquatic_mine_1,
LocationName.aquatic_mine_2,
LocationName.aquatic_mine_3,
LocationName.aquatic_mine_4,
LocationName.aquatic_mine_5,
LocationName.aquatic_mine_upgrade,
]
aquatic_mine_region = create_region(world, player, active_locations, LocationName.aquatic_mine_region,
aquatic_mine_region_locations, None)
death_chamber_region_locations = [
LocationName.death_chamber_1,
LocationName.death_chamber_2,
LocationName.death_chamber_3,
LocationName.death_chamber_4,
LocationName.death_chamber_5,
LocationName.death_chamber_upgrade,
]
death_chamber_region = create_region(world, player, active_locations, LocationName.death_chamber_region,
death_chamber_region_locations, None)
meteor_herd_region_locations = [
LocationName.meteor_herd_1,
LocationName.meteor_herd_2,
LocationName.meteor_herd_3,
LocationName.meteor_herd_4,
LocationName.meteor_herd_5,
LocationName.meteor_herd_upgrade,
]
meteor_herd_region = create_region(world, player, active_locations, LocationName.meteor_herd_region,
meteor_herd_region_locations, None)
radical_highway_region_locations = [
LocationName.radical_highway_1,
LocationName.radical_highway_2,
LocationName.radical_highway_3,
LocationName.radical_highway_4,
LocationName.radical_highway_5,
LocationName.radical_highway_upgrade,
]
radical_highway_region = create_region(world, player, active_locations, LocationName.radical_highway_region,
radical_highway_region_locations, None)
white_jungle_region_locations = [
LocationName.white_jungle_1,
LocationName.white_jungle_2,
LocationName.white_jungle_3,
LocationName.white_jungle_4,
LocationName.white_jungle_5,
LocationName.white_jungle_upgrade,
]
white_jungle_region = create_region(world, player, active_locations, LocationName.white_jungle_region,
white_jungle_region_locations, None)
sky_rail_region_locations = [
LocationName.sky_rail_1,
LocationName.sky_rail_2,
LocationName.sky_rail_3,
LocationName.sky_rail_4,
LocationName.sky_rail_5,
LocationName.sky_rail_upgrade,
]
sky_rail_region = create_region(world, player, active_locations, LocationName.sky_rail_region,
sky_rail_region_locations, None)
final_chase_region_locations = [
LocationName.final_chase_1,
LocationName.final_chase_2,
LocationName.final_chase_3,
LocationName.final_chase_4,
LocationName.final_chase_5,
LocationName.final_chase_upgrade,
]
final_chase_region = create_region(world, player, active_locations, LocationName.final_chase_region,
final_chase_region_locations, None)
iron_gate_region_locations = [
LocationName.iron_gate_1,
LocationName.iron_gate_2,
LocationName.iron_gate_3,
LocationName.iron_gate_4,
LocationName.iron_gate_5,
LocationName.iron_gate_upgrade,
]
iron_gate_region = create_region(world, player, active_locations, LocationName.iron_gate_region,
iron_gate_region_locations, None)
sand_ocean_region_locations = [
LocationName.sand_ocean_1,
LocationName.sand_ocean_2,
LocationName.sand_ocean_3,
LocationName.sand_ocean_4,
LocationName.sand_ocean_5,
LocationName.sand_ocean_upgrade,
]
sand_ocean_region = create_region(world, player, active_locations, LocationName.sand_ocean_region,
sand_ocean_region_locations, None)
lost_colony_region_locations = [
LocationName.lost_colony_1,
LocationName.lost_colony_2,
LocationName.lost_colony_3,
LocationName.lost_colony_4,
LocationName.lost_colony_5,
LocationName.lost_colony_upgrade,
]
lost_colony_region = create_region(world, player, active_locations, LocationName.lost_colony_region,
lost_colony_region_locations, None)
weapons_bed_region_locations = [
LocationName.weapons_bed_1,
LocationName.weapons_bed_2,
LocationName.weapons_bed_3,
LocationName.weapons_bed_4,
LocationName.weapons_bed_5,
LocationName.weapons_bed_upgrade,
]
weapons_bed_region = create_region(world, player, active_locations, LocationName.weapons_bed_region,
weapons_bed_region_locations, None)
cosmic_wall_region_locations = [
LocationName.cosmic_wall_1,
LocationName.cosmic_wall_2,
LocationName.cosmic_wall_3,
LocationName.cosmic_wall_4,
LocationName.cosmic_wall_5,
LocationName.cosmic_wall_upgrade,
]
cosmic_wall_region = create_region(world, player, active_locations, LocationName.cosmic_wall_region,
cosmic_wall_region_locations, None)
dry_lagoon_region_locations = [
LocationName.dry_lagoon_1,
LocationName.dry_lagoon_2,
LocationName.dry_lagoon_3,
LocationName.dry_lagoon_4,
LocationName.dry_lagoon_5,
LocationName.dry_lagoon_upgrade,
]
dry_lagoon_region = create_region(world, player, active_locations, LocationName.dry_lagoon_region,
dry_lagoon_region_locations, None)
egg_quarters_region_locations = [
LocationName.egg_quarters_1,
LocationName.egg_quarters_2,
LocationName.egg_quarters_3,
LocationName.egg_quarters_4,
LocationName.egg_quarters_5,
LocationName.egg_quarters_upgrade,
]
egg_quarters_region = create_region(world, player, active_locations, LocationName.egg_quarters_region,
egg_quarters_region_locations, None)
security_hall_region_locations = [
LocationName.security_hall_1,
LocationName.security_hall_2,
LocationName.security_hall_3,
LocationName.security_hall_4,
LocationName.security_hall_5,
LocationName.security_hall_upgrade,
]
security_hall_region = create_region(world, player, active_locations, LocationName.security_hall_region,
security_hall_region_locations, None)
route_280_region_locations = [
LocationName.route_280_1,
LocationName.route_280_2,
LocationName.route_280_3,
LocationName.route_280_4,
LocationName.route_280_5,
]
route_280_region = create_region(world, player, active_locations, LocationName.route_280_region,
route_280_region_locations, None)
mad_space_region_locations = [
LocationName.mad_space_1,
LocationName.mad_space_2,
LocationName.mad_space_3,
LocationName.mad_space_4,
LocationName.mad_space_5,
LocationName.mad_space_upgrade,
]
mad_space_region = create_region(world, player, active_locations, LocationName.mad_space_region,
mad_space_region_locations, None)
cannon_core_region_locations = [
LocationName.cannon_core_1,
LocationName.cannon_core_2,
LocationName.cannon_core_3,
LocationName.cannon_core_4,
LocationName.cannon_core_5,
]
cannon_core_region = create_region(world, player, active_locations, LocationName.cannon_core_region,
cannon_core_region_locations, None)
chao_garden_region_locations = [
LocationName.chao_beginner_race,
LocationName.chao_jewel_race,
LocationName.chao_challenge_race,
LocationName.chao_hero_race,
LocationName.chao_dark_race,
LocationName.chao_beginner_karate,
LocationName.chao_standard_karate,
LocationName.chao_expert_karate,
LocationName.chao_super_karate,
]
chao_garden_region = create_region(world, player, active_locations, LocationName.chao_garden_region,
chao_garden_region_locations, None)
biolizard_region_locations = [
LocationName.biolizard,
]
biolizard_region = create_region(world, player, active_locations, LocationName.biolizard_region,
biolizard_region_locations, None)
# Set up the regions correctly.
world.regions += [
menu_region,
gate_0_region,
gate_1_region,
gate_2_region,
gate_3_region,
gate_4_region,
gate_5_region,
city_escape_region,
metal_harbor_region,
green_forest_region,
pyramid_cave_region,
crazy_gadget_region,
final_rush_region,
prison_lane_region,
mission_street_region,
route_101_region,
hidden_base_region,
eternal_engine_region,
wild_canyon_region,
pumpkin_hill_region,
aquatic_mine_region,
death_chamber_region,
meteor_herd_region,
radical_highway_region,
white_jungle_region,
sky_rail_region,
final_chase_region,
iron_gate_region,
sand_ocean_region,
lost_colony_region,
weapons_bed_region,
cosmic_wall_region,
dry_lagoon_region,
egg_quarters_region,
security_hall_region,
route_280_region,
mad_space_region,
cannon_core_region,
chao_garden_region,
biolizard_region,
]
def connect_regions(world, player, gates: typing.List[LevelGate], cannon_core_emblems):
names: typing.Dict[str, int] = {}
connect(world, player, names, 'Menu', LocationName.gate_0_region)
connect(world, player, names, LocationName.gate_0_region, LocationName.cannon_core_region,
lambda state: (state.has(ItemName.emblem, player, cannon_core_emblems)))
connect(world, player, names, LocationName.cannon_core_region, LocationName.biolizard_region,
lambda state: (state.can_reach(LocationName.cannon_core_1, "Location", player)))
for i in range(len(gates[0].gate_levels)):
connect(world, player, names, LocationName.gate_0_region, shuffleable_regions[gates[0].gate_levels[i]])
if len(gates) >= 2:
connect(world, player, names, 'Menu', LocationName.gate_1_region,
lambda state: (state.has(ItemName.emblem, player, gates[1].gate_emblem_count)))
for i in range(len(gates[1].gate_levels)):
connect(world, player, names, LocationName.gate_1_region, shuffleable_regions[gates[1].gate_levels[i]])
if len(gates) >= 3:
connect(world, player, names, 'Menu', LocationName.gate_2_region,
lambda state: (state.has(ItemName.emblem, player, gates[2].gate_emblem_count)))
for i in range(len(gates[2].gate_levels)):
connect(world, player, names, LocationName.gate_2_region, shuffleable_regions[gates[2].gate_levels[i]])
if len(gates) >= 4:
connect(world, player, names, 'Menu', LocationName.gate_3_region,
lambda state: (state.has(ItemName.emblem, player, gates[3].gate_emblem_count)))
for i in range(len(gates[3].gate_levels)):
connect(world, player, names, LocationName.gate_3_region, shuffleable_regions[gates[3].gate_levels[i]])
if len(gates) >= 5:
connect(world, player, names, 'Menu', LocationName.gate_4_region,
lambda state: (state.has(ItemName.emblem, player, gates[4].gate_emblem_count)))
for i in range(len(gates[4].gate_levels)):
connect(world, player, names, LocationName.gate_4_region, shuffleable_regions[gates[4].gate_levels[i]])
if len(gates) >= 6:
connect(world, player, names, 'Menu', LocationName.gate_5_region,
lambda state: (state.has(ItemName.emblem, player, gates[5].gate_emblem_count)))
for i in range(len(gates[5].gate_levels)):
connect(world, player, names, LocationName.gate_5_region, shuffleable_regions[gates[5].gate_levels[i]])
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None):
# Shamelessly stolen from the ROR2 definition
ret = Region(name, None, name, player)
ret.world = world
if locations:
for location in locations:
loc_id = active_locations.get(location, 0)
if loc_id:
location = SA2BLocation(player, location, loc_id, ret)
ret.locations.append(location)
if exits:
for exit in exits:
ret.exits.append(Entrance(player, exit, ret))
return ret
def connect(world: MultiWorld, player: int, used_names: typing.Dict[str, int], source: str, target: str,
rule: typing.Optional[typing.Callable] = None):
source_region = world.get_region(source, player)
target_region = world.get_region(target, player)
if target not in used_names:
used_names[target] = 1
name = target
else:
used_names[target] += 1
name = target + (' ' * used_names[target])
connection = Entrance(player, name, source_region)
if rule:
connection.access_rule = rule
source_region.exits.append(connection)
connection.connect(target_region)

396
worlds/sa2b/Rules.py Normal file
View File

@ -0,0 +1,396 @@
from BaseClasses import MultiWorld
from .Names import LocationName, ItemName
from .Locations import first_mission_location_table, second_mission_location_table, third_mission_location_table, \
fourth_mission_location_table, fifth_mission_location_table, \
upgrade_location_table, chao_garden_location_table
from ..AutoWorld import LogicMixin
from ..generic.Rules import add_rule, set_rule
def set_mission_progress_rules(world: MultiWorld, player: int):
for (k1, v1), (k2, v2), (k3, v3), (k4, v4), (k5, v5) in \
zip(sorted(first_mission_location_table.items()),
sorted(second_mission_location_table.items()),
sorted(third_mission_location_table.items()),
sorted(fourth_mission_location_table.items()),
sorted(fifth_mission_location_table.items())):
if world.IncludeMissions[player].value >= 2:
set_rule(world.get_location(k2, player), lambda state, k1=k1: state.can_reach(k1, "Location", player))
if world.IncludeMissions[player].value >= 3:
set_rule(world.get_location(k3, player), lambda state, k2=k2: state.can_reach(k2, "Location", player))
if world.IncludeMissions[player].value >= 4:
set_rule(world.get_location(k4, player), lambda state, k3=k3: state.can_reach(k3, "Location", player))
if world.IncludeMissions[player].value >= 5:
set_rule(world.get_location(k5, player), lambda state, k4=k4: state.can_reach(k4, "Location", player))
def set_mission_upgrade_rules(world: MultiWorld, player: int):
# Mission 1 Upgrade Requirements
add_rule(world.get_location(LocationName.metal_harbor_1, player),
lambda state: state.has(ItemName.sonic_light_shoes, player))
add_rule(world.get_location(LocationName.pumpkin_hill_1, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player))
add_rule(world.get_location(LocationName.mission_street_1, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(world.get_location(LocationName.aquatic_mine_1, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player))
add_rule(world.get_location(LocationName.hidden_base_1, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(world.get_location(LocationName.pyramid_cave_1, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.death_chamber_1, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player) and
state.has(ItemName.knuckles_hammer_gloves, player))
add_rule(world.get_location(LocationName.eternal_engine_1, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_bazooka, player))
add_rule(world.get_location(LocationName.meteor_herd_1, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player) and
state.has(ItemName.knuckles_hammer_gloves, player))
add_rule(world.get_location(LocationName.crazy_gadget_1, player),
lambda state: state.has(ItemName.sonic_light_shoes, player) and
state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_flame_ring, player))
add_rule(world.get_location(LocationName.final_rush_1, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.egg_quarters_1, player),
lambda state: state.has(ItemName.rouge_pick_nails, player))
add_rule(world.get_location(LocationName.lost_colony_1, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.weapons_bed_1, player),
lambda state: state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.security_hall_1, player),
lambda state: state.has(ItemName.rouge_pick_nails, player))
add_rule(world.get_location(LocationName.white_jungle_1, player),
lambda state: state.has(ItemName.shadow_air_shoes, player))
add_rule(world.get_location(LocationName.mad_space_1, player),
lambda state: state.has(ItemName.rouge_pick_nails, player) and
state.has(ItemName.rouge_iron_boots, player))
add_rule(world.get_location(LocationName.cosmic_wall_1, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.cannon_core_1, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.knuckles_hammer_gloves, player) and
state.has(ItemName.knuckles_air_necklace, player) and
state.has(ItemName.sonic_bounce_bracelet, player))
# Mission 2 Upgrade Requirements
if world.IncludeMissions[player].value >= 2:
add_rule(world.get_location(LocationName.metal_harbor_2, player),
lambda state: state.has(ItemName.sonic_light_shoes, player))
add_rule(world.get_location(LocationName.mission_street_2, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(world.get_location(LocationName.hidden_base_2, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(world.get_location(LocationName.death_chamber_2, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player) and
state.has(ItemName.knuckles_hammer_gloves, player))
add_rule(world.get_location(LocationName.eternal_engine_2, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_bazooka, player))
add_rule(world.get_location(LocationName.crazy_gadget_2, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.lost_colony_2, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.weapons_bed_2, player),
lambda state: state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.security_hall_2, player),
lambda state: state.has(ItemName.rouge_pick_nails, player))
add_rule(world.get_location(LocationName.mad_space_2, player),
lambda state: state.has(ItemName.rouge_iron_boots, player))
add_rule(world.get_location(LocationName.cosmic_wall_2, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.cannon_core_2, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.eggman_jet_engine, player))
# Mission 3 Upgrade Requirements
if world.IncludeMissions[player].value >= 3:
add_rule(world.get_location(LocationName.city_escape_3, player),
lambda state: state.has(ItemName.sonic_mystic_melody, player))
add_rule(world.get_location(LocationName.wild_canyon_3, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player) and
state.has(ItemName.knuckles_hammer_gloves, player) and
state.has(ItemName.knuckles_mystic_melody, player))
add_rule(world.get_location(LocationName.prison_lane_3, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_mystic_melody, player))
add_rule(world.get_location(LocationName.metal_harbor_3, player),
lambda state: state.has(ItemName.sonic_light_shoes, player) and
state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_mystic_melody, player))
add_rule(world.get_location(LocationName.green_forest_3, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_mystic_melody, player))
add_rule(world.get_location(LocationName.pumpkin_hill_3, player),
lambda state: state.has(ItemName.knuckles_mystic_melody, player))
add_rule(world.get_location(LocationName.mission_street_3, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_mystic_melody, player))
add_rule(world.get_location(LocationName.aquatic_mine_3, player),
lambda state: state.has(ItemName.knuckles_mystic_melody, player))
add_rule(world.get_location(LocationName.hidden_base_3, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_mystic_melody, player))
add_rule(world.get_location(LocationName.pyramid_cave_3, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_mystic_melody, player))
add_rule(world.get_location(LocationName.death_chamber_3, player),
lambda state: state.has(ItemName.knuckles_mystic_melody, player) and
state.has(ItemName.knuckles_air_necklace, player) and
state.has(ItemName.knuckles_hammer_gloves, player))
add_rule(world.get_location(LocationName.eternal_engine_3, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_mystic_melody, player))
add_rule(world.get_location(LocationName.meteor_herd_3, player),
lambda state: state.has(ItemName.knuckles_mystic_melody, player))
add_rule(world.get_location(LocationName.crazy_gadget_3, player),
lambda state: state.has(ItemName.sonic_light_shoes, player) and
state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_flame_ring, player) and
state.has(ItemName.sonic_mystic_melody, player))
add_rule(world.get_location(LocationName.final_rush_3, player),
lambda state: state.has(ItemName.sonic_light_shoes, player) and
state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_mystic_melody, player))
add_rule(world.get_location(LocationName.iron_gate_3, player),
lambda state: state.has(ItemName.eggman_mystic_melody, player) and
state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.dry_lagoon_3, player),
lambda state: state.has(ItemName.rouge_mystic_melody, player) and
state.has(ItemName.rouge_pick_nails, player) and
state.has(ItemName.rouge_iron_boots, player))
add_rule(world.get_location(LocationName.sand_ocean_3, player),
lambda state: state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.radical_highway_3, player),
lambda state: state.has(ItemName.shadow_mystic_melody, player))
add_rule(world.get_location(LocationName.egg_quarters_3, player),
lambda state: state.has(ItemName.rouge_mystic_melody, player) and
state.has(ItemName.rouge_pick_nails, player) and
state.has(ItemName.rouge_iron_boots, player))
add_rule(world.get_location(LocationName.lost_colony_3, player),
lambda state: state.has(ItemName.eggman_mystic_melody, player) and
state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.weapons_bed_3, player),
lambda state: state.has(ItemName.eggman_mystic_melody, player) and
state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.security_hall_3, player),
lambda state: state.has(ItemName.rouge_treasure_scope, player))
add_rule(world.get_location(LocationName.white_jungle_3, player),
lambda state: state.has(ItemName.shadow_air_shoes, player) and
state.has(ItemName.shadow_mystic_melody, player))
add_rule(world.get_location(LocationName.sky_rail_3, player),
lambda state: state.has(ItemName.shadow_air_shoes, player) and
state.has(ItemName.shadow_mystic_melody, player))
add_rule(world.get_location(LocationName.mad_space_3, player),
lambda state: state.has(ItemName.rouge_mystic_melody, player) and
state.has(ItemName.rouge_iron_boots, player))
add_rule(world.get_location(LocationName.cosmic_wall_3, player),
lambda state: state.has(ItemName.eggman_mystic_melody, player) and
state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.final_chase_3, player),
lambda state: state.has(ItemName.shadow_air_shoes, player) and
state.has(ItemName.shadow_mystic_melody, player))
add_rule(world.get_location(LocationName.cannon_core_3, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.eggman_mystic_melody, player) and
state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.eggman_large_cannon, player) and
state.has(ItemName.rouge_mystic_melody, player) and
state.has(ItemName.knuckles_mystic_melody, player) and
state.has(ItemName.knuckles_hammer_gloves, player) and
state.has(ItemName.knuckles_air_necklace, player) and
state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_light_shoes, player))
# Mission 4 Upgrade Requirements
if world.IncludeMissions[player].value >= 4:
add_rule(world.get_location(LocationName.metal_harbor_4, player),
lambda state: state.has(ItemName.sonic_light_shoes, player))
add_rule(world.get_location(LocationName.pumpkin_hill_4, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player))
add_rule(world.get_location(LocationName.mission_street_4, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(world.get_location(LocationName.aquatic_mine_4, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player))
add_rule(world.get_location(LocationName.hidden_base_4, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(world.get_location(LocationName.pyramid_cave_4, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.death_chamber_4, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player) and
state.has(ItemName.knuckles_hammer_gloves, player))
add_rule(world.get_location(LocationName.eternal_engine_4, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_bazooka, player))
add_rule(world.get_location(LocationName.meteor_herd_4, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player) and
state.has(ItemName.knuckles_hammer_gloves, player))
add_rule(world.get_location(LocationName.crazy_gadget_4, player),
lambda state: state.has(ItemName.sonic_light_shoes, player) and
state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_flame_ring, player))
add_rule(world.get_location(LocationName.final_rush_4, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.egg_quarters_4, player),
lambda state: state.has(ItemName.rouge_pick_nails, player))
add_rule(world.get_location(LocationName.lost_colony_4, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.weapons_bed_4, player),
lambda state: state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.security_hall_4, player),
lambda state: state.has(ItemName.rouge_pick_nails, player))
add_rule(world.get_location(LocationName.white_jungle_4, player),
lambda state: state.has(ItemName.shadow_air_shoes, player))
add_rule(world.get_location(LocationName.mad_space_4, player),
lambda state: state.has(ItemName.rouge_pick_nails, player) and
state.has(ItemName.rouge_iron_boots, player))
add_rule(world.get_location(LocationName.cosmic_wall_4, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.cannon_core_4, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.knuckles_hammer_gloves, player) and
state.has(ItemName.knuckles_air_necklace, player) and
state.has(ItemName.sonic_bounce_bracelet, player))
# Mission 5 Upgrade Requirements
if world.IncludeMissions[player].value >= 5:
add_rule(world.get_location(LocationName.city_escape_5, player),
lambda state: state.has(ItemName.sonic_flame_ring, player))
add_rule(world.get_location(LocationName.wild_canyon_5, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player) and
state.has(ItemName.knuckles_sunglasses, player))
add_rule(world.get_location(LocationName.metal_harbor_5, player),
lambda state: state.has(ItemName.sonic_light_shoes, player))
add_rule(world.get_location(LocationName.pumpkin_hill_5, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player) and
state.has(ItemName.knuckles_sunglasses, player))
add_rule(world.get_location(LocationName.mission_street_5, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_bazooka, player))
add_rule(world.get_location(LocationName.aquatic_mine_5, player),
lambda state: state.has(ItemName.knuckles_mystic_melody, player) and
state.has(ItemName.knuckles_air_necklace, player) and
state.has(ItemName.knuckles_sunglasses, player))
add_rule(world.get_location(LocationName.hidden_base_5, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(world.get_location(LocationName.pyramid_cave_5, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.death_chamber_5, player),
lambda state: state.has(ItemName.knuckles_hammer_gloves, player) and
state.has(ItemName.knuckles_shovel_claws, player) and
state.has(ItemName.knuckles_mystic_melody, player) and
state.has(ItemName.knuckles_air_necklace, player))
add_rule(world.get_location(LocationName.eternal_engine_5, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_bazooka, player))
add_rule(world.get_location(LocationName.meteor_herd_5, player),
lambda state: state.has(ItemName.knuckles_hammer_gloves, player) and
state.has(ItemName.knuckles_sunglasses, player))
add_rule(world.get_location(LocationName.crazy_gadget_5, player),
lambda state: state.has(ItemName.sonic_light_shoes, player) and
state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_flame_ring, player))
add_rule(world.get_location(LocationName.final_rush_5, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.iron_gate_5, player),
lambda state: state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.dry_lagoon_5, player),
lambda state: state.has(ItemName.rouge_treasure_scope, player))
add_rule(world.get_location(LocationName.egg_quarters_5, player),
lambda state: state.has(ItemName.rouge_treasure_scope, player))
add_rule(world.get_location(LocationName.lost_colony_5, player),
lambda state: state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.weapons_bed_5, player),
lambda state: state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.security_hall_5, player),
lambda state: state.has(ItemName.rouge_pick_nails, player) and
state.has(ItemName.rouge_treasure_scope, player))
add_rule(world.get_location(LocationName.white_jungle_5, player),
lambda state: state.has(ItemName.shadow_air_shoes, player) and
state.has(ItemName.shadow_flame_ring, player))
add_rule(world.get_location(LocationName.mad_space_5, player),
lambda state: state.has(ItemName.rouge_pick_nails, player) and
state.has(ItemName.rouge_iron_boots, player))
add_rule(world.get_location(LocationName.cosmic_wall_5, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.cannon_core_5, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.eggman_jet_engine, player) and
state.has(ItemName.knuckles_hammer_gloves, player) and
state.has(ItemName.knuckles_air_necklace, player) and
state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.city_escape_upgrade, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_flame_ring, player))
add_rule(world.get_location(LocationName.wild_canyon_upgrade, player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player))
add_rule(world.get_location(LocationName.prison_lane_upgrade, player),
lambda state: state.has(ItemName.tails_bazooka, player))
add_rule(world.get_location(LocationName.hidden_base_upgrade, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_bazooka, player))
add_rule(world.get_location(LocationName.eternal_engine_upgrade, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(world.get_location(LocationName.meteor_herd_upgrade, player),
lambda state: state.has(ItemName.knuckles_hammer_gloves, player))
add_rule(world.get_location(LocationName.crazy_gadget_upgrade, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.final_rush_upgrade, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(world.get_location(LocationName.iron_gate_upgrade, player),
lambda state: state.has(ItemName.eggman_large_cannon, player))
add_rule(world.get_location(LocationName.dry_lagoon_upgrade, player),
lambda state: state.has(ItemName.rouge_pick_nails, player))
add_rule(world.get_location(LocationName.sand_ocean_upgrade, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(world.get_location(LocationName.radical_highway_upgrade, player),
lambda state: state.has(ItemName.shadow_air_shoes, player))
add_rule(world.get_location(LocationName.security_hall_upgrade, player),
lambda state: state.has(ItemName.rouge_mystic_melody, player) and
state.has(ItemName.rouge_iron_boots, player))
add_rule(world.get_location(LocationName.cosmic_wall_upgrade, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
def set_rules(world: MultiWorld, player: int):
# Mission Progression Rules (Mission 1 begets Mission 2, etc.)
set_mission_progress_rules(world, player)
# Upgrade Requirements for each location
set_mission_upgrade_rules(world, player)
# TODO: Place Level Emblem Requirements Here
# (Edge Case)
# Create some reasonable arbitrary logic for Chao Races to prevent having to grind Chaos Drives in the first level
# for loc in chao_garden_location_table:
# world.get_location(loc, player).add_item_rule(loc, lambda item: False)
world.completion_condition[player] = lambda state: state.has(ItemName.maria, player)

223
worlds/sa2b/__init__.py Normal file
View File

@ -0,0 +1,223 @@
import os
import typing
import math
from BaseClasses import Item, MultiWorld, Tutorial
from .Items import SA2BItem, ItemData, item_table, upgrades_table
from .Locations import SA2BLocation, all_locations, setup_locations
from .Options import sa2b_options
from .Regions import create_regions, shuffleable_regions, connect_regions, LevelGate, gate_0_whitelist_regions, \
gate_0_blacklist_regions
from .Rules import set_rules
from .Names import ItemName, LocationName
from ..AutoWorld import WebWorld, World
import Patch
class SA2BWeb(WebWorld):
theme = "partyTime"
setup_en = Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Sonic Adventure 2: Battle randomizer connected to an Archipelago Multiworld.",
"English",
"setup_en.md",
"setup/en",
["RaspberrySpaceJam", "PoryGone"]
)
tutorials = [setup_en]
def check_for_impossible_shuffle(shuffled_levels: typing.List[int], gate_0_range: int, world: MultiWorld):
blacklist_level_count = 0
for i in range(gate_0_range):
if shuffled_levels[i] in gate_0_blacklist_regions:
blacklist_level_count += 1
if blacklist_level_count == gate_0_range:
index_to_swap = world.random.randint(0, gate_0_range)
for i in range(len(shuffled_levels)):
if shuffled_levels[i] in gate_0_whitelist_regions:
shuffled_levels[i], shuffled_levels[index_to_swap] = shuffled_levels[index_to_swap], shuffled_levels[i]
break
class SA2BWorld(World):
"""
Sonic Adventure 2 Battle is an action platforming game. Play as Sonic, Tails, Knuckles, Shadow, Rogue, and Eggman across 31 stages and prevent the destruction of the earth.
"""
game: str = "Sonic Adventure 2 Battle"
options = sa2b_options
topology_present = False
data_version = 1
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = all_locations
music_map: typing.Dict[int, int]
emblems_for_cannons_core: int
region_emblem_map: typing.Dict[int, int]
web = SA2BWeb()
def _get_slot_data(self):
return {
"ModVersion": 100,
"MusicMap": self.music_map,
"MusicShuffle": self.world.MusicShuffle[self.player],
"DeathLink": self.world.DeathLink[self.player],
"IncludeMissions": self.world.IncludeMissions[self.player].value,
"EmblemPercentageForCannonsCore": self.world.EmblemPercentageForCannonsCore[self.player].value,
"NumberOfLevelGates": self.world.NumberOfLevelGates[self.player].value,
"LevelGateDistribution": self.world.LevelGateDistribution[self.player],
"EmblemsForCannonsCore": self.emblems_for_cannons_core,
"RegionEmblemMap": self.region_emblem_map,
}
def _create_items(self, name: str):
data = item_table[name]
return [self.create_item(name)] * data.quantity
def fill_slot_data(self) -> dict:
slot_data = self._get_slot_data()
slot_data["MusicMap"] = self.music_map
for option_name in sa2b_options:
option = getattr(self.world, option_name)[self.player]
slot_data[option_name] = option.value
return slot_data
def get_levels_per_gate(self) -> list:
levels_per_gate = list()
max_gate_index = self.world.NumberOfLevelGates[self.player]
average_level_count = 30 / (max_gate_index + 1)
levels_added = 0
for i in range(max_gate_index + 1):
levels_per_gate.append(average_level_count)
levels_added += average_level_count
additional_count_iterator = 0
while levels_added < 30:
levels_per_gate[additional_count_iterator] += 1
levels_added += 1
additional_count_iterator += 1 if additional_count_iterator < max_gate_index else -max_gate_index
if self.world.LevelGateDistribution[self.player] == 0 or self.world.LevelGateDistribution[self.player] == 2:
early_distribution = self.world.LevelGateDistribution[self.player] == 0
levels_to_distribute = 5
gate_index_offset = 0
while levels_to_distribute > 0:
if levels_per_gate[0 + gate_index_offset] == 1 or \
levels_per_gate[max_gate_index - gate_index_offset] == 1:
break
if early_distribution:
levels_per_gate[0 + gate_index_offset] += 1
levels_per_gate[max_gate_index - gate_index_offset] -= 1
else:
levels_per_gate[0 + gate_index_offset] -= 1
levels_per_gate[max_gate_index - gate_index_offset] += 1
gate_index_offset += 1
if gate_index_offset > math.floor(max_gate_index / 2):
gate_index_offset = 0
levels_to_distribute -= 1
return levels_per_gate
def generate_basic(self):
self.world.get_location(LocationName.biolizard, self.player).place_locked_item(self.create_item(ItemName.maria))
itempool: typing.List[SA2BItem] = []
# First Missions
total_required_locations = 31
# Mission Locations
total_required_locations *= self.world.IncludeMissions[self.player].value
# Upgrades
total_required_locations += 28
# Fill item pool with all required items
for item in {**upgrades_table}:
itempool += self._create_items(item)
total_emblem_count = total_required_locations - len(itempool)
# itempool += [self.create_item(ItemName.emblem)] * total_emblem_count
self.emblems_for_cannons_core = math.floor(
total_emblem_count * (self.world.EmblemPercentageForCannonsCore[self.player].value / 100.0))
shuffled_region_list = list(range(30))
emblem_requirement_list = list()
self.world.random.shuffle(shuffled_region_list)
levels_per_gate = self.get_levels_per_gate()
check_for_impossible_shuffle(shuffled_region_list, math.ceil(levels_per_gate[0]), self.world)
levels_added_to_gate = 0
total_levels_added = 0
current_gate = 0
current_gate_emblems = 0
gates = list()
gates.append(LevelGate(0))
for i in range(30):
gates[current_gate].gate_levels.append(shuffled_region_list[i])
emblem_requirement_list.append(current_gate_emblems)
levels_added_to_gate += 1
total_levels_added += 1
if levels_added_to_gate >= levels_per_gate[current_gate]:
current_gate += 1
if current_gate > self.world.NumberOfLevelGates[self.player].value:
current_gate = self.world.NumberOfLevelGates[self.player].value
else:
current_gate_emblems = max(
math.floor(total_emblem_count * math.pow(total_levels_added / 30.0, 2.0)), current_gate)
gates.append(LevelGate(current_gate_emblems))
levels_added_to_gate = 0
self.region_emblem_map = dict(zip(shuffled_region_list, emblem_requirement_list))
connect_regions(self.world, self.player, gates, self.emblems_for_cannons_core)
max_required_emblems = max(max(emblem_requirement_list), self.emblems_for_cannons_core)
itempool += [self.create_item(ItemName.emblem)] * max_required_emblems
itempool += [self.create_item(ItemName.emblem, True)] * (total_emblem_count - max_required_emblems)
self.world.itempool += itempool
if self.world.MusicShuffle[self.player] == "levels":
musiclist_o = list(range(0, 47))
musiclist_s = musiclist_o.copy()
self.world.random.shuffle(musiclist_s)
self.music_map = dict(zip(musiclist_o, musiclist_s))
elif self.world.MusicShuffle[self.player] == "full":
musiclist_o = list(range(0, 78))
musiclist_s = musiclist_o.copy()
self.world.random.shuffle(musiclist_s)
self.music_map = dict(zip(musiclist_o, musiclist_s))
else:
self.music_map = dict()
def create_regions(self):
location_table = setup_locations(self.world, self.player)
create_regions(self.world, self.player, location_table)
def create_item(self, name: str, force_non_progression=False) -> Item:
data = item_table[name]
created_item = SA2BItem(name, data.progression, data.code, self.player)
if name == ItemName.emblem:
created_item.skip_in_prog_balancing = True
if force_non_progression:
created_item.advancement = False
return created_item
def set_rules(self):
set_rules(self.world, self.player)
@classmethod
def stage_fill_hook(cls, world, progitempool, nonexcludeditempool, localrestitempool, nonlocalrestitempool,
restitempool, fill_locations):
if world.get_game_players("Sonic Adventure 2 Battle"):
progitempool.sort(
key=lambda item: 0 if (item.name != 'Emblem') else 1)

View File

@ -0,0 +1,30 @@
# Sonic Adventure 2: Battle
## 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?
The randomizer shuffles emblems and upgrade items into the AP item pool. The story mode is disabled, but stage select is available from the start. Levels can be locked behind gates requiring a certain number of emblems to unlock. Cannons Core will be locked behind a percentage of all available emblems, and completing Cannons Core will unlock the Biolizard boss. Progress towards unlocking Cannons Core and the next stage gate will be displayed on the Stage Select screen.
## What is the goal of Sonic Adventure 2: Battle when randomized?
The goal is to unlock and complete Cannons Core Mission 1, then complete the Biolizard boss fight.
## What items and locations get shuffled?
All 30 story stages leading up to Cannons Core will be shuffled and can be optionally placed behind emblem requirement gates. All emblems from the selected mission range and all 28 character upgrade items get shuffled.
## Which items can be in another player's world?
Any shuffled item can be in other players' worlds.
## What does another world's item look like in Sonic Adventure 2: Battle
Emblems have no visualization in the randomizer and items all retain their original appearance. You won't know if an item belongs to another player until you collect.
## When the player receives an emblem or item, what happens?
When the player collects an emblem or item, text will appear on screen to indicate who the item was for and what the item was. When collecting items in a level, the orignal item collection text will display and will likely not be correct.

View File

@ -0,0 +1,88 @@
# Sonic Adventure 2: Battle Randomizer Setup Guide
## Required Software
- Sonic Adventure 2: Battle from: [Sonic Adventure 2: Battle Steam Store Page](https://store.steampowered.com/app/213610/Sonic_Adventure_2/)
- Currently the DLC is not required for this mod, but it will be required in a future release.
- Sonic Adventure 2 Mod Loader from: [Sonic Retro Mod Loader Page](http://info.sonicretro.org/SA2_Mod_Loader )
- Microsoft Visual C++ 2013 from: [Microsoft Visual C++ 2013 Redistributable Page](https://www.microsoft.com/en-us/download/details.aspx?id=40784 )
- Archipelago Mod for Sonic Adventure 2: Battle
from: [Sonic Adventure 2: Battle Archipelago Randomizer Mod Releases Page](https://github.com/PoryGone/SA2B_Archipelago/releases/)
Optional:
- Sonic Adventure 2: Battle Archipelago PopTracker pack from: [SA2B_AP_Tracker Releases Page](https://github.com/PoryGone/SA2B_AP_Tracker/releases/)
## Installation Procedures
1. Install Sonic Adventure 2: Battle from Steam.
2. Launch the game at least once without mods.
3. Install Sonic Adventure 2 Mod Loader as per its instructions.
4. The folder you installed the Sonic Adventure 2 Mod Loader into will now have a `/mods` directory.
5. Unpack the Archipelago Mod into this folder, so that `/mods/SA2B_Archipelago` is a valid path.
6. In the SA2B_Archipelago folder, run the `CopyAPCppDLL.bat` script (a window will very quickly pop up and go away).
7. Launch the `SA2ModManager.exe` and make sure the SA2B_Archipelago mod is listed and enabled.
## Joining a MultiWorld Game
1. Before launching the game, run the `SA2ModManager.exe`, select the SA2B_Archipelago mod, and hit the `Configure...` button.
2. For the `Server IP` field under `AP Settings`, enter the address of the server, such as archipelago.gg:38281, your server host should be able to tell you this.
3. For the `PlayerName` field under `AP Settings`, enter your "name" field from the yaml, or website config.
4. For the `Password` field under `AP Settings`, enter the server password if one exists, otherwise leave blank.
5. Click The `Save` button then hit `Save & Play` to launch the game.
6. Create a new save to connect to the MultiWorld game. A "Connected to Archipelago" message will appear if you sucessfully connect. If you close the game during play, you can reconnect to the MultiWorld game by selecting the same save file slot.
## Additional Options
Some additional settings related to the Archipelago messages in game can be adjusted in the SA2ModManager if you select `Configure...` on the SA2B_Archipelago mod. This settings will be under a `General Settings` tab.
- Message Display Count: This is the maximum number of Archipelago messages that can be displayed on screen at any given time.
- Message Display Duration: This dictates how long Archipelago messages are displayed on screen (in seconds).
- Message Font Size: The is the size of the font used to display the messages from Archipelago.
## Troubleshooting
- "The following mods didn't load correctly: SA2B_Archipelago: DLL error - The specified module could not be found."
- Make sure the `APCpp.dll` is in the same folder as the `sonic2app.exe`. (See Installation Procedures step 6)
- Game is running too fast (Like Sonic).
- If using an NVidia graphics card:
1. Open the NVIDIA Control Panel.
2. Select `Manage 3D Settings` under `3D settings` on the left.
3. Select the `Program Settings` tab in the main window.
4. Click the `Add` button and select `sonic2app.exe` (or browse to the exe location), then click `Add Selected Program`.
5. Under `Specify the settings for this program:`, find the `Max Frame Rate` feature and click the Setting column for that feature.
6. Choose the `On` radial option and in the input box next to the slide enter a value of 60 (or 59 if 60 causes the game to crash).
- Controller input is not working.
1. Run the Launcher.exe which should be in the same folder as the SA2ModManager.
2. Select the `Player` tab and reselect the controller for the player 1 input method.
3. Click the `Save settings and launch SONIC ADVENTURE 2` button. (Any mod manager settings will apply even if the game is launched this way rather than through the mod manager)
- Game crashes after display logos.
- This may be caused by a high monitor refresh rate.
- Change the monitor refresh rate to 60 Hz [Change display refresh rate on Windows] (https://support.microsoft.com/en-us/windows/change-your-display-refresh-rate-in-windows-c8ea729e-0678-015c-c415-f806f04aae5a)
- This may also be fixed by setting Windows 7 compatibility mode on the sonic app:
1. Right click on the sonic2app.exe and select `Properties`.
2. Select the `Compatibility` tab.
3. Check the `Run this program in compatility mode for:` box and select Windows 7 in the drop down.
4. Click the `Apply` button.
- No resolution options in the Launcher.exe.
- In the `Graphics device` dropdown, select the device and display you plan to run the game on. The `Resolution` dropdown should populate once a graphics device is selected.
## Save File Safeguard (Advanced Option)
The mod contains a save file safeguard which associates a savefile to a specific Archipelago seed. By default, save files can only connect to Archipelago servers that match their seed. The safeguard can be disabled in the mod config.ini by setting `IgnoreFileSafety` to true. This is NOT recommended for the standard user as it will allow any save file to connect and send items to the Archipelago server.