Remove Slay the Spire (#4673)

* Remove Slay the Spire

* remove slay the spire
This commit is contained in:
KonoTyran
2025-04-25 11:54:53 -07:00
committed by GitHub
parent 8755d5cbc0
commit 2624a0a7ea
14 changed files with 8 additions and 464 deletions

View File

@@ -9,7 +9,6 @@ Currently, the following games are supported:
* Factorio
* Minecraft
* Subnautica
* Slay the Spire
* Risk of Rain 2
* The Legend of Zelda: Ocarina of Time
* Timespinner

View File

@@ -184,9 +184,6 @@
# Secret of Evermore
/worlds/soe/ @black-sliver
# Slay the Spire
/worlds/spire/ @KonoTyran
# Stardew Valley
/worlds/stardew_valley/ @agilbert1412

View File

@@ -117,8 +117,6 @@ flowchart LR
%% Java Based Games
subgraph Java
JM[Mod with Archipelago.MultiClient.Java]
STS[Slay the Spire]
JM <-- Mod the Spire --> STS
subgraph Minecraft
MCS[Minecraft Forge Server]
JMC[Any Java Minecraft Clients]

View File

@@ -72,7 +72,6 @@ non_apworlds: Set[str] = {
"Ocarina of Time",
"Overcooked! 2",
"Raft",
"Slay the Spire",
"Sudoku",
"Super Mario 64",
"VVVVVV",

View File

@@ -278,7 +278,7 @@ one file, removing the need to manage separate files if one chooses to do so.
As a precautionary measure, before submitting a multi-game yaml like this one in a synchronous/sync multiworld, please
confirm that the other players in the multi are OK with what you are submitting, and please be fairly reasonable about
the submission. (i.e. Multiple long games (SMZ3, OoT, HK, etc.) for a game intended to be <2 hrs is not likely considered
reasonable, but submitting a ChecksFinder alongside another game OR submitting multiple Slay the Spire runs is likely
reasonable, but submitting a ChecksFinder alongside another game is likely
OK)
To configure your file to generate multiple worlds, use 3 dashes `---` on an empty line to separate the ending of one
@@ -335,7 +335,7 @@ Minecraft:
---
description: Example of generating multiple worlds. World 3 of 3
description: Example of generating multiple worlds. World 2 of 2
name: ExampleFinder
game: ChecksFinder

View File

@@ -104,15 +104,7 @@ A list of all available items and locations can be found in the [website's datap
- Spirit Temple Silver Gauntlets Chest
world: false
# example block 3 - Slay the Spire
- items:
Boss Relic: 3
locations:
- Boss Relic 1
- Boss Relic 2
- Boss Relic 3
# example block 4 - Factorio
# example block 3 - Factorio
- items:
progressive-electric-energy-distribution: 2
electric-energy-accumulators: 1
@@ -125,7 +117,7 @@ A list of all available items and locations can be found in the [website's datap
percentage: 80
force: true
# example block 5 - Secret of Evermore
# example block 4 - Secret of Evermore
- items:
Levitate: 1
Revealer: 1
@@ -136,7 +128,7 @@ A list of all available items and locations can be found in the [website's datap
world: true
count: 2
# example block 6 - A Link to the Past
# example block 5 - A Link to the Past
- items:
Progressive Sword: 4
world:
@@ -150,12 +142,11 @@ A list of all available items and locations can be found in the [website's datap
player's Starter Chest 1 and removes the chosen item from the item pool.
2. This block will always trigger and will place the player's swords, bow, magic meter, strength upgrades, and hookshots
in their own dungeon major item chests.
3. This block will always trigger and will lock boss relics on the bosses.
4. This block has an 80% chance of occurring, and when it does, it will place all but 1 of the items randomly among the
3. This block has an 80% chance of occurring, and when it does, it will place all but 1 of the items randomly among the
four locations chosen here.
5. This block will always trigger and will attempt to place a random 2 of Levitate, Revealer and Energize into
4. This block will always trigger and will attempt to place a random 2 of Levitate, Revealer and Energize into
other players' Master Sword Pedestals or Boss Relic 1 locations.
6. This block will always trigger and will attempt to place a random number, between 1 and 4, of progressive swords
5. This block will always trigger and will attempt to place a random number, between 1 and 4, of progressive swords
into any locations within the game slots named BobsSlaytheSpire and BobsRogueLegacy.

View File

@@ -1,39 +0,0 @@
import typing
from BaseClasses import Item
from typing import Dict
class ItemData(typing.NamedTuple):
code: typing.Optional[int]
progression: bool
event: bool = False
item_table: Dict[str, ItemData] = {
'Card Draw': ItemData(8000, True),
'Rare Card Draw': ItemData(8001, True),
'Relic': ItemData(8002, True),
'Boss Relic': ItemData(8003, True),
# Event Items
'Victory': ItemData(None, True, True),
'Beat Act 1 Boss': ItemData(None, True, True),
'Beat Act 2 Boss': ItemData(None, True, True),
'Beat Act 3 Boss': ItemData(None, True, True),
}
item_pool: Dict[str, int] = {
'Card Draw': 15,
'Rare Card Draw': 2,
'Relic': 10,
'Boss Relic': 2
}
event_item_pairs: Dict[str, str] = {
"Heart Room": "Victory",
"Act 1 Boss": "Beat Act 1 Boss",
"Act 2 Boss": "Beat Act 2 Boss",
"Act 3 Boss": "Beat Act 3 Boss"
}

View File

@@ -1,35 +0,0 @@
location_table = {
'Card Draw 1': 19001,
'Card Draw 2': 19002,
'Card Draw 3': 19003,
'Card Draw 4': 19004,
'Card Draw 5': 19005,
'Card Draw 6': 19006,
'Card Draw 7': 19007,
'Card Draw 8': 19008,
'Card Draw 9': 19009,
'Card Draw 10': 19010,
'Card Draw 11': 19011,
'Card Draw 12': 19012,
'Card Draw 13': 19013,
'Card Draw 14': 19014,
'Card Draw 15': 19015,
'Rare Card Draw 1': 21001,
'Rare Card Draw 2': 21002,
'Relic 1': 20001,
'Relic 2': 20002,
'Relic 3': 20003,
'Relic 4': 20004,
'Relic 5': 20005,
'Relic 6': 20006,
'Relic 7': 20007,
'Relic 8': 20008,
'Relic 9': 20009,
'Relic 10': 20010,
'Boss Relic 1': 22001,
'Boss Relic 2': 22002,
'Heart Room': None,
'Act 1 Boss': None,
'Act 2 Boss': None,
'Act 3 Boss': None
}

View File

@@ -1,74 +0,0 @@
import typing
from dataclasses import dataclass
from Options import TextChoice, Range, Toggle, PerGameCommonOptions
class Character(TextChoice):
"""Enter the internal ID of the character to use.
if you don't know the exact ID to enter with the mod installed go to
`Mods -> Archipelago Multi-world -> config` to view a list of installed modded character IDs.
the downfall characters will only work if you have downfall installed.
Spire Take the Wheel will have your client pick a random character from the list of all your installed characters
including custom ones.
if the chosen character mod is not installed it will default back to 'The Ironclad'
"""
display_name = "Character"
option_The_Ironclad = 0
option_The_Silent = 1
option_The_Defect = 2
option_The_Watcher = 3
option_The_Hermit = 4
option_The_Slime_Boss = 5
option_The_Guardian = 6
option_The_Hexaghost = 7
option_The_Champ = 8
option_The_Gremlins = 9
option_The_Automaton = 10
option_The_Snecko = 11
option_spire_take_the_wheel = 12
class Ascension(Range):
"""What Ascension do you wish to play with."""
display_name = "Ascension"
range_start = 0
range_end = 20
default = 0
class FinalAct(Toggle):
"""Whether you will need to collect the 3 keys and beat the final act to complete the game."""
display_name = "Final Act"
option_true = 1
option_false = 0
default = 0
class Downfall(Toggle):
"""When Downfall is Installed this will switch the played mode to Downfall"""
display_name = "Downfall"
option_true = 1
option_false = 0
default = 0
class DeathLink(Range):
"""Percentage of health to lose when a death link is received."""
display_name = "Death Link %"
range_start = 0
range_end = 100
default = 0
@dataclass
class SpireOptions(PerGameCommonOptions):
character: Character
ascension: Ascension
final_act: FinalAct
downfall: Downfall
death_link: DeathLink

View File

@@ -1,11 +0,0 @@
def create_regions(world, player: int):
from . import create_region
from .Locations import location_table
world.regions += [
create_region(world, player, 'Menu', None, ['Neow\'s Room']),
create_region(world, player, 'The Spire', [location for location in location_table])
]
# link up our region with the entrance we just made
world.get_entrance('Neow\'s Room', player).connect(world.get_region('The Spire', player))

View File

@@ -1,74 +0,0 @@
from BaseClasses import MultiWorld
from ..AutoWorld import LogicMixin
from ..generic.Rules import set_rule
class SpireLogic(LogicMixin):
def _spire_has_relics(self, player: int, amount: int) -> bool:
count: int = self.count("Relic", player) + self.count("Boss Relic", player)
return count >= amount
def _spire_has_cards(self, player: int, amount: int) -> bool:
count = self.count("Card Draw", player) + self.count("Rare Card Draw", player)
return count >= amount
def set_rules(world: MultiWorld, player: int):
# Act 1 Card Draws
set_rule(world.get_location("Card Draw 1", player), lambda state: True)
set_rule(world.get_location("Card Draw 2", player), lambda state: True)
set_rule(world.get_location("Card Draw 3", player), lambda state: True)
set_rule(world.get_location("Card Draw 4", player), lambda state: state._spire_has_relics(player, 1))
set_rule(world.get_location("Card Draw 5", player), lambda state: state._spire_has_relics(player, 1))
# Act 1 Relics
set_rule(world.get_location("Relic 1", player), lambda state: state._spire_has_cards(player, 1))
set_rule(world.get_location("Relic 2", player), lambda state: state._spire_has_cards(player, 2))
set_rule(world.get_location("Relic 3", player), lambda state: state._spire_has_cards(player, 2))
# Act 1 Boss Event
set_rule(world.get_location("Act 1 Boss", player), lambda state: state._spire_has_cards(player, 3) and state._spire_has_relics(player, 2))
# Act 1 Boss Rewards
set_rule(world.get_location("Rare Card Draw 1", player), lambda state: state.has("Beat Act 1 Boss", player))
set_rule(world.get_location("Boss Relic 1", player), lambda state: state.has("Beat Act 1 Boss", player))
# Act 2 Card Draws
set_rule(world.get_location("Card Draw 6", player), lambda state: state.has("Beat Act 1 Boss", player))
set_rule(world.get_location("Card Draw 7", player), lambda state: state.has("Beat Act 1 Boss", player))
set_rule(world.get_location("Card Draw 8", player), lambda state: state.has("Beat Act 1 Boss", player) and state._spire_has_cards(player, 6) and state._spire_has_relics(player, 3))
set_rule(world.get_location("Card Draw 9", player), lambda state: state.has("Beat Act 1 Boss", player) and state._spire_has_cards(player, 6) and state._spire_has_relics(player, 4))
set_rule(world.get_location("Card Draw 10", player), lambda state: state.has("Beat Act 1 Boss", player) and state._spire_has_cards(player, 7) and state._spire_has_relics(player, 4))
# Act 2 Relics
set_rule(world.get_location("Relic 4", player), lambda state: state.has("Beat Act 1 Boss", player) and state._spire_has_cards(player, 7) and state._spire_has_relics(player, 2))
set_rule(world.get_location("Relic 5", player), lambda state: state.has("Beat Act 1 Boss", player) and state._spire_has_cards(player, 7) and state._spire_has_relics(player, 2))
set_rule(world.get_location("Relic 6", player), lambda state: state.has("Beat Act 1 Boss", player) and state._spire_has_cards(player, 7) and state._spire_has_relics(player, 3))
# Act 2 Boss Event
set_rule(world.get_location("Act 2 Boss", player), lambda state: state.has("Beat Act 1 Boss", player) and state._spire_has_cards(player, 7) and state._spire_has_relics(player, 4) and state.has("Boss Relic", player))
# Act 2 Boss Rewards
set_rule(world.get_location("Rare Card Draw 2", player), lambda state: state.has("Beat Act 2 Boss", player))
set_rule(world.get_location("Boss Relic 2", player), lambda state: state.has("Beat Act 2 Boss", player))
# Act 3 Card Draws
set_rule(world.get_location("Card Draw 11", player), lambda state: state.has("Beat Act 2 Boss", player))
set_rule(world.get_location("Card Draw 12", player), lambda state: state.has("Beat Act 2 Boss", player))
set_rule(world.get_location("Card Draw 13", player), lambda state: state.has("Beat Act 2 Boss", player) and state._spire_has_relics(player, 4))
set_rule(world.get_location("Card Draw 14", player), lambda state: state.has("Beat Act 2 Boss", player) and state._spire_has_relics(player, 4))
set_rule(world.get_location("Card Draw 15", player), lambda state: state.has("Beat Act 2 Boss", player) and state._spire_has_relics(player, 4))
# Act 3 Relics
set_rule(world.get_location("Relic 7", player), lambda state: state.has("Beat Act 2 Boss", player) and state._spire_has_relics(player, 4))
set_rule(world.get_location("Relic 8", player), lambda state: state.has("Beat Act 2 Boss", player) and state._spire_has_relics(player, 5))
set_rule(world.get_location("Relic 9", player), lambda state: state.has("Beat Act 2 Boss", player) and state._spire_has_relics(player, 5))
set_rule(world.get_location("Relic 10", player), lambda state: state.has("Beat Act 2 Boss", player) and state._spire_has_relics(player, 5))
# Act 3 Boss Event
set_rule(world.get_location("Act 3 Boss", player), lambda state: state.has("Beat Act 2 Boss", player) and state._spire_has_relics(player, 7) and state.has("Boss Relic", player, 2))
set_rule(world.get_location("Heart Room", player), lambda state: state.has("Beat Act 3 Boss", player))
world.completion_condition[player] = lambda state: state.has("Victory", player)

View File

@@ -1,103 +0,0 @@
import string
from BaseClasses import Entrance, Item, ItemClassification, Location, MultiWorld, Region, Tutorial
from .Items import event_item_pairs, item_pool, item_table
from .Locations import location_table
from .Options import SpireOptions
from .Regions import create_regions
from .Rules import set_rules
from ..AutoWorld import WebWorld, World
class SpireWeb(WebWorld):
tutorials = [Tutorial(
"Multiworld Setup Guide",
"A guide to setting up Slay the Spire for Archipelago. "
"This guide covers single-player, multiworld, and related software.",
"English",
"slay-the-spire_en.md",
"slay-the-spire/en",
["Phar"]
)]
class SpireWorld(World):
"""
A deck-building roguelike where you must craft a unique deck, encounter bizarre creatures, discover relics of
immense power, and Slay the Spire!
"""
options_dataclass = SpireOptions
options: SpireOptions
game = "Slay the Spire"
topology_present = False
web = SpireWeb()
required_client_version = (0, 3, 7)
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = location_table
def create_items(self):
# Fill out our pool with our items from item_pool, assuming 1 item if not present in item_pool
pool = []
for name, data in item_table.items():
if not data.event:
for amount in range(item_pool.get(name, 1)):
item = SpireItem(name, self.player)
pool.append(item)
self.multiworld.itempool += pool
# Pair up our event locations with our event items
for event, item in event_item_pairs.items():
event_item = SpireItem(item, self.player)
self.multiworld.get_location(event, self.player).place_locked_item(event_item)
def set_rules(self):
set_rules(self.multiworld, self.player)
def create_item(self, name: str) -> Item:
return SpireItem(name, self.player)
def create_regions(self):
create_regions(self.multiworld, self.player)
def fill_slot_data(self) -> dict:
slot_data = {
'seed': "".join(self.random.choice(string.ascii_letters) for i in range(16))
}
slot_data.update(self.options.as_dict("character", "ascension", "final_act", "downfall", "death_link"))
return slot_data
def get_filler_item_name(self) -> str:
return self.random.choice(["Card Draw", "Card Draw", "Card Draw", "Relic", "Relic"])
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
ret = Region(name, player, world)
if locations:
for location in locations:
loc_id = location_table.get(location, 0)
location = SpireLocation(player, location, loc_id, ret)
ret.locations.append(location)
if exits:
for exit in exits:
ret.exits.append(Entrance(player, exit, ret))
return ret
class SpireLocation(Location):
game: str = "Slay the Spire"
class SpireItem(Item):
game = "Slay the Spire"
def __init__(self, name, player: int = None):
item_data = item_table[name]
super(SpireItem, self).__init__(
name,
ItemClassification.progression if item_data.progression else ItemClassification.filler,
item_data.code, player
)

View File

@@ -1,35 +0,0 @@
# Slay the Spire (PC)
## Where is the options page?
The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
Every non-boss relic drop, every boss relic and rare card drop, and every other card draw is replaced with an
archipelago item. In heart runs, the blue key is also disconnected from the Archipelago item, so you can gather both.
## What items and locations get shuffled?
15 card packs, 10 relics, and 3 boss relics and rare card drops are shuffled into the item pool and can be found at any
location that would normally give you these items, except for card packs, which are found at every other normal enemy
encounter.
## Which items can be in another player's world?
Any of the items which can be shuffled may also be placed into another player's world. It is possible to choose to limit
certain items to your own world.
## When the player receives an item, what happens?
When the player receives an item, you will see the counter in the top right corner with the Archipelago symbol increment
by one. By clicking on this icon, it'll open a menu that lists all the items you received, but have not yet accepted.
You can take any relics and card packs sent to you and add them to your current run. It is advised that you do not open
this menu until you are outside an encounter or event to prevent the game from soft-locking.
## What happens if a player dies in a run?
When a player dies, they will be taken back to the main menu and will need to reconnect to start climbing the spire from
the beginning, but they will have access to all the items ever sent to them in the Archipelago menu in the top right.
Any items found in an earlier run will not be sent again if you encounter them in the same location.

View File

@@ -1,69 +0,0 @@
# Slay the Spire Setup Guide
## Required Software
For Steam-based installation, subscribe to the following mods:
- [ModTheSpire](https://steamcommunity.com/sharedfiles/filedetails/?id=1605060445)
- [BaseMod](https://steamcommunity.com/workshop/filedetails/?id=1605833019)
- [Archipelago Multiworld Randomizer](https://steamcommunity.com/sharedfiles/filedetails/?id=2596397288)
- (optional) [Downfall](https://steamcommunity.com/sharedfiles/filedetails/?id=1610056683)
- (required for downfall) [StSLib](https://steamcommunity.com/workshop/filedetails/?id=1609158507)
For GOG or Xbox PC Game Pass installation:
1. Download the official Steam Console Client [SteamCMD](https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip).
2. Unpack that .zip file into some folder and double-click on `steamcmd.exe`.
3. The client will now update itself. When it's ready type `login anonymous`. Now you are ready to download the actual
mods.
4. Run the following commands to download the required mod files:
- Mod the Spire: `workshop_download_item 646570 1605060445`
- BaseMod: `workshop_download_item 646570 1605833019`
- ArchipelagoMW: `workshop_download_item 646570 2596397288`
- (optional) Downfall: `workshop_download_item 646570 1610056683`
- (required for Downfall) StSLib: `workshop_download_item 646570 1609158507`
5. Open your Slay the Spire installation directory. By default on GOG this is `C:\GOG Games\Slay the Spire`, on PC Game
Pass this is `C:\XboxGames\Slay The Spire\Content`.
6. In the folder where you unzipped SteamCMD there will now be a `steamapps` folder. Copy ModTheSpire.jar from
`steamapps\workshop\content\646570\1605060445\ModTheSpire.jar` to your Slay The Spire installation directory.
7. Create a folder named `mods` inside the Slay the Spire installation directory. Each folder inside
`steamapps\workshop\content\646570` will have a single .jar file. Copy each of them except ModTheSpire.jar into the
`mods` folder you made.
8. Now open Notepad. Paste in the following text: `jre\bin\java.exe -jar ModTheSpire.jar`. Go to "File -> Save as" and
save it into your Slay the Spire installation directory with the name `"start.bat"`. Make sure to include the quotes
in the file name!
## Configuring your YAML file
### What is a YAML file and why do I need one?
Your YAML file contains a set of configuration options which provide the generator with information about how it should
generate your game. Each player of a multiworld will provide their own YAML file. This setup allows each player to enjoy
an experience customized for their taste, and different players in the same multiworld can all have different options.
### Where do I get a YAML file?
you can customize your options by visiting
the [Slay the Spire Options Page](/games/Slay%20the%20Spire/player-options).
### Connect to the MultiServer
For Steam-based installations, if you are subscribed to ModTheSpire, when you launch the game, you should have the
option to launch the game with mods.
For GOG or Xbox PC Game Pass intallations, launch the game by double-clicking the `start.bat` file you created earlier
which will give you the option to launch the game with mods.
On the mod loader screen, ensure you only have the following mods enabled and then start the game:
- BaseMod
- Archipelago Multiworld Randomizer
If playing with Downfall, also make sure the following are enabled:
- Downfall
- StSLib
Once you are in-game, you will be able to click the **Archipelago** menu option and enter the ip and port (separated by
a colon) in the hostname field and enter your player slot name in the Slot Name field. Then click connect, and now you
are ready to climb the spire!