SA2B v1.1.0 (#673)

Co-authored-by: RaspberrySpaceJam <tyler.summers@gmail.com>
This commit is contained in:
PoryGone 2022-06-20 15:12:13 -04:00 committed by GitHub
parent 03e9034a98
commit a9e530721d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 762 additions and 91 deletions

69
worlds/sa2b/GateBosses.py Normal file
View File

@ -0,0 +1,69 @@
import typing
speed_characters_1 = "Sonic vs Shadow 1"
speed_characters_2 = "Sonic vs Shadow 2"
mech_characters_1 = "Tails vs Eggman 1"
mech_characters_2 = "Tails vs Eggman 2"
hunt_characters_1 = "Knuckles vs Rouge 1"
big_foot = "F-6t BIG FOOT"
hot_shot = "B-3x HOT SHOT"
flying_dog = "R-1/A FLYING DOG"
egg_golem_sonic = "Egg Golem (Sonic)"
egg_golem_eggman = "Egg Golem (Eggman)"
king_boom_boo = "King Boom Boo"
gate_bosses_no_requirements_table = {
speed_characters_1: 0,
speed_characters_2: 1,
mech_characters_1: 2,
mech_characters_2: 3,
hunt_characters_1: 4,
big_foot: 5,
hot_shot: 6,
flying_dog: 7,
egg_golem_sonic: 8,
egg_golem_eggman: 9,
}
gate_bosses_with_requirements_table = {
king_boom_boo: 10,
}
all_gate_bosses_table = {
**gate_bosses_no_requirements_table,
**gate_bosses_with_requirements_table,
}
def get_boss_name(boss: int):
for key, value in gate_bosses_no_requirements_table.items():
if value == boss:
return key
for key, value in gate_bosses_with_requirements_table.items():
if value == boss:
return key
def boss_has_requirement(boss: int):
return boss >= len(gate_bosses_no_requirements_table)
def get_gate_bosses(world, player: int):
selected_bosses: typing.List[int] = []
boss_gates: typing.List[int] = []
available_bosses: typing.List[str] = list(gate_bosses_no_requirements_table.keys())
world.random.shuffle(available_bosses)
halfway = False
for x in range(world.number_of_level_gates[player]):
if (not halfway) and ((x + 1) / world.number_of_level_gates[player]) > 0.5:
available_bosses.extend(gate_bosses_with_requirements_table)
world.random.shuffle(available_bosses)
halfway = True
selected_bosses.append(all_gate_bosses_table[available_bosses[0]])
boss_gates.append(x + 1)
available_bosses.remove(available_bosses[0])
bosses: typing.Dict[int, int] = dict(zip(boss_gates, selected_bosses))
return bosses

View File

@ -7,6 +7,7 @@ from .Names import ItemName
class ItemData(typing.NamedTuple):
code: typing.Optional[int]
progression: bool
trap: bool = False
quantity: int = 1
event: bool = False
@ -62,6 +63,23 @@ upgrades_table = {
ItemName.rouge_iron_boots: ItemData(0xFF001C, True),
}
junk_table = {
ItemName.five_rings: ItemData(0xFF0020, False),
ItemName.ten_rings: ItemData(0xFF0021, False),
ItemName.twenty_rings: ItemData(0xFF0022, False),
ItemName.extra_life: ItemData(0xFF0023, False),
ItemName.shield: ItemData(0xFF0024, False),
ItemName.magnetic_shield: ItemData(0xFF0025, False),
ItemName.invincibility: ItemData(0xFF0026, False),
}
trap_table = {
ItemName.omochao_trap: ItemData(0xFF0030, False, True),
ItemName.timestop_trap: ItemData(0xFF0031, False, True),
ItemName.confuse_trap: ItemData(0xFF0032, False, True),
ItemName.tiny_trap: ItemData(0xFF0033, False, True),
}
event_table = {
ItemName.maria: ItemData(0xFF001D, True),
}
@ -70,6 +88,8 @@ event_table = {
item_table = {
**emblems_table,
**upgrades_table,
**junk_table,
**trap_table,
**event_table,
}

View File

@ -220,17 +220,92 @@ upgrade_location_table = {
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,
boss_gate_location_table = {
LocationName.gate_1_boss: 0xFF0100,
LocationName.gate_2_boss: 0xFF0101,
LocationName.gate_3_boss: 0xFF0102,
LocationName.gate_4_boss: 0xFF0103,
LocationName.gate_5_boss: 0xFF0104,
}
LocationName.chao_beginner_karate: 0xFF00C5,
LocationName.chao_standard_karate: 0xFF00C6,
LocationName.chao_expert_karate: 0xFF00C7,
LocationName.chao_super_karate: 0xFF00C8,
chao_garden_beginner_location_table = {
LocationName.chao_race_crab_pool_1: 0xFF0200,
LocationName.chao_race_crab_pool_2: 0xFF0201,
LocationName.chao_race_crab_pool_3: 0xFF0202,
LocationName.chao_race_stump_valley_1: 0xFF0203,
LocationName.chao_race_stump_valley_2: 0xFF0204,
LocationName.chao_race_stump_valley_3: 0xFF0205,
LocationName.chao_race_mushroom_forest_1: 0xFF0206,
LocationName.chao_race_mushroom_forest_2: 0xFF0207,
LocationName.chao_race_mushroom_forest_3: 0xFF0208,
LocationName.chao_race_block_canyon_1: 0xFF0209,
LocationName.chao_race_block_canyon_2: 0xFF020A,
LocationName.chao_race_block_canyon_3: 0xFF020B,
LocationName.chao_beginner_karate: 0xFF0300,
}
chao_garden_intermediate_location_table = {
LocationName.chao_race_aquamarine_1: 0xFF020C,
LocationName.chao_race_aquamarine_2: 0xFF020D,
LocationName.chao_race_aquamarine_3: 0xFF020E,
LocationName.chao_race_aquamarine_4: 0xFF020F,
LocationName.chao_race_aquamarine_5: 0xFF0210,
LocationName.chao_race_topaz_1: 0xFF0211,
LocationName.chao_race_topaz_2: 0xFF0212,
LocationName.chao_race_topaz_3: 0xFF0213,
LocationName.chao_race_topaz_4: 0xFF0214,
LocationName.chao_race_topaz_5: 0xFF0215,
LocationName.chao_race_peridot_1: 0xFF0216,
LocationName.chao_race_peridot_2: 0xFF0217,
LocationName.chao_race_peridot_3: 0xFF0218,
LocationName.chao_race_peridot_4: 0xFF0219,
LocationName.chao_race_peridot_5: 0xFF021A,
LocationName.chao_race_garnet_1: 0xFF021B,
LocationName.chao_race_garnet_2: 0xFF021C,
LocationName.chao_race_garnet_3: 0xFF021D,
LocationName.chao_race_garnet_4: 0xFF021E,
LocationName.chao_race_garnet_5: 0xFF021F,
LocationName.chao_race_onyx_1: 0xFF0220,
LocationName.chao_race_onyx_2: 0xFF0221,
LocationName.chao_race_onyx_3: 0xFF0222,
LocationName.chao_race_onyx_4: 0xFF0223,
LocationName.chao_race_onyx_5: 0xFF0224,
LocationName.chao_race_diamond_1: 0xFF0225,
LocationName.chao_race_diamond_2: 0xFF0226,
LocationName.chao_race_diamond_3: 0xFF0227,
LocationName.chao_race_diamond_4: 0xFF0228,
LocationName.chao_race_diamond_5: 0xFF0229,
LocationName.chao_standard_karate: 0xFF0301,
}
chao_garden_expert_location_table = {
LocationName.chao_race_challenge_1: 0xFF022A,
LocationName.chao_race_challenge_2: 0xFF022B,
LocationName.chao_race_challenge_3: 0xFF022C,
LocationName.chao_race_challenge_4: 0xFF022D,
LocationName.chao_race_challenge_5: 0xFF022E,
LocationName.chao_race_challenge_6: 0xFF022F,
LocationName.chao_race_challenge_7: 0xFF0230,
LocationName.chao_race_challenge_8: 0xFF0231,
LocationName.chao_race_challenge_9: 0xFF0232,
LocationName.chao_race_challenge_10: 0xFF0233,
LocationName.chao_race_challenge_11: 0xFF0234,
LocationName.chao_race_challenge_12: 0xFF0235,
LocationName.chao_race_hero_1: 0xFF0236,
LocationName.chao_race_hero_2: 0xFF0237,
LocationName.chao_race_hero_3: 0xFF0238,
LocationName.chao_race_hero_4: 0xFF0239,
LocationName.chao_race_dark_1: 0xFF023A,
LocationName.chao_race_dark_2: 0xFF023B,
LocationName.chao_race_dark_3: 0xFF023C,
LocationName.chao_race_dark_4: 0xFF023D,
LocationName.chao_expert_karate: 0xFF0302,
LocationName.chao_super_karate: 0xFF0303,
}
other_location_table = {
@ -245,15 +320,57 @@ all_locations = {
**fourth_mission_location_table,
**fifth_mission_location_table,
**upgrade_location_table,
**chao_garden_location_table,
**boss_gate_location_table,
**chao_garden_beginner_location_table,
**chao_garden_intermediate_location_table,
**chao_garden_expert_location_table,
**other_location_table,
}
location_table = {}
boss_gate_set = [
LocationName.gate_1_boss,
LocationName.gate_2_boss,
LocationName.gate_3_boss,
LocationName.gate_4_boss,
LocationName.gate_5_boss,
]
chao_karate_set = [
LocationName.chao_beginner_karate,
LocationName.chao_standard_karate,
LocationName.chao_expert_karate,
LocationName.chao_super_karate,
]
chao_race_prize_set = [
LocationName.chao_race_crab_pool_3,
LocationName.chao_race_stump_valley_3,
LocationName.chao_race_mushroom_forest_3,
LocationName.chao_race_block_canyon_3,
LocationName.chao_race_aquamarine_5,
LocationName.chao_race_topaz_5,
LocationName.chao_race_peridot_5,
LocationName.chao_race_garnet_5,
LocationName.chao_race_onyx_5,
LocationName.chao_race_diamond_5,
LocationName.chao_race_challenge_4,
LocationName.chao_race_challenge_8,
LocationName.chao_race_challenge_12,
LocationName.chao_race_hero_2,
LocationName.chao_race_hero_4,
LocationName.chao_race_dark_2,
LocationName.chao_race_dark_4,
]
def setup_locations(world, player: int):
location_table = {**first_mission_location_table}
location_table = {}
chao_location_table = {}
location_table.update({**first_mission_location_table})
if world.include_missions[player].value >= 2:
location_table.update({**second_mission_location_table})
@ -268,9 +385,30 @@ def setup_locations(world, player: int):
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})
if world.chao_garden_difficulty[player].value >= 1:
chao_location_table.update({**chao_garden_beginner_location_table})
if world.chao_garden_difficulty[player].value >= 2:
chao_location_table.update({**chao_garden_intermediate_location_table})
if world.chao_garden_difficulty[player].value >= 3:
chao_location_table.update({**chao_garden_expert_location_table})
for key, value in chao_location_table.items():
if key in chao_karate_set:
if world.include_chao_karate[player]:
location_table[key] = value
elif key not in chao_race_prize_set:
if world.chao_race_checks[player] == "all":
location_table[key] = value
else:
location_table[key] = value
for x in range(len(boss_gate_set)):
if x < world.number_of_level_gates[player].value:
location_table[boss_gate_set[x]] = boss_gate_location_table[boss_gate_set[x]]
return location_table

View File

@ -36,4 +36,17 @@ rouge_pick_nails = "Rouge - Pick Nails"
rouge_treasure_scope = "Rouge - Treasure Scope"
rouge_iron_boots = "Rouge - Iron Boots"
maria = "What Maria Wanted"
five_rings = "Five Rings"
ten_rings = "Ten Rings"
twenty_rings = "Twenty Rings"
extra_life = "Extra Life"
shield = "Shield"
magnetic_shield = "Magnetic Shield"
invincibility = "Invincibility"
omochao_trap = "OmoTrap"
timestop_trap = "Chaos Control Trap"
confuse_trap = "Confusion Trap"
tiny_trap = "Tiny Trap"
maria = "What Maria Wanted"

View File

@ -196,16 +196,85 @@ cannon_core_3 = "Cannon Core - 3"
cannon_core_4 = "Cannon Core - 4"
cannon_core_5 = "Cannon Core - 5"
# Boss Definitions
gate_1_boss = "Gate 1 Boss"
gate_2_boss = "Gate 2 Boss"
gate_3_boss = "Gate 3 Boss"
gate_4_boss = "Gate 4 Boss"
gate_5_boss = "Gate 5 Boss"
# 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"
chao_race_crab_pool_1 = "Chao Race - Beginner - Crab Pool 1"
chao_race_crab_pool_2 = "Chao Race - Beginner - Crab Pool 2"
chao_race_crab_pool_3 = "Chao Race - Beginner - Crab Pool 3"
chao_race_stump_valley_1 = "Chao Race - Beginner - Stump Valley 1"
chao_race_stump_valley_2 = "Chao Race - Beginner - Stump Valley 2"
chao_race_stump_valley_3 = "Chao Race - Beginner - Stump Valley 3"
chao_race_mushroom_forest_1 = "Chao Race - Beginner - Mushroom Forest 1"
chao_race_mushroom_forest_2 = "Chao Race - Beginner - Mushroom Forest 2"
chao_race_mushroom_forest_3 = "Chao Race - Beginner - Mushroom Forest 3"
chao_race_block_canyon_1 = "Chao Race - Beginner - Block Canyon 1"
chao_race_block_canyon_2 = "Chao Race - Beginner - Block Canyon 2"
chao_race_block_canyon_3 = "Chao Race - Beginner - Block Canyon 3"
chao_race_aquamarine_1 = "Chao Race - Jewel - Aquamarine 1"
chao_race_aquamarine_2 = "Chao Race - Jewel - Aquamarine 2"
chao_race_aquamarine_3 = "Chao Race - Jewel - Aquamarine 3"
chao_race_aquamarine_4 = "Chao Race - Jewel - Aquamarine 4"
chao_race_aquamarine_5 = "Chao Race - Jewel - Aquamarine 5"
chao_race_topaz_1 = "Chao Race - Jewel - Topaz 1"
chao_race_topaz_2 = "Chao Race - Jewel - Topaz 2"
chao_race_topaz_3 = "Chao Race - Jewel - Topaz 3"
chao_race_topaz_4 = "Chao Race - Jewel - Topaz 4"
chao_race_topaz_5 = "Chao Race - Jewel - Topaz 5"
chao_race_peridot_1 = "Chao Race - Jewel - Peridot 1"
chao_race_peridot_2 = "Chao Race - Jewel - Peridot 2"
chao_race_peridot_3 = "Chao Race - Jewel - Peridot 3"
chao_race_peridot_4 = "Chao Race - Jewel - Peridot 4"
chao_race_peridot_5 = "Chao Race - Jewel - Peridot 5"
chao_race_garnet_1 = "Chao Race - Jewel - Garnet 1"
chao_race_garnet_2 = "Chao Race - Jewel - Garnet 2"
chao_race_garnet_3 = "Chao Race - Jewel - Garnet 3"
chao_race_garnet_4 = "Chao Race - Jewel - Garnet 4"
chao_race_garnet_5 = "Chao Race - Jewel - Garnet 5"
chao_race_onyx_1 = "Chao Race - Jewel - Onyx 1"
chao_race_onyx_2 = "Chao Race - Jewel - Onyx 2"
chao_race_onyx_3 = "Chao Race - Jewel - Onyx 3"
chao_race_onyx_4 = "Chao Race - Jewel - Onyx 4"
chao_race_onyx_5 = "Chao Race - Jewel - Onyx 5"
chao_race_diamond_1 = "Chao Race - Jewel - Diamond 1"
chao_race_diamond_2 = "Chao Race - Jewel - Diamond 2"
chao_race_diamond_3 = "Chao Race - Jewel - Diamond 3"
chao_race_diamond_4 = "Chao Race - Jewel - Diamond 4"
chao_race_diamond_5 = "Chao Race - Jewel - Diamond 5"
chao_race_challenge_1 = "Chao Race - Challenge 1"
chao_race_challenge_2 = "Chao Race - Challenge 2"
chao_race_challenge_3 = "Chao Race - Challenge 3"
chao_race_challenge_4 = "Chao Race - Challenge 4"
chao_race_challenge_5 = "Chao Race - Challenge 5"
chao_race_challenge_6 = "Chao Race - Challenge 6"
chao_race_challenge_7 = "Chao Race - Challenge 7"
chao_race_challenge_8 = "Chao Race - Challenge 8"
chao_race_challenge_9 = "Chao Race - Challenge 9"
chao_race_challenge_10 = "Chao Race - Challenge 10"
chao_race_challenge_11 = "Chao Race - Challenge 11"
chao_race_challenge_12 = "Chao Race - Challenge 12"
chao_race_hero_1 = "Chao Race - Hero 1"
chao_race_hero_2 = "Chao Race - Hero 2"
chao_race_hero_3 = "Chao Race - Hero 3"
chao_race_hero_4 = "Chao Race - Hero 4"
chao_race_dark_1 = "Chao Race - Dark 1"
chao_race_dark_2 = "Chao Race - Dark 2"
chao_race_dark_3 = "Chao Race - Dark 3"
chao_race_dark_4 = "Chao Race - Dark 4"
chao_beginner_karate = "Chao Karate - Beginner"
chao_standard_karate = "Chao Karate - Standard"
chao_expert_karate = "Chao Karate - Expert"
chao_super_karate = "Chao Karate - Super"
# Other Definitions
green_hill = "Green Hill"
@ -221,6 +290,12 @@ gate_3_region = "Gate 3"
gate_4_region = "Gate 4"
gate_5_region = "Gate 5"
gate_1_boss_region = "Gate 1 Boss"
gate_2_boss_region = "Gate 2 Boss"
gate_3_boss_region = "Gate 3 Boss"
gate_4_boss_region = "Gate 4 Boss"
gate_5_boss_region = "Gate 5 Boss"
city_escape_region = "City Escape"
metal_harbor_region = "Metal Harbor"
green_forest_region = "Green Forest"
@ -261,4 +336,6 @@ cannon_core_region = "Cannon Core"
biolizard_region = "Biolizard"
chao_garden_region = "Chao Garden"
chao_garden_beginner_region = "Chao Garden - Beginner"
chao_garden_intermediate_region = "Chao Garden - Intermediate"
chao_garden_expert_region = "Chao Garden - Expert"

View File

@ -3,6 +3,65 @@ import typing
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList
class BaseTrapWeight(Choice):
"""
Base Class for Trap Weights
"""
option_none = 0
option_low = 1
option_medium = 2
option_high = 4
default = 2
class OmochaoTrapWeight(BaseTrapWeight):
"""
Likelihood of a receiving a trap which spawns several Omochao around the player
"""
display_name = "OmoTrap Weight"
class TimestopTrapWeight(BaseTrapWeight):
"""
Likelihood of a receiving a trap which briefly stops time
"""
display_name = "Chaos Control Trap Weight"
class ConfusionTrapWeight(BaseTrapWeight):
"""
Likelihood of a receiving a trap which causes the controls to be skewed for a period of time
"""
display_name = "Confusion Trap Weight"
class TinyTrapWeight(BaseTrapWeight):
"""
Likelihood of a receiving a trap which causes the player to become tiny
"""
display_name = "Tiny Trap Weight"
class JunkFillPercentage(Range):
"""
Replace a percentage of non-required emblems in the item pool with random junk items
"""
display_name = "Junk Fill Percentage"
range_start = 0
range_end = 100
default = 50
class TrapFillPercentage(Range):
"""
Replace a percentage of junk items in the item pool with random traps
"""
display_name = "Trap Fill Percentage"
range_start = 0
range_end = 100
default = 0
class IncludeMissions(Range):
"""
Allows logic to place items in a range of Missions for each level
@ -31,7 +90,7 @@ class EmblemPercentageForCannonsCore(Range):
class NumberOfLevelGates(Range):
"""
Allows logic to gate some levels behind emblem requirements
The number emblem-locked gates which lock sets of levels
"""
display_name = "Number of Level Gates"
range_start = 0
@ -53,6 +112,65 @@ class LevelGateDistribution(Choice):
default = 1
class LevelGateCosts(Choice):
"""
Determines how many emblems are required to unlock level gates
"""
display_name = "Level Gate Costs"
option_low = 0
option_medium = 1
option_high = 2
default = 2
class RequiredRank(Choice):
"""
Determines what minimum Rank is required to send a check for a mission
"""
display_name = "Required Rank"
option_e = 0
option_d = 1
option_c = 2
option_b = 3
option_a = 4
default = 0
class ChaoGardenDifficulty(Choice):
"""
Determines the number of chao garden difficulty levels included. Easier difficulty settings means fewer chao garden checks
None: No Chao Garden Activities have checks
Beginner: Beginner Races
Intermediate: Beginner and Jewel Races
Expert: Beginner, Jewel, Challenge, Hero, and Dark Races
"""
display_name = "Chao Garden Difficulty"
option_none = 0
option_beginner = 1
option_intermediate = 2
option_expert = 3
default = 0
class IncludeChaoKarate(Toggle):
"""
Determines whether the Chao Karate should be included as checks (Note: This setting requires purchase of the "Battle" DLC)
"""
display_name = "Include Chao Karate"
class ChaoRaceChecks(Choice):
"""
Determines which Chao Races grant checks
All: Each individual race grants a check
Prize: Only the races which grant Chao Toys grant checks (final race of each Beginner and Jewel cup, 4th, 8th, and 12th Challenge Races, 2nd and 4th Hero and Dark Races)
"""
display_name = "Chao Race Checks"
option_all = 0
option_prize = 1
default = 0
class MusicShuffle(Choice):
"""
What type of Music Shuffle is used
@ -68,10 +186,21 @@ class MusicShuffle(Choice):
sa2b_options: typing.Dict[str, type(Option)] = {
"death_link": DeathLink,
"music_shuffle": MusicShuffle,
"include_missions": IncludeMissions,
"required_rank": RequiredRank,
"emblem_percentage_for_cannons_core": EmblemPercentageForCannonsCore,
"number_of_level_gates": NumberOfLevelGates,
"level_gate_distribution": LevelGateDistribution,
"level_gate_costs": LevelGateCosts,
"chao_garden_difficulty": ChaoGardenDifficulty,
"include_chao_karate": IncludeChaoKarate,
"chao_race_checks": ChaoRaceChecks,
"junk_fill_percentage": JunkFillPercentage,
"trap_fill_percentage": TrapFillPercentage,
"omochao_trap_weight": OmochaoTrapWeight,
"timestop_trap_weight": TimestopTrapWeight,
"confusion_trap_weight": ConfusionTrapWeight,
"tiny_trap_weight": TinyTrapWeight,
"music_shuffle": MusicShuffle,
"death_link": DeathLink,
}

View File

@ -2,8 +2,9 @@ import typing
from BaseClasses import MultiWorld, Region, Entrance
from .Items import SA2BItem
from .Locations import SA2BLocation
from .Locations import SA2BLocation, boss_gate_location_table, boss_gate_set
from .Names import LocationName, ItemName
from .GateBosses import get_boss_name, all_gate_bosses_table, king_boom_boo
class LevelGate:
@ -93,6 +94,11 @@ def create_regions(world, player: int, active_locations):
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)
gate_1_boss_region = create_region(world, player, active_locations, 'Gate 1 Boss', [LocationName.gate_1_boss], None)
gate_2_boss_region = create_region(world, player, active_locations, 'Gate 2 Boss', [LocationName.gate_2_boss], None)
gate_3_boss_region = create_region(world, player, active_locations, 'Gate 3 Boss', [LocationName.gate_3_boss], None)
gate_4_boss_region = create_region(world, player, active_locations, 'Gate 4 Boss', [LocationName.gate_4_boss], None)
gate_5_boss_region = create_region(world, player, active_locations, 'Gate 5 Boss', [LocationName.gate_5_boss], None)
city_escape_region_locations = [
LocationName.city_escape_1,
@ -432,19 +438,91 @@ def create_regions(world, player: int, active_locations):
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,
chao_garden_beginner_region_locations = [
LocationName.chao_race_crab_pool_1,
LocationName.chao_race_crab_pool_2,
LocationName.chao_race_crab_pool_3,
LocationName.chao_race_stump_valley_1,
LocationName.chao_race_stump_valley_2,
LocationName.chao_race_stump_valley_3,
LocationName.chao_race_mushroom_forest_1,
LocationName.chao_race_mushroom_forest_2,
LocationName.chao_race_mushroom_forest_3,
LocationName.chao_race_block_canyon_1,
LocationName.chao_race_block_canyon_2,
LocationName.chao_race_block_canyon_3,
LocationName.chao_beginner_karate,
]
chao_garden_beginner_region = create_region(world, player, active_locations, LocationName.chao_garden_beginner_region,
chao_garden_beginner_region_locations, None)
chao_garden_intermediate_region_locations = [
LocationName.chao_race_aquamarine_1,
LocationName.chao_race_aquamarine_2,
LocationName.chao_race_aquamarine_3,
LocationName.chao_race_aquamarine_4,
LocationName.chao_race_aquamarine_5,
LocationName.chao_race_topaz_1,
LocationName.chao_race_topaz_2,
LocationName.chao_race_topaz_3,
LocationName.chao_race_topaz_4,
LocationName.chao_race_topaz_5,
LocationName.chao_race_peridot_1,
LocationName.chao_race_peridot_2,
LocationName.chao_race_peridot_3,
LocationName.chao_race_peridot_4,
LocationName.chao_race_peridot_5,
LocationName.chao_race_garnet_1,
LocationName.chao_race_garnet_2,
LocationName.chao_race_garnet_3,
LocationName.chao_race_garnet_4,
LocationName.chao_race_garnet_5,
LocationName.chao_race_onyx_1,
LocationName.chao_race_onyx_2,
LocationName.chao_race_onyx_3,
LocationName.chao_race_onyx_4,
LocationName.chao_race_onyx_5,
LocationName.chao_race_diamond_1,
LocationName.chao_race_diamond_2,
LocationName.chao_race_diamond_3,
LocationName.chao_race_diamond_4,
LocationName.chao_race_diamond_5,
LocationName.chao_standard_karate,
]
chao_garden_intermediate_region = create_region(world, player, active_locations, LocationName.chao_garden_intermediate_region,
chao_garden_intermediate_region_locations, None)
chao_garden_expert_region_locations = [
LocationName.chao_race_challenge_1,
LocationName.chao_race_challenge_2,
LocationName.chao_race_challenge_3,
LocationName.chao_race_challenge_4,
LocationName.chao_race_challenge_5,
LocationName.chao_race_challenge_6,
LocationName.chao_race_challenge_7,
LocationName.chao_race_challenge_8,
LocationName.chao_race_challenge_9,
LocationName.chao_race_challenge_10,
LocationName.chao_race_challenge_11,
LocationName.chao_race_challenge_12,
LocationName.chao_race_hero_1,
LocationName.chao_race_hero_2,
LocationName.chao_race_hero_3,
LocationName.chao_race_hero_4,
LocationName.chao_race_dark_1,
LocationName.chao_race_dark_2,
LocationName.chao_race_dark_3,
LocationName.chao_race_dark_4,
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)
chao_garden_expert_region = create_region(world, player, active_locations, LocationName.chao_garden_expert_region,
chao_garden_expert_region_locations, None)
biolizard_region_locations = [
LocationName.biolizard,
@ -461,6 +539,11 @@ def create_regions(world, player: int, active_locations):
gate_3_region,
gate_4_region,
gate_5_region,
gate_1_boss_region,
gate_2_boss_region,
gate_3_boss_region,
gate_4_boss_region,
gate_5_boss_region,
city_escape_region,
metal_harbor_region,
green_forest_region,
@ -492,12 +575,14 @@ def create_regions(world, player: int, active_locations):
route_280_region,
mad_space_region,
cannon_core_region,
chao_garden_region,
chao_garden_beginner_region,
chao_garden_intermediate_region,
chao_garden_expert_region,
biolizard_region,
]
def connect_regions(world, player, gates: typing.List[LevelGate], cannon_core_emblems):
def connect_regions(world, player, gates: typing.List[LevelGate], cannon_core_emblems, gate_bosses):
names: typing.Dict[str, int] = {}
connect(world, player, names, 'Menu', LocationName.gate_0_region)
@ -510,36 +595,97 @@ def connect_regions(world, player, gates: typing.List[LevelGate], cannon_core_em
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,
gates_len = len(gates)
if gates_len >= 2:
connect(world, player, names, 'Menu', LocationName.gate_1_boss_region,
lambda state: (state.has(ItemName.emblem, player, gates[1].gate_emblem_count)))
if gate_bosses[1] == all_gate_bosses_table[king_boom_boo]:
connect(world, player, names, LocationName.gate_1_boss_region, LocationName.gate_1_region,
lambda state: (state.has(ItemName.knuckles_shovel_claws, player)))
else:
connect(world, player, names, LocationName.gate_1_boss_region, LocationName.gate_1_region)
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,
if gates_len >= 3:
connect(world, player, names, 'Menu', LocationName.gate_2_boss_region,
lambda state: (state.has(ItemName.emblem, player, gates[2].gate_emblem_count)))
if gate_bosses[2] == all_gate_bosses_table[king_boom_boo]:
connect(world, player, names, LocationName.gate_2_boss_region, LocationName.gate_2_region,
lambda state: (state.has(ItemName.knuckles_shovel_claws, player)))
else:
connect(world, player, names, LocationName.gate_2_boss_region, LocationName.gate_2_region)
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,
if gates_len >= 4:
connect(world, player, names, 'Menu', LocationName.gate_3_boss_region,
lambda state: (state.has(ItemName.emblem, player, gates[3].gate_emblem_count)))
if gate_bosses[3] == all_gate_bosses_table[king_boom_boo]:
connect(world, player, names, LocationName.gate_3_boss_region, LocationName.gate_3_region,
lambda state: (state.has(ItemName.knuckles_shovel_claws, player)))
else:
connect(world, player, names, LocationName.gate_3_boss_region, LocationName.gate_3_region)
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,
if gates_len >= 5:
connect(world, player, names, 'Menu', LocationName.gate_4_boss_region,
lambda state: (state.has(ItemName.emblem, player, gates[4].gate_emblem_count)))
if gate_bosses[4] == all_gate_bosses_table[king_boom_boo]:
connect(world, player, names, LocationName.gate_4_boss_region, LocationName.gate_4_region,
lambda state: (state.has(ItemName.knuckles_shovel_claws, player)))
else:
connect(world, player, names, LocationName.gate_4_boss_region, LocationName.gate_4_region)
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,
if gates_len >= 6:
connect(world, player, names, 'Menu', LocationName.gate_5_boss_region,
lambda state: (state.has(ItemName.emblem, player, gates[5].gate_emblem_count)))
if gate_bosses[5] == all_gate_bosses_table[king_boom_boo]:
connect(world, player, names, LocationName.gate_5_boss_region, LocationName.gate_5_region,
lambda state: (state.has(ItemName.knuckles_shovel_claws, player)))
else:
connect(world, player, names, LocationName.gate_5_boss_region, LocationName.gate_5_region)
for i in range(len(gates[5].gate_levels)):
connect(world, player, names, LocationName.gate_5_region, shuffleable_regions[gates[5].gate_levels[i]])
if gates_len == 1:
connect(world, player, names, LocationName.gate_0_region, LocationName.chao_garden_beginner_region)
connect(world, player, names, LocationName.gate_0_region, LocationName.chao_garden_intermediate_region)
connect(world, player, names, LocationName.gate_0_region, LocationName.chao_garden_expert_region)
elif gates_len == 2:
connect(world, player, names, LocationName.gate_0_region, LocationName.chao_garden_beginner_region)
connect(world, player, names, LocationName.gate_0_region, LocationName.chao_garden_intermediate_region)
connect(world, player, names, LocationName.gate_1_region, LocationName.chao_garden_expert_region)
elif gates_len == 3:
connect(world, player, names, LocationName.gate_0_region, LocationName.chao_garden_beginner_region)
connect(world, player, names, LocationName.gate_1_region, LocationName.chao_garden_intermediate_region)
connect(world, player, names, LocationName.gate_2_region, LocationName.chao_garden_expert_region)
elif gates_len == 4:
connect(world, player, names, LocationName.gate_0_region, LocationName.chao_garden_beginner_region)
connect(world, player, names, LocationName.gate_1_region, LocationName.chao_garden_intermediate_region)
connect(world, player, names, LocationName.gate_3_region, LocationName.chao_garden_expert_region)
elif gates_len == 5:
connect(world, player, names, LocationName.gate_1_region, LocationName.chao_garden_beginner_region)
connect(world, player, names, LocationName.gate_2_region, LocationName.chao_garden_intermediate_region)
connect(world, player, names, LocationName.gate_3_region, LocationName.chao_garden_expert_region)
elif gates_len >= 6:
connect(world, player, names, LocationName.gate_1_region, LocationName.chao_garden_beginner_region)
connect(world, player, names, LocationName.gate_2_region, LocationName.chao_garden_intermediate_region)
connect(world, player, names, LocationName.gate_4_region, LocationName.chao_garden_expert_region)
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None):
# Shamelessly stolen from the ROR2 definition

View File

@ -1,10 +1,13 @@
import typing
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
upgrade_location_table, boss_gate_set
from ..AutoWorld import LogicMixin
from ..generic.Rules import add_rule, set_rule
from .GateBosses import boss_has_requirement
def set_mission_progress_rules(world: MultiWorld, player: int):
@ -276,7 +279,8 @@ def set_mission_upgrade_rules(world: MultiWorld, player: int):
# Mission 5 Upgrade Requirements
if world.include_missions[player].value >= 5:
add_rule(world.get_location(LocationName.city_escape_5, player),
lambda state: state.has(ItemName.sonic_flame_ring, player))
lambda state: state.has(ItemName.sonic_flame_ring, player) and
state.has(ItemName.sonic_light_shoes, 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))
@ -328,13 +332,11 @@ def set_mission_upgrade_rules(world: MultiWorld, player: int):
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))
state.has(ItemName.rouge_treasure_scope, player) and
state.has(ItemName.rouge_iron_boots, 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))
@ -380,18 +382,21 @@ def set_mission_upgrade_rules(world: MultiWorld, player: int):
lambda state: state.has(ItemName.eggman_jet_engine, player))
def set_rules(world: MultiWorld, player: int):
def set_boss_gate_rules(world: MultiWorld, player: int, gate_bosses: typing.Dict[int, int]):
for x in range(len(gate_bosses)):
if boss_has_requirement(gate_bosses[x + 1]):
add_rule(world.get_location(boss_gate_set[x], player),
lambda state: state.has(ItemName.knuckles_shovel_claws, player))
def set_rules(world: MultiWorld, player: int, gate_bosses: typing.Dict[int, int]):
# Mission Progression Rules (Mission 1 begets Mission 2, etc.)
set_mission_progress_rules(world, player)
# Upgrade Requirements for each location
# Upgrade Requirements for each mission 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)
# Upgrade Requirements for each boss gate
set_boss_gate_rules(world, player, gate_bosses)
world.completion_condition[player] = lambda state: state.has(ItemName.maria, player)

View File

@ -2,7 +2,7 @@ import typing
import math
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
from .Items import SA2BItem, ItemData, item_table, upgrades_table
from .Items import SA2BItem, ItemData, item_table, upgrades_table, junk_table, trap_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, \
@ -10,6 +10,8 @@ from .Regions import create_regions, shuffleable_regions, connect_regions, Level
from .Rules import set_rules
from .Names import ItemName, LocationName
from ..AutoWorld import WebWorld, World
from .GateBosses import get_gate_bosses, get_boss_name
import Patch
class SA2BWeb(WebWorld):
@ -49,21 +51,28 @@ class SA2BWorld(World):
game: str = "Sonic Adventure 2 Battle"
options = sa2b_options
topology_present = False
data_version = 1
data_version = 2
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = all_locations
location_table: typing.Dict[str, int]
music_map: typing.Dict[int, int]
emblems_for_cannons_core: int
region_emblem_map: typing.Dict[int, int]
gate_costs: typing.Dict[int, int]
gate_bosses: typing.Dict[int, int]
web = SA2BWeb()
def _get_slot_data(self):
return {
"ModVersion": 100,
"ModVersion": 101,
"MusicMap": self.music_map,
"MusicShuffle": self.world.music_shuffle[self.player].value,
"RequiredRank": self.world.required_rank[self.player].value,
"ChaoRaceChecks": self.world.chao_race_checks[self.player].value,
"ChaoGardenDifficulty": self.world.chao_garden_difficulty[self.player].value,
"DeathLink": self.world.death_link[self.player].value,
"IncludeMissions": self.world.include_missions[self.player].value,
"EmblemPercentageForCannonsCore": self.world.emblem_percentage_for_cannons_core[self.player].value,
@ -71,6 +80,8 @@ class SA2BWorld(World):
"LevelGateDistribution": self.world.level_gate_distribution[self.player].value,
"EmblemsForCannonsCore": self.emblems_for_cannons_core,
"RegionEmblemMap": self.region_emblem_map,
"GateCosts": self.gate_costs,
"GateBosses": self.gate_bosses,
}
def _create_items(self, name: str):
@ -122,31 +133,36 @@ class SA2BWorld(World):
return levels_per_gate
def generate_early(self):
self.gate_bosses = get_gate_bosses(self.world, self.player)
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.include_missions[self.player].value
# Upgrades
total_required_locations += 28
total_required_locations = len(self.location_table)
total_required_locations -= 1; # Locked Victory Location
# 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
# Cap at 180 Emblems
raw_emblem_count = total_required_locations - len(itempool)
total_emblem_count = min(raw_emblem_count, 180)
extra_junk_count = raw_emblem_count - total_emblem_count
self.emblems_for_cannons_core = math.floor(
total_emblem_count * (self.world.emblem_percentage_for_cannons_core[self.player].value / 100.0))
gate_cost_mult = 1.0
if self.world.level_gate_costs[self.player].value == 0:
gate_cost_mult = 0.6
elif self.world.level_gate_costs[self.player].value == 1:
gate_cost_mult = 0.8
shuffled_region_list = list(range(30))
emblem_requirement_list = list()
self.world.random.shuffle(shuffled_region_list)
@ -157,6 +173,8 @@ class SA2BWorld(World):
total_levels_added = 0
current_gate = 0
current_gate_emblems = 0
self.gate_costs = dict()
self.gate_costs[0] = 0
gates = list()
gates.append(LevelGate(0))
for i in range(30):
@ -170,20 +188,51 @@ class SA2BWorld(World):
current_gate = self.world.number_of_level_gates[self.player].value
else:
current_gate_emblems = max(
math.floor(total_emblem_count * math.pow(total_levels_added / 30.0, 2.0)), current_gate)
math.floor(total_emblem_count * math.pow(total_levels_added / 30.0, 2.0) * gate_cost_mult), current_gate)
gates.append(LevelGate(current_gate_emblems))
self.gate_costs[current_gate] = 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)
connect_regions(self.world, self.player, gates, self.emblems_for_cannons_core, self.gate_bosses)
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)
non_required_emblems = (total_emblem_count - max_required_emblems)
junk_count = math.floor(non_required_emblems * (self.world.junk_fill_percentage[self.player].value / 100.0))
itempool += [self.create_item(ItemName.emblem, True)] * (non_required_emblems - junk_count)
# Carve Traps out of junk_count
trap_weights = []
trap_weights += ([ItemName.omochao_trap] * self.world.omochao_trap_weight[self.player].value)
trap_weights += ([ItemName.timestop_trap] * self.world.timestop_trap_weight[self.player].value)
trap_weights += ([ItemName.confuse_trap] * self.world.confusion_trap_weight[self.player].value)
trap_weights += ([ItemName.tiny_trap] * self.world.tiny_trap_weight[self.player].value)
junk_count += extra_junk_count
trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.world.trap_fill_percentage[self.player].value / 100.0))
junk_count -= trap_count
junk_pool = []
junk_keys = list(junk_table.keys())
for i in range(junk_count):
junk_item = self.world.random.choice(junk_keys)
junk_pool += [self.create_item(junk_item)]
itempool += junk_pool
trap_pool = []
for i in range(trap_count):
trap_item = self.world.random.choice(trap_weights)
trap_pool += [self.create_item(trap_item)]
itempool += trap_pool
self.world.itempool += itempool
# Music Shuffle
if self.world.music_shuffle[self.player] == "levels":
musiclist_o = list(range(0, 47))
musiclist_s = musiclist_o.copy()
@ -198,18 +247,20 @@ class SA2BWorld(World):
self.music_map = dict()
def create_regions(self):
location_table = setup_locations(self.world, self.player)
create_regions(self.world, self.player, location_table)
self.location_table = setup_locations(self.world, self.player)
create_regions(self.world, self.player, self.location_table)
def create_item(self, name: str, force_non_progression=False) -> Item:
data = item_table[name]
if name == ItemName.emblem:
classification = ItemClassification.progression_skip_balancing
elif force_non_progression:
if force_non_progression:
classification = ItemClassification.filler
elif name == ItemName.emblem:
classification = ItemClassification.progression_skip_balancing
elif data.progression:
classification = ItemClassification.progression
elif data.trap:
classification = ItemClassification.trap
else:
classification = ItemClassification.filler
@ -218,7 +269,17 @@ class SA2BWorld(World):
return created_item
def set_rules(self):
set_rules(self.world, self.player)
set_rules(self.world, self.player, self.gate_bosses)
def write_spoiler(self, spoiler_handle: typing.TextIO):
spoiler_handle.write("\n")
header_text = "Sonic Adventure 2 Bosses for {}:\n"
header_text = header_text.format(self.world.player_name[self.player])
spoiler_handle.write(header_text)
for x in range(len(self.gate_bosses.values())):
text = "Gate {0} Boss: {1}\n"
text = text.format((x + 1), get_boss_name(self.gate_bosses[x + 1]))
spoiler_handle.writelines(text)
@classmethod
def stage_fill_hook(cls, world, progitempool, nonexcludeditempool, localrestitempool, nonlocalrestitempool,

View File

@ -6,7 +6,7 @@ The [player settings page for this game](../player-settings) contains all the op
## 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.
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 and a boss fight 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?
@ -14,7 +14,7 @@ The goal is to unlock and complete Cannons Core Mission 1, then complete the Bio
## 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.
All 30 story stages leading up to Cannons Core will be shuffled and can be optionally placed behind emblem requirement gates. Chao Garden emblems can optionally be added to the randomizer. All emblems from the selected mission range and all 28 character upgrade items get shuffled. At most 180 emblems will be added to the item pool, after which remaining items added will be random collectables (rings, shields, etc). Traps can also be optionally included.
## Which items can be in another player's world?
@ -28,3 +28,6 @@ Emblems have no visualization in the randomizer and items all retain their origi
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.
## How can I get started?
To get started playing Sonic Adventure 2: Battle in Archipelago, [go to the setup guide for this game](../../../tutorial/Sonic%20Adventure%202%20Battle/setup/en)

View File

@ -3,7 +3,7 @@
## 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.
- The Battle DLC is required if you choose to add Chao Karate locations to the randomizer
- 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
@ -13,6 +13,8 @@
- Sonic Adventure 2 Tracker
- PopTracker from: [PopTracker Releases Page](https://github.com/black-sliver/PopTracker/releases/)
- Sonic Adventure 2: Battle Archipelago PopTracker pack from: [SA2B AP Tracker Releases Page](https://github.com/PoryGone/SA2B_AP_Tracker/releases/)
- Quality of life mods
- SA2 Volume Controls from: [SA2 Volume Controls Release Page] (https://gamebanana.com/mods/381193)
## Installation Procedures
@ -48,16 +50,24 @@
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.
- 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)
- "sonic2app.exe - Entry Point Not Found"
- Make sure the `APCpp.dll` is up to date. Follow Installation Procedures step 6 to update the dll.
- Game is running too fast (Like Sonic).
- Limit framerate using the mod manager:
1. Launch `SA2ModManager.exe`.
2. Select the `Graphics` tab.
3. Check the `Lock framerate` box under the Visuals section.
4. Press the `Save` button.
- If using an NVidia graphics card:
1. Open the NVIDIA Control Panel.
2. Select `Manage 3D Settings` under `3D settings` on the left.