Skip to content

Commit

Permalink
beta support for rolling through Foundry with /attack
Browse files Browse the repository at this point in the history
  • Loading branch information
ChunkLightTuna committed Aug 13, 2024
1 parent ddee1dc commit b5e5698
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 87 deletions.
97 changes: 34 additions & 63 deletions src/combat.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {autoResizeApplicationExisting, item_roll, Logger} from "./util.mjs";
import {autoResizeApplicationExisting, Logger} from "./util.mjs";
import {combat_hooks, socket} from "./module.mjs";
import {COMBAT_ENABLED, COMBAT_HEALTH_ESTIMATE, COMBAT_HEALTH_ESTIMATE_TYPE, ID_MAP, MODULE_ID} from "./constants.mjs";
import {COMBAT_ENABLED, COMBAT_HEALTH_ESTIMATE, COMBAT_HEALTH_ESTIMATE_TYPE, MODULE_ID} from "./constants.mjs";
import {actor_to_discord_ids} from "./sync.mjs";

const on_combat_start = async (combat, updateData) => {
Expand Down Expand Up @@ -205,24 +205,27 @@ export function register_combat_settings_toggle() {


export function handle_incoming_rolls() {
socket.on('roll', async data => {
socket.on('roll', async (data, callback) => {
const actor = game.actors.find(a => a.id === data.actor_id)
if (actor === undefined) {
Logger.error(game.i18n.localize("oronder.Actor-Not-Found"))
return
}

const foundry_user_ids = Object.entries(game.settings.get(MODULE_ID, ID_MAP))
.filter(([_, v]) => v === data.discord_id)
.map(([k, _]) => k)

const actor_owners = Object.entries(actor.ownership)
.filter(([_, ownership_level]) => ownership_level === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
.map(([user_id, _]) => user_id)

const user_id = foundry_user_ids.filter(
user_id => actor_owners.includes(user_id)
)[0] || game.userId
// const foundry_user_ids = Object.entries(game.settings.get(MODULE_ID, ID_MAP))
// .filter(([_, v]) => v === data.discord_id)
// .map(([k, _]) => k)
//
// const actor_owners = Object.entries(actor.ownership)
// .filter(([_, ownership_level]) => ownership_level === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
// .map(([user_id, _]) => user_id)
//
// const user_id = foundry_user_ids.find(
// user_id => actor_owners.includes(user_id)
// ) || game.userId
//
// const user_name = game.users.players.find(p => p.id === user_id).name
// const flavor = `Sent via Oronder from ${user_name}.`

switch (data['type']) {
case 'stat':
Expand All @@ -231,68 +234,36 @@ export function handle_incoming_rolls() {
break
case 'attack':
Logger.info(`Attack Roll`)
//Actors who haven't been synced after 3/27/24 may only have reference to item name and not id
const item_match_fun = data?.item_id ?
i => i.id === data.item_id :
i => i.name === data.item_name

const item = actor.items.find(item_match_fun)
const item = actor.items.find(i => i.id === data.item_id)

if (item === undefined) {
Logger.error(game.i18n.localize("oronder.Item-Not-Found"))
return
}

//TODO: we want to use the roll from discord, but for now just focusing on formatting
const roll = item_roll(item)

await roll.toMessage({
user: game.user.id,
rollMode: 'roll',
speaker: ChatMessage.getSpeaker({actor}),
content: await renderTemplate('systems/dnd5e/templates/chat/item-card.hbs', {
user: game.user,
actor,
item,
data: item.getRollData(),
hasAttack: item.hasAttack,
hasDamage: item.hasDamage,
isHealing: item.isHealing,
rollType: item.system.actionType || 'Attack',
fullContext: true
})
})
let template = await renderTemplate('systems/dnd5e/templates/chat/item-card.hbs', {
actor,
item
})
await roll.toMessage({
user: game.user.id,
rollMode: 'roll',
speaker: ChatMessage.getSpeaker({actor}),
const atk = await item.rollAttack({
fastForward: true,
advantage: data.advantage || false,
disadvantage: data.disadvantage || false
})


const item_html = await renderTemplate(
'systems/dnd5e/templates/chat/item-card.hbs',
{actor, item}
)
const roll_html = await roll.render()
const spell_level = item.type === 'spell' ? Math.max(data.spell_level, item.system.level) : null
const versatile = item.type === 'weapon' ? data.versatile : null

await roll.toMessage({
speaker: ChatMessage.getSpeaker({actor}),
user: user_id,
content: [item_html, roll_html].join('\n')
const dmg = await item.rollDamage({
options: {
fastForward: true
},
critical: atk.isCritical,
spellLevel: spell_level,
versatile: versatile
})

await ChatMessage.create({
user: game.user.id,

// flavor: `${actor.name} attacks with ${itemName}!`,
rolls: [(await roll.roll()).toJSON()],
type: CONST.CHAT_MESSAGE_TYPES.ROLL
callback({
atk: `${atk.formula} = \`${atk.total}\``,
dmg: `${dmg.formula} = \`${dmg.total}\``
})

break
}
})
Expand Down
50 changes: 26 additions & 24 deletions src/sync.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,22 @@ function gen_item_deets(item, actor_lvl) {
return [damage, part[1]]
})

return {
const out = {
name: item.name,
id: item.id,
damage: damage_type_pair,
attack: attack.formula,
type: item.type
type: item.type,
img: fix_relative_url(item.img),
ability: item.system.abilityMod,
}

if (item.type === 'spell') {
out.level = item.system.level
out.level_scaling = item.system.scaling.mode === 'level'
}

return out
}

function fix_relative_url(url) {
Expand All @@ -108,12 +117,6 @@ export function enrich_actor(actor) {
.filter(item => item.type === "equipment" && item.system?.rarity)
.map(item => item.name)

const clone_spells = JSON.parse(JSON.stringify(
actor.items.filter(item => item.type === 'spell')
)).map(spell =>
(({name, img, system}) => ({name, img: fix_relative_url(img), ability: system.ability}))(spell)
)


let currency = actor.system['currency']
for (const key in currency) {
Expand Down Expand Up @@ -156,8 +159,7 @@ export function enrich_actor(actor) {
discord_ids: actor_to_discord_ids(actor),
weapons: weapons,
equipment: equipment,
portrait_url: portrait_url,
spells: clone_spells
portrait_url: portrait_url
}
}

Expand Down Expand Up @@ -200,20 +202,20 @@ export const actor_to_discord_ids = actor =>


export async function full_sync(clear_cache) {
if (clear_cache) {
game.actors
.filter(_ => localStorage.getItem(`${ACTORS}.${_.id}`))
.forEach(_ => localStorage.removeItem(`${ACTORS}.${_.id}`))
}

return Promise.all(game.actors.map(sync_actor)).then(res => {
const counts = Object.fromEntries(Object.entries(
Object.groupBy(res.map(i => Boolean(i)), b => b)
).map(([b, v]) => [b, v.length]))
const sync_count = counts[true] || 0
const skipped = counts[false] ? `, skipped ${counts[false] || 0}` : ''
Logger.warn(`Synced ${sync_count} actor${sync_count > 1 ? 's' : ''}${skipped}. Press F12 for details.`)
})
if (clear_cache) {
game.actors
.filter(_ => localStorage.getItem(`${ACTORS}.${_.id}`))
.forEach(_ => localStorage.removeItem(`${ACTORS}.${_.id}`))
}

return Promise.all(game.actors.map(sync_actor)).then(res => {
const counts = Object.fromEntries(Object.entries(
Object.groupBy(res.map(i => Boolean(i)), b => b)
).map(([b, v]) => [b, v.length]))
const sync_count = counts[true] || 0
const skipped = counts[false] ? `, skipped ${counts[false] || 0}` : ''
Logger.warn(`Synced ${sync_count} actor${sync_count > 1 ? 's' : ''}${skipped}. Press F12 for details.`)
})
}

export async function sync_actor(actor) {
Expand Down

0 comments on commit b5e5698

Please sign in to comment.