chrisk123999 / chris-premades

MIT License
46 stars 49 forks source link

[Suggestion] A few suggestions for the new BG3 weapon features #124

Closed Janner3D closed 10 months ago

Janner3D commented 1 year ago

The main reason I ask this is because that generally seems to be the way it works in BG3--you use the weapon action, and it automatically takes into account what you have equipped, and uses that. With this workflow, the weapon action would very well be wasted if the attack doesn't hit in the first place, which seems fair given the power of the debuffs these weapon actions provide and the fact they are recovered on a short rest.

game.user.updateTokenTargets();
const weapons = ['greataxe', 'battleaxe', 'greatsword', 'halberd'];
const availableWeapons = actor.items.filter((i) =>
    weapons.some((w) => i.system.baseItem === w)
);

if (!availableWeapons.length)
    return ui.notifications.warn(
        `You don't have any of the required weapons to use ${item.name}`
    );

const close = () => {
    return false;
};

const dialogWeapons = await Dialog.wait({
    title: 'Choose a weapon to attack',
    content: 'Available weapons:',
    buttons: availableWeapons.map((i) => ({
        label: i.name,
        callback: () => {
            return i;
        },
    })),
    close,
});

if (!dialogWeapons)
    return ui.notifications.warn(
        `You didn't choose any weapon to use ${item.name}`
    );

const originPoints = [
    'Top Left',
    'Top Mid',
    'Top Right',
    'Left Mid',
    'Center Placeholder',
    'Right Mid',
    'Bottom Left',
    'Bottom Mid',
    'Bottom Right',
];

const content = `<style>
#dialogPoint .dialog-buttons {
    display: grid;
    gap: 1em;
    grid-template-columns: 1fr 1fr 1fr;
}
</style>`;
/*

*/

let reach = 5;
const dialogPoint = await Dialog.wait(
    {
        title: 'Choose the point of origin',
        content,
        buttons: originPoints.map((i) => ({
            label:
                i === originPoints[4]
                    ? `<input type="text" id="reach-input" focus=true placeHolder="Reach: ${reach}"/>${dialogWeapons.name}`
                    : i,
            //icon: i === originPoints[4] ? `<img src="${dialogWeapons.img}" height="24px" width="24px"/>` : '',
            disabled: i === originPoints[4] ? true : false,
            callback: (html) => {
                const newReach = html[0].querySelector('#reach-input')?.value;
                if (!!newReach) reach = newReach;
                return i;
            },
        })),
        close,
    },
    { id: 'dialogPoint' }
);

ui.notifications.info(`Reach: ${reach}ft`);
if (!dialogPoint) return;
let x, y;
const { size: gridSize, distance: gridDistance } = canvas.scene.grid || {};
if (!gridSize || !gridDistance) return;
const { width: tokenWidth, height: tokenHeight } = token.document || {};
const sizeX = gridSize * tokenWidth;
const sizeY = gridSize * tokenHeight;
if (!tokenWidth || !tokenHeight) return;
if (dialogPoint === 'Top Left') {
    //x,y
    x = token.x;
    y = token.y;
    direction = 225;
} else if (dialogPoint === 'Top Right') {
    //x+sizeX, y
    x = token.x + sizeX;
    y = token.y;
    direction = 315;
} else if (dialogPoint === 'Bottom Left') {
    //x, y+sizeY
    x = token.x;
    y = token.y + sizeY;
    direction = 135;
} else if (dialogPoint === 'Bottom Right') {
    //x+sizeX, y+sizeY
    x = token.x + sizeX;
    y = token.y + sizeY;
    direction = 45;
} else if (dialogPoint === 'Top Mid') {
    //x+sizeX/2, y
    x = token.x + Math.floor(sizeX / 2);
    y = token.y;
    direction = 270;
} else if (dialogPoint === 'Bottom Mid') {
    //x+sizeX/2, y+sizeY
    x = token.x + Math.floor(sizeX / 2);
    y = token.y + sizeY;
    direction = 90;
} else if (dialogPoint === 'Left Mid') {
    //x, y+sizeY/2
    x = token.x;
    y = token.y + Math.floor(sizeY / 2);
    direction = 180;
} else if (dialogPoint === 'Right Mid') {
    //x+sizeX, y+sizeY/2
    x = token.x + sizeX;
    y = token.y + Math.floor(sizeY / 2);
    direction = 360;
}
let distance = dialogPoint.includes('Mid')
    ? dialogWeapons.system.range.value + Math.floor(gridDistance / 2)
    : dialogWeapons.system.range.value;
if (reach > 5) distance = distance + Number(reach) - 5;
const angle = dialogPoint.includes('Mid') ? 180 : 270;
const templateData = {
    borderColor: game.user.color,
    distance,
    fillColor: '#000000',
    user: game.user.id,
    t: 'cone',
    angle,
    x,
    y,
    direction,
};

const [template] = await canvas.scene.createEmbeddedDocuments(
    'MeasuredTemplate',
    [templateData]
);
await warpgate.wait(250);

let targetTokens = MidiQOL.selectTargetsForTemplate(template.object, true);
targetTokens = targetTokens.filter(
    (t) => t.document.disposition !== token.document.disposition
);

const targetTokensIDs = targetTokens.map((t) => t.id);
if (!targetTokensIDs.length) {
    ui.notifications.warn(`No valid targets for ${item.name}`);
    return afterMath(template);
}

let { value: rangeValue } = deepClone(dialogWeapons.system.range) || {};
if (reach >= 5) rangeValue = rangeValue + Number(reach) - 5;
game.user.updateTokenTargets(targetTokensIDs);
game.user.broadcastActivity({ targetTokensIDs });

const itemToRoll = dialogWeapons.clone(
    {
        'system.target': {
            value: targetTokens.length,
            width: null,
            units: '',
            type: 'creature',
        },
        'system.range.value': rangeValue,
        'flags.autoanimations.killAnim': true,
    },
    { keepId: true }
);

await MidiQOL.completeItemUse(
    itemToRoll,
    {},
    { targetUuids: targetTokens.map((t) => t.document.uuid) }
);

new Sequence()
    .effect()
        .atLocation(token)
        .center()
        .rotateTowards({x:template.x,y:template.y})
        .scaleToObject(12)

        .file("jb2a.melee_generic.slash.01.orange")
     .sound()
        .file("/Sounds/Combat/Weapons/Heavy_Slashing/*.ogg")
    .sound()
        .file("/Sounds/Combat/Weapons/Weapon_Swing_*.ogg")
    .play()

afterMath(template);

//---Functions---\\

async function afterMath(template) {
    game.user.updateTokenTargets();
    await template?.delete();
}
chrisk123999 commented 1 year ago

Head into the homebrew settings and enable weapon actions there, don't directly add the features out of the compendium. That will enable the automatic feature addition and removal. You can fill in the item descriptions in the journal just like all other temporary items from this module for proper descriptions. Turn on my VAE setting and it'll even fill in the effect descriptions too. All the features should be able to be used with or without the prompt.

Janner3D commented 1 year ago

Head into the homebrew settings and enable weapon actions there, don't directly add the features out of the compendium. That will enable the automatic feature addition and removal. You can fill in the item descriptions in the journal just like all other temporary items from this module for proper descriptions. Turn on my VAE setting and it'll even fill in the effect descriptions too. All the features should be able to be used with or without the prompt.

Ah, thank you for the clarification! I'll remove some of the suggestions since those are alrready implimented haha.