diff --git a/MultiServer.py b/MultiServer.py
index 02dabe9e232d..fe26361d6800 100644
--- a/MultiServer.py
+++ b/MultiServer.py
@@ -2,8 +2,8 @@
import argparse
import asyncio
-import copy
import collections
+import copy
import datetime
import functools
import hashlib
@@ -955,7 +955,16 @@ def register_location_checks(ctx: Context, team: int, slot: int, locations: typi
logging.info('(Team #%d) %s sent %s to %s (%s)' % (
team + 1, ctx.player_names[(team, slot)], ctx.item_names[item_id],
ctx.player_names[(team, target_player)], ctx.location_names[location]))
- info_text = json_format_send_event(new_item, target_player)
+
+ if ctx.item_names[item_id].endswith(" Stone"):
+ info_text = json_format_stone_event(new_item, target_player)
+ elif ctx.item_names[item_id].startswith("the dusted"):
+ info_text = json_format_dust_event(new_item, target_player)
+ elif ctx.item_names[item_id] == "Triforce Piece":
+ info_text = json_format_triforce_event(new_item, target_player)
+ else:
+ info_text = json_format_send_event(new_item, target_player)
+
ctx.broadcast_team(team, [info_text])
ctx.location_checks[team, slot] |= new_locations
@@ -1036,6 +1045,46 @@ def json_format_send_event(net_item: NetworkItem, receiving_player: int):
"item": net_item}
+def json_format_stone_event(net_item: NetworkItem, receiving_player: int):
+ parts = []
+ NetUtils.add_json_text(parts, net_item, type=NetUtils.JSONTypes.player_id)
+ NetUtils.add_json_text(parts, " found the ")
+ NetUtils.add_json_item(parts, net_item.item, net_item.player, net_item.flags)
+ NetUtils.add_json_text(parts, " (")
+ NetUtils.add_json_location(parts, net_item.location, net_item.player)
+ NetUtils.add_json_text(parts, ")")
+
+ return {"cmd": "PrintJSON", "data": parts, "type": "ItemSend",
+ "receiving": receiving_player,
+ "item": net_item}
+
+
+def json_format_triforce_event(net_item: NetworkItem, receiving_player: int):
+ parts = []
+ NetUtils.add_json_text(parts, net_item, type=NetUtils.JSONTypes.player_id)
+ NetUtils.add_json_text(parts, " also found a ")
+ NetUtils.add_json_item(parts, net_item.item, net_item.player, net_item.flags)
+ NetUtils.add_json_text(parts, "taped to the back of the stone!")
+
+ return {"cmd": "PrintJSON", "data": parts, "type": "ItemSend",
+ "receiving": receiving_player,
+ "item": net_item}
+
+
+def json_format_dust_event(net_item: NetworkItem, receiving_player: int):
+ parts = []
+ NetUtils.add_json_text(parts, net_item, type=NetUtils.JSONTypes.player_id)
+ NetUtils.add_json_text(parts, " found ")
+ NetUtils.add_json_item(parts, net_item.item, net_item.player, net_item.flags)
+ NetUtils.add_json_text(parts, ". (")
+ NetUtils.add_json_location(parts, net_item.location, net_item.player)
+ NetUtils.add_json_text(parts, ")")
+
+ return {"cmd": "PrintJSON", "data": parts, "type": "ItemSend",
+ "receiving": receiving_player,
+ "item": net_item}
+
+
def get_intended_text(input_text: str, possible_answers) -> typing.Tuple[str, bool, str]:
picks = Utils.get_fuzzy_results(input_text, possible_answers, limit=2)
if len(picks) > 1:
diff --git a/WebHostLib/static/assets/gauntletTracker.js b/WebHostLib/static/assets/gauntletTracker.js
new file mode 100644
index 000000000000..a698214b8dd6
--- /dev/null
+++ b/WebHostLib/static/assets/gauntletTracker.js
@@ -0,0 +1,49 @@
+window.addEventListener('load', () => {
+ // Reload tracker every 15 seconds
+ const url = window.location;
+ setInterval(() => {
+ const ajax = new XMLHttpRequest();
+ ajax.onreadystatechange = () => {
+ if (ajax.readyState !== 4) { return; }
+
+ // Create a fake DOM using the returned HTML
+ const domParser = new DOMParser();
+ const fakeDOM = domParser.parseFromString(ajax.responseText, 'text/html');
+
+ // Update item tracker
+ document.getElementById('inventory-table').innerHTML = fakeDOM.getElementById('inventory-table').innerHTML;
+ // Update only counters in the location-table
+ let counters = document.getElementsByClassName('counter');
+ const fakeCounters = fakeDOM.getElementsByClassName('counter');
+ for (let i = 0; i < counters.length; i++) {
+ counters[i].innerHTML = fakeCounters[i].innerHTML;
+ }
+ };
+ ajax.open('GET', url);
+ ajax.send();
+ }, 15000)
+
+ // Collapsible advancement sections
+ const categories = document.getElementsByClassName("location-category");
+ for (let i = 0; i < categories.length; i++) {
+ let hide_id = categories[i].id.split('-')[0];
+ if (hide_id == 'Total') {
+ continue;
+ }
+ categories[i].addEventListener('click', function() {
+ // Toggle the advancement list
+ document.getElementById(hide_id).classList.toggle("hide");
+ // Change text of the header
+ const tab_header = document.getElementById(hide_id+'-header').children[0];
+ const orig_text = tab_header.innerHTML;
+ let new_text;
+ if (orig_text.includes("▼")) {
+ new_text = orig_text.replace("▼", "▲");
+ }
+ else {
+ new_text = orig_text.replace("▲", "▼");
+ }
+ tab_header.innerHTML = new_text;
+ });
+ }
+});
diff --git a/WebHostLib/static/static/backgrounds/wallpaper.jpg b/WebHostLib/static/static/backgrounds/wallpaper.jpg
new file mode 100644
index 000000000000..811e2082eaeb
Binary files /dev/null and b/WebHostLib/static/static/backgrounds/wallpaper.jpg differ
diff --git a/WebHostLib/static/styles/gauntletTracker.css b/WebHostLib/static/styles/gauntletTracker.css
new file mode 100644
index 000000000000..224cdcdc55a0
--- /dev/null
+++ b/WebHostLib/static/styles/gauntletTracker.css
@@ -0,0 +1,102 @@
+#player-tracker-wrapper{
+ margin: 0;
+}
+
+#inventory-table{
+ border-top: 2px solid #000000;
+ border-left: 2px solid #000000;
+ border-right: 2px solid #000000;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ padding: 3px 3px 10px;
+ width: 384px;
+ background-color: #42b149;
+}
+
+#inventory-table td{
+ width: 40px;
+ height: 40px;
+ text-align: center;
+ vertical-align: middle;
+}
+
+#inventory-table img{
+ height: 100%;
+ max-width: 40px;
+ max-height: 40px;
+ filter: grayscale(100%) contrast(75%) brightness(30%);
+}
+
+#inventory-table img.acquired{
+ filter: none;
+}
+
+#inventory-table div.counted-item {
+ position: relative;
+}
+
+#inventory-table div.item-count {
+ position: absolute;
+ color: white;
+ font-family: "Minecraftia", monospace;
+ font-weight: bold;
+ bottom: 0;
+ right: 0;
+}
+
+#location-table{
+ width: 384px;
+ border-left: 2px solid #000000;
+ border-right: 2px solid #000000;
+ border-bottom: 2px solid #000000;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ background-color: #42b149;
+ padding: 0 3px 3px;
+ font-family: "Minecraftia", monospace;
+ font-size: 14px;
+ cursor: default;
+}
+
+#location-table th{
+ vertical-align: middle;
+ text-align: left;
+ padding-right: 10px;
+}
+
+#location-table td{
+ padding-top: 2px;
+ padding-bottom: 2px;
+ line-height: 20px;
+}
+
+#location-table td.counter {
+ text-align: right;
+ font-size: 14px;
+}
+
+#location-table td.toggle-arrow {
+ text-align: right;
+}
+
+#location-table tr#Total-header {
+ font-weight: bold;
+}
+
+#location-table img{
+ height: 100%;
+ max-width: 30px;
+ max-height: 30px;
+}
+
+#location-table tbody.locations {
+ font-size: 12px;
+}
+
+#location-table td.location-name {
+ padding-left: 16px;
+}
+
+.hide {
+ display: none;
+}
diff --git a/WebHostLib/templates/gauntletTracker.html b/WebHostLib/templates/gauntletTracker.html
new file mode 100644
index 000000000000..86bb27485bc9
--- /dev/null
+++ b/WebHostLib/templates/gauntletTracker.html
@@ -0,0 +1,85 @@
+
+
+
+ The Infinity Gauntlet
+
+
+
+
+
+
+
diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py
index 8f9fb1488122..28a1f410c20f 100644
--- a/WebHostLib/tracker.py
+++ b/WebHostLib/tracker.py
@@ -1,7 +1,7 @@
import collections
import datetime
import typing
-from typing import Counter, Optional, Dict, Any, Tuple
+from typing import Any, Counter, Dict, Optional, Tuple
from uuid import UUID
from flask import render_template
@@ -565,6 +565,28 @@ def __renderMinecraftTracker(multisave: Dict[str, Any], room: Room, locations: D
checks_done=checks_done, checks_in_area=checks_in_area, location_info=location_info,
**display_data)
+def __renderGauntletTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]],
+ inventory: Counter, team: int, player: int, playerName: str,
+ seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict,
+ saving_second: int) -> str:
+
+ icons = {
+ "Space Stone": "https://static.wikia.nocookie.net/marvelcinematicuniverse/images/0/0a/Space_Stone_VFX.png",
+ "Mind Stone": "https://static.wikia.nocookie.net/marvelcinematicuniverse/images/e/e4/Mind_Stone_VFX.png",
+ "Reality Stone": "https://static.wikia.nocookie.net/marvelcinematicuniverse/images/9/9b/Reality_Stone_VFX.png",
+ "Power Stone": "https://static.wikia.nocookie.net/marvelcinematicuniverse/images/d/d7/Power_Stone_VFX.png",
+ "Time Stone": "https://static.wikia.nocookie.net/marvelcinematicuniverse/images/f/f0/Time_Stone_VFX.png",
+ "Soul Stone": "https://static.wikia.nocookie.net/marvelcinematicuniverse/images/1/17/Soul_Stone_VFX.png",
+ "Wallpaper": "https://c.wallhere.com/photos/fe/3f/The_Legend_of_Zelda_video_games_Nintendo_The_Legend_of_Zelda_Majora's_Mask-223870.jpg!d",
+ }
+
+ return render_template("gauntletTracker.html",
+ inventory=inventory, icons=icons,
+ acquired_items={lookup_any_item_id_to_name[id] for id in inventory if
+ id in lookup_any_item_id_to_name},
+ player=player, team=team, room=room, player_name=playerName, saving_second = saving_second,
+ checks_done=checks_done, checks_in_area=checks_in_area)
+
def __renderOoTTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]],
inventory: Counter, team: int, player: int, playerName: str,
@@ -1574,7 +1596,8 @@ def attribute_item(team: int, recipient: int, item: int):
"A Link to the Past": __renderAlttpTracker,
"ChecksFinder": __renderChecksfinder,
"Super Metroid": __renderSuperMetroidTracker,
- "Starcraft 2 Wings of Liberty": __renderSC2WoLTracker
+ "Starcraft 2 Wings of Liberty": __renderSC2WoLTracker,
+ "Infinity Gauntlet": __renderGauntletTracker,
}
multi_trackers: typing.Dict[str, typing.Callable] = {
diff --git a/host.yaml b/host.yaml
index c2647c44caae..57ea591cec78 100644
--- a/host.yaml
+++ b/host.yaml
@@ -60,7 +60,7 @@ generator:
# Folder from which the player yaml files are pulled from
player_files_path: "Players"
#amount of players, 0 to infer from player files
- players: 0
+ players: 5
# general weights file, within the stated player_files_path location
# gets used if players is higher than the amount of per-player files found to fill remaining slots
weights_file_path: "weights.yaml"
diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py
index 3f68e34b3f3c..8bfd661eafd0 100644
--- a/worlds/alttp/__init__.py
+++ b/worlds/alttp/__init__.py
@@ -5,24 +5,24 @@
import typing
import Utils
-from BaseClasses import Item, CollectionState, Tutorial, MultiWorld
-from .Dungeons import create_dungeons, Dungeon
-from .EntranceShuffle import link_entrances, link_inverted_entrances, plando_connect, \
- indirect_connections, indirect_connections_inverted, indirect_connections_not_inverted
+from BaseClasses import CollectionState, Item, ItemClassification, MultiWorld, Tutorial
+from worlds.AutoWorld import LogicMixin, WebWorld, World
+from .Client import ALTTPSNIClient
+from .Dungeons import Dungeon, create_dungeons
+from .EntranceShuffle import indirect_connections, indirect_connections_inverted, indirect_connections_not_inverted, \
+ link_entrances, link_inverted_entrances, plando_connect
from .InvertedRegions import create_inverted_regions, mark_dark_world_regions
-from .ItemPool import generate_itempool, difficulties
-from .Items import item_init_table, item_name_groups, item_table, GetBeemizerItem
+from .ItemPool import difficulties, generate_itempool
+from .Items import GetBeemizerItem, item_init_table, item_name_groups, item_table
from .Options import alttp_options, smallkey_shuffle
-from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions, lookup_vanilla_location_to_entrance, \
- is_main_entrance
-from .Client import ALTTPSNIClient
-from .Rom import LocalRom, patch_rom, patch_race_rom, check_enemizer, patch_enemizer, apply_rom_settings, \
- get_hash_string, get_base_rom_path, LttPDeltaPatch
+from .Regions import create_regions, is_main_entrance, lookup_name_to_id, lookup_vanilla_location_to_entrance, \
+ mark_light_world_regions
+from .Rom import LocalRom, LttPDeltaPatch, apply_rom_settings, check_enemizer, get_base_rom_path, get_hash_string, \
+ patch_enemizer, patch_race_rom, patch_rom
from .Rules import set_rules
-from .Shops import create_shops, Shop, ShopSlotFill, ShopType, price_rate_display, price_type_display_name
-from .SubClasses import ALttPItem, LTTPRegionType
-from worlds.AutoWorld import World, WebWorld, LogicMixin
+from .Shops import Shop, ShopSlotFill, ShopType, create_shops, price_rate_display, price_type_display_name
from .StateHelpers import can_buy_unlimited
+from .SubClasses import ALttPItem, LTTPRegionType
lttp_logger = logging.getLogger("A Link to the Past")
@@ -230,6 +230,8 @@ class ALTTPWorld(World):
has_progressive_bows: bool
dungeons: typing.Dict[str, Dungeon]
+ counter: typing.ClassVar[int] = 0
+
def __init__(self, *args, **kwargs):
self.dungeon_local_item_names = set()
self.dungeon_specific_item_names = set()
@@ -244,7 +246,7 @@ def stage_assert_generate(cls, multiworld: MultiWorld):
if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file)
if multiworld.is_race:
- import xxtea
+ pass
for player in multiworld.get_game_players(cls.game):
if multiworld.worlds[player].use_enemizer:
check_enemizer(multiworld.worlds[player].enemizer_path)
@@ -460,10 +462,103 @@ def pre_fill(self):
raise FillError('Unable to place dungeon prizes')
@classmethod
- def stage_pre_fill(cls, world):
+ def stage_pre_fill(cls, world: MultiWorld):
from .Dungeons import fill_dungeons_restrictive
fill_dungeons_restrictive(world)
+ # Thanos did a sneaky.
+ from worlds.infinity_gauntlet import IGItem, IGWorld
+
+ replacement_table: typing.Dict[str, str] = {
+ "Single Arrow": "the dusted remains of a single arrow",
+ "Arrows (10)": "the dusted remains of 10 arrows",
+ "Single Bomb": "the dusted remains of a single bomb",
+ "Bombs (3)": "the dusted remains of 3 bombs",
+ "Bombs (10)": "the dusted remains of 10 bombs",
+ "Boss Heart Container": "the dusted remains of a Boss Heart Container",
+ "Sanctuary Heart Container": "the dusted remains of a Sanctuary Heart Container",
+ "Piece of Heart": "the dusted remains of a Piece of Heart",
+ "Rupee (1)": "the dusted remains of a green rupee",
+ "Rupees (5)": "the dusted remains of a blue rupee",
+ "Rupees (20)": "the dusted remains of a red rupee",
+ "Rupees (50)": "the dusted remains of a purple rupee",
+ "Rupees (100)": "the dusted remains of an orange rupee",
+ "Rupees (300)": "the dusted remains of a silver rupee",
+ "Bee": "the dusted remains of a bee",
+ "Bee Trap": "the dusted remains of a bunch of bees",
+ }
+
+ prog_replacement_table: typing.Dict[str, str] = {
+ "Progressive Mail": "the dusted remains of some Red Mail",
+ "Progressive Shield": "the dusted remains of a Mirror Shield",
+ "Progressive Sword": "the dusted remains of a sword",
+ }
+
+ prog_item_snapped: typing.Dict[str, typing.List[int]] = {
+ "Progressive Sword": [],
+ "Progressive Mail": [],
+ "Progressive Shield": [],
+ }
+
+ piece_index = 0
+ stones = [
+ "Taped to the Space Stone",
+ "Taped to the Reality Stone",
+ "Taped to the Power Stone",
+ "Taped to the Soul Stone",
+ "Taped to the Mind Stone",
+ "Taped to the Time Stone",
+ ]
+
+ infinity_player = [ig_world for ig_world in world.worlds.values() if isinstance(ig_world, IGWorld)][0].player
+ new_item_pool: typing.List[Item] = []
+ for item in world.itempool:
+ # Don't want to touch our beautiful items.
+ if isinstance(item, IGItem):
+ new_item_pool.append(item)
+ continue
+
+ if item.name == "Triforce Piece":
+ world.get_location(stones[piece_index], infinity_player).place_locked_item(item)
+ piece_index += 1
+ continue
+
+ if item.name in prog_item_snapped.keys():
+ if item.player not in prog_item_snapped[item.name]:
+ prog_item_snapped[item.name].append(item.player)
+ item_id = IGWorld.item_name_to_id[prog_replacement_table[item.name]]
+ item = IGItem(prog_replacement_table[item.name], ItemClassification.trap, item_id, infinity_player)
+ new_item_pool.append(item)
+ else:
+ new_item_pool.append(item)
+
+ continue
+
+ # 50% Chance for Item to get Thanos snapped
+ if item.name in replacement_table.keys():
+ if world.random.choice([True, False]):
+ item_id = IGWorld.item_name_to_id[replacement_table[item.name]]
+ item = IGItem(replacement_table[item.name], ItemClassification.trap, item_id, infinity_player)
+ new_item_pool.append(item)
+ continue
+ else:
+ new_item_pool.append(item)
+ continue
+
+ if item.name.startswith("Map "):
+ item = IGItem("the dusted remains of a map", ItemClassification.trap, 69898999, infinity_player)
+ new_item_pool.append(item)
+ continue
+
+ if item.name.startswith("Compass "):
+ item = IGItem("the dusted remains of a compass", ItemClassification.trap, 69898998, infinity_player)
+ new_item_pool.append(item)
+ continue
+
+ new_item_pool.append(item)
+
+ world.itempool = new_item_pool
+
@classmethod
def stage_post_fill(cls, world):
ShopSlotFill(world)
diff --git a/worlds/infinity_gauntlet/__init__.py b/worlds/infinity_gauntlet/__init__.py
new file mode 100644
index 000000000000..d46f31da8c58
--- /dev/null
+++ b/worlds/infinity_gauntlet/__init__.py
@@ -0,0 +1,106 @@
+from BaseClasses import CollectionState, Item, ItemClassification, Location, Region
+from worlds.AutoWorld import World
+
+
+class IGItem(Item):
+ game = "Infinity Gauntlet"
+
+
+class IGLocation(Location):
+ game = "Infinity Gauntlet"
+
+
+class IGWorld(World):
+ """There's literally no reason to be looking up this game."""
+ game = "Infinity Gauntlet"
+ data_version = 0
+ hidden = True
+
+ location_name_to_id = {
+ "Taped to the Space Stone": 69_888_000,
+ "Taped to the Reality Stone": 69_888_001,
+ "Taped to the Power Stone": 69_888_002,
+ "Taped to the Soul Stone": 69_888_003,
+ "Taped to the Mind Stone": 69_888_004,
+ "Taped to the Time Stone": 69_888_005,
+ }
+
+ item_name_to_id = {
+ "Space Stone": 69_888_000,
+ "Reality Stone": 69_888_001,
+ "Power Stone": 69_888_002,
+ "Soul Stone": 69_888_003,
+ "Mind Stone": 69_888_004,
+ "Time Stone": 69_888_005,
+
+ # "Snapped" Items
+ "the dusted remains of a single arrow": 69_889_000 + 0x43,
+ "the dusted remains of 10 arrows": 69_889_000 + 0x44,
+ "the dusted remains of a single bomb": 69_889_000 + 0x27,
+ "the dusted remains of 3 bombs": 69_889_000 + 0x28,
+ "the dusted remains of 10 bombs": 69_889_000 + 0x31,
+ "the dusted remains of some Red Mail": 69_889_000 + 0x60,
+ "the dusted remains of a Mirror Shield": 69_889_000 + 0x5F,
+ "the dusted remains of a Boss Heart Container": 69_889_000 + 0x3E,
+ "the dusted remains of a Sanctuary Heart Container": 69_889_000 + 0x3F,
+ "the dusted remains of a Piece of Heart": 69_889_000 + 0x17,
+ "the dusted remains of a green rupee": 69_889_000 + 0x34,
+ "the dusted remains of a blue rupee": 69_889_000 + 0x35,
+ "the dusted remains of a red rupee": 69_889_000 + 0x36,
+ "the dusted remains of a purple rupee": 69_889_000 + 0x41,
+ "the dusted remains of an orange rupee": 69_889_000 + 0x40,
+ "the dusted remains of a silver rupee": 69_889_000 + 0x46,
+ "the dusted remains of a map": 69_889_000 + 9999,
+ "the dusted remains of a compass": 69_889_000 + 9998,
+ "the dusted remains of a bee": 69_889_000 + 0x0E,
+ "the dusted remains of a bunch of bees": 69_889_000 + 0xB0,
+ "the dusted remains of a sword": 69_889_000 + 0x5E,
+ }
+
+ def create_item(self, name: str) -> IGItem:
+ classification = ItemClassification.progression_skip_balancing
+ if name.startswith("The dusted"):
+ classification = ItemClassification.trap
+
+ return IGItem(name, classification, self.item_name_to_id[name], self.player)
+
+ def create_regions(self):
+ menu_region = Region("Menu", self.player, self.multiworld)
+ self.multiworld.regions.append(menu_region)
+
+ menu_region.add_locations(self.location_name_to_id, IGLocation)
+
+ def get_filler_item_name(self) -> str:
+ return "Nothing"
+
+ def has_all_stones(self, state: CollectionState):
+ return state.has_all(
+ set([item for item in self.item_name_to_id.keys() if not item.startswith("The dusted")]),
+ self.player
+ )
+
+ def set_rules(self):
+ self.multiworld.get_location("Taped to the Space Stone", self.player).access_rule = lambda state: state.has(
+ "Space Stone", self.player)
+ self.multiworld.get_location("Taped to the Reality Stone", self.player).access_rule = lambda state: state.has(
+ "Reality Stone", self.player)
+ self.multiworld.get_location("Taped to the Power Stone", self.player).access_rule = lambda state: state.has(
+ "Power Stone", self.player)
+ self.multiworld.get_location("Taped to the Soul Stone", self.player).access_rule = lambda state: state.has(
+ "Soul Stone", self.player)
+ self.multiworld.get_location("Taped to the Mind Stone", self.player).access_rule = lambda state: state.has(
+ "Mind Stone", self.player)
+ self.multiworld.get_location("Taped to the Time Stone", self.player).access_rule = lambda state: state.has(
+ "Time Stone", self.player)
+
+ self.multiworld.completion_condition[self.player] = lambda state: self.has_all_stones(state)
+
+ def create_items(self):
+ self.multiworld.itempool += [
+ self.create_item("Space Stone"),
+ self.create_item("Reality Stone"),
+ self.create_item("Power Stone"),
+ self.create_item("Soul Stone"),
+ self.create_item("Mind Stone"),
+ self.create_item("Time Stone"),
+ ]