Files
DiceCloud/app/imports/api/properties/Attributes.ts
2025-01-10 09:35:30 +02:00

309 lines
7.6 KiB
TypeScript

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 { Expand, InferType } from '/imports/api/utility/TypedSimpleSchema';
/*
* 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' as const,
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' as const,
optional: true,
},
// Description of what the attribute is used for
description: {
type: 'inlineCalculationFieldToCompute' as const,
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' as const,
optional: true,
},
baseValue: {
type: 'computedOnlyField' as const,
optional: true,
},
spellSlotLevel: {
type: 'computedOnlyField' as const,
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 type Attribute = InferType<typeof AttributeSchema>;
export type ComputedOnlyAttribute = InferType<typeof ComputedOnlyAttributeSchema>;
export type ComputedAttribute = Expand<InferType<typeof AttributeSchema> & InferType<typeof ComputedOnlyAttributeSchema>>;
export { AttributeSchema, ComputedOnlyAttributeSchema, ComputedAttributeSchema };