| 
									
										
										
										
											2025-05-21 14:30:39 +02:00
										 |  |  | from BaseClasses import Region, MultiWorld, LocationProgressType, ItemClassification, CollectionState | 
					
						
							|  |  |  | from .items import ShapezItem | 
					
						
							|  |  |  | from .locations import ShapezLocation | 
					
						
							|  |  |  | from .data.strings import ITEMS, REGIONS, GOALS, LOCATIONS, OPTIONS | 
					
						
							|  |  |  | from worlds.generic.Rules import add_rule | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | shapesanity_processing = [REGIONS.full, REGIONS.half, REGIONS.piece, REGIONS.stitched, REGIONS.east_wind, | 
					
						
							|  |  |  |                           REGIONS.half_half, REGIONS.col_east_wind, REGIONS.col_half_half, REGIONS.col_full, | 
					
						
							|  |  |  |                           REGIONS.col_half] | 
					
						
							|  |  |  | shapesanity_coloring = [REGIONS.uncol, REGIONS.painted, REGIONS.mixed] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | all_regions = [ | 
					
						
							|  |  |  |     REGIONS.menu, REGIONS.belt, REGIONS.extract, REGIONS.main, | 
					
						
							|  |  |  |     REGIONS.levels_1, REGIONS.levels_2, REGIONS.levels_3, REGIONS.levels_4, REGIONS.levels_5, | 
					
						
							|  |  |  |     REGIONS.upgrades_1, REGIONS.upgrades_2, REGIONS.upgrades_3, REGIONS.upgrades_4, REGIONS.upgrades_5, | 
					
						
							|  |  |  |     REGIONS.paint_not_quad, REGIONS.cut_not_quad, REGIONS.rotate_cw, REGIONS.stack_shape, REGIONS.store_shape, | 
					
						
							|  |  |  |     REGIONS.trash_shape, REGIONS.blueprint, REGIONS.wiring, REGIONS.mam, REGIONS.any_building, | 
					
						
							|  |  |  |     REGIONS.all_buildings, REGIONS.all_buildings_x1_6_belt, | 
					
						
							|  |  |  |     *[REGIONS.sanity(processing, coloring) | 
					
						
							|  |  |  |       for processing in shapesanity_processing | 
					
						
							|  |  |  |       for coloring in shapesanity_coloring], | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_cut_half(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     return state.has(ITEMS.cutter, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_rotate_90(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     return state.has_any((ITEMS.rotator, ITEMS.rotator_ccw), player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_rotate_180(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     return state.has_any((ITEMS.rotator, ITEMS.rotator_ccw, ITEMS.rotator_180), player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_stack(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     return state.has(ITEMS.stacker, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_paint(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     return state.has_any((ITEMS.painter, ITEMS.painter_double), player) or can_use_quad_painter(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_mix_colors(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     return state.has(ITEMS.color_mixer, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def has_tunnel(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     return state.has_any((ITEMS.tunnel, ITEMS.tunnel_tier_ii), player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def has_balancer(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     return state.has(ITEMS.balancer, player) or state.has_all((ITEMS.comp_merger, ITEMS.comp_splitter), player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_use_quad_painter(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     return (state.has_all((ITEMS.painter_quad, ITEMS.wires), player) and | 
					
						
							|  |  |  |             state.has_any((ITEMS.switch, ITEMS.const_signal), player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_make_stitched_shape(state: CollectionState, player: int, floating: bool) -> bool: | 
					
						
							|  |  |  |     return (can_stack(state, player) and | 
					
						
							|  |  |  |             ((state.has(ITEMS.cutter_quad, player) and not floating) or | 
					
						
							|  |  |  |              (can_cut_half(state, player) and can_rotate_90(state, player)))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_build_mam(state: CollectionState, player: int, floating: bool) -> bool: | 
					
						
							|  |  |  |     return (can_make_stitched_shape(state, player, floating) and can_paint(state, player) and | 
					
						
							|  |  |  |             can_mix_colors(state, player) and has_balancer(state, player) and has_tunnel(state, player) and | 
					
						
							|  |  |  |             state.has_all((ITEMS.belt_reader, ITEMS.storage, ITEMS.item_filter, | 
					
						
							|  |  |  |                            ITEMS.wires, ITEMS.logic_gates, ITEMS.virtual_proc), player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_make_east_windmill(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     # Only used for shapesanity => single layers | 
					
						
							|  |  |  |     return (can_stack(state, player) and | 
					
						
							|  |  |  |             (state.has(ITEMS.cutter_quad, player) or (can_cut_half(state, player) and can_rotate_180(state, player)))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_make_half_half_shape(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     # Only used for shapesanity => single layers | 
					
						
							|  |  |  |     return can_stack(state, player) and state.has_any((ITEMS.cutter, ITEMS.cutter_quad), player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def can_make_half_shape(state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     # Only used for shapesanity => single layers | 
					
						
							|  |  |  |     return can_cut_half(state, player) or state.has_all((ITEMS.cutter_quad, ITEMS.stacker), player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def has_x_belt_multiplier(state: CollectionState, player: int, needed: float) -> bool: | 
					
						
							|  |  |  |     # Assumes there are no upgrade traps | 
					
						
							|  |  |  |     multiplier = 1.0 | 
					
						
							|  |  |  |     # Rising upgrades do the least improvement if received before other upgrades | 
					
						
							|  |  |  |     for _ in range(state.count(ITEMS.upgrade_rising_belt, player)): | 
					
						
							|  |  |  |         multiplier *= 2 | 
					
						
							|  |  |  |     multiplier += state.count(ITEMS.upgrade_gigantic_belt, player)*10 | 
					
						
							|  |  |  |     multiplier += state.count(ITEMS.upgrade_big_belt, player) | 
					
						
							|  |  |  |     multiplier += state.count(ITEMS.upgrade_small_belt, player)*0.1 | 
					
						
							|  |  |  |     return multiplier >= needed | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 17:01:57 +02:00
										 |  |  | def has_logic_list_building(state: CollectionState, player: int, buildings: list[str], index: int, | 
					
						
							| 
									
										
										
										
											2025-05-21 14:30:39 +02:00
										 |  |  |                             includeuseful: bool) -> bool: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Includes balancer, tunnel, and trash in logic in order to make them appear in earlier spheres | 
					
						
							|  |  |  |     if includeuseful and not (state.has(ITEMS.trash, player) and has_balancer(state, player) and | 
					
						
							|  |  |  |                               has_tunnel(state, player)): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if buildings[index] == ITEMS.cutter: | 
					
						
							|  |  |  |         if buildings.index(ITEMS.stacker) < index: | 
					
						
							|  |  |  |             return state.has_any((ITEMS.cutter, ITEMS.cutter_quad), player) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return can_cut_half(state, player) | 
					
						
							|  |  |  |     elif buildings[index] == ITEMS.rotator: | 
					
						
							|  |  |  |         return can_rotate_90(state, player) | 
					
						
							|  |  |  |     elif buildings[index] == ITEMS.stacker: | 
					
						
							|  |  |  |         return can_stack(state, player) | 
					
						
							|  |  |  |     elif buildings[index] == ITEMS.painter: | 
					
						
							|  |  |  |         return can_paint(state, player) | 
					
						
							|  |  |  |     elif buildings[index] == ITEMS.color_mixer: | 
					
						
							|  |  |  |         return can_mix_colors(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def create_shapez_regions(player: int, multiworld: MultiWorld, floating: bool, | 
					
						
							| 
									
										
										
										
											2025-07-28 17:01:57 +02:00
										 |  |  |                           included_locations: dict[str, tuple[str, LocationProgressType]], | 
					
						
							|  |  |  |                           location_name_to_id: dict[str, int], level_logic_buildings: list[str], | 
					
						
							|  |  |  |                           upgrade_logic_buildings: list[str], early_useful: str, goal: str) -> list[Region]: | 
					
						
							| 
									
										
										
										
											2025-05-21 14:30:39 +02:00
										 |  |  |     """Creates and returns a list of all regions with entrances and all locations placed correctly.""" | 
					
						
							| 
									
										
										
										
											2025-07-28 17:01:57 +02:00
										 |  |  |     regions: dict[str, Region] = {name: Region(name, player, multiworld) for name in all_regions} | 
					
						
							| 
									
										
										
										
											2025-05-21 14:30:39 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Creates ShapezLocations for every included location and puts them into the correct region | 
					
						
							|  |  |  |     for name, data in included_locations.items(): | 
					
						
							|  |  |  |         regions[data[0]].locations.append(ShapezLocation(player, name, location_name_to_id[name], | 
					
						
							|  |  |  |                                                          regions[data[0]], data[1])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create goal event | 
					
						
							|  |  |  |     if goal in [GOALS.vanilla, GOALS.mam]: | 
					
						
							|  |  |  |         goal_region = regions[REGIONS.levels_5] | 
					
						
							|  |  |  |     elif goal == GOALS.even_fasterer: | 
					
						
							|  |  |  |         goal_region = regions[REGIONS.upgrades_5] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         goal_region = regions[REGIONS.all_buildings] | 
					
						
							|  |  |  |     goal_location = ShapezLocation(player, LOCATIONS.goal, None, goal_region, LocationProgressType.DEFAULT) | 
					
						
							|  |  |  |     goal_location.place_locked_item(ShapezItem(ITEMS.goal, ItemClassification.progression_skip_balancing, None, player)) | 
					
						
							|  |  |  |     if goal == GOALS.efficiency_iii: | 
					
						
							|  |  |  |         add_rule(goal_location, lambda state: has_x_belt_multiplier(state, player, 8)) | 
					
						
							|  |  |  |     goal_region.locations.append(goal_location) | 
					
						
							|  |  |  |     multiworld.completion_condition[player] = lambda state: state.has(ITEMS.goal, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Connect Menu to rest of regions | 
					
						
							|  |  |  |     regions[REGIONS.menu].connect(regions[REGIONS.belt], "Placing belts", lambda state: state.has(ITEMS.belt, player)) | 
					
						
							|  |  |  |     regions[REGIONS.menu].connect(regions[REGIONS.extract], "Extracting shapes from patches", | 
					
						
							|  |  |  |                                   lambda state: state.has_any((ITEMS.extractor, ITEMS.extractor_chain), player)) | 
					
						
							|  |  |  |     regions[REGIONS.extract].connect( | 
					
						
							|  |  |  |         regions[REGIONS.main], "Transporting shapes over the canvas", | 
					
						
							|  |  |  |         lambda state: state.has_any((ITEMS.belt, ITEMS.comp_merger, ITEMS.comp_splitter), player) | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Connect achievement regions | 
					
						
							|  |  |  |     regions[REGIONS.main].connect(regions[REGIONS.paint_not_quad], "Painting with (double) painter", | 
					
						
							|  |  |  |                                   lambda state: state.has_any((ITEMS.painter, ITEMS.painter_double), player)) | 
					
						
							|  |  |  |     regions[REGIONS.extract].connect(regions[REGIONS.cut_not_quad], "Cutting with half cutter", | 
					
						
							|  |  |  |                                      lambda state: can_cut_half(state, player)) | 
					
						
							|  |  |  |     regions[REGIONS.extract].connect(regions[REGIONS.rotate_cw], "Rotating clockwise", | 
					
						
							|  |  |  |                                      lambda state: state.has(ITEMS.rotator, player)) | 
					
						
							|  |  |  |     regions[REGIONS.extract].connect(regions[REGIONS.stack_shape], "Stacking shapes", | 
					
						
							|  |  |  |                                      lambda state: can_stack(state, player)) | 
					
						
							|  |  |  |     regions[REGIONS.extract].connect(regions[REGIONS.store_shape], "Storing shapes", | 
					
						
							|  |  |  |                                      lambda state: state.has(ITEMS.storage, player)) | 
					
						
							|  |  |  |     regions[REGIONS.extract].connect(regions[REGIONS.trash_shape], "Trashing shapes", | 
					
						
							|  |  |  |                                      lambda state: state.has(ITEMS.trash, player)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect(regions[REGIONS.blueprint], "Copying and placing blueprints", | 
					
						
							|  |  |  |                                   lambda state: state.has(ITEMS.blueprints, player) and | 
					
						
							|  |  |  |                                                 can_make_stitched_shape(state, player, floating) and | 
					
						
							|  |  |  |                                                 can_paint(state, player) and can_mix_colors(state, player)) | 
					
						
							|  |  |  |     regions[REGIONS.menu].connect(regions[REGIONS.wiring], "Using the wires layer", | 
					
						
							|  |  |  |                                   lambda state: state.has(ITEMS.wires, player)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect(regions[REGIONS.mam], "Building a MAM", | 
					
						
							|  |  |  |                                   lambda state: can_build_mam(state, player, floating)) | 
					
						
							|  |  |  |     regions[REGIONS.menu].connect(regions[REGIONS.any_building], "Placing any building", lambda state: state.has_any(( | 
					
						
							|  |  |  |         ITEMS.belt, ITEMS.balancer, ITEMS.comp_merger, ITEMS.comp_splitter, ITEMS.tunnel, ITEMS.tunnel_tier_ii, | 
					
						
							|  |  |  |         ITEMS.extractor, ITEMS.extractor_chain, ITEMS.cutter, ITEMS.cutter_quad, ITEMS.rotator, ITEMS.rotator_ccw, | 
					
						
							|  |  |  |         ITEMS.rotator_180, ITEMS.stacker, ITEMS.painter, ITEMS.painter_double, ITEMS.painter_quad, ITEMS.color_mixer, | 
					
						
							|  |  |  |         ITEMS.trash, ITEMS.belt_reader, ITEMS.storage, ITEMS.switch, ITEMS.item_filter, ITEMS.display, ITEMS.wires | 
					
						
							|  |  |  |     ), player)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect(regions[REGIONS.all_buildings], "Using all main buildings", | 
					
						
							|  |  |  |                                   lambda state: can_make_stitched_shape(state, player, floating) and | 
					
						
							|  |  |  |                                                 can_paint(state, player) and can_mix_colors(state, player)) | 
					
						
							|  |  |  |     regions[REGIONS.all_buildings].connect(regions[REGIONS.all_buildings_x1_6_belt], | 
					
						
							|  |  |  |                                            "Delivering per second with 1.6x belt speed", | 
					
						
							|  |  |  |                                            lambda state: has_x_belt_multiplier(state, player, 1.6)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Progressively connect level and upgrade regions | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.levels_1], "Using first level building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, level_logic_buildings, 0, False)) | 
					
						
							|  |  |  |     regions[REGIONS.levels_1].connect( | 
					
						
							|  |  |  |         regions[REGIONS.levels_2], "Using second level building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, level_logic_buildings, 1, False)) | 
					
						
							|  |  |  |     regions[REGIONS.levels_2].connect( | 
					
						
							|  |  |  |         regions[REGIONS.levels_3], "Using third level building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, level_logic_buildings, 2, | 
					
						
							|  |  |  |                                               early_useful == OPTIONS.buildings_3)) | 
					
						
							|  |  |  |     regions[REGIONS.levels_3].connect( | 
					
						
							|  |  |  |         regions[REGIONS.levels_4], "Using fourth level building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, level_logic_buildings, 3, False)) | 
					
						
							|  |  |  |     regions[REGIONS.levels_4].connect( | 
					
						
							|  |  |  |         regions[REGIONS.levels_5], "Using fifth level building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, level_logic_buildings, 4, | 
					
						
							|  |  |  |                                               early_useful == OPTIONS.buildings_5)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.upgrades_1], "Using first upgrade building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 0, False)) | 
					
						
							|  |  |  |     regions[REGIONS.upgrades_1].connect( | 
					
						
							|  |  |  |         regions[REGIONS.upgrades_2], "Using second upgrade building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 1, False)) | 
					
						
							|  |  |  |     regions[REGIONS.upgrades_2].connect( | 
					
						
							|  |  |  |         regions[REGIONS.upgrades_3], "Using third upgrade building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 2, | 
					
						
							|  |  |  |                                               early_useful == OPTIONS.buildings_3)) | 
					
						
							|  |  |  |     regions[REGIONS.upgrades_3].connect( | 
					
						
							|  |  |  |         regions[REGIONS.upgrades_4], "Using fourth upgrade building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 3, False)) | 
					
						
							|  |  |  |     regions[REGIONS.upgrades_4].connect( | 
					
						
							|  |  |  |         regions[REGIONS.upgrades_5], "Using fifth upgrade building", | 
					
						
							|  |  |  |         lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 4, | 
					
						
							|  |  |  |                                               early_useful == OPTIONS.buildings_5)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Connect Uncolored shapesanity regions to Main | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.full, REGIONS.uncol)], "Delivering unprocessed", lambda state: True) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.half, REGIONS.uncol)], "Cutting in single half", | 
					
						
							|  |  |  |         lambda state: can_make_half_shape(state, player)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.piece, REGIONS.uncol)], "Cutting in single piece", | 
					
						
							|  |  |  |         lambda state: (can_cut_half(state, player) and can_rotate_90(state, player)) or | 
					
						
							|  |  |  |                       state.has(ITEMS.cutter_quad, player)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.half_half, REGIONS.uncol)], "Cutting and stacking into two halves", | 
					
						
							|  |  |  |         lambda state: can_make_half_half_shape(state, player)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.stitched, REGIONS.uncol)], "Stitching complex shapes", | 
					
						
							|  |  |  |         lambda state: can_make_stitched_shape(state, player, floating)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.east_wind, REGIONS.uncol)], "Rotating and stitching a single windmill half", | 
					
						
							|  |  |  |         lambda state: can_make_east_windmill(state, player)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.col_full, REGIONS.uncol)], "Painting with a quad painter or stitching", | 
					
						
							|  |  |  |         lambda state: can_make_stitched_shape(state, player, floating) or can_use_quad_painter(state, player)) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.col_east_wind, REGIONS.uncol)], "Why windmill, why?", | 
					
						
							|  |  |  |         lambda state: can_make_stitched_shape(state, player, floating) or | 
					
						
							|  |  |  |                       (can_use_quad_painter(state, player) and can_make_east_windmill(state, player))) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.col_half_half, REGIONS.uncol)], "Quad painting a half-half shape", | 
					
						
							|  |  |  |         lambda state: can_make_stitched_shape(state, player, floating) or | 
					
						
							|  |  |  |                       (can_use_quad_painter(state, player) and can_make_half_half_shape(state, player))) | 
					
						
							|  |  |  |     regions[REGIONS.main].connect( | 
					
						
							|  |  |  |         regions[REGIONS.sanity(REGIONS.col_half, REGIONS.uncol)], "Quad painting a half shape", | 
					
						
							|  |  |  |         lambda state: can_make_stitched_shape(state, player, floating) or | 
					
						
							|  |  |  |                       (can_use_quad_painter(state, player) and can_make_half_shape(state, player))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Progressively connect colored shapesanity regions | 
					
						
							|  |  |  |     for processing in shapesanity_processing: | 
					
						
							|  |  |  |         regions[REGIONS.sanity(processing, REGIONS.uncol)].connect( | 
					
						
							|  |  |  |             regions[REGIONS.sanity(processing, REGIONS.painted)], f"Painting a {processing.lower()} shape", | 
					
						
							|  |  |  |             lambda state: can_paint(state, player)) | 
					
						
							|  |  |  |         regions[REGIONS.sanity(processing, REGIONS.painted)].connect( | 
					
						
							|  |  |  |             regions[REGIONS.sanity(processing, REGIONS.mixed)], f"Mixing colors for a {processing.lower()} shape", | 
					
						
							|  |  |  |             lambda state: can_mix_colors(state, player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return [region for region in regions.values() if len(region.locations) or len(region.exits)] |