Implemented Active Effects.
Used AE to implement exhaustion, stimulants, and rest.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
const { ArrayField, SchemaField, StringField, NumberField, AnyField } =
|
||||
foundry.data.fields;
|
||||
|
||||
/**
|
||||
* Delta Green Active Effect system data — matches core change schema with `final` as the default phase.
|
||||
* @extends {foundry.data.ActiveEffectTypeDataModel}
|
||||
*/
|
||||
export default class DGActiveEffectTypeDataModel extends foundry.data
|
||||
.ActiveEffectTypeDataModel {
|
||||
/** @override */
|
||||
static defineSchema() {
|
||||
return {
|
||||
changes: new ArrayField(
|
||||
new SchemaField({
|
||||
key: new StringField({ required: true }),
|
||||
type: new StringField({
|
||||
required: true,
|
||||
blank: false,
|
||||
initial: "add",
|
||||
validate: DGActiveEffectTypeDataModel.#validateType,
|
||||
}),
|
||||
value: new AnyField({
|
||||
required: true,
|
||||
nullable: true,
|
||||
serializable: true,
|
||||
initial: "",
|
||||
}),
|
||||
phase: new StringField({
|
||||
required: true,
|
||||
blank: false,
|
||||
initial: "final",
|
||||
}),
|
||||
priority: new NumberField(),
|
||||
}),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @returns {true}
|
||||
*/
|
||||
static #validateType(type) {
|
||||
if (type.length < 3) throw new Error("must be at least three characters long");
|
||||
if (
|
||||
!/^custom\.-?\d+$/.test(type) &&
|
||||
!type.split(".").every((s) => /^[a-z0-9]+$/i.test(s))
|
||||
) {
|
||||
throw new Error(
|
||||
'A change type must either be a sequence of dot-delimited, alpha-numeric substrings or of the form "custom.{number}"',
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+34
-19
@@ -2,13 +2,11 @@ import HumanSkillsActorData from "./base/human-skills.js";
|
||||
import CharacterData from "./base/character.js";
|
||||
import DGHTMLField from "../fields/html-content-field.js";
|
||||
import {
|
||||
calculateHealthMax,
|
||||
calculateMeleeDamageBonusFormula,
|
||||
clampSanityRitualValue,
|
||||
agentResourceField,
|
||||
} from "./base/general.js";
|
||||
import {
|
||||
computeEquippedArmorProtection,
|
||||
initializeSanityIfUnset,
|
||||
prepareAgentSkillFlags,
|
||||
prepareBreakingPointHit,
|
||||
prepareSanityAdaptations,
|
||||
prepareStatisticsX5,
|
||||
removeLegacyRitualSkill,
|
||||
@@ -21,13 +19,19 @@ export default class AgentData extends CharacterData {
|
||||
static defineSchema() {
|
||||
const superData = super.defineSchema();
|
||||
|
||||
const { health: _health, wp: _wp, ...restSuper } = superData;
|
||||
|
||||
return {
|
||||
...superData,
|
||||
...restSuper,
|
||||
health: agentResourceField(10, 10),
|
||||
wp: agentResourceField(10, 10),
|
||||
...HumanSkillsActorData.defineSchema(),
|
||||
schemaVersion: new NumberField({ initial: 2.0 }),
|
||||
sanity: new SchemaField({
|
||||
value: new NumberField({ initial: 100 }),
|
||||
currentBreakingPoint: new NumberField({ initial: 101 }),
|
||||
value: new NumberField({ initial: 100, min: 0, integer: true }),
|
||||
max: new NumberField({ initial: 99, min: 0, integer: true }),
|
||||
currentBreakingPoint: new NumberField({ initial: 101, integer: true }),
|
||||
maxBonus: new NumberField({ initial: 0, integer: true }),
|
||||
adaptations: new SchemaField({
|
||||
violence: new SchemaField({
|
||||
incident1: new BooleanField({ initial: false }),
|
||||
@@ -41,12 +45,18 @@ export default class AgentData extends CharacterData {
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
rollTarget: new SchemaField({
|
||||
allSkills: new NumberField({ initial: 0, integer: true }),
|
||||
sanity: new NumberField({ initial: 0, integer: true }),
|
||||
statistics: new NumberField({ initial: 0, integer: true }),
|
||||
}),
|
||||
physical: new SchemaField({
|
||||
description: DGHTMLField(),
|
||||
wounds: new StringField({ initial: "" }),
|
||||
firstAidAttempted: new BooleanField({ initial: false }),
|
||||
exhausted: new BooleanField({ initial: false }),
|
||||
exhaustedPenalty: new NumberField({ initial: -20 }),
|
||||
suppressExhaustion: new BooleanField({ initial: false }),
|
||||
}),
|
||||
biography: new SchemaField({
|
||||
profession: new StringField({ initial: "" }),
|
||||
@@ -66,24 +76,29 @@ export default class AgentData extends CharacterData {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
prepareDerivedData() {
|
||||
prepareStatisticsX5(this.statistics);
|
||||
removeLegacyRitualSkill(this);
|
||||
clampSanityRitualValue(this.sanity);
|
||||
prepareAgentSkillFlags(this.skills);
|
||||
prepareBaseData() {
|
||||
// rollTarget is modified by add-type Active Effects each prepare cycle.
|
||||
this.rollTarget.allSkills = 0;
|
||||
this.rollTarget.sanity = 0;
|
||||
this.rollTarget.statistics = 0;
|
||||
}
|
||||
|
||||
this.wp.max = this.statistics.pow.value;
|
||||
this.health.max = calculateHealthMax(this.statistics);
|
||||
/** @inheritdoc */
|
||||
prepareDerivedData() {
|
||||
// Pre–final-AE setup only. Post-AE derived fields (max, x5 refresh,
|
||||
// breaking point, etc.) are applied in DeltaGreenActor.prepareData via
|
||||
// refreshDerivedAfterActiveEffects after the final Active Effect phase.
|
||||
prepareStatisticsX5(
|
||||
this.statistics,
|
||||
this.parent?._source?.system?.statistics,
|
||||
);
|
||||
removeLegacyRitualSkill(this);
|
||||
initializeSanityIfUnset(this.sanity, this.statistics);
|
||||
this.sanity.max = 99 - this.skills.unnatural.proficiency;
|
||||
prepareSanityAdaptations(this.sanity.adaptations);
|
||||
prepareBreakingPointHit(this.sanity);
|
||||
|
||||
this.health.protection = computeEquippedArmorProtection(
|
||||
this.parent.items,
|
||||
);
|
||||
this.statistics.str.meleeDamageBonusFormula =
|
||||
calculateMeleeDamageBonusFormula(this.statistics.str.value);
|
||||
|
||||
if (this.physical.exhaustedPenalty > 0) {
|
||||
this.physical.exhaustedPenalty =
|
||||
|
||||
@@ -3,7 +3,12 @@ const { SchemaField, NumberField, StringField, BooleanField } =
|
||||
|
||||
export function skillField(initialValue, label) {
|
||||
return new SchemaField({
|
||||
proficiency: new NumberField({ initial: initialValue }),
|
||||
proficiency: new NumberField({
|
||||
initial: initialValue,
|
||||
min: 0,
|
||||
max: 99,
|
||||
integer: true,
|
||||
}),
|
||||
label: new StringField({ initial: label }),
|
||||
failure: new BooleanField({ initial: false }),
|
||||
});
|
||||
@@ -11,15 +16,26 @@ export function skillField(initialValue, label) {
|
||||
|
||||
export function resourceField(initialValue, initialMax) {
|
||||
return new SchemaField({
|
||||
min: new NumberField({ initial: 0 }),
|
||||
value: new NumberField({ initial: initialValue }),
|
||||
max: new NumberField({ initial: initialMax }),
|
||||
min: new NumberField({ initial: 0, min: 0, integer: true }),
|
||||
value: new NumberField({ initial: initialValue, min: 0, integer: true }),
|
||||
max: new NumberField({ initial: initialMax, min: 0, integer: true }),
|
||||
});
|
||||
}
|
||||
|
||||
/** Agent resources that support Active Effect maximum bonuses. */
|
||||
export function agentResourceField(initialValue, initialMax) {
|
||||
return new SchemaField({
|
||||
min: new NumberField({ initial: 0, min: 0, integer: true }),
|
||||
value: new NumberField({ initial: initialValue, min: 0, integer: true }),
|
||||
max: new NumberField({ initial: initialMax, min: 0, integer: true }),
|
||||
maxBonus: new NumberField({ initial: 0, integer: true }),
|
||||
});
|
||||
}
|
||||
|
||||
export function statisticsField(initialValue) {
|
||||
return new SchemaField({
|
||||
value: new NumberField({ initial: initialValue }),
|
||||
value: new NumberField({ initial: initialValue, min: 0, integer: true }),
|
||||
modifier: new NumberField({ initial: 0, integer: true }),
|
||||
distinguishing_feature: new StringField({ initial: "" }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export default class HumanSkillsActorData extends TypeDataModel {
|
||||
swim: skillField(20, "Swim"),
|
||||
unarmed_combat: skillField(40, "Unarmed Combat"),
|
||||
unnatural: new SchemaField({
|
||||
proficiency: new NumberField({ initial: 0 }),
|
||||
proficiency: new NumberField({ initial: 0, min: 0, max: 99, integer: true }),
|
||||
label: new StringField({ initial: "Unnatural" }),
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
calculateHealthMax,
|
||||
calculateMeleeDamageBonusFormula,
|
||||
clampSanityRitualValue,
|
||||
cleanDerivedNumber,
|
||||
computeEquippedArmorProtection,
|
||||
initializeSanityIfUnset,
|
||||
prepareStatisticsX5,
|
||||
@@ -24,8 +25,9 @@ export default class NPCData extends CharacterData {
|
||||
...HumanSkillsActorData.defineSchema(),
|
||||
schemaVersion: new NumberField({ initial: 2.0 }),
|
||||
sanity: new SchemaField({
|
||||
value: new NumberField({ initial: 100 }),
|
||||
currentBreakingPoint: new NumberField({ initial: 101 }),
|
||||
value: new NumberField({ initial: 100, min: 0, integer: true }),
|
||||
max: new NumberField({ initial: 99, min: 0, integer: true }),
|
||||
currentBreakingPoint: new NumberField({ initial: 101, integer: true }),
|
||||
}),
|
||||
biography: new SchemaField({
|
||||
profession: new StringField({ initial: "" }),
|
||||
@@ -38,13 +40,28 @@ export default class NPCData extends CharacterData {
|
||||
|
||||
/** @inheritdoc */
|
||||
prepareDerivedData() {
|
||||
prepareStatisticsX5(this.statistics);
|
||||
const sourceStatistics = this.parent?._source?.system?.statistics;
|
||||
prepareStatisticsX5(this.statistics, sourceStatistics);
|
||||
initializeSanityIfUnset(this.sanity, this.statistics);
|
||||
this.sanity.max = 99 - this.skills.unnatural.proficiency;
|
||||
this.wp.max = this.statistics.pow.value;
|
||||
this.health.max = calculateHealthMax(this.statistics);
|
||||
this.sanity.max = cleanDerivedNumber(
|
||||
this,
|
||||
"sanity.max",
|
||||
99 - this.skills.unnatural.proficiency,
|
||||
);
|
||||
this.wp.max = cleanDerivedNumber(
|
||||
this,
|
||||
"wp.max",
|
||||
this.statistics.pow.effectiveValue ?? this.statistics.pow.value,
|
||||
);
|
||||
this.health.max = cleanDerivedNumber(
|
||||
this,
|
||||
"health.max",
|
||||
calculateHealthMax(this.statistics, sourceStatistics),
|
||||
);
|
||||
this.statistics.str.meleeDamageBonusFormula =
|
||||
calculateMeleeDamageBonusFormula(this.statistics.str.value);
|
||||
calculateMeleeDamageBonusFormula(
|
||||
this.statistics.str.effectiveValue ?? this.statistics.str.value,
|
||||
);
|
||||
removeLegacyRitualSkill(this);
|
||||
clampSanityRitualValue(this.sanity);
|
||||
this.health.protection = computeEquippedArmorProtection(this.parent.items);
|
||||
|
||||
@@ -3,6 +3,7 @@ import CharacterData from "./base/character.js";
|
||||
import DGHTMLField from "../fields/html-content-field.js";
|
||||
import {
|
||||
calculateHealthMax,
|
||||
cleanDerivedNumber,
|
||||
computeEquippedArmorProtection,
|
||||
prepareStatisticsX5,
|
||||
removeLegacyRitualSkill,
|
||||
@@ -33,14 +34,23 @@ export default class UnnaturalData extends CharacterData {
|
||||
|
||||
/** @inheritdoc */
|
||||
prepareDerivedData() {
|
||||
prepareStatisticsX5(this.statistics);
|
||||
const sourceStatistics = this.parent?._source?.system?.statistics;
|
||||
prepareStatisticsX5(this.statistics, sourceStatistics);
|
||||
|
||||
if (this.wp.maxNeedsUpdate) {
|
||||
this.wp.max = this.statistics.pow.value;
|
||||
this.wp.max = cleanDerivedNumber(
|
||||
this,
|
||||
"wp.max",
|
||||
this.statistics.pow.effectiveValue ?? this.statistics.pow.value,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.health.maxNeedsUpdate) {
|
||||
this.health.max = calculateHealthMax(this.statistics);
|
||||
this.health.max = cleanDerivedNumber(
|
||||
this,
|
||||
"health.max",
|
||||
calculateHealthMax(this.statistics, sourceStatistics),
|
||||
);
|
||||
}
|
||||
|
||||
removeLegacyRitualSkill(this);
|
||||
|
||||
@@ -9,6 +9,7 @@ export default class MotivationItemData extends foundry.abstract.TypeDataModel {
|
||||
disorder: new StringField({ initial: "" }),
|
||||
crossedOut: new BooleanField({ initial: false }),
|
||||
disorderCured: new BooleanField({ initial: false }),
|
||||
acuteEpisode: new BooleanField({ initial: false }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user