Files
dockipelago/worlds/dc1/__init__.py
Jonathan Tinney 7971961166
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
add schedule I, sonic 1/frontiers/heroes, spirit island
2026-04-02 23:46:36 -07:00

397 lines
21 KiB
Python

import json
import pkgutil
import typing
from typing import Mapping, Any, Optional
from BaseClasses import Region, LocationProgressType, Item, CollectionState, ItemClassification, Tutorial
from worlds.AutoWorld import World, WebWorld
from worlds.generic.Rules import set_rule
from .data import (NoruneGeoItems, MatatakiGeoItems, QueensGeoItems,
MuskaGeoItems, FactoryGeoItems, DHCGeoItems)
from . import Rules
from .Items import DarkCloudItem, ItemData
from .Location import DarkCloudLocation
from .Options import DarkCloudOptions
from .data.MiracleChest import MiracleChest
from .game_id import dc1_name
geo_funcs = [NoruneGeoItems.create_norune_atla, MatatakiGeoItems.create_matataki_atla,
QueensGeoItems.create_queens_atla, MuskaGeoItems.create_muska_atla,
FactoryGeoItems.create_factory_atla, DHCGeoItems.create_castle_atla]
geo_class = [NoruneGeoItems, MatatakiGeoItems, QueensGeoItems, MuskaGeoItems, FactoryGeoItems, DHCGeoItems]
dungeon_locations = json.loads(pkgutil.get_data(__name__, "data/atla_locations.json").decode())
# TODO webworld implementation as we get closer to completion.
class DarkCloudWeb(WebWorld):
theme = "jungle"
tutorials = [Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Super Mario Sunshine software on your computer. This guide covers single-player, "
"multiworld, and related software.",
"English",
"setup_en.md",
"setup/en",
["gyst"]
)]
bug_report_page = ""
class DarkCloudWorld(World):
"""
It is said superblal is still stuck in this game to this day.
"""
game = dc1_name
required_client_version = (0, 6, 1)
options_dataclass = Options.DarkCloudOptions
options: Options.DarkCloudOptions
topology_present = True
web = DarkCloudWeb()
item_name_to_id = {}
location_name_to_id = {}
item_name_to_classification = {}
filler_item_names = []
# Parse inventory item data
item_data = [[],[],[],[],[]]
reader = pkgutil.get_data(__name__, "data/item_data.csv").decode().splitlines()
for line in reader:
row = line.split(",")
item_name_to_id.update({row[0]: int(row[1])})
classification = ItemClassification(int(row[2]))
if classification == ItemClassification.filler:
filler_item_names.append(row[0])
# [3]-[7] are counts if an item should be added for a given town 0-5.
counts = []
for i in range(3, 8):
if row[i]:
counts.append(int(row[i]))
else:
counts.append(0)
item_data[0].append(ItemData(row[0], int(row[1]), classification, counts))
item_name_to_classification[row[0]] = classification
for i in geo_class:
item_name_to_id.update(i.ids)
item_name_to_classification.update(i.classifications)
for i in dungeon_locations:
location_name_to_id.update(i)
# Parse in the miracle chest data
mc_data = [[], [], [], [], []]
reader = pkgutil.get_data(__name__, "data/miracle_locations.csv").decode().splitlines()
for line in reader:
row = line.split(",")
mc_data[int(row[2])].append(MiracleChest(row[0], row[1], row[2], row[3], row[4]))
for i in mc_data:
for j in i:
location_name_to_id.update({str(j.name): int(j.ap_id)})
origin_region_name = "Norune"
def create_items(self):
for i in range(self.options.boss_goal):
self.multiworld.itempool.extend(geo_funcs[i](self.options, self.player))
if self.options.miracle_sanity:
for i in range(min(5, int(self.options.boss_goal))):
for item in self.item_data[i]:
self.multiworld.itempool.extend(item.to_items(self.player, self))
# Set up progressive items
def collect_item(self, state: "CollectionState", item: "Item", remove: bool = False) -> Optional[str]:
if not item.advancement:
return None
name = item.name
if name.startswith("Progressive "):
prog_table = Items.progressive_item_list[name]
if remove:
for item_name in reversed(prog_table):
if state.has(item_name, item.player):
return item_name
else:
for item_name in prog_table:
if not state.has(item_name, item.player):
return item_name
return super(DarkCloudWorld, self).collect_item(state, item, remove)
def create_regions(self):
regions: typing.Dict[str, Region] = {}
# Towns
norune = Region("Norune", self.player, self.multiworld)
matataki = Region("Matataki", self.player, self.multiworld)
queens = Region("Queens", self.player, self.multiworld)
muska = Region("Muska", self.player, self.multiworld)
factory = Region("Factory", self.player, self.multiworld)
dhc = Region("DHC", self.player, self.multiworld)
# Dungeons
dbc1 = Region("DBC1", self.player, self.multiworld)
dbc2 = Region("DBC2", self.player, self.multiworld)
wof1 = Region("WOF1", self.player, self.multiworld)
wof2 = Region("WOF2", self.player, self.multiworld)
sr1 = Region("SR1", self.player, self.multiworld)
sr2 = Region("SR2", self.player, self.multiworld)
smt1 = Region("SMT1", self.player, self.multiworld)
smt2 = Region("SMT2", self.player, self.multiworld)
ms1 = Region("MS1", self.player, self.multiworld)
ms2 = Region("MS2", self.player, self.multiworld)
got = Region("GOT", self.player, self.multiworld)
towns = [norune, matataki, queens, muska, factory, dhc]
dungeons = [dbc1, dbc2, wof1, wof2, sr1, sr2, smt1, smt2, ms1, ms2, got]
for town in towns:
regions[town.name] = town
for dungeon in dungeons:
regions[dungeon.name] = dungeon
# Only add locations for the relevant dungeons
for i in range(min(len(dungeons), self.options.boss_goal * 2)):
dun = dungeons[i]
dun_locs = dungeon_locations[i]
# Create locations, then add to the dungeons
for key in dun_locs:
loc = DarkCloudLocation(self.player, key, dun_locs[key], LocationProgressType.DEFAULT, dun)
dun.locations.append(loc)
if self.options.miracle_sanity:
for i in range(min(5, int(self.options.boss_goal))):
mcs = self.mc_data[i]
for chest in mcs:
loc = DarkCloudLocation(self.player, str(chest.name), int(chest.ap_id),
LocationProgressType.DEFAULT, towns[i])
loc.access_rule = lambda state, a=chest.req_char, b=chest.req_geo: Rules.chest_test(state,
self.player, a,
b)
towns[i].locations.append(loc)
# Connect Regions
def create_connection(from_region: str, to_region: str):
regions[from_region].connect(regions[to_region])
create_connection("Norune", "Matataki")
create_connection("Norune", "Queens")
create_connection("Norune", "Muska")
create_connection("Norune", "Factory")
create_connection("Norune", "DHC")
create_connection("Norune", "DBC1")
create_connection("Norune", "DBC2")
create_connection("Matataki", "WOF1")
create_connection("Matataki", "WOF2")
create_connection("Queens", "SR1")
create_connection("Queens", "SR2")
create_connection("Muska", "SMT1")
create_connection("Muska", "SMT2")
create_connection("Factory", "MS1")
create_connection("Factory", "MS2")
create_connection("DHC", "GOT")
self.multiworld.regions.extend(towns)
self.multiworld.regions.extend(dungeons)
def set_rules(self):
set_rule(self.multiworld.get_entrance("Norune -> DBC1", self.player), lambda state: True)
if hasattr(self.multiworld, "generation_is_fake"): # UT doesn't need the shop for its logic
set_rule(self.multiworld.get_entrance("Norune -> DBC2", self.player),
lambda state: Rules.xiao_available_ut(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> Matataki", self.player),
lambda state: Rules.xiao_available_ut(state, self.player))
set_rule(self.multiworld.get_entrance("Matataki -> WOF1", self.player),
lambda state: Rules.xiao_available_ut(state, self.player))
else:
set_rule(self.multiworld.get_entrance("Norune -> DBC2", self.player),
lambda state: Rules.xiao_available(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> Matataki", self.player),
lambda state: Rules.xiao_available(state, self.player))
set_rule(self.multiworld.get_entrance("Matataki -> WOF1", self.player),
lambda state: Rules.xiao_available(state, self.player))
if self.options.miracle_sanity and not hasattr(self.multiworld, "generation_is_fake"):
set_rule(self.multiworld.get_entrance("Matataki -> WOF2", self.player),
lambda state: Rules.goro_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> Queens", self.player),
lambda state: Rules.goro_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Queens -> SR1", self.player),
lambda state: Rules.goro_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Queens -> SR2", self.player),
lambda state: Rules.ruby_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> Muska", self.player),
lambda state: Rules.ruby_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Muska -> SMT1", self.player),
lambda state: Rules.ruby_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Muska -> SMT2", self.player),
lambda state: Rules.ungaga_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> Factory", self.player),
lambda state: Rules.ungaga_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Factory -> MS1", self.player),
lambda state: Rules.ungaga_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Factory -> MS2", self.player),
lambda state: Rules.osmond_available_items(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> DHC", self.player),
lambda state: Rules.got_accessible_items(state, self.player))
set_rule(self.multiworld.get_entrance("DHC -> GOT", self.player),
lambda state: Rules.got_accessible_items(state, self.player))
else:
set_rule(self.multiworld.get_entrance("Matataki -> WOF2", self.player),
lambda state: Rules.goro_available(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> Queens", self.player),
lambda state: Rules.goro_available(state, self.player))
set_rule(self.multiworld.get_entrance("Queens -> SR1", self.player),
lambda state: Rules.goro_available(state, self.player))
set_rule(self.multiworld.get_entrance("Queens -> SR2", self.player),
lambda state: Rules.ruby_available(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> Muska", self.player),
lambda state: Rules.ruby_available(state, self.player))
set_rule(self.multiworld.get_entrance("Muska -> SMT1", self.player),
lambda state: Rules.ruby_available(state, self.player))
set_rule(self.multiworld.get_entrance("Muska -> SMT2", self.player),
lambda state: Rules.ungaga_available(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> Factory", self.player),
lambda state: Rules.ungaga_available(state, self.player))
set_rule(self.multiworld.get_entrance("Factory -> MS1", self.player),
lambda state: Rules.ungaga_available(state, self.player))
set_rule(self.multiworld.get_entrance("Factory -> MS2", self.player),
lambda state: Rules.osmond_available(state, self.player))
set_rule(self.multiworld.get_entrance("Norune -> DHC", self.player),
lambda state: Rules.got_accessible(state, self.player))
set_rule(self.multiworld.get_entrance("DHC -> GOT", self.player),
lambda state: Rules.got_accessible(state, self.player))
# Set up completion goal
match self.options.boss_goal:
case 2:
if self.options.all_bosses:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.two_bosses_items(state,
self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.two_bosses(state,
self.player)
else:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.utan_access(state,
self.player)\
and Rules.goro_available_items(state, self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.utan_access(state,
self.player)\
and Rules.goro_available(state, self.player)
case 3:
if self.options.all_bosses:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.three_bosses_items(state,
self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.three_bosses(state,
self.player)
else:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.saia_access(state,
self.player)\
and Rules.ruby_available_items(state, self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.saia_access(state,
self.player)\
and Rules.ruby_available(state, self.player)
case 4:
if self.options.all_bosses:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.four_bosses_items(state,
self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.four_bosses(state,
self.player)
else:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.curse_access(state,
self.player)\
and Rules.ungaga_available_items(state, self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.curse_access(state,
self.player)\
and Rules.ungaga_available(state, self.player)
case 5:
if self.options.all_bosses:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.five_bosses_items(state,
self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.five_bosses(state,
self.player)
else:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.joe_access(state,
self.player)\
and Rules.osmond_available_items(
state, self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.joe_access(state,
self.player)\
and Rules.osmond_available(
state, self.player)
case 6:
if self.options.all_bosses:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.six_bosses_items(state,
self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.six_bosses(state,
self.player)
else:
if self.options.miracle_sanity:
self.multiworld.completion_condition[self.player] = lambda state: Rules.genie_access(state,
self.player) \
and Rules.osmond_available_items(
state, self.player)
else:
self.multiworld.completion_condition[self.player] = lambda state: Rules.genie_access(state,
self.player) \
and Rules.osmond_available(
state, self.player)
def create_item(self, name:str) -> DarkCloudItem:
classification = self.item_name_to_classification[name]
return DarkCloudItem(name, classification, self.item_name_to_id[name], self.player)
def get_filler_item_name(self) -> str:
return self.filler_item_names[self.random.randint(0, len(self.filler_item_names) - 1)]
def fill_slot_data(self) -> Mapping[str, Any]:
slot_data = {
"options": {
"goal": self.options.boss_goal.value,
"all_bosses": self.options.all_bosses.value,
"open_dungeon": self.options.open_dungeon.value,
"starter_weapons": self.options.starter_weapons.value,
"abs_multiplier": self.options.abs_multiplier.value,
"attach_multiplier": self.options.attach_multiplier.value,
"attach_mult_config": self.options.attach_mult_config.value,
"auto_build": self.options.auto_build.value,
"miracle_sanity": self.options.miracle_sanity.value,
"death_link": self.options.death_link.value,
},
}
return slot_data