import SimpleSchema from 'simpl-schema'; import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS'; import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema'; import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties'; import { CalculatedField } from '/imports/api/properties/subSchemas/computedField'; import { InlineCalculation } from '/imports/api/properties/subSchemas/inlineCalculationField'; import { ConstantValueType } from '/imports/parser/parseTree/constant'; import Property from '/imports/api/properties/Properties.type'; export type CreatureAttribute = Attribute & CreatureProperty & { total?: ConstantValueType; value?: ConstantValueType; modifier?: number; proficiency?: 0 | 0.49 | 0.5 | 1 | 2; advantage?: -1 | 0 | 1; constitutionMod?: number; hide?: true; overridden?: true; effectIds?: string[]; proficiencyIds?: string[]; definitions?: { _id: string, type: string, row?: number }[]; } export interface Attribute extends Property { type: 'attribute'; name?: string; variableName?: string; attributeType: 'ability' | 'stat' | 'modifier' | 'hitDice' | 'healthBar' | 'resource' | 'spellSlot' | 'utility'; hitDiceSize?: 'd1' | 'd2' | 'd4' | 'd6' | 'd8' | 'd10' | 'd12' | 'd20' | 'd100'; spellSlotLevel?: CalculatedField; healthBarColorMid?: string; healthBarColorLow?: string; healthBarNoDamage?: true; healthBarNoHealing?: true; healthBarNoDamageOverflow?: true; healthBarNoHealingOverflow?: true; healthBarDamageOrder?: number; healthBarHealingOrder?: number; baseValue?: CalculatedField; description?: InlineCalculation; damage?: number; decimal?: true; ignoreLowerLimit?: true; ignoreUpperLimit?: true; hideWhenTotalZero?: true; hideWhenValueZero?: true; reset?: string; } /* * Attributes are numbered stats of a character */ const AttributeSchema = createPropertySchema({ name: { type: String, optional: true, max: STORAGE_LIMITS.name, }, // The technical, lowercase, single-word name used in formulae variableName: { type: String, optional: true, regEx: VARIABLE_NAME_REGEX, min: 2, max: STORAGE_LIMITS.variableName, }, // How it is displayed and computed is determined by type attributeType: { type: String, allowedValues: [ 'ability', //Strength, Dex, Con, etc. 'stat', // Speed, Armor Class 'modifier', // Proficiency Bonus, displayed as +x 'hitDice', // d12 hit dice 'healthBar', // Hitpoints, Temporary Hitpoints 'resource', // Rages, sorcery points 'spellSlot', // Level 1, 2, 3... spell slots 'utility', // Aren't displayed, Jump height, Carry capacity ], defaultValue: 'stat', index: 1, }, // For type hitDice, the size needs to be stored separately hitDiceSize: { type: String, allowedValues: ['d1', 'd2', 'd4', 'd6', 'd8', 'd10', 'd12', 'd20', 'd100'], optional: true, }, // For type spellSlot, the level needs to be stored separately spellSlotLevel: { type: 'fieldToCompute', optional: true, }, // For type healthBar midColor, and lowColor can be set separately from the // property's color, which is used as the undamaged color 'healthBarColorMid': { type: String, regEx: /^#([a-f0-9]{3}){1,2}\b$/i, optional: true, }, 'healthBarColorLow': { type: String, regEx: /^#([a-f0-9]{3}){1,2}\b$/i, optional: true, }, // Control how the health bar takes damage or healing healthBarNoDamage: { type: Boolean, optional: true, }, healthBarNoHealing: { type: Boolean, optional: true, }, // Control how the health bar handles overflow healthBarNoDamageOverflow: { type: Boolean, optional: true, }, healthBarNoHealingOverflow: { type: Boolean, optional: true, }, // Control when the health bar takes damage or healing healthBarDamageOrder: { type: SimpleSchema.Integer, optional: true, }, healthBarHealingOrder: { type: SimpleSchema.Integer, optional: true, }, // The starting value, before effects baseValue: { type: 'fieldToCompute', optional: true, }, // Description of what the attribute is used for description: { type: 'inlineCalculationFieldToCompute', optional: true, }, // The damage done to the attribute, should always compute as positive damage: { type: SimpleSchema.Integer, optional: true, }, // Can the value be decimal? decimal: { type: Boolean, optional: true, }, // Can the total after damage be negative ignoreLowerLimit: { type: Boolean, optional: true, }, // Can the damage value be negative ignoreUpperLimit: { type: Boolean, optional: true, }, hideWhenTotalZero: { type: Boolean, optional: true, }, hideWhenValueZero: { type: Boolean, optional: true, }, // Automatically zero the adjustment on these conditions reset: { type: String, optional: true, regEx: VARIABLE_NAME_REGEX, min: 2, max: STORAGE_LIMITS.variableName, }, }); const ComputedOnlyAttributeSchema = createPropertySchema({ description: { type: 'computedOnlyInlineCalculationField', optional: true, }, baseValue: { type: 'computedOnlyField', optional: true, }, spellSlotLevel: { type: 'computedOnlyField', optional: true, }, // The computed value of the attribute total: { type: SimpleSchema.oneOf(Number, String, Boolean), optional: true, removeBeforeCompute: true, }, // The computed value of the attribute minus the damage value: { type: SimpleSchema.oneOf(Number, String, Boolean), defaultValue: 0, optional: true, removeBeforeCompute: true, }, // The computed modifier, provided the attribute type is `ability` modifier: { type: SimpleSchema.Integer, optional: true, removeBeforeCompute: true, }, // Attributes with proficiency grant it to all skills based on the attribute proficiency: { type: Number, allowedValues: [0, 0.49, 0.5, 1, 2], optional: true, removeBeforeCompute: true, }, // Attributes with advantage grant it to all skills based on the attribute advantage: { type: SimpleSchema.Integer, optional: true, allowedValues: [-1, 0, 1], removeBeforeCompute: true, }, // The computed creature constitution modifier for hit dice constitutionMod: { type: Number, optional: true, removeBeforeCompute: true, }, // Should this attribute hide hide: { type: Boolean, optional: true, removeBeforeCompute: true, }, // Denormalised tag if stat is overridden by one with the same variable name overridden: { type: Boolean, optional: true, removeBeforeCompute: true, }, // A list of effect ids targeting this attribute 'effectIds': { type: Array, optional: true, removeBeforeCompute: true, }, 'effectIds.$': { type: String, }, 'proficiencyIds': { type: Array, optional: true, removeBeforeCompute: true, }, 'proficiencyIds.$': { type: String, }, 'definitions': { type: Array, optional: true, removeBeforeCompute: true, }, 'definitions.$': { type: Object, }, 'definitions.$._id': { type: String, }, 'definitions.$.type': { optional: true, type: String, }, 'definitions.$.row': { type: Number, optional: true, }, // Triggers that fire when this property is damaged 'damageTriggerIds': { type: Object, optional: true, removeBeforeCompute: true, }, 'damageTriggerIds.before': { type: Array, optional: true, }, 'damageTriggerIds.before.$': { type: String, regEx: SimpleSchema.RegEx.Id, }, 'damageTriggerIds.after': { type: Array, optional: true, }, 'damageTriggerIds.after.$': { type: String, regEx: SimpleSchema.RegEx.Id, }, 'damageTriggerIds.afterChildren': { type: Array, optional: true, }, 'damageTriggerIds.afterChildren.$': { type: String, regEx: SimpleSchema.RegEx.Id, }, // Triggers that fire when this property is used to make a check 'checkTriggerIds': { type: Object, optional: true, removeBeforeCompute: true, }, 'checkTriggerIds.before': { type: Array, optional: true, }, 'checkTriggerIds.before.$': { type: String, regEx: SimpleSchema.RegEx.Id, }, 'checkTriggerIds.after': { type: Array, optional: true, }, 'checkTriggerIds.after.$': { type: String, regEx: SimpleSchema.RegEx.Id, }, 'checkTriggerIds.afterChildren': { type: Array, optional: true, }, 'checkTriggerIds.afterChildren.$': { type: String, regEx: SimpleSchema.RegEx.Id, }, }); const ComputedAttributeSchema = new SimpleSchema({}) .extend(ComputedOnlyAttributeSchema) .extend(AttributeSchema); export { AttributeSchema, ComputedOnlyAttributeSchema, ComputedAttributeSchema };