Skip to content

Commit

Permalink
DLCQuest : implement new game (ArchipelagoMW#1628)
Browse files Browse the repository at this point in the history
adding DLC Quest as a new game
  • Loading branch information
axe-y authored and Jouramie committed Feb 28, 2024
1 parent fb4ab6a commit 73e68d4
Show file tree
Hide file tree
Showing 12 changed files with 1,305 additions and 0 deletions.
133 changes: 133 additions & 0 deletions worlds/dlcquest/Items.py
Original file line number Diff line number Diff line change
@@ -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
79 changes: 79 additions & 0 deletions worlds/dlcquest/Locations.py
Original file line number Diff line number Diff line change
@@ -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
114 changes: 114 additions & 0 deletions worlds/dlcquest/Options.py
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 73e68d4

Please sign in to comment.