Improved typing of creature properties

This commit is contained in:
ThaumRystra
2025-01-12 23:28:43 +02:00
parent 0125367085
commit 1b05b8d3bf
9 changed files with 147 additions and 25 deletions

View File

@@ -7,6 +7,7 @@ import propertySchemasIndex from '/imports/api/properties/computedPropertySchema
import { storedIconsSchema } from '/imports/api/icons/Icons'; import { storedIconsSchema } from '/imports/api/icons/Icons';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
import { InferType, TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema'; import { InferType, TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
import type { ComputedProperty, ComputedPropertyTypeMap } from '/imports/api/properties/Property.type';
const PreComputeCreaturePropertySchema = new TypedSimpleSchema({ const PreComputeCreaturePropertySchema = new TypedSimpleSchema({
_id: { _id: {
@@ -153,15 +154,15 @@ for (key in propertySchemasIndex) {
} }
} }
export type CreaturePropertyByType<T extends keyof typeof propertySchemasIndex> = export type CreaturePropertyByType<T extends keyof ComputedPropertyTypeMap> =
InferType<typeof propertySchemasIndex[T]> ComputedProperty<T>
& InferType<typeof CreaturePropertySchema> & InferType<typeof CreaturePropertySchema>
& InferType<typeof ColorSchema> & InferType<typeof ColorSchema>
& InferType<typeof ChildSchema> & InferType<typeof ChildSchema>
& InferType<typeof SoftRemovableSchema> & InferType<typeof SoftRemovableSchema>
type ConvertToUnion<T> = T[keyof T]; type ConvertToUnion<T> = T[keyof T];
export type CreatureProperty = ConvertToUnion<{ [key in keyof typeof propertySchemasIndex]: CreaturePropertyByType<key> }>; export type CreatureProperty = ConvertToUnion<{ [key in keyof ComputedPropertyTypeMap]: CreaturePropertyByType<key> }>;
export default CreatureProperties; export default CreatureProperties;
export { export {

View File

@@ -1,5 +1,5 @@
import { PropTask } from '/imports/api/engine/action/tasks/Task'; import { PropTask } from '/imports/api/engine/action/tasks/Task';
import TaskResult from 'imports/api/engine/action/tasks/TaskResult'; import TaskResult from '/imports/api/engine/action/tasks/TaskResult';
import getPropertyTitle from '/imports/api/utility/getPropertyTitle'; import getPropertyTitle from '/imports/api/utility/getPropertyTitle';
import { findLast, filter, difference, intersection } from 'lodash'; import { findLast, filter, difference, intersection } from 'lodash';
import { getPropertiesOfType, getPropertyAncestors } from '/imports/api/engine/loadCreatures'; import { getPropertiesOfType, getPropertyAncestors } from '/imports/api/engine/loadCreatures';
@@ -53,9 +53,10 @@ export default async function applyBuffRemoverProperty(
} else { } else {
// Get all the buffs targeted by tags // Get all the buffs targeted by tags
const allBuffs = getPropertiesOfType(targetId, 'buff'); const allBuffs = getPropertiesOfType(targetId, 'buff');
const targetedBuffs = filter(allBuffs, buff => { const targetedBuffs = filter(allBuffs, (buff): boolean => {
if (buff.inactive) return false; if (buff.inactive) return false;
if (buffRemoverMatchTags(prop, buff)) return true; if (buffRemoverMatchTags(prop, buff)) return true;
return false;
}); });
// Remove the buffs // Remove the buffs
if (prop.removeAll) { if (prop.removeAll) {
@@ -65,7 +66,7 @@ export default async function applyBuffRemoverProperty(
}); });
} else { } else {
// Sort in reverse order // Sort in reverse order
targetedBuffs.sort((a, b) => b.order - a.order); targetedBuffs.sort((a, b) => b.left - a.left);
// Remove the one with the highest order // Remove the one with the highest order
const buff = targetedBuffs[0]; const buff = targetedBuffs[0];
if (buff) { if (buff) {

View File

@@ -1,9 +1,10 @@
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import Creatures from '/imports/api/creature/creatures/Creatures'; import Creatures from '/imports/api/creature/creatures/Creatures';
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables'; import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables';
import CreatureProperties, { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties'; import CreatureProperties, { CreatureProperty, CreaturePropertyByType } from '/imports/api/creature/creatureProperties/CreatureProperties';
import computeCreature from './computeCreature'; import computeCreature from './computeCreature';
import { getFilter } from '/imports/api/parenting/parentingFunctions'; import { getFilter } from '/imports/api/parenting/parentingFunctions';
import { ComputedPropertyTypeMap } from '../properties/Property.type';
const COMPUTE_DEBOUNCE_TIME = 100; // ms const COMPUTE_DEBOUNCE_TIME = 100; // ms
export const loadedCreatures: Map<string, LoadedCreature> = new Map(); // creatureId => {creature, properties, etc.} export const loadedCreatures: Map<string, LoadedCreature> = new Map(); // creatureId => {creature, properties, etc.}
@@ -81,13 +82,13 @@ export function getProperties(creatureId: string): CreatureProperty[] {
return props; return props;
} }
export function getPropertiesOfType(creatureId, propType) { export function getPropertiesOfType<T extends keyof ComputedPropertyTypeMap>(creatureId, propType: T): CreaturePropertyByType<T>[] {
const creature = loadedCreatures.get(creatureId); const creature = loadedCreatures.get(creatureId);
if (creature) { if (creature) {
const props = Array.from(creature.properties.values()) const props = Array.from(creature.properties.values())
.filter(prop => !prop.removed && prop.type === propType) .filter(prop => !prop.removed && prop.type === propType)
.sort((a, b) => a.left - b.left); .sort((a, b) => a.left - b.left);
return EJSON.clone(props); return EJSON.clone(props) as unknown as CreaturePropertyByType<T>[];
} }
// console.time(`Cache miss on creature properties: ${creatureId}`) // console.time(`Cache miss on creature properties: ${creatureId}`)
const props = CreatureProperties.find({ const props = CreatureProperties.find({
@@ -98,7 +99,7 @@ export function getPropertiesOfType(creatureId, propType) {
sort: { left: 1 }, sort: { left: 1 },
}).fetch(); }).fetch();
// console.timeEnd(`Cache miss on creature properties: ${creatureId}`); // console.timeEnd(`Cache miss on creature properties: ${creatureId}`);
return props; return props as unknown as CreaturePropertyByType<T>[];
} }
/** /**

View File

@@ -2,7 +2,7 @@ import SimpleSchema from 'simpl-schema';
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX'; import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema'; import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema';
import type { Expand, InferType } from '/imports/api/utility/TypedSimpleSchema'; import { Expand, InferType, TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
/* /*
* Attributes are numbered stats of a character * Attributes are numbered stats of a character
@@ -40,7 +40,7 @@ const AttributeSchema = createPropertySchema({
// For type hitDice, the size needs to be stored separately // For type hitDice, the size needs to be stored separately
hitDiceSize: { hitDiceSize: {
type: String, type: String,
allowedValues: ['d1', 'd2', 'd4', 'd6', 'd8', 'd10', 'd12', 'd20', 'd100'], allowedValues: ['d1', 'd2', 'd4', 'd6', 'd8', 'd10', 'd12', 'd20', 'd100'] as const,
optional: true, optional: true,
}, },
// For type spellSlot, the level needs to be stored separately // For type spellSlot, the level needs to be stored separately
@@ -297,7 +297,7 @@ const ComputedOnlyAttributeSchema = createPropertySchema({
}, },
}); });
const ComputedAttributeSchema = new SimpleSchema({}) const ComputedAttributeSchema = new TypedSimpleSchema({})
.extend(ComputedOnlyAttributeSchema) .extend(ComputedOnlyAttributeSchema)
.extend(AttributeSchema); .extend(AttributeSchema);

View File

@@ -1,7 +1,6 @@
import SimpleSchema from 'simpl-schema';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema'; import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema';
import { Expand, InferType } from '/imports/api/utility/TypedSimpleSchema'; import { Expand, InferType, TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
const BranchSchema = createPropertySchema({ const BranchSchema = createPropertySchema({
branchType: { branchType: {
@@ -53,12 +52,12 @@ const ComputedOnlyBranchSchema = createPropertySchema({
}, },
}); });
const ComputedBranchSchema = new SimpleSchema({}) const ComputedBranchSchema = new TypedSimpleSchema({})
.extend(BranchSchema) .extend(BranchSchema)
.extend(ComputedOnlyBranchSchema); .extend(ComputedOnlyBranchSchema);
export type Branch = InferType<typeof BranchSchema>; export type Branch = InferType<typeof BranchSchema>;
export type ComputedOnlyBranch = InferType<typeof ComputedOnlyBranchSchema>; export type ComputedOnlyBranch = InferType<typeof ComputedOnlyBranchSchema>;
export type ComputedBranch = Expand<InferType<typeof BranchSchema> & InferType<typeof ComputedOnlyBranchSchema>>; export type ComputedBranch = Expand<InferType<typeof BranchSchema> & InferType<typeof ComputedBranchSchema>>;
export { BranchSchema, ComputedBranchSchema, ComputedOnlyBranchSchema } export { BranchSchema, ComputedBranchSchema, ComputedOnlyBranchSchema }

View File

@@ -1,15 +1,16 @@
import SimpleSchema from 'simpl-schema'; import SimpleSchema from 'simpl-schema';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema'; import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema';
import { Expand, InferType, TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
let BuffSchema = createPropertySchema({ const BuffSchema = createPropertySchema({
name: { name: {
type: String, type: String,
optional: true, optional: true,
max: STORAGE_LIMITS.name, max: STORAGE_LIMITS.name,
}, },
description: { description: {
type: 'inlineCalculationFieldToCompute', type: 'inlineCalculationFieldToCompute' as const,
optional: true, optional: true,
}, },
hideRemoveButton: { hideRemoveButton: {
@@ -41,14 +42,14 @@ let BuffSchema = createPropertySchema({
}, },
}); });
let ComputedOnlyBuffSchema = createPropertySchema({ const ComputedOnlyBuffSchema = createPropertySchema({
description: { description: {
type: 'computedOnlyInlineCalculationField', type: 'computedOnlyInlineCalculationField' as const,
optional: true, optional: true,
max: STORAGE_LIMITS.description, max: STORAGE_LIMITS.description,
}, },
duration: { duration: {
type: 'computedOnlyField', type: 'computedOnlyField' as const,
optional: true, optional: true,
}, },
durationSpent: { durationSpent: {
@@ -74,8 +75,12 @@ let ComputedOnlyBuffSchema = createPropertySchema({
}, },
}); });
const ComputedBuffSchema = new SimpleSchema() const ComputedBuffSchema = new TypedSimpleSchema({})
.extend(BuffSchema) .extend(BuffSchema)
.extend(ComputedOnlyBuffSchema); .extend(ComputedOnlyBuffSchema);
export type Buff = InferType<typeof BuffSchema>;
export type ComputedOnlyBuff = InferType<typeof ComputedOnlyBuffSchema>;
export type ComputedBuff = Expand<InferType<typeof BuffSchema> & InferType<typeof ComputedOnlyBuffSchema>>;
export { BuffSchema, ComputedOnlyBuffSchema, ComputedBuffSchema }; export { BuffSchema, ComputedOnlyBuffSchema, ComputedBuffSchema };

View File

@@ -0,0 +1,115 @@
import { Attribute, ComputedAttribute, ComputedOnlyAttribute } from './Attributes';
import { Branch, ComputedBranch, ComputedOnlyBranch } from './Branches';
import { Buff, ComputedBuff, ComputedOnlyBuff } from './Buffs';
import { Class, ComputedClass, ComputedOnlyClass } from './Classes';
import { Constant, ComputedConstant, ComputedOnlyConstant } from './Constants';
import { Container, ComputedContainer, ComputedOnlyContainer } from './Containers';
import { CreatureTemplate, ComputedCreatureTemplate, ComputedOnlyCreatureTemplate } from './CreatureTemplates';
import { Damage, ComputedDamage, ComputedOnlyDamage } from './Damages';
import { DamageMultiplier, ComputedDamageMultiplier, ComputedOnlyDamageMultiplier } from './DamageMultipliers';
import { Effect, ComputedEffect, ComputedOnlyEffect } from './Effects';
import { Feature, ComputedFeature, ComputedOnlyFeature } from './Features';
import { Folder, ComputedFolder, ComputedOnlyFolder } from './Folders';
import { Item, ComputedItem, ComputedOnlyItem } from './Items';
import { Note, ComputedNote, ComputedOnlyNote } from './Notes';
import { PointBuy, ComputedPointBuy, ComputedOnlyPointBuy } from './PointBuys';
import { Proficiency, ComputedProficiency, ComputedOnlyProficiency } from './Proficiencies';
import { Reference, ComputedReference, ComputedOnlyReference } from './References';
import { Roll, ComputedRoll, ComputedOnlyRoll } from './Rolls';
import { SavingThrow, ComputedSavingThrow, ComputedOnlySavingThrow } from './SavingThrows';
import { Skill, ComputedSkill, ComputedOnlySkill } from './Skills';
import { Slot, ComputedSlot, ComputedOnlySlot } from './Slots';
import { SpellList, ComputedSpellList, ComputedOnlySpellList } from './SpellLists';
import { Spell, ComputedSpell, ComputedOnlySpell } from './Spells';
import { Toggle, ComputedToggle, ComputedOnlyToggle } from './Toggles';
import { Trigger, ComputedTrigger, ComputedOnlyTrigger } from './Triggers';
export type PropertyTypeMap = {
'attribute': Attribute,
'branch': Branch,
'buff': Buff,
'class': Class,
'constant': Constant,
'container': Container,
'creatureTemplate': CreatureTemplate,
'damage': Damage,
'damageMultiplier': DamageMultiplier,
'effect': Effect,
'feature': Feature,
'folder': Folder,
'item': Item,
'note': Note,
'pointBuy': PointBuy,
'proficiency': Proficiency,
'reference': Reference,
'roll': Roll,
'savingThrow': SavingThrow,
'skill': Skill,
'slot': Slot,
'spellList': SpellList,
'spell': Spell,
'toggle': Toggle,
'trigger': Trigger,
}
export type Property<T extends keyof PropertyTypeMap> = PropertyTypeMap[T]
export type ComputedPropertyTypeMap = {
'attribute': ComputedAttribute,
'branch': ComputedBranch,
'buff': ComputedBuff,
'class': ComputedClass,
'constant': ComputedConstant,
'container': ComputedContainer,
'creatureTemplate': ComputedCreatureTemplate,
'damage': ComputedDamage,
'damageMultiplier': ComputedDamageMultiplier,
'effect': ComputedEffect,
'feature': ComputedFeature,
'folder': ComputedFolder,
'item': ComputedItem,
'note': ComputedNote,
'pointBuy': ComputedPointBuy,
'proficiency': ComputedProficiency,
'reference': ComputedReference,
'roll': ComputedRoll,
'savingThrow': ComputedSavingThrow,
'skill': ComputedSkill,
'slot': ComputedSlot,
'spellList': ComputedSpellList,
'spell': ComputedSpell,
'toggle': ComputedToggle,
'trigger': ComputedTrigger,
}
export type ComputedProperty<T extends keyof ComputedPropertyTypeMap> = ComputedPropertyTypeMap[T]
export type ComputedOnlyPropertyTypeMap = {
'attribute': ComputedOnlyAttribute,
'branch': ComputedOnlyBranch,
'buff': ComputedOnlyBuff,
'class': ComputedOnlyClass,
'constant': ComputedOnlyConstant,
'container': ComputedOnlyContainer,
'creatureTemplate': ComputedOnlyCreatureTemplate,
'damage': ComputedOnlyDamage,
'damageMultiplier': ComputedOnlyDamageMultiplier,
'effect': ComputedOnlyEffect,
'feature': ComputedOnlyFeature,
'folder': ComputedOnlyFolder,
'item': ComputedOnlyItem,
'note': ComputedOnlyNote,
'pointBuy': ComputedOnlyPointBuy,
'proficiency': ComputedOnlyProficiency,
'reference': ComputedOnlyReference,
'roll': ComputedOnlyRoll,
'savingThrow': ComputedOnlySavingThrow,
'skill': ComputedOnlySkill,
'slot': ComputedOnlySlot,
'spellList': ComputedOnlySpellList,
'spell': ComputedOnlySpell,
'toggle': ComputedOnlyToggle,
'trigger': ComputedOnlyTrigger,
}
export type ComputedOnlyProperty<T extends keyof ComputedOnlyPropertyTypeMap> = ComputedOnlyPropertyTypeMap[T]

View File

@@ -1,5 +1,5 @@
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
import { TypedSimpleSchema } from 'imports/api/utility/TypedSimpleSchema'; import { TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
const ErrorSchema = new TypedSimpleSchema({ const ErrorSchema = new TypedSimpleSchema({
message: { message: {

View File

@@ -1,6 +1,6 @@
import SimpleSchema from 'simpl-schema'; import SimpleSchema from 'simpl-schema';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
import { TypedSimpleSchema } from 'imports/api/utility/TypedSimpleSchema'; import { TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
const TagTargetingSchema = new TypedSimpleSchema({ const TagTargetingSchema = new TypedSimpleSchema({
// True when targeting by tags instead of stats // True when targeting by tags instead of stats