129 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			129 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | from typing import Dict, Any | ||
|  | 
 | ||
|  | from BaseClasses import CollectionState, Item, MultiWorld, Tutorial, Region | ||
|  | from Options import OptionError | ||
|  | from worlds.AutoWorld import LogicMixin, World, WebWorld | ||
|  | from .items import item_table, PaintItem, item_data_table, traps, deathlink_traps | ||
|  | from .locations import location_table, PaintLocation, location_data_table | ||
|  | from .options import PaintOptions | ||
|  | 
 | ||
|  | 
 | ||
|  | class PaintWebWorld(WebWorld): | ||
|  |     theme = "partyTime" | ||
|  | 
 | ||
|  |     setup_en = Tutorial( | ||
|  |         tutorial_name="Start Guide", | ||
|  |         description="A guide to playing Paint in Archipelago.", | ||
|  |         language="English", | ||
|  |         file_name="guide_en.md", | ||
|  |         link="guide/en", | ||
|  |         authors=["MarioManTAW"] | ||
|  |     ) | ||
|  | 
 | ||
|  |     tutorials = [setup_en] | ||
|  | 
 | ||
|  | 
 | ||
|  | class PaintWorld(World): | ||
|  |     """
 | ||
|  |     The classic Microsoft app, reimagined as an Archipelago game! Find your tools, expand your canvas, and paint the | ||
|  |     greatest image the world has ever seen. | ||
|  |     """
 | ||
|  |     game = "Paint" | ||
|  |     options_dataclass = PaintOptions | ||
|  |     options: PaintOptions | ||
|  |     web = PaintWebWorld() | ||
|  |     location_name_to_id = location_table | ||
|  |     item_name_to_id = item_table | ||
|  |     origin_region_name = "Canvas" | ||
|  | 
 | ||
|  |     def generate_early(self) -> None: | ||
|  |         if self.options.canvas_size_increment < 50 and self.options.logic_percent <= 55: | ||
|  |             if self.multiworld.players == 1: | ||
|  |                 raise OptionError("Logic Percent must be greater than 55 when generating a single-player world with " | ||
|  |                                   "Canvas Size Increment below 50.") | ||
|  | 
 | ||
|  |     def get_filler_item_name(self) -> str: | ||
|  |         if self.random.randint(0, 99) >= self.options.trap_count: | ||
|  |             return "Additional Palette Color" | ||
|  |         elif self.options.death_link: | ||
|  |             return self.random.choice(deathlink_traps) | ||
|  |         else: | ||
|  |             return self.random.choice(traps) | ||
|  | 
 | ||
|  |     def create_item(self, name: str) -> PaintItem: | ||
|  |         item = PaintItem(name, item_data_table[name].type, item_data_table[name].code, self.player) | ||
|  |         return item | ||
|  | 
 | ||
|  |     def create_items(self) -> None: | ||
|  |         starting_tools = ["Brush", "Pencil", "Eraser/Color Eraser", "Airbrush", "Line", "Rectangle", "Ellipse", | ||
|  |                           "Rounded Rectangle"] | ||
|  |         self.push_precollected(self.create_item("Magnifier")) | ||
|  |         self.push_precollected(self.create_item(starting_tools.pop(self.options.starting_tool))) | ||
|  |         items_to_create = ["Free-Form Select", "Select", "Fill With Color", "Pick Color", "Text", "Curve", "Polygon"] | ||
|  |         items_to_create += starting_tools | ||
|  |         items_to_create += ["Progressive Canvas Width"] * (400 // self.options.canvas_size_increment) | ||
|  |         items_to_create += ["Progressive Canvas Height"] * (300 // self.options.canvas_size_increment) | ||
|  |         depth_items = ["Progressive Color Depth (Red)", "Progressive Color Depth (Green)", | ||
|  |                        "Progressive Color Depth (Blue)"] | ||
|  |         for item in depth_items: | ||
|  |             self.push_precollected(self.create_item(item)) | ||
|  |         items_to_create += depth_items * 6 | ||
|  |         pre_filled = len(items_to_create) | ||
|  |         to_fill = len(self.get_region("Canvas").locations) | ||
|  |         if pre_filled > to_fill: | ||
|  |             raise OptionError(f"{self.player_name}'s Paint world has too few locations for its required items. " | ||
|  |                               "Consider adding more locations by raising logic percent or adding fractional checks. " | ||
|  |                               "Alternatively, increasing the canvas size increment will require fewer items.") | ||
|  |         while len(items_to_create) < (to_fill - pre_filled) * (self.options.trap_count / 100) + pre_filled: | ||
|  |             if self.options.death_link: | ||
|  |                 items_to_create += [self.random.choice(deathlink_traps)] | ||
|  |             else: | ||
|  |                 items_to_create += [self.random.choice(traps)] | ||
|  |         while len(items_to_create) < to_fill: | ||
|  |             items_to_create += ["Additional Palette Color"] | ||
|  |         self.multiworld.itempool += [self.create_item(item) for item in items_to_create] | ||
|  | 
 | ||
|  |     def create_regions(self) -> None: | ||
|  |         canvas = Region("Canvas", self.player, self.multiworld) | ||
|  |         canvas.locations += [PaintLocation(self.player, loc_name, loc_data.address, canvas) | ||
|  |                              for loc_name, loc_data in location_data_table.items() | ||
|  |                              if location_exists_with_options(self, loc_data.address)] | ||
|  | 
 | ||
|  |         self.multiworld.regions += [canvas] | ||
|  | 
 | ||
|  |     def set_rules(self) -> None: | ||
|  |         from .rules import set_completion_rules | ||
|  |         set_completion_rules(self, self.player) | ||
|  | 
 | ||
|  |     def fill_slot_data(self) -> Dict[str, Any]: | ||
|  |         return dict(self.options.as_dict("logic_percent", "goal_percent", "goal_image", "death_link", | ||
|  |                                          "canvas_size_increment"), version="0.5.2") | ||
|  | 
 | ||
|  |     def collect(self, state: CollectionState, item: Item) -> bool: | ||
|  |         change = super().collect(state, item) | ||
|  |         if change: | ||
|  |             state.paint_percent_stale[self.player] = True | ||
|  |         return change | ||
|  | 
 | ||
|  |     def remove(self, state: CollectionState, item: Item) -> bool: | ||
|  |         change = super().remove(state, item) | ||
|  |         if change: | ||
|  |             state.paint_percent_stale[self.player] = True | ||
|  |         return change | ||
|  | 
 | ||
|  | 
 | ||
|  | def location_exists_with_options(world: PaintWorld, location: int): | ||
|  |     l = location % 198600 | ||
|  |     return l <= world.options.logic_percent * 4 and (l % 4 == 0 or | ||
|  |                                                     (l > world.options.half_percent_checks * 4 and l % 2 == 0) or | ||
|  |                                                     l > world.options.quarter_percent_checks * 4) | ||
|  | 
 | ||
|  | 
 | ||
|  | class PaintState(LogicMixin): | ||
|  |     paint_percent_available: dict[int, float]  # per player | ||
|  |     paint_percent_stale: dict[int, bool] | ||
|  | 
 | ||
|  |     def init_mixin(self, multiworld: MultiWorld) -> None: | ||
|  |         self.paint_percent_available = {player: 0 for player in multiworld.get_game_players("Paint")} | ||
|  |         self.paint_percent_stale = {player: True for player in multiworld.get_game_players("Paint")} |