dndbeyond_src/ddb_main/packages/rules-engine/es/managers/CharacterFeaturesManager.js
2025-05-28 15:36:51 -07:00

187 lines
8.7 KiB
JavaScript

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { orderBy } from 'lodash';
import { characterActions } from "../actions";
import { ApiAdapterUtils } from "../apiAdapter";
import { CharacterUtils } from "../engine/Character";
import { FeatAccessors, FeatSimulators } from "../engine/Feat";
import { HelperUtils } from "../engine/Helper";
import { featDefinitionMap, getFeatManager } from "./FeatManager";
import { apiCreatorSelectors, rulesEngineSelectors } from "../selectors";
import { FeaturesManager } from './FeaturesManager';
export class CharacterFeaturesManager extends FeaturesManager {
constructor(params) {
super(params);
//TODO GFS THIS!!!!
this.getBlessings = () => __awaiter(this, void 0, void 0, function* () {
// TODO get the blessings only?
/**
* this could be on the character obj under blessings key
*/
const blessingManagers = yield this.getCharacterFeatures();
this.blessingManagers = blessingManagers;
return blessingManagers;
});
this.getBlessing = (id) => { var _a; return (_a = this.blessingManagers.find((blessing) => id === blessing.getId())) !== null && _a !== void 0 ? _a : null; };
// TODO is there value in moving these to hasFeatures and hasBlessings
this.hasBlessings = () => __awaiter(this, void 0, void 0, function* () {
const blessings = yield this.getBlessings();
return blessings.length > 0;
});
this.hasBlessing = (blessing) => !!this.blessingManagers.find((characterBlessing) => characterBlessing.getId() === blessing.getId());
this.handlePreferenceChange = (preferenceKey, value) => {
const typedPrefKey = CharacterUtils.getPreferenceKey(preferenceKey);
if (typedPrefKey !== null) {
this.dispatch(characterActions.preferenceChoose(typedPrefKey, value));
}
};
this.params = params;
this.blessingManagers = [];
}
mapFeatsToManagers(feats) {
return feats.map((feat) => getFeatManager(Object.assign(Object.assign({}, this.params), { feat })));
}
/**
* Gets feat managers for all of the feats on the character.
*/
getFeats() {
const feats = rulesEngineSelectors.getFeats(this.state);
return this.mapFeatsToManagers(feats);
}
/**
* Gets feat managers for all feats except those tagged as being data origin only.
*/
getStandardFeats() {
const feats = rulesEngineSelectors.getStandardFeats(this.state);
return this.mapFeatsToManagers(feats);
}
/**
* Gets feat managers for feats with the designated primary data origin,
* but only for feats that are tagged as being data origin only.
* Useful for secret feats that come from Class Features or Racial Traits
*/
getDataOriginOnlyFeatsByPrimary(primaryType, primaryId) {
var _a;
const lookup = rulesEngineSelectors.getDataOriginOnlyFeatLookup(this.state);
const feats = (_a = lookup.byPrimary[primaryType][primaryId]) !== null && _a !== void 0 ? _a : [];
return this.mapFeatsToManagers(feats);
}
/**
* Gets feat managers for feats with the designated parent data origin,
* but only for feats that are tagged as being data origin only.
* Useful for secret feats that come from Backgrounds via Feat Lists
*/
getDataOriginOnlyFeatsByParent(parentType, parentId) {
var _a;
const lookup = rulesEngineSelectors.getDataOriginOnlyFeatLookup(this.state);
const feats = (_a = lookup.byParent[parentType][parentId]) !== null && _a !== void 0 ? _a : [];
return this.mapFeatsToManagers(feats);
}
getFeatLookup() {
return HelperUtils.generateNonNullLookup(this.getFeats(), (feat) => feat.getId());
}
getFeatById(id) {
return HelperUtils.lookupDataOrFallback(this.getFeatLookup(), id);
}
updateFeatShoppe(currentShoppe) {
const feats = currentShoppe.map((manager) => {
const currentManager = this.getFeatById(manager.getId());
if (currentManager) {
return currentManager;
}
else {
const featDefinitionContract = featDefinitionMap.get(manager.getId());
if (featDefinitionContract) {
const simFeat = FeatSimulators.simulateFeat(featDefinitionContract);
return getFeatManager(Object.assign(Object.assign({}, this.params), { feat: simFeat }));
}
return manager;
}
});
return feats;
}
transformLoadedFeats(data) {
const feats = data.map((definition) => {
featDefinitionMap.set(definition.id, definition);
const simFeat = FeatSimulators.simulateFeat(definition);
const featManager = this.getFeatById(FeatAccessors.getId(simFeat));
return featManager ? featManager : getFeatManager(Object.assign(Object.assign({}, this.params), { feat: simFeat }));
});
return orderBy(feats, (feat) => feat.getName());
}
getFeatShoppe(additionalConfig) {
return __awaiter(this, void 0, void 0, function* () {
const loadFeats = apiCreatorSelectors.makeLoadAvailableFeats(this.state);
const response = yield loadFeats(additionalConfig);
let data = ApiAdapterUtils.getResponseData(response);
if (data) {
return this.transformLoadedFeats(data);
}
return [];
});
}
getBlessingShoppe(additionalConfig) {
return __awaiter(this, void 0, void 0, function* () {
const blessings = yield this.getFeaturesShoppe({
params: {
category: 'blessing',
},
});
return orderBy(blessings, (blessing) => blessing.getName());
});
}
//UTILS
// filter to only allow one of each simulated repeatable feat group
filterUniqueSimulatedRepeatableFeats(feats, parentFeats) {
const tracker = new Set();
return feats.filter((feat) => {
if (!feat.isSimulatedRepeatableFeat()) {
return true;
}
const repeatableGroupId = feat.getRepeatableGroupId();
if (repeatableGroupId !== null) {
// find the parent of repeatable feat group
const parentFeat = parentFeats.find((f) => f.getId() === repeatableGroupId);
// don't include repeatable feats if the parent is not added
if (parentFeat === null || parentFeat === void 0 ? void 0 : parentFeat.isSimulatedRepeatableFeat()) {
return parentFeat.getId() === feat.getId();
}
if (tracker.has(repeatableGroupId)) {
return false;
}
tracker.add(repeatableGroupId);
return true;
}
return false;
});
}
//get an array of feats that are available to be added to the character
getAvailableFeats(feats) {
const available = feats.filter((feat) => feat.canAdd());
const allRepeatableParentFeats = feats.filter((feat) => feat.isRepeatableFeatParent());
return this.filterUniqueSimulatedRepeatableFeats(available, allRepeatableParentFeats);
}
//get an array of feats that are not available to be added to the character due to prerequisites not being met
getUnavailableFeats(feats) {
const getUnavailableFeats = feats.filter((feat) => !feat.canAdd());
const allRepeatableParentFeats = feats.filter((feat) => feat.isRepeatableFeatParent());
return this.filterUniqueSimulatedRepeatableFeats(getUnavailableFeats, allRepeatableParentFeats);
}
// PREFERENCES
getEnforceFeatRules() {
const preferences = rulesEngineSelectors.getCharacterPreferences(this.state);
return preferences.enforceFeatRules;
}
setEnforceFeatRules(value) {
const preferenceKey = 'enforceFeatRules';
this.handlePreferenceChange(preferenceKey, value);
}
}