fix: check available adventure points when adding rated entries
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Filter and count `ActivatableSkill` entries.
|
||||
*
|
||||
* @author Lukas Obermann
|
||||
*/
|
||||
|
||||
import { filter, OrderedMap, size } from "../../../Data/OrderedMap"
|
||||
import { Record } from "../../../Data/Record"
|
||||
import { ActivatableSkillDependent } from "../../Models/ActiveEntries/ActivatableSkillDependent"
|
||||
import { HeroModel, HeroModelRecord } from "../../Models/Hero/HeroModel"
|
||||
import { pipe } from "../pipe"
|
||||
|
||||
type ActivatableSkillEntriesAccessor =
|
||||
(hero: HeroModelRecord) => OrderedMap<string, Record<ActivatableSkillDependent>>
|
||||
|
||||
const { active } = ActivatableSkillDependent.AL
|
||||
|
||||
/**
|
||||
* Get all active `ActivatableSkillDependent` entries from the specified domain.
|
||||
*/
|
||||
export const getActiveSkillEntries =
|
||||
(domain: "spells" | "liturgicalChants") =>
|
||||
pipe (
|
||||
HeroModel.AL[domain] as ActivatableSkillEntriesAccessor,
|
||||
filter (active)
|
||||
)
|
||||
|
||||
/**
|
||||
* Count all active skills from the specified domain.
|
||||
*/
|
||||
export const countActiveSkillEntries =
|
||||
(domain: "spells" | "liturgicalChants") =>
|
||||
pipe (
|
||||
getActiveSkillEntries (domain),
|
||||
size
|
||||
)
|
||||
@@ -0,0 +1,286 @@
|
||||
import { ActionCreatorWithPayload } from "@reduxjs/toolkit"
|
||||
import { useCallback } from "react"
|
||||
import {
|
||||
ImprovementCost,
|
||||
adventurePointsForActivation,
|
||||
adventurePointsForIncrement,
|
||||
adventurePointsForRange,
|
||||
} from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { useTranslate } from "../../shared/hooks/translate.ts"
|
||||
import { isOk } from "../../shared/utils/result.ts"
|
||||
import { Translate } from "../../shared/utils/translate.ts"
|
||||
import { showAlert } from "../slices/alertsSlice.ts"
|
||||
import {
|
||||
getAlertForReason,
|
||||
useAreEnoughAdventurePointsAvailableToBuy,
|
||||
} from "./areEnoughAdventurePointsAvailableToBuy.ts"
|
||||
import { useAppDispatch } from "./redux.ts"
|
||||
|
||||
const useHandleAdd = (
|
||||
id: number,
|
||||
improvementCost: ImprovementCost,
|
||||
createAddAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
dispatch: ReturnType<typeof useAppDispatch>,
|
||||
areEnoughAdventurePointsAvailableToBuy: ReturnType<
|
||||
typeof useAreEnoughAdventurePointsAvailableToBuy
|
||||
>,
|
||||
translate: Translate,
|
||||
) =>
|
||||
useCallback((): void => {
|
||||
const enoughAP = areEnoughAdventurePointsAvailableToBuy(
|
||||
adventurePointsForActivation(improvementCost),
|
||||
)
|
||||
|
||||
if (enoughAP !== undefined) {
|
||||
if (isOk(enoughAP)) {
|
||||
dispatch(createAddAction({ id }))
|
||||
} else {
|
||||
dispatch(showAlert(getAlertForReason(enoughAP.error, translate)))
|
||||
}
|
||||
}
|
||||
}, [
|
||||
areEnoughAdventurePointsAvailableToBuy,
|
||||
createAddAction,
|
||||
dispatch,
|
||||
id,
|
||||
improvementCost,
|
||||
translate,
|
||||
])
|
||||
|
||||
const useHandleAddPoint = (
|
||||
id: number,
|
||||
value: number,
|
||||
improvementCost: ImprovementCost,
|
||||
createIncrementAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
dispatch: ReturnType<typeof useAppDispatch>,
|
||||
areEnoughAdventurePointsAvailableToBuy: ReturnType<
|
||||
typeof useAreEnoughAdventurePointsAvailableToBuy
|
||||
>,
|
||||
translate: Translate,
|
||||
) =>
|
||||
useCallback((): void => {
|
||||
const enoughAP = areEnoughAdventurePointsAvailableToBuy(
|
||||
adventurePointsForIncrement(improvementCost, value),
|
||||
)
|
||||
|
||||
if (enoughAP !== undefined) {
|
||||
if (isOk(enoughAP)) {
|
||||
dispatch(createIncrementAction({ id }))
|
||||
} else {
|
||||
dispatch(showAlert(getAlertForReason(enoughAP.error, translate)))
|
||||
}
|
||||
}
|
||||
}, [
|
||||
areEnoughAdventurePointsAvailableToBuy,
|
||||
createIncrementAction,
|
||||
dispatch,
|
||||
id,
|
||||
improvementCost,
|
||||
translate,
|
||||
value,
|
||||
])
|
||||
|
||||
const useHandleSetToMaximumPoints = (
|
||||
id: number,
|
||||
value: number,
|
||||
maximum: number,
|
||||
improvementCost: ImprovementCost,
|
||||
createSetAction: ActionCreatorWithPayload<{ id: number; value: number }>,
|
||||
dispatch: ReturnType<typeof useAppDispatch>,
|
||||
areEnoughAdventurePointsAvailableToBuy: ReturnType<
|
||||
typeof useAreEnoughAdventurePointsAvailableToBuy
|
||||
>,
|
||||
translate: Translate,
|
||||
) =>
|
||||
useCallback((): void => {
|
||||
const enoughAP = areEnoughAdventurePointsAvailableToBuy(
|
||||
adventurePointsForRange(improvementCost, value, maximum),
|
||||
)
|
||||
|
||||
if (enoughAP !== undefined) {
|
||||
if (isOk(enoughAP)) {
|
||||
dispatch(createSetAction({ id, value: maximum }))
|
||||
} else {
|
||||
dispatch(showAlert(getAlertForReason(enoughAP.error, translate)))
|
||||
}
|
||||
}
|
||||
}, [
|
||||
areEnoughAdventurePointsAvailableToBuy,
|
||||
createSetAction,
|
||||
dispatch,
|
||||
id,
|
||||
improvementCost,
|
||||
maximum,
|
||||
translate,
|
||||
value,
|
||||
])
|
||||
|
||||
const useHandleSetToMinimumPoints = (
|
||||
id: number,
|
||||
minimum: number,
|
||||
createSetAction: ActionCreatorWithPayload<{ id: number; value: number }>,
|
||||
dispatch: ReturnType<typeof useAppDispatch>,
|
||||
) =>
|
||||
useCallback((): void => {
|
||||
dispatch(createSetAction({ id, value: minimum }))
|
||||
}, [createSetAction, dispatch, id, minimum])
|
||||
|
||||
const useHandleRemovePoint = (
|
||||
id: number,
|
||||
createDecrementAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
dispatch: ReturnType<typeof useAppDispatch>,
|
||||
) =>
|
||||
useCallback(() => {
|
||||
dispatch(createDecrementAction({ id }))
|
||||
}, [createDecrementAction, dispatch, id])
|
||||
|
||||
const useHandleRemove = (
|
||||
id: number,
|
||||
createRemoveAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
dispatch: ReturnType<typeof useAppDispatch>,
|
||||
) =>
|
||||
useCallback(() => {
|
||||
dispatch(createRemoveAction({ id }))
|
||||
}, [createRemoveAction, dispatch, id])
|
||||
|
||||
/**
|
||||
* Returns a set of callbacks for interaction with rated entries.
|
||||
*/
|
||||
export const useRatedActions = (
|
||||
id: number,
|
||||
value: number,
|
||||
maximum: number,
|
||||
minimum: number,
|
||||
improvementCost: ImprovementCost,
|
||||
createIncrementAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
createDecrementAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
createSetAction: ActionCreatorWithPayload<{ id: number; value: number }>,
|
||||
) => {
|
||||
const dispatch = useAppDispatch()
|
||||
const areEnoughAdventurePointsAvailableToBuy = useAreEnoughAdventurePointsAvailableToBuy()
|
||||
const translate = useTranslate()
|
||||
|
||||
const handleAddPoint = useHandleAddPoint(
|
||||
id,
|
||||
value,
|
||||
improvementCost,
|
||||
createIncrementAction,
|
||||
dispatch,
|
||||
areEnoughAdventurePointsAvailableToBuy,
|
||||
translate,
|
||||
)
|
||||
|
||||
const handleSetToMaximumPoints = useHandleSetToMaximumPoints(
|
||||
id,
|
||||
value,
|
||||
maximum,
|
||||
improvementCost,
|
||||
createSetAction,
|
||||
dispatch,
|
||||
areEnoughAdventurePointsAvailableToBuy,
|
||||
translate,
|
||||
)
|
||||
|
||||
const handleSetToMinimumPoints = useHandleSetToMinimumPoints(
|
||||
id,
|
||||
minimum,
|
||||
createSetAction,
|
||||
dispatch,
|
||||
)
|
||||
|
||||
const handleRemovePoint = useHandleRemovePoint(id, createDecrementAction, dispatch)
|
||||
|
||||
return {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of callbacks for interaction with active activatable rated
|
||||
* entries.
|
||||
*/
|
||||
export const useActiveActivatableActions = (
|
||||
id: number,
|
||||
value: number,
|
||||
maximum: number,
|
||||
minimum: number,
|
||||
improvementCost: ImprovementCost,
|
||||
createIncrementAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
createDecrementAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
createSetAction: ActionCreatorWithPayload<{ id: number; value: number }>,
|
||||
createRemoveAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
) => {
|
||||
const dispatch = useAppDispatch()
|
||||
const areEnoughAdventurePointsAvailableToBuy = useAreEnoughAdventurePointsAvailableToBuy()
|
||||
const translate = useTranslate()
|
||||
|
||||
const handleAddPoint = useHandleAddPoint(
|
||||
id,
|
||||
value,
|
||||
improvementCost,
|
||||
createIncrementAction,
|
||||
dispatch,
|
||||
areEnoughAdventurePointsAvailableToBuy,
|
||||
translate,
|
||||
)
|
||||
|
||||
const handleSetToMaximumPoints = useHandleSetToMaximumPoints(
|
||||
id,
|
||||
value,
|
||||
maximum,
|
||||
improvementCost,
|
||||
createSetAction,
|
||||
dispatch,
|
||||
areEnoughAdventurePointsAvailableToBuy,
|
||||
translate,
|
||||
)
|
||||
|
||||
const handleSetToMinimumPoints = useHandleSetToMinimumPoints(
|
||||
id,
|
||||
minimum,
|
||||
createSetAction,
|
||||
dispatch,
|
||||
)
|
||||
|
||||
const handleRemovePoint = useHandleRemovePoint(id, createDecrementAction, dispatch)
|
||||
|
||||
const handleRemove = useHandleRemove(id, createRemoveAction, dispatch)
|
||||
|
||||
return {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of callbacks for interaction with inactive activatable rated
|
||||
* entries.
|
||||
*/
|
||||
export const useInactiveActivatableActions = (
|
||||
id: number,
|
||||
improvementCost: ImprovementCost,
|
||||
createAddAction: ActionCreatorWithPayload<{ id: number }>,
|
||||
) => {
|
||||
const dispatch = useAppDispatch()
|
||||
const areEnoughAdventurePointsAvailableToBuy = useAreEnoughAdventurePointsAvailableToBuy()
|
||||
const translate = useTranslate()
|
||||
|
||||
const handleAdd = useHandleAdd(
|
||||
id,
|
||||
improvementCost,
|
||||
createAddAction,
|
||||
dispatch,
|
||||
areEnoughAdventurePointsAvailableToBuy,
|
||||
translate,
|
||||
)
|
||||
|
||||
return {
|
||||
handleAdd,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC } from "react"
|
||||
import { FC, useMemo } from "react"
|
||||
import { useLocaleCompare } from "../../../shared/hooks/localeCompare.ts"
|
||||
import { useTranslate } from "../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../shared/hooks/translateMap.ts"
|
||||
@@ -6,6 +6,7 @@ import { isNotNullish } from "../../../shared/utils/nullable.ts"
|
||||
import { assertExhaustive } from "../../../shared/utils/typeSafety.ts"
|
||||
import { useAppSelector } from "../../hooks/redux.ts"
|
||||
import {
|
||||
selectNewApplicationsAndUsesCache,
|
||||
selectStaticAttributes,
|
||||
selectStaticBlessedTraditions,
|
||||
selectStaticDiseases,
|
||||
@@ -37,17 +38,30 @@ export const InlineLibrarySkill: FC<Props> = ({ id }) => {
|
||||
const regions = useAppSelector(selectStaticRegions)
|
||||
const entry = useAppSelector(selectStaticSkills)[id]
|
||||
const translation = translateMap(entry?.translations)
|
||||
const cache = useAppSelector(selectNewApplicationsAndUsesCache)
|
||||
|
||||
const newApplications = useMemo(
|
||||
() =>
|
||||
(entry === undefined ? [] : cache.newApplications[entry.id] ?? [])
|
||||
.map(x => translateMap(x.data.translations)?.name)
|
||||
.filter(isNotNullish)
|
||||
.sort(localeCompare),
|
||||
[cache.newApplications, entry, localeCompare, translateMap],
|
||||
)
|
||||
|
||||
const uses = useMemo(
|
||||
() =>
|
||||
(entry === undefined ? [] : cache.uses[entry.id] ?? [])
|
||||
.map(x => translateMap(x.data.translations)?.name)
|
||||
.filter(isNotNullish)
|
||||
.sort(localeCompare),
|
||||
[cache.uses, entry, localeCompare, translateMap],
|
||||
)
|
||||
|
||||
if (entry === undefined || translation === undefined) {
|
||||
return <InlineLibraryPlaceholder />
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
const newApplications = []
|
||||
|
||||
// TODO: Implement
|
||||
const uses = []
|
||||
|
||||
const applications = (() => {
|
||||
switch (entry.applications.tag) {
|
||||
case "Derived":
|
||||
@@ -86,14 +100,18 @@ export const InlineLibrarySkill: FC<Props> = ({ id }) => {
|
||||
<InlineLibraryTemplate className="Skill" title={translation?.name ?? entry.id.toString()}>
|
||||
<InlineLibraryProperties
|
||||
list={[
|
||||
{
|
||||
label: translate("New Applications"),
|
||||
value: newApplications.join(", "),
|
||||
},
|
||||
{
|
||||
label: translate("Uses"),
|
||||
value: uses.join(", "),
|
||||
},
|
||||
newApplications.length === 0
|
||||
? undefined
|
||||
: {
|
||||
label: translate("New Applications"),
|
||||
value: newApplications.join(", "),
|
||||
},
|
||||
uses.length === 0
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Uses"),
|
||||
value: uses.join(", "),
|
||||
},
|
||||
createCheck(translate, translateMap, attributes, entry.check),
|
||||
{
|
||||
label: translate("Applications"),
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import { FC, useCallback } from "react"
|
||||
import { FC, MouseEvent, useCallback } from "react"
|
||||
import { IconButton } from "../../../../../shared/components/iconButton/IconButton.tsx"
|
||||
import { NumberBox } from "../../../../../shared/components/numberBox/NumberBox.tsx"
|
||||
import {
|
||||
attributeImprovementCost,
|
||||
minimumAttributeValue,
|
||||
} from "../../../../../shared/domain/rated/attribute.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useAppDispatch } from "../../../../hooks/redux.ts"
|
||||
import { useRatedActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { DisplayedAttribute } from "../../../../selectors/attributeSelectors.ts"
|
||||
import { decrementAttribute, incrementAttribute } from "../../../../slices/attributesSlice.ts"
|
||||
import {
|
||||
decrementAttribute,
|
||||
incrementAttribute,
|
||||
setAttribute,
|
||||
} from "../../../../slices/attributesSlice.ts"
|
||||
import { AttributeBorder } from "./AttributeBorder.tsx"
|
||||
|
||||
type Props = {
|
||||
@@ -24,21 +32,53 @@ export const AttributeListItem: FC<Props> = props => {
|
||||
const translateMap = useTranslateMap()
|
||||
const translations = translateMap(attribute.static.translations)
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const {
|
||||
dynamic: { value },
|
||||
static: { id },
|
||||
maximum: max,
|
||||
maximum,
|
||||
isDecreasable,
|
||||
isIncreasable,
|
||||
} = attribute
|
||||
|
||||
const valueHeader = isInCharacterCreation ? `${value} / ${max}` : value
|
||||
const valueHeader = isInCharacterCreation ? `${value} / ${maximum}` : value
|
||||
|
||||
const handleAdd = useCallback(() => dispatch(incrementAttribute(id)), [dispatch, id])
|
||||
const { handleAddPoint, handleRemovePoint, handleSetToMaximumPoints, handleSetToMinimumPoints } =
|
||||
useRatedActions(
|
||||
id,
|
||||
value,
|
||||
maximum ?? value,
|
||||
minimumAttributeValue,
|
||||
attributeImprovementCost,
|
||||
incrementAttribute,
|
||||
decrementAttribute,
|
||||
setAttribute,
|
||||
)
|
||||
|
||||
const handleRemove = useCallback(() => dispatch(decrementAttribute(id)), [dispatch, id])
|
||||
const handleAdd = useCallback(
|
||||
(event: MouseEvent<HTMLButtonElement>) => {
|
||||
if (isIncreasable) {
|
||||
if (event.shiftKey && maximum !== undefined) {
|
||||
handleSetToMaximumPoints()
|
||||
} else {
|
||||
handleAddPoint()
|
||||
}
|
||||
}
|
||||
},
|
||||
[handleAddPoint, handleSetToMaximumPoints, isIncreasable, maximum],
|
||||
)
|
||||
|
||||
const handleRemove = useCallback(
|
||||
(event: MouseEvent<HTMLButtonElement>) => {
|
||||
if (isDecreasable) {
|
||||
if (event.shiftKey) {
|
||||
handleSetToMinimumPoints()
|
||||
} else {
|
||||
handleRemovePoint()
|
||||
}
|
||||
}
|
||||
},
|
||||
[handleRemovePoint, handleSetToMinimumPoints, isDecreasable],
|
||||
)
|
||||
|
||||
return (
|
||||
<AttributeBorder
|
||||
@@ -56,7 +96,7 @@ export const AttributeListItem: FC<Props> = props => {
|
||||
}
|
||||
tooltipMargin={11}
|
||||
>
|
||||
{isInCharacterCreation ? <NumberBox max={max} /> : null}
|
||||
{isInCharacterCreation ? <NumberBox max={maximum} /> : null}
|
||||
<IconButton
|
||||
className="add"
|
||||
icon=""
|
||||
|
||||
+45
-37
@@ -1,15 +1,21 @@
|
||||
import { AttributeReference } from "optolith-database-schema/types/_SimpleReferences"
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import { ListItem } from "../../../../../shared/components/list/ListItem.tsx"
|
||||
import { ListItemGroup } from "../../../../../shared/components/list/ListItemGroup.tsx"
|
||||
import { ListItemName } from "../../../../../shared/components/list/ListItemName.tsx"
|
||||
import { ListItemSeparator } from "../../../../../shared/components/list/ListItemSeparator.tsx"
|
||||
import { ListItemValues } from "../../../../../shared/components/list/ListItemValues.tsx"
|
||||
import { ImprovementCost } from "../../../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useRatedActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { selectCanRemove } from "../../../../selectors/characterSelectors.ts"
|
||||
import { DisplayedCloseCombatTechnique } from "../../../../selectors/combatTechniquesSelectors.ts"
|
||||
import {
|
||||
decrementCloseCombatTechnique,
|
||||
incrementCloseCombatTechnique,
|
||||
setCloseCombatTechnique,
|
||||
} from "../../../../slices/closeCombatTechniqueSlice.ts"
|
||||
import { selectStaticAttributes } from "../../../../slices/databaseSlice.ts"
|
||||
import {
|
||||
changeInlineLibraryEntry,
|
||||
@@ -22,33 +28,22 @@ import { SkillRating } from "../skills/SkillRating.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
id: number
|
||||
name: string
|
||||
sr: number
|
||||
primary: AttributeReference[]
|
||||
ic: ImprovementCost
|
||||
addDisabled: boolean
|
||||
removeDisabled: boolean
|
||||
at: number
|
||||
pa?: number
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
closeCombatTechnique: DisplayedCloseCombatTechnique
|
||||
}
|
||||
|
||||
const CloseCombatTechniquesListItem: FC<Props> = props => {
|
||||
const {
|
||||
insertTopMargin,
|
||||
id,
|
||||
name,
|
||||
sr,
|
||||
primary,
|
||||
ic,
|
||||
addDisabled,
|
||||
removeDisabled,
|
||||
at,
|
||||
pa,
|
||||
addPoint,
|
||||
removePoint,
|
||||
closeCombatTechnique: {
|
||||
static: { id, primary_attribute, improvement_cost, translations },
|
||||
dynamic: { value },
|
||||
maximum,
|
||||
minimum,
|
||||
isIncreasable,
|
||||
isDecreasable,
|
||||
attackBase,
|
||||
parryBase,
|
||||
},
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -57,6 +52,20 @@ const CloseCombatTechniquesListItem: FC<Props> = props => {
|
||||
const inlineLibraryEntryId = useAppSelector(selectInlineLibraryEntryId)
|
||||
const canRemove = useAppSelector(selectCanRemove)
|
||||
|
||||
const { name = "???" } = translateMap(translations) ?? {}
|
||||
|
||||
const { handleAddPoint, handleRemovePoint, handleSetToMaximumPoints, handleSetToMinimumPoints } =
|
||||
useRatedActions(
|
||||
id,
|
||||
value,
|
||||
maximum,
|
||||
minimum,
|
||||
fromRaw(improvement_cost),
|
||||
incrementCloseCombatTechnique,
|
||||
decrementCloseCombatTechnique,
|
||||
setCloseCombatTechnique,
|
||||
)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() =>
|
||||
dispatch(
|
||||
@@ -67,11 +76,11 @@ const CloseCombatTechniquesListItem: FC<Props> = props => {
|
||||
|
||||
const attributes = useAppSelector(selectStaticAttributes)
|
||||
|
||||
const primaryStr = primary
|
||||
const primaryStr = primary_attribute
|
||||
.map(ref => translateMap(attributes[ref.id.attribute]?.translations)?.abbreviation ?? "")
|
||||
.join("/")
|
||||
|
||||
const customClassName = `attr--${primary.map(ref => ref.id.attribute).join("-")}`
|
||||
const customClassName = `attr--${primary_attribute.map(ref => ref.id.attribute).join("-")}`
|
||||
|
||||
const primaryClassName = `primary ${customClassName}`
|
||||
|
||||
@@ -87,28 +96,27 @@ const CloseCombatTechniquesListItem: FC<Props> = props => {
|
||||
<ListItemSeparator />
|
||||
<ListItemGroup text={translate("Close Combat")} />
|
||||
<ListItemValues>
|
||||
<SkillRating sr={sr} addPoint={addPoint} />
|
||||
<SkillImprovementCost ic={ic} />
|
||||
<SkillRating sr={value} />
|
||||
<SkillImprovementCost ic={fromRaw(improvement_cost)} />
|
||||
<SkillAdditionalValues
|
||||
addValues={[
|
||||
{ className: primaryClassName, value: primaryStr },
|
||||
{ className: "at", value: at },
|
||||
{ className: "at", value: attackBase },
|
||||
{ className: "atpa" },
|
||||
{
|
||||
className: "pa",
|
||||
value: pa ?? "—",
|
||||
value: parryBase ?? "—",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={addDisabled}
|
||||
ic={ic}
|
||||
id={id}
|
||||
removeDisabled={removeDisabled}
|
||||
sr={sr}
|
||||
addPoint={addPoint}
|
||||
removePoint={canRemove ? removePoint : undefined}
|
||||
addDisabled={!isIncreasable}
|
||||
removeDisabled={!isDecreasable}
|
||||
addPoint={handleAddPoint}
|
||||
setToMax={handleSetToMaximumPoints}
|
||||
removePoint={canRemove ? handleRemovePoint : undefined}
|
||||
setToMin={canRemove ? handleSetToMinimumPoints : undefined}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -26,14 +26,6 @@ import {
|
||||
DisplayedCombatTechnique,
|
||||
selectVisibleCombatTechniques,
|
||||
} from "../../../../selectors/combatTechniquesSelectors.ts"
|
||||
import {
|
||||
decrementCloseCombatTechnique,
|
||||
incrementCloseCombatTechnique,
|
||||
} from "../../../../slices/closeCombatTechniqueSlice.ts"
|
||||
import {
|
||||
decrementRangedCombatTechnique,
|
||||
incrementRangedCombatTechnique,
|
||||
} from "../../../../slices/rangedCombatTechniqueSlice.ts"
|
||||
import {
|
||||
changeCombatTechniquesSortOrder,
|
||||
selectCombatTechniquesSortOrder,
|
||||
@@ -100,26 +92,6 @@ export const CombatTechniques: FC = () => {
|
||||
[filterText, localeCompare, sortOrder, translateMap, visibleCombatTechniques],
|
||||
)
|
||||
|
||||
const handleAddCloseCombatTechniquePoint = useCallback(
|
||||
(id: number) => dispatch(incrementCloseCombatTechnique(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleRemoveCloseCombatTechniquePoint = useCallback(
|
||||
(id: number) => dispatch(decrementCloseCombatTechnique(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddRangedCombatTechniquePoint = useCallback(
|
||||
(id: number) => dispatch(incrementRangedCombatTechnique(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleRemoveRangedCombatTechniquePoint = useCallback(
|
||||
(id: number) => dispatch(decrementRangedCombatTechnique(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
return (
|
||||
<Page id="combat-techniques">
|
||||
<Options>
|
||||
@@ -146,35 +118,22 @@ export const CombatTechniques: FC = () => {
|
||||
</Options>
|
||||
<Main>
|
||||
<ListHeader>
|
||||
<ListHeaderTag className="name">
|
||||
{translate("combattechniques.header.name")}
|
||||
<ListHeaderTag className="name">{translate("Name")}</ListHeaderTag>
|
||||
<ListHeaderTag className="group">{translate("Group")}</ListHeaderTag>
|
||||
<ListHeaderTag className="value" hint={translate("Combat Technique Rating")}>
|
||||
{translate("CTR")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="group">
|
||||
{translate("combattechniques.header.group")}
|
||||
<ListHeaderTag className="ic" hint={translate("Improvement Cost")}>
|
||||
{translate("IC")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag
|
||||
className="value"
|
||||
hint={translate("combattechniques.header.combattechniquerating.tooltip")}
|
||||
>
|
||||
{translate("combattechniques.header.combattechniquerating")}
|
||||
<ListHeaderTag className="primary" hint={translate("Primary Attribute(s)")}>
|
||||
{translate("P")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag
|
||||
className="ic"
|
||||
hint={translate("combattechniques.header.improvementcost.tooltip")}
|
||||
>
|
||||
{translate("combattechniques.header.improvementcost")}
|
||||
<ListHeaderTag className="at" hint={translate("Attack")}>
|
||||
{translate("AT")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag
|
||||
className="primary"
|
||||
hint={translate("combattechniques.header.primaryattribute.tooltip")}
|
||||
>
|
||||
{translate("combattechniques.header.primaryattribute")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="at" hint={translate("combattechniques.header.attack.tooltip")}>
|
||||
{translate("combattechniques.header.attack")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="pa" hint={translate("combattechniques.header.parry.tooltip")}>
|
||||
{translate("combattechniques.header.parry")}
|
||||
<ListHeaderTag className="pa" hint={translate("Parry")}>
|
||||
{translate("PA")}
|
||||
</ListHeaderTag>
|
||||
{canRemove ? <ListHeaderTag className="btn-placeholder" /> : null}
|
||||
<ListHeaderTag className="btn-placeholder" />
|
||||
@@ -184,44 +143,26 @@ export const CombatTechniques: FC = () => {
|
||||
{list.length > 0 ? (
|
||||
<List>
|
||||
{list.map((x, i) => {
|
||||
const translation = translateMap(x.static.translations)
|
||||
|
||||
if (x.kind === "close") {
|
||||
return (
|
||||
<CloseCombatTechniquesListItem
|
||||
key={`close--${x.static.id}`}
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, list[i - 1])}
|
||||
id={x.static.id}
|
||||
name={translation?.name ?? ""}
|
||||
sr={x.dynamic.value}
|
||||
primary={x.static.primary_attribute}
|
||||
ic={fromRaw(x.static.improvement_cost)}
|
||||
addDisabled={!x.isIncreasable}
|
||||
removeDisabled={!x.isDecreasable}
|
||||
at={x.attackBase}
|
||||
pa={x.parryBase}
|
||||
addPoint={handleAddCloseCombatTechniquePoint}
|
||||
removePoint={handleRemoveCloseCombatTechniquePoint}
|
||||
/>
|
||||
)
|
||||
switch (x.kind) {
|
||||
case "close":
|
||||
return (
|
||||
<CloseCombatTechniquesListItem
|
||||
key={`close--${x.static.id}`}
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, list[i - 1])}
|
||||
closeCombatTechnique={x}
|
||||
/>
|
||||
)
|
||||
case "ranged":
|
||||
return (
|
||||
<RangedCombatTechniquesListItem
|
||||
key={`ranged--${x.static.id}`}
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, list[i - 1])}
|
||||
rangedCombatTechnique={x}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return assertExhaustive(x)
|
||||
}
|
||||
|
||||
return (
|
||||
<RangedCombatTechniquesListItem
|
||||
key={`ranged--${x.static.id}`}
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, list[i - 1])}
|
||||
id={x.static.id}
|
||||
name={translation?.name ?? ""}
|
||||
sr={x.dynamic.value}
|
||||
primary={x.static.primary_attribute}
|
||||
ic={fromRaw(x.static.improvement_cost)}
|
||||
addDisabled={!x.isIncreasable}
|
||||
removeDisabled={!x.isDecreasable}
|
||||
at={x.attackBase}
|
||||
addPoint={handleAddRangedCombatTechniquePoint}
|
||||
removePoint={handleRemoveRangedCombatTechniquePoint}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
) : (
|
||||
|
||||
+43
-34
@@ -1,20 +1,26 @@
|
||||
import { AttributeReference } from "optolith-database-schema/types/_SimpleReferences"
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import { ListItem } from "../../../../../shared/components/list/ListItem.tsx"
|
||||
import { ListItemGroup } from "../../../../../shared/components/list/ListItemGroup.tsx"
|
||||
import { ListItemName } from "../../../../../shared/components/list/ListItemName.tsx"
|
||||
import { ListItemSeparator } from "../../../../../shared/components/list/ListItemSeparator.tsx"
|
||||
import { ListItemValues } from "../../../../../shared/components/list/ListItemValues.tsx"
|
||||
import { ImprovementCost } from "../../../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useRatedActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { selectCanRemove } from "../../../../selectors/characterSelectors.ts"
|
||||
import { DisplayedRangedCombatTechnique } from "../../../../selectors/combatTechniquesSelectors.ts"
|
||||
import { selectStaticAttributes } from "../../../../slices/databaseSlice.ts"
|
||||
import {
|
||||
changeInlineLibraryEntry,
|
||||
selectInlineLibraryEntryId,
|
||||
} from "../../../../slices/inlineWikiSlice.ts"
|
||||
import {
|
||||
decrementRangedCombatTechnique,
|
||||
incrementRangedCombatTechnique,
|
||||
setRangedCombatTechnique,
|
||||
} from "../../../../slices/rangedCombatTechniqueSlice.ts"
|
||||
import { SkillAdditionalValues } from "../skills/SkillAdditionalValues.tsx"
|
||||
import { SkillButtons } from "../skills/SkillButtons.tsx"
|
||||
import { SkillImprovementCost } from "../skills/SkillImprovementCost.tsx"
|
||||
@@ -22,31 +28,21 @@ import { SkillRating } from "../skills/SkillRating.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
id: number
|
||||
name: string
|
||||
sr: number
|
||||
primary: AttributeReference[]
|
||||
ic: ImprovementCost
|
||||
addDisabled: boolean
|
||||
removeDisabled: boolean
|
||||
at: number
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
rangedCombatTechnique: DisplayedRangedCombatTechnique
|
||||
}
|
||||
|
||||
const RangedCombatTechniquesListItem: FC<Props> = props => {
|
||||
const {
|
||||
insertTopMargin,
|
||||
id,
|
||||
name,
|
||||
sr,
|
||||
primary,
|
||||
ic,
|
||||
addDisabled,
|
||||
removeDisabled,
|
||||
at,
|
||||
addPoint,
|
||||
removePoint,
|
||||
rangedCombatTechnique: {
|
||||
static: { id, primary_attribute, improvement_cost, translations },
|
||||
dynamic: { value },
|
||||
maximum,
|
||||
minimum,
|
||||
isIncreasable,
|
||||
isDecreasable,
|
||||
attackBase,
|
||||
},
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -55,6 +51,20 @@ const RangedCombatTechniquesListItem: FC<Props> = props => {
|
||||
const inlineLibraryEntryId = useAppSelector(selectInlineLibraryEntryId)
|
||||
const canRemove = useAppSelector(selectCanRemove)
|
||||
|
||||
const { name = "???" } = translateMap(translations) ?? {}
|
||||
|
||||
const { handleAddPoint, handleRemovePoint, handleSetToMaximumPoints, handleSetToMinimumPoints } =
|
||||
useRatedActions(
|
||||
id,
|
||||
value,
|
||||
maximum,
|
||||
minimum,
|
||||
fromRaw(improvement_cost),
|
||||
incrementRangedCombatTechnique,
|
||||
decrementRangedCombatTechnique,
|
||||
setRangedCombatTechnique,
|
||||
)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() =>
|
||||
dispatch(
|
||||
@@ -65,11 +75,11 @@ const RangedCombatTechniquesListItem: FC<Props> = props => {
|
||||
|
||||
const attributes = useAppSelector(selectStaticAttributes)
|
||||
|
||||
const primaryStr = primary
|
||||
const primaryStr = primary_attribute
|
||||
.map(ref => translateMap(attributes[ref.id.attribute]?.translations)?.abbreviation ?? "")
|
||||
.join("/")
|
||||
|
||||
const customClassName = `attr--${primary.map(ref => ref.id.attribute).join("-")}`
|
||||
const customClassName = `attr--${primary_attribute.map(ref => ref.id.attribute).join("-")}`
|
||||
|
||||
const primaryClassName = `primary ${customClassName}`
|
||||
|
||||
@@ -85,12 +95,12 @@ const RangedCombatTechniquesListItem: FC<Props> = props => {
|
||||
<ListItemSeparator />
|
||||
<ListItemGroup text={translate("Ranged Combat")} />
|
||||
<ListItemValues>
|
||||
<SkillRating sr={sr} addPoint={addPoint} />
|
||||
<SkillImprovementCost ic={ic} />
|
||||
<SkillRating sr={value} />
|
||||
<SkillImprovementCost ic={fromRaw(improvement_cost)} />
|
||||
<SkillAdditionalValues
|
||||
addValues={[
|
||||
{ className: primaryClassName, value: primaryStr },
|
||||
{ className: "at", value: at },
|
||||
{ className: "at", value: attackBase },
|
||||
{ className: "atpa" },
|
||||
{
|
||||
className: "pa",
|
||||
@@ -100,13 +110,12 @@ const RangedCombatTechniquesListItem: FC<Props> = props => {
|
||||
/>
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={addDisabled}
|
||||
ic={ic}
|
||||
id={id}
|
||||
removeDisabled={removeDisabled}
|
||||
sr={sr}
|
||||
addPoint={addPoint}
|
||||
removePoint={canRemove ? removePoint : undefined}
|
||||
addDisabled={!isIncreasable}
|
||||
removeDisabled={!isDecreasable}
|
||||
addPoint={handleAddPoint}
|
||||
setToMax={handleSetToMaximumPoints}
|
||||
removePoint={canRemove ? handleRemovePoint : undefined}
|
||||
setToMin={canRemove ? handleSetToMinimumPoints : undefined}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
+5
-2
@@ -45,6 +45,10 @@ const ActiveBlessingsListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const handleRemove = useCallback(() => {
|
||||
remove(id)
|
||||
}, [remove, id])
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Blessing", blessing: id })),
|
||||
[dispatch, id],
|
||||
@@ -71,8 +75,7 @@ const ActiveBlessingsListItem: FC<Props> = props => {
|
||||
<SkillFill />
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
id={id}
|
||||
removePoint={canRemove ? remove : undefined}
|
||||
removePoint={canRemove ? handleRemove : undefined}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
+33
-12
@@ -11,9 +11,16 @@ import { LiturgiesSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useLocaleCompare } from "../../../../../shared/hooks/localeCompare.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { selectCanRemove } from "../../../../selectors/characterSelectors.ts"
|
||||
import { selectActiveBlessedTradition } from "../../../../selectors/traditionSelectors.ts"
|
||||
import {
|
||||
decrementCeremony,
|
||||
incrementCeremony,
|
||||
removeCeremony,
|
||||
setCeremony,
|
||||
} from "../../../../slices/ceremoniesSlice.ts"
|
||||
import { selectStaticAspects } from "../../../../slices/databaseSlice.ts"
|
||||
import {
|
||||
changeInlineLibraryEntry,
|
||||
@@ -29,9 +36,6 @@ type Props = {
|
||||
insertTopMargin?: boolean
|
||||
ceremony: DisplayedActiveCeremony
|
||||
sortOrder: LiturgiesSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveCeremoniesListItem: FC<Props> = props => {
|
||||
@@ -40,13 +44,12 @@ const ActiveCeremoniesListItem: FC<Props> = props => {
|
||||
ceremony: {
|
||||
dynamic: { value },
|
||||
static: { id, check, check_penalty, traditions, improvement_cost, translations },
|
||||
maximum,
|
||||
minimum,
|
||||
isDecreasable,
|
||||
isIncreasable,
|
||||
},
|
||||
sortOrder,
|
||||
addPoint,
|
||||
removePoint,
|
||||
remove,
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -60,6 +63,24 @@ const ActiveCeremoniesListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
id,
|
||||
value,
|
||||
maximum,
|
||||
minimum ?? 0,
|
||||
fromRaw(improvement_cost),
|
||||
incrementCeremony,
|
||||
decrementCeremony,
|
||||
setCeremony,
|
||||
removeCeremony,
|
||||
)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Ceremony", ceremony: id })),
|
||||
[dispatch, id],
|
||||
@@ -95,19 +116,19 @@ const ActiveCeremoniesListItem: FC<Props> = props => {
|
||||
}
|
||||
/>
|
||||
<ListItemValues>
|
||||
<SkillRating sr={value} addPoint={addPoint} />
|
||||
<SkillRating sr={value} />
|
||||
<SkillCheck check={check} checkPenalty={check_penalty} />
|
||||
<SkillFill />
|
||||
<SkillImprovementCost ic={fromRaw(improvement_cost)} />
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isIncreasable}
|
||||
ic={fromRaw(improvement_cost)}
|
||||
id={id}
|
||||
removeDisabled={!isDecreasable}
|
||||
sr={value}
|
||||
addPoint={addPoint}
|
||||
removePoint={canRemove ? (value === 0 ? remove : removePoint) : undefined}
|
||||
addPoint={handleAddPoint}
|
||||
setToMax={handleSetToMaximumPoints}
|
||||
removePoint={canRemove ? (value === 0 ? handleRemovePoint : handleRemove) : undefined}
|
||||
setToMin={canRemove && value > 0 ? handleSetToMinimumPoints : undefined}
|
||||
decrementIsRemove={value === 0}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
+33
-12
@@ -11,6 +11,7 @@ import { LiturgiesSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useLocaleCompare } from "../../../../../shared/hooks/localeCompare.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { selectCanRemove } from "../../../../selectors/characterSelectors.ts"
|
||||
import { selectActiveBlessedTradition } from "../../../../selectors/traditionSelectors.ts"
|
||||
@@ -19,6 +20,12 @@ import {
|
||||
changeInlineLibraryEntry,
|
||||
selectInlineLibraryEntryId,
|
||||
} from "../../../../slices/inlineWikiSlice.ts"
|
||||
import {
|
||||
decrementLiturgicalChant,
|
||||
incrementLiturgicalChant,
|
||||
removeLiturgicalChant,
|
||||
setLiturgicalChant,
|
||||
} from "../../../../slices/liturgicalChantsSlice.ts"
|
||||
import { SkillButtons } from "../skills/SkillButtons.tsx"
|
||||
import { SkillCheck } from "../skills/SkillCheck.tsx"
|
||||
import { SkillFill } from "../skills/SkillFill.tsx"
|
||||
@@ -29,9 +36,6 @@ type Props = {
|
||||
insertTopMargin?: boolean
|
||||
liturgicalChant: DisplayedActiveLiturgicalChant
|
||||
sortOrder: LiturgiesSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveLiturgicalChantsListItem: FC<Props> = props => {
|
||||
@@ -40,13 +44,12 @@ const ActiveLiturgicalChantsListItem: FC<Props> = props => {
|
||||
liturgicalChant: {
|
||||
dynamic: { value },
|
||||
static: { id, check, check_penalty, traditions, improvement_cost, translations },
|
||||
maximum,
|
||||
minimum,
|
||||
isDecreasable,
|
||||
isIncreasable,
|
||||
},
|
||||
sortOrder,
|
||||
addPoint,
|
||||
removePoint,
|
||||
remove,
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -60,6 +63,24 @@ const ActiveLiturgicalChantsListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
id,
|
||||
value,
|
||||
maximum,
|
||||
minimum ?? 0,
|
||||
fromRaw(improvement_cost),
|
||||
incrementLiturgicalChant,
|
||||
decrementLiturgicalChant,
|
||||
setLiturgicalChant,
|
||||
removeLiturgicalChant,
|
||||
)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "LiturgicalChant", liturgical_chant: id })),
|
||||
[dispatch, id],
|
||||
@@ -98,19 +119,19 @@ const ActiveLiturgicalChantsListItem: FC<Props> = props => {
|
||||
}
|
||||
/>
|
||||
<ListItemValues>
|
||||
<SkillRating sr={value} addPoint={addPoint} />
|
||||
<SkillRating sr={value} />
|
||||
<SkillCheck check={check} checkPenalty={check_penalty} />
|
||||
<SkillFill />
|
||||
<SkillImprovementCost ic={fromRaw(improvement_cost)} />
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isIncreasable}
|
||||
ic={fromRaw(improvement_cost)}
|
||||
id={id}
|
||||
removeDisabled={!isDecreasable}
|
||||
sr={value}
|
||||
addPoint={addPoint}
|
||||
removePoint={canRemove ? (value === 0 ? remove : removePoint) : undefined}
|
||||
addPoint={handleAddPoint}
|
||||
setToMax={handleSetToMaximumPoints}
|
||||
removePoint={canRemove ? (value === 0 ? handleRemovePoint : handleRemove) : undefined}
|
||||
setToMin={canRemove && value > 0 ? handleSetToMinimumPoints : undefined}
|
||||
decrementIsRemove={value === 0}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
+5
-1
@@ -43,6 +43,10 @@ const InactiveBlessingsListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const handleAdd = useCallback(() => {
|
||||
add(id)
|
||||
}, [add, id])
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Blessing", blessing: id })),
|
||||
[dispatch, id],
|
||||
@@ -68,7 +72,7 @@ const InactiveBlessingsListItem: FC<Props> = props => {
|
||||
<ListItemValues>
|
||||
<SkillFill />
|
||||
</ListItemValues>
|
||||
<SkillButtons id={id} addPoint={add} selectForInfo={handleSelectForInfo} />
|
||||
<SkillButtons addPoint={handleAdd} selectForInfo={handleSelectForInfo} />
|
||||
</ListItem>
|
||||
)
|
||||
}
|
||||
|
||||
+5
-5
@@ -11,8 +11,10 @@ import { LiturgiesSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useLocaleCompare } from "../../../../../shared/hooks/localeCompare.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { selectActiveBlessedTradition } from "../../../../selectors/traditionSelectors.ts"
|
||||
import { addCeremony } from "../../../../slices/ceremoniesSlice.ts"
|
||||
import { selectStaticAspects } from "../../../../slices/databaseSlice.ts"
|
||||
import {
|
||||
changeInlineLibraryEntry,
|
||||
@@ -27,7 +29,6 @@ type Props = {
|
||||
insertTopMargin?: boolean
|
||||
ceremony: DisplayedInactiveCeremony
|
||||
sortOrder: LiturgiesSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveCeremoniesListItem: FC<Props> = props => {
|
||||
@@ -38,7 +39,6 @@ const InactiveCeremoniesListItem: FC<Props> = props => {
|
||||
isAvailable,
|
||||
},
|
||||
sortOrder,
|
||||
add,
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -51,6 +51,8 @@ const InactiveCeremoniesListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(id, fromRaw(improvement_cost), addCeremony)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Ceremony", ceremony: id })),
|
||||
[dispatch, id],
|
||||
@@ -93,9 +95,7 @@ const InactiveCeremoniesListItem: FC<Props> = props => {
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isAvailable}
|
||||
ic={fromRaw(improvement_cost)}
|
||||
id={id}
|
||||
addPoint={add}
|
||||
addPoint={handleAdd}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
+9
-5
@@ -11,6 +11,7 @@ import { LiturgiesSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useLocaleCompare } from "../../../../../shared/hooks/localeCompare.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { selectActiveBlessedTradition } from "../../../../selectors/traditionSelectors.ts"
|
||||
import { selectStaticAspects } from "../../../../slices/databaseSlice.ts"
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
changeInlineLibraryEntry,
|
||||
selectInlineLibraryEntryId,
|
||||
} from "../../../../slices/inlineWikiSlice.ts"
|
||||
import { addLiturgicalChant } from "../../../../slices/liturgicalChantsSlice.ts"
|
||||
import { SkillButtons } from "../skills/SkillButtons.tsx"
|
||||
import { SkillCheck } from "../skills/SkillCheck.tsx"
|
||||
import { SkillFill } from "../skills/SkillFill.tsx"
|
||||
@@ -27,7 +29,6 @@ type Props = {
|
||||
insertTopMargin?: boolean
|
||||
liturgicalChant: DisplayedInactiveLiturgicalChant
|
||||
sortOrder: LiturgiesSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveLiturgicalChantsListItem: FC<Props> = props => {
|
||||
@@ -38,7 +39,6 @@ const InactiveLiturgicalChantsListItem: FC<Props> = props => {
|
||||
isAvailable,
|
||||
},
|
||||
sortOrder,
|
||||
add,
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -51,6 +51,12 @@ const InactiveLiturgicalChantsListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
id,
|
||||
fromRaw(improvement_cost),
|
||||
addLiturgicalChant,
|
||||
)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "LiturgicalChant", liturgical_chant: id })),
|
||||
[dispatch, id],
|
||||
@@ -96,9 +102,7 @@ const InactiveLiturgicalChantsListItem: FC<Props> = props => {
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isAvailable}
|
||||
ic={fromRaw(improvement_cost)}
|
||||
id={id}
|
||||
addPoint={add}
|
||||
addPoint={handleAdd}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -28,18 +28,6 @@ import {
|
||||
selectVisibleInactiveLiturgies,
|
||||
} from "../../../../selectors/liturgicalChantSelectors.ts"
|
||||
import { addBlessing, removeBlessing } from "../../../../slices/blessingsSlice.ts"
|
||||
import {
|
||||
addCeremony,
|
||||
decrementCeremony,
|
||||
incrementCeremony,
|
||||
removeCeremony,
|
||||
} from "../../../../slices/ceremoniesSlice.ts"
|
||||
import {
|
||||
addLiturgicalChant,
|
||||
decrementLiturgicalChant,
|
||||
incrementLiturgicalChant,
|
||||
removeLiturgicalChant,
|
||||
} from "../../../../slices/liturgicalChantsSlice.ts"
|
||||
import {
|
||||
changeLiturgiesSortOrder,
|
||||
selectLiturgiesSortOrder,
|
||||
@@ -103,44 +91,13 @@ export const LiturgicalChants: FC = () => {
|
||||
[visibleActiveLiturgies, inactiveFilterText, sortOrder, translateMap, localeCompare],
|
||||
)
|
||||
|
||||
const handleAddLiturgicalChantPoint = useCallback(
|
||||
(id: number) => dispatch(incrementLiturgicalChant(id)),
|
||||
// TODO: Check AP
|
||||
const handleAddBlessing = useCallback((id: number) => dispatch(addBlessing({ id })), [dispatch])
|
||||
const handleRemoveBlessing = useCallback(
|
||||
(id: number) => dispatch(removeBlessing({ id })),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleRemoveLiturgicalChantPoint = useCallback(
|
||||
(id: number) => dispatch(decrementLiturgicalChant(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddCeremonyPoint = useCallback(
|
||||
(id: number) => dispatch(incrementCeremony(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleRemoveCeremonyPoint = useCallback(
|
||||
(id: number) => dispatch(decrementCeremony(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddBlessing = useCallback((id: number) => dispatch(addBlessing(id)), [dispatch])
|
||||
|
||||
const handleAddLiturgicalChant = useCallback(
|
||||
(id: number) => dispatch(addLiturgicalChant(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddCeremony = useCallback((id: number) => dispatch(addCeremony(id)), [dispatch])
|
||||
|
||||
const handleRemoveBlessing = useCallback((id: number) => dispatch(removeBlessing(id)), [dispatch])
|
||||
|
||||
const handleRemoveLiturgicalChant = useCallback(
|
||||
(id: number) => dispatch(removeLiturgicalChant(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleRemoveCeremony = useCallback((id: number) => dispatch(removeCeremony(id)), [dispatch])
|
||||
|
||||
const { isOpen: isSlideinVisible, open: openSlidein, close: closeSlidein } = useModalState()
|
||||
|
||||
return (
|
||||
@@ -177,29 +134,17 @@ export const LiturgicalChants: FC = () => {
|
||||
</Options>
|
||||
<Main classOnly>
|
||||
<ListHeader>
|
||||
<ListHeaderTag className="name">
|
||||
{translate("liturgicalchants.header.name")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="name">{translate("Name")}</ListHeaderTag>
|
||||
<ListHeaderTag className="group">
|
||||
{translate("liturgicalchants.header.traditions")}
|
||||
{sortOrder === LiturgiesSortOrder.Group
|
||||
? ` / ${translate("liturgicalchants.header.group")}`
|
||||
: null}
|
||||
{sortOrder === LiturgiesSortOrder.Group ? ` / ${translate("Group")}` : null}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="check">
|
||||
{translate("liturgicalchants.header.check")}
|
||||
<ListHeaderTag className="check">{translate("Check")}</ListHeaderTag>
|
||||
<ListHeaderTag className="mod" hint={translate("Check Modifier")}>
|
||||
{translate("Mod")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag
|
||||
className="mod"
|
||||
hint={translate("liturgicalchants.header.checkmodifier.tooltip")}
|
||||
>
|
||||
{translate("liturgicalchants.header.checkmodifier")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag
|
||||
className="ic"
|
||||
hint={translate("liturgicalchants.header.improvementcost.tooltip")}
|
||||
>
|
||||
{translate("liturgicalchants.header.improvementcost")}
|
||||
<ListHeaderTag className="ic" hint={translate("Improvement Cost")}>
|
||||
{translate("IC")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="btn-placeholder" />
|
||||
</ListHeader>
|
||||
@@ -226,7 +171,6 @@ export const LiturgicalChants: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
liturgicalChant={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddLiturgicalChant}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -237,7 +181,6 @@ export const LiturgicalChants: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
ceremony={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddCeremony}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -282,35 +225,20 @@ export const LiturgicalChants: FC = () => {
|
||||
</Options>
|
||||
<Main>
|
||||
<ListHeader>
|
||||
<ListHeaderTag className="name">
|
||||
{translate("liturgicalchants.header.name")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="name">{translate("Name")}</ListHeaderTag>
|
||||
<ListHeaderTag className="group">
|
||||
{translate("liturgicalchants.header.traditions")}
|
||||
{sortOrder === LiturgiesSortOrder.Group
|
||||
? ` / ${translate("liturgicalchants.header.group")}`
|
||||
: null}
|
||||
{sortOrder === LiturgiesSortOrder.Group ? ` / ${translate("Group")}` : null}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag
|
||||
className="value"
|
||||
hint={translate("liturgicalchants.header.skillrating.tooltip")}
|
||||
>
|
||||
{translate("liturgicalchants.header.skillrating")}
|
||||
<ListHeaderTag className="value" hint={translate("Skill Rating")}>
|
||||
{translate("SR")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="check">
|
||||
{translate("liturgicalchants.header.check")}
|
||||
<ListHeaderTag className="check">{translate("Check")}</ListHeaderTag>
|
||||
<ListHeaderTag className="mod" hint={translate("Check Modifier")}>
|
||||
{translate("Mod")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag
|
||||
className="mod"
|
||||
hint={translate("liturgicalchants.header.checkmodifier.tooltip")}
|
||||
>
|
||||
{translate("liturgicalchants.header.checkmodifier")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag
|
||||
className="ic"
|
||||
hint={translate("liturgicalchants.header.improvementcost.tooltip")}
|
||||
>
|
||||
{translate("liturgicalchants.header.improvementcost")}
|
||||
<ListHeaderTag className="ic" hint={translate("Improvement Cost")}>
|
||||
{translate("IC")}
|
||||
</ListHeaderTag>
|
||||
{canRemove ? <ListHeaderTag className="btn-placeholder" /> : null}
|
||||
<ListHeaderTag className="btn-placeholder" />
|
||||
@@ -339,9 +267,6 @@ export const LiturgicalChants: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
liturgicalChant={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddLiturgicalChantPoint}
|
||||
removePoint={handleRemoveLiturgicalChantPoint}
|
||||
remove={handleRemoveLiturgicalChant}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -352,9 +277,6 @@ export const LiturgicalChants: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
ceremony={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddCeremonyPoint}
|
||||
removePoint={handleRemoveCeremonyPoint}
|
||||
remove={handleRemoveCeremony}
|
||||
/>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { useCallback } from "react"
|
||||
import { MouseEvent, useCallback } from "react"
|
||||
import { IconButton } from "../../../../../shared/components/iconButton/IconButton.tsx"
|
||||
import { ListItemButtons } from "../../../../../shared/components/list/ListItemButtons.tsx"
|
||||
import { ImprovementCost } from "../../../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
|
||||
type Props = {
|
||||
activateDisabled?: boolean
|
||||
addDisabled?: boolean
|
||||
ic?: ImprovementCost
|
||||
id: number
|
||||
isNotActive?: boolean
|
||||
removeDisabled?: boolean
|
||||
sr?: number
|
||||
activate?(id: number): void
|
||||
addPoint?(id: number): void
|
||||
removePoint?(id: number): void
|
||||
selectForInfo(id: number): void
|
||||
decrementIsRemove?: boolean
|
||||
activate?(): void
|
||||
addPoint?(): void
|
||||
setToMax?(): void
|
||||
removePoint?(): void
|
||||
setToMin?(): void
|
||||
selectForInfo(): void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,40 +24,43 @@ export const SkillButtons: React.FC<Props> = props => {
|
||||
const {
|
||||
activateDisabled,
|
||||
addDisabled,
|
||||
ic,
|
||||
id,
|
||||
isNotActive,
|
||||
removeDisabled,
|
||||
sr,
|
||||
decrementIsRemove,
|
||||
activate,
|
||||
addPoint,
|
||||
setToMax,
|
||||
setToMin,
|
||||
removePoint,
|
||||
selectForInfo,
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const boundSelectForInfo = useCallback(() => selectForInfo(id), [selectForInfo, id])
|
||||
|
||||
const getRemoveIcon = () =>
|
||||
(sr !== undefined && sr === 0 && removeDisabled !== true) || ic === undefined
|
||||
? "\uE90b"
|
||||
: "\uE909"
|
||||
|
||||
const handleActivation = useCallback(
|
||||
() => (typeof activate === "function" ? activate(id) : undefined),
|
||||
[activate, id],
|
||||
)
|
||||
|
||||
const handleAddPoint = useCallback(
|
||||
() => (addDisabled !== true && typeof addPoint === "function" ? addPoint(id) : undefined),
|
||||
[addPoint, id, addDisabled],
|
||||
(event: MouseEvent<HTMLButtonElement>) => {
|
||||
if (addDisabled !== true && typeof addPoint === "function") {
|
||||
if (event.shiftKey && typeof setToMax === "function") {
|
||||
setToMax()
|
||||
} else {
|
||||
addPoint()
|
||||
}
|
||||
}
|
||||
},
|
||||
[addDisabled, addPoint, setToMax],
|
||||
)
|
||||
|
||||
const handleRemovePoint = useCallback(
|
||||
() =>
|
||||
removeDisabled !== true && typeof removePoint === "function" ? removePoint(id) : undefined,
|
||||
[removePoint, id, removeDisabled],
|
||||
(event: MouseEvent<HTMLButtonElement>) => {
|
||||
if (removeDisabled !== true && typeof removePoint === "function") {
|
||||
if (event.shiftKey && typeof setToMin === "function") {
|
||||
setToMin()
|
||||
} else {
|
||||
removePoint()
|
||||
}
|
||||
}
|
||||
},
|
||||
[removeDisabled, removePoint, setToMin],
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -66,7 +68,7 @@ export const SkillButtons: React.FC<Props> = props => {
|
||||
{isNotActive === true ? (
|
||||
<IconButton
|
||||
icon=""
|
||||
onClick={handleActivation}
|
||||
onClick={activate}
|
||||
disabled={activateDisabled}
|
||||
label={translate("Activate")}
|
||||
flat
|
||||
@@ -84,7 +86,7 @@ export const SkillButtons: React.FC<Props> = props => {
|
||||
) : null}
|
||||
{typeof removePoint === "function" ? (
|
||||
<IconButton
|
||||
icon={getRemoveIcon()}
|
||||
icon={decrementIsRemove === true ? "\uE90b" : "\uE909"}
|
||||
onClick={handleRemovePoint}
|
||||
disabled={removeDisabled}
|
||||
label={translate("Decrement")}
|
||||
@@ -93,12 +95,7 @@ export const SkillButtons: React.FC<Props> = props => {
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
<IconButton
|
||||
icon=""
|
||||
onClick={boundSelectForInfo}
|
||||
label={translate("Show details")}
|
||||
flat
|
||||
/>
|
||||
<IconButton icon="" onClick={selectForInfo} label={translate("Show details")} flat />
|
||||
</ListItemButtons>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { FC } from "react"
|
||||
import { ListItemGroup } from "../../../../../shared/components/list/ListItemGroup.tsx"
|
||||
|
||||
type Props = {
|
||||
addText?: string
|
||||
group?: number
|
||||
getGroupName?: (id: number) => string
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a row section that display a skill group.
|
||||
*/
|
||||
export const SkillGroup: FC<Props> = props => {
|
||||
const { addText, group, getGroupName } = props
|
||||
|
||||
if (addText === undefined && (group === undefined || getGroupName === undefined)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <ListItemGroup group={group} getGroupName={getGroupName} text={addText} />
|
||||
}
|
||||
@@ -1,125 +1,100 @@
|
||||
import {
|
||||
SkillCheckPenalty,
|
||||
SkillCheck as SkillCheckType,
|
||||
} from "optolith-database-schema/types/_SkillCheck"
|
||||
import { memo, useCallback } from "react"
|
||||
import { memo, useCallback, useMemo } from "react"
|
||||
import { ListItem } from "../../../../../shared/components/list/ListItem.tsx"
|
||||
import { ListItemGroup } from "../../../../../shared/components/list/ListItemGroup.tsx"
|
||||
import { ListItemName } from "../../../../../shared/components/list/ListItemName.tsx"
|
||||
import { ListItemSeparator } from "../../../../../shared/components/list/ListItemSeparator.tsx"
|
||||
import { ListItemValues } from "../../../../../shared/components/list/ListItemValues.tsx"
|
||||
import { ImprovementCost } from "../../../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useRatedActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { SelectGetById } from "../../../../selectors/basicCapabilitySelectors.ts"
|
||||
import { selectCanRemove } from "../../../../selectors/characterSelectors.ts"
|
||||
import { DisplayedSkill } from "../../../../selectors/skillsSelectors.ts"
|
||||
import {
|
||||
changeInlineLibraryEntry,
|
||||
selectInlineLibraryEntryId,
|
||||
} from "../../../../slices/inlineWikiSlice.ts"
|
||||
import { AdditionalValue, SkillAdditionalValues } from "./SkillAdditionalValues.tsx"
|
||||
import { selectSkillsCultureRatingVisibility } from "../../../../slices/settingsSlice.ts"
|
||||
import { decrementSkill, incrementSkill, setSkill } from "../../../../slices/skillsSlice.ts"
|
||||
import { SkillButtons } from "./SkillButtons.tsx"
|
||||
import { SkillCheck } from "./SkillCheck.tsx"
|
||||
import { SkillFill } from "./SkillFill.tsx"
|
||||
import { SkillGroup } from "./SkillGroup.tsx"
|
||||
import { SkillImprovementCost } from "./SkillImprovementCost.tsx"
|
||||
import { SkillRating } from "./SkillRating.tsx"
|
||||
|
||||
type Props = {
|
||||
activateDisabled?: boolean
|
||||
addDisabled?: boolean
|
||||
addFillElement?: boolean
|
||||
addValues?: AdditionalValue[]
|
||||
addText?: string
|
||||
check?: SkillCheckType
|
||||
checkDisabled?: boolean
|
||||
checkmod?: SkillCheckPenalty
|
||||
group?: number
|
||||
ic?: ImprovementCost
|
||||
id: number
|
||||
insertTopMargin?: boolean
|
||||
isNotActive?: boolean
|
||||
name: string
|
||||
noIncrease?: boolean
|
||||
removeDisabled?: boolean
|
||||
sr?: number
|
||||
typ?: boolean
|
||||
untyp?: boolean
|
||||
activate?(id: number): void
|
||||
addPoint?(id: number): void
|
||||
removePoint?(id: number): void
|
||||
getGroupName?: (id: number) => string
|
||||
skill: DisplayedSkill
|
||||
}
|
||||
|
||||
const SkillListItem: React.FC<Props> = props => {
|
||||
const {
|
||||
activateDisabled,
|
||||
addDisabled,
|
||||
addFillElement,
|
||||
addValues,
|
||||
addText,
|
||||
check,
|
||||
checkDisabled,
|
||||
checkmod,
|
||||
getGroupName,
|
||||
group,
|
||||
ic,
|
||||
id,
|
||||
insertTopMargin,
|
||||
isNotActive,
|
||||
name,
|
||||
noIncrease,
|
||||
removeDisabled,
|
||||
sr,
|
||||
typ,
|
||||
untyp,
|
||||
activate,
|
||||
addPoint,
|
||||
removePoint,
|
||||
skill: {
|
||||
static: { id, check, group, improvement_cost, translations },
|
||||
dynamic: { value },
|
||||
maximum,
|
||||
minimum,
|
||||
isIncreasable,
|
||||
isDecreasable,
|
||||
commonness,
|
||||
},
|
||||
} = props
|
||||
|
||||
const translateMap = useTranslateMap()
|
||||
const dispatch = useAppDispatch()
|
||||
const inlineLibraryEntryId = useAppSelector(selectInlineLibraryEntryId)
|
||||
const canRemove = useAppSelector(selectCanRemove)
|
||||
|
||||
const cultureRatingVisibility = useAppSelector(selectSkillsCultureRatingVisibility)
|
||||
|
||||
const { name = "???" } = translateMap(translations) ?? {}
|
||||
|
||||
const { handleAddPoint, handleRemovePoint, handleSetToMaximumPoints, handleSetToMinimumPoints } =
|
||||
useRatedActions(
|
||||
id,
|
||||
value,
|
||||
maximum,
|
||||
minimum,
|
||||
fromRaw(improvement_cost),
|
||||
incrementSkill,
|
||||
decrementSkill,
|
||||
setSkill,
|
||||
)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Skill", skill: id })),
|
||||
[dispatch, id],
|
||||
)
|
||||
|
||||
const getStaticSkillGroupById = useAppSelector(SelectGetById.Static.SkillGroup)
|
||||
const groupName = useMemo(
|
||||
() => translateMap(getStaticSkillGroupById(group.id.skill_group)?.translations)?.name,
|
||||
[getStaticSkillGroupById, group.id.skill_group, translateMap],
|
||||
)
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
noIncrease={noIncrease}
|
||||
recommended={typ}
|
||||
unrecommended={untyp}
|
||||
insertTopMargin={insertTopMargin}
|
||||
active={inlineLibraryEntryId?.tag === "Skill" && inlineLibraryEntryId.skill === id}
|
||||
recommended={cultureRatingVisibility && commonness === "common"}
|
||||
unrecommended={cultureRatingVisibility && commonness === "uncommon"}
|
||||
>
|
||||
<ListItemName name={name} onClick={handleSelectForInfo} />
|
||||
<ListItemSeparator />
|
||||
<SkillGroup addText={addText} group={group} getGroupName={getGroupName} />
|
||||
<ListItemGroup text={groupName} />
|
||||
<ListItemValues>
|
||||
<SkillRating
|
||||
isNotActive={isNotActive}
|
||||
noIncrease={noIncrease}
|
||||
sr={sr}
|
||||
addPoint={addPoint}
|
||||
/>
|
||||
{checkDisabled !== true && check !== undefined ? (
|
||||
<SkillCheck check={check} checkPenalty={checkmod} />
|
||||
) : null}
|
||||
{addFillElement === true ? <SkillFill /> : null}
|
||||
<SkillImprovementCost ic={ic} />
|
||||
<SkillAdditionalValues addValues={addValues} />
|
||||
<SkillRating sr={value} />
|
||||
<SkillCheck check={check} />
|
||||
<SkillImprovementCost ic={fromRaw(improvement_cost)} />
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
activateDisabled={activateDisabled}
|
||||
addDisabled={addDisabled}
|
||||
ic={ic}
|
||||
id={id}
|
||||
isNotActive={isNotActive}
|
||||
removeDisabled={removeDisabled}
|
||||
sr={sr}
|
||||
activate={activate}
|
||||
addPoint={addPoint}
|
||||
removePoint={canRemove ? removePoint : undefined}
|
||||
addDisabled={!isIncreasable}
|
||||
removeDisabled={!isDecreasable}
|
||||
addPoint={handleAddPoint}
|
||||
setToMax={handleSetToMaximumPoints}
|
||||
removePoint={canRemove ? handleRemovePoint : undefined}
|
||||
setToMin={canRemove ? handleSetToMinimumPoints : undefined}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -4,18 +4,17 @@ type Props = {
|
||||
isNotActive?: boolean
|
||||
noIncrease?: boolean
|
||||
sr?: number
|
||||
addPoint?(id: number): void
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a row section that displays a skill rating.
|
||||
*/
|
||||
export const SkillRating: FC<Props> = props => {
|
||||
const { isNotActive, noIncrease, sr, addPoint } = props
|
||||
const { isNotActive, noIncrease, sr } = props
|
||||
|
||||
if (typeof sr === "number") {
|
||||
return <div className="sr">{sr}</div>
|
||||
} else if (addPoint === undefined && isNotActive !== true && noIncrease !== true) {
|
||||
} else if (isNotActive !== true && noIncrease !== true) {
|
||||
return <div className="sr empty" />
|
||||
}
|
||||
|
||||
|
||||
@@ -26,14 +26,12 @@ import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { InlineLibrary } from "../../../../inlineLibrary/InlineLibrary.tsx"
|
||||
import { selectCanRemove } from "../../../../selectors/characterSelectors.ts"
|
||||
import { DisplayedSkill, selectVisibleSkills } from "../../../../selectors/skillsSelectors.ts"
|
||||
import { selectStaticSkillGroups } from "../../../../slices/databaseSlice.ts"
|
||||
import {
|
||||
changeSkillsSortOrder,
|
||||
selectSkillsCultureRatingVisibility,
|
||||
selectSkillsSortOrder,
|
||||
switchSkillsCultureRatingVisibility,
|
||||
} from "../../../../slices/settingsSlice.ts"
|
||||
import { decrementSkill, incrementSkill } from "../../../../slices/skillsSlice.ts"
|
||||
import { SkillListItem } from "./SkillListItem.tsx"
|
||||
import "./Skills.scss"
|
||||
|
||||
@@ -62,7 +60,6 @@ export const Skills: FC = () => {
|
||||
)
|
||||
|
||||
const canRemove = useAppSelector(selectCanRemove)
|
||||
const skillGroups = useAppSelector(selectStaticSkillGroups)
|
||||
|
||||
const [filterText, setFilterText] = useState("")
|
||||
const sortOrder = useAppSelector(selectSkillsSortOrder)
|
||||
@@ -70,7 +67,9 @@ export const Skills: FC = () => {
|
||||
(id: SkillsSortOrder) => dispatch(changeSkillsSortOrder(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const visibleSkills = useAppSelector(selectVisibleSkills)
|
||||
|
||||
const list = useMemo(
|
||||
() =>
|
||||
visibleSkills
|
||||
@@ -106,15 +105,6 @@ export const Skills: FC = () => {
|
||||
[filterText, localeCompare, sortOrder, translateMap, visibleSkills],
|
||||
)
|
||||
|
||||
const handleAdd = useCallback((id: number) => dispatch(incrementSkill(id)), [dispatch])
|
||||
|
||||
const handleRemove = useCallback((id: number) => dispatch(decrementSkill(id)), [dispatch])
|
||||
|
||||
const getGroupName = useCallback(
|
||||
(id: number) => translateMap(skillGroups[id]?.translations)?.name ?? "",
|
||||
[skillGroups, translateMap],
|
||||
)
|
||||
|
||||
return (
|
||||
<Page id="skills">
|
||||
<Options>
|
||||
@@ -140,21 +130,21 @@ export const Skills: FC = () => {
|
||||
/>
|
||||
<Grid size="medium">
|
||||
<Checkbox checked={cultureRatingVisibility} onClick={handlSwitchCultureRatingVisibility}>
|
||||
{translate("skills.commonskills")}
|
||||
{translate("Common Skills")}
|
||||
</Checkbox>
|
||||
{cultureRatingVisibility ? <RecommendedReference /> : null}
|
||||
</Grid>
|
||||
</Options>
|
||||
<Main>
|
||||
<ListHeader>
|
||||
<ListHeaderTag className="name">{translate("skills.header.name")}</ListHeaderTag>
|
||||
<ListHeaderTag className="group">{translate("skills.header.group")}</ListHeaderTag>
|
||||
<ListHeaderTag className="value" hint={translate("skills.header.skillrating.tooltip")}>
|
||||
{translate("skills.header.skillrating")}
|
||||
<ListHeaderTag className="name">{translate("Name")}</ListHeaderTag>
|
||||
<ListHeaderTag className="group">{translate("Group")}</ListHeaderTag>
|
||||
<ListHeaderTag className="value" hint={translate("Skill Rating")}>
|
||||
{translate("SR")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="check">{translate("skills.header.check")}</ListHeaderTag>
|
||||
<ListHeaderTag className="ic" hint={translate("skills.header.improvementcost.tooltip")}>
|
||||
{translate("skills.header.improvementcost")}
|
||||
<ListHeaderTag className="check">{translate("Check")}</ListHeaderTag>
|
||||
<ListHeaderTag className="ic" hint={translate("Improvement Cost")}>
|
||||
{translate("IC")}
|
||||
</ListHeaderTag>
|
||||
{canRemove ? <ListHeaderTag className="btn-placeholder" /> : null}
|
||||
<ListHeaderTag className="btn-placeholder" />
|
||||
@@ -163,29 +153,13 @@ export const Skills: FC = () => {
|
||||
<Scroll stable>
|
||||
{list.length > 0 ? (
|
||||
<List>
|
||||
{list.map((x, i) => {
|
||||
const translation = translateMap(x.static.translations)
|
||||
return (
|
||||
<SkillListItem
|
||||
key={x.static.id}
|
||||
id={x.static.id}
|
||||
typ={cultureRatingVisibility && x.commonness === "common"}
|
||||
untyp={cultureRatingVisibility && x.commonness === "uncommon"}
|
||||
name={translation?.name ?? ""}
|
||||
sr={x.dynamic.value}
|
||||
check={x.static.check}
|
||||
ic={fromRaw(x.static.improvement_cost)}
|
||||
addDisabled={!x.isIncreasable}
|
||||
addPoint={handleAdd}
|
||||
removeDisabled={!x.isDecreasable}
|
||||
removePoint={handleRemove}
|
||||
addFillElement
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, list[i - 1])}
|
||||
group={x.static.group.id.skill_group}
|
||||
getGroupName={getGroupName}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{list.map((x, i) => (
|
||||
<SkillListItem
|
||||
key={x.static.id}
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, list[i - 1])}
|
||||
skill={x}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
) : (
|
||||
<ListPlaceholder type="skills" message={translate("No Results")} />
|
||||
|
||||
+32
-23
@@ -1,28 +1,45 @@
|
||||
import { FC, memo } from "react"
|
||||
import {
|
||||
ImprovementCost,
|
||||
fromRaw,
|
||||
} from "../../../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { DisplayedActiveAnimistPower } from "../../../../../shared/domain/rated/spellActive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { assertExhaustive } from "../../../../../shared/utils/typeSafety.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import {
|
||||
decrementAnimistPower,
|
||||
incrementAnimistPower,
|
||||
removeAnimistPower,
|
||||
setAnimistPower,
|
||||
} from "../../../../slices/magicalActions/animistPowersSlice.ts"
|
||||
import { ActiveMagicalActionsListItem } from "./ActiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
animistPower: DisplayedActiveAnimistPower
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveAnimistPowersListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, animistPower, sortOrder, addPoint, removePoint, remove } = props
|
||||
const { insertTopMargin, animistPower, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
animistPower.static.id,
|
||||
animistPower.dynamic.value,
|
||||
animistPower.maximum,
|
||||
animistPower.minimum ?? 0,
|
||||
animistPower.improvementCost,
|
||||
incrementAnimistPower,
|
||||
decrementAnimistPower,
|
||||
setAnimistPower,
|
||||
removeAnimistPower,
|
||||
)
|
||||
|
||||
return (
|
||||
<ActiveMagicalActionsListItem
|
||||
kind="AnimistPower"
|
||||
@@ -30,20 +47,12 @@ const ActiveAnimistPowersListItem: FC<Props> = props => {
|
||||
magicalAction={animistPower}
|
||||
sortOrder={sortOrder}
|
||||
groupName={translate("Animist Powers")}
|
||||
improvementCost={(() => {
|
||||
switch (animistPower.static.improvement_cost.tag) {
|
||||
case "Fixed":
|
||||
return fromRaw(animistPower.static.improvement_cost.fixed)
|
||||
case "ByPrimaryPatron":
|
||||
// TODO: Replace with derived improvement cost
|
||||
return ImprovementCost.D
|
||||
default:
|
||||
return assertExhaustive(animistPower.static.improvement_cost)
|
||||
}
|
||||
})()}
|
||||
addPoint={addPoint}
|
||||
removePoint={removePoint}
|
||||
remove={remove}
|
||||
improvementCost={animistPower.improvementCost}
|
||||
addPoint={handleAddPoint}
|
||||
removePoint={handleRemovePoint}
|
||||
setToMaximumPoints={handleSetToMaximumPoints}
|
||||
setToMinimumPoints={handleSetToMinimumPoints}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ const ActiveCantripsListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const handleRemove = useCallback(() => {
|
||||
remove(id)
|
||||
}, [remove, id])
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Cantrip", cantrip: id })),
|
||||
[dispatch, id],
|
||||
@@ -75,8 +79,7 @@ const ActiveCantripsListItem: FC<Props> = props => {
|
||||
<SkillFill />
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
id={id}
|
||||
removePoint={canRemove ? remove : undefined}
|
||||
removePoint={canRemove ? handleRemove : undefined}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -3,22 +3,44 @@ import { cursesImprovementCost } from "../../../../../shared/domain/rated/magica
|
||||
import { DisplayedActiveCurse } from "../../../../../shared/domain/rated/spellActive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import {
|
||||
decrementCurse,
|
||||
incrementCurse,
|
||||
removeCurse,
|
||||
setCurse,
|
||||
} from "../../../../slices/magicalActions/cursesSlice.ts"
|
||||
import { ActiveMagicalActionsListItem } from "./ActiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
curse: DisplayedActiveCurse
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveCursesListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, curse, sortOrder, addPoint, removePoint, remove } = props
|
||||
const { insertTopMargin, curse, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
curse.static.id,
|
||||
curse.dynamic.value,
|
||||
curse.maximum,
|
||||
curse.minimum ?? 0,
|
||||
cursesImprovementCost,
|
||||
incrementCurse,
|
||||
decrementCurse,
|
||||
setCurse,
|
||||
removeCurse,
|
||||
)
|
||||
|
||||
return (
|
||||
<ActiveMagicalActionsListItem
|
||||
kind="Curse"
|
||||
@@ -28,9 +50,11 @@ const ActiveCursesListItem: FC<Props> = props => {
|
||||
groupName={translate("Curses")}
|
||||
checkPenalty={curse.static.check_penalty}
|
||||
improvementCost={cursesImprovementCost}
|
||||
addPoint={addPoint}
|
||||
removePoint={removePoint}
|
||||
remove={remove}
|
||||
addPoint={handleAddPoint}
|
||||
removePoint={handleRemovePoint}
|
||||
setToMaximumPoints={handleSetToMaximumPoints}
|
||||
setToMinimumPoints={handleSetToMinimumPoints}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+31
-7
@@ -3,22 +3,44 @@ import { dominationRitualsImprovementCost } from "../../../../../shared/domain/r
|
||||
import { DisplayedActiveDominationRitual } from "../../../../../shared/domain/rated/spellActive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import {
|
||||
decrementDominationRitual,
|
||||
incrementDominationRitual,
|
||||
removeDominationRitual,
|
||||
setDominationRitual,
|
||||
} from "../../../../slices/magicalActions/dominationRitualsSlice.ts"
|
||||
import { ActiveMagicalActionsListItem } from "./ActiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
dominationRitual: DisplayedActiveDominationRitual
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveDominationRitualsListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, dominationRitual, sortOrder, addPoint, removePoint, remove } = props
|
||||
const { insertTopMargin, dominationRitual, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
dominationRitual.static.id,
|
||||
dominationRitual.dynamic.value,
|
||||
dominationRitual.maximum,
|
||||
dominationRitual.minimum ?? 0,
|
||||
dominationRitualsImprovementCost,
|
||||
incrementDominationRitual,
|
||||
decrementDominationRitual,
|
||||
setDominationRitual,
|
||||
removeDominationRitual,
|
||||
)
|
||||
|
||||
return (
|
||||
<ActiveMagicalActionsListItem
|
||||
kind="DominationRitual"
|
||||
@@ -28,9 +50,11 @@ const ActiveDominationRitualsListItem: FC<Props> = props => {
|
||||
groupName={translate("Domination Rituals")}
|
||||
checkPenalty={dominationRitual.static.check_penalty}
|
||||
improvementCost={dominationRitualsImprovementCost}
|
||||
addPoint={addPoint}
|
||||
removePoint={removePoint}
|
||||
remove={remove}
|
||||
addPoint={handleAddPoint}
|
||||
removePoint={handleRemovePoint}
|
||||
setToMaximumPoints={handleSetToMaximumPoints}
|
||||
setToMinimumPoints={handleSetToMinimumPoints}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+31
-7
@@ -3,22 +3,44 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedActiveElvenMagicalSong } from "../../../../../shared/domain/rated/spellActive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import {
|
||||
decrementElvenMagicalSong,
|
||||
incrementElvenMagicalSong,
|
||||
removeElvenMagicalSong,
|
||||
setElvenMagicalSong,
|
||||
} from "../../../../slices/magicalActions/elvenMagicalSongsSlice.ts"
|
||||
import { ActiveMagicalActionsListItem } from "./ActiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
elvenMagicalSong: DisplayedActiveElvenMagicalSong
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveElvenMagicalSongsListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, elvenMagicalSong, sortOrder, addPoint, removePoint, remove } = props
|
||||
const { insertTopMargin, elvenMagicalSong, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
elvenMagicalSong.static.id,
|
||||
elvenMagicalSong.dynamic.value,
|
||||
elvenMagicalSong.maximum,
|
||||
elvenMagicalSong.minimum ?? 0,
|
||||
fromRaw(elvenMagicalSong.static.improvement_cost),
|
||||
incrementElvenMagicalSong,
|
||||
decrementElvenMagicalSong,
|
||||
setElvenMagicalSong,
|
||||
removeElvenMagicalSong,
|
||||
)
|
||||
|
||||
return (
|
||||
<ActiveMagicalActionsListItem
|
||||
kind="ElvenMagicalSong"
|
||||
@@ -28,9 +50,11 @@ const ActiveElvenMagicalSongsListItem: FC<Props> = props => {
|
||||
groupName={translate("Elven Magical Songs")}
|
||||
checkPenalty={elvenMagicalSong.static.check_penalty}
|
||||
improvementCost={fromRaw(elvenMagicalSong.static.improvement_cost)}
|
||||
addPoint={addPoint}
|
||||
removePoint={removePoint}
|
||||
remove={remove}
|
||||
addPoint={handleAddPoint}
|
||||
removePoint={handleRemovePoint}
|
||||
setToMaximumPoints={handleSetToMaximumPoints}
|
||||
setToMinimumPoints={handleSetToMinimumPoints}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,22 +3,44 @@ import { geodeRitualsImprovementCost } from "../../../../../shared/domain/rated/
|
||||
import { DisplayedActiveGeodeRitual } from "../../../../../shared/domain/rated/spellActive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import {
|
||||
decrementGeodeRitual,
|
||||
incrementGeodeRitual,
|
||||
removeGeodeRitual,
|
||||
setGeodeRitual,
|
||||
} from "../../../../slices/magicalActions/geodeRitualsSlice.ts"
|
||||
import { ActiveMagicalActionsListItem } from "./ActiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
geodeRitual: DisplayedActiveGeodeRitual
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveGeodeRitualsListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, geodeRitual, sortOrder, addPoint, removePoint, remove } = props
|
||||
const { insertTopMargin, geodeRitual, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
geodeRitual.static.id,
|
||||
geodeRitual.dynamic.value,
|
||||
geodeRitual.maximum,
|
||||
geodeRitual.minimum ?? 0,
|
||||
geodeRitualsImprovementCost,
|
||||
incrementGeodeRitual,
|
||||
decrementGeodeRitual,
|
||||
setGeodeRitual,
|
||||
removeGeodeRitual,
|
||||
)
|
||||
|
||||
return (
|
||||
<ActiveMagicalActionsListItem
|
||||
kind="GeodeRitual"
|
||||
@@ -27,9 +49,11 @@ const ActiveGeodeRitualsListItem: FC<Props> = props => {
|
||||
sortOrder={sortOrder}
|
||||
groupName={translate("Geode Rituals")}
|
||||
improvementCost={geodeRitualsImprovementCost}
|
||||
addPoint={addPoint}
|
||||
removePoint={removePoint}
|
||||
remove={remove}
|
||||
addPoint={handleAddPoint}
|
||||
removePoint={handleRemovePoint}
|
||||
setToMaximumPoints={handleSetToMaximumPoints}
|
||||
setToMinimumPoints={handleSetToMinimumPoints}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,22 +3,44 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedActiveJesterTrick } from "../../../../../shared/domain/rated/spellActive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import {
|
||||
decrementJesterTrick,
|
||||
incrementJesterTrick,
|
||||
removeJesterTrick,
|
||||
setJesterTrick,
|
||||
} from "../../../../slices/magicalActions/jesterTricksSlice.ts"
|
||||
import { ActiveMagicalActionsListItem } from "./ActiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
jesterTrick: DisplayedActiveJesterTrick
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveJesterTricksListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, jesterTrick, sortOrder, addPoint, removePoint, remove } = props
|
||||
const { insertTopMargin, jesterTrick, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
jesterTrick.static.id,
|
||||
jesterTrick.dynamic.value,
|
||||
jesterTrick.maximum,
|
||||
jesterTrick.minimum ?? 0,
|
||||
fromRaw(jesterTrick.static.improvement_cost),
|
||||
incrementJesterTrick,
|
||||
decrementJesterTrick,
|
||||
setJesterTrick,
|
||||
removeJesterTrick,
|
||||
)
|
||||
|
||||
return (
|
||||
<ActiveMagicalActionsListItem
|
||||
kind="JesterTrick"
|
||||
@@ -28,9 +50,11 @@ const ActiveJesterTricksListItem: FC<Props> = props => {
|
||||
groupName={translate("Jester Tricks")}
|
||||
checkPenalty={jesterTrick.static.check_penalty}
|
||||
improvementCost={fromRaw(jesterTrick.static.improvement_cost)}
|
||||
addPoint={addPoint}
|
||||
removePoint={removePoint}
|
||||
remove={remove}
|
||||
addPoint={handleAddPoint}
|
||||
removePoint={handleRemovePoint}
|
||||
setToMaximumPoints={handleSetToMaximumPoints}
|
||||
setToMinimumPoints={handleSetToMinimumPoints}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+14
-10
@@ -42,10 +42,12 @@ type Props<T extends DisplayedActiveMagicalAction> = {
|
||||
sortOrder: SpellsSortOrder
|
||||
groupName: string
|
||||
checkPenalty?: SkillCheckPenalty
|
||||
improvementCost?: ImprovementCost
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
improvementCost: ImprovementCost
|
||||
addPoint: () => void
|
||||
removePoint: () => void
|
||||
setToMaximumPoints: () => void
|
||||
setToMinimumPoints: () => void
|
||||
remove: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,14 +66,16 @@ export const ActiveMagicalActionsListItem = <T extends DisplayedActiveMagicalAct
|
||||
improvementCost,
|
||||
addPoint,
|
||||
removePoint,
|
||||
setToMaximumPoints,
|
||||
setToMinimumPoints,
|
||||
remove,
|
||||
} = props
|
||||
|
||||
const {
|
||||
dynamic: { value },
|
||||
static: { id, check, property, translations },
|
||||
isDecreasable,
|
||||
dynamic: { value },
|
||||
isIncreasable,
|
||||
isDecreasable,
|
||||
} = magicalAction
|
||||
|
||||
const translateMap = useTranslateMap()
|
||||
@@ -107,19 +111,19 @@ export const ActiveMagicalActionsListItem = <T extends DisplayedActiveMagicalAct
|
||||
text={sortOrder === SpellsSortOrder.Group ? `${propertyName} / ${groupName}` : propertyName}
|
||||
/>
|
||||
<ListItemValues>
|
||||
<SkillRating sr={value} addPoint={addPoint} />
|
||||
<SkillRating sr={value} />
|
||||
<SkillCheck check={check} checkPenalty={checkPenalty} />
|
||||
<SkillFill />
|
||||
<SkillImprovementCost ic={improvementCost} />
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isIncreasable}
|
||||
ic={improvementCost}
|
||||
id={id}
|
||||
removeDisabled={!isDecreasable}
|
||||
sr={value}
|
||||
addPoint={addPoint}
|
||||
setToMax={setToMaximumPoints}
|
||||
removePoint={canRemove ? (value === 0 ? remove : removePoint) : undefined}
|
||||
setToMin={canRemove && value > 0 ? setToMinimumPoints : undefined}
|
||||
decrementIsRemove={value === 0}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -3,22 +3,44 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedActiveMagicalDance } from "../../../../../shared/domain/rated/spellActive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import {
|
||||
decrementMagicalDance,
|
||||
incrementMagicalDance,
|
||||
removeMagicalDance,
|
||||
setMagicalDance,
|
||||
} from "../../../../slices/magicalActions/magicalDancesSlice.ts"
|
||||
import { ActiveMagicalActionsListItem } from "./ActiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
magicalDance: DisplayedActiveMagicalDance
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveMagicalDancesListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, magicalDance, sortOrder, addPoint, removePoint, remove } = props
|
||||
const { insertTopMargin, magicalDance, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
magicalDance.static.id,
|
||||
magicalDance.dynamic.value,
|
||||
magicalDance.maximum,
|
||||
magicalDance.minimum ?? 0,
|
||||
fromRaw(magicalDance.static.improvement_cost),
|
||||
incrementMagicalDance,
|
||||
decrementMagicalDance,
|
||||
setMagicalDance,
|
||||
removeMagicalDance,
|
||||
)
|
||||
|
||||
return (
|
||||
<ActiveMagicalActionsListItem
|
||||
kind="MagicalDance"
|
||||
@@ -27,9 +49,11 @@ const ActiveMagicalDancesListItem: FC<Props> = props => {
|
||||
sortOrder={sortOrder}
|
||||
groupName={translate("Magical Dances")}
|
||||
improvementCost={fromRaw(magicalDance.static.improvement_cost)}
|
||||
addPoint={addPoint}
|
||||
removePoint={removePoint}
|
||||
remove={remove}
|
||||
addPoint={handleAddPoint}
|
||||
removePoint={handleRemovePoint}
|
||||
setToMaximumPoints={handleSetToMaximumPoints}
|
||||
setToMinimumPoints={handleSetToMinimumPoints}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+31
-7
@@ -3,22 +3,44 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedActiveMagicalMelody } from "../../../../../shared/domain/rated/spellActive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import {
|
||||
decrementMagicalMelody,
|
||||
incrementMagicalMelody,
|
||||
removeMagicalMelody,
|
||||
setMagicalMelody,
|
||||
} from "../../../../slices/magicalActions/magicalMelodiesSlice.ts"
|
||||
import { ActiveMagicalActionsListItem } from "./ActiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
magicalMelody: DisplayedActiveMagicalMelody
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveMagicalMelodiesListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, magicalMelody, sortOrder, addPoint, removePoint, remove } = props
|
||||
const { insertTopMargin, magicalMelody, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
magicalMelody.static.id,
|
||||
magicalMelody.dynamic.value,
|
||||
magicalMelody.maximum,
|
||||
magicalMelody.minimum ?? 0,
|
||||
fromRaw(magicalMelody.static.improvement_cost),
|
||||
incrementMagicalMelody,
|
||||
decrementMagicalMelody,
|
||||
setMagicalMelody,
|
||||
removeMagicalMelody,
|
||||
)
|
||||
|
||||
return (
|
||||
<ActiveMagicalActionsListItem
|
||||
kind="MagicalMelody"
|
||||
@@ -28,9 +50,11 @@ const ActiveMagicalMelodiesListItem: FC<Props> = props => {
|
||||
groupName={translate("Magical Melodies")}
|
||||
checkPenalty={magicalMelody.static.check_penalty}
|
||||
improvementCost={fromRaw(magicalMelody.static.improvement_cost)}
|
||||
addPoint={addPoint}
|
||||
removePoint={removePoint}
|
||||
remove={remove}
|
||||
addPoint={handleAddPoint}
|
||||
removePoint={handleRemovePoint}
|
||||
setToMaximumPoints={handleSetToMaximumPoints}
|
||||
setToMinimumPoints={handleSetToMinimumPoints}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { DisplayedActiveRitual } from "../../../../../shared/domain/rated/spellA
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { SelectGetById } from "../../../../selectors/basicCapabilitySelectors.ts"
|
||||
import { selectCanRemove } from "../../../../selectors/characterSelectors.ts"
|
||||
@@ -16,6 +17,12 @@ import {
|
||||
changeInlineLibraryEntry,
|
||||
selectInlineLibraryEntryId,
|
||||
} from "../../../../slices/inlineWikiSlice.ts"
|
||||
import {
|
||||
decrementRitual,
|
||||
incrementRitual,
|
||||
removeRitual,
|
||||
setRitual,
|
||||
} from "../../../../slices/ritualsSlice.ts"
|
||||
import { SkillButtons } from "../skills/SkillButtons.tsx"
|
||||
import { SkillCheck } from "../skills/SkillCheck.tsx"
|
||||
import { SkillFill } from "../skills/SkillFill.tsx"
|
||||
@@ -26,9 +33,6 @@ type Props = {
|
||||
insertTopMargin?: boolean
|
||||
ritual: DisplayedActiveRitual
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveRitualsListItem: FC<Props> = props => {
|
||||
@@ -37,14 +41,13 @@ const ActiveRitualsListItem: FC<Props> = props => {
|
||||
ritual: {
|
||||
dynamic: { value },
|
||||
static: { id, check, check_penalty, property, improvement_cost, translations },
|
||||
maximum,
|
||||
minimum,
|
||||
isDecreasable,
|
||||
isIncreasable,
|
||||
isUnfamiliar,
|
||||
},
|
||||
sortOrder,
|
||||
addPoint,
|
||||
removePoint,
|
||||
remove,
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -56,6 +59,24 @@ const ActiveRitualsListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
id,
|
||||
value,
|
||||
maximum,
|
||||
minimum ?? 0,
|
||||
fromRaw(improvement_cost),
|
||||
incrementRitual,
|
||||
decrementRitual,
|
||||
setRitual,
|
||||
removeRitual,
|
||||
)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Ritual", ritual: id })),
|
||||
[dispatch, id],
|
||||
@@ -82,19 +103,19 @@ const ActiveRitualsListItem: FC<Props> = props => {
|
||||
}
|
||||
/>
|
||||
<ListItemValues>
|
||||
<SkillRating sr={value} addPoint={addPoint} />
|
||||
<SkillRating sr={value} />
|
||||
<SkillCheck check={check} checkPenalty={check_penalty} />
|
||||
<SkillFill />
|
||||
<SkillImprovementCost ic={fromRaw(improvement_cost)} />
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isIncreasable}
|
||||
ic={fromRaw(improvement_cost)}
|
||||
id={id}
|
||||
removeDisabled={!isDecreasable}
|
||||
sr={value}
|
||||
addPoint={addPoint}
|
||||
removePoint={canRemove ? (value === 0 ? remove : removePoint) : undefined}
|
||||
addPoint={handleAddPoint}
|
||||
setToMax={handleSetToMaximumPoints}
|
||||
removePoint={canRemove ? (value === 0 ? handleRemovePoint : handleRemove) : undefined}
|
||||
setToMin={canRemove && value > 0 ? handleSetToMinimumPoints : undefined}
|
||||
decrementIsRemove={value === 0}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { DisplayedActiveSpell } from "../../../../../shared/domain/rated/spellAc
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { SelectGetById } from "../../../../selectors/basicCapabilitySelectors.ts"
|
||||
import { selectCanRemove } from "../../../../selectors/characterSelectors.ts"
|
||||
@@ -16,6 +17,12 @@ import {
|
||||
changeInlineLibraryEntry,
|
||||
selectInlineLibraryEntryId,
|
||||
} from "../../../../slices/inlineWikiSlice.ts"
|
||||
import {
|
||||
decrementSpell,
|
||||
incrementSpell,
|
||||
removeSpell,
|
||||
setSpell,
|
||||
} from "../../../../slices/spellsSlice.ts"
|
||||
import { SkillButtons } from "../skills/SkillButtons.tsx"
|
||||
import { SkillCheck } from "../skills/SkillCheck.tsx"
|
||||
import { SkillFill } from "../skills/SkillFill.tsx"
|
||||
@@ -26,9 +33,6 @@ type Props = {
|
||||
insertTopMargin?: boolean
|
||||
spell: DisplayedActiveSpell
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveSpellsListItem: FC<Props> = props => {
|
||||
@@ -37,14 +41,13 @@ const ActiveSpellsListItem: FC<Props> = props => {
|
||||
spell: {
|
||||
dynamic: { value },
|
||||
static: { id, check, check_penalty, property, improvement_cost, translations },
|
||||
maximum,
|
||||
minimum,
|
||||
isDecreasable,
|
||||
isIncreasable,
|
||||
isUnfamiliar,
|
||||
},
|
||||
sortOrder,
|
||||
addPoint,
|
||||
removePoint,
|
||||
remove,
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -54,7 +57,25 @@ const ActiveSpellsListItem: FC<Props> = props => {
|
||||
const canRemove = useAppSelector(selectCanRemove)
|
||||
const getProperty = useAppSelector(SelectGetById.Static.Property)
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
const { name = "???" } = translateMap(translations) ?? {}
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
id,
|
||||
value,
|
||||
maximum,
|
||||
minimum ?? 0,
|
||||
fromRaw(improvement_cost),
|
||||
incrementSpell,
|
||||
decrementSpell,
|
||||
setSpell,
|
||||
removeSpell,
|
||||
)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Spell", spell: id })),
|
||||
@@ -82,19 +103,19 @@ const ActiveSpellsListItem: FC<Props> = props => {
|
||||
}
|
||||
/>
|
||||
<ListItemValues>
|
||||
<SkillRating sr={value} addPoint={addPoint} />
|
||||
<SkillRating sr={value} />
|
||||
<SkillCheck check={check} checkPenalty={check_penalty} />
|
||||
<SkillFill />
|
||||
<SkillImprovementCost ic={fromRaw(improvement_cost)} />
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isIncreasable}
|
||||
ic={fromRaw(improvement_cost)}
|
||||
id={id}
|
||||
removeDisabled={!isDecreasable}
|
||||
sr={value}
|
||||
addPoint={addPoint}
|
||||
removePoint={canRemove ? (value === 0 ? remove : removePoint) : undefined}
|
||||
addPoint={handleAddPoint}
|
||||
setToMax={handleSetToMaximumPoints}
|
||||
removePoint={canRemove ? (value === 0 ? handleRemovePoint : handleRemove) : undefined}
|
||||
setToMin={canRemove && value > 0 ? handleSetToMinimumPoints : undefined}
|
||||
decrementIsRemove={value === 0}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
+31
-7
@@ -3,22 +3,44 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedActiveZibiljaRitual } from "../../../../../shared/domain/rated/spellActive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useActiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import {
|
||||
decrementZibiljaRitual,
|
||||
incrementZibiljaRitual,
|
||||
removeZibiljaRitual,
|
||||
setZibiljaRitual,
|
||||
} from "../../../../slices/magicalActions/zibiljaRitualsSlice.ts"
|
||||
import { ActiveMagicalActionsListItem } from "./ActiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
zibiljaRitual: DisplayedActiveZibiljaRitual
|
||||
sortOrder: SpellsSortOrder
|
||||
addPoint: (id: number) => void
|
||||
removePoint: (id: number) => void
|
||||
remove: (id: number) => void
|
||||
}
|
||||
|
||||
const ActiveZibiljaRitualsListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, zibiljaRitual, sortOrder, addPoint, removePoint, remove } = props
|
||||
const { insertTopMargin, zibiljaRitual, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const {
|
||||
handleAddPoint,
|
||||
handleRemovePoint,
|
||||
handleSetToMaximumPoints,
|
||||
handleSetToMinimumPoints,
|
||||
handleRemove,
|
||||
} = useActiveActivatableActions(
|
||||
zibiljaRitual.static.id,
|
||||
zibiljaRitual.dynamic.value,
|
||||
zibiljaRitual.maximum,
|
||||
zibiljaRitual.minimum ?? 0,
|
||||
fromRaw(zibiljaRitual.static.improvement_cost),
|
||||
incrementZibiljaRitual,
|
||||
decrementZibiljaRitual,
|
||||
setZibiljaRitual,
|
||||
removeZibiljaRitual,
|
||||
)
|
||||
|
||||
return (
|
||||
<ActiveMagicalActionsListItem
|
||||
kind="ZibiljaRitual"
|
||||
@@ -28,9 +50,11 @@ const ActiveZibiljaRitualsListItem: FC<Props> = props => {
|
||||
groupName={translate("Zibilja Rituals")}
|
||||
checkPenalty={zibiljaRitual.static.check_penalty}
|
||||
improvementCost={fromRaw(zibiljaRitual.static.improvement_cost)}
|
||||
addPoint={addPoint}
|
||||
removePoint={removePoint}
|
||||
remove={remove}
|
||||
addPoint={handleAddPoint}
|
||||
removePoint={handleRemovePoint}
|
||||
setToMaximumPoints={handleSetToMaximumPoints}
|
||||
setToMinimumPoints={handleSetToMinimumPoints}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+11
-19
@@ -1,26 +1,28 @@
|
||||
import { FC, memo } from "react"
|
||||
import {
|
||||
ImprovementCost,
|
||||
fromRaw,
|
||||
} from "../../../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { DisplayedInactiveAnimistPower } from "../../../../../shared/domain/rated/spellInactive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { assertExhaustive } from "../../../../../shared/utils/typeSafety.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { addAnimistPower } from "../../../../slices/magicalActions/animistPowersSlice.ts"
|
||||
import { InactiveMagicalActionsListItem } from "./InactiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
animistPower: DisplayedInactiveAnimistPower
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveAnimistPowersListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, animistPower, sortOrder, add } = props
|
||||
const { insertTopMargin, animistPower, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
animistPower.static.id,
|
||||
animistPower.improvementCost,
|
||||
addAnimistPower,
|
||||
)
|
||||
|
||||
return (
|
||||
<InactiveMagicalActionsListItem
|
||||
kind="AnimistPower"
|
||||
@@ -28,18 +30,8 @@ const InactiveAnimistPowersListItem: FC<Props> = props => {
|
||||
magicalAction={animistPower}
|
||||
sortOrder={sortOrder}
|
||||
groupName={translate("Animist Powers")}
|
||||
improvementCost={(() => {
|
||||
switch (animistPower.static.improvement_cost.tag) {
|
||||
case "Fixed":
|
||||
return fromRaw(animistPower.static.improvement_cost.fixed)
|
||||
case "ByPrimaryPatron":
|
||||
// TODO: Replace with derived improvement cost
|
||||
return ImprovementCost.D
|
||||
default:
|
||||
return assertExhaustive(animistPower.static.improvement_cost)
|
||||
}
|
||||
})()}
|
||||
add={add}
|
||||
improvementCost={animistPower.improvementCost}
|
||||
add={handleAdd}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ const InactiveCantripsListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const handleAdd = useCallback(() => {
|
||||
add(id)
|
||||
}, [add, id])
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Cantrip", cantrip: id })),
|
||||
[dispatch, id],
|
||||
@@ -72,7 +76,7 @@ const InactiveCantripsListItem: FC<Props> = props => {
|
||||
<ListItemValues>
|
||||
<SkillFill />
|
||||
</ListItemValues>
|
||||
<SkillButtons id={id} addPoint={add} selectForInfo={handleSelectForInfo} />
|
||||
<SkillButtons addPoint={handleAdd} selectForInfo={handleSelectForInfo} />
|
||||
</ListItem>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,20 +3,27 @@ import { cursesImprovementCost } from "../../../../../shared/domain/rated/magica
|
||||
import { DisplayedInactiveCurse } from "../../../../../shared/domain/rated/spellInactive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { addCurse } from "../../../../slices/magicalActions/cursesSlice.ts"
|
||||
import { InactiveMagicalActionsListItem } from "./InactiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
curse: DisplayedInactiveCurse
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveCursesListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, curse, sortOrder, add } = props
|
||||
const { insertTopMargin, curse, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
curse.static.id,
|
||||
cursesImprovementCost,
|
||||
addCurse,
|
||||
)
|
||||
|
||||
return (
|
||||
<InactiveMagicalActionsListItem
|
||||
kind="Curse"
|
||||
@@ -26,7 +33,7 @@ const InactiveCursesListItem: FC<Props> = props => {
|
||||
groupName={translate("Curses")}
|
||||
checkPenalty={curse.static.check_penalty}
|
||||
improvementCost={cursesImprovementCost}
|
||||
add={add}
|
||||
add={handleAdd}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+10
-3
@@ -3,20 +3,27 @@ import { dominationRitualsImprovementCost } from "../../../../../shared/domain/r
|
||||
import { DisplayedInactiveDominationRitual } from "../../../../../shared/domain/rated/spellInactive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { addDominationRitual } from "../../../../slices/magicalActions/dominationRitualsSlice.ts"
|
||||
import { InactiveMagicalActionsListItem } from "./InactiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
dominationRitual: DisplayedInactiveDominationRitual
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveDominationRitualsListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, dominationRitual, sortOrder, add } = props
|
||||
const { insertTopMargin, dominationRitual, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
dominationRitual.static.id,
|
||||
dominationRitualsImprovementCost,
|
||||
addDominationRitual,
|
||||
)
|
||||
|
||||
return (
|
||||
<InactiveMagicalActionsListItem
|
||||
kind="DominationRitual"
|
||||
@@ -26,7 +33,7 @@ const InactiveDominationRitualsListItem: FC<Props> = props => {
|
||||
groupName={translate("Domination Rituals")}
|
||||
checkPenalty={dominationRitual.static.check_penalty}
|
||||
improvementCost={dominationRitualsImprovementCost}
|
||||
add={add}
|
||||
add={handleAdd}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+10
-3
@@ -3,20 +3,27 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedInactiveElvenMagicalSong } from "../../../../../shared/domain/rated/spellInactive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { addElvenMagicalSong } from "../../../../slices/magicalActions/elvenMagicalSongsSlice.ts"
|
||||
import { InactiveMagicalActionsListItem } from "./InactiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
elvenMagicalSong: DisplayedInactiveElvenMagicalSong
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveElvenMagicalSongsListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, elvenMagicalSong, sortOrder, add } = props
|
||||
const { insertTopMargin, elvenMagicalSong, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
elvenMagicalSong.static.id,
|
||||
fromRaw(elvenMagicalSong.static.improvement_cost),
|
||||
addElvenMagicalSong,
|
||||
)
|
||||
|
||||
return (
|
||||
<InactiveMagicalActionsListItem
|
||||
kind="ElvenMagicalSong"
|
||||
@@ -26,7 +33,7 @@ const InactiveElvenMagicalSongsListItem: FC<Props> = props => {
|
||||
groupName={translate("Elven Magical Songs")}
|
||||
checkPenalty={elvenMagicalSong.static.check_penalty}
|
||||
improvementCost={fromRaw(elvenMagicalSong.static.improvement_cost)}
|
||||
add={add}
|
||||
add={handleAdd}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+10
-3
@@ -3,20 +3,27 @@ import { geodeRitualsImprovementCost } from "../../../../../shared/domain/rated/
|
||||
import { DisplayedInactiveGeodeRitual } from "../../../../../shared/domain/rated/spellInactive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { addGeodeRitual } from "../../../../slices/magicalActions/geodeRitualsSlice.ts"
|
||||
import { InactiveMagicalActionsListItem } from "./InactiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
geodeRitual: DisplayedInactiveGeodeRitual
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveGeodeRitualsListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, geodeRitual, sortOrder, add } = props
|
||||
const { insertTopMargin, geodeRitual, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
geodeRitual.static.id,
|
||||
geodeRitualsImprovementCost,
|
||||
addGeodeRitual,
|
||||
)
|
||||
|
||||
return (
|
||||
<InactiveMagicalActionsListItem
|
||||
kind="GeodeRitual"
|
||||
@@ -25,7 +32,7 @@ const InactiveGeodeRitualsListItem: FC<Props> = props => {
|
||||
sortOrder={sortOrder}
|
||||
groupName={translate("Geode Rituals")}
|
||||
improvementCost={geodeRitualsImprovementCost}
|
||||
add={add}
|
||||
add={handleAdd}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+10
-3
@@ -3,20 +3,27 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedInactiveJesterTrick } from "../../../../../shared/domain/rated/spellInactive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { addJesterTrick } from "../../../../slices/magicalActions/jesterTricksSlice.ts"
|
||||
import { InactiveMagicalActionsListItem } from "./InactiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
jesterTrick: DisplayedInactiveJesterTrick
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveJesterTricksListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, jesterTrick, sortOrder, add } = props
|
||||
const { insertTopMargin, jesterTrick, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
jesterTrick.static.id,
|
||||
fromRaw(jesterTrick.static.improvement_cost),
|
||||
addJesterTrick,
|
||||
)
|
||||
|
||||
return (
|
||||
<InactiveMagicalActionsListItem
|
||||
kind="JesterTrick"
|
||||
@@ -26,7 +33,7 @@ const InactiveJesterTricksListItem: FC<Props> = props => {
|
||||
groupName={translate("Jester Tricks")}
|
||||
checkPenalty={jesterTrick.static.check_penalty}
|
||||
improvementCost={fromRaw(jesterTrick.static.improvement_cost)}
|
||||
add={add}
|
||||
add={handleAdd}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+2
-8
@@ -41,7 +41,7 @@ type Props<T extends DisplayedInactiveMagicalAction> = {
|
||||
groupName: string
|
||||
checkPenalty?: SkillCheckPenalty
|
||||
improvementCost?: ImprovementCost
|
||||
add: (id: number) => void
|
||||
add: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,13 +101,7 @@ export const InactiveMagicalActionsListItem = <T extends DisplayedInactiveMagica
|
||||
<SkillFill />
|
||||
<SkillImprovementCost ic={improvementCost} />
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isAvailable}
|
||||
ic={improvementCost}
|
||||
id={id}
|
||||
addPoint={add}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
<SkillButtons addDisabled={!isAvailable} addPoint={add} selectForInfo={handleSelectForInfo} />
|
||||
</ListItem>
|
||||
)
|
||||
}
|
||||
|
||||
+10
-3
@@ -3,20 +3,27 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedInactiveMagicalDance } from "../../../../../shared/domain/rated/spellInactive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { addMagicalDance } from "../../../../slices/magicalActions/magicalDancesSlice.ts"
|
||||
import { InactiveMagicalActionsListItem } from "./InactiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
magicalDance: DisplayedInactiveMagicalDance
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveMagicalDancesListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, magicalDance, sortOrder, add } = props
|
||||
const { insertTopMargin, magicalDance, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
magicalDance.static.id,
|
||||
fromRaw(magicalDance.static.improvement_cost),
|
||||
addMagicalDance,
|
||||
)
|
||||
|
||||
return (
|
||||
<InactiveMagicalActionsListItem
|
||||
kind="MagicalDance"
|
||||
@@ -25,7 +32,7 @@ const InactiveMagicalDancesListItem: FC<Props> = props => {
|
||||
sortOrder={sortOrder}
|
||||
groupName={translate("Magical Dances")}
|
||||
improvementCost={fromRaw(magicalDance.static.improvement_cost)}
|
||||
add={add}
|
||||
add={handleAdd}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
+10
-3
@@ -3,20 +3,27 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedInactiveMagicalMelody } from "../../../../../shared/domain/rated/spellInactive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { addMagicalMelody } from "../../../../slices/magicalActions/magicalMelodiesSlice.ts"
|
||||
import { InactiveMagicalActionsListItem } from "./InactiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
magicalMelody: DisplayedInactiveMagicalMelody
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveMagicalMelodiesListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, magicalMelody, sortOrder, add } = props
|
||||
const { insertTopMargin, magicalMelody, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
magicalMelody.static.id,
|
||||
fromRaw(magicalMelody.static.improvement_cost),
|
||||
addMagicalMelody,
|
||||
)
|
||||
|
||||
return (
|
||||
<InactiveMagicalActionsListItem
|
||||
kind="MagicalMelody"
|
||||
@@ -26,7 +33,7 @@ const InactiveMagicalMelodiesListItem: FC<Props> = props => {
|
||||
groupName={translate("Magical Melodies")}
|
||||
checkPenalty={magicalMelody.static.check_penalty}
|
||||
improvementCost={fromRaw(magicalMelody.static.improvement_cost)}
|
||||
add={add}
|
||||
add={handleAdd}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,14 @@ import { DisplayedInactiveRitual } from "../../../../../shared/domain/rated/spel
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { SelectGetById } from "../../../../selectors/basicCapabilitySelectors.ts"
|
||||
import {
|
||||
changeInlineLibraryEntry,
|
||||
selectInlineLibraryEntryId,
|
||||
} from "../../../../slices/inlineWikiSlice.ts"
|
||||
import { addRitual } from "../../../../slices/ritualsSlice.ts"
|
||||
import { SkillButtons } from "../skills/SkillButtons.tsx"
|
||||
import { SkillCheck } from "../skills/SkillCheck.tsx"
|
||||
import { SkillFill } from "../skills/SkillFill.tsx"
|
||||
@@ -24,7 +26,6 @@ type Props = {
|
||||
insertTopMargin?: boolean
|
||||
ritual: DisplayedInactiveRitual
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveRitualsListItem: FC<Props> = props => {
|
||||
@@ -36,7 +37,6 @@ const InactiveRitualsListItem: FC<Props> = props => {
|
||||
isUnfamiliar,
|
||||
},
|
||||
sortOrder,
|
||||
add,
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -47,6 +47,8 @@ const InactiveRitualsListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(id, fromRaw(improvement_cost), addRitual)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Ritual", ritual: id })),
|
||||
[dispatch, id],
|
||||
@@ -62,6 +64,7 @@ const InactiveRitualsListItem: FC<Props> = props => {
|
||||
insertTopMargin={insertTopMargin}
|
||||
active={inlineLibraryEntryId?.tag === "Ritual" && inlineLibraryEntryId.ritual === id}
|
||||
unrecommended={isUnfamiliar}
|
||||
disabled={!isAvailable}
|
||||
>
|
||||
<ListItemName name={name} onClick={handleSelectForInfo} />
|
||||
<ListItemSeparator />
|
||||
@@ -79,9 +82,7 @@ const InactiveRitualsListItem: FC<Props> = props => {
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isAvailable}
|
||||
ic={fromRaw(improvement_cost)}
|
||||
id={id}
|
||||
addPoint={add}
|
||||
addPoint={handleAdd}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -9,12 +9,14 @@ import { DisplayedInactiveSpell } from "../../../../../shared/domain/rated/spell
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useTranslateMap } from "../../../../../shared/hooks/translateMap.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { useAppDispatch, useAppSelector } from "../../../../hooks/redux.ts"
|
||||
import { SelectGetById } from "../../../../selectors/basicCapabilitySelectors.ts"
|
||||
import {
|
||||
changeInlineLibraryEntry,
|
||||
selectInlineLibraryEntryId,
|
||||
} from "../../../../slices/inlineWikiSlice.ts"
|
||||
import { addSpell } from "../../../../slices/spellsSlice.ts"
|
||||
import { SkillButtons } from "../skills/SkillButtons.tsx"
|
||||
import { SkillCheck } from "../skills/SkillCheck.tsx"
|
||||
import { SkillFill } from "../skills/SkillFill.tsx"
|
||||
@@ -24,7 +26,6 @@ type Props = {
|
||||
insertTopMargin?: boolean
|
||||
spell: DisplayedInactiveSpell
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveSpellsListItem: FC<Props> = props => {
|
||||
@@ -36,7 +37,6 @@ const InactiveSpellsListItem: FC<Props> = props => {
|
||||
isUnfamiliar,
|
||||
},
|
||||
sortOrder,
|
||||
add,
|
||||
} = props
|
||||
|
||||
const translate = useTranslate()
|
||||
@@ -47,6 +47,8 @@ const InactiveSpellsListItem: FC<Props> = props => {
|
||||
|
||||
const { name = "" } = translateMap(translations) ?? {}
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(id, fromRaw(improvement_cost), addSpell)
|
||||
|
||||
const handleSelectForInfo = useCallback(
|
||||
() => dispatch(changeInlineLibraryEntry({ tag: "Spell", spell: id })),
|
||||
[dispatch, id],
|
||||
@@ -62,6 +64,7 @@ const InactiveSpellsListItem: FC<Props> = props => {
|
||||
insertTopMargin={insertTopMargin}
|
||||
active={inlineLibraryEntryId?.tag === "Spell" && inlineLibraryEntryId.spell === id}
|
||||
unrecommended={isUnfamiliar}
|
||||
disabled={!isAvailable}
|
||||
>
|
||||
<ListItemName name={name} onClick={handleSelectForInfo} />
|
||||
<ListItemSeparator />
|
||||
@@ -79,9 +82,7 @@ const InactiveSpellsListItem: FC<Props> = props => {
|
||||
</ListItemValues>
|
||||
<SkillButtons
|
||||
addDisabled={!isAvailable}
|
||||
ic={fromRaw(improvement_cost)}
|
||||
id={id}
|
||||
addPoint={add}
|
||||
addPoint={handleAdd}
|
||||
selectForInfo={handleSelectForInfo}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
+10
-3
@@ -3,20 +3,27 @@ import { fromRaw } from "../../../../../shared/domain/adventurePoints/improvemen
|
||||
import { DisplayedInactiveZibiljaRitual } from "../../../../../shared/domain/rated/spellInactive.ts"
|
||||
import { SpellsSortOrder } from "../../../../../shared/domain/sortOrders.ts"
|
||||
import { useTranslate } from "../../../../../shared/hooks/translate.ts"
|
||||
import { useInactiveActivatableActions } from "../../../../hooks/ratedActions.ts"
|
||||
import { addZibiljaRitual } from "../../../../slices/magicalActions/zibiljaRitualsSlice.ts"
|
||||
import { InactiveMagicalActionsListItem } from "./InactiveMagicalActionsListItem.tsx"
|
||||
|
||||
type Props = {
|
||||
insertTopMargin?: boolean
|
||||
zibiljaRitual: DisplayedInactiveZibiljaRitual
|
||||
sortOrder: SpellsSortOrder
|
||||
add: (id: number) => void
|
||||
}
|
||||
|
||||
const InactiveZibiljaRitualsListItem: FC<Props> = props => {
|
||||
const { insertTopMargin, zibiljaRitual, sortOrder, add } = props
|
||||
const { insertTopMargin, zibiljaRitual, sortOrder } = props
|
||||
|
||||
const translate = useTranslate()
|
||||
|
||||
const { handleAdd } = useInactiveActivatableActions(
|
||||
zibiljaRitual.static.id,
|
||||
fromRaw(zibiljaRitual.static.improvement_cost),
|
||||
addZibiljaRitual,
|
||||
)
|
||||
|
||||
return (
|
||||
<InactiveMagicalActionsListItem
|
||||
kind="ZibiljaRitual"
|
||||
@@ -26,7 +33,7 @@ const InactiveZibiljaRitualsListItem: FC<Props> = props => {
|
||||
groupName={translate("Zibilja Rituals")}
|
||||
checkPenalty={zibiljaRitual.static.check_penalty}
|
||||
improvementCost={fromRaw(zibiljaRitual.static.improvement_cost)}
|
||||
add={add}
|
||||
add={handleAdd}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,73 +30,7 @@ import {
|
||||
selectVisibleInactiveSpellworks,
|
||||
} from "../../../../selectors/spellSelectors.ts"
|
||||
import { addCantrip, removeCantrip } from "../../../../slices/cantripsSlice.ts"
|
||||
import {
|
||||
addAnimistPower,
|
||||
decrementAnimistPower,
|
||||
incrementAnimistPower,
|
||||
removeAnimistPower,
|
||||
} from "../../../../slices/magicalActions/animistPowersSlice.ts"
|
||||
import {
|
||||
addCurse,
|
||||
decrementCurse,
|
||||
incrementCurse,
|
||||
removeCurse,
|
||||
} from "../../../../slices/magicalActions/cursesSlice.ts"
|
||||
import {
|
||||
addDominationRitual,
|
||||
decrementDominationRitual,
|
||||
incrementDominationRitual,
|
||||
removeDominationRitual,
|
||||
} from "../../../../slices/magicalActions/dominationRitualsSlice.ts"
|
||||
import {
|
||||
addElvenMagicalSong,
|
||||
decrementElvenMagicalSong,
|
||||
incrementElvenMagicalSong,
|
||||
removeElvenMagicalSong,
|
||||
} from "../../../../slices/magicalActions/elvenMagicalSongsSlice.ts"
|
||||
import {
|
||||
addGeodeRitual,
|
||||
decrementGeodeRitual,
|
||||
incrementGeodeRitual,
|
||||
removeGeodeRitual,
|
||||
} from "../../../../slices/magicalActions/geodeRitualsSlice.ts"
|
||||
import {
|
||||
addJesterTrick,
|
||||
decrementJesterTrick,
|
||||
incrementJesterTrick,
|
||||
removeJesterTrick,
|
||||
} from "../../../../slices/magicalActions/jesterTricksSlice.ts"
|
||||
import {
|
||||
addMagicalDance,
|
||||
decrementMagicalDance,
|
||||
incrementMagicalDance,
|
||||
removeMagicalDance,
|
||||
} from "../../../../slices/magicalActions/magicalDancesSlice.ts"
|
||||
import {
|
||||
addMagicalMelody,
|
||||
decrementMagicalMelody,
|
||||
incrementMagicalMelody,
|
||||
removeMagicalMelody,
|
||||
} from "../../../../slices/magicalActions/magicalMelodiesSlice.ts"
|
||||
import {
|
||||
addZibiljaRitual,
|
||||
decrementZibiljaRitual,
|
||||
incrementZibiljaRitual,
|
||||
removeZibiljaRitual,
|
||||
} from "../../../../slices/magicalActions/zibiljaRitualsSlice.ts"
|
||||
import {
|
||||
addRitual,
|
||||
decrementRitual,
|
||||
incrementRitual,
|
||||
removeRitual,
|
||||
} from "../../../../slices/ritualsSlice.ts"
|
||||
import { changeSpellsSortOrder, selectSpellsSortOrder } from "../../../../slices/settingsSlice.ts"
|
||||
import {
|
||||
addSpell,
|
||||
decrementSpell,
|
||||
incrementSpell,
|
||||
removeSpell,
|
||||
} from "../../../../slices/spellsSlice.ts"
|
||||
import { ActiveAnimistPowersListItem } from "./ActiveAnimistPowersListItem.tsx"
|
||||
import { ActiveCantripsListItem } from "./ActiveCantripsListItem.tsx"
|
||||
import { ActiveCursesListItem } from "./ActiveCursesListItem.tsx"
|
||||
@@ -191,163 +125,10 @@ export const Spells: FC = () => {
|
||||
],
|
||||
)
|
||||
|
||||
const handleAddCantrip = useCallback((id: number) => dispatch(addCantrip(id)), [dispatch])
|
||||
const handleRemoveCantrip = useCallback((id: number) => dispatch(removeCantrip(id)), [dispatch])
|
||||
|
||||
const handleAddSpell = useCallback((id: number) => dispatch(addSpell(id)), [dispatch])
|
||||
const handleRemoveSpell = useCallback((id: number) => dispatch(removeSpell(id)), [dispatch])
|
||||
const handleAddSpellPoint = useCallback((id: number) => dispatch(incrementSpell(id)), [dispatch])
|
||||
const handleRemoveSpellPoint = useCallback(
|
||||
(id: number) => dispatch(decrementSpell(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddRitual = useCallback((id: number) => dispatch(addRitual(id)), [dispatch])
|
||||
const handleRemoveRitual = useCallback((id: number) => dispatch(removeRitual(id)), [dispatch])
|
||||
const handleAddRitualPoint = useCallback(
|
||||
(id: number) => dispatch(incrementRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveRitualPoint = useCallback(
|
||||
(id: number) => dispatch(decrementRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddCurse = useCallback((id: number) => dispatch(addCurse(id)), [dispatch])
|
||||
const handleRemoveCurse = useCallback((id: number) => dispatch(removeCurse(id)), [dispatch])
|
||||
const handleAddCursePoint = useCallback((id: number) => dispatch(incrementCurse(id)), [dispatch])
|
||||
const handleRemoveCursePoint = useCallback(
|
||||
(id: number) => dispatch(decrementCurse(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddElvenMagicalSong = useCallback(
|
||||
(id: number) => dispatch(addElvenMagicalSong(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveElvenMagicalSong = useCallback(
|
||||
(id: number) => dispatch(removeElvenMagicalSong(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleAddElvenMagicalSongPoint = useCallback(
|
||||
(id: number) => dispatch(incrementElvenMagicalSong(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveElvenMagicalSongPoint = useCallback(
|
||||
(id: number) => dispatch(decrementElvenMagicalSong(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddDominationRitual = useCallback(
|
||||
(id: number) => dispatch(addDominationRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveDominationRitual = useCallback(
|
||||
(id: number) => dispatch(removeDominationRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleAddDominationRitualPoint = useCallback(
|
||||
(id: number) => dispatch(incrementDominationRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveDominationRitualPoint = useCallback(
|
||||
(id: number) => dispatch(decrementDominationRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddMagicalDance = useCallback(
|
||||
(id: number) => dispatch(addMagicalDance(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveMagicalDance = useCallback(
|
||||
(id: number) => dispatch(removeMagicalDance(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleAddMagicalDancePoint = useCallback(
|
||||
(id: number) => dispatch(incrementMagicalDance(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveMagicalDancePoint = useCallback(
|
||||
(id: number) => dispatch(decrementMagicalDance(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddMagicalMelody = useCallback(
|
||||
(id: number) => dispatch(addMagicalMelody(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveMagicalMelody = useCallback(
|
||||
(id: number) => dispatch(removeMagicalMelody(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleAddMagicalMelodyPoint = useCallback(
|
||||
(id: number) => dispatch(incrementMagicalMelody(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveMagicalMelodyPoint = useCallback(
|
||||
(id: number) => dispatch(decrementMagicalMelody(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddJesterTrick = useCallback((id: number) => dispatch(addJesterTrick(id)), [dispatch])
|
||||
const handleRemoveJesterTrick = useCallback(
|
||||
(id: number) => dispatch(removeJesterTrick(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleAddJesterTrickPoint = useCallback(
|
||||
(id: number) => dispatch(incrementJesterTrick(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveJesterTrickPoint = useCallback(
|
||||
(id: number) => dispatch(decrementJesterTrick(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddAnimistPower = useCallback(
|
||||
(id: number) => dispatch(addAnimistPower(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveAnimistPower = useCallback(
|
||||
(id: number) => dispatch(removeAnimistPower(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleAddAnimistPowerPoint = useCallback(
|
||||
(id: number) => dispatch(incrementAnimistPower(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveAnimistPowerPoint = useCallback(
|
||||
(id: number) => dispatch(decrementAnimistPower(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddGeodeRitual = useCallback((id: number) => dispatch(addGeodeRitual(id)), [dispatch])
|
||||
const handleRemoveGeodeRitual = useCallback(
|
||||
(id: number) => dispatch(removeGeodeRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleAddGeodeRitualPoint = useCallback(
|
||||
(id: number) => dispatch(incrementGeodeRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveGeodeRitualPoint = useCallback(
|
||||
(id: number) => dispatch(decrementGeodeRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
const handleAddZibiljaRitual = useCallback(
|
||||
(id: number) => dispatch(addZibiljaRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveZibiljaRitual = useCallback(
|
||||
(id: number) => dispatch(removeZibiljaRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleAddZibiljaRitualPoint = useCallback(
|
||||
(id: number) => dispatch(incrementZibiljaRitual(id)),
|
||||
[dispatch],
|
||||
)
|
||||
const handleRemoveZibiljaRitualPoint = useCallback(
|
||||
(id: number) => dispatch(decrementZibiljaRitual(id)),
|
||||
// TODO: Check available AP
|
||||
const handleAddCantrip = useCallback((id: number) => dispatch(addCantrip({ id })), [dispatch])
|
||||
const handleRemoveCantrip = useCallback(
|
||||
(id: number) => dispatch(removeCantrip({ id })),
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
@@ -391,17 +172,17 @@ export const Spells: FC = () => {
|
||||
</Options>
|
||||
<Main classOnly>
|
||||
<ListHeader>
|
||||
<ListHeaderTag className="name">{translate("spells.header.name")}</ListHeaderTag>
|
||||
<ListHeaderTag className="name">{translate("Name")}</ListHeaderTag>
|
||||
<ListHeaderTag className="group">
|
||||
{translate("spells.header.property")}
|
||||
{sortOrder === "group" ? ` / ${translate("spells.header.group")}` : null}
|
||||
{translate("Property")}
|
||||
{sortOrder === "group" ? ` / ${translate("Group")}` : null}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="check">{translate("spells.header.check")}</ListHeaderTag>
|
||||
<ListHeaderTag className="mod" hint={translate("spells.header.checkmodifier.tooltip")}>
|
||||
{translate("spells.header.checkmodifier")}
|
||||
<ListHeaderTag className="check">{translate("Check")}</ListHeaderTag>
|
||||
<ListHeaderTag className="mod" hint={translate("Check Modifier")}>
|
||||
{translate("Mod")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="ic" hint={translate("spells.header.improvementcost.tooltip")}>
|
||||
{translate("spells.header.improvementcost")}
|
||||
<ListHeaderTag className="ic" hint={translate("Improvement Cost")}>
|
||||
{translate("IC")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="btn-placeholder" />
|
||||
<ListHeaderTag className="btn-placeholder" />
|
||||
@@ -429,7 +210,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
spell={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddSpell}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -440,7 +220,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
ritual={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddRitual}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -451,7 +230,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
curse={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddCurse}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -462,7 +240,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
elvenMagicalSong={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddElvenMagicalSong}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -473,7 +250,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
dominationRitual={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddDominationRitual}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -484,7 +260,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
magicalDance={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddMagicalDance}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -495,7 +270,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
magicalMelody={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddMagicalMelody}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -506,7 +280,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
jesterTrick={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddJesterTrick}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -517,7 +290,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
animistPower={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddAnimistPower}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -528,7 +300,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
geodeRitual={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddGeodeRitual}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -539,7 +310,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, inactiveList[i - 1])}
|
||||
zibiljaRitual={x}
|
||||
sortOrder={sortOrder}
|
||||
add={handleAddZibiljaRitual}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -589,20 +359,20 @@ export const Spells: FC = () => {
|
||||
</Options>
|
||||
<Main>
|
||||
<ListHeader>
|
||||
<ListHeaderTag className="name">{translate("spells.header.name")}</ListHeaderTag>
|
||||
<ListHeaderTag className="name">{translate("Name")}</ListHeaderTag>
|
||||
<ListHeaderTag className="group">
|
||||
{translate("spells.header.property")}
|
||||
{sortOrder === "group" ? ` / ${translate("spells.header.group")}` : null}
|
||||
{translate("Property")}
|
||||
{sortOrder === "group" ? ` / ${translate("Group")}` : null}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="value" hint={translate("spells.header.skillrating.tooltip")}>
|
||||
{translate("spells.header.skillrating")}
|
||||
<ListHeaderTag className="value" hint={translate("Skill Rating")}>
|
||||
{translate("SR")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="check">{translate("spells.header.check")}</ListHeaderTag>
|
||||
<ListHeaderTag className="mod" hint={translate("spells.header.checkmodifier.tooltip")}>
|
||||
{translate("spells.header.checkmodifier")}
|
||||
<ListHeaderTag className="check">{translate("Check")}</ListHeaderTag>
|
||||
<ListHeaderTag className="mod" hint={translate("Check Modifier")}>
|
||||
{translate("Mod")}
|
||||
</ListHeaderTag>
|
||||
<ListHeaderTag className="ic" hint={translate("spells.header.improvementcost.tooltip")}>
|
||||
{translate("spells.header.improvementcost")}
|
||||
<ListHeaderTag className="ic" hint={translate("Improvement Cost")}>
|
||||
{translate("IC")}
|
||||
</ListHeaderTag>
|
||||
{canRemove ? <ListHeaderTag className="btn-placeholder" /> : null}
|
||||
<ListHeaderTag className="btn-placeholder" />
|
||||
@@ -631,9 +401,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
spell={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddSpellPoint}
|
||||
removePoint={handleRemoveSpellPoint}
|
||||
remove={handleRemoveSpell}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -644,9 +411,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
ritual={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddRitualPoint}
|
||||
removePoint={handleRemoveRitualPoint}
|
||||
remove={handleRemoveRitual}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -657,9 +421,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
curse={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddCursePoint}
|
||||
removePoint={handleRemoveCursePoint}
|
||||
remove={handleRemoveCurse}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -670,9 +431,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
elvenMagicalSong={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddElvenMagicalSongPoint}
|
||||
removePoint={handleRemoveElvenMagicalSongPoint}
|
||||
remove={handleRemoveElvenMagicalSong}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -683,9 +441,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
dominationRitual={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddDominationRitualPoint}
|
||||
removePoint={handleRemoveDominationRitualPoint}
|
||||
remove={handleRemoveDominationRitual}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -696,9 +451,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
magicalDance={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddMagicalDancePoint}
|
||||
removePoint={handleRemoveMagicalDancePoint}
|
||||
remove={handleRemoveMagicalDance}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -709,9 +461,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
magicalMelody={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddMagicalMelodyPoint}
|
||||
removePoint={handleRemoveMagicalMelodyPoint}
|
||||
remove={handleRemoveMagicalMelody}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -722,9 +471,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
jesterTrick={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddJesterTrickPoint}
|
||||
removePoint={handleRemoveJesterTrickPoint}
|
||||
remove={handleRemoveJesterTrick}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -735,9 +481,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
animistPower={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddAnimistPowerPoint}
|
||||
removePoint={handleRemoveAnimistPowerPoint}
|
||||
remove={handleRemoveAnimistPower}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -748,9 +491,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
geodeRitual={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddGeodeRitualPoint}
|
||||
removePoint={handleRemoveGeodeRitualPoint}
|
||||
remove={handleRemoveGeodeRitual}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -761,9 +501,6 @@ export const Spells: FC = () => {
|
||||
insertTopMargin={isTopMarginNeeded(sortOrder, x, activeList[i - 1])}
|
||||
zibiljaRitual={x}
|
||||
sortOrder={sortOrder}
|
||||
addPoint={handleAddZibiljaRitualPoint}
|
||||
removePoint={handleRemoveZibiljaRitualPoint}
|
||||
remove={handleRemoveZibiljaRitual}
|
||||
/>
|
||||
)
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
ImprovementCost,
|
||||
adventurePointsForRange,
|
||||
} from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { RatedAdventurePointsCache } from "../../shared/domain/adventurePoints/ratedEntry.ts"
|
||||
import { count } from "../../shared/utils/array.ts"
|
||||
import {
|
||||
selectCurrentCharacter,
|
||||
@@ -43,16 +42,13 @@ import { selectMagicalAndBlessedAdvantagesAndDisadvantagesCache } from "../slice
|
||||
import { SelectAll } from "./basicCapabilitySelectors.ts"
|
||||
|
||||
const sumRatedMaps = (
|
||||
...ratedMaps: (Record<number, { cachedAdventurePoints: RatedAdventurePointsCache }> | undefined)[]
|
||||
): SpentAdventurePoints =>
|
||||
...ratedMaps: (Record<number, { cachedAdventurePoints: AdventurePointsCache }> | undefined)[]
|
||||
): AdventurePointsCache =>
|
||||
ratedMaps
|
||||
.flatMap(ratedMap => Object.values(ratedMap ?? {}))
|
||||
.reduce(
|
||||
(acc, rated) => ({
|
||||
general: acc.general + rated.cachedAdventurePoints.general,
|
||||
bound: acc.bound + rated.cachedAdventurePoints.bound,
|
||||
}),
|
||||
{ general: 0, bound: 0 },
|
||||
(acc, rated) => addAdventurePointsCaches(acc, rated.cachedAdventurePoints),
|
||||
emptyAdventurePointsCache,
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -104,7 +100,7 @@ export const selectAdventurePointsSpentOnLiturgicalChants = createSelector(
|
||||
sumRatedMaps,
|
||||
)
|
||||
|
||||
const sumTinyActivatables = (tinyActivatables: TinyActivatable[]): SpentAdventurePoints => ({
|
||||
const sumTinyActivatables = (tinyActivatables: TinyActivatable[]): AdventurePointsCache => ({
|
||||
general: count(tinyActivatables, isTinyActivatableActive),
|
||||
bound: 0,
|
||||
})
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { createSelector } from "@reduxjs/toolkit"
|
||||
import { Attribute } from "optolith-database-schema/types/Attribute"
|
||||
import { getCarryingCapacity } from "../../shared/domain/equipment.ts"
|
||||
import {
|
||||
AdvantageIdentifier,
|
||||
AttributeIdentifier,
|
||||
OptionalRuleIdentifier,
|
||||
} from "../../shared/domain/identifier.ts"
|
||||
import {
|
||||
createEmptyDynamicAttribute,
|
||||
getAttributeValue,
|
||||
} from "../../shared/domain/rated/attribute.ts"
|
||||
import {
|
||||
getAttributeMaximum,
|
||||
getAttributeMinimaByAssociatedAttributes,
|
||||
@@ -15,7 +20,6 @@ import {
|
||||
import { Rated } from "../../shared/domain/rated/ratedEntry.ts"
|
||||
import { isNotNullish } from "../../shared/utils/nullable.ts"
|
||||
import { createPropertySelector } from "../../shared/utils/redux.ts"
|
||||
import { attributeValue, createInitialDynamicAttribute } from "../slices/attributesSlice.ts"
|
||||
import {
|
||||
selectAttributeAdjustmentId,
|
||||
selectBlessedPrimaryAttributeDependencies,
|
||||
@@ -160,7 +164,7 @@ export const selectVisibleAttributes = createSelector(
|
||||
.sort((a, b) => a.id - b.id)
|
||||
.map(attribute => {
|
||||
const dynamicAttribute =
|
||||
getDynamicAttributeById(attribute.id) ?? createInitialDynamicAttribute(attribute.id)
|
||||
getDynamicAttributeById(attribute.id) ?? createEmptyDynamicAttribute(attribute.id)
|
||||
|
||||
const minimum = getAttributeMinimum(
|
||||
derivedCharacteristics.lifePoints.purchased,
|
||||
@@ -228,7 +232,7 @@ export const selectVisibleAttributes = createSelector(
|
||||
*/
|
||||
export const selectCarryingCapacity = createSelector(
|
||||
createPropertySelector(selectDynamicAttributes, AttributeIdentifier.Strength),
|
||||
(strength): number => (strength?.value ?? 8) * 2,
|
||||
(strength): number => getCarryingCapacity(getAttributeValue(strength)),
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -258,7 +262,7 @@ export const selectAvailableAdjustments = createSelector(
|
||||
const canNotSwitch =
|
||||
current !== undefined &&
|
||||
current.maximum !== undefined &&
|
||||
current.maximum - selectableAdjustment.value < attributeValue(current.dynamic)
|
||||
current.maximum - selectableAdjustment.value < getAttributeValue(current.dynamic)
|
||||
|
||||
if (canNotSwitch) {
|
||||
return {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "../../shared/domain/identifier.ts"
|
||||
import { getHighestAttributeValue } from "../../shared/domain/rated/attribute.ts"
|
||||
import {
|
||||
createEmptyDynamicCombatTechnique,
|
||||
getAttackBaseForClose,
|
||||
getAttackBaseForRanged,
|
||||
getParryBaseForClose,
|
||||
@@ -26,8 +27,6 @@ import {
|
||||
selectDynamicAdvantages,
|
||||
selectDynamicGeneralSpecialAbilities,
|
||||
} from "../slices/characterSlice.ts"
|
||||
import { createInitialDynamicCloseCombatTechnique } from "../slices/closeCombatTechniqueSlice.ts"
|
||||
import { createInitialDynamicRangedCombatTechnique } from "../slices/rangedCombatTechniqueSlice.ts"
|
||||
import { SelectAll, SelectGetById } from "./basicCapabilitySelectors.ts"
|
||||
import { selectCanRemove, selectIsInCharacterCreation } from "./characterSelectors.ts"
|
||||
import { selectFilterApplyingRatedDependencies } from "./dependencySelectors.ts"
|
||||
@@ -123,7 +122,7 @@ export const selectVisibleCloseCombatTechniques = createSelector(
|
||||
.map(combatTechnique => {
|
||||
const dynamicCloseCombatTechnique =
|
||||
getDynamicCloseCombatTechniqueById(combatTechnique.id) ??
|
||||
createInitialDynamicCloseCombatTechnique(combatTechnique.id)
|
||||
createEmptyDynamicCombatTechnique(combatTechnique.id)
|
||||
|
||||
const minimum = getCombatTechniqueMinimum(
|
||||
rangedCombatTechniquesAt10,
|
||||
@@ -220,7 +219,7 @@ export const selectVisibleRangedCombatTechniques = createSelector(
|
||||
|
||||
const dynamicRangedCombatTechnique =
|
||||
getDynamicRangedCombatTechniqueById(combatTechnique.id) ??
|
||||
createInitialDynamicRangedCombatTechnique(combatTechnique.id)
|
||||
createEmptyDynamicCombatTechnique(combatTechnique.id)
|
||||
|
||||
const minimum = getCombatTechniqueMinimum(
|
||||
rangedCombatTechniquesAt10,
|
||||
|
||||
@@ -3,7 +3,11 @@ import {
|
||||
DerivedCharacteristic,
|
||||
DerivedCharacteristicTranslation,
|
||||
} from "optolith-database-schema/types/DerivedCharacteristic"
|
||||
import { firstLevel, isActive } from "../../shared/domain/activatable/activatableEntry.ts"
|
||||
import {
|
||||
firstLevel,
|
||||
isActive,
|
||||
isOptionActive,
|
||||
} from "../../shared/domain/activatable/activatableEntry.ts"
|
||||
import {
|
||||
modifierByIsActive,
|
||||
modifierByIsActives,
|
||||
@@ -20,10 +24,10 @@ import {
|
||||
MagicalSpecialAbilityIdentifier,
|
||||
OptionalRuleIdentifier,
|
||||
} from "../../shared/domain/identifier.ts"
|
||||
import { getAttributeValue } from "../../shared/domain/rated/attribute.ts"
|
||||
import { Rated } from "../../shared/domain/rated/ratedEntry.ts"
|
||||
import { filterNonNullable } from "../../shared/utils/array.ts"
|
||||
import { createPropertySelector } from "../../shared/utils/redux.ts"
|
||||
import { attributeValue } from "../slices/attributesSlice.ts"
|
||||
import {
|
||||
selectArcaneEnergyPermanentlyLost,
|
||||
selectArcaneEnergyPermanentlyLostBoughtBack,
|
||||
@@ -119,7 +123,7 @@ export const selectLifePoints = createSelector(
|
||||
if (race === undefined || staticEntry === undefined) {
|
||||
return undefined
|
||||
} else {
|
||||
const conValue = attributeValue(con)
|
||||
const conValue = getAttributeValue(con)
|
||||
const base = race.base_values.life_points + conValue * 2
|
||||
const modifier = modifierByLevel(incrementor, decrementor)
|
||||
const value = base + modifier + purchased - permanentlyLost
|
||||
@@ -289,7 +293,7 @@ export const selectKarmaPoints = createSelector(
|
||||
)
|
||||
|
||||
const divideAttributeSumByRound = (attributes: (Rated | undefined)[], divisor: number) =>
|
||||
Math.round(attributes.reduce((acc, attr) => acc + attributeValue(attr), 0) / divisor)
|
||||
Math.round(attributes.reduce((acc, attr) => acc + getAttributeValue(attr), 0) / divisor)
|
||||
|
||||
/**
|
||||
* Returns the static and dynamic values for spirit.
|
||||
@@ -453,13 +457,7 @@ export const selectMovement = createSelector(
|
||||
return undefined
|
||||
} else {
|
||||
const oneLegged = 3
|
||||
const isOneLeggedActive =
|
||||
maimed?.instances.some(
|
||||
instance =>
|
||||
instance.options?.[0]?.type === "Predefined" &&
|
||||
instance.options?.[0]?.id.type === "Generic" &&
|
||||
instance.options?.[0]?.id.value === oneLegged,
|
||||
) ?? false
|
||||
const isOneLeggedActive = isOptionActive(maimed, { tag: "General", general: oneLegged })
|
||||
|
||||
const base = isOneLeggedActive
|
||||
? Math.round(race.base_values.movement / 2)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { createSelector } from "@reduxjs/toolkit"
|
||||
import { Blessing } from "optolith-database-schema/types/Blessing"
|
||||
import {
|
||||
TinyActivatable,
|
||||
getOptions,
|
||||
isTinyActivatableActive,
|
||||
} from "../../shared/domain/activatable/activatableEntry.ts"
|
||||
@@ -72,23 +74,30 @@ const selectActiveAspectKnowledges = createSelector(
|
||||
),
|
||||
aspectKnowledge =>
|
||||
getOptions(aspectKnowledge).flatMap(option =>
|
||||
option.type === "Predefined" && option.id.type === "Aspect" ? [option.id.value] : [],
|
||||
option.type === "Predefined" && option.id.tag === "Aspect" ? [option.id.aspect] : [],
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns the blessings, split by active and inactive.
|
||||
*/
|
||||
export const selectVisibleBlessings = createSelector(
|
||||
const selectVisibleBlessings = createSelector(
|
||||
SelectAll.Static.Blessings,
|
||||
SelectGetById.Dynamic.Blessing,
|
||||
(
|
||||
staticBlessings,
|
||||
getDynamicBlessingById,
|
||||
): [active: DisplayedActiveBlessing[], inactive: DisplayedActiveBlessing[]] =>
|
||||
(staticBlessings, getDynamicBlessingById) =>
|
||||
partition(
|
||||
staticBlessings.map(blessing => ({ kind: "blessing", static: blessing })),
|
||||
staticBlessing => isTinyActivatableActive(getDynamicBlessingById(staticBlessing.static.id)),
|
||||
staticBlessings.map(staticBlessing => ({
|
||||
kind: "blessing" as const,
|
||||
static: staticBlessing,
|
||||
dynamic: getDynamicBlessingById(staticBlessing.id),
|
||||
})),
|
||||
(
|
||||
blessing,
|
||||
): blessing is {
|
||||
kind: "blessing"
|
||||
static: Blessing
|
||||
dynamic: TinyActivatable
|
||||
} => isTinyActivatableActive(blessing.dynamic),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createSelector } from "@reduxjs/toolkit"
|
||||
import { ProfessionIdentifier } from "../../shared/domain/identifier.ts"
|
||||
import { isInRange } from "../../shared/utils/range.ts"
|
||||
import { selectIsCharacterCreationFinished } from "../slices/characterSlice.ts"
|
||||
import { selectAdventurePointsAvailable } from "./adventurePointSelectors.ts"
|
||||
import { selectCurrentProfession } from "./professionSelectors.ts"
|
||||
@@ -17,8 +18,7 @@ export const selectShowFinishCharacterCreation = createSelector(
|
||||
*/
|
||||
export const selectCanFinishCharacterCreation = createSelector(
|
||||
selectAdventurePointsAvailable,
|
||||
(availableAdventurePoints): boolean =>
|
||||
availableAdventurePoints >= 0 && availableAdventurePoints <= 10,
|
||||
(availableAdventurePoints): boolean => isInRange([0, 10], availableAdventurePoints),
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from "../../shared/domain/identifier.ts"
|
||||
import { getHighestAttributeValue } from "../../shared/domain/rated/attribute.ts"
|
||||
import { Rated } from "../../shared/domain/rated/ratedEntry.ts"
|
||||
import { getSkillCommonness } from "../../shared/domain/rated/skill.ts"
|
||||
import { createEmptyDynamicSkill, getSkillCommonness } from "../../shared/domain/rated/skill.ts"
|
||||
import {
|
||||
getSkillMaximum,
|
||||
getSkillMinimum,
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
selectDynamicAdvantages,
|
||||
selectDynamicGeneralSpecialAbilities,
|
||||
} from "../slices/characterSlice.ts"
|
||||
import { createInitialDynamicSkill } from "../slices/skillsSlice.ts"
|
||||
import { SelectAll, SelectGetById } from "./basicCapabilitySelectors.ts"
|
||||
import { selectCanRemove, selectIsInCharacterCreation } from "./characterSelectors.ts"
|
||||
import { selectCurrentCulture } from "./cultureSelectors.ts"
|
||||
@@ -72,7 +71,7 @@ export const selectVisibleSkills = createSelector(
|
||||
filterApplyingDependencies,
|
||||
): DisplayedSkill[] =>
|
||||
staticSkills.map(skill => {
|
||||
const dynamicSkill = getDynamicSkillById(skill.id) ?? createInitialDynamicSkill(skill.id)
|
||||
const dynamicSkill = getDynamicSkillById(skill.id) ?? createEmptyDynamicSkill(skill.id)
|
||||
|
||||
const minimum = getSkillMinimum(
|
||||
getDynamicSkillById,
|
||||
|
||||
@@ -3,8 +3,10 @@ import { getOptions } from "../../shared/domain/activatable/activatableEntry.ts"
|
||||
import {
|
||||
AdvantageIdentifier,
|
||||
MagicalSpecialAbilityIdentifier,
|
||||
MagicalTraditionIdentifier,
|
||||
createIdentifierObject,
|
||||
} from "../../shared/domain/identifier.ts"
|
||||
import { getImprovementCostForAnimistPower } from "../../shared/domain/rated/animistPower.ts"
|
||||
import {
|
||||
countActiveSpellworks,
|
||||
countActiveSpellworksByImprovementCost,
|
||||
@@ -78,6 +80,7 @@ const selectSpellworksAbove10ByProperty = createSelector(
|
||||
SelectGetById.Static.AnimistPower,
|
||||
SelectGetById.Static.GeodeRitual,
|
||||
SelectGetById.Static.ZibiljaRitual,
|
||||
SelectGetById.Static.MagicalRune,
|
||||
SelectAll.Dynamic.Spells,
|
||||
SelectAll.Dynamic.Rituals,
|
||||
SelectAll.Dynamic.Curses,
|
||||
@@ -89,6 +92,7 @@ const selectSpellworksAbove10ByProperty = createSelector(
|
||||
SelectAll.Dynamic.AnimistPowers,
|
||||
SelectAll.Dynamic.GeodeRituals,
|
||||
SelectAll.Dynamic.ZibiljaRituals,
|
||||
SelectAll.Dynamic.MagicalRunes,
|
||||
(
|
||||
getStaticSpellById,
|
||||
getStaticRitualById,
|
||||
@@ -101,6 +105,7 @@ const selectSpellworksAbove10ByProperty = createSelector(
|
||||
getStaticAnimistPowerById,
|
||||
getStaticGeodeRitualById,
|
||||
getStaticZibiljaRitualById,
|
||||
getStaticMagicalRuneById,
|
||||
dynamicSpells,
|
||||
dynamicRituals,
|
||||
dynamicCurses,
|
||||
@@ -112,6 +117,7 @@ const selectSpellworksAbove10ByProperty = createSelector(
|
||||
dynamicAnimistPowers,
|
||||
dynamicGeodeRituals,
|
||||
dynamicZibiljaRituals,
|
||||
dynamicMagicalRunes,
|
||||
) =>
|
||||
getSpellworksAbove10ByProperty(
|
||||
id =>
|
||||
@@ -139,6 +145,8 @@ const selectSpellworksAbove10ByProperty = createSelector(
|
||||
return getStaticGeodeRitualById(id.geode_ritual)
|
||||
case "ZibiljaRitual":
|
||||
return getStaticZibiljaRitualById(id.zibilja_ritual)
|
||||
case "MagicalRune":
|
||||
return getStaticMagicalRuneById(id.magical_rune)
|
||||
default:
|
||||
return assertExhaustive(id)
|
||||
}
|
||||
@@ -154,6 +162,7 @@ const selectSpellworksAbove10ByProperty = createSelector(
|
||||
dynamicAnimistPowers,
|
||||
dynamicGeodeRituals,
|
||||
dynamicZibiljaRituals,
|
||||
dynamicMagicalRunes,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -164,7 +173,7 @@ const selectActivePropertyKnowledges = createSelector(
|
||||
),
|
||||
propertyKnowledge =>
|
||||
getOptions(propertyKnowledge).flatMap(option =>
|
||||
option.type === "Predefined" && option.id.type === "Property" ? [option.id.value] : [],
|
||||
option.type === "Predefined" && option.id.tag === "Property" ? [option.id.property] : [],
|
||||
),
|
||||
)
|
||||
|
||||
@@ -646,6 +655,8 @@ export const selectVisibleActiveAnimistPowers = createSelector(
|
||||
selectFilterApplyingRatedDependencies,
|
||||
selectSpellworksAbove10ByProperty,
|
||||
selectActivePropertyKnowledges,
|
||||
SelectGetById.Static.Patron,
|
||||
SelectGetById.Dynamic.MagicalTradition,
|
||||
(
|
||||
getStaticAnimistPowerById,
|
||||
dynamicAnimistPowers,
|
||||
@@ -656,8 +667,12 @@ export const selectVisibleActiveAnimistPowers = createSelector(
|
||||
filterApplyingDependencies,
|
||||
spellworksAbove10ByProperty,
|
||||
activePropertyKnowledges,
|
||||
getStaticPatronById,
|
||||
getDynamicMagicalTraditionById,
|
||||
): DisplayedActiveAnimistPower[] => {
|
||||
if (startExperienceLevel === undefined) {
|
||||
const traditionAnimists = getDynamicMagicalTraditionById(MagicalTraditionIdentifier.Animisten)
|
||||
|
||||
if (startExperienceLevel === undefined || traditionAnimists === undefined) {
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -673,6 +688,18 @@ export const selectVisibleActiveAnimistPowers = createSelector(
|
||||
spellworksAbove10ByProperty,
|
||||
activePropertyKnowledges,
|
||||
)
|
||||
.map(animistPower => ({
|
||||
...animistPower,
|
||||
improvementCost: getImprovementCostForAnimistPower(
|
||||
getStaticPatronById,
|
||||
traditionAnimists,
|
||||
animistPower.static.improvement_cost,
|
||||
),
|
||||
}))
|
||||
.filter(
|
||||
(animistPower): animistPower is DisplayedActiveAnimistPower =>
|
||||
animistPower.improvementCost !== undefined,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1013,7 +1040,34 @@ export const selectVisibleInactiveAnimistPowers = createSelector(
|
||||
selectActiveMagicalTraditions,
|
||||
selectIsMaximumOfSpellworksReached,
|
||||
selectIsEntryAvailable,
|
||||
getInactiveAnimistPowers,
|
||||
SelectGetById.Static.Patron,
|
||||
SelectGetById.Dynamic.MagicalTradition,
|
||||
|
||||
(
|
||||
staticAnimistPowers,
|
||||
getDynamicAnimistPowerById,
|
||||
activeMagicalTraditions,
|
||||
isMaximumCountReached,
|
||||
isEntryAvailable,
|
||||
getStaticPatronById,
|
||||
getDynamicMagicalTraditionById,
|
||||
) => {
|
||||
const traditionAnimists = getDynamicMagicalTraditionById(MagicalTraditionIdentifier.Animisten)
|
||||
|
||||
if (traditionAnimists === undefined) {
|
||||
return []
|
||||
}
|
||||
|
||||
return getInactiveAnimistPowers(
|
||||
staticAnimistPowers,
|
||||
getDynamicAnimistPowerById,
|
||||
activeMagicalTraditions,
|
||||
isMaximumCountReached,
|
||||
isEntryAvailable,
|
||||
getStaticPatronById,
|
||||
traditionAnimists,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import { ActionCreatorWithPayload, AnyAction, Draft, createAction } from "@reduxjs/toolkit"
|
||||
import { ImprovementCost } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import {
|
||||
BoundAdventurePoints,
|
||||
cachedAdventurePointsForActivatable,
|
||||
} from "../../shared/domain/adventurePoints/ratedEntry.ts"
|
||||
import { RatedDependency } from "../../shared/domain/rated/ratedDependency.ts"
|
||||
import {
|
||||
ActivatableRated,
|
||||
ActivatableRatedMap,
|
||||
ActivatableRatedValue,
|
||||
} from "../../shared/domain/rated/ratedEntry.ts"
|
||||
import { cachedAdventurePointsForActivatable } from "../../shared/domain/adventurePoints/ratedEntry.ts"
|
||||
import { ActivatableRated, ActivatableRatedMap } from "../../shared/domain/rated/ratedEntry.ts"
|
||||
import { Reducer, createImmerReducer } from "../../shared/utils/redux.ts"
|
||||
import { CharacterState } from "./characterSlice.ts"
|
||||
import { DatabaseState } from "./databaseSlice.ts"
|
||||
@@ -19,37 +11,6 @@ import { DatabaseState } from "./databaseSlice.ts"
|
||||
* enhancements.
|
||||
*/
|
||||
export type ActivatableRatedSlice<N extends string, E extends string> = {
|
||||
/**
|
||||
* Creates a new entry with an initial value if active. The initial adventure
|
||||
* points cache is calculated from the initial value.
|
||||
*/
|
||||
create: (
|
||||
database: DatabaseState,
|
||||
id: number,
|
||||
value: ActivatableRatedValue,
|
||||
options?: Partial<{
|
||||
dependencies: RatedDependency[]
|
||||
boundAdventurePoints: BoundAdventurePoints[]
|
||||
}>,
|
||||
) => ActivatableRated
|
||||
|
||||
/**
|
||||
* Creates a new entry with no initial value.
|
||||
*/
|
||||
createInitial: (
|
||||
id: number,
|
||||
options?: Partial<{
|
||||
dependencies: RatedDependency[]
|
||||
boundAdventurePoints: BoundAdventurePoints[]
|
||||
}>,
|
||||
) => ActivatableRated
|
||||
|
||||
/**
|
||||
* Takes an entry that may not exist (because its instance has not been used
|
||||
* yet) and returns its value.
|
||||
*/
|
||||
getValue: (entry: ActivatableRated | undefined) => ActivatableRatedValue
|
||||
|
||||
/**
|
||||
* The actions that can be dispatched to modify the state.
|
||||
*/
|
||||
@@ -57,22 +18,27 @@ export type ActivatableRatedSlice<N extends string, E extends string> = {
|
||||
/**
|
||||
* Add the entry with the given id.
|
||||
*/
|
||||
addAction: ActionCreatorWithPayload<number, `${N}/add${E}`>
|
||||
addAction: ActionCreatorWithPayload<{ id: number }, `${N}/add${E}`>
|
||||
|
||||
/**
|
||||
* Remove the entry with the given id.
|
||||
*/
|
||||
removeAction: ActionCreatorWithPayload<number, `${N}/remove${E}`>
|
||||
removeAction: ActionCreatorWithPayload<{ id: number }, `${N}/remove${E}`>
|
||||
|
||||
/**
|
||||
* Increments the value of the entry with the given id.
|
||||
*/
|
||||
incrementAction: ActionCreatorWithPayload<number, `${N}/increment${E}`>
|
||||
incrementAction: ActionCreatorWithPayload<{ id: number }, `${N}/increment${E}`>
|
||||
|
||||
/**
|
||||
* Decrements the value of the entry with the given id.
|
||||
*/
|
||||
decrementAction: ActionCreatorWithPayload<number, `${N}/decrement${E}`>
|
||||
decrementAction: ActionCreatorWithPayload<{ id: number }, `${N}/decrement${E}`>
|
||||
|
||||
/**
|
||||
* Sets the value of the entry with the given id.
|
||||
*/
|
||||
setAction: ActionCreatorWithPayload<{ id: number; value: number }, `${N}/set${E}`>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,103 +54,87 @@ export const createActivatableRatedSlice = <N extends string, E extends string>(
|
||||
namespace: N
|
||||
entityName: E
|
||||
getState: (state: Draft<CharacterState>) => Draft<ActivatableRatedMap>
|
||||
getImprovementCost: (id: number, database: DatabaseState) => ImprovementCost
|
||||
getImprovementCost: (
|
||||
id: number,
|
||||
database: DatabaseState,
|
||||
character: CharacterState,
|
||||
) => ImprovementCost
|
||||
createEmptyActivatableRated: (id: number) => ActivatableRated
|
||||
}): ActivatableRatedSlice<N, E> => {
|
||||
const updateCachedAdventurePoints = (entry: Draft<ActivatableRated>, database: DatabaseState) => {
|
||||
const updateCachedAdventurePoints = (
|
||||
entry: Draft<ActivatableRated>,
|
||||
database: DatabaseState,
|
||||
character: Draft<CharacterState>,
|
||||
) => {
|
||||
entry.cachedAdventurePoints = cachedAdventurePointsForActivatable(
|
||||
entry.value,
|
||||
entry.boundAdventurePoints,
|
||||
config.getImprovementCost(entry.id, database),
|
||||
config.getImprovementCost(entry.id, database, character),
|
||||
)
|
||||
return entry
|
||||
}
|
||||
|
||||
const create: ActivatableRatedSlice<N, E>["create"] = (
|
||||
database,
|
||||
id,
|
||||
value,
|
||||
{ dependencies = [], boundAdventurePoints = [] } = {},
|
||||
) =>
|
||||
updateCachedAdventurePoints(
|
||||
{
|
||||
id,
|
||||
value: value === undefined ? undefined : Math.max(0, value),
|
||||
cachedAdventurePoints: {
|
||||
general: 0,
|
||||
bound: 0,
|
||||
},
|
||||
dependencies,
|
||||
boundAdventurePoints,
|
||||
},
|
||||
database,
|
||||
)
|
||||
|
||||
const createInitial: ActivatableRatedSlice<N, E>["createInitial"] = (
|
||||
id,
|
||||
{ dependencies = [], boundAdventurePoints = [] } = {},
|
||||
) => ({
|
||||
id,
|
||||
value: undefined,
|
||||
cachedAdventurePoints: {
|
||||
general: 0,
|
||||
bound: 0,
|
||||
},
|
||||
dependencies,
|
||||
boundAdventurePoints,
|
||||
enhancements: {},
|
||||
})
|
||||
|
||||
const getValue: ActivatableRatedSlice<N, E>["getValue"] = entry => entry?.value
|
||||
|
||||
const addAction = createAction<number, `${N}/add${E}`>(
|
||||
const addAction = createAction<{ id: number }, `${N}/add${E}`>(
|
||||
`${config.namespace}/add${config.entityName}`,
|
||||
)
|
||||
|
||||
const removeAction = createAction<number, `${N}/remove${E}`>(
|
||||
const removeAction = createAction<{ id: number }, `${N}/remove${E}`>(
|
||||
`${config.namespace}/remove${config.entityName}`,
|
||||
)
|
||||
|
||||
const incrementAction = createAction<number, `${N}/increment${E}`>(
|
||||
const incrementAction = createAction<{ id: number }, `${N}/increment${E}`>(
|
||||
`${config.namespace}/increment${config.entityName}`,
|
||||
)
|
||||
|
||||
const decrementAction = createAction<number, `${N}/decrement${E}`>(
|
||||
const decrementAction = createAction<{ id: number }, `${N}/decrement${E}`>(
|
||||
`${config.namespace}/decrement${config.entityName}`,
|
||||
)
|
||||
|
||||
const setAction = createAction<{ id: number; value: number }, `${N}/set${E}`>(
|
||||
`${config.namespace}/set${config.entityName}`,
|
||||
)
|
||||
|
||||
const reducer = createImmerReducer(
|
||||
(state: Draft<CharacterState>, action, database: DatabaseState) => {
|
||||
if (addAction.match(action)) {
|
||||
config.getState(state)[action.payload] ??= create(database, action.payload, 0)
|
||||
config.getState(state)[action.payload.id] ??= config.createEmptyActivatableRated(
|
||||
action.payload.id,
|
||||
)
|
||||
} else if (removeAction.match(action)) {
|
||||
delete config.getState(state)[action.payload]
|
||||
delete config.getState(state)[action.payload.id]
|
||||
} else if (incrementAction.match(action)) {
|
||||
const entry = (config.getState(state)[action.payload] ??= createInitial(action.payload))
|
||||
const entry = (config.getState(state)[action.payload.id] ??=
|
||||
config.createEmptyActivatableRated(action.payload.id))
|
||||
if (entry.value === undefined) {
|
||||
entry.value = 0
|
||||
} else {
|
||||
entry.value++
|
||||
}
|
||||
updateCachedAdventurePoints(entry, database)
|
||||
updateCachedAdventurePoints(entry, database, state)
|
||||
} else if (decrementAction.match(action)) {
|
||||
const entry = config.getState(state)[action.payload]
|
||||
const entry = config.getState(state)[action.payload.id]
|
||||
if (entry !== undefined && entry.value !== undefined && entry.value > 0) {
|
||||
entry.value--
|
||||
updateCachedAdventurePoints(entry, database)
|
||||
updateCachedAdventurePoints(entry, database, state)
|
||||
}
|
||||
} else if (setAction.match(action)) {
|
||||
const entry = (config.getState(state)[action.payload.id] ??=
|
||||
config.createEmptyActivatableRated(action.payload.id))
|
||||
if (action.payload.value >= 0) {
|
||||
entry.value = action.payload.value
|
||||
updateCachedAdventurePoints(entry, database, state)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
create,
|
||||
createInitial,
|
||||
getValue,
|
||||
actions: {
|
||||
addAction,
|
||||
removeAction,
|
||||
incrementAction,
|
||||
decrementAction,
|
||||
setAction,
|
||||
},
|
||||
reducer,
|
||||
}
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import { ActionCreatorWithPayload, AnyAction, Draft, createAction } from "@reduxjs/toolkit"
|
||||
import { ImprovementCost } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import {
|
||||
BoundAdventurePoints,
|
||||
cachedAdventurePointsForActivatableWithEnhancements,
|
||||
} from "../../shared/domain/adventurePoints/ratedEntry.ts"
|
||||
import { cachedAdventurePointsForActivatableWithEnhancements } from "../../shared/domain/adventurePoints/ratedEntry.ts"
|
||||
import { RegistrationMethod } from "../../shared/domain/dependencies/registrationHelpers.ts"
|
||||
import { Enhancement } from "../../shared/domain/rated/enhancement.ts"
|
||||
import { RatedDependency } from "../../shared/domain/rated/ratedDependency.ts"
|
||||
import {
|
||||
ActivatableRatedValue,
|
||||
ActivatableRatedWithEnhancements,
|
||||
ActivatableRatedWithEnhancementsMap,
|
||||
} from "../../shared/domain/rated/ratedEntry.ts"
|
||||
@@ -21,40 +15,6 @@ import { DatabaseState } from "./databaseSlice.ts"
|
||||
* enhancements.
|
||||
*/
|
||||
export type ActivatableRatedWithEnhancementsSlice<N extends string, E extends string> = {
|
||||
/**
|
||||
* Creates a new entry with an initial value if active. The initial adventure
|
||||
* points cache is calculated from the initial value.
|
||||
*/
|
||||
create: (
|
||||
database: DatabaseState,
|
||||
id: number,
|
||||
value: ActivatableRatedValue,
|
||||
options?: Partial<{
|
||||
dependencies: RatedDependency[]
|
||||
boundAdventurePoints: BoundAdventurePoints[]
|
||||
enhancements: {
|
||||
[id: number]: Enhancement
|
||||
}
|
||||
}>,
|
||||
) => ActivatableRatedWithEnhancements
|
||||
|
||||
/**
|
||||
* Creates a new entry with no initial value.
|
||||
*/
|
||||
createInitial: (
|
||||
id: number,
|
||||
options?: Partial<{
|
||||
dependencies: RatedDependency[]
|
||||
boundAdventurePoints: BoundAdventurePoints[]
|
||||
}>,
|
||||
) => ActivatableRatedWithEnhancements
|
||||
|
||||
/**
|
||||
* Takes an entry that may not exist (because its instance has not been used
|
||||
* yet) and returns its value.
|
||||
*/
|
||||
getValue: (entry: ActivatableRatedWithEnhancements | undefined) => ActivatableRatedValue
|
||||
|
||||
/**
|
||||
* The actions that can be dispatched to modify the state.
|
||||
*/
|
||||
@@ -62,22 +22,27 @@ export type ActivatableRatedWithEnhancementsSlice<N extends string, E extends st
|
||||
/**
|
||||
* Add the entry with the given id.
|
||||
*/
|
||||
addAction: ActionCreatorWithPayload<number, `${N}/add${E}`>
|
||||
addAction: ActionCreatorWithPayload<{ id: number }, `${N}/add${E}`>
|
||||
|
||||
/**
|
||||
* Remove the entry with the given id.
|
||||
*/
|
||||
removeAction: ActionCreatorWithPayload<number, `${N}/remove${E}`>
|
||||
removeAction: ActionCreatorWithPayload<{ id: number }, `${N}/remove${E}`>
|
||||
|
||||
/**
|
||||
* Increments the value of the entry with the given id.
|
||||
*/
|
||||
incrementAction: ActionCreatorWithPayload<number, `${N}/increment${E}`>
|
||||
incrementAction: ActionCreatorWithPayload<{ id: number }, `${N}/increment${E}`>
|
||||
|
||||
/**
|
||||
* Decrements the value of the entry with the given id.
|
||||
*/
|
||||
decrementAction: ActionCreatorWithPayload<number, `${N}/decrement${E}`>
|
||||
decrementAction: ActionCreatorWithPayload<{ id: number }, `${N}/decrement${E}`>
|
||||
|
||||
/**
|
||||
* Sets the value of the entry with the given id.
|
||||
*/
|
||||
setAction: ActionCreatorWithPayload<{ id: number; value: number }, `${N}/set${E}`>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,6 +77,7 @@ export const createActivatableRatedWithEnhancementsSlice = <
|
||||
prerequisites: P[],
|
||||
sourceId: IdO,
|
||||
) => void
|
||||
createEmptyActivatableRatedWithEnhancements: (id: number) => ActivatableRatedWithEnhancements
|
||||
}): ActivatableRatedWithEnhancementsSlice<N, E> => {
|
||||
const updateCachedAdventurePoints = (
|
||||
entry: Draft<ActivatableRatedWithEnhancements>,
|
||||
@@ -128,80 +94,48 @@ export const createActivatableRatedWithEnhancementsSlice = <
|
||||
return entry
|
||||
}
|
||||
|
||||
const create: ActivatableRatedWithEnhancementsSlice<N, E>["create"] = (
|
||||
database,
|
||||
id,
|
||||
value,
|
||||
{ dependencies = [], boundAdventurePoints = [], enhancements = {} } = {},
|
||||
) =>
|
||||
updateCachedAdventurePoints(
|
||||
{
|
||||
id,
|
||||
value: value === undefined ? undefined : Math.max(0, value),
|
||||
cachedAdventurePoints: {
|
||||
general: 0,
|
||||
bound: 0,
|
||||
},
|
||||
dependencies,
|
||||
boundAdventurePoints,
|
||||
enhancements,
|
||||
},
|
||||
database,
|
||||
)
|
||||
|
||||
const createInitial: ActivatableRatedWithEnhancementsSlice<N, E>["createInitial"] = (
|
||||
id,
|
||||
{ dependencies = [], boundAdventurePoints = [] } = {},
|
||||
) => ({
|
||||
id,
|
||||
value: undefined,
|
||||
cachedAdventurePoints: {
|
||||
general: 0,
|
||||
bound: 0,
|
||||
},
|
||||
dependencies,
|
||||
boundAdventurePoints,
|
||||
enhancements: {},
|
||||
})
|
||||
|
||||
const getValue: ActivatableRatedWithEnhancementsSlice<N, E>["getValue"] = entry => entry?.value
|
||||
|
||||
const addAction = createAction<number, `${N}/add${E}`>(
|
||||
const addAction = createAction<{ id: number }, `${N}/add${E}`>(
|
||||
`${config.namespace}/add${config.entityName}`,
|
||||
)
|
||||
|
||||
const removeAction = createAction<number, `${N}/remove${E}`>(
|
||||
const removeAction = createAction<{ id: number }, `${N}/remove${E}`>(
|
||||
`${config.namespace}/remove${config.entityName}`,
|
||||
)
|
||||
|
||||
const incrementAction = createAction<number, `${N}/increment${E}`>(
|
||||
const incrementAction = createAction<{ id: number }, `${N}/increment${E}`>(
|
||||
`${config.namespace}/increment${config.entityName}`,
|
||||
)
|
||||
|
||||
const decrementAction = createAction<number, `${N}/decrement${E}`>(
|
||||
const decrementAction = createAction<{ id: number }, `${N}/decrement${E}`>(
|
||||
`${config.namespace}/decrement${config.entityName}`,
|
||||
)
|
||||
|
||||
const setAction = createAction<{ id: number; value: number }, `${N}/set${E}`>(
|
||||
`${config.namespace}/set${config.entityName}`,
|
||||
)
|
||||
|
||||
const reducer = createImmerReducer(
|
||||
(state: Draft<CharacterState>, action, database: DatabaseState) => {
|
||||
if (addAction.match(action)) {
|
||||
config.getState(state)[action.payload] ??= create(database, action.payload, 0)
|
||||
config.getState(state)[action.payload.id] ??=
|
||||
config.createEmptyActivatableRatedWithEnhancements(action.payload.id)
|
||||
config.registerOrUnregisterPrerequisitesAsDependencies(
|
||||
RegistrationMethod.Add,
|
||||
state,
|
||||
config.getPrerequisites(action.payload, database),
|
||||
config.createIdentifierObject(action.payload),
|
||||
config.getPrerequisites(action.payload.id, database),
|
||||
config.createIdentifierObject(action.payload.id),
|
||||
)
|
||||
} else if (removeAction.match(action)) {
|
||||
config.registerOrUnregisterPrerequisitesAsDependencies(
|
||||
RegistrationMethod.Remove,
|
||||
state,
|
||||
config.getPrerequisites(action.payload, database),
|
||||
config.createIdentifierObject(action.payload),
|
||||
config.getPrerequisites(action.payload.id, database),
|
||||
config.createIdentifierObject(action.payload.id),
|
||||
)
|
||||
delete config.getState(state)[action.payload]
|
||||
delete config.getState(state)[action.payload.id]
|
||||
} else if (incrementAction.match(action)) {
|
||||
const entry = (config.getState(state)[action.payload] ??= createInitial(action.payload))
|
||||
const entry = (config.getState(state)[action.payload.id] ??=
|
||||
config.createEmptyActivatableRatedWithEnhancements(action.payload.id))
|
||||
if (entry.value === undefined) {
|
||||
entry.value = 0
|
||||
} else {
|
||||
@@ -209,24 +143,29 @@ export const createActivatableRatedWithEnhancementsSlice = <
|
||||
}
|
||||
updateCachedAdventurePoints(entry, database)
|
||||
} else if (decrementAction.match(action)) {
|
||||
const entry = config.getState(state)[action.payload]
|
||||
const entry = config.getState(state)[action.payload.id]
|
||||
if (entry !== undefined && entry.value !== undefined && entry.value > 0) {
|
||||
entry.value--
|
||||
updateCachedAdventurePoints(entry, database)
|
||||
}
|
||||
} else if (setAction.match(action)) {
|
||||
const entry = (config.getState(state)[action.payload.id] ??=
|
||||
config.createEmptyActivatableRatedWithEnhancements(action.payload.id))
|
||||
if (action.payload.value >= 0) {
|
||||
entry.value = action.payload.value
|
||||
updateCachedAdventurePoints(entry, database)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
create,
|
||||
createInitial,
|
||||
getValue,
|
||||
actions: {
|
||||
addAction,
|
||||
removeAction,
|
||||
incrementAction,
|
||||
decrementAction,
|
||||
setAction,
|
||||
},
|
||||
reducer,
|
||||
}
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
import { ImprovementCost } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import {
|
||||
attributeImprovementCost,
|
||||
createEmptyDynamicAttribute,
|
||||
} from "../../shared/domain/rated/attribute.ts"
|
||||
import { createRatedSlice } from "./ratedSlice.ts"
|
||||
|
||||
const {
|
||||
create,
|
||||
createInitial,
|
||||
getValue,
|
||||
actions: { incrementAction, decrementAction },
|
||||
reducer,
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
actions: {
|
||||
incrementEntry: incrementAttribute,
|
||||
setEntry: setAttribute,
|
||||
decrementEntry: decrementAttribute,
|
||||
},
|
||||
reducer: attributesReducer,
|
||||
} = createRatedSlice({
|
||||
namespace: "attributes",
|
||||
entityName: "Attribute",
|
||||
getState: state => state.attributes,
|
||||
minValue: 8,
|
||||
getImprovementCost: () => ImprovementCost.E,
|
||||
getImprovementCost: () => attributeImprovementCost,
|
||||
createEmptyRated: createEmptyDynamicAttribute,
|
||||
})
|
||||
|
||||
export {
|
||||
getValue as attributeValue,
|
||||
reducer as attributesReducer,
|
||||
create as createDynamicAttribute,
|
||||
createInitial as createInitialDynamicAttribute,
|
||||
decrementAction as decrementAttribute,
|
||||
incrementAction as incrementAttribute,
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { ImprovementCost, fromRaw } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { registerOrUnregisterPrerequisitesOfLiturgyAsDependencies } from "../../shared/domain/dependencies/fullPrerequisiteRegistrationAsDependencyForType.ts"
|
||||
import { createIdentifierObject } from "../../shared/domain/identifier.ts"
|
||||
import { createEmptyDynamicLiturgicalChant } from "../../shared/domain/rated/liturgicalChant.ts"
|
||||
import { createActivatableRatedWithEnhancementsSlice } from "./activatableRatedWithEnhancementsSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicCeremony,
|
||||
createInitial: createInitialDynamicCeremony,
|
||||
getValue: getCeremonyValue,
|
||||
actions: {
|
||||
addAction: addCeremony,
|
||||
removeAction: removeCeremony,
|
||||
incrementAction: incrementCeremony,
|
||||
decrementAction: decrementCeremony,
|
||||
setAction: setCeremony,
|
||||
},
|
||||
reducer: ceremoniesReducer,
|
||||
} = createActivatableRatedWithEnhancementsSlice({
|
||||
@@ -27,4 +26,5 @@ export const {
|
||||
createIdentifierObject: id => createIdentifierObject("Ceremony", id),
|
||||
registerOrUnregisterPrerequisitesAsDependencies:
|
||||
registerOrUnregisterPrerequisitesOfLiturgyAsDependencies,
|
||||
createEmptyActivatableRatedWithEnhancements: createEmptyDynamicLiturgicalChant,
|
||||
})
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { AnyAction, createAction } from "@reduxjs/toolkit"
|
||||
import { Draft } from "immer"
|
||||
import { createDynamicActivatable } from "../../shared/domain/activatable/activatableEntry.ts"
|
||||
import { Character } from "../../shared/domain/character.ts"
|
||||
import {
|
||||
AdvantageIdentifier,
|
||||
BlessedTraditionIdentifier,
|
||||
MagicalTraditionIdentifier,
|
||||
} from "../../shared/domain/identifier.ts"
|
||||
import { createImmerReducer, reduceReducers } from "../../shared/utils/redux.ts"
|
||||
import { DraftReducer, reduceDraftReducers } from "../../shared/utils/redux.ts"
|
||||
import { RootState } from "../store.ts"
|
||||
import { advantagesReducer } from "./advantagesSlice.ts"
|
||||
import { attributesReducer } from "./attributesSlice.ts"
|
||||
@@ -26,6 +26,7 @@ import { geodeRitualsReducer } from "./magicalActions/geodeRitualsSlice.ts"
|
||||
import { jesterTricksReducer } from "./magicalActions/jesterTricksSlice.ts"
|
||||
import { magicalDancesReducer } from "./magicalActions/magicalDancesSlice.ts"
|
||||
import { magicalMelodiesReducer } from "./magicalActions/magicalMelodiesSlice.ts"
|
||||
import { magicalRunesReducer } from "./magicalActions/magicalRunesSlice.ts"
|
||||
import { zibiljaRitualsReducer } from "./magicalActions/zibiljaRitualsSlice.ts"
|
||||
import { personalDataReducer } from "./personalDataSlice.ts"
|
||||
import { professionReducer } from "./professionSlice.ts"
|
||||
@@ -53,10 +54,12 @@ const staticInitialState: Omit<CharacterState, "dateCreated" | "dateLastModified
|
||||
id: 1,
|
||||
variantId: 1,
|
||||
selectedAttributeAdjustmentId: 1,
|
||||
dependencies: [],
|
||||
},
|
||||
culture: {
|
||||
id: 1,
|
||||
isCulturalPackageApplied: false,
|
||||
dependencies: [],
|
||||
},
|
||||
profession: {
|
||||
id: 1,
|
||||
@@ -65,12 +68,14 @@ const staticInitialState: Omit<CharacterState, "dateCreated" | "dateLastModified
|
||||
rules: {
|
||||
includeAllPublications: true,
|
||||
includePublications: [],
|
||||
publicationDependencies: [],
|
||||
focusRules: {},
|
||||
optionalRules: {},
|
||||
},
|
||||
states: {},
|
||||
personalData: {
|
||||
sex: { type: "Male" },
|
||||
sexDependencies: [],
|
||||
socialStatus: {
|
||||
dependencies: [],
|
||||
},
|
||||
@@ -118,7 +123,6 @@ const staticInitialState: Omit<CharacterState, "dateCreated" | "dateLastModified
|
||||
krallenkettenzauber: {},
|
||||
liturgicalStyleSpecialAbilities: {},
|
||||
lycantropicGifts: {},
|
||||
magicalRunes: {},
|
||||
magicalSigns: {},
|
||||
magicalSpecialAbilities: {},
|
||||
magicalTraditions: {
|
||||
@@ -147,6 +151,88 @@ const staticInitialState: Omit<CharacterState, "dateCreated" | "dateLastModified
|
||||
wandEnchantments: {},
|
||||
weaponEnchantments: {},
|
||||
},
|
||||
attributes: {
|
||||
1: {
|
||||
id: 1,
|
||||
value: 13,
|
||||
dependencies: [],
|
||||
cachedAdventurePoints: {
|
||||
general: 75,
|
||||
bound: 0,
|
||||
},
|
||||
boundAdventurePoints: [],
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
value: 13,
|
||||
dependencies: [],
|
||||
cachedAdventurePoints: {
|
||||
general: 75,
|
||||
bound: 0,
|
||||
},
|
||||
boundAdventurePoints: [],
|
||||
},
|
||||
3: {
|
||||
id: 3,
|
||||
value: 13,
|
||||
dependencies: [],
|
||||
cachedAdventurePoints: {
|
||||
general: 75,
|
||||
bound: 0,
|
||||
},
|
||||
boundAdventurePoints: [],
|
||||
},
|
||||
4: {
|
||||
id: 4,
|
||||
value: 13,
|
||||
dependencies: [],
|
||||
cachedAdventurePoints: {
|
||||
general: 75,
|
||||
bound: 0,
|
||||
},
|
||||
boundAdventurePoints: [],
|
||||
},
|
||||
5: {
|
||||
id: 5,
|
||||
value: 12,
|
||||
dependencies: [],
|
||||
cachedAdventurePoints: {
|
||||
general: 60,
|
||||
bound: 0,
|
||||
},
|
||||
boundAdventurePoints: [],
|
||||
},
|
||||
6: {
|
||||
id: 6,
|
||||
value: 12,
|
||||
dependencies: [],
|
||||
cachedAdventurePoints: {
|
||||
general: 60,
|
||||
bound: 0,
|
||||
},
|
||||
boundAdventurePoints: [],
|
||||
},
|
||||
7: {
|
||||
id: 7,
|
||||
value: 12,
|
||||
dependencies: [],
|
||||
cachedAdventurePoints: {
|
||||
general: 60,
|
||||
bound: 0,
|
||||
},
|
||||
boundAdventurePoints: [],
|
||||
},
|
||||
8: {
|
||||
id: 8,
|
||||
value: 12,
|
||||
dependencies: [],
|
||||
cachedAdventurePoints: {
|
||||
general: 60,
|
||||
bound: 0,
|
||||
},
|
||||
boundAdventurePoints: [],
|
||||
},
|
||||
},
|
||||
magicalPrimaryAttributeDependencies: [],
|
||||
blessedPrimaryAttributeDependencies: [],
|
||||
derivedCharacteristics: {
|
||||
@@ -212,6 +298,7 @@ const staticInitialState: Omit<CharacterState, "dateCreated" | "dateLastModified
|
||||
animistPowers: {},
|
||||
geodeRituals: {},
|
||||
zibiljaRituals: {},
|
||||
magicalRunes: {},
|
||||
},
|
||||
blessings: {
|
||||
1: {
|
||||
@@ -255,6 +342,7 @@ const staticInitialState: Omit<CharacterState, "dateCreated" | "dateLastModified
|
||||
},
|
||||
// creatures: {}
|
||||
// pact: {}
|
||||
pactDependencies: [],
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -654,12 +742,6 @@ export const selectDynamicLiturgicalStyleSpecialAbilities = (state: RootState) =
|
||||
export const selectDynamicLycantropicGifts = (state: RootState) =>
|
||||
selectCurrentCharacter(state)?.specialAbilities.lycantropicGifts
|
||||
|
||||
/**
|
||||
* Select the magical runes of the currently open character.
|
||||
*/
|
||||
export const selectDynamicMagicalRunes = (state: RootState) =>
|
||||
selectCurrentCharacter(state)?.specialAbilities.magicalRunes
|
||||
|
||||
/**
|
||||
* Select the magical signs of the currently open character.
|
||||
*/
|
||||
@@ -932,6 +1014,12 @@ export const selectDynamicGeodeRituals = (state: RootState) =>
|
||||
export const selectDynamicZibiljaRituals = (state: RootState) =>
|
||||
selectCurrentCharacter(state)?.magicalActions.zibiljaRituals
|
||||
|
||||
/**
|
||||
* Select the magical runes of the currently open character.
|
||||
*/
|
||||
export const selectDynamicMagicalRunes = (state: RootState) =>
|
||||
selectCurrentCharacter(state)?.magicalActions.magicalRunes
|
||||
|
||||
/**
|
||||
* Select the blessings of the currently open character.
|
||||
*/
|
||||
@@ -986,7 +1074,7 @@ export const deleteAvatar = createAction("character/deleteAvatar")
|
||||
*/
|
||||
export const finishCharacterCreation = createAction("character/finishCharacterCreation")
|
||||
|
||||
const generalCharacterReducer = createImmerReducer((state: Draft<CharacterState>, action) => {
|
||||
const generalCharacterReducer: DraftReducer<CharacterState> = (state, action) => {
|
||||
if (setName.match(action)) {
|
||||
state.name = action.payload
|
||||
} else if (setAvatar.match(action)) {
|
||||
@@ -996,13 +1084,13 @@ const generalCharacterReducer = createImmerReducer((state: Draft<CharacterState>
|
||||
} else if (finishCharacterCreation.match(action)) {
|
||||
state.isCharacterCreationFinished = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* The state reducer for a character.
|
||||
*/
|
||||
export const characterReducer = reduceReducers<
|
||||
Draft<CharacterState>,
|
||||
export const characterReducer = reduceDraftReducers<
|
||||
CharacterState,
|
||||
AnyAction,
|
||||
[database: DatabaseState]
|
||||
>(
|
||||
@@ -1030,6 +1118,7 @@ export const characterReducer = reduceReducers<
|
||||
animistPowersReducer,
|
||||
geodeRitualsReducer,
|
||||
zibiljaRitualsReducer,
|
||||
magicalRunesReducer,
|
||||
blessingsReducer,
|
||||
liturgicalChantsReducer,
|
||||
ceremoniesReducer,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { ImprovementCost, fromRaw } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { createEmptyDynamicCombatTechnique } from "../../shared/domain/rated/combatTechnique.ts"
|
||||
import { createRatedSlice } from "./ratedSlice.ts"
|
||||
|
||||
const {
|
||||
create,
|
||||
createInitial,
|
||||
getValue,
|
||||
actions: { incrementAction, decrementAction },
|
||||
reducer,
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
actions: {
|
||||
incrementEntry: incrementCloseCombatTechnique,
|
||||
setEntry: setCloseCombatTechnique,
|
||||
decrementEntry: decrementCloseCombatTechnique,
|
||||
},
|
||||
reducer: closeCombatTechniquesReducer,
|
||||
} = createRatedSlice({
|
||||
namespace: "combatTechniques/close",
|
||||
entityName: "CloseCombatTechnique",
|
||||
@@ -14,13 +17,5 @@ const {
|
||||
minValue: 6,
|
||||
getImprovementCost: (id, database) =>
|
||||
fromRaw(database.closeCombatTechniques[id]?.improvement_cost) ?? ImprovementCost.D,
|
||||
createEmptyRated: createEmptyDynamicCombatTechnique,
|
||||
})
|
||||
|
||||
export {
|
||||
reducer as closeCombatTechniquesReducer,
|
||||
create as createDynamicCloseCombatTechnique,
|
||||
createInitial as createInitialDynamicCloseCombatTechnique,
|
||||
decrementAction as decrementCloseCombatTechnique,
|
||||
getValue as getCloseCombatTechniqueValue,
|
||||
incrementAction as incrementCloseCombatTechnique,
|
||||
}
|
||||
|
||||
@@ -45,6 +45,78 @@ const initialDatabaseState: DatabaseState = {
|
||||
brews: {},
|
||||
cantrips: {},
|
||||
cache: {
|
||||
activatableSelectOptions: {
|
||||
advancedCombatSpecialAbilities: {},
|
||||
advancedKarmaSpecialAbilities: {},
|
||||
advancedMagicalSpecialAbilities: {},
|
||||
advancedSkillSpecialAbilities: {},
|
||||
advantages: {},
|
||||
ancestorGlyphs: {},
|
||||
arcaneOrbEnchantments: {},
|
||||
attireEnchantments: {},
|
||||
blessedTraditions: {},
|
||||
bowlEnchantments: {},
|
||||
brawlingSpecialAbilities: {},
|
||||
cauldronEnchantments: {},
|
||||
ceremonialItemSpecialAbilities: {},
|
||||
chronicleEnchantments: {},
|
||||
combatSpecialAbilities: {},
|
||||
combatStyleSpecialAbilities: {},
|
||||
commandSpecialAbilities: {},
|
||||
daggerRituals: {},
|
||||
disadvantages: {},
|
||||
familiarSpecialAbilities: {},
|
||||
fatePointSexSpecialAbilities: {},
|
||||
fatePointSpecialAbilities: {},
|
||||
foolsHatEnchantments: {},
|
||||
generalSpecialAbilities: {},
|
||||
instrumentEnchantments: {},
|
||||
karmaSpecialAbilities: {},
|
||||
krallenkettenzauber: {},
|
||||
liturgicalStyleSpecialAbilities: {},
|
||||
lycantropicGifts: {},
|
||||
magicalSpecialAbilities: {},
|
||||
magicalTraditions: {},
|
||||
magicStyleSpecialAbilities: {},
|
||||
orbEnchantments: {},
|
||||
pactGifts: {},
|
||||
protectiveWardingCircleSpecialAbilities: {},
|
||||
ringEnchantments: {},
|
||||
sermons: {},
|
||||
sexSpecialAbilities: {},
|
||||
sickleRituals: {},
|
||||
sikaryanDrainSpecialAbilities: {},
|
||||
staffEnchantments: {},
|
||||
skillStyleSpecialAbilities: {},
|
||||
spellSwordEnchantments: {},
|
||||
toyEnchantments: {},
|
||||
trinkhornzauber: {},
|
||||
vampiricGifts: {},
|
||||
visions: {},
|
||||
wandEnchantments: {},
|
||||
weaponEnchantments: {},
|
||||
},
|
||||
ancestorBloodAdvantages: {
|
||||
ids: [],
|
||||
},
|
||||
magicalAndBlessedAdvantagesAndDisadvantages: {
|
||||
advantages: {
|
||||
magical: {
|
||||
ids: [],
|
||||
},
|
||||
blessed: {
|
||||
ids: [],
|
||||
},
|
||||
},
|
||||
disadvantages: {
|
||||
magical: {
|
||||
ids: [],
|
||||
},
|
||||
blessed: {
|
||||
ids: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
newApplicationsAndUses: {
|
||||
newApplications: {},
|
||||
uses: {},
|
||||
@@ -614,4 +686,14 @@ export const selectStaticWeaponEnchantments = (state: RootState) =>
|
||||
export const selectStaticWeapons = (state: RootState) => state.database.weapons
|
||||
export const selectStaticZibiljaRituals = (state: RootState) => state.database.zibiljaRituals
|
||||
|
||||
export const selectCache = (state: RootState) => state.database.cache
|
||||
export const selectActivatableSelectOptionsCache = (state: RootState) =>
|
||||
state.database.cache.activatableSelectOptions
|
||||
export const selectAncestorBloodAdvantagesCache = (state: RootState) =>
|
||||
state.database.cache.ancestorBloodAdvantages
|
||||
export const selectMagicalAndBlessedAdvantagesAndDisadvantagesCache = (state: RootState) =>
|
||||
state.database.cache.magicalAndBlessedAdvantagesAndDisadvantages
|
||||
export const selectNewApplicationsAndUsesCache = (state: RootState) =>
|
||||
state.database.cache.newApplicationsAndUses
|
||||
|
||||
export const databaseReducer = databaseSlice.reducer
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
/* eslint-disable max-len */
|
||||
import { createAction } from "@reduxjs/toolkit"
|
||||
import { createImmerReducer } from "../../shared/utils/redux.ts"
|
||||
import { DraftReducer } from "../../shared/utils/redux.ts"
|
||||
import { CharacterState } from "./characterSlice.ts"
|
||||
|
||||
export const incrementLifePoints = createAction("derivedCharacteristics/incrementLifePoints")
|
||||
@@ -50,7 +50,7 @@ export const decrementKarmaPointsBoughtBack = createAction(
|
||||
"derivedCharacteristics/decrementKarmaPointsBoughtBack",
|
||||
)
|
||||
|
||||
export const derivedCharacteristicsReducer = createImmerReducer<CharacterState>((state, action) => {
|
||||
export const derivedCharacteristicsReducer: DraftReducer<CharacterState> = (state, action) => {
|
||||
if (incrementLifePoints.match(action)) {
|
||||
state.derivedCharacteristics.lifePoints.purchased++
|
||||
} else if (decrementLifePoints.match(action)) {
|
||||
@@ -106,4 +106,4 @@ export const derivedCharacteristicsReducer = createImmerReducer<CharacterState>(
|
||||
state.derivedCharacteristics.karmaPoints.permanentlyLostBoughtBack--
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { ImprovementCost, fromRaw } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { registerOrUnregisterPrerequisitesOfLiturgyAsDependencies } from "../../shared/domain/dependencies/fullPrerequisiteRegistrationAsDependencyForType.ts"
|
||||
import { createIdentifierObject } from "../../shared/domain/identifier.ts"
|
||||
import { createEmptyDynamicLiturgicalChant } from "../../shared/domain/rated/liturgicalChant.ts"
|
||||
import { createActivatableRatedWithEnhancementsSlice } from "./activatableRatedWithEnhancementsSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicLiturgicalChant,
|
||||
createInitial: createInitialDynamicLiturgicalChant,
|
||||
getValue: getLiturgicalChantValue,
|
||||
actions: {
|
||||
addAction: addLiturgicalChant,
|
||||
removeAction: removeLiturgicalChant,
|
||||
incrementAction: incrementLiturgicalChant,
|
||||
decrementAction: decrementLiturgicalChant,
|
||||
setAction: setLiturgicalChant,
|
||||
},
|
||||
reducer: liturgicalChantsReducer,
|
||||
} = createActivatableRatedWithEnhancementsSlice({
|
||||
@@ -27,4 +26,5 @@ export const {
|
||||
createIdentifierObject: id => createIdentifierObject("LiturgicalChant", id),
|
||||
registerOrUnregisterPrerequisitesAsDependencies:
|
||||
registerOrUnregisterPrerequisitesOfLiturgyAsDependencies,
|
||||
createEmptyActivatableRatedWithEnhancements: createEmptyDynamicLiturgicalChant,
|
||||
})
|
||||
|
||||
@@ -1,36 +1,39 @@
|
||||
import { ImprovementCost, fromRaw } from "../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { assertExhaustive } from "../../../shared/utils/typeSafety.ts"
|
||||
import { ImprovementCost } from "../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { MagicalTraditionIdentifier } from "../../../shared/domain/identifier.ts"
|
||||
import { getImprovementCostForAnimistPower } from "../../../shared/domain/rated/animistPower.ts"
|
||||
import { createEmptyDynamicMagicalAction } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicAnimistPower,
|
||||
createInitial: createInitialDynamicAnimistPower,
|
||||
getValue: getAnimistPowerValue,
|
||||
actions: {
|
||||
addAction: addAnimistPower,
|
||||
removeAction: removeAnimistPower,
|
||||
incrementAction: incrementAnimistPower,
|
||||
decrementAction: decrementAnimistPower,
|
||||
setAction: setAnimistPower,
|
||||
},
|
||||
reducer: animistPowersReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
namespace: "animistPowers",
|
||||
entityName: "AnimistPower",
|
||||
getState: state => state.magicalActions.animistPowers,
|
||||
getImprovementCost: (id, database) => {
|
||||
getImprovementCost: (id, database, character) => {
|
||||
const animistPower = database.animistPowers[id]
|
||||
if (animistPower === undefined) {
|
||||
const traditionAnimists =
|
||||
character.specialAbilities.magicalTraditions[MagicalTraditionIdentifier.Animisten]
|
||||
|
||||
if (animistPower === undefined || traditionAnimists === undefined) {
|
||||
return ImprovementCost.D
|
||||
}
|
||||
switch (animistPower.improvement_cost.tag) {
|
||||
case "Fixed":
|
||||
return fromRaw(animistPower.improvement_cost.fixed)
|
||||
case "ByPrimaryPatron":
|
||||
// TODO: Replace with derived improvement cost
|
||||
return ImprovementCost.D
|
||||
default:
|
||||
return assertExhaustive(animistPower.improvement_cost)
|
||||
}
|
||||
|
||||
return (
|
||||
getImprovementCostForAnimistPower(
|
||||
patronId => database.patrons[patronId],
|
||||
traditionAnimists,
|
||||
animistPower.improvement_cost,
|
||||
) ?? ImprovementCost.D
|
||||
)
|
||||
},
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { cursesImprovementCost } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import {
|
||||
createEmptyDynamicMagicalAction,
|
||||
cursesImprovementCost,
|
||||
} from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicCurse,
|
||||
createInitial: createInitialDynamicCurse,
|
||||
getValue: getCurseValue,
|
||||
actions: {
|
||||
addAction: addCurse,
|
||||
removeAction: removeCurse,
|
||||
incrementAction: incrementCurse,
|
||||
decrementAction: decrementCurse,
|
||||
setAction: setCurse,
|
||||
},
|
||||
reducer: cursesReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
@@ -18,4 +19,5 @@ export const {
|
||||
entityName: "Curse",
|
||||
getState: state => state.magicalActions.curses,
|
||||
getImprovementCost: (_id, _database) => cursesImprovementCost,
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { dominationRitualsImprovementCost } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import {
|
||||
createEmptyDynamicMagicalAction,
|
||||
dominationRitualsImprovementCost,
|
||||
} from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicDominationRitual,
|
||||
createInitial: createInitialDynamicDominationRitual,
|
||||
getValue: getDominationRitualValue,
|
||||
actions: {
|
||||
addAction: addDominationRitual,
|
||||
removeAction: removeDominationRitual,
|
||||
incrementAction: incrementDominationRitual,
|
||||
decrementAction: decrementDominationRitual,
|
||||
setAction: setDominationRitual,
|
||||
},
|
||||
reducer: dominationRitualsReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
@@ -18,4 +19,5 @@ export const {
|
||||
entityName: "DominationRitual",
|
||||
getState: state => state.magicalActions.dominationRituals,
|
||||
getImprovementCost: (_id, _database) => dominationRitualsImprovementCost,
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { ImprovementCost, fromRaw } from "../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { createEmptyDynamicMagicalAction } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicElvenMagicalSong,
|
||||
createInitial: createInitialDynamicElvenMagicalSong,
|
||||
getValue: getElvenMagicalSongValue,
|
||||
actions: {
|
||||
addAction: addElvenMagicalSong,
|
||||
removeAction: removeElvenMagicalSong,
|
||||
incrementAction: incrementElvenMagicalSong,
|
||||
decrementAction: decrementElvenMagicalSong,
|
||||
setAction: setElvenMagicalSong,
|
||||
},
|
||||
reducer: elvenMagicalSongsReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
@@ -19,4 +18,5 @@ export const {
|
||||
getState: state => state.magicalActions.elvenMagicalSongs,
|
||||
getImprovementCost: (id, database) =>
|
||||
fromRaw(database.elvenMagicalSongs[id]?.improvement_cost) ?? ImprovementCost.D,
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { geodeRitualsImprovementCost } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import {
|
||||
createEmptyDynamicMagicalAction,
|
||||
geodeRitualsImprovementCost,
|
||||
} from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicGeodeRitual,
|
||||
createInitial: createInitialDynamicGeodeRitual,
|
||||
getValue: getGeodeRitualValue,
|
||||
actions: {
|
||||
addAction: addGeodeRitual,
|
||||
removeAction: removeGeodeRitual,
|
||||
incrementAction: incrementGeodeRitual,
|
||||
decrementAction: decrementGeodeRitual,
|
||||
setAction: setGeodeRitual,
|
||||
},
|
||||
reducer: geodeRitualsReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
@@ -18,4 +19,5 @@ export const {
|
||||
entityName: "GeodeRitual",
|
||||
getState: state => state.magicalActions.geodeRituals,
|
||||
getImprovementCost: (_id, _database) => geodeRitualsImprovementCost,
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { ImprovementCost, fromRaw } from "../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { createEmptyDynamicMagicalAction } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicJesterTrick,
|
||||
createInitial: createInitialDynamicJesterTrick,
|
||||
getValue: getJesterTrickValue,
|
||||
actions: {
|
||||
addAction: addJesterTrick,
|
||||
removeAction: removeJesterTrick,
|
||||
incrementAction: incrementJesterTrick,
|
||||
decrementAction: decrementJesterTrick,
|
||||
setAction: setJesterTrick,
|
||||
},
|
||||
reducer: jesterTricksReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
@@ -19,4 +18,5 @@ export const {
|
||||
getState: state => state.magicalActions.jesterTricks,
|
||||
getImprovementCost: (id, database) =>
|
||||
fromRaw(database.jesterTricks[id]?.improvement_cost) ?? ImprovementCost.D,
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { ImprovementCost, fromRaw } from "../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { createEmptyDynamicMagicalAction } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicMagicalDance,
|
||||
createInitial: createInitialDynamicMagicalDance,
|
||||
getValue: getMagicalDanceValue,
|
||||
actions: {
|
||||
addAction: addMagicalDance,
|
||||
removeAction: removeMagicalDance,
|
||||
incrementAction: incrementMagicalDance,
|
||||
decrementAction: decrementMagicalDance,
|
||||
setAction: setMagicalDance,
|
||||
},
|
||||
reducer: magicalDancesReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
@@ -19,4 +18,5 @@ export const {
|
||||
getState: state => state.magicalActions.magicalDances,
|
||||
getImprovementCost: (id, database) =>
|
||||
fromRaw(database.magicalDances[id]?.improvement_cost) ?? ImprovementCost.D,
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { ImprovementCost, fromRaw } from "../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { createEmptyDynamicMagicalAction } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicMagicalMelody,
|
||||
createInitial: createInitialDynamicMagicalMelody,
|
||||
getValue: getMagicalMelodyValue,
|
||||
actions: {
|
||||
addAction: addMagicalMelody,
|
||||
removeAction: removeMagicalMelody,
|
||||
incrementAction: incrementMagicalMelody,
|
||||
decrementAction: decrementMagicalMelody,
|
||||
setAction: setMagicalMelody,
|
||||
},
|
||||
reducer: magicalMelodiesReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
@@ -19,4 +18,5 @@ export const {
|
||||
getState: state => state.magicalActions.magicalMelodies,
|
||||
getImprovementCost: (id, database) =>
|
||||
fromRaw(database.magicalMelodies[id]?.improvement_cost) ?? ImprovementCost.D,
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ImprovementCost } from "../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { createEmptyDynamicMagicalAction } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { getImprovementCostForMagicalRune } from "../../../shared/domain/rated/magicalRune.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
actions: {
|
||||
addAction: addMagicalRune,
|
||||
removeAction: removeMagicalRune,
|
||||
incrementAction: incrementMagicalRune,
|
||||
decrementAction: decrementMagicalRune,
|
||||
setAction: setMagicalRune,
|
||||
},
|
||||
reducer: magicalRunesReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
namespace: "magicalRunes",
|
||||
entityName: "MagicalRune",
|
||||
getState: state => state.magicalActions.magicalRunes,
|
||||
getImprovementCost: (id, database) => {
|
||||
const staticEntry = database.magicalRunes[id]
|
||||
return (
|
||||
getImprovementCostForMagicalRune(
|
||||
// staticEntry?.options,
|
||||
staticEntry?.improvement_cost ?? { tag: "Constant", constant: { value: "D" } },
|
||||
) ?? ImprovementCost.D
|
||||
)
|
||||
},
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
@@ -1,16 +1,15 @@
|
||||
import { ImprovementCost, fromRaw } from "../../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { createEmptyDynamicMagicalAction } from "../../../shared/domain/rated/magicalActions.ts"
|
||||
import { createActivatableRatedSlice } from "../activatableRatedSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicZibiljaRitual,
|
||||
createInitial: createInitialDynamicZibiljaRitual,
|
||||
getValue: getZibiljaRitualValue,
|
||||
actions: {
|
||||
addAction: addZibiljaRitual,
|
||||
removeAction: removeZibiljaRitual,
|
||||
incrementAction: incrementZibiljaRitual,
|
||||
decrementAction: decrementZibiljaRitual,
|
||||
setAction: setZibiljaRitual,
|
||||
},
|
||||
reducer: zibiljaRitualsReducer,
|
||||
} = createActivatableRatedSlice({
|
||||
@@ -19,4 +18,5 @@ export const {
|
||||
getState: state => state.magicalActions.zibiljaRituals,
|
||||
getImprovementCost: (id, database) =>
|
||||
fromRaw(database.zibiljaRituals[id]?.improvement_cost) ?? ImprovementCost.D,
|
||||
createEmptyActivatableRated: createEmptyDynamicMagicalAction,
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Height, Weight, WeightDiceOffsetStrategy } from "optolith-database-sche
|
||||
import { DieType } from "optolith-database-schema/types/_Dice"
|
||||
import { rollDice, separateDice } from "../../shared/utils/dice.ts"
|
||||
import { even, parseInt, randomInt } from "../../shared/utils/math.ts"
|
||||
import { createImmerReducer } from "../../shared/utils/redux.ts"
|
||||
import { DraftReducer } from "../../shared/utils/redux.ts"
|
||||
import { assertExhaustive } from "../../shared/utils/typeSafety.ts"
|
||||
import { CharacterState } from "./characterSlice.ts"
|
||||
|
||||
@@ -78,7 +78,7 @@ export const rerollWeight = createAction(
|
||||
}),
|
||||
)
|
||||
|
||||
export const personalDataReducer = createImmerReducer<CharacterState>((state, action) => {
|
||||
export const personalDataReducer: DraftReducer<CharacterState> = (state, action) => {
|
||||
if (setFamily.match(action)) {
|
||||
state.personalData.family = action.payload === "" ? undefined : action.payload
|
||||
} else if (setPlaceOfBirth.match(action)) {
|
||||
@@ -131,4 +131,4 @@ export const personalDataReducer = createImmerReducer<CharacterState>((state, ac
|
||||
} else if (setOtherInfo.match(action)) {
|
||||
state.personalData.otherInfo = action.payload === "" ? undefined : action.payload
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { createAction } from "@reduxjs/toolkit"
|
||||
import { createImmerReducer } from "../../shared/utils/redux.ts"
|
||||
import { DraftReducer } from "../../shared/utils/redux.ts"
|
||||
import { CharacterState } from "./characterSlice.ts"
|
||||
|
||||
export const setProfession = createAction<{ id: number; instanceId: number }>(
|
||||
@@ -9,7 +9,7 @@ export const setProfession = createAction<{ id: number; instanceId: number }>(
|
||||
export const setProfessionVariant = createAction<number>("profession/setProfessionVariant")
|
||||
export const setCustomProfessionName = createAction<string>("profession/setCustomProfessionName")
|
||||
|
||||
export const professionReducer = createImmerReducer<CharacterState>((state, action) => {
|
||||
export const professionReducer: DraftReducer<CharacterState> = (state, action) => {
|
||||
if (setProfession.match(action)) {
|
||||
state.profession.id = action.payload.id
|
||||
state.profession.instanceId = action.payload.instanceId
|
||||
@@ -19,4 +19,4 @@ export const professionReducer = createImmerReducer<CharacterState>((state, acti
|
||||
} else if (setCustomProfessionName.match(action)) {
|
||||
state.profession.customName = action.payload.length > 0 ? action.payload : undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { createAction } from "@reduxjs/toolkit"
|
||||
import { createImmerReducer } from "../../shared/utils/redux.ts"
|
||||
import { DraftReducer } from "../../shared/utils/redux.ts"
|
||||
import { CharacterState } from "./characterSlice.ts"
|
||||
|
||||
export const changeAttributeAdjustmentId = createAction<number>("race/changeAttributeAdjustmentId")
|
||||
|
||||
export const raceReducer = createImmerReducer<CharacterState>((state, action) => {
|
||||
export const raceReducer: DraftReducer<CharacterState> = (state, action) => {
|
||||
if (changeAttributeAdjustmentId.match(action)) {
|
||||
state.race.selectedAttributeAdjustmentId = action.payload
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { ImprovementCost, fromRaw } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { createEmptyDynamicCombatTechnique } from "../../shared/domain/rated/combatTechnique.ts"
|
||||
import { createRatedSlice } from "./ratedSlice.ts"
|
||||
|
||||
const {
|
||||
create,
|
||||
createInitial,
|
||||
getValue,
|
||||
actions: { incrementAction, decrementAction },
|
||||
reducer,
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
actions: {
|
||||
incrementEntry: incrementRangedCombatTechnique,
|
||||
setEntry: setRangedCombatTechnique,
|
||||
decrementEntry: decrementRangedCombatTechnique,
|
||||
},
|
||||
reducer: rangedCombatTechniquesReducer,
|
||||
} = createRatedSlice({
|
||||
namespace: "combatTechniques/ranged",
|
||||
entityName: "RangedCombatTechnique",
|
||||
@@ -14,13 +17,5 @@ const {
|
||||
minValue: 6,
|
||||
getImprovementCost: (id, database) =>
|
||||
fromRaw(database.rangedCombatTechniques[id]?.improvement_cost) ?? ImprovementCost.D,
|
||||
createEmptyRated: createEmptyDynamicCombatTechnique,
|
||||
})
|
||||
|
||||
export {
|
||||
create as createDynamicRangedCombatTechnique,
|
||||
createInitial as createInitialDynamicRangedCombatTechnique,
|
||||
decrementAction as decrementRangedCombatTechnique,
|
||||
getValue as getRangedCombatTechniqueValue,
|
||||
incrementAction as incrementRangedCombatTechnique,
|
||||
reducer as rangedCombatTechniquesReducer,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { ActionCreatorWithPayload, AnyAction, Draft, createAction } from "@reduxjs/toolkit"
|
||||
import { ImprovementCost } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import {
|
||||
BoundAdventurePoints,
|
||||
cachedAdventurePoints,
|
||||
} from "../../shared/domain/adventurePoints/ratedEntry.ts"
|
||||
import { RatedDependency } from "../../shared/domain/rated/ratedDependency.ts"
|
||||
import { Rated, RatedMap, RatedValue } from "../../shared/domain/rated/ratedEntry.ts"
|
||||
import { Reducer, createImmerReducer } from "../../shared/utils/redux.ts"
|
||||
import { cachedAdventurePoints } from "../../shared/domain/adventurePoints/ratedEntry.ts"
|
||||
import { Rated, RatedMap } from "../../shared/domain/rated/ratedEntry.ts"
|
||||
import { DraftReducer } from "../../shared/utils/redux.ts"
|
||||
import { CharacterState } from "./characterSlice.ts"
|
||||
import { DatabaseState } from "./databaseSlice.ts"
|
||||
|
||||
@@ -14,37 +10,6 @@ import { DatabaseState } from "./databaseSlice.ts"
|
||||
* A slice that handles rated entries.
|
||||
*/
|
||||
export type RatedSlice<N extends string, E extends string> = {
|
||||
/**
|
||||
* Creates a new entry with an initial value if active. The initial adventure
|
||||
* points cache is calculated from the initial value.
|
||||
*/
|
||||
create: (
|
||||
database: DatabaseState,
|
||||
id: number,
|
||||
value: RatedValue,
|
||||
options?: Partial<{
|
||||
dependencies: RatedDependency[]
|
||||
boundAdventurePoints: BoundAdventurePoints[]
|
||||
}>,
|
||||
) => Rated
|
||||
|
||||
/**
|
||||
* Creates a new entry with no initial value.
|
||||
*/
|
||||
createInitial: (
|
||||
id: number,
|
||||
options?: Partial<{
|
||||
dependencies: RatedDependency[]
|
||||
boundAdventurePoints: BoundAdventurePoints[]
|
||||
}>,
|
||||
) => Rated
|
||||
|
||||
/**
|
||||
* Takes an entry that may not exist (because its instance has not been used
|
||||
* yet) and returns its value.
|
||||
*/
|
||||
getValue: (entry: Rated | undefined) => RatedValue
|
||||
|
||||
/**
|
||||
* The actions that can be dispatched to modify the state.
|
||||
*/
|
||||
@@ -52,18 +17,23 @@ export type RatedSlice<N extends string, E extends string> = {
|
||||
/**
|
||||
* Increments the value of the entry with the given id.
|
||||
*/
|
||||
incrementAction: ActionCreatorWithPayload<number, `${N}/increment${E}`>
|
||||
incrementEntry: ActionCreatorWithPayload<{ id: number }, `${N}/increment${E}`>
|
||||
|
||||
/**
|
||||
* Sets the value of the entry with the given id.
|
||||
*/
|
||||
setEntry: ActionCreatorWithPayload<{ id: number; value: number }, `${N}/set${E}`>
|
||||
|
||||
/**
|
||||
* Decrements the value of the entry with the given id.
|
||||
*/
|
||||
decrementAction: ActionCreatorWithPayload<number, `${N}/decrement${E}`>
|
||||
decrementEntry: ActionCreatorWithPayload<{ id: number }, `${N}/decrement${E}`>
|
||||
}
|
||||
|
||||
/**
|
||||
* The reducer that handles the actions.
|
||||
*/
|
||||
reducer: Reducer<CharacterState, AnyAction, [database: DatabaseState]>
|
||||
reducer: DraftReducer<CharacterState, AnyAction, [database: DatabaseState]>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,6 +46,7 @@ export const createRatedSlice = <N extends string, E extends string>(config: {
|
||||
getState: (state: Draft<CharacterState>) => Draft<RatedMap>
|
||||
minValue: number
|
||||
getImprovementCost: (id: number, database: DatabaseState) => ImprovementCost
|
||||
createEmptyRated: (id: number) => Rated
|
||||
}): RatedSlice<N, E> => {
|
||||
const updateCachedAdventurePoints = (entry: Draft<Rated>, database: DatabaseState) => {
|
||||
entry.cachedAdventurePoints = cachedAdventurePoints(
|
||||
@@ -87,72 +58,49 @@ export const createRatedSlice = <N extends string, E extends string>(config: {
|
||||
return entry
|
||||
}
|
||||
|
||||
const create: RatedSlice<N, E>["create"] = (
|
||||
database,
|
||||
id,
|
||||
value,
|
||||
{ dependencies = [], boundAdventurePoints = [] } = {},
|
||||
) =>
|
||||
updateCachedAdventurePoints(
|
||||
{
|
||||
id,
|
||||
value: Math.max(config.minValue, value),
|
||||
cachedAdventurePoints: {
|
||||
general: 0,
|
||||
bound: 0,
|
||||
},
|
||||
dependencies,
|
||||
boundAdventurePoints,
|
||||
},
|
||||
database,
|
||||
)
|
||||
|
||||
const createInitial: RatedSlice<N, E>["createInitial"] = (
|
||||
id,
|
||||
{ dependencies = [], boundAdventurePoints = [] } = {},
|
||||
) => ({
|
||||
id,
|
||||
value: config.minValue,
|
||||
cachedAdventurePoints: {
|
||||
general: 0,
|
||||
bound: 0,
|
||||
},
|
||||
dependencies,
|
||||
boundAdventurePoints,
|
||||
})
|
||||
|
||||
const getValue: RatedSlice<N, E>["getValue"] = entry => entry?.value ?? config.minValue
|
||||
|
||||
const incrementAction = createAction<number, `${N}/increment${E}`>(
|
||||
const incrementEntry = createAction<{ id: number }, `${N}/increment${E}`>(
|
||||
`${config.namespace}/increment${config.entityName}`,
|
||||
)
|
||||
const decrementAction = createAction<number, `${N}/decrement${E}`>(
|
||||
const setEntry = createAction<{ id: number; value: number }, `${N}/set${E}`>(
|
||||
`${config.namespace}/set${config.entityName}`,
|
||||
)
|
||||
const decrementEntry = createAction<{ id: number }, `${N}/decrement${E}`>(
|
||||
`${config.namespace}/decrement${config.entityName}`,
|
||||
)
|
||||
|
||||
const reducer = createImmerReducer(
|
||||
(state: Draft<CharacterState>, action, database: DatabaseState) => {
|
||||
if (incrementAction.match(action)) {
|
||||
const entry = (config.getState(state)[action.payload] ??= createInitial(action.payload))
|
||||
entry.value++
|
||||
const reducer: DraftReducer<CharacterState, AnyAction, [database: DatabaseState]> = (
|
||||
state,
|
||||
action,
|
||||
database,
|
||||
) => {
|
||||
if (incrementEntry.match(action)) {
|
||||
const entry = (config.getState(state)[action.payload.id] ??= config.createEmptyRated(
|
||||
action.payload.id,
|
||||
))
|
||||
entry.value++
|
||||
updateCachedAdventurePoints(entry, database)
|
||||
} else if (setEntry.match(action)) {
|
||||
const entry = (config.getState(state)[action.payload.id] ??= config.createEmptyRated(
|
||||
action.payload.id,
|
||||
))
|
||||
if (action.payload.value >= config.minValue) {
|
||||
entry.value = action.payload.value
|
||||
updateCachedAdventurePoints(entry, database)
|
||||
} else if (decrementAction.match(action)) {
|
||||
const entry = config.getState(state)[action.payload]
|
||||
if (entry !== undefined && entry.value > config.minValue) {
|
||||
entry.value--
|
||||
updateCachedAdventurePoints(entry, database)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
} else if (decrementEntry.match(action)) {
|
||||
const entry = config.getState(state)[action.payload.id]
|
||||
if (entry !== undefined && entry.value > config.minValue) {
|
||||
entry.value--
|
||||
updateCachedAdventurePoints(entry, database)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
create,
|
||||
createInitial,
|
||||
getValue,
|
||||
actions: {
|
||||
incrementAction,
|
||||
decrementAction,
|
||||
incrementEntry,
|
||||
decrementEntry,
|
||||
setEntry,
|
||||
},
|
||||
reducer,
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { ImprovementCost, fromRaw } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { registerOrUnregisterPrerequisitesOfSpellworkAsDependencies } from "../../shared/domain/dependencies/fullPrerequisiteRegistrationAsDependencyForType.ts"
|
||||
import { createIdentifierObject } from "../../shared/domain/identifier.ts"
|
||||
import { createEmptyDynamicSpell } from "../../shared/domain/rated/spell.ts"
|
||||
import { createActivatableRatedWithEnhancementsSlice } from "./activatableRatedWithEnhancementsSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicRitual,
|
||||
createInitial: createInitialDynamicRitual,
|
||||
getValue: getRitualValue,
|
||||
actions: {
|
||||
addAction: addRitual,
|
||||
removeAction: removeRitual,
|
||||
incrementAction: incrementRitual,
|
||||
decrementAction: decrementRitual,
|
||||
setAction: setRitual,
|
||||
},
|
||||
reducer: ritualsReducer,
|
||||
} = createActivatableRatedWithEnhancementsSlice({
|
||||
@@ -27,4 +26,5 @@ export const {
|
||||
createIdentifierObject: id => createIdentifierObject("Ritual", id),
|
||||
registerOrUnregisterPrerequisitesAsDependencies:
|
||||
registerOrUnregisterPrerequisitesOfSpellworkAsDependencies,
|
||||
createEmptyActivatableRatedWithEnhancements: createEmptyDynamicSpell,
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { createAction } from "@reduxjs/toolkit"
|
||||
import { OptionalRuleIdentifier } from "../../shared/domain/identifier.ts"
|
||||
import { createImmerReducer } from "../../shared/utils/redux.ts"
|
||||
import { DraftReducer } from "../../shared/utils/redux.ts"
|
||||
import { CharacterState } from "./characterSlice.ts"
|
||||
|
||||
export const switchIncludeAllPublications = createAction("rules/switchIncludeAllPublications")
|
||||
@@ -12,7 +12,7 @@ export const changeOptionalRuleOption = createAction<{ id: number; option: numbe
|
||||
"rules/changeOptionalRuleOption",
|
||||
)
|
||||
|
||||
export const rulesReducer = createImmerReducer<CharacterState>((state, action) => {
|
||||
export const rulesReducer: DraftReducer<CharacterState> = (state, action) => {
|
||||
if (switchIncludeAllPublications.match(action)) {
|
||||
state.rules.includeAllPublications = !state.rules.includeAllPublications
|
||||
} else if (switchIncludePublication.match(action)) {
|
||||
@@ -54,4 +54,4 @@ export const rulesReducer = createImmerReducer<CharacterState>((state, action) =
|
||||
;(state.rules.optionalRules[action.payload.id]!.options ??= [])[0] = action.payload.option
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { ImprovementCost, fromRaw } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { createEmptyDynamicSkill } from "../../shared/domain/rated/skill.ts"
|
||||
import { createRatedSlice } from "./ratedSlice.ts"
|
||||
|
||||
const {
|
||||
create,
|
||||
createInitial,
|
||||
getValue,
|
||||
actions: { incrementAction, decrementAction },
|
||||
reducer,
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
actions: { incrementEntry: incrementSkill, decrementEntry: decrementSkill, setEntry: setSkill },
|
||||
reducer: skillsReducer,
|
||||
} = createRatedSlice({
|
||||
namespace: "skills",
|
||||
entityName: "Skill",
|
||||
@@ -14,13 +13,5 @@ const {
|
||||
minValue: 0,
|
||||
getImprovementCost: (id, database) =>
|
||||
fromRaw(database.skills[id]?.improvement_cost) ?? ImprovementCost.D,
|
||||
createEmptyRated: createEmptyDynamicSkill,
|
||||
})
|
||||
|
||||
export {
|
||||
create as createDynamicSkill,
|
||||
createInitial as createInitialDynamicSkill,
|
||||
decrementAction as decrementSkill,
|
||||
incrementAction as incrementSkill,
|
||||
getValue as skillValue,
|
||||
reducer as skillsReducer,
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { ImprovementCost, fromRaw } from "../../shared/domain/adventurePoints/improvementCost.ts"
|
||||
import { registerOrUnregisterPrerequisitesOfSpellworkAsDependencies } from "../../shared/domain/dependencies/fullPrerequisiteRegistrationAsDependencyForType.ts"
|
||||
import { createIdentifierObject } from "../../shared/domain/identifier.ts"
|
||||
import { createEmptyDynamicSpell } from "../../shared/domain/rated/spell.ts"
|
||||
import { createActivatableRatedWithEnhancementsSlice } from "./activatableRatedWithEnhancementsSlice.ts"
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
export const {
|
||||
create: createDynamicSpell,
|
||||
createInitial: createInitialDynamicSpell,
|
||||
getValue: getSpellValue,
|
||||
actions: {
|
||||
addAction: addSpell,
|
||||
removeAction: removeSpell,
|
||||
incrementAction: incrementSpell,
|
||||
decrementAction: decrementSpell,
|
||||
setAction: setSpell,
|
||||
},
|
||||
reducer: spellsReducer,
|
||||
} = createActivatableRatedWithEnhancementsSlice({
|
||||
@@ -27,4 +26,5 @@ export const {
|
||||
createIdentifierObject: id => createIdentifierObject("Ritual", id),
|
||||
registerOrUnregisterPrerequisitesAsDependencies:
|
||||
registerOrUnregisterPrerequisitesOfSpellworkAsDependencies,
|
||||
createEmptyActivatableRatedWithEnhancements: createEmptyDynamicSpell,
|
||||
})
|
||||
|
||||
@@ -14,12 +14,12 @@ export type TinyActivatableSlice<N extends string, E extends string> = {
|
||||
/**
|
||||
* Adds the entry with the given id.
|
||||
*/
|
||||
addAction: ActionCreatorWithPayload<number, `${N}/add${E}`>
|
||||
addAction: ActionCreatorWithPayload<{ id: number }, `${N}/add${E}`>
|
||||
|
||||
/**
|
||||
* Remove the entry with the given id.
|
||||
*/
|
||||
removeAction: ActionCreatorWithPayload<number, `${N}/remove${E}`>
|
||||
removeAction: ActionCreatorWithPayload<{ id: number }, `${N}/remove${E}`>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,25 +36,25 @@ export const createTinyActivatableSlice = <N extends string, E extends string>(c
|
||||
entityName: E
|
||||
getState: (state: Draft<CharacterState>) => Draft<TinyActivatableMap>
|
||||
}): TinyActivatableSlice<N, E> => {
|
||||
const addAction = createAction<number, `${N}/add${E}`>(
|
||||
const addAction = createAction<{ id: number }, `${N}/add${E}`>(
|
||||
`${config.namespace}/add${config.entityName}`,
|
||||
)
|
||||
const removeAction = createAction<number, `${N}/remove${E}`>(
|
||||
const removeAction = createAction<{ id: number }, `${N}/remove${E}`>(
|
||||
`${config.namespace}/remove${config.entityName}`,
|
||||
)
|
||||
|
||||
const reducer = createImmerReducer((state: Draft<CharacterState>, action) => {
|
||||
const focusedState = config.getState(state)
|
||||
if (addAction.match(action)) {
|
||||
if (!Object.hasOwn(focusedState, action.payload)) {
|
||||
focusedState[action.payload] = {
|
||||
id: action.payload,
|
||||
if (!Object.hasOwn(focusedState, action.payload.id)) {
|
||||
focusedState[action.payload.id] = {
|
||||
id: action.payload.id,
|
||||
active: true,
|
||||
}
|
||||
}
|
||||
} else if (removeAction.match(action)) {
|
||||
if (Object.hasOwn(focusedState, action.payload)) {
|
||||
delete focusedState[action.payload]
|
||||
if (Object.hasOwn(focusedState, action.payload.id)) {
|
||||
delete focusedState[action.payload.id]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -21,7 +21,7 @@ export const RecommendedReference: FC<Props> = props => {
|
||||
<div className="recommended-ref">
|
||||
<div className="unrec">
|
||||
<div className="icon" />
|
||||
<div className="name">{translate("showfrequency.unfamiliarspells")}</div>
|
||||
<div className="name">{translate("Unfamiliar Spells")}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -32,16 +32,16 @@ export const RecommendedReference: FC<Props> = props => {
|
||||
{strongly === true ? (
|
||||
<div className="strongly-recommended">
|
||||
<div className="icon" />
|
||||
<div className="name">{translate("showfrequency.stronglyrecommended")}</div>
|
||||
<div className="name">{translate("Strongly Recommended")}</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="rec">
|
||||
<div className="icon" />
|
||||
<div className="name">{translate("showfrequency.common")}</div>
|
||||
<div className="name">{translate("Common")}</div>
|
||||
</div>
|
||||
<div className="unrec">
|
||||
<div className="icon" />
|
||||
<div className="name">{translate("showfrequency.uncommon")}</div>
|
||||
<div className="name">{translate("Uncommon")}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { describe, it } from "node:test"
|
||||
import { AdventurePointsCache } from "./cache.ts"
|
||||
import { ImprovementCost } from "./improvementCost.ts"
|
||||
import { RatedAdventurePointsCache, cachedAdventurePoints } from "./ratedEntry.ts"
|
||||
import { cachedAdventurePoints } from "./ratedEntry.ts"
|
||||
|
||||
describe("cachedAdventurePoints", () => {
|
||||
it("returns the calculated value if bound adventure points are only granted at at least the current rating", () => {
|
||||
assert.deepEqual<RatedAdventurePointsCache>(
|
||||
assert.deepEqual<AdventurePointsCache>(
|
||||
cachedAdventurePoints(9, 8, [{ rating: 9, adventurePoints: 10 }], ImprovementCost.E),
|
||||
{
|
||||
general: 15,
|
||||
@@ -15,7 +16,7 @@ describe("cachedAdventurePoints", () => {
|
||||
})
|
||||
|
||||
it("returns the split calculated value if bound adventure points have to be considered", () => {
|
||||
assert.deepEqual<RatedAdventurePointsCache>(
|
||||
assert.deepEqual<AdventurePointsCache>(
|
||||
cachedAdventurePoints(9, 8, [{ rating: 8, adventurePoints: 10 }], ImprovementCost.E),
|
||||
{
|
||||
general: 5,
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { range } from "../../utils/array.ts"
|
||||
import {
|
||||
AdventurePointsCache,
|
||||
addAdventurePointsCaches,
|
||||
emptyAdventurePointsCache,
|
||||
} from "./cache.ts"
|
||||
import { ImprovementCost, adventurePointsForIncrement } from "./improvementCost.ts"
|
||||
|
||||
/**
|
||||
@@ -6,7 +11,7 @@ import { ImprovementCost, adventurePointsForIncrement } from "./improvementCost.
|
||||
* entry. They don’t effect the costs of the rating at the time of granting, so
|
||||
* the rating at which they have been granted is stored as well.
|
||||
*/
|
||||
export type BoundAdventurePoints = {
|
||||
export type BoundAdventurePointsForRated = {
|
||||
/**
|
||||
* The rating at which they have been granted. If the adventure points have
|
||||
* been granted when the entry was not active yet, the rating is `undefined`.
|
||||
@@ -19,35 +24,8 @@ export type BoundAdventurePoints = {
|
||||
adventurePoints: number
|
||||
}
|
||||
|
||||
/**
|
||||
* The accumulated used adventure points value of all value increases. It is
|
||||
* split by used bound and used general adventure points.
|
||||
*/
|
||||
export type RatedAdventurePointsCache = {
|
||||
/**
|
||||
* The used general adventure points.
|
||||
*/
|
||||
general: number
|
||||
|
||||
/**
|
||||
* The used bound adventure points.
|
||||
*/
|
||||
bound: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two caches together.
|
||||
*/
|
||||
const addCache = (
|
||||
cache1: RatedAdventurePointsCache,
|
||||
cache2: RatedAdventurePointsCache,
|
||||
): RatedAdventurePointsCache => ({
|
||||
general: cache1.general + cache2.general,
|
||||
bound: cache1.bound + cache2.bound,
|
||||
})
|
||||
|
||||
const groupBoundAdventurePointsByRating = (
|
||||
boundAdventurePoints: BoundAdventurePoints[],
|
||||
boundAdventurePoints: BoundAdventurePointsForRated[],
|
||||
): ReadonlyMap<number | "activation", number> =>
|
||||
boundAdventurePoints.reduce((map, { rating: boundRating, adventurePoints }) => {
|
||||
const key = boundRating ?? "activation"
|
||||
@@ -60,7 +38,7 @@ const accumulateCache = (
|
||||
initialApplicableBoundKey: number | "activation",
|
||||
boundByValue: ReadonlyMap<number | "activation", number>,
|
||||
ic: ImprovementCost,
|
||||
): RatedAdventurePointsCache => {
|
||||
): AdventurePointsCache => {
|
||||
const { usedGeneral, usedBound } = range(startValue, endValue).reduce(
|
||||
(acc, currentValue) => {
|
||||
const costForStep = adventurePointsForIncrement(ic, currentValue - 1)
|
||||
@@ -97,14 +75,11 @@ const accumulateCache = (
|
||||
export const cachedAdventurePoints = (
|
||||
value: number,
|
||||
minValue: number,
|
||||
boundAdventurePoints: BoundAdventurePoints[],
|
||||
boundAdventurePoints: BoundAdventurePointsForRated[],
|
||||
ic: ImprovementCost,
|
||||
): RatedAdventurePointsCache => {
|
||||
): AdventurePointsCache => {
|
||||
if (minValue >= value) {
|
||||
return {
|
||||
general: 0,
|
||||
bound: 0,
|
||||
}
|
||||
return emptyAdventurePointsCache
|
||||
} else {
|
||||
const boundByValue = groupBoundAdventurePointsByRating(boundAdventurePoints)
|
||||
return accumulateCache(minValue + 1, value, minValue, boundByValue, ic)
|
||||
@@ -118,14 +93,11 @@ export const cachedAdventurePoints = (
|
||||
*/
|
||||
export const cachedAdventurePointsForActivatable = (
|
||||
value: number | undefined,
|
||||
boundAdventurePoints: BoundAdventurePoints[],
|
||||
boundAdventurePoints: BoundAdventurePointsForRated[],
|
||||
ic: ImprovementCost,
|
||||
): RatedAdventurePointsCache => {
|
||||
): AdventurePointsCache => {
|
||||
if (value === undefined) {
|
||||
return {
|
||||
general: 0,
|
||||
bound: 0,
|
||||
}
|
||||
return emptyAdventurePointsCache
|
||||
} else {
|
||||
const boundByValue = groupBoundAdventurePointsByRating(boundAdventurePoints)
|
||||
return accumulateCache(0, value, "activation", boundByValue, ic)
|
||||
@@ -140,23 +112,20 @@ export const cachedAdventurePointsForActivatable = (
|
||||
*/
|
||||
export const cachedAdventurePointsForActivatableWithEnhancements = (
|
||||
value: number | undefined,
|
||||
boundAdventurePoints: BoundAdventurePoints[],
|
||||
boundAdventurePoints: BoundAdventurePointsForRated[],
|
||||
ic: ImprovementCost,
|
||||
enhancements: number[],
|
||||
getAdventurePointsModifierForEnhancement: (enhancementId: number) => number,
|
||||
): RatedAdventurePointsCache => {
|
||||
): AdventurePointsCache => {
|
||||
if (value === undefined) {
|
||||
return {
|
||||
general: 0,
|
||||
bound: 0,
|
||||
}
|
||||
return emptyAdventurePointsCache
|
||||
} else {
|
||||
const boundByValue = groupBoundAdventurePointsByRating(boundAdventurePoints)
|
||||
const enhancementAdventurePoints = enhancements.reduce(
|
||||
(acc, enhancementId) => acc + getAdventurePointsModifierForEnhancement(enhancementId),
|
||||
0,
|
||||
)
|
||||
return addCache(accumulateCache(0, value, "activation", boundByValue, ic), {
|
||||
return addAdventurePointsCaches(accumulateCache(0, value, "activation", boundByValue, ic), {
|
||||
general: enhancementAdventurePoints,
|
||||
bound: 0,
|
||||
})
|
||||
|
||||
@@ -17,10 +17,12 @@ export const baseCharacter: Character = {
|
||||
id: 1,
|
||||
variantId: 1,
|
||||
selectedAttributeAdjustmentId: 1,
|
||||
dependencies: [],
|
||||
},
|
||||
culture: {
|
||||
id: 1,
|
||||
isCulturalPackageApplied: false,
|
||||
dependencies: [],
|
||||
},
|
||||
profession: {
|
||||
id: 1,
|
||||
@@ -29,12 +31,14 @@ export const baseCharacter: Character = {
|
||||
rules: {
|
||||
includeAllPublications: false,
|
||||
includePublications: [],
|
||||
publicationDependencies: [],
|
||||
focusRules: {},
|
||||
optionalRules: {},
|
||||
},
|
||||
states: {},
|
||||
personalData: {
|
||||
sex: { type: "Male" },
|
||||
sexDependencies: [],
|
||||
socialStatus: {
|
||||
dependencies: [],
|
||||
},
|
||||
@@ -69,7 +73,6 @@ export const baseCharacter: Character = {
|
||||
krallenkettenzauber: {},
|
||||
liturgicalStyleSpecialAbilities: {},
|
||||
lycantropicGifts: {},
|
||||
magicalRunes: {},
|
||||
magicalSigns: {},
|
||||
magicalSpecialAbilities: {},
|
||||
magicalTraditions: {},
|
||||
@@ -93,6 +96,8 @@ export const baseCharacter: Character = {
|
||||
weaponEnchantments: {},
|
||||
},
|
||||
attributes: {},
|
||||
magicalPrimaryAttributeDependencies: [],
|
||||
blessedPrimaryAttributeDependencies: [],
|
||||
derivedCharacteristics: {
|
||||
lifePoints: {
|
||||
purchased: 0,
|
||||
@@ -127,12 +132,11 @@ export const baseCharacter: Character = {
|
||||
animistPowers: {},
|
||||
geodeRituals: {},
|
||||
zibiljaRituals: {},
|
||||
magicalRunes: {},
|
||||
},
|
||||
blessings: [],
|
||||
liturgicalChants: {},
|
||||
ceremonies: {},
|
||||
magicalPrimaryAttributeDependencies: [],
|
||||
blessedPrimaryAttributeDependencies: [],
|
||||
// items: {}
|
||||
// hitZoneArmors: {}
|
||||
purse: {
|
||||
@@ -143,4 +147,5 @@ export const baseCharacter: Character = {
|
||||
},
|
||||
// creatures: {}
|
||||
// pact: {}
|
||||
pactDependencies: [],
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { ActivatableMap, TinyActivatableMap } from "./activatable/activatableEntry.ts"
|
||||
import { Color } from "./color.ts"
|
||||
import { CultureDependency } from "./culture.ts"
|
||||
import { Energy, EnergyWithBuyBack } from "./energy.ts"
|
||||
import { Pact } from "./pact.ts"
|
||||
import { Pact, PactDependency } from "./pact.ts"
|
||||
import { Purse } from "./purse.ts"
|
||||
import { RatedDependency } from "./rated/ratedDependency.ts"
|
||||
import { RaceDependency } from "./race.ts"
|
||||
import { PrimaryAttributeDependency } from "./rated/primaryAttribute.ts"
|
||||
import {
|
||||
ActivatableRatedMap,
|
||||
ActivatableRatedWithEnhancementsMap,
|
||||
@@ -11,8 +13,9 @@ import {
|
||||
} from "./rated/ratedEntry.ts"
|
||||
import { FocusRuleInstance } from "./rules/focusRule.ts"
|
||||
import { OptionalRuleInstance } from "./rules/optionalRule.ts"
|
||||
import { Sex } from "./sex.ts"
|
||||
import { Sex, SexDependency } from "./sex.ts"
|
||||
import { SocialStatusDependency } from "./socialStatus.ts"
|
||||
import { PublicationDependency } from "./sources/publicationDependency.ts"
|
||||
import { StateInstance } from "./state.ts"
|
||||
|
||||
/**
|
||||
@@ -81,6 +84,11 @@ export type Character = {
|
||||
* The identifier of the attribute adjustment that has been selected from the race.
|
||||
*/
|
||||
selectedAttributeAdjustmentId: number
|
||||
|
||||
/**
|
||||
* Dependencies on the selected race.
|
||||
*/
|
||||
dependencies: RaceDependency[]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,6 +104,11 @@ export type Character = {
|
||||
* Describes whether the cultural package has been applied when creating the character.
|
||||
*/
|
||||
isCulturalPackageApplied: boolean
|
||||
|
||||
/**
|
||||
* Dependencies on the selected culture.
|
||||
*/
|
||||
dependencies: CultureDependency[]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,6 +165,11 @@ export type Character = {
|
||||
*/
|
||||
includePublications: number[]
|
||||
|
||||
/**
|
||||
* Dependencies on certain publications.
|
||||
*/
|
||||
publicationDependencies: PublicationDependency[]
|
||||
|
||||
/**
|
||||
* A map of focus rules that may be active for the character.
|
||||
*/
|
||||
@@ -183,6 +201,11 @@ export type Character = {
|
||||
*/
|
||||
sex: Sex
|
||||
|
||||
/**
|
||||
* Dependencies on the character’s sex.
|
||||
*/
|
||||
sexDependencies: SexDependency[]
|
||||
|
||||
/**
|
||||
* The family names and/or family members.
|
||||
*/
|
||||
@@ -285,7 +308,6 @@ export type Character = {
|
||||
krallenkettenzauber: ActivatableMap
|
||||
liturgicalStyleSpecialAbilities: ActivatableMap
|
||||
lycantropicGifts: ActivatableMap
|
||||
magicalRunes: ActivatableMap
|
||||
magicalSigns: ActivatableMap
|
||||
magicalSpecialAbilities: ActivatableMap
|
||||
magicalTraditions: ActivatableMap
|
||||
@@ -311,6 +333,9 @@ export type Character = {
|
||||
|
||||
attributes: RatedMap
|
||||
|
||||
magicalPrimaryAttributeDependencies: PrimaryAttributeDependency[]
|
||||
blessedPrimaryAttributeDependencies: PrimaryAttributeDependency[]
|
||||
|
||||
derivedCharacteristics: {
|
||||
lifePoints: Energy
|
||||
arcaneEnergy: EnergyWithBuyBack
|
||||
@@ -338,18 +363,17 @@ export type Character = {
|
||||
animistPowers: ActivatableRatedMap
|
||||
geodeRituals: ActivatableRatedMap
|
||||
zibiljaRituals: ActivatableRatedMap
|
||||
magicalRunes: ActivatableRatedMap
|
||||
}
|
||||
|
||||
blessings: TinyActivatableMap
|
||||
liturgicalChants: ActivatableRatedWithEnhancementsMap
|
||||
ceremonies: ActivatableRatedWithEnhancementsMap
|
||||
|
||||
magicalPrimaryAttributeDependencies: RatedDependency[]
|
||||
blessedPrimaryAttributeDependencies: RatedDependency[]
|
||||
|
||||
// items: {}
|
||||
// hitZoneArmors: {}
|
||||
purse: Purse
|
||||
// creatures: {}
|
||||
pact?: Pact
|
||||
pactDependencies: PactDependency[]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Culture } from "optolith-database-schema/types/Culture"
|
||||
import { ActivatableIdentifier } from "optolith-database-schema/types/_IdentifierGroup"
|
||||
import { TranslateMap } from "../utils/translate.ts"
|
||||
|
||||
/**
|
||||
@@ -12,3 +13,29 @@ export const getCulture = (cultures: Record<number, Culture>, id: number): Cultu
|
||||
*/
|
||||
export const getFullCultureName = (translateMap: TranslateMap, culture: Culture): string =>
|
||||
translateMap(culture.translations)?.name ?? ""
|
||||
|
||||
/**
|
||||
* A dependency on a culture.
|
||||
*/
|
||||
export type CultureDependency = Readonly<{
|
||||
/**
|
||||
* The identifier of the dependency source.
|
||||
*/
|
||||
sourceId: ActivatableIdentifier
|
||||
|
||||
/**
|
||||
* The top-level index of the prerequisite. If the prerequisite is part of a
|
||||
* group or disjunction, this is the index of the group or disjunction.
|
||||
*/
|
||||
index: number
|
||||
|
||||
/**
|
||||
* Is the source prerequisite part of a prerequisite disjunction?
|
||||
*/
|
||||
isPartOfDisjunction: boolean
|
||||
|
||||
/**
|
||||
* The required culture's identifier.
|
||||
*/
|
||||
id: number
|
||||
}>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Returns the carrying capacity of a character with the given strength value.
|
||||
*/
|
||||
export const getCarryingCapacity = (strength: number): number => strength * 2
|
||||
+657
-111
@@ -1,4 +1,5 @@
|
||||
import * as ID from "optolith-database-schema/types/_Identifier"
|
||||
import { assertExhaustive } from "../utils/typeSafety.ts"
|
||||
|
||||
// TODO: Update for new identifier mappings
|
||||
|
||||
@@ -143,6 +144,17 @@ export enum SkillIdentifier {
|
||||
Clothworking = 59,
|
||||
}
|
||||
|
||||
/**
|
||||
* Used identifiers of skill groups.
|
||||
*/
|
||||
export enum SkillGroupIdentifier {
|
||||
Physical = 1,
|
||||
Social = 2,
|
||||
Nature = 3,
|
||||
Knowledge = 4,
|
||||
Craft = 5,
|
||||
}
|
||||
|
||||
/**
|
||||
* Used identifiers of advantages.
|
||||
*/
|
||||
@@ -154,28 +166,28 @@ export enum AdvantageIdentifier {
|
||||
Luck = 14,
|
||||
ExceptionalSkill = 16,
|
||||
ExceptionalCombatTechnique = 17,
|
||||
IncreasedAstralPower = 23,
|
||||
IncreasedKarmaPoints = 24,
|
||||
IncreasedLifePoints = 25,
|
||||
IncreasedSpirit = 26,
|
||||
IncreasedToughness = 27,
|
||||
ImmunityToPoison = 28,
|
||||
ImmunityToDisease = 29,
|
||||
MagicalAttunement = 32,
|
||||
Rich = 36,
|
||||
SociallyAdaptable = 40,
|
||||
InspireConfidence = 46,
|
||||
WeaponAptitude = 47,
|
||||
Spellcaster = 50,
|
||||
Unyielding = 54, // Eisern
|
||||
LargeSpellSelection = 58,
|
||||
HatredOf = 68,
|
||||
Prediger = 77,
|
||||
Visionaer = 78,
|
||||
ZahlreichePredigten = 79,
|
||||
ZahlreicheVisionen = 80,
|
||||
LeichterGang = 92,
|
||||
Einkommen = 99,
|
||||
IncreasedAstralPower = 20,
|
||||
IncreasedKarmaPoints = 21,
|
||||
IncreasedLifePoints = 22,
|
||||
IncreasedSpirit = 23,
|
||||
IncreasedToughness = 24,
|
||||
ImmunityToPoison = 25,
|
||||
ImmunityToDisease = 26,
|
||||
MagicalAttunement = 29,
|
||||
Rich = 33,
|
||||
SociallyAdaptable = 37,
|
||||
InspireConfidence = 43,
|
||||
WeaponAptitude = 44,
|
||||
Spellcaster = 47,
|
||||
Unyielding = 51, // Eisern
|
||||
HatredOf = 55,
|
||||
LargeSpellSelection = 66,
|
||||
LeichterGang = 85,
|
||||
Preacher = 91,
|
||||
Visionary = 92,
|
||||
ManySermons = 93,
|
||||
ManyVisions = 94,
|
||||
Einkommen = 129,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,28 +198,28 @@ export enum DisadvantageIdentifier {
|
||||
AfraidOf = 1,
|
||||
Poor = 2,
|
||||
Slow = 4,
|
||||
NoFlyingBalm = 17,
|
||||
NoFamiliar = 18,
|
||||
MagicalRestriction = 24,
|
||||
DecreasedArcanePower = 26,
|
||||
DecreasedKarmaPoints = 27,
|
||||
DecreasedLifePoints = 28,
|
||||
DecreasedSpirit = 29,
|
||||
DecreasedToughness = 30,
|
||||
BadLuck = 31,
|
||||
PersonalityFlaw = 33,
|
||||
Principles = 34,
|
||||
BadHabit = 36,
|
||||
NegativeTrait = 37, // Schlechte Eigenschaft
|
||||
Stigma = 45,
|
||||
Deaf = 47, // Taub
|
||||
Incompetent = 48,
|
||||
Obligations = 50, // Verpflichtungen
|
||||
Maimed = 51, // Verstümmelt
|
||||
NoFlyingBalm = 14,
|
||||
NoFamiliar = 15,
|
||||
MagicalRestriction = 21,
|
||||
DecreasedArcanePower = 23,
|
||||
DecreasedKarmaPoints = 24,
|
||||
DecreasedLifePoints = 25,
|
||||
DecreasedSpirit = 26,
|
||||
DecreasedToughness = 27,
|
||||
BadLuck = 28,
|
||||
PersonalityFlaw = 30,
|
||||
Principles = 31,
|
||||
BadHabit = 33,
|
||||
NegativeTrait = 34, // Schlechte Eigenschaft
|
||||
Stigma = 42,
|
||||
Deaf = 44, // Taub
|
||||
Incompetent = 45,
|
||||
Obligations = 47, // Verpflichtungen
|
||||
Maimed = 48, // Verstümmelt
|
||||
BrittleBones = 56, // Gläsern
|
||||
SmallSpellSelection = 59,
|
||||
WenigePredigten = 72,
|
||||
WenigeVisionen = 73,
|
||||
SmallSpellSelection = 64,
|
||||
FewerSermons = 70,
|
||||
FewerVisions = 71,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,8 +240,12 @@ export enum CombatSpecialAbilityIdentifier {
|
||||
* Used identifiers of general special abilities.
|
||||
*/
|
||||
export enum GeneralSpecialAbilityIdentifier {
|
||||
SkillSpecialization = 9,
|
||||
CraftInstruments = 17,
|
||||
Hunter = 18,
|
||||
Literacy = 27,
|
||||
Language = 29,
|
||||
LanguageSpecialization = 30,
|
||||
FireEater = 53,
|
||||
}
|
||||
|
||||
@@ -239,6 +255,7 @@ export enum GeneralSpecialAbilityIdentifier {
|
||||
export enum MagicalSpecialAbilityIdentifier {
|
||||
PropertyKnowledge = 3,
|
||||
GrosseMeditation = 12,
|
||||
Adaptation = 18,
|
||||
Imitationszauberei = 51,
|
||||
}
|
||||
|
||||
@@ -269,6 +286,21 @@ export enum MagicalTraditionIdentifier {
|
||||
Runenschoepfer = 24,
|
||||
}
|
||||
|
||||
/**
|
||||
* Used identifiers of magical special abilities.
|
||||
*/
|
||||
export enum MagicStyleSpecialAbilityIdentifier {
|
||||
ScholarDesMagierkollegsZuHoningen = 24,
|
||||
MadaschwesternStil = 55,
|
||||
}
|
||||
|
||||
/**
|
||||
* Used identifiers of pact gifts.
|
||||
*/
|
||||
export enum PactGiftIdentifier {
|
||||
DunklesAbbildDerBuendnisgabe = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
* Used identifiers of aspects.
|
||||
*/
|
||||
@@ -305,6 +337,18 @@ export enum BlessedTraditionIdentifier {
|
||||
}
|
||||
|
||||
type TagPropertyOptions = {
|
||||
Publication: ID.PublicationIdentifier
|
||||
ExperienceLevel: ID.ExperienceLevelIdentifier
|
||||
CoreRule: ID.CoreRuleIdentifier
|
||||
FocusRule: ID.FocusRuleIdentifier
|
||||
Subject: ID.SubjectIdentifier
|
||||
OptionalRule: ID.OptionalRuleIdentifier
|
||||
Race: ID.RaceIdentifier
|
||||
Culture: ID.CultureIdentifier
|
||||
Profession: ID.ProfessionIdentifier
|
||||
ProfessionVariant: ID.ProfessionVariantIdentifier
|
||||
Curriculum: ID.CurriculumIdentifier
|
||||
Guideline: ID.GuidelineIdentifier
|
||||
Advantage: ID.AdvantageIdentifier
|
||||
Disadvantage: ID.DisadvantageIdentifier
|
||||
GeneralSpecialAbility: ID.GeneralSpecialAbilityIdentifier
|
||||
@@ -336,6 +380,7 @@ type TagPropertyOptions = {
|
||||
MagicalTradition: ID.MagicalTraditionIdentifier
|
||||
BlessedTradition: ID.BlessedTraditionIdentifier
|
||||
PactGift: ID.PactGiftIdentifier
|
||||
VampiricGift: ID.VampiricGiftIdentifier
|
||||
SikaryanDrainSpecialAbility: ID.SikaryanDrainSpecialAbilityIdentifier
|
||||
LycantropicGift: ID.LycantropicGiftIdentifier
|
||||
SkillStyleSpecialAbility: ID.SkillStyleSpecialAbilityIdentifier
|
||||
@@ -353,13 +398,25 @@ type TagPropertyOptions = {
|
||||
ChronicleEnchantment: ID.ChronicleEnchantmentIdentifier
|
||||
Krallenkettenzauber: ID.KrallenkettenzauberIdentifier
|
||||
Trinkhornzauber: ID.TrinkhornzauberIdentifier
|
||||
MagicalRune: ID.MagicalRuneIdentifier
|
||||
MagicalSign: ID.MagicalSignIdentifier
|
||||
Language: ID.LanguageIdentifier
|
||||
Script: ID.ScriptIdentifier
|
||||
Continent: ID.ContinentIdentifier
|
||||
SocialStatus: ID.SocialStatusIdentifier
|
||||
Attribute: ID.AttributeIdentifier
|
||||
Skill: ID.SkillIdentifier
|
||||
SkillGroup: ID.SkillGroupIdentifier
|
||||
CloseCombatTechnique: ID.CloseCombatTechniqueIdentifier
|
||||
RangedCombatTechnique: ID.RangedCombatTechniqueIdentifier
|
||||
Cantrip: ID.CantripIdentifier
|
||||
Spell: ID.SpellIdentifier
|
||||
Ritual: ID.RitualIdentifier
|
||||
Cantrip: ID.CantripIdentifier
|
||||
Property: ID.PropertyIdentifier
|
||||
LiturgicalChant: ID.LiturgicalChantIdentifier
|
||||
Ceremony: ID.CeremonyIdentifier
|
||||
Blessing: ID.BlessingIdentifier
|
||||
Aspect: ID.AspectIdentifier
|
||||
Curse: ID.CurseIdentifier
|
||||
ElvenMagicalSong: ID.ElvenMagicalSongIdentifier
|
||||
DominationRitual: ID.DominationRitualIdentifier
|
||||
@@ -369,9 +426,12 @@ type TagPropertyOptions = {
|
||||
AnimistPower: ID.AnimistPowerIdentifier
|
||||
GeodeRitual: ID.GeodeRitualIdentifier
|
||||
ZibiljaRitual: ID.ZibiljaRitualIdentifier
|
||||
Blessing: ID.BlessingIdentifier
|
||||
LiturgicalChant: ID.LiturgicalChantIdentifier
|
||||
Ceremony: ID.CeremonyIdentifier
|
||||
AnimalType: ID.AnimalTypeIdentifier
|
||||
TargetCategory: ID.TargetCategoryIdentifier
|
||||
General: ID.GeneralIdentifier
|
||||
Element: ID.ElementIdentifier
|
||||
AnimalShapeSize: ID.AnimalShapeSizeIdentifier
|
||||
Patron: ID.PatronIdentifier
|
||||
Ammunition: ID.AmmunitionIdentifier
|
||||
Animal: ID.AnimalIdentifier
|
||||
AnimalCare: ID.AnimalCareIdentifier
|
||||
@@ -401,44 +461,56 @@ type TagPropertyOptions = {
|
||||
Vehicle: ID.VehicleIdentifier
|
||||
Weapon: ID.WeaponIdentifier
|
||||
WeaponAccessory: ID.WeaponAccessoryIdentifier
|
||||
Reach: ID.ReachIdentifier
|
||||
PatronCategory: ID.PatronCategoryIdentifier
|
||||
PersonalityTrait: ID.PersonalityTraitIdentifier
|
||||
HairColor: ID.HairColorIdentifier
|
||||
EyeColor: ID.EyeColorIdentifier
|
||||
PactCategory: ID.PactCategoryIdentifier
|
||||
PactDomain: ID.PactDomainIdentifier
|
||||
AnimistTribe: ID.AnimistTribeIdentifier
|
||||
Influence: ID.InfluenceIdentifier
|
||||
Condition: ID.ConditionIdentifier
|
||||
State: ID.StateIdentifier
|
||||
Disease: ID.DiseaseIdentifier
|
||||
SexPractice: ID.SexPracticeIdentifier
|
||||
TradeSecret: ID.TradeSecretIdentifier
|
||||
AnimalShape: ID.AnimalShapeIdentifier
|
||||
ArcaneBardTradition: ID.ArcaneBardTraditionIdentifier
|
||||
ArcaneDancerTradition: ID.ArcaneDancerTraditionIdentifier
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
const TagPropertyMap: {
|
||||
[K in keyof TagPropertyOptions]: (id: number) => TagPropertyOptions[K]
|
||||
} = {
|
||||
Publication: id => ({ tag: "Publication", publication: id }),
|
||||
ExperienceLevel: id => ({ tag: "ExperienceLevel", experience_level: id }),
|
||||
CoreRule: id => ({ tag: "CoreRule", core_rule: id }),
|
||||
FocusRule: id => ({ tag: "FocusRule", focus_rule: id }),
|
||||
Subject: id => ({ tag: "Subject", subject: id }),
|
||||
OptionalRule: id => ({ tag: "OptionalRule", optional_rule: id }),
|
||||
Race: id => ({ tag: "Race", race: id }),
|
||||
Culture: id => ({ tag: "Culture", culture: id }),
|
||||
Profession: id => ({ tag: "Profession", profession: id }),
|
||||
ProfessionVariant: id => ({ tag: "ProfessionVariant", profession_variant: id }),
|
||||
Curriculum: id => ({ tag: "Curriculum", curriculum: id }),
|
||||
Guideline: id => ({ tag: "Guideline", guideline: id }),
|
||||
Advantage: id => ({ tag: "Advantage", advantage: id }),
|
||||
Disadvantage: id => ({ tag: "Disadvantage", disadvantage: id }),
|
||||
GeneralSpecialAbility: id => ({ tag: "GeneralSpecialAbility", general_special_ability: id }),
|
||||
FatePointSpecialAbility: id => ({
|
||||
tag: "FatePointSpecialAbility",
|
||||
fate_point_special_ability: id,
|
||||
}),
|
||||
FatePointSpecialAbility: id => ({ tag: "FatePointSpecialAbility", fate_point_special_ability: id }),
|
||||
CombatSpecialAbility: id => ({ tag: "CombatSpecialAbility", combat_special_ability: id }),
|
||||
MagicalSpecialAbility: id => ({ tag: "MagicalSpecialAbility", magical_special_ability: id }),
|
||||
StaffEnchantment: id => ({ tag: "StaffEnchantment", staff_enchantment: id }),
|
||||
FamiliarSpecialAbility: id => ({ tag: "FamiliarSpecialAbility", familiar_special_ability: id }),
|
||||
KarmaSpecialAbility: id => ({ tag: "KarmaSpecialAbility", karma_special_ability: id }),
|
||||
ProtectiveWardingCircleSpecialAbility: id => ({
|
||||
tag: "ProtectiveWardingCircleSpecialAbility",
|
||||
protective_warding_circle_special_ability: id,
|
||||
}),
|
||||
CombatStyleSpecialAbility: id => ({
|
||||
tag: "CombatStyleSpecialAbility",
|
||||
combat_style_special_ability: id,
|
||||
}),
|
||||
AdvancedCombatSpecialAbility: id => ({
|
||||
tag: "AdvancedCombatSpecialAbility",
|
||||
advanced_combat_special_ability: id,
|
||||
}),
|
||||
ProtectiveWardingCircleSpecialAbility: id => ({ tag: "ProtectiveWardingCircleSpecialAbility", protective_warding_circle_special_ability: id }),
|
||||
CombatStyleSpecialAbility: id => ({ tag: "CombatStyleSpecialAbility", combat_style_special_ability: id }),
|
||||
AdvancedCombatSpecialAbility: id => ({ tag: "AdvancedCombatSpecialAbility", advanced_combat_special_ability: id }),
|
||||
CommandSpecialAbility: id => ({ tag: "CommandSpecialAbility", command_special_ability: id }),
|
||||
MagicStyleSpecialAbility: id => ({
|
||||
tag: "MagicStyleSpecialAbility",
|
||||
magic_style_special_ability: id,
|
||||
}),
|
||||
AdvancedMagicalSpecialAbility: id => ({
|
||||
tag: "AdvancedMagicalSpecialAbility",
|
||||
advanced_magical_special_ability: id,
|
||||
}),
|
||||
MagicStyleSpecialAbility: id => ({ tag: "MagicStyleSpecialAbility", magic_style_special_ability: id }),
|
||||
AdvancedMagicalSpecialAbility: id => ({ tag: "AdvancedMagicalSpecialAbility", advanced_magical_special_ability: id }),
|
||||
SpellSwordEnchantment: id => ({ tag: "SpellSwordEnchantment", spell_sword_enchantment: id }),
|
||||
DaggerRitual: id => ({ tag: "DaggerRitual", dagger_ritual: id }),
|
||||
InstrumentEnchantment: id => ({ tag: "InstrumentEnchantment", instrument_enchantment: id }),
|
||||
@@ -447,45 +519,25 @@ const TagPropertyMap: {
|
||||
WandEnchantment: id => ({ tag: "WandEnchantment", wand_enchantment: id }),
|
||||
BrawlingSpecialAbility: id => ({ tag: "BrawlingSpecialAbility", brawling_special_ability: id }),
|
||||
AncestorGlyph: id => ({ tag: "AncestorGlyph", ancestor_glyph: id }),
|
||||
CeremonialItemSpecialAbility: id => ({
|
||||
tag: "CeremonialItemSpecialAbility",
|
||||
ceremonial_item_special_ability: id,
|
||||
}),
|
||||
CeremonialItemSpecialAbility: id => ({ tag: "CeremonialItemSpecialAbility", ceremonial_item_special_ability: id }),
|
||||
Sermon: id => ({ tag: "Sermon", sermon: id }),
|
||||
LiturgicalStyleSpecialAbility: id => ({
|
||||
tag: "LiturgicalStyleSpecialAbility",
|
||||
liturgical_style_special_ability: id,
|
||||
}),
|
||||
AdvancedKarmaSpecialAbility: id => ({
|
||||
tag: "AdvancedKarmaSpecialAbility",
|
||||
advanced_karma_special_ability: id,
|
||||
}),
|
||||
LiturgicalStyleSpecialAbility: id => ({ tag: "LiturgicalStyleSpecialAbility", liturgical_style_special_ability: id }),
|
||||
AdvancedKarmaSpecialAbility: id => ({ tag: "AdvancedKarmaSpecialAbility", advanced_karma_special_ability: id }),
|
||||
Vision: id => ({ tag: "Vision", vision: id }),
|
||||
MagicalTradition: id => ({ tag: "MagicalTradition", magical_tradition: id }),
|
||||
BlessedTradition: id => ({ tag: "BlessedTradition", blessed_tradition: id }),
|
||||
PactGift: id => ({ tag: "PactGift", pact_gift: id }),
|
||||
SikaryanDrainSpecialAbility: id => ({
|
||||
tag: "SikaryanDrainSpecialAbility",
|
||||
sikaryan_drain_special_ability: id,
|
||||
}),
|
||||
VampiricGift: id => ({ tag: "VampiricGift", vampiric_gift: id }),
|
||||
SikaryanDrainSpecialAbility: id => ({ tag: "SikaryanDrainSpecialAbility", sikaryan_drain_special_ability: id }),
|
||||
LycantropicGift: id => ({ tag: "LycantropicGift", lycantropic_gift: id }),
|
||||
SkillStyleSpecialAbility: id => ({
|
||||
tag: "SkillStyleSpecialAbility",
|
||||
skill_style_special_ability: id,
|
||||
}),
|
||||
AdvancedSkillSpecialAbility: id => ({
|
||||
tag: "AdvancedSkillSpecialAbility",
|
||||
advanced_skill_special_ability: id,
|
||||
}),
|
||||
SkillStyleSpecialAbility: id => ({ tag: "SkillStyleSpecialAbility", skill_style_special_ability: id }),
|
||||
AdvancedSkillSpecialAbility: id => ({ tag: "AdvancedSkillSpecialAbility", advanced_skill_special_ability: id }),
|
||||
ArcaneOrbEnchantment: id => ({ tag: "ArcaneOrbEnchantment", arcane_orb_enchantment: id }),
|
||||
CauldronEnchantment: id => ({ tag: "CauldronEnchantment", cauldron_enchantment: id }),
|
||||
FoolsHatEnchantment: id => ({ tag: "FoolsHatEnchantment", fools_hat_enchantment: id }),
|
||||
ToyEnchantment: id => ({ tag: "ToyEnchantment", toy_enchantment: id }),
|
||||
BowlEnchantment: id => ({ tag: "BowlEnchantment", bowl_enchantment: id }),
|
||||
FatePointSexSpecialAbility: id => ({
|
||||
tag: "FatePointSexSpecialAbility",
|
||||
fate_point_sex_special_ability: id,
|
||||
}),
|
||||
FatePointSexSpecialAbility: id => ({ tag: "FatePointSexSpecialAbility", fate_point_sex_special_ability: id }),
|
||||
SexSpecialAbility: id => ({ tag: "SexSpecialAbility", sex_special_ability: id }),
|
||||
WeaponEnchantment: id => ({ tag: "WeaponEnchantment", weapon_enchantment: id }),
|
||||
SickleRitual: id => ({ tag: "SickleRitual", sickle_ritual: id }),
|
||||
@@ -493,13 +545,25 @@ const TagPropertyMap: {
|
||||
ChronicleEnchantment: id => ({ tag: "ChronicleEnchantment", chronicle_enchantment: id }),
|
||||
Krallenkettenzauber: id => ({ tag: "Krallenkettenzauber", krallenkettenzauber: id }),
|
||||
Trinkhornzauber: id => ({ tag: "Trinkhornzauber", trinkhornzauber: id }),
|
||||
MagicalRune: id => ({ tag: "MagicalRune", magical_rune: id }),
|
||||
MagicalSign: id => ({ tag: "MagicalSign", magical_sign: id }),
|
||||
Language: id => ({ tag: "Language", language: id }),
|
||||
Script: id => ({ tag: "Script", script: id }),
|
||||
Continent: id => ({ tag: "Continent", continent: id }),
|
||||
SocialStatus: id => ({ tag: "SocialStatus", social_status: id }),
|
||||
Attribute: id => ({ tag: "Attribute", attribute: id }),
|
||||
Skill: id => ({ tag: "Skill", skill: id }),
|
||||
SkillGroup: id => ({ tag: "SkillGroup", skill_group: id }),
|
||||
CloseCombatTechnique: id => ({ tag: "CloseCombatTechnique", close_combat_technique: id }),
|
||||
RangedCombatTechnique: id => ({ tag: "RangedCombatTechnique", ranged_combat_technique: id }),
|
||||
Cantrip: id => ({ tag: "Cantrip", cantrip: id }),
|
||||
Spell: id => ({ tag: "Spell", spell: id }),
|
||||
Ritual: id => ({ tag: "Ritual", ritual: id }),
|
||||
Cantrip: id => ({ tag: "Cantrip", cantrip: id }),
|
||||
Property: id => ({ tag: "Property", property: id }),
|
||||
LiturgicalChant: id => ({ tag: "LiturgicalChant", liturgical_chant: id }),
|
||||
Ceremony: id => ({ tag: "Ceremony", ceremony: id }),
|
||||
Blessing: id => ({ tag: "Blessing", blessing: id }),
|
||||
Aspect: id => ({ tag: "Aspect", aspect: id }),
|
||||
Curse: id => ({ tag: "Curse", curse: id }),
|
||||
ElvenMagicalSong: id => ({ tag: "ElvenMagicalSong", elven_magical_song: id }),
|
||||
DominationRitual: id => ({ tag: "DominationRitual", domination_ritual: id }),
|
||||
@@ -509,9 +573,12 @@ const TagPropertyMap: {
|
||||
AnimistPower: id => ({ tag: "AnimistPower", animist_power: id }),
|
||||
GeodeRitual: id => ({ tag: "GeodeRitual", geode_ritual: id }),
|
||||
ZibiljaRitual: id => ({ tag: "ZibiljaRitual", zibilja_ritual: id }),
|
||||
Blessing: id => ({ tag: "Blessing", blessing: id }),
|
||||
LiturgicalChant: id => ({ tag: "LiturgicalChant", liturgical_chant: id }),
|
||||
Ceremony: id => ({ tag: "Ceremony", ceremony: id }),
|
||||
AnimalType: id => ({ tag: "AnimalType", animal_type: id }),
|
||||
TargetCategory: id => ({ tag: "TargetCategory", target_category: id }),
|
||||
General: id => ({ tag: "General", general: id }),
|
||||
Element: id => ({ tag: "Element", element: id }),
|
||||
AnimalShapeSize: id => ({ tag: "AnimalShapeSize", animal_shape_size: id }),
|
||||
Patron: id => ({ tag: "Patron", patron: id }),
|
||||
Ammunition: id => ({ tag: "Ammunition", ammunition: id }),
|
||||
Animal: id => ({ tag: "Animal", animal: id }),
|
||||
AnimalCare: id => ({ tag: "AnimalCare", animal_care: id }),
|
||||
@@ -524,14 +591,8 @@ const TagPropertyMap: {
|
||||
Elixir: id => ({ tag: "Elixir", elixir: id }),
|
||||
EquipmentOfBlessedOnes: id => ({ tag: "EquipmentOfBlessedOnes", equipment_of_blessed_ones: id }),
|
||||
GemOrPreciousStone: id => ({ tag: "GemOrPreciousStone", gem_or_precious_stone: id }),
|
||||
IlluminationLightSource: id => ({
|
||||
tag: "IlluminationLightSource",
|
||||
illumination_light_source: id,
|
||||
}),
|
||||
IlluminationRefillsOrSupplies: id => ({
|
||||
tag: "IlluminationRefillsOrSupplies",
|
||||
illumination_refills_or_supplies: id,
|
||||
}),
|
||||
IlluminationLightSource: id => ({ tag: "IlluminationLightSource", illumination_light_source: id }),
|
||||
IlluminationRefillsOrSupplies: id => ({ tag: "IlluminationRefillsOrSupplies", illumination_refills_or_supplies: id }),
|
||||
Jewelry: id => ({ tag: "Jewelry", jewelry: id }),
|
||||
Liebesspielzeug: id => ({ tag: "Liebesspielzeug", liebesspielzeug: id }),
|
||||
LuxuryGood: id => ({ tag: "LuxuryGood", luxury_good: id }),
|
||||
@@ -547,6 +608,23 @@ const TagPropertyMap: {
|
||||
Vehicle: id => ({ tag: "Vehicle", vehicle: id }),
|
||||
Weapon: id => ({ tag: "Weapon", weapon: id }),
|
||||
WeaponAccessory: id => ({ tag: "WeaponAccessory", weapon_accessory: id }),
|
||||
Reach: id => ({ tag: "Reach", reach: id }),
|
||||
PatronCategory: id => ({ tag: "PatronCategory", patron_category: id }),
|
||||
PersonalityTrait: id => ({ tag: "PersonalityTrait", personality_trait: id }),
|
||||
HairColor: id => ({ tag: "HairColor", hair_color: id }),
|
||||
EyeColor: id => ({ tag: "EyeColor", eye_color: id }),
|
||||
PactCategory: id => ({ tag: "PactCategory", pact_category: id }),
|
||||
PactDomain: id => ({ tag: "PactDomain", pact_domain: id }),
|
||||
AnimistTribe: id => ({ tag: "AnimistTribe", animist_tribe: id }),
|
||||
Influence: id => ({ tag: "Influence", influence: id }),
|
||||
Condition: id => ({ tag: "Condition", condition: id }),
|
||||
State: id => ({ tag: "State", state: id }),
|
||||
Disease: id => ({ tag: "Disease", disease: id }),
|
||||
SexPractice: id => ({ tag: "SexPractice", sex_practice: id }),
|
||||
TradeSecret: id => ({ tag: "TradeSecret", trade_secret: id }),
|
||||
AnimalShape: id => ({ tag: "AnimalShape", animal_shape: id }),
|
||||
ArcaneBardTradition: id => ({ tag: "ArcaneBardTradition", arcane_bard_tradition: id }),
|
||||
ArcaneDancerTradition: id => ({ tag: "ArcaneDancerTradition", arcane_dancer_tradition: id }),
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -556,3 +634,471 @@ export const createIdentifierObject = <T extends keyof TagPropertyOptions>(
|
||||
tag: T,
|
||||
id: number,
|
||||
): TagPropertyOptions[T] => TagPropertyMap[tag](id)
|
||||
|
||||
/**
|
||||
* Returns a function by a type name that creates an identifier object from a
|
||||
* plain identifier.
|
||||
*/
|
||||
export const getCreateIdentifierObject = <T extends keyof TagPropertyOptions>(
|
||||
tag: T,
|
||||
): ((id: number) => TagPropertyOptions[T]) => TagPropertyMap[tag]
|
||||
|
||||
/**
|
||||
* Splits an identifier object into its type name and plain identifier.
|
||||
*/
|
||||
export const splitIdentifierObject = <T extends keyof TagPropertyOptions>(
|
||||
obj: TagPropertyOptions[T],
|
||||
): [tag: T, id: number] => {
|
||||
// prettier-ignore
|
||||
switch (obj.tag) {
|
||||
case "Publication": return [obj.tag as T, obj.publication]
|
||||
case "ExperienceLevel": return [obj.tag as T, obj.experience_level]
|
||||
case "CoreRule": return [obj.tag as T, obj.core_rule]
|
||||
case "FocusRule": return [obj.tag as T, obj.focus_rule]
|
||||
case "Subject": return [obj.tag as T, obj.subject]
|
||||
case "OptionalRule": return [obj.tag as T, obj.optional_rule]
|
||||
case "Race": return [obj.tag as T, obj.race]
|
||||
case "Culture": return [obj.tag as T, obj.culture]
|
||||
case "Profession": return [obj.tag as T, obj.profession]
|
||||
case "ProfessionVariant": return [obj.tag as T, obj.profession_variant]
|
||||
case "Curriculum": return [obj.tag as T, obj.curriculum]
|
||||
case "Guideline": return [obj.tag as T, obj.guideline]
|
||||
case "Advantage": return [obj.tag as T, obj.advantage]
|
||||
case "Disadvantage": return [obj.tag as T, obj.disadvantage]
|
||||
case "GeneralSpecialAbility": return [obj.tag as T, obj.general_special_ability]
|
||||
case "FatePointSpecialAbility": return [obj.tag as T, obj.fate_point_special_ability]
|
||||
case "CombatSpecialAbility": return [obj.tag as T, obj.combat_special_ability]
|
||||
case "MagicalSpecialAbility": return [obj.tag as T, obj.magical_special_ability]
|
||||
case "StaffEnchantment": return [obj.tag as T, obj.staff_enchantment]
|
||||
case "FamiliarSpecialAbility": return [obj.tag as T, obj.familiar_special_ability]
|
||||
case "KarmaSpecialAbility": return [obj.tag as T, obj.karma_special_ability]
|
||||
case "ProtectiveWardingCircleSpecialAbility": return [obj.tag as T, obj.protective_warding_circle_special_ability]
|
||||
case "CombatStyleSpecialAbility": return [obj.tag as T, obj.combat_style_special_ability]
|
||||
case "AdvancedCombatSpecialAbility": return [obj.tag as T, obj.advanced_combat_special_ability]
|
||||
case "CommandSpecialAbility": return [obj.tag as T, obj.command_special_ability]
|
||||
case "MagicStyleSpecialAbility": return [obj.tag as T, obj.magic_style_special_ability]
|
||||
case "AdvancedMagicalSpecialAbility": return [obj.tag as T, obj.advanced_magical_special_ability]
|
||||
case "SpellSwordEnchantment": return [obj.tag as T, obj.spell_sword_enchantment]
|
||||
case "DaggerRitual": return [obj.tag as T, obj.dagger_ritual]
|
||||
case "InstrumentEnchantment": return [obj.tag as T, obj.instrument_enchantment]
|
||||
case "AttireEnchantment": return [obj.tag as T, obj.attire_enchantment]
|
||||
case "OrbEnchantment": return [obj.tag as T, obj.orb_enchantment]
|
||||
case "WandEnchantment": return [obj.tag as T, obj.wand_enchantment]
|
||||
case "BrawlingSpecialAbility": return [obj.tag as T, obj.brawling_special_ability]
|
||||
case "AncestorGlyph": return [obj.tag as T, obj.ancestor_glyph]
|
||||
case "CeremonialItemSpecialAbility": return [obj.tag as T, obj.ceremonial_item_special_ability]
|
||||
case "Sermon": return [obj.tag as T, obj.sermon]
|
||||
case "LiturgicalStyleSpecialAbility": return [obj.tag as T, obj.liturgical_style_special_ability]
|
||||
case "AdvancedKarmaSpecialAbility": return [obj.tag as T, obj.advanced_karma_special_ability]
|
||||
case "Vision": return [obj.tag as T, obj.vision]
|
||||
case "MagicalTradition": return [obj.tag as T, obj.magical_tradition]
|
||||
case "BlessedTradition": return [obj.tag as T, obj.blessed_tradition]
|
||||
case "PactGift": return [obj.tag as T, obj.pact_gift]
|
||||
case "VampiricGift": return [obj.tag as T, obj.vampiric_gift]
|
||||
case "SikaryanDrainSpecialAbility": return [obj.tag as T, obj.sikaryan_drain_special_ability]
|
||||
case "LycantropicGift": return [obj.tag as T, obj.lycantropic_gift]
|
||||
case "SkillStyleSpecialAbility": return [obj.tag as T, obj.skill_style_special_ability]
|
||||
case "AdvancedSkillSpecialAbility": return [obj.tag as T, obj.advanced_skill_special_ability]
|
||||
case "ArcaneOrbEnchantment": return [obj.tag as T, obj.arcane_orb_enchantment]
|
||||
case "CauldronEnchantment": return [obj.tag as T, obj.cauldron_enchantment]
|
||||
case "FoolsHatEnchantment": return [obj.tag as T, obj.fools_hat_enchantment]
|
||||
case "ToyEnchantment": return [obj.tag as T, obj.toy_enchantment]
|
||||
case "BowlEnchantment": return [obj.tag as T, obj.bowl_enchantment]
|
||||
case "FatePointSexSpecialAbility": return [obj.tag as T, obj.fate_point_sex_special_ability]
|
||||
case "SexSpecialAbility": return [obj.tag as T, obj.sex_special_ability]
|
||||
case "WeaponEnchantment": return [obj.tag as T, obj.weapon_enchantment]
|
||||
case "SickleRitual": return [obj.tag as T, obj.sickle_ritual]
|
||||
case "RingEnchantment": return [obj.tag as T, obj.ring_enchantment]
|
||||
case "ChronicleEnchantment": return [obj.tag as T, obj.chronicle_enchantment]
|
||||
case "Krallenkettenzauber": return [obj.tag as T, obj.krallenkettenzauber]
|
||||
case "Trinkhornzauber": return [obj.tag as T, obj.trinkhornzauber]
|
||||
case "MagicalRune": return [obj.tag as T, obj.magical_rune]
|
||||
case "MagicalSign": return [obj.tag as T, obj.magical_sign]
|
||||
case "Language": return [obj.tag as T, obj.language]
|
||||
case "Script": return [obj.tag as T, obj.script]
|
||||
case "Continent": return [obj.tag as T, obj.continent]
|
||||
case "SocialStatus": return [obj.tag as T, obj.social_status]
|
||||
case "Attribute": return [obj.tag as T, obj.attribute]
|
||||
case "Skill": return [obj.tag as T, obj.skill]
|
||||
case "SkillGroup": return [obj.tag as T, obj.skill_group]
|
||||
case "CloseCombatTechnique": return [obj.tag as T, obj.close_combat_technique]
|
||||
case "RangedCombatTechnique": return [obj.tag as T, obj.ranged_combat_technique]
|
||||
case "Spell": return [obj.tag as T, obj.spell]
|
||||
case "Ritual": return [obj.tag as T, obj.ritual]
|
||||
case "Cantrip": return [obj.tag as T, obj.cantrip]
|
||||
case "Property": return [obj.tag as T, obj.property]
|
||||
case "LiturgicalChant": return [obj.tag as T, obj.liturgical_chant]
|
||||
case "Ceremony": return [obj.tag as T, obj.ceremony]
|
||||
case "Blessing": return [obj.tag as T, obj.blessing]
|
||||
case "Aspect": return [obj.tag as T, obj.aspect]
|
||||
case "Curse": return [obj.tag as T, obj.curse]
|
||||
case "ElvenMagicalSong": return [obj.tag as T, obj.elven_magical_song]
|
||||
case "DominationRitual": return [obj.tag as T, obj.domination_ritual]
|
||||
case "MagicalMelody": return [obj.tag as T, obj.magical_melody]
|
||||
case "MagicalDance": return [obj.tag as T, obj.magical_dance]
|
||||
case "JesterTrick": return [obj.tag as T, obj.jester_trick]
|
||||
case "AnimistPower": return [obj.tag as T, obj.animist_power]
|
||||
case "GeodeRitual": return [obj.tag as T, obj.geode_ritual]
|
||||
case "ZibiljaRitual": return [obj.tag as T, obj.zibilja_ritual]
|
||||
case "AnimalType": return [obj.tag as T, obj.animal_type]
|
||||
case "TargetCategory": return [obj.tag as T, obj.target_category]
|
||||
case "General": return [obj.tag as T, obj.general]
|
||||
case "Element": return [obj.tag as T, obj.element]
|
||||
case "AnimalShapeSize": return [obj.tag as T, obj.animal_shape_size]
|
||||
case "Patron": return [obj.tag as T, obj.patron]
|
||||
case "Ammunition": return [obj.tag as T, obj.ammunition]
|
||||
case "Animal": return [obj.tag as T, obj.animal]
|
||||
case "AnimalCare": return [obj.tag as T, obj.animal_care]
|
||||
case "Armor": return [obj.tag as T, obj.armor]
|
||||
case "BandageOrRemedy": return [obj.tag as T, obj.bandage_or_remedy]
|
||||
case "Book": return [obj.tag as T, obj.book]
|
||||
case "CeremonialItem": return [obj.tag as T, obj.ceremonial_item]
|
||||
case "Clothes": return [obj.tag as T, obj.clothes]
|
||||
case "Container": return [obj.tag as T, obj.container]
|
||||
case "Elixir": return [obj.tag as T, obj.elixir]
|
||||
case "EquipmentOfBlessedOnes": return [obj.tag as T, obj.equipment_of_blessed_ones]
|
||||
case "GemOrPreciousStone": return [obj.tag as T, obj.gem_or_precious_stone]
|
||||
case "IlluminationLightSource": return [obj.tag as T, obj.illumination_light_source]
|
||||
case "IlluminationRefillsOrSupplies": return [obj.tag as T, obj.illumination_refills_or_supplies]
|
||||
case "Jewelry": return [obj.tag as T, obj.jewelry]
|
||||
case "Liebesspielzeug": return [obj.tag as T, obj.liebesspielzeug]
|
||||
case "LuxuryGood": return [obj.tag as T, obj.luxury_good]
|
||||
case "MagicalArtifact": return [obj.tag as T, obj.magical_artifact]
|
||||
case "MusicalInstrument": return [obj.tag as T, obj.musical_instrument]
|
||||
case "OrienteeringAid": return [obj.tag as T, obj.orienteering_aid]
|
||||
case "Poison": return [obj.tag as T, obj.poison]
|
||||
case "RopeOrChain": return [obj.tag as T, obj.rope_or_chain]
|
||||
case "Stationary": return [obj.tag as T, obj.stationary]
|
||||
case "ThievesTool": return [obj.tag as T, obj.thieves_tool]
|
||||
case "ToolOfTheTrade": return [obj.tag as T, obj.tool_of_the_trade]
|
||||
case "TravelGearOrTool": return [obj.tag as T, obj.travel_gear_or_tool]
|
||||
case "Vehicle": return [obj.tag as T, obj.vehicle]
|
||||
case "Weapon": return [obj.tag as T, obj.weapon]
|
||||
case "WeaponAccessory": return [obj.tag as T, obj.weapon_accessory]
|
||||
case "Reach": return [obj.tag as T, obj.reach]
|
||||
case "PatronCategory": return [obj.tag as T, obj.patron_category]
|
||||
case "PersonalityTrait": return [obj.tag as T, obj.personality_trait]
|
||||
case "HairColor": return [obj.tag as T, obj.hair_color]
|
||||
case "EyeColor": return [obj.tag as T, obj.eye_color]
|
||||
case "PactCategory": return [obj.tag as T, obj.pact_category]
|
||||
case "PactDomain": return [obj.tag as T, obj.pact_domain]
|
||||
case "AnimistTribe": return [obj.tag as T, obj.animist_tribe]
|
||||
case "Influence": return [obj.tag as T, obj.influence]
|
||||
case "Condition": return [obj.tag as T, obj.condition]
|
||||
case "State": return [obj.tag as T, obj.state]
|
||||
case "Disease": return [obj.tag as T, obj.disease]
|
||||
case "SexPractice": return [obj.tag as T, obj.sex_practice]
|
||||
case "TradeSecret": return [obj.tag as T, obj.trade_secret]
|
||||
case "AnimalShape": return [obj.tag as T, obj.animal_shape]
|
||||
case "ArcaneBardTradition": return [obj.tag as T, obj.arcane_bard_tradition]
|
||||
case "ArcaneDancerTradition": return [obj.tag as T, obj.arcane_dancer_tradition]
|
||||
default: return assertExhaustive(obj)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two identifiers are equal.
|
||||
*/
|
||||
export const equalsIdentifier = <T extends TagPropertyOptions[keyof TagPropertyOptions]>(
|
||||
a: T,
|
||||
b: T,
|
||||
): boolean => {
|
||||
// prettier-ignore
|
||||
switch (a.tag) {
|
||||
case "Publication": return b.tag === "Publication" && a.publication === b.publication
|
||||
case "ExperienceLevel": return b.tag === "ExperienceLevel" && a.experience_level === b.experience_level
|
||||
case "CoreRule": return b.tag === "CoreRule" && a.core_rule === b.core_rule
|
||||
case "FocusRule": return b.tag === "FocusRule" && a.focus_rule === b.focus_rule
|
||||
case "Subject": return b.tag === "Subject" && a.subject === b.subject
|
||||
case "OptionalRule": return b.tag === "OptionalRule" && a.optional_rule === b.optional_rule
|
||||
case "Race": return b.tag === "Race" && a.race === b.race
|
||||
case "Culture": return b.tag === "Culture" && a.culture === b.culture
|
||||
case "Profession": return b.tag === "Profession" && a.profession === b.profession
|
||||
case "ProfessionVariant": return b.tag === "ProfessionVariant" && a.profession_variant === b.profession_variant
|
||||
case "Curriculum": return b.tag === "Curriculum" && a.curriculum === b.curriculum
|
||||
case "Guideline": return b.tag === "Guideline" && a.guideline === b.guideline
|
||||
case "Advantage": return b.tag === "Advantage" && a.advantage === b.advantage
|
||||
case "Disadvantage": return b.tag === "Disadvantage" && a.disadvantage === b.disadvantage
|
||||
case "GeneralSpecialAbility": return b.tag === "GeneralSpecialAbility" && a.general_special_ability === b.general_special_ability
|
||||
case "FatePointSpecialAbility": return b.tag === "FatePointSpecialAbility" && a.fate_point_special_ability === b.fate_point_special_ability
|
||||
case "CombatSpecialAbility": return b.tag === "CombatSpecialAbility" && a.combat_special_ability === b.combat_special_ability
|
||||
case "MagicalSpecialAbility": return b.tag === "MagicalSpecialAbility" && a.magical_special_ability === b.magical_special_ability
|
||||
case "StaffEnchantment": return b.tag === "StaffEnchantment" && a.staff_enchantment === b.staff_enchantment
|
||||
case "FamiliarSpecialAbility": return b.tag === "FamiliarSpecialAbility" && a.familiar_special_ability === b.familiar_special_ability
|
||||
case "KarmaSpecialAbility": return b.tag === "KarmaSpecialAbility" && a.karma_special_ability === b.karma_special_ability
|
||||
case "ProtectiveWardingCircleSpecialAbility": return b.tag === "ProtectiveWardingCircleSpecialAbility" && a.protective_warding_circle_special_ability === b.protective_warding_circle_special_ability
|
||||
case "CombatStyleSpecialAbility": return b.tag === "CombatStyleSpecialAbility" && a.combat_style_special_ability === b.combat_style_special_ability
|
||||
case "AdvancedCombatSpecialAbility": return b.tag === "AdvancedCombatSpecialAbility" && a.advanced_combat_special_ability === b.advanced_combat_special_ability
|
||||
case "CommandSpecialAbility": return b.tag === "CommandSpecialAbility" && a.command_special_ability === b.command_special_ability
|
||||
case "MagicStyleSpecialAbility": return b.tag === "MagicStyleSpecialAbility" && a.magic_style_special_ability === b.magic_style_special_ability
|
||||
case "AdvancedMagicalSpecialAbility": return b.tag === "AdvancedMagicalSpecialAbility" && a.advanced_magical_special_ability === b.advanced_magical_special_ability
|
||||
case "SpellSwordEnchantment": return b.tag === "SpellSwordEnchantment" && a.spell_sword_enchantment === b.spell_sword_enchantment
|
||||
case "DaggerRitual": return b.tag === "DaggerRitual" && a.dagger_ritual === b.dagger_ritual
|
||||
case "InstrumentEnchantment": return b.tag === "InstrumentEnchantment" && a.instrument_enchantment === b.instrument_enchantment
|
||||
case "AttireEnchantment": return b.tag === "AttireEnchantment" && a.attire_enchantment === b.attire_enchantment
|
||||
case "OrbEnchantment": return b.tag === "OrbEnchantment" && a.orb_enchantment === b.orb_enchantment
|
||||
case "WandEnchantment": return b.tag === "WandEnchantment" && a.wand_enchantment === b.wand_enchantment
|
||||
case "BrawlingSpecialAbility": return b.tag === "BrawlingSpecialAbility" && a.brawling_special_ability === b.brawling_special_ability
|
||||
case "AncestorGlyph": return b.tag === "AncestorGlyph" && a.ancestor_glyph === b.ancestor_glyph
|
||||
case "CeremonialItemSpecialAbility": return b.tag === "CeremonialItemSpecialAbility" && a.ceremonial_item_special_ability === b.ceremonial_item_special_ability
|
||||
case "Sermon": return b.tag === "Sermon" && a.sermon === b.sermon
|
||||
case "LiturgicalStyleSpecialAbility": return b.tag === "LiturgicalStyleSpecialAbility" && a.liturgical_style_special_ability === b.liturgical_style_special_ability
|
||||
case "AdvancedKarmaSpecialAbility": return b.tag === "AdvancedKarmaSpecialAbility" && a.advanced_karma_special_ability === b.advanced_karma_special_ability
|
||||
case "Vision": return b.tag === "Vision" && a.vision === b.vision
|
||||
case "MagicalTradition": return b.tag === "MagicalTradition" && a.magical_tradition === b.magical_tradition
|
||||
case "BlessedTradition": return b.tag === "BlessedTradition" && a.blessed_tradition === b.blessed_tradition
|
||||
case "PactGift": return b.tag === "PactGift" && a.pact_gift === b.pact_gift
|
||||
case "VampiricGift": return b.tag === "VampiricGift" && a.vampiric_gift === b.vampiric_gift
|
||||
case "SikaryanDrainSpecialAbility": return b.tag === "SikaryanDrainSpecialAbility" && a.sikaryan_drain_special_ability === b.sikaryan_drain_special_ability
|
||||
case "LycantropicGift": return b.tag === "LycantropicGift" && a.lycantropic_gift === b.lycantropic_gift
|
||||
case "SkillStyleSpecialAbility": return b.tag === "SkillStyleSpecialAbility" && a.skill_style_special_ability === b.skill_style_special_ability
|
||||
case "AdvancedSkillSpecialAbility": return b.tag === "AdvancedSkillSpecialAbility" && a.advanced_skill_special_ability === b.advanced_skill_special_ability
|
||||
case "ArcaneOrbEnchantment": return b.tag === "ArcaneOrbEnchantment" && a.arcane_orb_enchantment === b.arcane_orb_enchantment
|
||||
case "CauldronEnchantment": return b.tag === "CauldronEnchantment" && a.cauldron_enchantment === b.cauldron_enchantment
|
||||
case "FoolsHatEnchantment": return b.tag === "FoolsHatEnchantment" && a.fools_hat_enchantment === b.fools_hat_enchantment
|
||||
case "ToyEnchantment": return b.tag === "ToyEnchantment" && a.toy_enchantment === b.toy_enchantment
|
||||
case "BowlEnchantment": return b.tag === "BowlEnchantment" && a.bowl_enchantment === b.bowl_enchantment
|
||||
case "FatePointSexSpecialAbility": return b.tag === "FatePointSexSpecialAbility" && a.fate_point_sex_special_ability === b.fate_point_sex_special_ability
|
||||
case "SexSpecialAbility": return b.tag === "SexSpecialAbility" && a.sex_special_ability === b.sex_special_ability
|
||||
case "WeaponEnchantment": return b.tag === "WeaponEnchantment" && a.weapon_enchantment === b.weapon_enchantment
|
||||
case "SickleRitual": return b.tag === "SickleRitual" && a.sickle_ritual === b.sickle_ritual
|
||||
case "RingEnchantment": return b.tag === "RingEnchantment" && a.ring_enchantment === b.ring_enchantment
|
||||
case "ChronicleEnchantment": return b.tag === "ChronicleEnchantment" && a.chronicle_enchantment === b.chronicle_enchantment
|
||||
case "Krallenkettenzauber": return b.tag === "Krallenkettenzauber" && a.krallenkettenzauber === b.krallenkettenzauber
|
||||
case "Trinkhornzauber": return b.tag === "Trinkhornzauber" && a.trinkhornzauber === b.trinkhornzauber
|
||||
case "MagicalRune": return b.tag === "MagicalRune" && a.magical_rune === b.magical_rune
|
||||
case "MagicalSign": return b.tag === "MagicalSign" && a.magical_sign === b.magical_sign
|
||||
case "Language": return b.tag === "Language" && a.language === b.language
|
||||
case "Script": return b.tag === "Script" && a.script === b.script
|
||||
case "Continent": return b.tag === "Continent" && a.continent === b.continent
|
||||
case "SocialStatus": return b.tag === "SocialStatus" && a.social_status === b.social_status
|
||||
case "Attribute": return b.tag === "Attribute" && a.attribute === b.attribute
|
||||
case "Skill": return b.tag === "Skill" && a.skill === b.skill
|
||||
case "SkillGroup": return b.tag === "SkillGroup" && a.skill_group === b.skill_group
|
||||
case "CloseCombatTechnique": return b.tag === "CloseCombatTechnique" && a.close_combat_technique === b.close_combat_technique
|
||||
case "RangedCombatTechnique": return b.tag === "RangedCombatTechnique" && a.ranged_combat_technique === b.ranged_combat_technique
|
||||
case "Spell": return b.tag === "Spell" && a.spell === b.spell
|
||||
case "Ritual": return b.tag === "Ritual" && a.ritual === b.ritual
|
||||
case "Cantrip": return b.tag === "Cantrip" && a.cantrip === b.cantrip
|
||||
case "Property": return b.tag === "Property" && a.property === b.property
|
||||
case "LiturgicalChant": return b.tag === "LiturgicalChant" && a.liturgical_chant === b.liturgical_chant
|
||||
case "Ceremony": return b.tag === "Ceremony" && a.ceremony === b.ceremony
|
||||
case "Blessing": return b.tag === "Blessing" && a.blessing === b.blessing
|
||||
case "Aspect": return b.tag === "Aspect" && a.aspect === b.aspect
|
||||
case "Curse": return b.tag === "Curse" && a.curse === b.curse
|
||||
case "ElvenMagicalSong": return b.tag === "ElvenMagicalSong" && a.elven_magical_song === b.elven_magical_song
|
||||
case "DominationRitual": return b.tag === "DominationRitual" && a.domination_ritual === b.domination_ritual
|
||||
case "MagicalMelody": return b.tag === "MagicalMelody" && a.magical_melody === b.magical_melody
|
||||
case "MagicalDance": return b.tag === "MagicalDance" && a.magical_dance === b.magical_dance
|
||||
case "JesterTrick": return b.tag === "JesterTrick" && a.jester_trick === b.jester_trick
|
||||
case "AnimistPower": return b.tag === "AnimistPower" && a.animist_power === b.animist_power
|
||||
case "GeodeRitual": return b.tag === "GeodeRitual" && a.geode_ritual === b.geode_ritual
|
||||
case "ZibiljaRitual": return b.tag === "ZibiljaRitual" && a.zibilja_ritual === b.zibilja_ritual
|
||||
case "AnimalType": return b.tag === "AnimalType" && a.animal_type === b.animal_type
|
||||
case "TargetCategory": return b.tag === "TargetCategory" && a.target_category === b.target_category
|
||||
case "General": return b.tag === "General" && a.general === b.general
|
||||
case "Element": return b.tag === "Element" && a.element === b.element
|
||||
case "AnimalShapeSize": return b.tag === "AnimalShapeSize" && a.animal_shape_size === b.animal_shape_size
|
||||
case "Patron": return b.tag === "Patron" && a.patron === b.patron
|
||||
case "Ammunition": return b.tag === "Ammunition" && a.ammunition === b.ammunition
|
||||
case "Animal": return b.tag === "Animal" && a.animal === b.animal
|
||||
case "AnimalCare": return b.tag === "AnimalCare" && a.animal_care === b.animal_care
|
||||
case "Armor": return b.tag === "Armor" && a.armor === b.armor
|
||||
case "BandageOrRemedy": return b.tag === "BandageOrRemedy" && a.bandage_or_remedy === b.bandage_or_remedy
|
||||
case "Book": return b.tag === "Book" && a.book === b.book
|
||||
case "CeremonialItem": return b.tag === "CeremonialItem" && a.ceremonial_item === b.ceremonial_item
|
||||
case "Clothes": return b.tag === "Clothes" && a.clothes === b.clothes
|
||||
case "Container": return b.tag === "Container" && a.container === b.container
|
||||
case "Elixir": return b.tag === "Elixir" && a.elixir === b.elixir
|
||||
case "EquipmentOfBlessedOnes": return b.tag === "EquipmentOfBlessedOnes" && a.equipment_of_blessed_ones === b.equipment_of_blessed_ones
|
||||
case "GemOrPreciousStone": return b.tag === "GemOrPreciousStone" && a.gem_or_precious_stone === b.gem_or_precious_stone
|
||||
case "IlluminationLightSource": return b.tag === "IlluminationLightSource" && a.illumination_light_source === b.illumination_light_source
|
||||
case "IlluminationRefillsOrSupplies": return b.tag === "IlluminationRefillsOrSupplies" && a.illumination_refills_or_supplies === b.illumination_refills_or_supplies
|
||||
case "Jewelry": return b.tag === "Jewelry" && a.jewelry === b.jewelry
|
||||
case "Liebesspielzeug": return b.tag === "Liebesspielzeug" && a.liebesspielzeug === b.liebesspielzeug
|
||||
case "LuxuryGood": return b.tag === "LuxuryGood" && a.luxury_good === b.luxury_good
|
||||
case "MagicalArtifact": return b.tag === "MagicalArtifact" && a.magical_artifact === b.magical_artifact
|
||||
case "MusicalInstrument": return b.tag === "MusicalInstrument" && a.musical_instrument === b.musical_instrument
|
||||
case "OrienteeringAid": return b.tag === "OrienteeringAid" && a.orienteering_aid === b.orienteering_aid
|
||||
case "Poison": return b.tag === "Poison" && a.poison === b.poison
|
||||
case "RopeOrChain": return b.tag === "RopeOrChain" && a.rope_or_chain === b.rope_or_chain
|
||||
case "Stationary": return b.tag === "Stationary" && a.stationary === b.stationary
|
||||
case "ThievesTool": return b.tag === "ThievesTool" && a.thieves_tool === b.thieves_tool
|
||||
case "ToolOfTheTrade": return b.tag === "ToolOfTheTrade" && a.tool_of_the_trade === b.tool_of_the_trade
|
||||
case "TravelGearOrTool": return b.tag === "TravelGearOrTool" && a.travel_gear_or_tool === b.travel_gear_or_tool
|
||||
case "Vehicle": return b.tag === "Vehicle" && a.vehicle === b.vehicle
|
||||
case "Weapon": return b.tag === "Weapon" && a.weapon === b.weapon
|
||||
case "WeaponAccessory": return b.tag === "WeaponAccessory" && a.weapon_accessory === b.weapon_accessory
|
||||
case "Reach": return b.tag === "Reach" && a.reach === b.reach
|
||||
case "PatronCategory": return b.tag === "PatronCategory" && a.patron_category === b.patron_category
|
||||
case "PersonalityTrait": return b.tag === "PersonalityTrait" && a.personality_trait === b.personality_trait
|
||||
case "HairColor": return b.tag === "HairColor" && a.hair_color === b.hair_color
|
||||
case "EyeColor": return b.tag === "EyeColor" && a.eye_color === b.eye_color
|
||||
case "PactCategory": return b.tag === "PactCategory" && a.pact_category === b.pact_category
|
||||
case "PactDomain": return b.tag === "PactDomain" && a.pact_domain === b.pact_domain
|
||||
case "AnimistTribe": return b.tag === "AnimistTribe" && a.animist_tribe === b.animist_tribe
|
||||
case "Influence": return b.tag === "Influence" && a.influence === b.influence
|
||||
case "Condition": return b.tag === "Condition" && a.condition === b.condition
|
||||
case "State": return b.tag === "State" && a.state === b.state
|
||||
case "Disease": return b.tag === "Disease" && a.disease === b.disease
|
||||
case "SexPractice": return b.tag === "SexPractice" && a.sex_practice === b.sex_practice
|
||||
case "TradeSecret": return b.tag === "TradeSecret" && a.trade_secret === b.trade_secret
|
||||
case "AnimalShape": return b.tag === "AnimalShape" && a.animal_shape === b.animal_shape
|
||||
case "ArcaneBardTradition": return b.tag === "ArcaneBardTradition" && a.arcane_bard_tradition === b.arcane_bard_tradition
|
||||
case "ArcaneDancerTradition": return b.tag === "ArcaneDancerTradition" && a.arcane_dancer_tradition === b.arcane_dancer_tradition
|
||||
default: return assertExhaustive(a)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an identifier object to a string.
|
||||
*/
|
||||
export const identifierObjectToString = (
|
||||
obj: TagPropertyOptions[keyof TagPropertyOptions],
|
||||
): string => {
|
||||
// prettier-ignore
|
||||
switch (obj.tag) {
|
||||
case "Publication": return `${obj.tag}_${obj.publication}`
|
||||
case "ExperienceLevel": return `${obj.tag}_${obj.experience_level}`
|
||||
case "CoreRule": return `${obj.tag}_${obj.core_rule}`
|
||||
case "FocusRule": return `${obj.tag}_${obj.focus_rule}`
|
||||
case "Subject": return `${obj.tag}_${obj.subject}`
|
||||
case "OptionalRule": return `${obj.tag}_${obj.optional_rule}`
|
||||
case "Race": return `${obj.tag}_${obj.race}`
|
||||
case "Culture": return `${obj.tag}_${obj.culture}`
|
||||
case "Profession": return `${obj.tag}_${obj.profession}`
|
||||
case "ProfessionVariant": return `${obj.tag}_${obj.profession_variant}`
|
||||
case "Curriculum": return `${obj.tag}_${obj.curriculum}`
|
||||
case "Guideline": return `${obj.tag}_${obj.guideline}`
|
||||
case "Advantage": return `${obj.tag}_${obj.advantage}`
|
||||
case "Disadvantage": return `${obj.tag}_${obj.disadvantage}`
|
||||
case "GeneralSpecialAbility": return `${obj.tag}_${obj.general_special_ability}`
|
||||
case "FatePointSpecialAbility": return `${obj.tag}_${obj.fate_point_special_ability}`
|
||||
case "CombatSpecialAbility": return `${obj.tag}_${obj.combat_special_ability}`
|
||||
case "MagicalSpecialAbility": return `${obj.tag}_${obj.magical_special_ability}`
|
||||
case "StaffEnchantment": return `${obj.tag}_${obj.staff_enchantment}`
|
||||
case "FamiliarSpecialAbility": return `${obj.tag}_${obj.familiar_special_ability}`
|
||||
case "KarmaSpecialAbility": return `${obj.tag}_${obj.karma_special_ability}`
|
||||
case "ProtectiveWardingCircleSpecialAbility": return `${obj.tag}_${obj.protective_warding_circle_special_ability}`
|
||||
case "CombatStyleSpecialAbility": return `${obj.tag}_${obj.combat_style_special_ability}`
|
||||
case "AdvancedCombatSpecialAbility": return `${obj.tag}_${obj.advanced_combat_special_ability}`
|
||||
case "CommandSpecialAbility": return `${obj.tag}_${obj.command_special_ability}`
|
||||
case "MagicStyleSpecialAbility": return `${obj.tag}_${obj.magic_style_special_ability}`
|
||||
case "AdvancedMagicalSpecialAbility": return `${obj.tag}_${obj.advanced_magical_special_ability}`
|
||||
case "SpellSwordEnchantment": return `${obj.tag}_${obj.spell_sword_enchantment}`
|
||||
case "DaggerRitual": return `${obj.tag}_${obj.dagger_ritual}`
|
||||
case "InstrumentEnchantment": return `${obj.tag}_${obj.instrument_enchantment}`
|
||||
case "AttireEnchantment": return `${obj.tag}_${obj.attire_enchantment}`
|
||||
case "OrbEnchantment": return `${obj.tag}_${obj.orb_enchantment}`
|
||||
case "WandEnchantment": return `${obj.tag}_${obj.wand_enchantment}`
|
||||
case "BrawlingSpecialAbility": return `${obj.tag}_${obj.brawling_special_ability}`
|
||||
case "AncestorGlyph": return `${obj.tag}_${obj.ancestor_glyph}`
|
||||
case "CeremonialItemSpecialAbility": return `${obj.tag}_${obj.ceremonial_item_special_ability}`
|
||||
case "Sermon": return `${obj.tag}_${obj.sermon}`
|
||||
case "LiturgicalStyleSpecialAbility": return `${obj.tag}_${obj.liturgical_style_special_ability}`
|
||||
case "AdvancedKarmaSpecialAbility": return `${obj.tag}_${obj.advanced_karma_special_ability}`
|
||||
case "Vision": return `${obj.tag}_${obj.vision}`
|
||||
case "MagicalTradition": return `${obj.tag}_${obj.magical_tradition}`
|
||||
case "BlessedTradition": return `${obj.tag}_${obj.blessed_tradition}`
|
||||
case "PactGift": return `${obj.tag}_${obj.pact_gift}`
|
||||
case "VampiricGift": return `${obj.tag}_${obj.vampiric_gift}`
|
||||
case "SikaryanDrainSpecialAbility": return `${obj.tag}_${obj.sikaryan_drain_special_ability}`
|
||||
case "LycantropicGift": return `${obj.tag}_${obj.lycantropic_gift}`
|
||||
case "SkillStyleSpecialAbility": return `${obj.tag}_${obj.skill_style_special_ability}`
|
||||
case "AdvancedSkillSpecialAbility": return `${obj.tag}_${obj.advanced_skill_special_ability}`
|
||||
case "ArcaneOrbEnchantment": return `${obj.tag}_${obj.arcane_orb_enchantment}`
|
||||
case "CauldronEnchantment": return `${obj.tag}_${obj.cauldron_enchantment}`
|
||||
case "FoolsHatEnchantment": return `${obj.tag}_${obj.fools_hat_enchantment}`
|
||||
case "ToyEnchantment": return `${obj.tag}_${obj.toy_enchantment}`
|
||||
case "BowlEnchantment": return `${obj.tag}_${obj.bowl_enchantment}`
|
||||
case "FatePointSexSpecialAbility": return `${obj.tag}_${obj.fate_point_sex_special_ability}`
|
||||
case "SexSpecialAbility": return `${obj.tag}_${obj.sex_special_ability}`
|
||||
case "WeaponEnchantment": return `${obj.tag}_${obj.weapon_enchantment}`
|
||||
case "SickleRitual": return `${obj.tag}_${obj.sickle_ritual}`
|
||||
case "RingEnchantment": return `${obj.tag}_${obj.ring_enchantment}`
|
||||
case "ChronicleEnchantment": return `${obj.tag}_${obj.chronicle_enchantment}`
|
||||
case "Krallenkettenzauber": return `${obj.tag}_${obj.krallenkettenzauber}`
|
||||
case "Trinkhornzauber": return `${obj.tag}_${obj.trinkhornzauber}`
|
||||
case "MagicalRune": return `${obj.tag}_${obj.magical_rune}`
|
||||
case "MagicalSign": return `${obj.tag}_${obj.magical_sign}`
|
||||
case "Language": return `${obj.tag}_${obj.language}`
|
||||
case "Script": return `${obj.tag}_${obj.script}`
|
||||
case "Continent": return `${obj.tag}_${obj.continent}`
|
||||
case "SocialStatus": return `${obj.tag}_${obj.social_status}`
|
||||
case "Attribute": return `${obj.tag}_${obj.attribute}`
|
||||
case "Skill": return `${obj.tag}_${obj.skill}`
|
||||
case "SkillGroup": return `${obj.tag}_${obj.skill_group}`
|
||||
case "CloseCombatTechnique": return `${obj.tag}_${obj.close_combat_technique}`
|
||||
case "RangedCombatTechnique": return `${obj.tag}_${obj.ranged_combat_technique}`
|
||||
case "Spell": return `${obj.tag}_${obj.spell}`
|
||||
case "Ritual": return `${obj.tag}_${obj.ritual}`
|
||||
case "Cantrip": return `${obj.tag}_${obj.cantrip}`
|
||||
case "Property": return `${obj.tag}_${obj.property}`
|
||||
case "LiturgicalChant": return `${obj.tag}_${obj.liturgical_chant}`
|
||||
case "Ceremony": return `${obj.tag}_${obj.ceremony}`
|
||||
case "Blessing": return `${obj.tag}_${obj.blessing}`
|
||||
case "Aspect": return `${obj.tag}_${obj.aspect}`
|
||||
case "Curse": return `${obj.tag}_${obj.curse}`
|
||||
case "ElvenMagicalSong": return `${obj.tag}_${obj.elven_magical_song}`
|
||||
case "DominationRitual": return `${obj.tag}_${obj.domination_ritual}`
|
||||
case "MagicalMelody": return `${obj.tag}_${obj.magical_melody}`
|
||||
case "MagicalDance": return `${obj.tag}_${obj.magical_dance}`
|
||||
case "JesterTrick": return `${obj.tag}_${obj.jester_trick}`
|
||||
case "AnimistPower": return `${obj.tag}_${obj.animist_power}`
|
||||
case "GeodeRitual": return `${obj.tag}_${obj.geode_ritual}`
|
||||
case "ZibiljaRitual": return `${obj.tag}_${obj.zibilja_ritual}`
|
||||
case "AnimalType": return `${obj.tag}_${obj.animal_type}`
|
||||
case "TargetCategory": return `${obj.tag}_${obj.target_category}`
|
||||
case "General": return `${obj.tag}_${obj.general}`
|
||||
case "Element": return `${obj.tag}_${obj.element}`
|
||||
case "AnimalShapeSize": return `${obj.tag}_${obj.animal_shape_size}`
|
||||
case "Patron": return `${obj.tag}_${obj.patron}`
|
||||
case "Ammunition": return `${obj.tag}_${obj.ammunition}`
|
||||
case "Animal": return `${obj.tag}_${obj.animal}`
|
||||
case "AnimalCare": return `${obj.tag}_${obj.animal_care}`
|
||||
case "Armor": return `${obj.tag}_${obj.armor}`
|
||||
case "BandageOrRemedy": return `${obj.tag}_${obj.bandage_or_remedy}`
|
||||
case "Book": return `${obj.tag}_${obj.book}`
|
||||
case "CeremonialItem": return `${obj.tag}_${obj.ceremonial_item}`
|
||||
case "Clothes": return `${obj.tag}_${obj.clothes}`
|
||||
case "Container": return `${obj.tag}_${obj.container}`
|
||||
case "Elixir": return `${obj.tag}_${obj.elixir}`
|
||||
case "EquipmentOfBlessedOnes": return `${obj.tag}_${obj.equipment_of_blessed_ones}`
|
||||
case "GemOrPreciousStone": return `${obj.tag}_${obj.gem_or_precious_stone}`
|
||||
case "IlluminationLightSource": return `${obj.tag}_${obj.illumination_light_source}`
|
||||
case "IlluminationRefillsOrSupplies": return `${obj.tag}_${obj.illumination_refills_or_supplies}`
|
||||
case "Jewelry": return `${obj.tag}_${obj.jewelry}`
|
||||
case "Liebesspielzeug": return `${obj.tag}_${obj.liebesspielzeug}`
|
||||
case "LuxuryGood": return `${obj.tag}_${obj.luxury_good}`
|
||||
case "MagicalArtifact": return `${obj.tag}_${obj.magical_artifact}`
|
||||
case "MusicalInstrument": return `${obj.tag}_${obj.musical_instrument}`
|
||||
case "OrienteeringAid": return `${obj.tag}_${obj.orienteering_aid}`
|
||||
case "Poison": return `${obj.tag}_${obj.poison}`
|
||||
case "RopeOrChain": return `${obj.tag}_${obj.rope_or_chain}`
|
||||
case "Stationary": return `${obj.tag}_${obj.stationary}`
|
||||
case "ThievesTool": return `${obj.tag}_${obj.thieves_tool}`
|
||||
case "ToolOfTheTrade": return `${obj.tag}_${obj.tool_of_the_trade}`
|
||||
case "TravelGearOrTool": return `${obj.tag}_${obj.travel_gear_or_tool}`
|
||||
case "Vehicle": return `${obj.tag}_${obj.vehicle}`
|
||||
case "Weapon": return `${obj.tag}_${obj.weapon}`
|
||||
case "WeaponAccessory": return `${obj.tag}_${obj.weapon_accessory}`
|
||||
case "Reach": return `${obj.tag}_${obj.reach}`
|
||||
case "PatronCategory": return `${obj.tag}_${obj.patron_category}`
|
||||
case "PersonalityTrait": return `${obj.tag}_${obj.personality_trait}`
|
||||
case "HairColor": return `${obj.tag}_${obj.hair_color}`
|
||||
case "EyeColor": return `${obj.tag}_${obj.eye_color}`
|
||||
case "PactCategory": return `${obj.tag}_${obj.pact_category}`
|
||||
case "PactDomain": return `${obj.tag}_${obj.pact_domain}`
|
||||
case "AnimistTribe": return `${obj.tag}_${obj.animist_tribe}`
|
||||
case "Influence": return `${obj.tag}_${obj.influence}`
|
||||
case "Condition": return `${obj.tag}_${obj.condition}`
|
||||
case "State": return `${obj.tag}_${obj.state}`
|
||||
case "Disease": return `${obj.tag}_${obj.disease}`
|
||||
case "SexPractice": return `${obj.tag}_${obj.sex_practice}`
|
||||
case "TradeSecret": return `${obj.tag}_${obj.trade_secret}`
|
||||
case "AnimalShape": return `${obj.tag}_${obj.animal_shape}`
|
||||
case "ArcaneBardTradition": return `${obj.tag}_${obj.arcane_bard_tradition}`
|
||||
case "ArcaneDancerTradition": return `${obj.tag}_${obj.arcane_dancer_tradition}`
|
||||
default: return assertExhaustive(obj)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { ActivatableIdentifier } from "optolith-database-schema/types/_IdentifierGroup"
|
||||
import {
|
||||
PactCategoryReference,
|
||||
PactDomainReference,
|
||||
} from "optolith-database-schema/types/_SimpleReferences"
|
||||
|
||||
/**
|
||||
* A active pact between a character and a creature.
|
||||
*/
|
||||
@@ -44,3 +50,27 @@ export type PactDomain =
|
||||
kind: "Custom"
|
||||
name: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A dependency on a pact.
|
||||
*/
|
||||
export type PactDependency = {
|
||||
sourceId: ActivatableIdentifier
|
||||
index: number
|
||||
isPartOfDisjunction: boolean
|
||||
|
||||
/**
|
||||
* The required pact category.
|
||||
*/
|
||||
category: PactCategoryReference
|
||||
|
||||
/**
|
||||
* The required domain(s).
|
||||
*/
|
||||
domain?: PactDomainReference[]
|
||||
|
||||
/**
|
||||
* The required pact level.
|
||||
*/
|
||||
level?: number
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Race, RaceVariant } from "optolith-database-schema/types/Race"
|
||||
import { ActivatableIdentifier } from "optolith-database-schema/types/_IdentifierGroup"
|
||||
import { mapNullableDefault } from "../utils/nullable.ts"
|
||||
import { TranslateMap } from "../utils/translate.ts"
|
||||
|
||||
@@ -26,3 +27,34 @@ export const getFullRaceName = (
|
||||
str => ` (${str})`,
|
||||
"",
|
||||
)}`
|
||||
|
||||
/**
|
||||
* A dependency on a race.
|
||||
*/
|
||||
export type RaceDependency = Readonly<{
|
||||
/**
|
||||
* The identifier of the dependency source.
|
||||
*/
|
||||
sourceId: ActivatableIdentifier
|
||||
|
||||
/**
|
||||
* The top-level index of the prerequisite. If the prerequisite is part of a
|
||||
* group or disjunction, this is the index of the group or disjunction.
|
||||
*/
|
||||
index: number
|
||||
|
||||
/**
|
||||
* Is the source prerequisite part of a prerequisite disjunction?
|
||||
*/
|
||||
isPartOfDisjunction: boolean
|
||||
|
||||
/**
|
||||
* The required race's identifier.
|
||||
*/
|
||||
id: number
|
||||
|
||||
/**
|
||||
* Whether the required race is required to be active or inactive.
|
||||
*/
|
||||
active: boolean
|
||||
}>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { AnimistPowerImprovementCost } from "optolith-database-schema/types/magicalActions/AnimistPower"
|
||||
import { mapNullable } from "../../utils/nullable.ts"
|
||||
import { assertExhaustive } from "../../utils/typeSafety.ts"
|
||||
import { Activatable, getFirstOptionOfType } from "../activatable/activatableEntry.ts"
|
||||
import { ImprovementCost, fromRaw } from "../adventurePoints/improvementCost.ts"
|
||||
import { GetById } from "../getTypes.ts"
|
||||
|
||||
/**
|
||||
* Returns the improvement cost for an animist power.
|
||||
*/
|
||||
export const getImprovementCostForAnimistPower = (
|
||||
getStaticPatronById: GetById.Static.Patron,
|
||||
dynamicTradition: Activatable,
|
||||
improvementCost: AnimistPowerImprovementCost,
|
||||
): ImprovementCost | undefined => {
|
||||
switch (improvementCost.tag) {
|
||||
case "Fixed":
|
||||
return fromRaw(improvementCost.fixed)
|
||||
case "ByPrimaryPatron": {
|
||||
const primaryPatronId = getFirstOptionOfType(dynamicTradition, "Patron")
|
||||
const primaryPatron = mapNullable(primaryPatronId, getStaticPatronById)
|
||||
return mapNullable(primaryPatron?.improvement_cost, fromRaw)
|
||||
}
|
||||
default:
|
||||
return assertExhaustive(improvementCost)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AttributeReference } from "optolith-database-schema/types/_SimpleReferences"
|
||||
import { ImprovementCost } from "../adventurePoints/improvementCost.ts"
|
||||
import { Rated } from "./ratedEntry.ts"
|
||||
|
||||
/**
|
||||
@@ -6,6 +7,11 @@ import { Rated } from "./ratedEntry.ts"
|
||||
*/
|
||||
export const minimumAttributeValue = 8
|
||||
|
||||
/**
|
||||
* The improvement cost of an attribute.
|
||||
*/
|
||||
export const attributeImprovementCost = ImprovementCost.E
|
||||
|
||||
/**
|
||||
* Creates an initial dynamic attribute entry.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,10 @@ import { AttributeIdentifier } from "../identifier.ts"
|
||||
import { getAttributeValue } from "./attribute.ts"
|
||||
import { getHighestRequiredAttributeForCombatTechnique } from "./combatTechnique.ts"
|
||||
import { getHighestRequiredAttributeForLiturgicalChant } from "./liturgicalChant.ts"
|
||||
import {
|
||||
PrimaryAttributeDependency,
|
||||
primaryAttributeDependencyToRatedDependency,
|
||||
} from "./primaryAttribute.ts"
|
||||
import { RatedDependency, flattenMinimumRestrictions } from "./ratedDependency.ts"
|
||||
import { Rated } from "./ratedEntry.ts"
|
||||
import { getHighestRequiredAttributeForSkill } from "./skill.ts"
|
||||
@@ -23,9 +27,9 @@ export const getAttributeMinimum = (
|
||||
purchasedArcaneEnergy: number,
|
||||
purchasedKarmaPoints: number,
|
||||
singleHighestMagicalPrimaryAttributeId: number | undefined,
|
||||
magicalPrimaryAttributeDependencies: RatedDependency[],
|
||||
magicalPrimaryAttributeDependencies: PrimaryAttributeDependency[],
|
||||
blessedPrimaryAttributeId: number | undefined,
|
||||
blessedPrimaryAttributeDependencies: RatedDependency[],
|
||||
blessedPrimaryAttributeDependencies: PrimaryAttributeDependency[],
|
||||
filterApplyingDependencies: (dependencies: RatedDependency[]) => RatedDependency[],
|
||||
getSkillCheckAttributeMinimum: (id: number) => number | undefined,
|
||||
dynamicAttribute: Rated,
|
||||
@@ -43,7 +47,9 @@ export const getAttributeMinimum = (
|
||||
? [
|
||||
purchasedArcaneEnergy,
|
||||
...flattenMinimumRestrictions(
|
||||
filterApplyingDependencies(magicalPrimaryAttributeDependencies),
|
||||
filterApplyingDependencies(
|
||||
magicalPrimaryAttributeDependencies.map(primaryAttributeDependencyToRatedDependency),
|
||||
),
|
||||
),
|
||||
]
|
||||
: []),
|
||||
@@ -51,7 +57,9 @@ export const getAttributeMinimum = (
|
||||
? [
|
||||
purchasedKarmaPoints,
|
||||
...flattenMinimumRestrictions(
|
||||
filterApplyingDependencies(blessedPrimaryAttributeDependencies),
|
||||
filterApplyingDependencies(
|
||||
blessedPrimaryAttributeDependencies.map(primaryAttributeDependencyToRatedDependency),
|
||||
),
|
||||
),
|
||||
]
|
||||
: []),
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { CloseCombatTechnique } from "optolith-database-schema/types/CombatTechnique_Close"
|
||||
import { RangedCombatTechnique } from "optolith-database-schema/types/CombatTechnique_Ranged"
|
||||
import { CombatTechniqueIdentifier } from "optolith-database-schema/types/_IdentifierGroup"
|
||||
import { AttributeReference } from "optolith-database-schema/types/_SimpleReferences"
|
||||
import {
|
||||
Activatable,
|
||||
PredefinedActivatableOption,
|
||||
countOptions,
|
||||
} from "../activatable/activatableEntry.ts"
|
||||
import { Activatable, countOptions } from "../activatable/activatableEntry.ts"
|
||||
import { AttributeIdentifier } from "../identifier.ts"
|
||||
import { getAttributeValue } from "./attribute.ts"
|
||||
import { Rated } from "./ratedEntry.ts"
|
||||
@@ -48,10 +45,16 @@ export type CombinedCombatTechnique =
|
||||
*/
|
||||
export const getCombinedId = (
|
||||
combatTechnique: CombinedCombatTechnique,
|
||||
): PredefinedActivatableOption["id"] =>
|
||||
): CombatTechniqueIdentifier =>
|
||||
combatTechnique.tag === "CloseCombatTechnique"
|
||||
? { type: "CloseCombatTechnique", value: combatTechnique.closeCombatTechnique.id }
|
||||
: { type: "RangedCombatTechnique", value: combatTechnique.rangedCombatTechnique.id }
|
||||
? {
|
||||
tag: "CloseCombatTechnique",
|
||||
close_combat_technique: combatTechnique.closeCombatTechnique.id,
|
||||
}
|
||||
: {
|
||||
tag: "RangedCombatTechnique",
|
||||
ranged_combat_technique: combatTechnique.rangedCombatTechnique.id,
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary attribute of a combined combat technique.
|
||||
|
||||
@@ -2,12 +2,13 @@ import { ExperienceLevel } from "optolith-database-schema/types/ExperienceLevel"
|
||||
import { AttributeReference } from "optolith-database-schema/types/_SimpleReferences"
|
||||
import { filterNonNullable } from "../../utils/array.ts"
|
||||
import { Activatable, countOptions, isActive } from "../activatable/activatableEntry.ts"
|
||||
import { FilterApplyingRatedDependencies } from "../dependencies/filterApplyingDependencies.ts"
|
||||
import {
|
||||
CombinedCombatTechnique,
|
||||
getCombinedId,
|
||||
getCombinedPrimaryAttribute,
|
||||
} from "./combatTechnique.ts"
|
||||
import { RatedDependency, flattenMinimumRestrictions } from "./ratedDependency.ts"
|
||||
import { flattenMinimumRestrictions } from "./ratedDependency.ts"
|
||||
import { Rated } from "./ratedEntry.ts"
|
||||
|
||||
const getCombatTechniqueMinimumForHunter = (
|
||||
@@ -31,7 +32,7 @@ export const getCombatTechniqueMinimum = (
|
||||
staticCombatTechnique: CombinedCombatTechnique,
|
||||
dynamicCombatTechnique: Rated,
|
||||
hunter: Activatable | undefined,
|
||||
filterApplyingDependencies: (dependencies: RatedDependency[]) => RatedDependency[],
|
||||
filterApplyingDependencies: FilterApplyingRatedDependencies,
|
||||
): number => {
|
||||
const minimumValues = filterNonNullable([
|
||||
6,
|
||||
|
||||
@@ -10,15 +10,26 @@ export type EnhancementDependency =
|
||||
/**
|
||||
* The depending enhancement.
|
||||
*/
|
||||
id: number
|
||||
sourceId: number
|
||||
}
|
||||
| {
|
||||
tag: "External"
|
||||
|
||||
/**
|
||||
* The depending activatable.
|
||||
* The identifier of the dependency source.
|
||||
*/
|
||||
id: ActivatableIdentifier
|
||||
sourceId: ActivatableIdentifier
|
||||
|
||||
/**
|
||||
* The top-level index of the prerequisite. If the prerequisite is part of a
|
||||
* group or disjunction, this is the index of the group or disjunction.
|
||||
*/
|
||||
index: number
|
||||
|
||||
/**
|
||||
* Is the source prerequisite part of a prerequisite disjunction?
|
||||
*/
|
||||
isPartOfDisjunction: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,7 @@ import { assertExhaustive } from "../../utils/typeSafety.ts"
|
||||
import { Activatable, countOptions } from "../activatable/activatableEntry.ts"
|
||||
import { compareImprovementCost, fromRaw } from "../adventurePoints/improvementCost.ts"
|
||||
import { All } from "../getTypes.ts"
|
||||
import { AspectIdentifier } from "../identifier.ts"
|
||||
import { AspectIdentifier, createIdentifierObject } from "../identifier.ts"
|
||||
import { LiturgiesSortOrder } from "../sortOrders.ts"
|
||||
import { DisplayedActiveLiturgy } from "./liturgicalChantActive.ts"
|
||||
import { DisplayedInactiveLiturgy } from "./liturgicalChantInactive.ts"
|
||||
@@ -83,6 +83,21 @@ export const flattenAspectIds = (traditions: SkillTradition[]): number[] =>
|
||||
})
|
||||
.map(aspect => aspect.id.aspect)
|
||||
|
||||
/**
|
||||
* Checks if the passed liturgical chant features the passed aspect.
|
||||
*/
|
||||
export const hasAspectById = (id: number, traditions: SkillTradition[]): boolean =>
|
||||
traditions.some(tradition => {
|
||||
switch (tradition.tag) {
|
||||
case "GeneralAspect":
|
||||
return tradition.general_aspect.id.aspect === id
|
||||
case "Tradition":
|
||||
return tradition.tradition.aspects?.some(aspect => aspect.id.aspect === id) ?? false
|
||||
default:
|
||||
return assertExhaustive(tradition)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Returns the translation of all aspects relevant to the current active blessed
|
||||
* tradition. If the tradition has no aspects, the name of the tradition is used
|
||||
@@ -221,10 +236,10 @@ export const getHighestRequiredAttributeForLiturgicalChant = (
|
||||
return undefined
|
||||
}
|
||||
|
||||
const exceptionalSkillBonus = countOptions(exceptionalSkill, {
|
||||
type,
|
||||
value: staticLiturgicalChant.id,
|
||||
})
|
||||
const exceptionalSkillBonus = countOptions(
|
||||
exceptionalSkill,
|
||||
createIdentifierObject(type, staticLiturgicalChant.id),
|
||||
)
|
||||
|
||||
return {
|
||||
id: singleHighestAttributeId,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user