Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indexed moves #1023

Merged
merged 5 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Next Release

- Brought back support for the "Custom Moves" folder ([#1023](https://github.com/ben/foundry-ironsworn/pull/1023))

## 1.24.0

This is a major update that includes Sundered Isles content, but also brings along a host of changes:
Expand Down
7 changes: 5 additions & 2 deletions src/module/datasworn2/finding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,13 @@ interface PackAndIndex {

export async function getPackAndIndexForCompendiumKey(
ruleset: DataswornRulesetKey,
type: keyof typeof COMPENDIUM_KEY_MAP
type: keyof typeof COMPENDIUM_KEY_MAP,
additionalFields?: string[]
): Promise<PackAndIndex> {
const pack = game.packs.get(COMPENDIUM_KEY_MAP[type][ruleset])
const index = await pack?.getIndex({ fields: ['flags'] })
const index = await pack?.getIndex({
fields: ['flags', ...((additionalFields ?? []) as any[])]
})
return { pack, index }
}

Expand Down
57 changes: 53 additions & 4 deletions src/module/features/custommoves.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
} from '../datasworn2'
import { DataswornRulesetKey, IronswornSettings } from '../helpers/settings'
import type { Move, MoveCategory } from '@datasworn/core/dist/Datasworn'
import { moveTriggerIsRollable } from '../rolls/preroll-dialog'
import { compact } from 'lodash-es'
import { IronswornItem } from '../item/item'
import { SFMoveModel } from '../item/subtypes/sfmove'

interface DisplayMoveRuleset {
displayName: string
Expand All @@ -22,14 +26,20 @@ export interface DisplayMove {
color: string | null
displayName: string
uuid: string
triggerText?: string
isRollable: boolean
oracles: string[]
ds?: Move
}

const INDEXES: Record<string, any> = {}
async function ensureIndex(rsKey: DataswornRulesetKey) {
const compendiumKey = COMPENDIUM_KEY_MAP.move[rsKey]
if (INDEXES[compendiumKey] == null) {
const { index } = await getPackAndIndexForCompendiumKey(rsKey, 'move')
const { index } = await getPackAndIndexForCompendiumKey(rsKey, 'move', [
'system.Trigger',
'system.dsOracleIds'
])
INDEXES[compendiumKey] = index
}
}
Expand All @@ -55,19 +65,58 @@ export async function createMoveTreeForRuleset(
color: move.color ?? null,
displayName: move.name,
uuid: indexEntry.uuid, // TODO: move.uuid
triggerText: indexEntry.system?.Trigger?.Text,
isRollable: moveTriggerIsRollable(indexEntry?.system?.Trigger),
oracles: indexEntry.system?.dsOracleIds ?? [],
ds: move
}
})
}))
}
}

function customFolderMoveCategory(): DisplayMoveRuleset | undefined {
const name = game.i18n.localize('IRONSWORN.MOVES.Custom Moves')
const rootFolder = game.items?.directory?.folders.find((x) => x.name === name)
if (!rootFolder) return undefined

const category: DisplayMoveCategory = {
displayName: name,
color: (rootFolder as any).color?.css ?? null,
moves: []
}

for (const item of rootFolder.contents) {
if (!(item instanceof IronswornItem)) continue
if (item.type !== 'sfmove') continue
const system = item.system as SFMoveModel

category.moves.push({
displayName: item.name ?? '(unnamed)',
uuid: item.uuid,
color: null,
isRollable: moveTriggerIsRollable(system.Trigger),
oracles: system.dsOracleIds ?? [],
triggerText: system.Trigger?.Text
})
}
if (category.moves.length === 0) return undefined

return {
displayName: name,
categories: [category]
}
}

export async function createMergedMoveTree(): Promise<DisplayMoveRuleset[]> {
// Pre-load compendium indexes
await Promise.all(IronswornSettings.enabledRulesets.map(ensureIndex))
return await Promise.all(
IronswornSettings.enabledRulesets.map(createMoveTreeForRuleset)
)
return compact([
...(await Promise.all(
IronswornSettings.enabledRulesets.map(createMoveTreeForRuleset)
)),
customFolderMoveCategory()
])
}

// TODO dataforged has a key for move colours...., but they appear to have changed significantly since the last time i updated them! they'll be fixed for 2.0, but until then, here's a workaround.
Expand Down
6 changes: 2 additions & 4 deletions src/module/rolls/preroll-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,8 @@ function rollableOptions(trigger: SFMoveTrigger) {
)
}

export function moveHasRollableOptions(move: IronswornItem<'sfmove'>) {
if (!move.assert('sfmove')) return false
const options = rollableOptions(move.system.Trigger)
return options.length > 0
export function moveTriggerIsRollable(trigger?: SFMoveTrigger) : boolean {
return !!trigger && rollableOptions(trigger).length > 0
}

export function getStatData(
Expand Down
14 changes: 7 additions & 7 deletions src/module/vue/components/buttons/btn-sendmovetochat.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@
</template>

<script setup lang="ts">
import { inject } from 'vue'
import { createSfMoveChatMessage } from '../../../chat/sf-move-chat-message'
import type { DisplayMove } from '../../../features/custommoves'
import { $ItemKey } from '../../provisions.js'
import IronBtn from './iron-btn.vue'
import { IronswornItem } from '../../../item/item'

interface Props
extends /* @vue-ignore */ Omit<PropsOf<typeof IronBtn>, 'tooltip'> {
move: DisplayMove
}

defineProps<Props>()
const props = defineProps<Props>()

const $item = inject($ItemKey)

function sendToChat(e) {
if ($item) createSfMoveChatMessage($item)
async function sendToChat(e) {
const foundryMove = (await fromUuid(
props.move.uuid
)) as IronswornItem<'sfmove'>
if (foundryMove) createSfMoveChatMessage(foundryMove)
}
</script>
71 changes: 71 additions & 0 deletions src/module/vue/components/move/move-content.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<template>
<RulesTextMove
:data="moveObj"
:is-progress-move="foundryMove.system.isProgressMove"
:class="$style.summary"
>
<template #after-footer>
<OracleTreeNode
v-for="node of oracleNodes"
:key="node.displayName"
:class="$style.oracle"
:node="node"
/>
</template>
</RulesTextMove>
</template>

<script setup lang="ts">
import { ref, provide } from 'vue'
import { uniq, compact } from 'lodash-es'
import { IronswornItem } from '../../../item/item'
import { OracleTable } from '../../../roll-table/oracle-table'
import { ItemKey, $ItemKey } from '../../provisions.js'
import type { DisplayMove } from '../../../features/custommoves'
import type { IOracleTreeNode } from '../../../features/customoracles'

import RulesTextMove from '../rules-text/rules-text-move.vue'
import OracleTreeNode from '../oracle-tree-node.vue'

const props = defineProps<{
move: DisplayMove
}>()

const foundryMove = (await fromUuid(props.move.uuid)) as IronswornItem<'sfmove'>
const moveObj = ref(foundryMove.toObject())
provide(ItemKey, moveObj as any)
provide($ItemKey, foundryMove)

const oracleDsIds = uniq([
...(foundryMove?.system?.dsOracleIds ?? []),
...Object.values(props.move.ds?.oracles ?? {}).map((x) => x._id)
])
const oracleNodes: IOracleTreeNode[] = await Promise.all(
oracleDsIds.map(async (oid) => {
const t = await OracleTable.getByDsId(oid)
return {
displayName: t?.name ?? '(missing)',
tables: compact([t?.uuid]),
children: []
}
})
)
</script>

<style lang="scss" module>
.oracle {
border-width: var(--ironsworn-border-width-md);
border-style: solid;
border-radius: var(--ironsworn-border-radius-sm);
border-color: var(--ironsworn-color-border);
padding: 0;

h4 {
font-size: var(--font-size-16);

button.icon-button {
height: inherit;
}
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class="movesheet-row"
:class="$style.wrapper"
data-tooltip-direction="LEFT"
:base-id="`move_row_${item._id}`"
:base-id="`move_row_${move.uuid}`"
:content-wrapper-class="$style.contentWrapper"
:toggle-wrapper-is="`h${headingLevel}`"
:toggle-section-class="[$style.toggleSection, toggleSectionClass]"
Expand All @@ -15,8 +15,7 @@
:toggle-wrapper-class="$style.toggleWrapper"
:toggle-label="move?.displayName"
:data-highlighted="dataHighlight"
:data-move-id="item._id"
:data-move-uuid="$item.uuid"
:data-move-uuid="move.uuid"
>
<template #after-toggle>
<section
Expand All @@ -28,10 +27,10 @@
<slot name="controls">
<slot
name="btn-roll-move"
v-bind="{ disabled: !canRoll, move, class: $style.btn }"
v-bind="{ disabled: !move.isRollable, move, class: $style.btn }"
>
<BtnRollmove
:disabled="!canRoll"
:disabled="!move.isRollable"
:move="move"
:class="$style.btn"
/>
Expand All @@ -57,40 +56,23 @@
</section>
</template>
<template #default>
<RulesTextMove
:data="item"
:is-progress-move="$item.system.isProgressMove"
:class="$style.summary"
>
<template #after-footer>
<OracleTreeNode
v-for="node of oracleNodes"
:key="node.displayName"
:class="$style.oracle"
:node="node"
/>
</template>
</RulesTextMove>
<MoveContent :move="move" />
</template>
</Collapsible>
</template>

<script setup lang="ts">
import { computed, onMounted, provide, ref, watch } from 'vue'
import type { DisplayMove } from '../../features/custommoves'
import type { IOracleTreeNode } from '../../features/customoracles'
import type { IronswornItem } from '../../item/item'
import { moveHasRollableOptions } from '../../rolls/preroll-dialog'
import BtnRollmove from './buttons/btn-rollmove.vue'
import BtnSendmovetochat from './buttons/btn-sendmovetochat.vue'
import OracleTreeNode from './oracle-tree-node.vue'
import RulesTextMove from './rules-text/rules-text-move.vue'
import Collapsible from './collapsible/collapsible.vue'
import BtnOracle from './buttons/btn-oracle.vue'
import { ItemKey, $ItemKey } from '../provisions.js'
import { enrichMarkdown } from '../vue-plugin.js'
import { compact, uniq } from 'lodash-es'
import { OracleTable } from '../../roll-table/oracle-table'
import { computed, onMounted, ref, watch } from 'vue'
import type { DisplayMove } from '../../../features/custommoves'
import type { IOracleTreeNode } from '../../../features/customoracles'
import { compact } from 'lodash-es'
import { OracleTable } from '../../../roll-table/oracle-table'

import BtnRollmove from '../buttons/btn-rollmove.vue'
import BtnSendmovetochat from '../buttons/btn-sendmovetochat.vue'
import Collapsible from '../collapsible/collapsible.vue'
import BtnOracle from '../buttons/btn-oracle.vue'
import MoveContent from './move-content.vue'

const props = withDefaults(
defineProps<{
Expand Down Expand Up @@ -125,20 +107,10 @@ const props = withDefaults(
}
)

const $item = (await fromUuid(props.move.uuid)) as IronswornItem<'sfmove'>
const item = ref($item.toObject())

provide(ItemKey, item as any)
provide($ItemKey, $item)

const $collapsible = ref<typeof Collapsible>()

const oracleDsIds = uniq([
...($item?.system?.dsOracleIds ?? []),
...Object.values(props.move.ds?.oracles ?? {}).map((x) => x._id)
])
const oracleNodes: IOracleTreeNode[] = await Promise.all(
oracleDsIds.map(async (oid) => {
props.move.oracles.map(async (oid) => {
const t = await OracleTable.getByDsId(oid)
return {
displayName: t?.name ?? '(missing)',
Expand All @@ -148,15 +120,11 @@ const oracleNodes: IOracleTreeNode[] = await Promise.all(
})
)

const canRoll = computed(() => {
return moveHasRollableOptions($item)
})
const preventOracle = computed(() => {
return oracleNodes.length !== 1
})

const toggleTooltip = ref($item.system.Trigger?.Text)
enrichMarkdown(toggleTooltip.value).then((x) => (toggleTooltip.value = x))
const toggleTooltip = props.move.triggerText

const dataHighlight = ref(false)
async function flashHighlight() {
Expand Down
2 changes: 1 addition & 1 deletion src/module/vue/components/sf-move-category-rows.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import type { DisplayMoveCategory } from '../../features/custommoves.js'
import SfMoverow from './sf-moverow.vue'
import SfMoverow from './move/sf-moverow.vue'
import Collapsible from './collapsible/collapsible.vue'
import { snakeCase } from 'lodash-es'
import { enrichMarkdown } from '../vue-plugin'
Expand Down
2 changes: 1 addition & 1 deletion src/module/vue/components/sf-movesheetmoves.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import { computed, nextTick, provide, reactive, ref } from 'vue'
import type { DisplayMoveCategory } from '../../features/custommoves'
import { createMergedMoveTree } from '../../features/custommoves'
import SfMoveCategoryRows from './sf-move-category-rows.vue'
import SfMoverow from './sf-moverow.vue'
import SfMoverow from './move/sf-moverow.vue'
import IronBtn from './buttons/iron-btn.vue'

const state = reactive({
Expand Down
2 changes: 1 addition & 1 deletion src/module/vue/components/site/site-moves.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ import { $ActorKey, ActorKey } from '../../provisions'
import BtnOracle from '../buttons/btn-oracle.vue'
import BtnRollmove from '../buttons/btn-rollmove.vue'

import SfMoverow from '../sf-moverow.vue'
import SfMoverow from '../move/sf-moverow.vue'

const site = inject(ActorKey) as Ref<ActorSource<'site'>>
const $site = inject($ActorKey) as IronswornActor<'site'>
Expand Down
Loading