Skip to content

Commit bbef7a4

Browse files
authored
DLCQuest : implement new game (#1628)
adding DLC Quest as a new game
1 parent 8e7bbb4 commit bbef7a4

12 files changed

+1305
-0
lines changed

worlds/dlcquest/Items.py

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import csv
2+
import enum
3+
import math
4+
from typing import Protocol, Union, Dict, List
5+
from BaseClasses import Item, ItemClassification
6+
from . import Options, data
7+
from dataclasses import dataclass, field
8+
from random import Random
9+
10+
11+
class DLCQuestItem(Item):
12+
game: str = "DLCQuest"
13+
14+
15+
offset = 120_000
16+
17+
18+
class Group(enum.Enum):
19+
DLC = enum.auto()
20+
DLCQuest = enum.auto()
21+
Freemium = enum.auto()
22+
Item = enum.auto()
23+
Coin = enum.auto()
24+
Trap = enum.auto()
25+
26+
27+
@dataclass(frozen=True)
28+
class ItemData:
29+
code_without_offset: offset
30+
name: str
31+
classification: ItemClassification
32+
groups: set[Group] = field(default_factory=frozenset)
33+
34+
def __post_init__(self):
35+
if not isinstance(self.groups, frozenset):
36+
super().__setattr__("groups", frozenset(self.groups))
37+
38+
@property
39+
def code(self):
40+
return offset + self.code_without_offset if self.code_without_offset is not None else None
41+
42+
def has_any_group(self, *group: Group) -> bool:
43+
groups = set(group)
44+
return bool(groups.intersection(self.groups))
45+
46+
47+
def load_item_csv():
48+
try:
49+
from importlib.resources import files
50+
except ImportError:
51+
from importlib_resources import files # noqa
52+
53+
items = []
54+
with files(data).joinpath("items.csv").open() as file:
55+
item_reader = csv.DictReader(file)
56+
for item in item_reader:
57+
id = int(item["id"]) if item["id"] else None
58+
classification = ItemClassification[item["classification"]]
59+
groups = {Group[group] for group in item["groups"].split(",") if group}
60+
items.append(ItemData(id, item["name"], classification, groups))
61+
return items
62+
63+
64+
all_items: List[ItemData] = load_item_csv()
65+
item_table: Dict[str, ItemData] = {}
66+
items_by_group: Dict[Group, List[ItemData]] = {}
67+
68+
69+
def initialize_item_table():
70+
item_table.update({item.name: item for item in all_items})
71+
72+
73+
def initialize_groups():
74+
for item in all_items:
75+
for group in item.groups:
76+
item_group = items_by_group.get(group, list())
77+
item_group.append(item)
78+
items_by_group[group] = item_group
79+
80+
81+
initialize_item_table()
82+
initialize_groups()
83+
84+
85+
def create_trap_items(world, World_Options: Options.DLCQuestOptions, trap_needed: int, random: Random) -> List[Item]:
86+
traps = []
87+
for i in range(trap_needed):
88+
trap = random.choice(items_by_group[Group.Trap])
89+
traps.append(world.create_item(trap))
90+
91+
return traps
92+
93+
94+
def create_items(world, World_Options: Options.DLCQuestOptions, locations_count: int, random: Random):
95+
created_items = []
96+
if World_Options[Options.Campaign] == Options.Campaign.option_basic or World_Options[
97+
Options.Campaign] == Options.Campaign.option_both:
98+
for item in items_by_group[Group.DLCQuest]:
99+
if item.has_any_group(Group.DLC):
100+
created_items.append(world.create_item(item))
101+
if item.has_any_group(Group.Item) and World_Options[
102+
Options.ItemShuffle] == Options.ItemShuffle.option_shuffled:
103+
created_items.append(world.create_item(item))
104+
if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin:
105+
coin_bundle_needed = math.floor(825 / World_Options[Options.CoinSanityRange])
106+
for item in items_by_group[Group.DLCQuest]:
107+
if item.has_any_group(Group.Coin):
108+
for i in range(coin_bundle_needed):
109+
created_items.append(world.create_item(item))
110+
if 825 % World_Options[Options.CoinSanityRange] != 0:
111+
created_items.append(world.create_item(item))
112+
113+
if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die or World_Options[
114+
Options.Campaign] == Options.Campaign.option_both:
115+
for item in items_by_group[Group.Freemium]:
116+
if item.has_any_group(Group.DLC):
117+
created_items.append(world.create_item(item))
118+
if item.has_any_group(Group.Item) and World_Options[
119+
Options.ItemShuffle] == Options.ItemShuffle.option_shuffled:
120+
created_items.append(world.create_item(item))
121+
if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin:
122+
coin_bundle_needed = math.floor(889 / World_Options[Options.CoinSanityRange])
123+
for item in items_by_group[Group.Freemium]:
124+
if item.has_any_group(Group.Coin):
125+
for i in range(coin_bundle_needed):
126+
created_items.append(world.create_item(item))
127+
if 889 % World_Options[Options.CoinSanityRange] != 0:
128+
created_items.append(world.create_item(item))
129+
130+
trap_items = create_trap_items(world, World_Options, locations_count - len(created_items), random)
131+
created_items += trap_items
132+
133+
return created_items

worlds/dlcquest/Locations.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from BaseClasses import Location, MultiWorld
2+
from . import Options
3+
4+
5+
class DLCQuestLocation(Location):
6+
game: str = "DLCQuest"
7+
8+
9+
offset = 120_000
10+
11+
location_table = {
12+
"Movement Pack": offset + 0,
13+
"Animation Pack": offset + 1,
14+
"Audio Pack": offset + 2,
15+
"Pause Menu Pack": offset + 3,
16+
"Time is Money Pack": offset + 4,
17+
"Double Jump Pack": offset + 5,
18+
"Pet Pack": offset + 6,
19+
"Sexy Outfits Pack": offset + 7,
20+
"Top Hat Pack": offset + 8,
21+
"Map Pack": offset + 9,
22+
"Gun Pack": offset + 10,
23+
"The Zombie Pack": offset + 11,
24+
"Night Map Pack": offset + 12,
25+
"Psychological Warfare Pack": offset + 13,
26+
"Armor for your Horse Pack": offset + 14,
27+
"Finish the Fight Pack": offset + 15,
28+
"Particles Pack": offset + 16,
29+
"Day One Patch Pack": offset + 17,
30+
"Checkpoint Pack": offset + 18,
31+
"Incredibly Important Pack": offset + 19,
32+
"Wall Jump Pack": offset + 20,
33+
"Health Bar Pack": offset + 21,
34+
"Parallax Pack": offset + 22,
35+
"Harmless Plants Pack": offset + 23,
36+
"Death of Comedy Pack": offset + 24,
37+
"Canadian Dialog Pack": offset + 25,
38+
"DLC NPC Pack": offset + 26,
39+
"Cut Content Pack": offset + 27,
40+
"Name Change Pack": offset + 28,
41+
"Season Pass": offset + 29,
42+
"High Definition Next Gen Pack": offset + 30,
43+
"Increased HP Pack": offset + 31,
44+
"Remove Ads Pack": offset + 32,
45+
"Big Sword Pack": offset + 33,
46+
"Really Big Sword Pack": offset + 34,
47+
"Unfathomable Sword Pack": offset + 35,
48+
"Pickaxe": offset + 36,
49+
"Gun": offset + 37,
50+
"Sword": offset + 38,
51+
"Wooden Sword": offset + 39,
52+
"Box of Various Supplies": offset + 40,
53+
"Humble Indie Bindle": offset + 41,
54+
"Double Jump Alcove Sheep": offset + 42,
55+
"Double Jump Floating Sheep": offset + 43,
56+
"Sexy Outfits Sheep": offset + 44,
57+
"Forest High Sheep": offset + 45,
58+
"Forest Low Sheep": offset + 46,
59+
"Between Trees Sheep": offset + 47,
60+
"Hole in the Wall Sheep": offset + 48,
61+
"Shepherd Sheep": offset + 49,
62+
"Top Hat Sheep": offset + 50,
63+
"North West Ceiling Sheep": offset + 51,
64+
"North West Alcove Sheep": offset + 52,
65+
"West Cave Sheep": offset + 53,
66+
"Cutscene Sheep": offset + 54,
67+
"Not Exactly Noble": offset + 55,
68+
"Story is Important": offset + 56,
69+
"Nice Try": offset + 57,
70+
"I Get That Reference!": offset + 58,
71+
}
72+
73+
for i in range(1, 826):
74+
item_coin = f"DLC Quest: {i} Coin"
75+
location_table[item_coin] = offset + 58 + i
76+
77+
for i in range(1, 890):
78+
item_coin_freemium = f"Live Freemium or Die: {i} Coin"
79+
location_table[item_coin_freemium] = offset + 825 + 58 + i

worlds/dlcquest/Options.py

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
from typing import Union, Dict, runtime_checkable, Protocol
2+
from Options import Option, DeathLink, Choice, Toggle, SpecialRange
3+
from dataclasses import dataclass
4+
5+
6+
@runtime_checkable
7+
class DLCQuestOption(Protocol):
8+
internal_name: str
9+
10+
11+
@dataclass
12+
class DLCQuestOptions:
13+
options: Dict[str, Union[bool, int]]
14+
15+
def __getitem__(self, item: Union[str, DLCQuestOption]) -> Union[bool, int]:
16+
if isinstance(item, DLCQuestOption):
17+
item = item.internal_name
18+
19+
return self.options.get(item, None)
20+
21+
22+
class FalseDoubleJump(Choice):
23+
"""If you can do a double jump without the pack for it (glitch)."""
24+
internal_name = "double_jump_glitch"
25+
display_name = "Double Jump glitch"
26+
option_none = 0
27+
option_simple = 1
28+
option_all = 2
29+
default = 0
30+
31+
32+
class TimeIsMoney(Choice):
33+
"""Is your time worth the money, are you ready to grind your sword by hand?"""
34+
internal_name = "time_is_money"
35+
display_name = "Time Is Money"
36+
option_required = 0
37+
option_optional = 1
38+
default = 0
39+
40+
41+
class CoinSanity(Choice):
42+
"""This is for the insane it can be 825 check, it is coin sanity"""
43+
internal_name = "coinsanity"
44+
display_name = "CoinSanity"
45+
option_none = 0
46+
option_coin = 1
47+
default = 0
48+
49+
50+
class CoinSanityRange(SpecialRange):
51+
"""This is the amount of coin in a coin bundle"""
52+
internal_name = "coinbundlequantity"
53+
display_name = "Coin Bundle Quantity"
54+
range_start = 1
55+
range_end = 100
56+
default = 20
57+
58+
59+
class EndingChoice(Choice):
60+
"""This is for the ending type of the basic game"""
61+
internal_name = "ending_choice"
62+
display_name = "Ending Choice"
63+
option_any = 0
64+
option_true = 1
65+
default = 1
66+
67+
68+
class Campaign(Choice):
69+
"""Whitch game you wana play to end"""
70+
internal_name = "campaign"
71+
display_name = "Campaign"
72+
option_basic = 0
73+
option_live_freemium_or_die = 1
74+
option_both = 2
75+
default = 0
76+
77+
78+
class ItemShuffle(Choice):
79+
"""Should Inventory Items be separate from their DLCs and shuffled in the item pool"""
80+
internal_name = "item_shuffle"
81+
display_name = "Item Shuffle"
82+
option_disabled = 0
83+
option_shuffled = 1
84+
default = 0
85+
86+
87+
DLCQuest_options: Dict[str, type(Option)] = {
88+
option.internal_name: option
89+
for option in [
90+
FalseDoubleJump,
91+
CoinSanity,
92+
CoinSanityRange,
93+
TimeIsMoney,
94+
EndingChoice,
95+
Campaign,
96+
ItemShuffle,
97+
]
98+
}
99+
default_options = {option.internal_name: option.default for option in DLCQuest_options.values()}
100+
DLCQuest_options["death_link"] = DeathLink
101+
102+
103+
def fetch_options(world, player: int) -> DLCQuestOptions:
104+
return DLCQuestOptions({option: get_option_value(world, player, option) for option in DLCQuest_options})
105+
106+
107+
def get_option_value(world, player: int, name: str) -> Union[bool, int]:
108+
assert name in DLCQuest_options, f"{name} is not a valid option for DLC Quest."
109+
110+
value = getattr(world, name)
111+
112+
if issubclass(DLCQuest_options[name], Toggle):
113+
return bool(value[player].value)
114+
return value[player].value

0 commit comments

Comments
 (0)