mirror of
				https://github.com/MarioSpore/Grinch-AP.git
				synced 2025-10-21 20:21:32 -06:00 
			
		
		
		
	SA2B: v2.1 Content Update (#1563)
Changelog:
Features:
- New goal
  - Grand Prix
    - Complete all of the Kart Races to win!
- New optional Location Checks
  - Omosanity (Activating Omochao)
  - Kart Race Mode
- Ring Loss option
  - `Classic` - lose all rings on hit
  - `Modern` - lose 20 rings on hit
  - `OHKO` - instantly die on hit, regardless of ring count (shields still protect you)
- New Trap
  - Pong Trap
Quality of Life:
- SA2B is now distributed as an `.apworld`
- Maximum possible number of Emblems in item pool is increased from 180 to 250
- An indicator now shows on the Stage Select screen when `Cannon's Core` is available
- Certain traps (`Exposition` and `Pong`) are now possible to receive on `Route 101` and `Route 280`
- Certain traps (`Confusion`, `Chaos Control`, `Exposition` and `Pong`) are now possible to receive on `FinalHazard`
Bug Fixes:
- Actually swap Intermediate and Expert Chao Races correctly
- Don't always grant double score for killing Gold Beetles anymore
- Ensure upgrades are applied properly, even when received while dying
- Fix the Message Queue getting disordered when receiving many messages in quick succession
- Fix Logic errors
  - `City Escape - 3` (Hard Logic) now requires no upgrades
  - `Mission Street - Pipe 2` (Hard Logic) now requires no upgrades
  - `Crazy Gadget - Pipe 3` (Hard Logic) now requires no upgrades
  - `Egg Quarters - 3` (Hard Logic) now requires only `Rouge - Mystic Melody`
  - `Mad Space - 5` (Hard Logic) now requires no upgrades
Co-authored-by: RaspberrySpaceJam <tyler.summers@gmail.com>
			
			
This commit is contained in:
		| @@ -9,7 +9,7 @@ from .Regions import create_regions, shuffleable_regions, connect_regions, Level | ||||
|     gate_0_blacklist_regions | ||||
| from .Rules import set_rules | ||||
| from .Names import ItemName, LocationName | ||||
| from ..AutoWorld import WebWorld, World | ||||
| from worlds.AutoWorld import WebWorld, World | ||||
| from .GateBosses import get_gate_bosses, get_boss_name | ||||
| from .Missions import get_mission_table, get_mission_count_table, get_first_and_last_cannons_core_missions | ||||
| import Patch | ||||
| @@ -52,7 +52,7 @@ class SA2BWorld(World): | ||||
|     game: str = "Sonic Adventure 2 Battle" | ||||
|     option_definitions = sa2b_options | ||||
|     topology_present = False | ||||
|     data_version = 4 | ||||
|     data_version = 5 | ||||
|  | ||||
|     item_name_groups = item_groups | ||||
|     item_name_to_id = {name: data.code for name, data in item_table.items()} | ||||
| @@ -71,17 +71,21 @@ class SA2BWorld(World): | ||||
|  | ||||
|     def _get_slot_data(self): | ||||
|         return { | ||||
|             "ModVersion": 200, | ||||
|             "ModVersion": 201, | ||||
|             "Goal": self.multiworld.goal[self.player].value, | ||||
|             "MusicMap": self.music_map, | ||||
|             "MissionMap": self.mission_map, | ||||
|             "MissionCountMap": self.mission_count_map, | ||||
|             "MusicShuffle": self.multiworld.music_shuffle[self.player].value, | ||||
|             "Narrator": self.multiworld.narrator[self.player].value, | ||||
|             "MinigameTrapDifficulty": self.multiworld.minigame_trap_difficulty[self.player].value, | ||||
|             "RingLoss": self.multiworld.ring_loss[self.player].value, | ||||
|             "RequiredRank": self.multiworld.required_rank[self.player].value, | ||||
|             "ChaoKeys": self.multiworld.keysanity[self.player].value, | ||||
|             "Whistlesanity": self.multiworld.whistlesanity[self.player].value, | ||||
|             "GoldBeetles": self.multiworld.beetlesanity[self.player].value, | ||||
|             "OmochaoChecks": self.multiworld.omosanity[self.player].value, | ||||
|             "KartRaceChecks": self.multiworld.kart_race_checks[self.player].value, | ||||
|             "ChaoRaceChecks": self.multiworld.chao_race_checks[self.player].value, | ||||
|             "ChaoGardenDifficulty": self.multiworld.chao_garden_difficulty[self.player].value, | ||||
|             "DeathLink": self.multiworld.death_link[self.player].value, | ||||
| @@ -145,13 +149,45 @@ class SA2BWorld(World): | ||||
|         return levels_per_gate | ||||
|  | ||||
|     def generate_early(self): | ||||
|         self.gate_bosses = get_gate_bosses(self.multiworld, self.player) | ||||
|         if self.multiworld.goal[self.player].value == 3: | ||||
|             # Turn off everything else for Grand Prix goal | ||||
|             self.multiworld.number_of_level_gates[self.player].value = 0 | ||||
|             self.multiworld.emblem_percentage_for_cannons_core[self.player].value = 0 | ||||
|             self.multiworld.junk_fill_percentage[self.player].value = 100 | ||||
|             self.multiworld.trap_fill_percentage[self.player].value = 100 | ||||
|             self.multiworld.omochao_trap_weight[self.player].value = 0 | ||||
|             self.multiworld.timestop_trap_weight[self.player].value = 0 | ||||
|             self.multiworld.confusion_trap_weight[self.player].value = 0 | ||||
|             self.multiworld.tiny_trap_weight[self.player].value = 0 | ||||
|             self.multiworld.gravity_trap_weight[self.player].value = 0 | ||||
|  | ||||
|     def generate_basic(self): | ||||
|             valid_trap_weights = self.multiworld.exposition_trap_weight[self.player].value + self.multiworld.pong_trap_weight[self.player].value | ||||
|  | ||||
|             if valid_trap_weights == 0: | ||||
|                 self.multiworld.exposition_trap_weight[self.player].value = 4 | ||||
|                 self.multiworld.pong_trap_weight[self.player].value = 4 | ||||
|  | ||||
|             if self.multiworld.kart_race_checks[self.player].value == 0: | ||||
|                 self.multiworld.kart_race_checks[self.player].value = 2 | ||||
|  | ||||
|             self.gate_bosses = {} | ||||
|         else: | ||||
|             self.gate_bosses = get_gate_bosses(self.multiworld, self.player) | ||||
|  | ||||
|     def create_regions(self): | ||||
|         self.mission_map       = get_mission_table(self.multiworld, self.player) | ||||
|         self.mission_count_map = get_mission_count_table(self.multiworld, self.player) | ||||
|  | ||||
|         self.location_table = setup_locations(self.multiworld, self.player, self.mission_map, self.mission_count_map) | ||||
|         create_regions(self.multiworld, self.player, self.location_table) | ||||
|  | ||||
|         # Not Generate Basic | ||||
|         if self.multiworld.goal[self.player].value == 0 or self.multiworld.goal[self.player].value == 2: | ||||
|             self.multiworld.get_location(LocationName.finalhazard, self.player).place_locked_item(self.create_item(ItemName.maria)) | ||||
|         elif self.multiworld.goal[self.player].value == 1: | ||||
|             self.multiworld.get_location(LocationName.green_hill, self.player).place_locked_item(self.create_item(ItemName.maria)) | ||||
|         elif self.multiworld.goal[self.player].value == 3: | ||||
|             self.multiworld.get_location(LocationName.grand_prix, self.player).place_locked_item(self.create_item(ItemName.maria)) | ||||
|  | ||||
|         itempool: typing.List[SA2BItem] = [] | ||||
|  | ||||
| @@ -159,18 +195,19 @@ class SA2BWorld(World): | ||||
|         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) | ||||
|  | ||||
|         if self.multiworld.goal[self.player].value == 1 or self.multiworld.goal[self.player].value == 2: | ||||
|             # Some flavor of Chaos Emerald Hunt | ||||
|             for item in {**emeralds_table}: | ||||
|         if self.multiworld.goal[self.player].value != 3: | ||||
|             # Fill item pool with all required items | ||||
|             for item in {**upgrades_table}: | ||||
|                 itempool += self._create_items(item) | ||||
|  | ||||
|         # Cap at 180 Emblems | ||||
|             if self.multiworld.goal[self.player].value == 1 or self.multiworld.goal[self.player].value == 2: | ||||
|                 # Some flavor of Chaos Emerald Hunt | ||||
|                 for item in {**emeralds_table}: | ||||
|                     itempool += self._create_items(item) | ||||
|  | ||||
|         # Cap at 250 Emblems | ||||
|         raw_emblem_count = total_required_locations - len(itempool) | ||||
|         total_emblem_count = min(raw_emblem_count, 180) | ||||
|         total_emblem_count = min(raw_emblem_count, 250) | ||||
|         extra_junk_count = raw_emblem_count - total_emblem_count | ||||
|  | ||||
|         self.emblems_for_cannons_core = math.floor( | ||||
| @@ -234,6 +271,7 @@ class SA2BWorld(World): | ||||
|         trap_weights += ([ItemName.gravity_trap] * self.multiworld.gravity_trap_weight[self.player].value) | ||||
|         trap_weights += ([ItemName.exposition_trap] * self.multiworld.exposition_trap_weight[self.player].value) | ||||
|         #trap_weights += ([ItemName.darkness_trap] * self.multiworld.darkness_trap_weight[self.player].value) | ||||
|         trap_weights += ([ItemName.pong_trap] * self.multiworld.pong_trap_weight[self.player].value) | ||||
|  | ||||
|         junk_count += extra_junk_count | ||||
|         trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.multiworld.trap_fill_percentage[self.player].value / 100.0)) | ||||
| @@ -309,13 +347,6 @@ class SA2BWorld(World): | ||||
|  | ||||
|             self.music_map = dict(zip(musiclist_o, musiclist_s)) | ||||
|  | ||||
|     def create_regions(self): | ||||
|         self.mission_map       = get_mission_table(self.multiworld, self.player) | ||||
|         self.mission_count_map = get_mission_count_table(self.multiworld, self.player) | ||||
|  | ||||
|         self.location_table = setup_locations(self.multiworld, self.player, self.mission_map, self.mission_count_map) | ||||
|         create_regions(self.multiworld, self.player, self.location_table) | ||||
|  | ||||
|     def create_item(self, name: str, force_non_progression=False) -> Item: | ||||
|         data = item_table[name] | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 PoryGone
					PoryGone