All files / js iactions.js

76.99% Statements 87/113
32.35% Branches 11/34
100% Functions 5/5
76.99% Lines 87/113

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 11473x 73x 73x 73x 73x 73x 73x 73x 73x 73x 73x 73x 73x 4x 4x 4x     4x           4x     4x   4x     4x     4x 4x 73x 73x 4x 4x 4x 73x 73x 73x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x             4x 4x 4x     4x 4x 2x 2x 4x 4x 4x 4x 4x 4x 4x 4x 1x 1x 4x 73x 73x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x   1x   1x 1x  
// iactions.js — Item action menu (port of iactions.c)
// Shows a menu of possible actions for an inventory item.
 
import { game } from './gstate.js';
import { BULLWHIP, COIN_CLASS, CREAM_PIE, EUCALYPTUS_LEAF, FOOD_CLASS, LUMP_OF_ROYAL_JELLY, MAGIC_MARKER, POTION_CLASS, POT_OIL, SPBOOK_CLASS, TOOL_CLASS, TOUCHSTONE, WAND_CLASS, WEAPON_CLASS } from './objects.js';
import { CQ_CANNED, PICK_ONE } from './const.js';
import { doname, xname } from './objnam.js';
import { cmdq_add_key } from './input.js';
import { add_menu, create_nhwindow_menu, destroy_nhwindow_menu,
         end_menu, select_menu, start_menu } from './menu.js';
 
// C ref: apply.c apply_ok — can this item be applied?
export function apply_ok(obj) {
    if (!obj) return false;
    const cls = game.objects[obj.otyp]?.oc_class ?? obj.oclass;
    if (cls === TOOL_CLASS || cls === WAND_CLASS || cls === COIN_CLASS) {
        return true;
    }
    if (cls === WEAPON_CLASS) {
        const name = (game.objects[obj.otyp]?.oc_name || game.objects[obj.otyp]?.oc_descr || '').toLowerCase();
        if (obj.otyp === BULLWHIP || /pick|axe|pole/.test(name)) {
            return true;
        }
    }
    if (cls === POTION_CLASS && obj.otyp === POT_OIL) {
        return true;
    }
    if (cls === FOOD_CLASS && (obj.otyp === CREAM_PIE
        || obj.otyp === EUCALYPTUS_LEAF
        || obj.otyp === LUMP_OF_ROYAL_JELLY)) {
        return true;
    }
    if (obj.otyp === TOUCHSTONE) {
        return true;
    }
    return false;
}
 
// C ref: iactions.c:698 — "Do what with %s?"
async function itemactions_title(otmp) {
    return `Do what with the ${xname(otmp)}?`;
}
 
// C ref: iactions.c:277 itemactions — show menu of item actions
export async function itemactions(otmp) {
    const win = create_nhwindow_menu();
    win.statusTitleOnly = true;
    // C iactions menus are shown as corner overlays without forcing a
    // backdrop docrt/status repaint between menu transitions.
    win.skipOverlayBackdropRedraw = true;
    start_menu(win);
    const itemName = xname(otmp);
    const cls = game.objects[otmp?.otyp]?.oc_class ?? otmp.oclass;
 
    if (apply_ok(otmp)) {
        let text = 'Use or apply this item';
        if (otmp.otyp === MAGIC_MARKER) {
            text = 'Write on something with this marker';
        }
        add_menu(win, 'a', 'a', 0, 0, 8, text, 0);
    }
    add_menu(win, 'c', 'c', 0, 0, 8, `Name this specific ${itemName}`, 0);
    add_menu(win, 'd', 'd', 0, 0, 8, 'Drop this item', 0);
    if (otmp.otyp === MAGIC_MARKER) {
        add_menu(win, 'E', 'E', 0, 0, 8, 'Scribble graffiti on the floor', 0);
    }
    add_menu(win, 'i', 'i', 0, 0, 8, 'Adjust inventory by assigning new letter', 0);
    if (cls === SPBOOK_CLASS) {
        add_menu(win, 'r', 'r', 0, 0, 8, `Study this spellbook`, 0);
    }
    add_menu(win, 't', 't', 0, 0, 8, 'Throw this item', 0);
    add_menu(win, 'w', 'w', 0, 0, 8, 'Wield this item in your hands', 0);
    add_menu(win, '/', '/', 0, 0, 8, 'Look up information about this', 0);
 
    end_menu(win, await itemactions_title(otmp));
    const selected = await select_menu(win, PICK_ONE);
    destroy_nhwindow_menu(win);
    if (!selected?.count || !Array.isArray(selected.items) || !selected.items[0]) return;
    const action = selected.items[0].identifier;
    await itemactions_pushkeys(otmp, action);
}
 
// C ref: iactions.c:139 itemactions_pushkeys
async function itemactions_pushkeys(otmp, action) {
    if (!otmp || action == null) return;
    const act = typeof action === 'number' ? String.fromCharCode(action) : String(action);
    const invlet = typeof otmp.invlet === 'string' ? otmp.invlet : null;
    const invletCode = invlet ? invlet.charCodeAt(0) : 0;
 
    const queueCommandWithInvlet = (cmdChar) => {
        cmdq_add_key(CQ_CANNED, cmdChar.charCodeAt(0));
        if (invletCode) cmdq_add_key(CQ_CANNED, invletCode);
    };
 
    switch (act) {
    case 'a':
    case 'd':
    case 'E':
    case 'r':
    case 't':
    case 'w':
        queueCommandWithInvlet(act);
        break;
    case 'c':
        await doname(otmp);
        break;
    case 'i':
    case '/':
        break;
    default:
        break;
    }
}