Files
Archipelago/worlds/minecraft/__init__.py
alwaysintreble 8742aadc72 Player tracker (#710)
* Player tracker: implement a stylized tracker (#447)

* Move generic tracker to a WebWorld method

* render both a generic tracker at generic_tracker and the specific tracker at /tracker

* create a base template for generic specific tracker and instantiate some information before callng it

* some baseline for the playerTracker.html. update information fed from tracker.py

* playerTracker: finish implementing icons and generic locations rendering. hide any unacquired progression items when not using icons. Place the name of the progression item under its icon.

* player tracker: starting work on regions table

* player tracker: change method calls

* Move generic tracker to a WebWorld method

* render both a generic tracker at generic_tracker and the specific tracker at /tracker

* create a base template for generic specific tracker and instantiate some information before callng it

* some baseline for the playerTracker.html. update information fed from tracker.py

* playerTracker: finish implementing icons and generic locations rendering. hide any unacquired progression items when not using icons. Place the name of the progression item under its icon.

* player tracker: starting work on regions table

* player tracker: change method calls

* Move generic tracker to a WebWorld method

* create a base template for generic specific tracker and instantiate some information before callng it

* some baseline for the playerTracker.html. update information fed from tracker.py

* playerTracker: finish implementing icons and generic locations rendering. hide any unacquired progression items when not using icons. Place the name of the progression item under its icon.

* player tracker: starting work on regions table

* player tracker: switch item, icon and location tables to flex views. Some styling based on theme

* Player Tracker: Finish building html template for all blocks. Set groundwork for theme styling

* Player Tracker: Implement tracker class. Document tracker usage.

* Player Tracker: Add button to switch between trackers. Some styling for styled tracker.

* Player Tracker: reword some text. Attempt to fix page refreshing.

* Player Tracker: reremove the TODOs that got merged back in accidentally.

* player tracker: move render_template import to webworld so it isn't required outside of webhost

* Player Tracker: code cleanup, typing. Add inventory with names to PlayerTracker class in case custom trackers want to use it to change their prog_items attribute.

* Player Tracker: delete a line I forgot about. Add typing to theme.

* Player Tracker: Generate checks_done automatically so worlds don't have to do it

* Player Tracker: Add typing to PlayerTracker class in webworld method. Update documentation

* Player Tracker: code cleanup

* Player Tracker: Sort of implement fetch (works but could be better). Make playerTracker.html more readable.

* specific trackers: significant html cleanup. DOM Endpoint auto updating page every 30 seconds

* Changes by Kono

* specific trackers: cache and only load the data once every minute

* specific tracker: allow for one icon placement to be used for multiple items.

* Player tracker fixes/updates (#635)

* Move generic tracker to a WebWorld method

* render both a generic tracker at generic_tracker and the specific tracker at /tracker

* create a base template for generic specific tracker and instantiate some information before callng it

* some baseline for the playerTracker.html. update information fed from tracker.py

* playerTracker: finish implementing icons and generic locations rendering. hide any unacquired progression items when not using icons. Place the name of the progression item under its icon.

* player tracker: starting work on regions table

* player tracker: change method calls

* Move generic tracker to a WebWorld method

* render both a generic tracker at generic_tracker and the specific tracker at /tracker

* create a base template for generic specific tracker and instantiate some information before callng it

* some baseline for the playerTracker.html. update information fed from tracker.py

* playerTracker: finish implementing icons and generic locations rendering. hide any unacquired progression items when not using icons. Place the name of the progression item under its icon.

* player tracker: starting work on regions table

* player tracker: change method calls

* Move generic tracker to a WebWorld method

* create a base template for generic specific tracker and instantiate some information before callng it

* some baseline for the playerTracker.html. update information fed from tracker.py

* playerTracker: finish implementing icons and generic locations rendering. hide any unacquired progression items when not using icons. Place the name of the progression item under its icon.

* player tracker: starting work on regions table

* player tracker: switch item, icon and location tables to flex views. Some styling based on theme

* Player Tracker: Finish building html template for all blocks. Set groundwork for theme styling

* Player Tracker: Implement tracker class. Document tracker usage.

* Player Tracker: Add button to switch between trackers. Some styling for styled tracker.

* Player Tracker: reword some text. Attempt to fix page refreshing.

* Player Tracker: reremove the TODOs that got merged back in accidentally.

* player tracker: move render_template import to webworld so it isn't required outside of webhost

* Player Tracker: code cleanup, typing. Add inventory with names to PlayerTracker class in case custom trackers want to use it to change their prog_items attribute.

* Player Tracker: delete a line I forgot about. Add typing to theme.

* Player Tracker: Generate checks_done automatically so worlds don't have to do it

* Player Tracker: Add typing to PlayerTracker class in webworld method. Update documentation

* Player Tracker: code cleanup

* Player Tracker: Sort of implement fetch (works but could be better). Make playerTracker.html more readable.

* specific trackers: significant html cleanup. DOM Endpoint auto updating page every 30 seconds

* Changes by Kono

* specific trackers: cache and only load the data once every minute

* specific tracker: allow for one icon placement to be used for multiple items.

* lttp: move tracker to new format. will need more modification to generic solution to handle region keys tracking. likely a new html template that inherits the current

* lttp: fix broken icons rendering, add in progressive mail that i forgor. reorder some icons

* tracker: fix non edited trackers being broken from changes.

* tracker: move theme application before modify method so trackers can use a different theme than the world if desired.

* tracker: starting work on key tracking.

* tracker: styling and cleanup by Farrak

* tracker: styling and cleanup by Farrak

* tracker: styling and cleanup of playerTracker.html

* Revert playerTracker.html

* trackers: rename some files for clarity. move trackers into their own subdirectory

* small tracker.py cleanup

* move minecraft tracker to new system

* add item link attributing from upstream

* change getPlayerTracker to get_player_tracker. refactor broken linkings

* refactor styling files to trackers folders

* fix broken image in minecraft tracker. move oot tracker to new system

* clean up my oot nightmare

* rename lttpKeysTracker to zeldaKeysTracker. Move oot to keys tracker

* implement zeldaKeysTracker.js. fix table locations hiding/showing
2022-06-25 17:01:42 -04:00

258 lines
14 KiB
Python

import os
import json
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, get_postgame_advancements
from .Regions import mc_regions, link_minecraft_structures, default_connections
from .Rules import set_advancement_rules, set_completion_rules
from worlds.generic.Rules import exclusion_rules
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification
from .Options import minecraft_options
from ..AutoWorld import World, WebWorld
client_version = 9
class MinecraftWebWorld(WebWorld):
theme = "jungle"
bug_report_page = "https://github.com/KonoTyran/Minecraft_AP_Randomizer/issues/new?assignees=&labels=bug&template=bug_report.yaml&title=%5BBug%5D%3A+Brief+Description+of+bug+here"
setup = Tutorial(
"Multiworld Setup Tutorial",
"A guide to setting up the Archipelago Minecraft software on your computer. This guide covers"
"single-player, multiworld, and related software.",
"English",
"minecraft_en.md",
"minecraft/en",
["Kono Tyran"]
)
setup_es = Tutorial(
setup.tutorial_name,
setup.description,
"Español",
"minecraft_es.md",
"minecraft/es",
["Edos"]
)
setup_sv = Tutorial(
setup.tutorial_name,
setup.description,
"Swedish",
"minecraft_sv.md",
"minecraft/sv",
["Albinum"]
)
tutorials = [setup, setup_es, setup_sv]
def modify_tracker(self, tracker):
tracker.icons = {
"Wooden Pickaxe": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d2/Wooden_Pickaxe_JE3_BE3.png",
"Stone Pickaxe": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/c/c4/Stone_Pickaxe_JE2_BE2.png",
"Iron Pickaxe": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d1/Iron_Pickaxe_JE3_BE2.png",
"Diamond Pickaxe": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/e/e7/Diamond_Pickaxe_JE3_BE3.png",
"Wooden Sword": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d5/Wooden_Sword_JE2_BE2.png",
"Stone Sword": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/b/b1/Stone_Sword_JE2_BE2.png",
"Iron Sword": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/8/8e/Iron_Sword_JE2_BE2.png",
"Diamond Sword": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/4/44/Diamond_Sword_JE3_BE3.png",
"Leather Tunic": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/b/b7/Leather_Tunic_JE4_BE2.png",
"Iron Chestplate": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/31/Iron_Chestplate_JE2_BE2.png",
"Diamond Chestplate": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/e/e0/Diamond_Chestplate_JE3_BE2.png",
"Iron Ingot": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/fc/Iron_Ingot_JE3_BE2.png",
"Block of Iron": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/7/7e/Block_of_Iron_JE4_BE3.png",
"Brewing": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/b/b3/Brewing_Stand_%28empty%29_JE10.png",
"Ender Pearls": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/f6/Ender_Pearl_JE3_BE2.png",
"Bucket": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/fc/Bucket_JE2_BE2.png",
"Archery": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/a/ab/Bow_%28Pull_2%29_JE1_BE1.png",
"Shield": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/c/c6/Shield_JE2_BE1.png",
"Bed": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/6/6a/Red_Bed_%28N%29.png",
"Netherite Scrap": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/33/Netherite_Scrap_JE2_BE1.png",
"Flint and Steel": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/9/94/Flint_and_Steel_JE4_BE2.png",
"Enchanting": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/31/Enchanting_Table.gif",
"Fishing Rod": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/7/7f/Fishing_Rod_JE2_BE2.png",
"Campfire": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/9/91/Campfire_JE2_BE2.gif",
"Bottle": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/7/75/Water_Bottle_JE2_BE2.png",
"Spyglass": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/c/c1/Spyglass_JE2_BE1.png",
}
tracker.progressive_items = [
"Progressive Tools", "Progressive Weapons", "Progressive Armor", "Progressive Resource Crafting",
"Brewing", "Ender Pearls", "Bucket", "Archery", "Shield", "Bed", "Bottle", "Netherite Scrap",
"Flint and Steel", "Enchanting", "Fishing Rod", "Campfire", "Spyglass"
]
tracker.progressive_names = {
"Progressive Tools": ["Wooden Pickaxe", "Stone Pickaxe", "Iron Pickaxe", "Diamond Pickaxe"],
"Progressive Weapons": ["Wooden Sword", "Stone Sword", "Iron Sword", "Diamond Sword"],
"Progressive Armor": ["Leather Tunic", "Iron Chestplate", "Diamond Chestplate"],
"Progressive Resource Crafting": ["Iron Ingot", "Iron Ingot", "Block of Iron"]
}
tracker.regions = {
"Story": ["Minecraft", "Stone Age", "Getting an Upgrade", "Acquire Hardware", "Suit Up",
"Not Today, Thank You", "Isn't It Iron Pick", "Diamonds!", "Cover Me With Diamonds", "Enchanter",
"Hot Stuff", "Ice Bucket Challenge", "We Need to Go Deeper", "Zombie Doctor", "Eye Spy", "The End?"],
"Nether": ["Nether", "Return to Sender", "Uneasy Alliance", "Those Were the Days", "War Pigs",
"Hidden in the Depths", "Country Lode, Take Me Home", "Cover Me in Debris", "Subspace Bubble",
"A Terrible Fortress", "Spooky Scary SKeleton", "This Boat Has Legs", "Hot Tourist Destinations"],
"The End": ["The End", "Free the End", "The Next Generation", "Remote Getaway",
"The City at the End of the Game", "Sky's the Limit", "Great View From Up Here",
"The End... Again...", "You Need a Mint"],
"Adventure": ["Adventure", "Voluntary Exile", "Is It a Bird?", "Is It a Balloon?", "Is It a Plane?",
"Hero of the Village", "Monster Hunter", "A Throwaway Joke", "Very Very Frightening",
"Take Aim", "Sniper Duel", "Bullseye", "Monsters Hunted", "Postmortal", "What a Deal!",
"Hired Help", "Sticky Situation", "Ol' Betsy", "Two Birds, One Arrow",
"Who's the Pillager Now?", "Arbalistic", "Sweet Dreams", "Adventuring Time", "Surge Protector",
"Light as a Rabbit"],
"Husbandry": ["Husbandry", "Bee Our Guest", "The Parrots and the Bats", "Two by Two", "Best Friends Forever",
"A Complete Catalogue", "Fishy Business", "Tactical Fishing", "Total Beelocation",
"A Seedy Place", "A Balanced Diet", "Serious Dedication", "Whatever Floats Your Goat!",
"Glow and Behold!", "Wax On", "Wax Off", "The Cutest Predator",
"The Healing Power of Friendship"],
"Archipelago": ["Getting Wood", "Time to Mine!", "Hot Topic", "Bake Bread", "The Lie", "On a Rail",
"Time to Strike!", "Cow Tipper", "When Pigs Fly", "Overkill", "Librarian", "Overpowered"]
}
class MinecraftWorld(World):
"""
Minecraft is a game about creativity. In a world made entirely of cubes, you explore, discover, mine,
craft, and try not to explode. Delve deep into the earth and discover abandoned mines, ancient
structures, and materials to create a portal to another world. Defeat the Ender Dragon, and claim
victory!
"""
game: str = "Minecraft"
options = minecraft_options
topology_present = True
web = MinecraftWebWorld()
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 = 7
def _get_mc_data(self):
exits = [connection[0] for connection in default_connections]
return {
'world_seed': self.world.slot_seeds[self.player].getrandbits(32),
'seed_name': self.world.seed_name,
'player_name': self.world.get_player_name(self.player),
'player_id': self.player,
'client_version': client_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].value,
'egg_shards_required': min(self.world.egg_shards_required[self.player].value,
self.world.egg_shards_available[self.player].value),
'egg_shards_available': self.world.egg_shards_available[self.player].value,
'required_bosses': self.world.required_bosses[self.player].current_key,
'MC35': bool(self.world.send_defeated_mobs[self.player].value),
'death_link': bool(self.world.death_link[self.player].value),
'starting_items': str(self.world.starting_items[self.player].value),
'race': self.world.is_race,
}
def generate_basic(self):
# Generate item pool
itempool = []
junk_pool = junk_weights.copy()
# Add all required progression items
for (name, num) in required_items.items():
itempool += [name] * num
# Add structure compasses if desired
if self.world.structure_compasses[self.player]:
structures = [connection[1] for connection in default_connections]
for struct_name in structures:
itempool.append(f"Structure Compass ({struct_name})")
# Add dragon egg shards
if self.world.egg_shards_required[self.player] > 0:
itempool += ["Dragon Egg Shard"] * self.world.egg_shards_available[self.player]
# Add bee traps if desired
bee_trap_quantity = ceil(self.world.bee_traps[self.player] * (len(self.location_names)-len(itempool)) * 0.01)
itempool += ["Bee Trap"] * bee_trap_quantity
# Fill remaining items with randomly generated junk
itempool += self.world.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()), k=len(self.location_names)-len(itempool))
# Convert itempool into real items
itempool = [item for item in map(lambda name: self.create_item(name), itempool)]
# Choose locations to automatically exclude based on settings
exclusion_pool = set()
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("Defeat Ender Dragon"))
self.world.get_location("Wither", self.player).place_locked_item(self.create_item("Defeat Wither"))
self.world.itempool += itempool
def get_filler_item_name(self) -> str:
return self.world.random.choices(list(junk_weights.keys()), weights=list(junk_weights.values()))[0]
def set_rules(self):
set_advancement_rules(self.world, self.player)
set_completion_rules(self.world, self.player)
def create_regions(self):
def MCRegion(region_name: str, exits=[]):
ret = Region(region_name, None, region_name, self.player, self.world)
ret.locations = [MinecraftAdvancement(self.player, loc_name, loc_data.id, ret)
for loc_name, loc_data in advancement_table.items()
if loc_data.region == region_name]
for exit in exits:
ret.exits.append(Entrance(self.player, exit, ret))
return ret
self.world.regions += [MCRegion(*r) for r in mc_regions]
link_minecraft_structures(self.world, self.player)
def generate_output(self, output_directory: str):
data = self._get_mc_data()
filename = f"AP_{self.world.seed_name}_P{self.player}_{self.world.get_file_safe_player_name(self.player)}.apmc"
with open(os.path.join(output_directory, filename), 'wb') as f:
f.write(b64encode(bytes(json.dumps(data), 'utf-8')))
def fill_slot_data(self):
slot_data = self._get_mc_data()
for option_name in minecraft_options:
option = getattr(self.world, option_name)[self.player]
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:
item_data = item_table[name]
if name == "Bee Trap":
classification = ItemClassification.trap
# prevent books from going on excluded locations
elif name in ("Sharpness III Book", "Infinity Book", "Looting III Book"):
classification = ItemClassification.useful
elif item_data.progression:
classification = ItemClassification.progression
else:
classification = ItemClassification.filler
item = MinecraftItem(name, classification, item_data.code, self.player)
return item
def mc_update_output(raw_data, server, port):
data = json.loads(b64decode(raw_data))
data['server'] = server
data['port'] = port
return b64encode(bytes(json.dumps(data), 'utf-8'))