diff --git a/worlds/dlcquest/Items.py b/worlds/dlcquest/Items.py new file mode 100644 index 000000000000..a740ecf549b2 --- /dev/null +++ b/worlds/dlcquest/Items.py @@ -0,0 +1,133 @@ +import csv +import enum +import math +from typing import Protocol, Union, Dict, List +from BaseClasses import Item, ItemClassification +from . import Options, data +from dataclasses import dataclass, field +from random import Random + + +class DLCQuestItem(Item): + game: str = "DLCQuest" + + +offset = 120_000 + + +class Group(enum.Enum): + DLC = enum.auto() + DLCQuest = enum.auto() + Freemium = enum.auto() + Item = enum.auto() + Coin = enum.auto() + Trap = enum.auto() + + +@dataclass(frozen=True) +class ItemData: + code_without_offset: offset + name: str + classification: ItemClassification + groups: set[Group] = field(default_factory=frozenset) + + def __post_init__(self): + if not isinstance(self.groups, frozenset): + super().__setattr__("groups", frozenset(self.groups)) + + @property + def code(self): + return offset + self.code_without_offset if self.code_without_offset is not None else None + + def has_any_group(self, *group: Group) -> bool: + groups = set(group) + return bool(groups.intersection(self.groups)) + + +def load_item_csv(): + try: + from importlib.resources import files + except ImportError: + from importlib_resources import files # noqa + + items = [] + with files(data).joinpath("items.csv").open() as file: + item_reader = csv.DictReader(file) + for item in item_reader: + id = int(item["id"]) if item["id"] else None + classification = ItemClassification[item["classification"]] + groups = {Group[group] for group in item["groups"].split(",") if group} + items.append(ItemData(id, item["name"], classification, groups)) + return items + + +all_items: List[ItemData] = load_item_csv() +item_table: Dict[str, ItemData] = {} +items_by_group: Dict[Group, List[ItemData]] = {} + + +def initialize_item_table(): + item_table.update({item.name: item for item in all_items}) + + +def initialize_groups(): + for item in all_items: + for group in item.groups: + item_group = items_by_group.get(group, list()) + item_group.append(item) + items_by_group[group] = item_group + + +initialize_item_table() +initialize_groups() + + +def create_trap_items(world, World_Options: Options.DLCQuestOptions, trap_needed: int, random: Random) -> List[Item]: + traps = [] + for i in range(trap_needed): + trap = random.choice(items_by_group[Group.Trap]) + traps.append(world.create_item(trap)) + + return traps + + +def create_items(world, World_Options: Options.DLCQuestOptions, locations_count: int, random: Random): + created_items = [] + if World_Options[Options.Campaign] == Options.Campaign.option_basic or World_Options[ + Options.Campaign] == Options.Campaign.option_both: + for item in items_by_group[Group.DLCQuest]: + if item.has_any_group(Group.DLC): + created_items.append(world.create_item(item)) + if item.has_any_group(Group.Item) and World_Options[ + Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + created_items.append(world.create_item(item)) + if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: + coin_bundle_needed = math.floor(825 / World_Options[Options.CoinSanityRange]) + for item in items_by_group[Group.DLCQuest]: + if item.has_any_group(Group.Coin): + for i in range(coin_bundle_needed): + created_items.append(world.create_item(item)) + if 825 % World_Options[Options.CoinSanityRange] != 0: + created_items.append(world.create_item(item)) + + if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die or World_Options[ + Options.Campaign] == Options.Campaign.option_both: + for item in items_by_group[Group.Freemium]: + if item.has_any_group(Group.DLC): + created_items.append(world.create_item(item)) + if item.has_any_group(Group.Item) and World_Options[ + Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + created_items.append(world.create_item(item)) + if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: + coin_bundle_needed = math.floor(889 / World_Options[Options.CoinSanityRange]) + for item in items_by_group[Group.Freemium]: + if item.has_any_group(Group.Coin): + for i in range(coin_bundle_needed): + created_items.append(world.create_item(item)) + if 889 % World_Options[Options.CoinSanityRange] != 0: + created_items.append(world.create_item(item)) + + trap_items = create_trap_items(world, World_Options, locations_count - len(created_items), random) + created_items += trap_items + + return created_items diff --git a/worlds/dlcquest/Locations.py b/worlds/dlcquest/Locations.py new file mode 100644 index 000000000000..08d37e781216 --- /dev/null +++ b/worlds/dlcquest/Locations.py @@ -0,0 +1,79 @@ +from BaseClasses import Location, MultiWorld +from . import Options + + +class DLCQuestLocation(Location): + game: str = "DLCQuest" + + +offset = 120_000 + +location_table = { + "Movement Pack": offset + 0, + "Animation Pack": offset + 1, + "Audio Pack": offset + 2, + "Pause Menu Pack": offset + 3, + "Time is Money Pack": offset + 4, + "Double Jump Pack": offset + 5, + "Pet Pack": offset + 6, + "Sexy Outfits Pack": offset + 7, + "Top Hat Pack": offset + 8, + "Map Pack": offset + 9, + "Gun Pack": offset + 10, + "The Zombie Pack": offset + 11, + "Night Map Pack": offset + 12, + "Psychological Warfare Pack": offset + 13, + "Armor for your Horse Pack": offset + 14, + "Finish the Fight Pack": offset + 15, + "Particles Pack": offset + 16, + "Day One Patch Pack": offset + 17, + "Checkpoint Pack": offset + 18, + "Incredibly Important Pack": offset + 19, + "Wall Jump Pack": offset + 20, + "Health Bar Pack": offset + 21, + "Parallax Pack": offset + 22, + "Harmless Plants Pack": offset + 23, + "Death of Comedy Pack": offset + 24, + "Canadian Dialog Pack": offset + 25, + "DLC NPC Pack": offset + 26, + "Cut Content Pack": offset + 27, + "Name Change Pack": offset + 28, + "Season Pass": offset + 29, + "High Definition Next Gen Pack": offset + 30, + "Increased HP Pack": offset + 31, + "Remove Ads Pack": offset + 32, + "Big Sword Pack": offset + 33, + "Really Big Sword Pack": offset + 34, + "Unfathomable Sword Pack": offset + 35, + "Pickaxe": offset + 36, + "Gun": offset + 37, + "Sword": offset + 38, + "Wooden Sword": offset + 39, + "Box of Various Supplies": offset + 40, + "Humble Indie Bindle": offset + 41, + "Double Jump Alcove Sheep": offset + 42, + "Double Jump Floating Sheep": offset + 43, + "Sexy Outfits Sheep": offset + 44, + "Forest High Sheep": offset + 45, + "Forest Low Sheep": offset + 46, + "Between Trees Sheep": offset + 47, + "Hole in the Wall Sheep": offset + 48, + "Shepherd Sheep": offset + 49, + "Top Hat Sheep": offset + 50, + "North West Ceiling Sheep": offset + 51, + "North West Alcove Sheep": offset + 52, + "West Cave Sheep": offset + 53, + "Cutscene Sheep": offset + 54, + "Not Exactly Noble": offset + 55, + "Story is Important": offset + 56, + "Nice Try": offset + 57, + "I Get That Reference!": offset + 58, +} + +for i in range(1, 826): + item_coin = f"DLC Quest: {i} Coin" + location_table[item_coin] = offset + 58 + i + +for i in range(1, 890): + item_coin_freemium = f"Live Freemium or Die: {i} Coin" + location_table[item_coin_freemium] = offset + 825 + 58 + i diff --git a/worlds/dlcquest/Options.py b/worlds/dlcquest/Options.py new file mode 100644 index 000000000000..c89c938b5b22 --- /dev/null +++ b/worlds/dlcquest/Options.py @@ -0,0 +1,114 @@ +from typing import Union, Dict, runtime_checkable, Protocol +from Options import Option, DeathLink, Choice, Toggle, SpecialRange +from dataclasses import dataclass + + +@runtime_checkable +class DLCQuestOption(Protocol): + internal_name: str + + +@dataclass +class DLCQuestOptions: + options: Dict[str, Union[bool, int]] + + def __getitem__(self, item: Union[str, DLCQuestOption]) -> Union[bool, int]: + if isinstance(item, DLCQuestOption): + item = item.internal_name + + return self.options.get(item, None) + + +class FalseDoubleJump(Choice): + """If you can do a double jump without the pack for it (glitch).""" + internal_name = "double_jump_glitch" + display_name = "Double Jump glitch" + option_none = 0 + option_simple = 1 + option_all = 2 + default = 0 + + +class TimeIsMoney(Choice): + """Is your time worth the money, are you ready to grind your sword by hand?""" + internal_name = "time_is_money" + display_name = "Time Is Money" + option_required = 0 + option_optional = 1 + default = 0 + + +class CoinSanity(Choice): + """This is for the insane it can be 825 check, it is coin sanity""" + internal_name = "coinsanity" + display_name = "CoinSanity" + option_none = 0 + option_coin = 1 + default = 0 + + +class CoinSanityRange(SpecialRange): + """This is the amount of coin in a coin bundle""" + internal_name = "coinbundlequantity" + display_name = "Coin Bundle Quantity" + range_start = 1 + range_end = 100 + default = 20 + + +class EndingChoice(Choice): + """This is for the ending type of the basic game""" + internal_name = "ending_choice" + display_name = "Ending Choice" + option_any = 0 + option_true = 1 + default = 1 + + +class Campaign(Choice): + """Whitch game you wana play to end""" + internal_name = "campaign" + display_name = "Campaign" + option_basic = 0 + option_live_freemium_or_die = 1 + option_both = 2 + default = 0 + + +class ItemShuffle(Choice): + """Should Inventory Items be separate from their DLCs and shuffled in the item pool""" + internal_name = "item_shuffle" + display_name = "Item Shuffle" + option_disabled = 0 + option_shuffled = 1 + default = 0 + + +DLCQuest_options: Dict[str, type(Option)] = { + option.internal_name: option + for option in [ + FalseDoubleJump, + CoinSanity, + CoinSanityRange, + TimeIsMoney, + EndingChoice, + Campaign, + ItemShuffle, + ] +} +default_options = {option.internal_name: option.default for option in DLCQuest_options.values()} +DLCQuest_options["death_link"] = DeathLink + + +def fetch_options(world, player: int) -> DLCQuestOptions: + return DLCQuestOptions({option: get_option_value(world, player, option) for option in DLCQuest_options}) + + +def get_option_value(world, player: int, name: str) -> Union[bool, int]: + assert name in DLCQuest_options, f"{name} is not a valid option for DLC Quest." + + value = getattr(world, name) + + if issubclass(DLCQuest_options[name], Toggle): + return bool(value[player].value) + return value[player].value diff --git a/worlds/dlcquest/Regions.py b/worlds/dlcquest/Regions.py new file mode 100644 index 000000000000..5553cb48428e --- /dev/null +++ b/worlds/dlcquest/Regions.py @@ -0,0 +1,325 @@ +import math +from BaseClasses import MultiWorld, Region, Location, Entrance, ItemClassification +from .Locations import DLCQuestLocation, location_table +from .Rules import create_event +from . import Options + +DLCQuestRegion = ["Movement Pack", "Behind Tree", "Psychological Warfare", "Double Jump Left", + "Double Jump Behind the Tree", "The Forest", "Final Room"] + + +def add_coin_freemium(region: Region, Coin: int, player: int): + number_coin = f"{Coin} coins freemium" + location_coin = f"{region.name} coins freemium" + location = DLCQuestLocation(player, location_coin, None, region) + region.locations.append(location) + location.place_locked_item(create_event(player, number_coin)) + + +def add_coin_dlcquest(region: Region, Coin: int, player: int): + number_coin = f"{Coin} coins" + location_coin = f"{region.name} coins" + location = DLCQuestLocation(player, location_coin, None, region) + region.locations.append(location) + location.place_locked_item(create_event(player, number_coin)) + + +def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQuestOptions): + Regmenu = Region("Menu", player, world) + if World_Options[Options.Campaign] == Options.Campaign.option_basic or World_Options[ + Options.Campaign] == Options.Campaign.option_both: + Regmenu.exits += [Entrance(player, "DLC Quest Basic", Regmenu)] + if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die or World_Options[ + Options.Campaign] == Options.Campaign.option_both: + Regmenu.exits += [Entrance(player, "Live Freemium or Die", Regmenu)] + world.regions.append(Regmenu) + + if World_Options[Options.Campaign] == Options.Campaign.option_basic or World_Options[ + Options.Campaign] == Options.Campaign.option_both: + + Regmoveright = Region("Move Right", player, world, "Start of the basic game") + Locmoveright_name = ["Movement Pack", "Animation Pack", "Audio Pack", "Pause Menu Pack"] + Regmoveright.exits = [Entrance(player, "Moving", Regmoveright)] + Regmoveright.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regmoveright) for + loc_name in Locmoveright_name] + add_coin_dlcquest(Regmoveright, 4, player) + if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: + coin_bundle_needed = math.floor(825 / World_Options[Options.CoinSanityRange]) + for i in range(coin_bundle_needed): + item_coin = f"DLC Quest: {World_Options[Options.CoinSanityRange] * (i + 1)} Coin" + Regmoveright.locations += [ + DLCQuestLocation(player, item_coin, location_table[item_coin], Regmoveright)] + if 825 % World_Options[Options.CoinSanityRange] != 0: + Regmoveright.locations += [ + DLCQuestLocation(player, "DLC Quest: 825 Coin", location_table["DLC Quest: 825 Coin"], + Regmoveright)] + world.regions.append(Regmoveright) + + Regmovpack = Region("Movement Pack", player, world) + Locmovpack_name = ["Time is Money Pack", "Psychological Warfare Pack", "Armor for your Horse Pack", + "Shepherd Sheep"] + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + Locmovpack_name += ["Sword"] + Regmovpack.exits = [Entrance(player, "Tree", Regmovpack), Entrance(player, "Cloud", Regmovpack)] + Regmovpack.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regmovpack) for loc_name + in Locmovpack_name] + add_coin_dlcquest(Regmovpack, 46, player) + world.regions.append(Regmovpack) + + Regbtree = Region("Behind Tree", player, world) + Locbtree_name = ["Double Jump Pack", "Map Pack", "Between Trees Sheep", "Hole in the Wall Sheep"] + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + Locbtree_name += ["Gun"] + Regbtree.exits = [Entrance(player, "Behind Tree Double Jump", Regbtree), + Entrance(player, "Forest Entrance", Regbtree)] + Regbtree.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regbtree) for loc_name in + Locbtree_name] + add_coin_dlcquest(Regbtree, 60, player) + world.regions.append(Regbtree) + + Regpsywarfare = Region("Psychological Warfare", player, world) + Locpsywarfare_name = ["West Cave Sheep"] + Regpsywarfare.exits = [Entrance(player, "Cloud Double Jump", Regpsywarfare)] + Regpsywarfare.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regpsywarfare) for + loc_name in Locpsywarfare_name] + add_coin_dlcquest(Regpsywarfare, 100, player) + world.regions.append(Regpsywarfare) + + Regdoubleleft = Region("Double Jump Total Left", player, world) + Locdoubleleft_name = ["Pet Pack", "Top Hat Pack", "North West Alcove Sheep"] + Regdoubleleft.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regdoubleleft) for + loc_name in + Locdoubleleft_name] + Regdoubleleft.exits = [Entrance(player, "Cave Tree", Regdoubleleft), + Entrance(player, "Cave Roof", Regdoubleleft)] + add_coin_dlcquest(Regdoubleleft, 50, player) + world.regions.append(Regdoubleleft) + + Regdoubleleftcave = Region("Double Jump Total Left Cave", player, world) + Locdoubleleftcave_name = ["Top Hat Sheep"] + Regdoubleleftcave.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regdoubleleftcave) + for loc_name in Locdoubleleftcave_name] + add_coin_dlcquest(Regdoubleleftcave, 9, player) + world.regions.append(Regdoubleleftcave) + + Regdoubleleftroof = Region("Double Jump Total Left Roof", player, world) + Locdoubleleftroof_name = ["North West Ceiling Sheep"] + Regdoubleleftroof.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regdoubleleftroof) + for loc_name in Locdoubleleftroof_name] + add_coin_dlcquest(Regdoubleleftroof, 10, player) + world.regions.append(Regdoubleleftroof) + + Regdoubletree = Region("Double Jump Behind Tree", player, world) + Locdoubletree_name = ["Sexy Outfits Pack", "Double Jump Alcove Sheep", "Sexy Outfits Sheep"] + Regdoubletree.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regdoubletree) for + loc_name in + Locdoubletree_name] + Regdoubletree.exits = [Entrance(player, "True Double Jump", Regdoubletree)] + add_coin_dlcquest(Regdoubletree, 89, player) + world.regions.append(Regdoubletree) + + Regtruedoublejump = Region("True Double Jump Behind Tree", player, world) + Loctruedoublejump_name = ["Double Jump Floating Sheep", "Cutscene Sheep"] + Regtruedoublejump.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regtruedoublejump) + for loc_name in Loctruedoublejump_name] + add_coin_dlcquest(Regtruedoublejump, 7, player) + world.regions.append(Regtruedoublejump) + + Regforest = Region("The Forest", player, world) + Locforest_name = ["Gun Pack", "Night Map Pack"] + Regforest.exits = [Entrance(player, "Behind Ogre", Regforest), + Entrance(player, "Forest Double Jump", Regforest)] + Regforest.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regforest) for loc_name in + Locforest_name] + add_coin_dlcquest(Regforest, 169, player) + world.regions.append(Regforest) + + Regforestdoublejump = Region("The Forest whit double Jump", player, world) + Locforestdoublejump_name = ["The Zombie Pack", "Forest Low Sheep"] + Regforestdoublejump.exits = [Entrance(player, "Forest True Double Jump", Regforestdoublejump)] + Regforestdoublejump.locations += [ + DLCQuestLocation(player, loc_name, location_table[loc_name], Regforestdoublejump) for loc_name in + Locforestdoublejump_name] + add_coin_dlcquest(Regforestdoublejump, 76, player) + world.regions.append(Regforestdoublejump) + + Regforesttruedoublejump = Region("The Forest whit double Jump Part 2", player, world) + Locforesttruedoublejump_name = ["Forest High Sheep"] + Regforesttruedoublejump.locations += [ + DLCQuestLocation(player, loc_name, location_table[loc_name], Regforesttruedoublejump) + for loc_name in Locforesttruedoublejump_name] + add_coin_dlcquest(Regforesttruedoublejump, 203, player) + world.regions.append(Regforesttruedoublejump) + + Regfinalroom = Region("The Final Boss Room", player, world) + Locfinalroom_name = ["Finish the Fight Pack"] + Regfinalroom.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regfinalroom) for + loc_name in + Locfinalroom_name] + world.regions.append(Regfinalroom) + + loc_win = DLCQuestLocation(player, "Winning Basic", None, world.get_region("The Final Boss Room", player)) + world.get_region("The Final Boss Room", player).locations.append(loc_win) + loc_win.place_locked_item(create_event(player, "Victory Basic")) + + world.get_entrance("DLC Quest Basic", player).connect(world.get_region("Move Right", player)) + + world.get_entrance("Moving", player).connect(world.get_region("Movement Pack", player)) + + world.get_entrance("Tree", player).connect(world.get_region("Behind Tree", player)) + + world.get_entrance("Cloud", player).connect(world.get_region("Psychological Warfare", player)) + + world.get_entrance("Cloud Double Jump", player).connect(world.get_region("Double Jump Total Left", player)) + + world.get_entrance("Cave Tree", player).connect(world.get_region("Double Jump Total Left Cave", player)) + + world.get_entrance("Cave Roof", player).connect(world.get_region("Double Jump Total Left Roof", player)) + + world.get_entrance("Forest Entrance", player).connect(world.get_region("The Forest", player)) + + world.get_entrance("Behind Tree Double Jump", player).connect( + world.get_region("Double Jump Behind Tree", player)) + + world.get_entrance("Behind Ogre", player).connect(world.get_region("The Final Boss Room", player)) + + world.get_entrance("Forest Double Jump", player).connect( + world.get_region("The Forest whit double Jump", player)) + + world.get_entrance("Forest True Double Jump", player).connect( + world.get_region("The Forest whit double Jump Part 2", player)) + + world.get_entrance("True Double Jump", player).connect(world.get_region("True Double Jump Behind Tree", player)) + + if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die or World_Options[ + Options.Campaign] == Options.Campaign.option_both: + + Regfreemiumstart = Region("Freemium Start", player, world) + Locfreemiumstart_name = ["Particles Pack", "Day One Patch Pack", "Checkpoint Pack", "Incredibly Important Pack", + "Nice Try", "Story is Important", "I Get That Reference!"] + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + Locfreemiumstart_name += ["Wooden Sword"] + Regfreemiumstart.exits = [Entrance(player, "Vines", Regfreemiumstart)] + Regfreemiumstart.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regfreemiumstart) + for loc_name in + Locfreemiumstart_name] + add_coin_freemium(Regfreemiumstart, 50, player) + if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: + coin_bundle_needed = math.floor(889 / World_Options[Options.CoinSanityRange]) + for i in range(coin_bundle_needed): + item_coin_freemium = f"Live Freemium or Die: {World_Options[Options.CoinSanityRange] * (i + 1)} Coin" + Regfreemiumstart.locations += [ + DLCQuestLocation(player, item_coin_freemium, location_table[item_coin_freemium], + Regfreemiumstart)] + if 889 % World_Options[Options.CoinSanityRange] != 0: + Regfreemiumstart.locations += [ + DLCQuestLocation(player, "Live Freemium or Die: 889 Coin", + location_table["Live Freemium or Die: 889 Coin"], + Regfreemiumstart)] + world.regions.append(Regfreemiumstart) + + Regbehindvine = Region("Behind the Vines", player, world) + Locbehindvine_name = ["Wall Jump Pack", "Health Bar Pack", "Parallax Pack"] + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + Locbehindvine_name += ["Pickaxe"] + Regbehindvine.exits = [Entrance(player, "Wall Jump Entrance", Regbehindvine)] + Regbehindvine.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regbehindvine) for + loc_name in Locbehindvine_name] + add_coin_freemium(Regbehindvine, 95, player) + world.regions.append(Regbehindvine) + + Regwalljump = Region("Wall Jump", player, world) + Locwalljump_name = ["Harmless Plants Pack", "Death of Comedy Pack", "Canadian Dialog Pack", "DLC NPC Pack"] + Regwalljump.exits = [Entrance(player, "Harmless Plants", Regwalljump), + Entrance(player, "Pickaxe Hard Cave", Regwalljump)] + Regwalljump.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regwalljump) for + loc_name in Locwalljump_name] + add_coin_freemium(Regwalljump, 150, player) + world.regions.append(Regwalljump) + + Regfakeending = Region("Fake Ending", player, world) + Locfakeending_name = ["Cut Content Pack", "Name Change Pack"] + Regfakeending.exits = [Entrance(player, "Name Change Entrance", Regfakeending), + Entrance(player, "Cut Content Entrance", Regfakeending)] + Regfakeending.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regfakeending) for + loc_name in Locfakeending_name] + world.regions.append(Regfakeending) + + Reghardcave = Region("Hard Cave", player, world) + add_coin_freemium(Reghardcave, 20, player) + Reghardcave.exits = [Entrance(player, "Hard Cave Wall Jump", Reghardcave)] + world.regions.append(Reghardcave) + + Reghardcavewalljump = Region("Hard Cave Wall Jump", player, world) + Lochardcavewalljump_name = ["Increased HP Pack"] + Reghardcavewalljump.locations += [ + DLCQuestLocation(player, loc_name, location_table[loc_name], Reghardcavewalljump) for + loc_name in Lochardcavewalljump_name] + add_coin_freemium(Reghardcavewalljump, 130, player) + world.regions.append(Reghardcavewalljump) + + Regcutcontent = Region("Cut Content", player, world) + Loccutcontent_name = [] + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + Loccutcontent_name += ["Humble Indie Bindle"] + Regcutcontent.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regcutcontent) for + loc_name in Loccutcontent_name] + add_coin_freemium(Regcutcontent, 200, player) + world.regions.append(Regcutcontent) + + Regnamechange = Region("Name Change", player, world) + Locnamechange_name = [] + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + Locnamechange_name += ["Box of Various Supplies"] + Regnamechange.exits = [Entrance(player, "Behind Rocks", Regnamechange)] + Regnamechange.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regnamechange) for + loc_name in Locnamechange_name] + world.regions.append(Regnamechange) + + Regtopright = Region("Top Right", player, world) + Loctopright_name = ["Season Pass", "High Definition Next Gen Pack"] + Regtopright.exits = [Entrance(player, "Blizzard", Regtopright)] + Regtopright.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regtopright) for + loc_name in Loctopright_name] + add_coin_freemium(Regtopright, 90, player) + world.regions.append(Regtopright) + + Regseason = Region("Season", player, world) + Locseason_name = ["Remove Ads Pack", "Not Exactly Noble"] + Regseason.exits = [Entrance(player, "Boss Door", Regseason)] + Regseason.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regseason) for + loc_name in Locseason_name] + add_coin_freemium(Regseason, 154, player) + world.regions.append(Regseason) + + Regfinalboss = Region("Final Boss", player, world) + Locfinalboss_name = ["Big Sword Pack", "Really Big Sword Pack", "Unfathomable Sword Pack"] + Regfinalboss.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regfinalboss) for + loc_name in Locfinalboss_name] + world.regions.append(Regfinalboss) + + loc_wining = DLCQuestLocation(player, "Winning Freemium", None, world.get_region("Final Boss", player)) + world.get_region("Final Boss", player).locations.append(loc_wining) + loc_wining.place_locked_item(create_event(player, "Victory Freemium")) + + world.get_entrance("Live Freemium or Die", player).connect(world.get_region("Freemium Start", player)) + + world.get_entrance("Vines", player).connect(world.get_region("Behind the Vines", player)) + + world.get_entrance("Wall Jump Entrance", player).connect(world.get_region("Wall Jump", player)) + + world.get_entrance("Harmless Plants", player).connect(world.get_region("Fake Ending", player)) + + world.get_entrance("Pickaxe Hard Cave", player).connect(world.get_region("Hard Cave", player)) + + world.get_entrance("Hard Cave Wall Jump", player).connect(world.get_region("Hard Cave Wall Jump", player)) + + world.get_entrance("Name Change Entrance", player).connect(world.get_region("Name Change", player)) + + world.get_entrance("Cut Content Entrance", player).connect(world.get_region("Cut Content", player)) + + world.get_entrance("Behind Rocks", player).connect(world.get_region("Top Right", player)) + + world.get_entrance("Blizzard", player).connect(world.get_region("Season", player)) + + world.get_entrance("Boss Door", player).connect(world.get_region("Final Boss", player)) diff --git a/worlds/dlcquest/Rules.py b/worlds/dlcquest/Rules.py new file mode 100644 index 000000000000..ac9cd23d53c8 --- /dev/null +++ b/worlds/dlcquest/Rules.py @@ -0,0 +1,370 @@ +import math +import re +from .Locations import DLCQuestLocation +from ..generic.Rules import add_rule, set_rule +from .Items import DLCQuestItem +from BaseClasses import ItemClassification +from . import Options + + +def create_event(player, event: str): + return DLCQuestItem(event, ItemClassification.progression, None, player) + + +def set_rules(world, player, World_Options: Options.DLCQuestOptions): + def has_enough_coin(player: int, coin: int): + def has_coin(state, player: int, coins: int): + coin_possessed = 0 + for i in [4, 7, 9, 10, 46, 50, 60, 76, 89, 100, 169, 203]: + name_coin = f"{i} coins" + if state.has(name_coin, player): + coin_possessed += i + + return coin_possessed >= coins + + return lambda state: has_coin(state, player, coin) + + def has_enough_coin_freemium(player: int, coin: int): + def has_coin(state, player: int, coins: int): + coin_possessed = 0 + for i in [20, 50, 90, 95, 130, 150, 154, 200]: + name_coin = f"{i} coins freemium" + if state.has(name_coin, player): + coin_possessed += i + + return coin_possessed >= coins + + return lambda state: has_coin(state, player, coin) + + if World_Options[Options.Campaign] == Options.Campaign.option_basic or World_Options[ + Options.Campaign] == Options.Campaign.option_both: + set_rule(world.get_entrance("Moving", player), + lambda state: state.has("Movement Pack", player)) + set_rule(world.get_entrance("Cloud", player), + lambda state: state.has("Psychological Warfare Pack", player)) + set_rule(world.get_entrance("Forest Entrance", player), + lambda state: state.has("Map Pack", player)) + set_rule(world.get_entrance("Forest True Double Jump", player), + lambda state: state.has("Double Jump Pack", player)) + + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_disabled: + set_rule(world.get_entrance("Behind Ogre", player), + lambda state: state.has("Gun Pack", player)) + + if World_Options[Options.TimeIsMoney] == Options.TimeIsMoney.option_required: + set_rule(world.get_entrance("Tree", player), + lambda state: state.has("Time is Money Pack", player)) + set_rule(world.get_entrance("Cave Tree", player), + lambda state: state.has("Time is Money Pack", player)) + set_rule(world.get_location("Shepherd Sheep", player), + lambda state: state.has("Time is Money Pack", player)) + set_rule(world.get_location("North West Ceiling Sheep", player), + lambda state: state.has("Time is Money Pack", player)) + set_rule(world.get_location("North West Alcove Sheep", player), + lambda state: state.has("Time is Money Pack", player)) + set_rule(world.get_location("West Cave Sheep", player), + lambda state: state.has("Time is Money Pack", player)) + + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + set_rule(world.get_entrance("Behind Ogre", player), + lambda state: state.has("Gun", player)) + set_rule(world.get_entrance("Tree", player), + lambda state: state.has("Sword", player) or state.has("Gun", player)) + set_rule(world.get_entrance("Cave Tree", player), + lambda state: state.has("Sword", player) or state.has("Gun", player)) + set_rule(world.get_entrance("True Double Jump", player), + lambda state: state.has("Double Jump Pack", player)) + set_rule(world.get_location("Shepherd Sheep", player), + lambda state: state.has("Sword", player) or state.has("Gun", player)) + set_rule(world.get_location("North West Ceiling Sheep", player), + lambda state: state.has("Sword", player) or state.has("Gun", player)) + set_rule(world.get_location("North West Alcove Sheep", player), + lambda state: state.has("Sword", player) or state.has("Gun", player)) + set_rule(world.get_location("West Cave Sheep", player), + lambda state: state.has("Sword", player) or state.has("Gun", player)) + + if World_Options[Options.TimeIsMoney] == Options.TimeIsMoney.option_required: + set_rule(world.get_location("Sword", player), + lambda state: state.has("Time is Money Pack", player)) + + if World_Options[Options.FalseDoubleJump] == Options.FalseDoubleJump.option_none: + set_rule(world.get_entrance("Cloud Double Jump", player), + lambda state: state.has("Double Jump Pack", player)) + set_rule(world.get_entrance("Forest Double Jump", player), + lambda state: state.has("Double Jump Pack", player)) + + if World_Options[Options.FalseDoubleJump] == Options.FalseDoubleJump.option_none or World_Options[ + Options.FalseDoubleJump] == Options.FalseDoubleJump.option_simple: + set_rule(world.get_entrance("Behind Tree Double Jump", player), + lambda state: state.has("Double Jump Pack", player)) + set_rule(world.get_entrance("Cave Roof", player), + lambda state: state.has("Double Jump Pack", player)) + + if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: + number_of_bundle = math.floor(825 / World_Options[Options.CoinSanityRange]) + for i in range(number_of_bundle): + + item_coin = "DLC Quest: number Coin" + item_coin_loc = re.sub("number", str(World_Options[Options.CoinSanityRange] * (i + 1)), item_coin) + set_rule(world.get_location(item_coin_loc, player), + has_enough_coin(player, World_Options[Options.CoinSanityRange] * (i + 1))) + if 825 % World_Options[Options.CoinSanityRange] != 0: + set_rule(world.get_location("DLC Quest: 825 Coin", player), + has_enough_coin(player, 825)) + + set_rule(world.get_location("Movement Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(4 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Animation Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Audio Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Pause Menu Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Time is Money Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(20 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Double Jump Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(100 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Pet Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Sexy Outfits Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Top Hat Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Map Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(140 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Gun Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(75 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("The Zombie Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Night Map Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(75 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Psychological Warfare Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(50 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Armor for your Horse Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(250 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Finish the Fight Pack", player), + lambda state: state.has("DLC Quest: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + + if World_Options[Options.CoinSanity] == Options.CoinSanity.option_none: + set_rule(world.get_location("Movement Pack", player), + has_enough_coin(player, 4)) + set_rule(world.get_location("Animation Pack", player), + has_enough_coin(player, 5)) + set_rule(world.get_location("Audio Pack", player), + has_enough_coin(player, 5)) + set_rule(world.get_location("Pause Menu Pack", player), + has_enough_coin(player, 5)) + set_rule(world.get_location("Time is Money Pack", player), + has_enough_coin(player, 20)) + set_rule(world.get_location("Double Jump Pack", player), + has_enough_coin(player, 100)) + set_rule(world.get_location("Pet Pack", player), + has_enough_coin(player, 5)) + set_rule(world.get_location("Sexy Outfits Pack", player), + has_enough_coin(player, 5)) + set_rule(world.get_location("Top Hat Pack", player), + has_enough_coin(player, 5)) + set_rule(world.get_location("Map Pack", player), + has_enough_coin(player, 140)) + set_rule(world.get_location("Gun Pack", player), + has_enough_coin(player, 75)) + set_rule(world.get_location("The Zombie Pack", player), + has_enough_coin(player, 5)) + set_rule(world.get_location("Night Map Pack", player), + has_enough_coin(player, 75)) + set_rule(world.get_location("Psychological Warfare Pack", player), + has_enough_coin(player, 50)) + set_rule(world.get_location("Armor for your Horse Pack", player), + has_enough_coin(player, 250)) + set_rule(world.get_location("Finish the Fight Pack", player), + has_enough_coin(player, 5)) + + + if World_Options[Options.EndingChoice] == Options.EndingChoice.option_any: + set_rule(world.get_location("Winning Basic", player), + lambda state: state.has("Finish the Fight Pack", player)) + if World_Options[Options.EndingChoice] == Options.EndingChoice.option_true: + set_rule(world.get_location("Winning Basic", player), + lambda state: state.has("Armor for your Horse Pack", player) and state.has("Finish the Fight Pack", + player)) + + if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die or World_Options[ + Options.Campaign] == Options.Campaign.option_both: + set_rule(world.get_entrance("Wall Jump Entrance", player), + lambda state: state.has("Wall Jump Pack", player)) + set_rule(world.get_entrance("Harmless Plants", player), + lambda state: state.has("Harmless Plants Pack", player)) + set_rule(world.get_entrance("Pickaxe Hard Cave", player), + lambda state: state.has("Pickaxe", player)) + set_rule(world.get_entrance("Name Change Entrance", player), + lambda state: state.has("Name Change Pack", player)) + set_rule(world.get_entrance("Cut Content Entrance", player), + lambda state: state.has("Cut Content Pack", player)) + set_rule(world.get_entrance("Blizzard", player), + lambda state: state.has("Season Pass", player)) + set_rule(world.get_entrance("Boss Door", player), + lambda state: state.has("Big Sword Pack", player) and state.has("Really Big Sword Pack", + player) and state.has( + "Unfathomable Sword Pack", player)) + set_rule(world.get_location("I Get That Reference!", player), + lambda state: state.has("Death of Comedy Pack", player)) + set_rule(world.get_location("Story is Important", player), + lambda state: state.has("DLC NPC Pack", player)) + + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_disabled: + set_rule(world.get_entrance("Vines", player), + lambda state: state.has("Incredibly Important Pack", player)) + set_rule(world.get_entrance("Behind Rocks", player), + lambda state: state.can_reach("Cut Content", 'region', player)) + + if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + set_rule(world.get_entrance("Vines", player), + lambda state: state.has("Wooden Sword", player) or state.has("Pickaxe", player)) + set_rule(world.get_entrance("Behind Rocks", player), + lambda state: state.has("Pickaxe", player)) + + set_rule(world.get_location("Wooden Sword", player), + lambda state: state.has("Incredibly Important Pack", player)) + set_rule(world.get_location("Pickaxe", player), + lambda state: state.has("Humble Indie Bindle", player)) + set_rule(world.get_location("Humble Indie Bindle", player), + lambda state: state.has("Box of Various Supplies", player) and state.can_reach("Cut Content", + 'region', player)) + set_rule(world.get_location("Box of Various Supplies", player), + lambda state: state.can_reach("Cut Content", 'region', player)) + + if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: + number_of_bundle = math.floor(889 / World_Options[Options.CoinSanityRange]) + for i in range(number_of_bundle): + + item_coin_freemium = "Live Freemium or Die: number Coin" + item_coin_loc_freemium = re.sub("number", str(World_Options[Options.CoinSanityRange] * (i + 1)), + item_coin_freemium) + set_rule(world.get_location(item_coin_loc_freemium, player), + has_enough_coin_freemium(player, World_Options[Options.CoinSanityRange] * (i + 1))) + if 889 % World_Options[Options.CoinSanityRange] != 0: + set_rule(world.get_location("Live Freemium or Die: 889 Coin", player), + has_enough_coin_freemium(player, 889)) + + set_rule(world.get_entrance("Boss Door", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(889 / World_Options[Options.CoinSanityRange]))) + + set_rule(world.get_location("Particles Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Day One Patch Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Checkpoint Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Incredibly Important Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(15 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Wall Jump Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(35 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Health Bar Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Parallax Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(5 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Harmless Plants Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(130 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Death of Comedy Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(15 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Canadian Dialog Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(10 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("DLC NPC Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(15 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Cut Content Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(40 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Name Change Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(150 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Season Pass", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(199 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("High Definition Next Gen Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(20 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Increased HP Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(10 / World_Options[Options.CoinSanityRange]))) + set_rule(world.get_location("Remove Ads Pack", player), + lambda state: state.has("Live Freemium or Die: Coin Bundle", player, + math.ceil(25 / World_Options[Options.CoinSanityRange]))) + + if World_Options[Options.CoinSanity] == Options.CoinSanity.option_none: + set_rule(world.get_entrance("Boss Door", player), + has_enough_coin_freemium(player, 889)) + + set_rule(world.get_location("Particles Pack", player), + has_enough_coin_freemium(player, 5)) + set_rule(world.get_location("Day One Patch Pack", player), + has_enough_coin_freemium(player, 5)) + set_rule(world.get_location("Checkpoint Pack", player), + has_enough_coin_freemium(player, 5)) + set_rule(world.get_location("Incredibly Important Pack", player), + has_enough_coin_freemium(player, 15)) + set_rule(world.get_location("Wall Jump Pack", player), + has_enough_coin_freemium(player, 35)) + set_rule(world.get_location("Health Bar Pack", player), + has_enough_coin_freemium(player, 5)) + set_rule(world.get_location("Parallax Pack", player), + has_enough_coin_freemium(player, 5)) + set_rule(world.get_location("Harmless Plants Pack", player), + has_enough_coin_freemium(player, 130)) + set_rule(world.get_location("Death of Comedy Pack", player), + has_enough_coin_freemium(player, 15)) + set_rule(world.get_location("Canadian Dialog Pack", player), + has_enough_coin_freemium(player, 10)) + set_rule(world.get_location("DLC NPC Pack", player), + has_enough_coin_freemium(player, 15)) + set_rule(world.get_location("Cut Content Pack", player), + has_enough_coin_freemium(player, 40)) + set_rule(world.get_location("Name Change Pack", player), + has_enough_coin_freemium(player, 150)) + set_rule(world.get_location("Season Pass", player), + has_enough_coin_freemium(player, 199)) + set_rule(world.get_location("High Definition Next Gen Pack", player), + has_enough_coin_freemium(player, 20)) + set_rule(world.get_location("Increased HP Pack", player), + has_enough_coin_freemium(player, 10)) + set_rule(world.get_location("Remove Ads Pack", player), + has_enough_coin_freemium(player, 25)) + + + + if World_Options[Options.Campaign] == Options.Campaign.option_basic: + world.completion_condition[player] = lambda state: state.has("Victory Basic", player) + + if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die: + world.completion_condition[player] = lambda state: state.has("Victory Freemium", player) + + if World_Options[Options.Campaign] == Options.Campaign.option_both: + world.completion_condition[player] = lambda state: state.has("Victory Basic", player) and state.has( + "Victory Freemium", player) diff --git a/worlds/dlcquest/Script/__init__.py b/worlds/dlcquest/Script/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/worlds/dlcquest/Script/export_items.py b/worlds/dlcquest/Script/export_items.py new file mode 100644 index 000000000000..d946238d3251 --- /dev/null +++ b/worlds/dlcquest/Script/export_items.py @@ -0,0 +1,26 @@ +"""Items export script +This script can be used to export all the AP items into a json file in the output folder. This file is used by the tests +of the mod to ensure it can handle all possible items. + +To run the script, use `python -m worlds.dlcquest.Script.export_items` from the repository root. +""" + +import json +import os.path + +from worlds.dlcquest import item_table + +if not os.path.isdir("output"): + os.mkdir("output") + +if __name__ == "__main__": + with open("output/dlc_quest_item_table.json", "w+") as f: + items = { + item.name: { + "code": item.code, + "classification": item.classification.name + } + for item in item_table.values() + if item.code is not None + } + json.dump({"items": items}, f) diff --git a/worlds/dlcquest/Script/export_locations.py b/worlds/dlcquest/Script/export_locations.py new file mode 100644 index 000000000000..392e05ab3e05 --- /dev/null +++ b/worlds/dlcquest/Script/export_locations.py @@ -0,0 +1,20 @@ +"""Locations export script +This script can be used to export all the AP locations into a json file in the output folder. This file is used by the +tests of the mod to ensure it can handle all possible locations. + +To run the script, use `python -m worlds.stardew_valley.scripts.export_locations` from the repository root. +""" + +import json +import os + +from worlds.dlcquest import location_table + +if not os.path.isdir("output"): + os.mkdir("output") + +if __name__ == "__main__": + with open("output/dlc_quest_location_table.json", "w+") as f: + locations = location_table + + json.dump({"locations": locations}, f) diff --git a/worlds/dlcquest/__init__.py b/worlds/dlcquest/__init__.py new file mode 100644 index 000000000000..9afde0ea2641 --- /dev/null +++ b/worlds/dlcquest/__init__.py @@ -0,0 +1,82 @@ +from typing import Dict, Any, Iterable, Optional, Union +from BaseClasses import Tutorial +from worlds.AutoWorld import World, WebWorld +from .Items import DLCQuestItem, item_table, ItemData, create_items +from .Locations import location_table, DLCQuestLocation +from .Options import DLCQuest_options, DLCQuestOptions, fetch_options +from .Rules import set_rules +from .Regions import create_regions + +client_version = 0 + + +class DLCqwebworld(WebWorld): + tutorials = [Tutorial( + "Multiworld Setup Tutorial", + "A guide to setting up the Archipelago DLCQuest game on your computer.", + "English", + "setup_en.md", + "setup/en", + ["axe_y"] + )] + + +class DLCqworld(World): + """ + DLCQuest is a metroid ish game where everything is an in-game dlc. + """ + game = "DLCQuest" + topology_present = False + web = DLCqwebworld() + + item_name_to_id = {name: data.code for name, data in item_table.items()} + location_name_to_id = location_table + + data_version = 0 + + option_definitions = DLCQuest_options + + def generate_early(self): + self.options = fetch_options(self.multiworld, self.player) + + def create_regions(self): + create_regions(self.multiworld, self.player, self.options) + + def set_rules(self): + set_rules(self.multiworld, self.player, self.options) + + def create_event(self, event: str): + return DLCQuestItem(event, True, None, self.player) + + def create_items(self): + locations_count = len([location + for location in self.multiworld.get_locations(self.player) + if not location.event]) + + items_to_exclude = [excluded_items + for excluded_items in self.multiworld.precollected_items[self.player]] + + created_items = create_items(self, self.options, locations_count + len(items_to_exclude), self.multiworld.random) + + self.multiworld.itempool += created_items + + for item in items_to_exclude: + if item in self.multiworld.itempool: + self.multiworld.itempool.remove(item) + + def create_item(self, item: Union[str, ItemData]) -> DLCQuestItem: + if isinstance(item, str): + item = item_table[item] + + return DLCQuestItem(item.name, item.classification, item.code, self.player) + + def fill_slot_data(self): + return { + "death_link": self.multiworld.death_link[self.player].value, + "ending_choice": self.multiworld.ending_choice[self.player].value, + "campaign": self.multiworld.campaign[self.player].value, + "coinsanity": self.multiworld.coinsanity[self.player].value, + "coinbundlerange": self.multiworld.coinbundlequantity[self.player].value, + "item_shuffle": self.multiworld.item_shuffle[self.player].value, + "seed": self.multiworld.per_slot_randoms[self.player].randrange(99999999) + } diff --git a/worlds/dlcquest/data/items.csv b/worlds/dlcquest/data/items.csv new file mode 100644 index 000000000000..cc5ac0bbe438 --- /dev/null +++ b/worlds/dlcquest/data/items.csv @@ -0,0 +1,48 @@ +id,name,classification,groups +0,Movement Pack,progression,"DLC,DLCQuest" +1,Animation Pack,filler,"DLC,DLCQuest" +2,Audio Pack,filler,"DLC,DLCQuest" +3,Pause Menu Pack,useful,"DLC,DLCQuest" +4,Time is Money Pack,progression,"DLC,DLCQuest" +5,Double Jump Pack,progression,"DLC,DLCQuest" +6,Pet Pack,filler,"DLC,DLCQuest" +7,Sexy Outfits Pack,filler,"DLC,DLCQuest" +8,Top Hat Pack,filler,"DLC,DLCQuest" +9,Map Pack,progression,"DLC,DLCQuest" +10,Gun Pack,progression,"DLC,DLCQuest" +11,The Zombie Pack,filler,"DLC,DLCQuest" +12,Night Map Pack,useful,"DLC,DLCQuest" +13,Psychological Warfare Pack,progression,"DLC,DLCQuest" +14,Armor for your Horse Pack,progression,"DLC,DLCQuest" +15,Finish the Fight Pack,progression,"DLC,DLCQuest" +16,Particles Pack,filler,"DLC,Freemium" +17,Day One Patch Pack,useful,"DLC,Freemium" +18,Checkpoint Pack,useful,"DLC,Freemium" +19,Incredibly Important Pack,progression,"DLC,Freemium" +20,Wall Jump Pack,progression,"DLC,Freemium" +21,Health Bar Pack,useful,"DLC,Freemium" +22,Parallax Pack,filler,"DLC,Freemium" +23,Harmless Plants Pack,progression,"DLC,Freemium" +24,Death of Comedy Pack,progression,"DLC,Freemium" +25,Canadian Dialog Pack,filler,"DLC,Freemium" +26,DLC NPC Pack,progression,"DLC,Freemium" +27,Cut Content Pack,progression,"DLC,Freemium" +28,Name Change Pack,progression,"DLC,Freemium" +29,Pickaxe,progression,"Item,Freemium" +30,Season Pass,progression,"DLC,Freemium" +31,High Definition Next Gen Pack,filler,"DLC,Freemium" +32,Increased HP Pack,useful,"DLC,Freemium" +33,Removed Ads Pack,filler,"DLC,Freemium" +34,Big Sword Pack,progression,"DLC,Freemium" +35,Really Big Sword Pack,progression,"DLC,Freemium" +36,Unfathomable Sword Pack,progression,"DLC,Freemium" +37,Gun,progression,"Item,DLCQuest" +38,Sword,progression,"Item,DLCQuest" +39,Wooden Sword,progression,"Item,Freemium" +40,Box of Various Supplies,progression,"Item,Freemium" +41,Humble Indie Bindle,progression,"Item,Freemium" +42,DLC Quest: Coin Bundle,progression,"Coin,DLCQuest" +43,Live Freemium or Die: Coin Bundle,progression,"Coin,Freemium" +44,Zombie Sheep,trap,Trap +45,Temporary Spike,trap,Trap +46,Loading Screen,trap,Trap \ No newline at end of file diff --git a/worlds/dlcquest/docs/en_DLCQuest.md b/worlds/dlcquest/docs/en_DLCQuest.md new file mode 100644 index 000000000000..333b1e8ed9bb --- /dev/null +++ b/worlds/dlcquest/docs/en_DLCQuest.md @@ -0,0 +1,51 @@ +# DLC Quest + +## 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? + +DLCs are obtained as checks for the multiworld. There are also some other optional checks in DLC Quest + + +## What is the goal of DLC Quest? + +DLC Quest has two campaigns, and the player can choose which one they will play for their slot. +They can also choose to do both campaigns. + + +## What are location checks in DLC Quest? + +Location checks in DLC Quest always include: +- DLC Purchases from the shopkeeper +- Awardment-related objectives + - Killing Sheep in DLC Quest + - Specific Awardment objectives in Live Freemium or Die + +There also are a number of location checks that are optional, and individual players choose to include them or not in their shuffling: +- Items that your character can obtain in various ways + - Swords + - Gun + - Box of Various Supplies + - Humble Indie Bindle + - Pickaxe +- Coinsanity: Coins, either individually or as custom-sized bundles + + +## Which items can be in another player's world? + +Every DLC in the game is shuffled in the item pool. The items related to the optional checks described above are also in the pool + +There are also some brand new trap items, used as filler, based on vanilla game annoyances +- Zombie Sheep +- Loading Screens +- Temporary Spikes + +## When the player receives an item, what happens? + +Every time an item is received while online, a notification appears on screen informing the player of it. +Some items also include an animation or cutscene that will play immediately upon receiving it. + +Items that are received while offline will not play any animation or cutscene, and simply be active when logging in. \ No newline at end of file diff --git a/worlds/dlcquest/docs/setup_en.md b/worlds/dlcquest/docs/setup_en.md new file mode 100644 index 000000000000..8111c654df4e --- /dev/null +++ b/worlds/dlcquest/docs/setup_en.md @@ -0,0 +1,57 @@ +# Stardew Valley Randomizer Setup Guide + +## Required Software + +- DLC Quest on PC (Recommended: [Steam version](https://store.steampowered.com/app/230050/DLC_Quest/)) +- [DLCQuestipelago](https://github.com/agilbert1412/DLCQuestipelago/releases) +- BepinEx (Used as a modloader for DLCQuest. The Mod release above includes BepInEx if you pick the full installer version) + +## Optional Software +- Archipelago from the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases) + - (Only for the TextClient) + +## Configuring your YAML file + +### What is a YAML file and why do I need one? + +See the guide on setting up a basic YAML at the Archipelago setup +guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en) + +### Where do I get a YAML file? + +You can customize your settings by visiting the [DLC Quest Player Settings Page](../player-settings) + +## Joining a MultiWorld Game + +### Installing the mod + +- Download the [DLCQuestipelago mod release](https://github.com/agilbert1412/DLCQuestipelago/releases). If this is your first time installing the mod, or if you are not comfortable with manually editing files, you should pick the Installer. It will handle most of the work for you + + +- Extract the .zip archive to a location of your choice + + +- Run "DLCQuestipelagoInstaller.exe" +![image](https://i.imgur.com/2sPhMgs.png) +- The installer should describe what it is doing each step of the way, and will ask for your input when necessary. + - It will allow you to choose where to install your modded game, and offer a default location + - It will **try** to find your DLCQuest game on your computer, and should it fail, it will ask you to input the path to it + - It will offer the choice of creating a desktop shortcut for the modded launcher + +### Connect to the MultiServer + +- Locate the file "ArchipelagoConnectionInfo.json", at the root of your modded installation. You can edit this file with any text editor, and you need to enter the server ip address, port and your slotname into the relevant fields. + + +- Run BepInEx.NET.Framework.Launcher.exe. If you opted for a desktop shortcut, you will find it with an icon and a more recognizable name. +- ![image](https://i.imgur.com/ZUiFrhf.png) + + +- Your game should launch alongside a modloader console, which will contain important debugging information if you run into problems. +- The game should automatically connect, and attempt reconnecting if your internet or the server fails, during your playthrough. + +### Interacting with the MultiWorld from in-game + +You cannot send commands to the server or chat with the other players from DLC Quest, as the game lacks a proper way to input text. +You can keep track of the server activity in your BepInEx console, as Archipelago chat messages will be displayed in it. +You will need to use an [Archipelago Text Client](https://github.com/ArchipelagoMW/Archipelago/releases) if you want to send commands. \ No newline at end of file