예제 #1
0
def scroll_commands(page_size=4):
    import df_utils

    return {
        "scroll up [<positive_num>]":
        df_utils.async_action(scroll_up, 'positive_num'),
        "scroll down [<positive_num>]":
        df_utils.async_action(scroll_down, 'positive_num'),
        "page up [<positive_num>]":
        df_utils.AsyncFunction(
            scroll_up,
            format_args=lambda **kw: [kw['positive_num'] * page_size]),
        "page down [<positive_num>]":
        df_utils.AsyncFunction(
            scroll_down,
            format_args=lambda **kw: [kw['positive_num'] * page_size]),
    }
예제 #2
0
def simple_click(*fields):
    import df_utils

    async def to_call(menu, field_list):
        for field in field_list:
            cmp = menu.get(field)
            if cmp:
                await click_component(cmp)
                return

    return df_utils.async_action(to_call, fields)
예제 #3
0
def inventory_commands():
    import df_utils
    inventory_wrapper = InventoryMenuWrapper()

    async def inventory_focus(menu, new_row, new_col):
        inventory = menu['inventory']
        await inventory_wrapper.focus_box(inventory, new_row, new_col)

    async def click_button(menu, name):
        await click_component(menu[name])

    commands = {
        "item <positive_index>":
        df_utils.async_action(inventory_focus, None, 'positive_index'),
        "row <positive_index>":
        df_utils.async_action(inventory_focus, 'positive_index', None),
        "ok":
        df_utils.async_action(click_button, "okButton"),
        "trash can":
        df_utils.async_action(click_button, "trashCan"),
    }
    return commands
예제 #4
0
async def buy_item_index(menu, idx: int):
    buttons = menu["forSaleButtons"]
    await menu_utils.focus_component(buttons[idx])


async def buy_item(menu, n: int):
    await server.mouse_click(count=n)


async def click_range(menu, start, end):
    await inventory_wrapper.click_range(menu["inventory"], start, end)


mapping = {
    "sell <positive_index> [through <positive_index2>]":
    df_utils.async_action(click_range, "positive_index", "positive_index2"),
    "item <positive_index>":
    df_utils.async_action(focus_item, "positive_index", "item"),
    "row <positive_index>":
    df_utils.async_action(focus_item, "positive_index", "row"),
    "(shop | for sale)":
    df_utils.async_action(focus_menu_section, "forSale"),
    "backpack | (player items)":
    df_utils.async_action(focus_menu_section, "inventory"),
    "buy [<positive_num>]":
    df_utils.async_action(buy_item, "positive_num"),
    **menu_utils.scroll_commands(),
}


def load_grammar():
예제 #5
0
    if menu["currentTab"] == "HOST_TAB":
        button_index = game_idx + 1  # skip "host new farm" slot
        try:
            btn = menu["slotButtons"][button_index]
        except IndexError:
            return
        await menu_utils.click_component(btn)


mapping = {
    "host":
    menu_utils.simple_click("hostTab"),
    "join":
    menu_utils.simple_click("joinTab"),
    "host new farm":
    df_utils.async_action(host_new_farm),
    "join lan game":
    df_utils.async_action(join_lan_game),
    "refresh":
    menu_utils.simple_click("refreshButton"),
    "[host | load] (farm | game) <positive_index>":
    df_utils.async_action(load_game, "positive_index"),
    "[go] back":
    menu_utils.simple_click("backButton"),
}


def load_grammar():
    extras = [rules.num, df_utils.positive_index, df_utils.positive_num]
    defaults = {"positive_num": 1}
    grammar = menu_utils.build_menu_grammar(mapping,
예제 #6
0

async def buy_animal(menu, animal_index: str):
    await menu_utils.click_component(menu["animalsToPurchase"][0])


mapping = {
    "ok":
    menu_utils.simple_click("doneNamingButton"),
    "cancel":
    menu_utils.simple_click(
        "okButton"),  # strange but using existing field names
    "random":
    menu_utils.simple_click("randomButton"),
    "(buy | purchase) <animals>":
    df_utils.async_action(buy_animal, "animals"),
    "pan <direction_keys>":
    objective.objective_action(objective.HoldKeyObjective, "direction_keys"),
}


def load_grammar():
    extras = [
        df.Choice("direction_keys", game.direction_keys),
        rules.num,
        df_utils.positive_index,
        df_utils.positive_num,
        df.Choice("animals", animals),
    ]
    defaults = {"positive_num": 1}
    grammar = menu_utils.build_menu_grammar(mapping,
예제 #7
0
async def click_equipment_icon(page, item):
    cmp = menu_utils.find_component_by_field(page['equipmentIcons'], 'name',
                                             item["name"])
    await menu_utils.focus_component(cmp)
    with server.player_items_stream() as stream:
        player_items = await stream.next()
    if player_items['cursorSlotItem'] and not player_items['equippedItems'][
            item['field']]:
        await menu_utils.click_component(cmp)
    else:
        await menu_utils.focus_component(cmp)


mapping = {
    "item <positive_index>":
    df_utils.async_action(focus_item, None, 'positive_index'),
    "row <positive_index>":
    df_utils.async_action(focus_item, 'positive_index', None),
    "trash can":
    menu_utils.simple_click("trashCan"),
    "<equipment_icons>":
    df_utils.async_action(click_equipment_icon, 'equipment_icons'),
}

equipment_icons = {
    "boots": {
        "name": "Boots",
        "field": "boots"
    },
    "hat": {
        "name": "Hat",
예제 #8
0

async def click_button(name):
    menu = await get_carpenter_menu()
    await menu_utils.click_component(menu[name])


async def move_cursor_tile(direction, amount):
    await game.move_mouse_in_direction(direction, amount * 64)


mapping = {
    "pan <direction_keys>":
    objective.objective_action(objective.HoldKeyObjective, "direction_keys"),
    "previous":
    df_utils.async_action(click_button, "backButton"),
    "cancel":
    df_utils.async_action(click_button, "cancelButton"),
    "demolish [buildings]":
    df_utils.async_action(click_button, "demolishButton"),
    "next":
    df_utils.async_action(click_button, "forwardButton"),
    "move [buildings]":
    df_utils.async_action(click_button, "moveButton"),
    "build":
    df_utils.async_action(click_button, "okButton"),
    "paint":
    df_utils.async_action(click_button, "paintButton"),
    "upgrade":
    df_utils.async_action(click_button, "upgradeIcon"),
}
예제 #9
0
import title_menu, menu_utils, server, df_utils, game, container_menu, objective, server, constants

BILLBOARD = 'billboard'


async def get_billboard_menu():
    return await menu_utils.get_active_menu(BILLBOARD)


async def click_button(name):
    menu = await get_billboard_menu()
    await menu_utils.click_component(menu[name])


mapping = {
    "accept quest": df_utils.async_action(click_button, "acceptQuestButton"),
}


@menu_utils.valid_menu_test
def is_active():
    game.get_context_menu(BILLBOARD)


def load_grammar():
    grammar = df.Grammar("billboard_menu")
    main_rule = df.MappingRule(
        name="billboard_menu_rule",
        mapping=mapping,
        extras=[
            df.Choice("direction_keys", game.direction_keys),
예제 #10
0
async def get_shipping_menu():
    menu = await menu_utils.get_active_menu(menu_type='itemsToGrabMenu')
    if not menu['shippingBin']:
        raise menu_utils.InvalidMenuOption()
    return menu


async def focus_item(new_row, new_col):
    menu = await get_shipping_menu()
    submenu = menu['inventoryMenu']
    await wrapper.focus_box(submenu, new_row, new_col)


mapping = {
    "item <positive_index>":
    df_utils.async_action(focus_item, None, 'positive_index'),
    "row <positive_index>":
    df_utils.async_action(focus_item, 'positive_index', None),
    "ok":
    df_utils.async_action(menu_utils.click_menu_button, 'okButton',
                          get_shipping_menu),
    "undo":
    df_utils.async_action(menu_utils.click_menu_button, 'lastShippedHolder',
                          get_shipping_menu),
}


@menu_utils.valid_menu_test
def is_active():
    menu = game.get_context_menu('itemsToGrabMenu')
    return menu['shippingBin']
예제 #11
0
import dragonfly as df
import functools
from srabuilder import rules
import characters, locations, fishing_menu, title_menu, menu_utils, server, df_utils, game, container_menu, objective, constants, items


async def click_skip_btn():
    evt = game.get_context_value('GAME_EVENT')
    if 'skipBounds' in evt:
        await menu_utils.click_component(evt['skipBounds'])


mapping = {
    "skip [cutscene | event]": df_utils.async_action(click_skip_btn),
}


def is_active():
    evt = game.get_context_value('GAME_EVENT')
    return evt is not None and evt['skippable']


def load_grammar():
    grammar = df.Grammar("cutscene")
    main_rule = df.MappingRule(
        name="cutscene_rule",
        mapping=mapping,
        context=df.FuncContext(is_active),
    )
    grammar.add_rule(main_rule)
    grammar.load()
예제 #12
0
    await menu_utils.focus_component(current_position)


def sort_fn(current_cmp, direction_index, multiplier, cmp):
    center = cmp["center"]
    current_center = current_cmp["center"]
    val, target_val = current_center[direction_index], center[direction_index]
    direction_diff = (target_val - val) * multiplier
    side_index = 0 if direction_index == 1 else 1
    side_diff = abs(current_center[side_index] - center[side_index])
    right_direction = 0 if direction_diff > 0 else 1
    return (right_direction, 0.1 * direction_diff + 0.9 * side_diff)


mapping = {
    "<direction_nums> [<positive_num>]": df_utils.async_action(
        move_cursor_to_next_component, "direction_nums", "positive_num"
    ),
}


def load_grammar():
    extras = [
        rules.num,
        df_utils.positive_index,
        df_utils.positive_num,
        df.Choice("direction_nums", game.direction_nums),
    ]
    grammar = menu_utils.build_menu_grammar(mapping, validate_any_menu, extras=extras)
    grammar.load()
예제 #13
0
mouse_directions = {
    "up": constants.NORTH,
    "right": constants.EAST,
    "down": constants.SOUTH,
    "left": constants.WEST,
}


async def move_mouse_by_tile(direction, n):
    await game.move_mouse_in_direction(direction, n * 64)


non_repeat_mapping = {
    "click [<positive_num>]":
    df_utils.async_action(server.mouse_click, "left", "positive_num"),
    "right click [<positive_num>]":
    df_utils.async_action(server.mouse_click, "right", "positive_num"),
    "mouse <mouse_directions> [<positive_num>]":
    df_utils.async_action(move_mouse_by_tile, "mouse_directions",
                          "positive_num"),
    "small mouse <mouse_directions> [<positive_num>]":
    df_utils.async_action(game.move_mouse_in_direction, "mouse_directions",
                          "positive_num"),
    "write game state":
    df_utils.async_action(game.write_game_state),
    "(action | check)":
    df_utils.async_action(game.press_key, constants.ACTION_BUTTON),
    "(escape | [open | close] menu)":
    df_utils.async_action(game.press_key, constants.MENU_BUTTON),
    "hold mouse":
예제 #14
0
async def click_main_button(menu, btn_name: str):
    button = menu_utils.find_component_by_field(menu['buttons'], 'name',
                                                btn_name)
    await menu_utils.click_component(button)


def get_submenu(tm, menu_type):
    menu_utils.validate_menu_type(TITLE_MENU, tm)
    submenu = tm.get('subMenu')
    menu_utils.validate_menu_type(menu_type, submenu)
    return submenu


mapping = {
    "<main_buttons> [game]":
    df_utils.async_action(click_main_button, 'main_buttons'),
    "[change | select] (language | languages)":
    menu_utils.simple_click('languageButton'),
    "about":
    menu_utils.simple_click('aboutButton'),
}


def load_grammar():
    grammar = menu_utils.build_menu_grammar(
        mapping,
        get_title_menu,
        extras=[main_button_choice],
        defaults={'positive_num': 1},
    )
    grammar.load()
예제 #15
0
    cmp = menu_utils.find_component_by_field(menu["tabs"], 'name', tab_name)
    await menu_utils.click_component(cmp)

tabs = {
    "inventory": "inventory",
    "skills": "skills",
    "social": "social",
    "map": "map",
    "crafting": "crafting",
    "collections": "collections",
    "options": "options",
    "exit [game]": "exit",
}

mapping = {
    "<tabs>": df_utils.async_action(click_tab, 'tabs'),
}

@menu_utils.valid_menu_test
def is_active():
    game.get_context_menu('gameMenu')

def load_grammar():
    grammar = df.Grammar("game_menu")
    main_rule = df.MappingRule(
        name="game_menu_rule",
        mapping=mapping,
        extras=[
            df.Choice("tabs", tabs),
            df_utils.positive_num,
        ],
예제 #16
0
def get_collections_page(menu):
    from game_menu import game_menu

    menu_utils.validate_menu_type("gameMenu", menu)
    page = game_menu.get_page_by_name(menu, "collectionsPage")
    return page


async def click_side_tab(menu, idx: int):
    cmp = menu["tabs"][idx]
    await menu_utils.click_component(cmp)


mapping = {
    "<tabs>": df_utils.async_action(click_side_tab, "tabs"),
    "previous": menu_utils.simple_click("backButton"),
    "next": menu_utils.simple_click("forwardButton"),
}


def get_grammar():
    extras = [
        df_utils.positive_num,
        df_utils.positive_index,
        tabs,
    ]
    grammar = menu_utils.build_menu_grammar(mapping,
                                            get_collections_page,
                                            extras=extras)
    return grammar
예제 #17
0
    "(wood | twigs)": constants.TWIG,
    "weeds": constants.WEEDS,
    "debris": "debris",
}

mapping = {
    "<direction_keys>":
    objective.objective_action(objective.HoldKeyObjective, "direction_keys"),
    "<direction_nums> <n>":
    objective.objective_action(objective.MoveNTilesObjective, "direction_nums",
                               "n"),
    "start swinging [tool]":
    objective.objective_action(objective.HoldKeyObjective,
                               constants.USE_TOOL_BUTTON),
    "[equip] item <positive_index>":
    df_utils.async_action(game.equip_item_by_index, "positive_index"),
    "equip [melee] weapon":
    df_utils.async_action(game.equip_melee_weapon),
    "equip <items>":
    df_utils.async_action(game.equip_item_by_name, "items"),
    "nearest <items> [<positive_index>]":
    objective.function_objective(go_to_object, "items", "positive_index"),
    "jump <direction_nums> [<positive_num>]":
    df_utils.async_action(move_and_face_previous_direction, "direction_nums",
                          "positive_num"),
    "go to bed":
    objective.function_objective(go_to_bed),
    "go to shipping bin":
    objective.function_objective(go_to_shipping_bin),
    "start shopping":
    objective.function_objective(objective.start_shopping),
예제 #18
0
        'containsMouse'] else 'inventoryMenu'
    submenu = menu[submenu_name]
    submenu_wrapper = item_grab[submenu_name]
    await submenu_wrapper.focus_box(submenu, new_row, new_col)


async def click_range(start, end):
    menu = await get_container_menu()
    submenu = menu['inventoryMenu']
    submenu_wrapper = item_grab['inventoryMenu']
    await submenu_wrapper.click_range(submenu, start, end)


mapping = {
    "deposit <positive_index>":
    df_utils.async_action(click_range, "positive_index", None),
    "deposit <positive_index> through <positive_index2>":
    df_utils.async_action(click_range, "positive_index", 'positive_index2'),
    "item <positive_index>":
    df_utils.async_action(focus_item, None, 'positive_index'),
    "row <positive_index>":
    df_utils.async_action(focus_item, 'positive_index', None),
    "backpack":
    df_utils.async_action(set_item_grab_submenu, 'inventoryMenu'),
    "container":
    df_utils.async_action(set_item_grab_submenu, 'itemsToGrabMenu'),
    "ok":
    df_utils.async_action(click_button, 'okButton'),
    "(trash | garbage) can":
    df_utils.async_action(click_button, 'trashCan'),
    "[add to] existing stacks":
예제 #19
0
    from game_menu import game_menu

    menu_utils.validate_menu_type("gameMenu", menu)
    page = game_menu.get_page_by_name(menu, "socialPage")
    return page


async def click_npc(menu, name: str):
    index = menu["names"].index(name) - menu["slotPosition"]
    if 0 <= index < PAGE_SIZE:
        await menu_utils.click_component(menu["characterSlots"][index])


mapping = {
    **menu_utils.scroll_commands(page_size=PAGE_SIZE), "<npcs>":
    df_utils.async_action(click_npc, "npcs")
}


def get_grammar():
    extras = [
        df_utils.positive_num,
        df.Choice("npcs", characters.npcs),
        df_utils.positive_index,
        df_utils.dictation_rule(),
    ]
    grammar = menu_utils.build_menu_grammar(mapping,
                                            get_social_page,
                                            extras=extras)
    return grammar
예제 #20
0
    button_index = game_idx
    try:
        btn = menu["deleteButtons"][button_index]
    except IndexError:
        return
    await menu_utils.click_component(btn)


mapping = {
    "[go] back":
    menu_utils.simple_click("backButton"),
    "(yes | ok)":
    menu_utils.simple_click("okDeleteButton"),
    "(no | cancel)":
    menu_utils.simple_click("cancelDeleteButton"),
    "(load [game] | [load] game) <positive_index>":
    df_utils.async_action(load_game, "positive_index"),
    "delete [game] <positive_index>":
    df_utils.async_action(delete_game, "positive_index"),
    **menu_utils.scroll_commands(),
}


def load_grammar():
    grammar = menu_utils.build_menu_grammar(
        mapping,
        validate_load_game_menu,
        extras=[df_utils.positive_index, df_utils.positive_num],
    )
    grammar.load()
예제 #21
0
    page = game_menu.get_page_by_name(menu, "skillsPage")
    return page


async def focus_item_dictation(page, text):
    cmp = approximate_matching.match_component(text, page["specialItems"], "hoverText")
    await menu_utils.focus_component(cmp)


async def focus_skill(page, index):
    cmp = page["skillAreas"][index]
    await menu_utils.focus_component(cmp)


mapping = {
    "<skills>": df_utils.async_action(focus_skill, "skills"),
    "<dictation>": df_utils.async_action(focus_item_dictation, "dictation"),
}


def load_grammar():
    extras = [df_utils.dictation_rule(), df.Choice("skills", skills)]
    grammar = menu_utils.build_menu_grammar(mapping, get_inventory_page, extras=extras)
    grammar.load()


# {
#     "xPositionOnScreen": 413,
#     "yPositionOnScreen": 140,
#     "upperRightCloseButton": null,
#     "containsMouse": true,
예제 #22
0
    if top_index is not None:
        await menu_utils.click_component(menu['responseCC'][top_index])


async def get_dialogue_menu():
    return await menu_utils.get_active_menu(DIALOGUE_BOX)


async def focus_item(idx):
    menu = await get_dialogue_menu()
    await menu_utils.click_component(menu['responseCC'][idx])


mapping = {
    "(item | response) <positive_index>":
    df_utils.async_action(focus_item, 'positive_index'),
    "<dictation>":
    df_utils.async_action(do_dictation, 'dictation')
}


@menu_utils.valid_menu_test
def is_active():
    game.get_context_menu(DIALOGUE_BOX)


def load_grammar():
    grammar = df.Grammar("dialogue_menu")
    main_rule = df.MappingRule(
        name="dialogue_menu_rule",
        mapping=mapping,
예제 #23
0
    "direction": "Direction",
    "hair": "Hair",
    "pants": "Pants Style",
    "(pet | animal) [preference]": "Pet",
    "shirt": "Shirt",
    "skin": "Skin",
    "(wallets | money style)": "Wallets",
    "(difficulty | profit margin)": "Difficulty",
    "[starting] cabins": "Cabins",
}

mapping = {
    "name":
    menu_utils.simple_click("nameBoxCC"),
    "((nearby | close) cabin layout | cabin layout (nearby | close))":
    df_utils.async_action(click_cabin_layout, 0),
    "(separate cabin layout | cabin layout separate)":
    df_utils.async_action(click_cabin_layout, 1),
    "farm name":
    menu_utils.simple_click("farmnameBoxCC"),
    "favorite thing":
    menu_utils.simple_click("favThingBoxCC"),
    "(random | [roll] dice)":
    menu_utils.simple_click("randomButton"),
    "(ok [button] | start game)":
    menu_utils.simple_click("okButton"),
    "skip (intro | introduction)":
    menu_utils.simple_click("skipIntroButton"),
    "help":
    menu_utils.simple_click("coopHelpButton"),
    "next":
예제 #24
0
import dragonfly as df
from srabuilder import rules
import menu_utils, server, df_utils, game, objective, server, constants

LEVEL_UP_MENU = 'shippingMenu'

categories_list = ('farming', 'foraging', 'fishing', 'mining', 'other')
categories = {category: i for i, category in enumerate(categories_list)}

async def click_category(menu, idx: int):
    cmp = menu['categories'][idx]
    if cmp['visible']:
        await menu_utils.click_component(cmp)

mapping = {
    "ok": menu_utils.simple_click("okButton"),
    '<category>': df_utils.async_action(click_category, 'category'),
    "(back | previous)": menu_utils.simple_click("backButton"),
    "(forward | next)": menu_utils.simple_click("forwardButton"),
}

def load_grammar():
    grammar = menu_utils.build_menu_grammar(mapping, LEVEL_UP_MENU, extras=[df.Choice('category', categories)])
    grammar.load()
    
    "(seventy | seven) five": 75,
    "(eighty | eight zero)": 80,
    "(eighty | eight) five": 85,
    "(ninety | nine zero)": 90,
    "(ninety | nine) five": 95,
    "one (hundred | zero zero)": 100,
    "one (hundred [and] | (oh | zero)) five": 105,
    "one [hundred [and]] (ten | one zero)": 110,
    "one [hundred [and]] (fifteen | one five)": 115,
    "one [hundred [and]] (twenty | too zero)": 120,
}


async def select_floor(menu, floor: int):
    floor_index = floor // 5
    await menu_utils.click_component(menu['elevators'][floor_index])


mapping = {
    "[floor | level] <mine_elevator_floors>":
    df_utils.async_action(select_floor, 'mine_elevator_floors'),
}


def load_grammar():
    extras = [df.Choice("mine_elevator_floors", floors_map)]
    grammar = menu_utils.build_menu_grammar(mapping,
                                            MINE_ELEVATOR_MENU,
                                            extras=extras)
    grammar.load()
예제 #26
0

async def start_fishing():
    async with server.player_status_stream() as stream:
        await game.equip_item_by_name(constants.FISHING_ROD)
    async with server.tool_status_stream() as tss:
        await cast_fishing_rod(tss)
        await wait_for_nibble(tss)


async def cast_fishing_rod(tss):
    async with game.press_and_release(constants.USE_TOOL_BUTTON):
        await tss.wait(
            lambda t: t['isTimingCast'] and t['castingPower'] > 0.95,
            timeout=10)


async def wait_for_nibble(tss):
    tool_status = await tss.wait(lambda t: t['isNibbling'] or not t['inUse'])
    if tool_status['inUse']:
        await game.press_key(constants.USE_TOOL_BUTTON)
        await tss.wait(lambda t: t['isReeling'])


mapping = {"catch fish": df_utils.async_action(catch_fish)}


def load_grammar():
    grammar = menu_utils.build_menu_grammar(mapping, FISHING_MENU)
    grammar.load()
예제 #27
0
async def focus_quest(menu, n):
    quest = menu["questLogButtons"][n]
    await menu_utils.click_component(quest)


mapping = {
    "previous":
    menu_utils.simple_click("backButton"),
    "next":
    menu_utils.simple_click("forwardButton"),
    "cancel [quest]":
    menu_utils.simple_click("cancelQuestButton"),
    "scroll up":
    menu_utils.simple_click("upArrow"),
    "scroll down":
    menu_utils.simple_click("downArrow"),
    "[collect] (reward | rewards)":
    menu_utils.simple_click("rewardBox"),
    "(item | quest) <positive_index>":
    df_utils.async_action(focus_quest, "positive_index"),
}


def load_grammar():
    extras = [df_utils.positive_index]
    grammar = menu_utils.build_menu_grammar(mapping,
                                            QUEST_LOG_MENU,
                                            extras=extras)
    grammar.load()
예제 #28
0
            await menu_utils.focus_component(cmp)
            return True
    return False


async def focus_item_dictation(page, text):
    items_on_page = [x[1]["name"] for x in page["currentRecipePage"]]
    best_idx = approximate_matching.do_match(str(text), items_on_page)
    if best_idx is not None:
        cmp = page["currentRecipePage"][best_idx][0]
        await menu_utils.focus_component(cmp)


mapping = {
    "<craftable_items>":
    df_utils.async_action(focus_item, "craftable_items"),
    "scroll up [<positive_num>]":
    df_utils.async_action(menu_utils.scroll_up, "positive_num"),
    "scroll down [<positive_num>]":
    df_utils.async_action(menu_utils.scroll_down, "positive_num"),
    **menu_utils.inventory_commands(),
    "<dictation>":
    df_utils.async_action(focus_item_dictation, "dictation"),
}


def load_grammar():
    grammar = df.Grammar("crafting_page")
    extras = [
        df_utils.positive_num,
        df_utils.positive_index,