mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
MC: 1.17 support (#120)
* MC: add death_link option * Minecraft: 1.17 advancements and logic support * Update Minecraft tracker to 1.17 * Minecraft: add tests for new advancements * removed jdk/forge download install out of iss and into MinecraftClient.py using flag --install * Add required_bosses option choices are none, ender_dragon, wither, both postgame advancements are set according to the required boss for completion * fix docstring for PostgameAdvancements * Minecraft: add starting_items List of dicts: item, amount, nbt * Update descriptions for AdvancementGoal and EggShardsRequired * Minecraft: fix tests for required_bosses attribute * Minecraft: updated logic for various dragon-related advancements Split the logic into can_respawn and can_kill dragon Free the End, Monsters Hunted, The End Again still require both respawn and kill, since the player needs to kill and be credited with the kill You Need a Mint and Is It a Plane now require only respawn, since the dragon need only be alive; if killed out of logic, it's ok The Next Generation only requires kill, since the egg spawns regardless of whether the player was credited with the kill or not * Minecraft client: ignore prereleases unless --prerelease flag is on * explicitly state all defaults change structure shuffle and structure compass defaults to true update install tutorial to point to player-settings page, as well as removing instructions for manual install * Minecraft client: add Minecraft version check Adds a minecraft_version field in the apmc, and downloads only mods which contain that version in the name of the .jar file. This ensures that the client remains compatible even if new mods are released for later versions, since they won't download a mod for a later version than the apmc says. Co-authored-by: Kono Tyran <Kono.Tyran@gmail.com>
This commit is contained in:
@@ -56,10 +56,12 @@ item_table = {
|
||||
"Structure Compass (End City)": ItemData(45041, True),
|
||||
"Shulker Box": ItemData(45042, False),
|
||||
"Dragon Egg Shard": ItemData(45043, True),
|
||||
"Spyglass": ItemData(45044, True),
|
||||
"Bee Trap (Minecraft)": ItemData(45100, False),
|
||||
|
||||
"Blaze Rods": ItemData(None, True),
|
||||
"Victory": ItemData(None, True)
|
||||
"Defeat Ender Dragon": ItemData(None, True),
|
||||
"Defeat Wither": ItemData(None, True),
|
||||
}
|
||||
|
||||
# 33 required items
|
||||
@@ -87,6 +89,7 @@ required_items = {
|
||||
"Infinity Book": 1,
|
||||
"3 Ender Pearls": 4,
|
||||
"Saddle": 1,
|
||||
"Spyglass": 1,
|
||||
}
|
||||
|
||||
junk_weights = {
|
||||
|
@@ -108,9 +108,21 @@ advancement_table = {
|
||||
"Overkill": AdvData(42089, 'Nether Fortress'),
|
||||
"Librarian": AdvData(42090, 'Overworld'),
|
||||
"Overpowered": AdvData(42091, 'Bastion Remnant'),
|
||||
"Wax On": AdvData(42092, 'Overworld'),
|
||||
"Wax Off": AdvData(42093, 'Overworld'),
|
||||
"The Cutest Predator": AdvData(42094, 'Overworld'),
|
||||
"The Healing Power of Friendship": AdvData(42095, 'Overworld'),
|
||||
"Is It a Bird?": AdvData(42096, 'Overworld'),
|
||||
"Is It a Balloon?": AdvData(42097, 'The Nether'),
|
||||
"Is It a Plane?": AdvData(42098, 'The End'),
|
||||
"Surge Protector": AdvData(42099, 'Overworld'),
|
||||
"Light as a Rabbit": AdvData(42100, 'Overworld'),
|
||||
"Glow and Behold!": AdvData(42101, 'Overworld'),
|
||||
"Whatever Floats Your Goat!": AdvData(42102, 'Overworld'),
|
||||
|
||||
"Blaze Spawner": AdvData(None, 'Nether Fortress'),
|
||||
"Ender Dragon": AdvData(None, 'The End')
|
||||
"Ender Dragon": AdvData(None, 'The End'),
|
||||
"Wither": AdvData(None, 'Nether Fortress'),
|
||||
}
|
||||
|
||||
exclusion_table = {
|
||||
@@ -126,23 +138,39 @@ exclusion_table = {
|
||||
"Uneasy Alliance",
|
||||
"Cover Me in Debris",
|
||||
"A Complete Catalogue",
|
||||
"Surge Protector",
|
||||
"Light as a Rabbit", # will be normal in 1.18
|
||||
},
|
||||
"insane": {
|
||||
"unreasonable": {
|
||||
"How Did We Get Here?",
|
||||
"Adventuring Time",
|
||||
},
|
||||
"postgame": {
|
||||
"Free the End",
|
||||
"The Next Generation",
|
||||
"The End... Again...",
|
||||
"You Need a Mint",
|
||||
"Monsters Hunted",
|
||||
}
|
||||
|
||||
def get_postgame_advancements(required_bosses):
|
||||
|
||||
postgame_advancements = {
|
||||
"ender_dragon": {
|
||||
"Free the End",
|
||||
"The Next Generation",
|
||||
"The End... Again...",
|
||||
"You Need a Mint",
|
||||
"Monsters Hunted",
|
||||
"Is It a Plane?",
|
||||
},
|
||||
"wither": {
|
||||
"Withering Heights",
|
||||
"Bring Home the Beacon",
|
||||
"Beaconator",
|
||||
"A Furious Cocktail",
|
||||
"How Did We Get Here?",
|
||||
"Monsters Hunted",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
events_table = {
|
||||
"Ender Dragon": "Victory"
|
||||
}
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {loc_data.id: loc_name for loc_name, loc_data in advancement_table.items() if
|
||||
loc_data.id}
|
||||
advancements = set()
|
||||
if required_bosses in {"ender_dragon", "both"}:
|
||||
advancements.update(postgame_advancements["ender_dragon"])
|
||||
if required_bosses in {"wither", "both"}:
|
||||
advancements.update(postgame_advancements["wither"])
|
||||
return advancements
|
||||
|
@@ -1,37 +1,51 @@
|
||||
import typing
|
||||
from Options import Choice, Option, Toggle, Range
|
||||
from Options import Choice, Option, Toggle, Range, OptionList, DeathLink
|
||||
|
||||
|
||||
class AdvancementGoal(Range):
|
||||
"""Number of advancements required to spawn the Ender Dragon."""
|
||||
"""Number of advancements required to spawn bosses."""
|
||||
displayname = "Advancement Goal"
|
||||
range_start = 0
|
||||
range_end = 87
|
||||
default = 50
|
||||
range_end = 92
|
||||
default = 40
|
||||
|
||||
|
||||
class EggShardsRequired(Range):
|
||||
"""Number of dragon egg shards to collect before the Ender Dragon will spawn."""
|
||||
"""Number of dragon egg shards to collect to spawn bosses."""
|
||||
displayname = "Egg Shards Required"
|
||||
range_start = 0
|
||||
range_end = 30
|
||||
range_end = 40
|
||||
default = 0
|
||||
|
||||
|
||||
class EggShardsAvailable(Range):
|
||||
"""Number of dragon egg shards available to collect."""
|
||||
displayname = "Egg Shards Available"
|
||||
range_start = 0
|
||||
range_end = 30
|
||||
range_end = 40
|
||||
default = 0
|
||||
|
||||
|
||||
class BossGoal(Choice):
|
||||
"""Bosses which must be defeated to finish the game."""
|
||||
displayname = "Required Bosses"
|
||||
option_none = 0
|
||||
option_ender_dragon = 1
|
||||
option_wither = 2
|
||||
option_both = 3
|
||||
default = 1
|
||||
|
||||
|
||||
class ShuffleStructures(Toggle):
|
||||
"""Enables shuffling of villages, outposts, fortresses, bastions, and end cities."""
|
||||
displayname = "Shuffle Structures"
|
||||
default = 1
|
||||
|
||||
|
||||
class StructureCompasses(Toggle):
|
||||
"""Adds structure compasses to the item pool, which point to the nearest indicated structure."""
|
||||
displayname = "Structure Compasses"
|
||||
default = 1
|
||||
|
||||
|
||||
class BeeTraps(Range):
|
||||
@@ -39,6 +53,7 @@ class BeeTraps(Range):
|
||||
displayname = "Bee Trap Percentage"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 0
|
||||
|
||||
|
||||
class CombatDifficulty(Choice):
|
||||
@@ -53,33 +68,46 @@ class CombatDifficulty(Choice):
|
||||
class HardAdvancements(Toggle):
|
||||
"""Enables certain RNG-reliant or tedious advancements."""
|
||||
displayname = "Include Hard Advancements"
|
||||
default = 0
|
||||
|
||||
|
||||
class InsaneAdvancements(Toggle):
|
||||
class UnreasonableAdvancements(Toggle):
|
||||
"""Enables the extremely difficult advancements "How Did We Get Here?" and "Adventuring Time.\""""
|
||||
displayname = "Include Insane Advancements"
|
||||
displayname = "Include Unreasonable Advancements"
|
||||
default = 0
|
||||
|
||||
|
||||
class PostgameAdvancements(Toggle):
|
||||
"""Enables advancements that require spawning and defeating the Ender Dragon."""
|
||||
"""Enables advancements that require spawning and defeating the required bosses."""
|
||||
displayname = "Include Postgame Advancements"
|
||||
default = 0
|
||||
|
||||
|
||||
class SendDefeatedMobs(Toggle):
|
||||
"""Send killed mobs to other Minecraft worlds which have this option enabled."""
|
||||
displayname = "Send Defeated Mobs"
|
||||
default = 0
|
||||
|
||||
|
||||
class StartingItems(OptionList):
|
||||
"""Start with these items. Each entry should be of this format: {item: "item_name", amount: #, nbt: "nbt_string"}"""
|
||||
displayname = "Starting Items"
|
||||
default = 0
|
||||
|
||||
|
||||
minecraft_options: typing.Dict[str, type(Option)] = {
|
||||
"advancement_goal": AdvancementGoal,
|
||||
"egg_shards_required": EggShardsRequired,
|
||||
"egg_shards_available": EggShardsAvailable,
|
||||
"shuffle_structures": ShuffleStructures,
|
||||
"structure_compasses": StructureCompasses,
|
||||
"bee_traps": BeeTraps,
|
||||
"combat_difficulty": CombatDifficulty,
|
||||
"include_hard_advancements": HardAdvancements,
|
||||
"include_insane_advancements": InsaneAdvancements,
|
||||
"include_postgame_advancements": PostgameAdvancements,
|
||||
"send_defeated_mobs": SendDefeatedMobs,
|
||||
"advancement_goal": AdvancementGoal,
|
||||
"egg_shards_required": EggShardsRequired,
|
||||
"egg_shards_available": EggShardsAvailable,
|
||||
"required_bosses": BossGoal,
|
||||
"shuffle_structures": ShuffleStructures,
|
||||
"structure_compasses": StructureCompasses,
|
||||
"bee_traps": BeeTraps,
|
||||
"combat_difficulty": CombatDifficulty,
|
||||
"include_hard_advancements": HardAdvancements,
|
||||
"include_unreasonable_advancements": UnreasonableAdvancements,
|
||||
"include_postgame_advancements": PostgameAdvancements,
|
||||
"send_defeated_mobs": SendDefeatedMobs,
|
||||
"starting_items": StartingItems,
|
||||
"death_link": DeathLink,
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
from ..generic.Rules import set_rule
|
||||
from .Locations import exclusion_table, events_table
|
||||
from ..generic.Rules import set_rule, add_rule
|
||||
from .Locations import exclusion_table, get_postgame_advancements
|
||||
from BaseClasses import MultiWorld
|
||||
from ..AutoWorld import LogicMixin
|
||||
|
||||
@@ -9,6 +9,9 @@ class MinecraftLogic(LogicMixin):
|
||||
def _mc_has_iron_ingots(self, player: int):
|
||||
return self.has('Progressive Tools', player) and self.has('Progressive Resource Crafting', player)
|
||||
|
||||
def _mc_has_copper_ingots(self, player: int):
|
||||
return self.has('Progressive Tools', player) and self.has('Progressive Resource Crafting', player)
|
||||
|
||||
def _mc_has_gold_ingots(self, player: int):
|
||||
return self.has('Progressive Resource Crafting', player) and (self.has('Progressive Tools', player, 2) or self.can_reach('The Nether', 'Region', player))
|
||||
|
||||
@@ -21,6 +24,9 @@ class MinecraftLogic(LogicMixin):
|
||||
def _mc_has_bottle(self, player: int):
|
||||
return self.has('Bottles', player) and self.has('Progressive Resource Crafting', player)
|
||||
|
||||
def _mc_has_spyglass(self, player: int):
|
||||
return self._mc_has_copper_ingots(player) and self.has('Spyglass', player) and self._mc_can_adventure(player)
|
||||
|
||||
def _mc_can_enchant(self, player: int):
|
||||
return self.has('Enchanting', player) and self._mc_has_diamond_pickaxe(player) # mine obsidian and lapis
|
||||
|
||||
@@ -81,48 +87,32 @@ class MinecraftLogic(LogicMixin):
|
||||
return self._mc_fortress_loot(player) and (normal_kill or self.can_reach('The Nether', 'Region', player) or self.can_reach('The End', 'Region', player))
|
||||
return self._mc_fortress_loot(player) and normal_kill
|
||||
|
||||
def _mc_can_respawn_ender_dragon(self, player: int):
|
||||
return self.can_reach('The Nether', 'Region', player) and self.can_reach('The End', 'Region', player) and \
|
||||
self.has('Progressive Resource Crafting', player) # smelt sand into glass
|
||||
|
||||
def _mc_can_kill_ender_dragon(self, player: int):
|
||||
# Since it is possible to kill the dragon without getting any of the advancements related to it, we need to require that it can be respawned.
|
||||
respawn_dragon = self.can_reach('The Nether', 'Region', player) and self.has('Progressive Resource Crafting', player)
|
||||
if self._mc_combat_difficulty(player) == 'easy':
|
||||
return respawn_dragon and self.has("Progressive Weapons", player, 3) and self.has("Progressive Armor", player, 2) and \
|
||||
return self.has("Progressive Weapons", player, 3) and self.has("Progressive Armor", player, 2) and \
|
||||
self.has('Archery', player) and self._mc_can_brew_potions(player) and self._mc_can_enchant(player)
|
||||
if self._mc_combat_difficulty(player) == 'hard':
|
||||
return respawn_dragon and ((self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player)) or \
|
||||
(self.has('Progressive Weapons', player, 1) and self.has('Bed', player)))
|
||||
return respawn_dragon and self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and self.has('Archery', player)
|
||||
return (self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player)) or \
|
||||
(self.has('Progressive Weapons', player, 1) and self.has('Bed', player))
|
||||
return self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and self.has('Archery', player)
|
||||
|
||||
def _mc_has_structure_compass(self, entrance_name: str, player: int):
|
||||
if not self.world.structure_compasses[player]:
|
||||
return True
|
||||
return self.has(f"Structure Compass ({self.world.get_entrance(entrance_name, player).connected_region.name})", player)
|
||||
|
||||
|
||||
def set_rules(world: MultiWorld, player: int):
|
||||
def reachable_locations(state):
|
||||
postgame_advancements = exclusion_table['postgame'].copy()
|
||||
for event in events_table.keys():
|
||||
postgame_advancements.add(event)
|
||||
return [location for location in world.get_locations() if
|
||||
location.player == player and
|
||||
location.name not in postgame_advancements and
|
||||
location.can_reach(state)]
|
||||
# Sets rules on entrances and advancements that are always applied
|
||||
def set_advancement_rules(world: MultiWorld, player: int):
|
||||
|
||||
# Retrieves the appropriate structure compass for the given entrance
|
||||
def get_struct_compass(entrance_name):
|
||||
struct = world.get_entrance(entrance_name, player).connected_region.name
|
||||
return f"Structure Compass ({struct})"
|
||||
|
||||
# 92 total advancements. Goal is to complete X advancements and then Free the End.
|
||||
# There are 5 advancements which cannot be included for dragon spawning (4 postgame, Free the End)
|
||||
# Hence the true maximum is (92 - 5) = 87
|
||||
goal = world.advancement_goal[player]
|
||||
egg_shards = min(world.egg_shards_required[player], world.egg_shards_available[player])
|
||||
can_complete = lambda state: len(reachable_locations(state)) >= goal and state.has("Dragon Egg Shard", player, egg_shards) and state.can_reach('The End', 'Region', player) and state._mc_can_kill_ender_dragon(player)
|
||||
|
||||
if world.logic[player] != 'nologic':
|
||||
world.completion_condition[player] = lambda state: state.has('Victory', player)
|
||||
|
||||
set_rule(world.get_entrance("Nether Portal", player), lambda state: state.has('Flint and Steel', player) and
|
||||
(state.has('Bucket', player) or state.has('Progressive Tools', player, 3)) and
|
||||
state._mc_has_iron_ingots(player))
|
||||
@@ -133,7 +123,8 @@ def set_rules(world: MultiWorld, player: int):
|
||||
set_rule(world.get_entrance("Nether Structure 2", player), lambda state: state._mc_can_adventure(player) and state._mc_has_structure_compass("Nether Structure 2", player))
|
||||
set_rule(world.get_entrance("The End Structure", player), lambda state: state._mc_can_adventure(player) and state._mc_has_structure_compass("The End Structure", player))
|
||||
|
||||
set_rule(world.get_location("Ender Dragon", player), lambda state: can_complete(state))
|
||||
set_rule(world.get_location("Ender Dragon", player), lambda state: state._mc_can_kill_ender_dragon(player))
|
||||
set_rule(world.get_location("Wither", player), lambda state: state._mc_can_kill_wither(player))
|
||||
set_rule(world.get_location("Blaze Spawner", player), lambda state: state._mc_fortress_loot(player))
|
||||
|
||||
set_rule(world.get_location("Who is Cutting Onions?", player), lambda state: state._mc_can_piglin_trade(player))
|
||||
@@ -142,7 +133,7 @@ def set_rules(world: MultiWorld, player: int):
|
||||
set_rule(world.get_location("Very Very Frightening", player), lambda state: state.has("Channeling Book", player) and state._mc_can_use_anvil(player) and state._mc_can_enchant(player) and \
|
||||
((world.get_region('Village', player).entrances[0].parent_region.name != 'The End' and state.can_reach('Village', 'Region', player)) or state.can_reach('Zombie Doctor', 'Location', player))) # need villager into the overworld for lightning strike
|
||||
set_rule(world.get_location("Hot Stuff", player), lambda state: state.has("Bucket", player) and state._mc_has_iron_ingots(player))
|
||||
set_rule(world.get_location("Free the End", player), lambda state: can_complete(state))
|
||||
set_rule(world.get_location("Free the End", player), lambda state: state._mc_can_respawn_ender_dragon(player) and state._mc_can_kill_ender_dragon(player))
|
||||
set_rule(world.get_location("A Furious Cocktail", player), lambda state: state._mc_can_brew_potions(player) and
|
||||
state.has("Fishing Rod", player) and # Water Breathing
|
||||
state.can_reach('The Nether', 'Region', player) and # Regeneration, Fire Resistance, gold nuggets
|
||||
@@ -154,7 +145,7 @@ def set_rules(world: MultiWorld, player: int):
|
||||
set_rule(world.get_location("Not Today, Thank You", player), lambda state: state.has("Shield", player) and state._mc_has_iron_ingots(player))
|
||||
set_rule(world.get_location("Isn't It Iron Pick", player), lambda state: state.has("Progressive Tools", player, 2) and state._mc_has_iron_ingots(player))
|
||||
set_rule(world.get_location("Local Brewery", player), lambda state: state._mc_can_brew_potions(player))
|
||||
set_rule(world.get_location("The Next Generation", player), lambda state: can_complete(state))
|
||||
set_rule(world.get_location("The Next Generation", player), lambda state: state._mc_can_kill_ender_dragon(player))
|
||||
set_rule(world.get_location("Fishy Business", player), lambda state: state.has("Fishing Rod", player))
|
||||
set_rule(world.get_location("Hot Tourist Destinations", player), lambda state: True)
|
||||
set_rule(world.get_location("This Boat Has Legs", player), lambda state: (state._mc_fortress_loot(player) or state._mc_complete_raid(player)) and
|
||||
@@ -188,7 +179,7 @@ def set_rules(world: MultiWorld, player: int):
|
||||
set_rule(world.get_location("Total Beelocation", player), lambda state: state.has("Silk Touch Book", player) and state._mc_can_use_anvil(player) and state._mc_can_enchant(player))
|
||||
set_rule(world.get_location("Arbalistic", player), lambda state: state._mc_craft_crossbow(player) and state.has("Piercing IV Book", player) and
|
||||
state._mc_can_use_anvil(player) and state._mc_can_enchant(player))
|
||||
set_rule(world.get_location("The End... Again...", player), lambda state: can_complete(state))
|
||||
set_rule(world.get_location("The End... Again...", player), lambda state: state._mc_can_respawn_ender_dragon(player) and state._mc_can_kill_ender_dragon(player))
|
||||
set_rule(world.get_location("Acquire Hardware", player), lambda state: state._mc_has_iron_ingots(player))
|
||||
set_rule(world.get_location("Not Quite \"Nine\" Lives", player), lambda state: state._mc_can_piglin_trade(player) and state.has("Progressive Resource Crafting", player, 2))
|
||||
set_rule(world.get_location("Cover Me With Diamonds", player), lambda state: state.has("Progressive Armor", player, 2) and state.can_reach("Diamonds!", "Location", player))
|
||||
@@ -196,9 +187,10 @@ def set_rules(world: MultiWorld, player: int):
|
||||
set_rule(world.get_location("Hired Help", player), lambda state: state.has("Progressive Resource Crafting", player, 2) and state._mc_has_iron_ingots(player))
|
||||
set_rule(world.get_location("Return to Sender", player), lambda state: True)
|
||||
set_rule(world.get_location("Sweet Dreams", player), lambda state: state.has("Bed", player) or state.can_reach('Village', 'Region', player))
|
||||
set_rule(world.get_location("You Need a Mint", player), lambda state: can_complete(state) and state._mc_has_bottle(player))
|
||||
set_rule(world.get_location("You Need a Mint", player), lambda state: state._mc_can_respawn_ender_dragon(player) and state._mc_has_bottle(player))
|
||||
set_rule(world.get_location("Adventure", player), lambda state: True)
|
||||
set_rule(world.get_location("Monsters Hunted", player), lambda state: can_complete(state) and state._mc_can_kill_wither(player) and state.has("Fishing Rod", player)) # pufferfish for Water Breathing
|
||||
set_rule(world.get_location("Monsters Hunted", player), lambda state: state._mc_can_respawn_ender_dragon(player) and state._mc_can_kill_ender_dragon(player) and
|
||||
state._mc_can_kill_wither(player) and state.has("Fishing Rod", player)) # pufferfish for Water Breathing
|
||||
set_rule(world.get_location("Enchanter", player), lambda state: state._mc_can_enchant(player))
|
||||
set_rule(world.get_location("Voluntary Exile", player), lambda state: state._mc_basic_combat(player))
|
||||
set_rule(world.get_location("Eye Spy", player), lambda state: state._mc_enter_stronghold(player))
|
||||
@@ -224,7 +216,7 @@ def set_rules(world: MultiWorld, player: int):
|
||||
set_rule(world.get_location("Uneasy Alliance", player), lambda state: state._mc_has_diamond_pickaxe(player) and state.has('Fishing Rod', player))
|
||||
set_rule(world.get_location("Diamonds!", player), lambda state: state.has("Progressive Tools", player, 2) and state._mc_has_iron_ingots(player))
|
||||
set_rule(world.get_location("A Terrible Fortress", player), lambda state: True) # since you don't have to fight anything
|
||||
set_rule(world.get_location("A Throwaway Joke", player), lambda state: True) # kill drowned
|
||||
set_rule(world.get_location("A Throwaway Joke", player), lambda state: state._mc_can_adventure(player)) # kill drowned
|
||||
set_rule(world.get_location("Minecraft", player), lambda state: True)
|
||||
set_rule(world.get_location("Sticky Situation", player), lambda state: state.has("Campfire", player) and state._mc_has_bottle(player))
|
||||
set_rule(world.get_location("Ol' Betsy", player), lambda state: state._mc_craft_crossbow(player))
|
||||
@@ -249,3 +241,42 @@ def set_rules(world: MultiWorld, player: int):
|
||||
set_rule(world.get_location("Librarian", player), lambda state: state.has("Enchanting", player))
|
||||
set_rule(world.get_location("Overpowered", player), lambda state: state._mc_has_iron_ingots(player) and
|
||||
state.has('Progressive Tools', player, 2) and state._mc_basic_combat(player)) # mine gold blocks w/ iron pick
|
||||
set_rule(world.get_location("Wax On", player), lambda state: state._mc_has_copper_ingots(player) and state.has('Campfire', player) and
|
||||
state.has('Progressive Resource Crafting', player, 2))
|
||||
set_rule(world.get_location("Wax Off", player), lambda state: state._mc_has_copper_ingots(player) and state.has('Campfire', player) and
|
||||
state.has('Progressive Resource Crafting', player, 2))
|
||||
set_rule(world.get_location("The Cutest Predator", player), lambda state: state._mc_has_iron_ingots(player) and state.has('Bucket', player))
|
||||
set_rule(world.get_location("The Healing Power of Friendship", player), lambda state: state._mc_has_iron_ingots(player) and state.has('Bucket', player))
|
||||
set_rule(world.get_location("Is It a Bird?", player), lambda state: state._mc_has_spyglass(player) and state._mc_can_adventure(player))
|
||||
set_rule(world.get_location("Is It a Balloon?", player), lambda state: state._mc_has_spyglass(player))
|
||||
set_rule(world.get_location("Is It a Plane?", player), lambda state: state._mc_has_spyglass(player) and state._mc_can_respawn_ender_dragon(player))
|
||||
set_rule(world.get_location("Surge Protector", player), lambda state: state.has("Channeling Book", player) and state._mc_can_use_anvil(player) and state._mc_can_enchant(player) and \
|
||||
((world.get_region('Village', player).entrances[0].parent_region.name != 'The End' and state.can_reach('Village', 'Region', player)) or state.can_reach('Zombie Doctor', 'Location', player)))
|
||||
set_rule(world.get_location("Light as a Rabbit", player), lambda state: state._mc_can_adventure(player) and state._mc_has_iron_ingots(player) and state.has('Bucket', player))
|
||||
set_rule(world.get_location("Glow and Behold!", player), lambda state: state._mc_can_adventure(player))
|
||||
set_rule(world.get_location("Whatever Floats Your Goat!", player), lambda state: state._mc_can_adventure(player))
|
||||
|
||||
# Sets rules on completion condition and postgame advancements
|
||||
def set_completion_rules(world: MultiWorld, player: int):
|
||||
def reachable_locations(state):
|
||||
postgame_advancements = get_postgame_advancements(world.required_bosses[player].current_key)
|
||||
return [location for location in world.get_locations() if
|
||||
location.player == player and
|
||||
location.name not in postgame_advancements and
|
||||
location.address != None and
|
||||
location.can_reach(state)]
|
||||
|
||||
def defeated_required_bosses(state):
|
||||
return (world.required_bosses[player].current_key not in {"ender_dragon", "both"} or state.has("Defeat Ender Dragon", player)) and \
|
||||
(world.required_bosses[player].current_key not in {"wither", "both"} or state.has("Defeat Wither", player))
|
||||
|
||||
# 103 total advancements. Goal is to complete X advancements and then defeat the dragon.
|
||||
# There are 11 possible postgame advancements; 5 for dragon, 5 for wither, 1 shared between them
|
||||
# Hence the max for completion is 92
|
||||
egg_shards = min(world.egg_shards_required[player], world.egg_shards_available[player])
|
||||
completion_requirements = lambda state: len(reachable_locations(state)) >= world.advancement_goal[player] and \
|
||||
state.has("Dragon Egg Shard", player, egg_shards)
|
||||
world.completion_condition[player] = lambda state: completion_requirements(state) and defeated_required_bosses(state)
|
||||
# Set rules on postgame advancements
|
||||
for adv_name in get_postgame_advancements(world.required_bosses[player].current_key):
|
||||
add_rule(world.get_location(adv_name, player), completion_requirements)
|
||||
|
@@ -4,16 +4,17 @@ from base64 import b64encode, b64decode
|
||||
from math import ceil
|
||||
|
||||
from .Items import MinecraftItem, item_table, required_items, junk_weights
|
||||
from .Locations import MinecraftAdvancement, advancement_table, exclusion_table, events_table
|
||||
from .Locations import MinecraftAdvancement, advancement_table, exclusion_table, get_postgame_advancements
|
||||
from .Regions import mc_regions, link_minecraft_structures, default_connections
|
||||
from .Rules import set_rules
|
||||
from .Rules import set_advancement_rules, set_completion_rules
|
||||
from worlds.generic.Rules import exclusion_rules
|
||||
|
||||
from BaseClasses import Region, Entrance, Item
|
||||
from .Options import minecraft_options
|
||||
from ..AutoWorld import World
|
||||
|
||||
client_version = 6
|
||||
client_version = 7
|
||||
minecraft_version = "1.17.1"
|
||||
|
||||
class MinecraftWorld(World):
|
||||
"""
|
||||
@@ -29,7 +30,7 @@ class MinecraftWorld(World):
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = {name: data.id for name, data in advancement_table.items()}
|
||||
|
||||
data_version = 3
|
||||
data_version = 4
|
||||
|
||||
def _get_mc_data(self):
|
||||
exits = [connection[0] for connection in default_connections]
|
||||
@@ -39,12 +40,16 @@ class MinecraftWorld(World):
|
||||
'player_name': self.world.get_player_name(self.player),
|
||||
'player_id': self.player,
|
||||
'client_version': client_version,
|
||||
'minecraft_version': minecraft_version,
|
||||
'structures': {exit: self.world.get_entrance(exit, self.player).connected_region.name for exit in exits},
|
||||
'advancement_goal': self.world.advancement_goal[self.player],
|
||||
'egg_shards_required': min(self.world.egg_shards_required[self.player], self.world.egg_shards_available[self.player]),
|
||||
'egg_shards_available': self.world.egg_shards_available[self.player],
|
||||
'required_bosses': self.world.required_bosses[self.player].current_key,
|
||||
'MC35': bool(self.world.send_defeated_mobs[self.player]),
|
||||
'race': self.world.is_race
|
||||
'death_link': bool(self.world.death_link[self.player]),
|
||||
'starting_items': str(self.world.starting_items[self.player].value),
|
||||
'race': self.world.is_race,
|
||||
}
|
||||
|
||||
def generate_basic(self):
|
||||
@@ -72,20 +77,24 @@ class MinecraftWorld(World):
|
||||
|
||||
# Choose locations to automatically exclude based on settings
|
||||
exclusion_pool = set()
|
||||
exclusion_types = ['hard', 'insane', 'postgame']
|
||||
exclusion_types = ['hard', 'unreasonable']
|
||||
for key in exclusion_types:
|
||||
if not getattr(self.world, f"include_{key}_advancements")[self.player]:
|
||||
exclusion_pool.update(exclusion_table[key])
|
||||
# For postgame advancements, check with the boss goal
|
||||
exclusion_pool.update(get_postgame_advancements(self.world.required_bosses[self.player].current_key))
|
||||
exclusion_rules(self.world, self.player, exclusion_pool)
|
||||
|
||||
# Prefill event locations with their events
|
||||
self.world.get_location("Blaze Spawner", self.player).place_locked_item(self.create_item("Blaze Rods"))
|
||||
self.world.get_location("Ender Dragon", self.player).place_locked_item(self.create_item("Victory"))
|
||||
self.world.get_location("Ender Dragon", self.player).place_locked_item(self.create_item("Defeat Ender Dragon"))
|
||||
self.world.get_location("Wither", self.player).place_locked_item(self.create_item("Defeat Wither"))
|
||||
|
||||
self.world.itempool += itempool
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
set_advancement_rules(self.world, self.player)
|
||||
set_completion_rules(self.world, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
def MCRegion(region_name: str, exits=[]):
|
||||
@@ -110,7 +119,8 @@ class MinecraftWorld(World):
|
||||
slot_data = self._get_mc_data()
|
||||
for option_name in minecraft_options:
|
||||
option = getattr(self.world, option_name)[self.player]
|
||||
slot_data[option_name] = int(option.value)
|
||||
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}:
|
||||
slot_data[option_name] = int(option.value)
|
||||
return slot_data
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
|
Reference in New Issue
Block a user