Grabbed dndbeyond's source code

```
~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js
```
This commit is contained in:
2025-05-28 11:50:03 -07:00
commit 8df9031d27
3592 changed files with 319051 additions and 0 deletions
@@ -0,0 +1,93 @@
import { characterActions } from "../actions";
import { AbilityAccessors } from "../engine/Ability";
import { AbilityScoreStatTypeEnum } from "../engine/Core";
import { HelperUtils } from "../engine/Helper";
import { RuleDataAccessors } from "../engine/RuleData";
import { rulesEngineSelectors } from "../selectors";
import { FeaturesManager } from './FeaturesManager';
export const abilityDefinitionMap = new Map();
const abilityMangerMap = new Map();
export const getAbilityManager = (params) => {
const { ability } = params;
const abilityId = AbilityAccessors.getId(ability);
if (abilityMangerMap.has(abilityId)) {
const abilityManager = abilityMangerMap.get(abilityId);
if (!abilityManager) {
throw new Error(`AbilityManager for ability ${abilityId} is null`);
}
if (abilityManager.ability === ability) {
return abilityManager;
}
}
const newAbilityManager = new AbilityManager(params);
abilityMangerMap.set(abilityId, newAbilityManager);
return newAbilityManager;
};
export class AbilityManager extends FeaturesManager {
constructor(params) {
super(params);
//Accessors
this.getId = () => AbilityAccessors.getId(this.ability);
this.getEntityTypeId = () => AbilityAccessors.getEntityTypeId(this.ability);
this.getName = () => AbilityAccessors.getName(this.ability);
this.getLabel = () => AbilityAccessors.getLabel(this.ability);
this.getStatKey = () => AbilityAccessors.getStatKey(this.ability);
this.getBaseScore = () => AbilityAccessors.getBaseScore(this.ability);
this.getTotalScore = () => AbilityAccessors.getTotalScore(this.ability);
this.getModifier = () => AbilityAccessors.getModifier(this.ability);
this.getMaxStatScore = () => AbilityAccessors.getMaxStatScore(this.ability);
this.getProficiencyLevel = () => AbilityAccessors.getProficiencyLevel(this.ability);
this.getIsSaveProficiencyModified = () => AbilityAccessors.getIsSaveProficiencyModified(this.ability);
this.getSave = () => AbilityAccessors.getSave(this.ability);
this.getIsSaveModifierModified = () => AbilityAccessors.getIsSaveModifierModified(this.ability);
this.getOverrideScore = () => AbilityAccessors.getOverrideScore(this.ability);
this.getOtherBonus = () => AbilityAccessors.getOtherBonus(this.ability);
this.getRacialBonus = () => AbilityAccessors.getRacialBonus(this.ability);
this.getClassBonuses = () => AbilityAccessors.getClassBonuses(this.ability);
this.getMiscBonus = () => AbilityAccessors.getMiscBonus(this.ability);
this.getSetScore = () => AbilityAccessors.getSetScore(this.ability);
this.getStackingBonus = () => AbilityAccessors.getStackingBonus(this.ability);
this.getAllStatBonusSuppliers = () => AbilityAccessors.getAllStatBonusSuppliers(this.ability);
this.getStatSetScoreSuppliers = () => AbilityAccessors.getStatSetScoreSuppliers(this.ability);
this.getStatMaxBonusSuppliers = () => AbilityAccessors.getStatMaxBonusSuppliers(this.ability);
this.getStackingBonusSuppliers = () => AbilityAccessors.getStackingBonusSuppliers(this.ability);
//handlers
this.handleScoreChange = (value) => {
this.dispatch(characterActions.abilityScoreSet(this.getId(), AbilityScoreStatTypeEnum.BASE, value));
};
this.handleOverrideScoreChange = (value) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const minStatScore = RuleDataAccessors.getMinStatScore(ruleData);
const maxStatScore = RuleDataAccessors.getMaxStatScore(ruleData);
if (value !== null) {
value = HelperUtils.clampInt(value, minStatScore, maxStatScore);
}
this.dispatch(characterActions.abilityScoreSet(this.getId(), AbilityScoreStatTypeEnum.OVERRIDE, value));
return value;
};
this.handleOtherBonusChange = (value) => {
// TODO: lets get a why to this
const minBonusScore = -10;
const maxBonusScore = 10;
if (value !== null) {
value = HelperUtils.clampInt(value, minBonusScore, maxBonusScore);
}
this.dispatch(characterActions.abilityScoreSet(this.getId(), AbilityScoreStatTypeEnum.BONUS, value));
return value;
};
this.params = params;
this.ability = params.ability;
}
//Rule data accessors
getAbilityRuleData() {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const abilityDataLookup = RuleDataAccessors.getStatsLookup(ruleData);
return abilityDataLookup[this.getId()];
}
getCompendiumText() {
const abilityRuleData = this.getAbilityRuleData();
// TODO: we should accessor all the things?
// maybe gfs will be the time to do it?
return abilityRuleData.compendiumText;
}
}
@@ -0,0 +1,127 @@
import { ActionAccessors } from "../engine/Action";
import { ActivationRenderers } from "../engine/Activation";
import { ClassFeatureUtils, ClassUtils, Constants, EntityUtils, HelperUtils, ItemUtils, RuleDataUtils, rulesEngineSelectors, } from "../index";
import { BaseManager } from './BaseManager';
export const actionManagerMap = new Map();
export const getActionManager = (params) => {
const { action } = params;
const key = action.uniqueKey;
if (actionManagerMap.has(key)) {
const actionManager = actionManagerMap.get(key);
if (!actionManager) {
throw new Error(`ActionManager for action ${key} is null`);
}
if (actionManager.action === action) {
return actionManager;
}
}
const newActionManager = new ActionManager(params);
actionManagerMap.set(key, newActionManager);
return newActionManager;
};
export class ActionManager extends BaseManager {
constructor(params) {
super(params);
// Accessors
this.getLimitedUse = () => ActionAccessors.getLimitedUse(this.action);
this.getName = () => ActionAccessors.getName(this.action);
this.getDataOrigin = () => ActionAccessors.getDataOrigin(this.action);
this.getDataOriginType = () => ActionAccessors.getDataOriginType(this.action);
this.getMappingId = () => ActionAccessors.getMappingId(this.action);
this.getMappingEntityTypeId = () => ActionAccessors.getMappingEntityTypeId(this.action);
this.getSnippet = () => ActionAccessors.getSnippet(this.action);
this.getUniqueKey = () => ActionAccessors.getUniqueKey(this.action);
this.getProficiency = () => ActionAccessors.isProficient(this.action);
this.getDamage = () => ActionAccessors.getDamage(this.action);
this.getRequiresAttackRoll = () => ActionAccessors.requiresAttackRoll(this.action);
this.getToHit = () => ActionAccessors.getToHit(this.action);
this.getRequiresSavingThrow = () => ActionAccessors.requiresSavingThrow(this.action);
this.getSaveStatId = () => ActionAccessors.getSaveStatId(this.action);
this.getAttackSaveValue = () => ActionAccessors.getAttackSaveValue(this.action);
this.getAttackRangeId = () => ActionAccessors.getAttackRangeId(this.action);
this.isOffhand = () => ActionAccessors.isOffhand(this.action);
this.getActionTypeId = () => ActionAccessors.getActionTypeId(this.action);
this.isCustomized = () => ActionAccessors.isCustomized(this.action);
this.getAttackRange = () => ActionAccessors.getRange(this.action);
this.getAttackReach = () => ActionAccessors.getReach(this.action);
this.getActivation = () => ActionAccessors.getActivation(this.action);
// Utils
this.getClassLevel = () => ClassUtils.getLevel(this.getDataOrigin().parent); // when CLASS_FEATURE
this.getScaleValue = () => ClassFeatureUtils.getLevelScale(this.getDataOrigin().primary); // when CLASS_FEATURE
this.getItem = () => {
// WHEN ITEM
const inventoryLookup = rulesEngineSelectors.getInventoryLookup(this.state);
const itemContract = this.getDataOrigin().primary;
const itemMappingId = ItemUtils.getMappingId(itemContract);
return HelperUtils.lookupDataOrFallback(inventoryLookup, itemMappingId);
};
this.getMetaItems = () => {
const damage = this.getDamage();
const attackTypeId = this.getAttackRangeId();
let attackType = null;
if (attackTypeId) {
attackType = RuleDataUtils.getAttackTypeRangeName(attackTypeId);
}
const isOffhand = this.isOffhand();
let combinedMetaItems = [];
if (attackType) {
combinedMetaItems.push(`${attackType} Attack`);
}
if (damage.isMartialArts) {
combinedMetaItems.push('Martial Arts');
}
if (damage.value && damage.dataOrigin) {
combinedMetaItems.push(EntityUtils.getDataOriginName(damage.dataOrigin));
}
switch (this.getActionTypeId()) {
case Constants.ActionTypeEnum.WEAPON:
if (isOffhand) {
combinedMetaItems.push('Dual Wield');
}
break;
default:
// not implemented
}
if (this.isCustomized()) {
combinedMetaItems.push('Customized');
}
return combinedMetaItems;
};
this.getActionType = (rangeType) => {
const actionTypeId = this.getActionTypeId();
let actionType = actionTypeId;
switch (actionTypeId) {
case Constants.ActionTypeEnum.WEAPON:
if (rangeType === Constants.AttackTypeRangeEnum.MELEE) {
actionType = Constants.ActionTypeEnum.GENERAL;
}
break;
case Constants.ActionTypeEnum.GENERAL:
if (rangeType === Constants.AttackTypeRangeEnum.MELEE) {
actionType = Constants.ActionTypeEnum.WEAPON;
}
break;
case Constants.ActionTypeEnum.SPELL:
break;
default:
//not implemented
}
return actionType;
};
this.getRenderedActivation = () => {
let activation = this.getActivation();
return activation
? ActivationRenderers.renderActivation(activation, rulesEngineSelectors.getRuleData(this.state))
: null;
};
this.params = params;
this.action = params.action;
}
static getActionManager(key) {
const actionManager = actionManagerMap.get(key);
if (!actionManager) {
throw new Error(`ActionManager for action ${key} is null`);
}
return actionManager;
}
}
@@ -0,0 +1,86 @@
import { orderBy } from 'lodash';
import { RuleDataUtils } from "../engine/RuleData";
import { Constants } from "../index";
import { getActionManager } from "./ActionManager";
import { getActivatableManager } from "./ActivatableManager";
import { getAttackManager } from "./AttackManager";
import { MessageManager } from "./MessageManager";
import { rulesEngineSelectors } from "../selectors";
export var ActivationGroupKeyEnum;
(function (ActivationGroupKeyEnum) {
ActivationGroupKeyEnum["ACTIONS"] = "ACTIONS";
ActivationGroupKeyEnum["BONUS_ACTIONS"] = "BONUS_ACTIONS";
ActivationGroupKeyEnum["REACTIONS"] = "REACTIONS";
ActivationGroupKeyEnum["OTHER"] = "OTHER";
})(ActivationGroupKeyEnum || (ActivationGroupKeyEnum = {}));
export class ActionsManager extends MessageManager {
constructor(params) {
super(params);
this.params = params;
}
getActions() {
const actions = rulesEngineSelectors.getActions(this.state);
return actions.map((action) => getActionManager(Object.assign(Object.assign({}, this.params), { action })));
}
getCustomActions() {
const customActions = rulesEngineSelectors.getCustomActions(this.state);
return customActions.map((action) => getActionManager(Object.assign(Object.assign({}, this.params), { action })));
}
getAttacksPerActionInfo() {
return rulesEngineSelectors.getAttacksPerActionInfo(this.state);
}
getAttackGroups() {
const attackGroups = {};
const attacks = rulesEngineSelectors.getAttacks(this.state);
const attackManagers = attacks.map((attack) => getAttackManager(Object.assign(Object.assign({}, this.params), { attack })));
attackGroups[ActivationGroupKeyEnum.ACTIONS] = attackManagers.filter((manager) => manager.isActionGroup());
attackGroups[ActivationGroupKeyEnum.BONUS_ACTIONS] = attackManagers.filter((manager) => manager.isBonusActionGroup());
attackGroups[ActivationGroupKeyEnum.REACTIONS] = attackManagers.filter((manager) => manager.isReactionGroup());
attackGroups[ActivationGroupKeyEnum.OTHER] = attackManagers.filter((manager) => manager.isOtherGroup());
return attackGroups;
}
getBasicActions() {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return ruleData.basicActions;
}
getBasicActionGroups() {
const basicActionGroups = {};
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const actionBasicActions = RuleDataUtils.getActivationTypeBasicActions(Constants.ActivationTypeEnum.ACTION, ruleData);
const bonusBasicActions = RuleDataUtils.getActivationTypeBasicActions(Constants.ActivationTypeEnum.BONUS_ACTION, ruleData);
const reactionBasicActions = RuleDataUtils.getActivationTypeBasicActions(Constants.ActivationTypeEnum.REACTION, ruleData);
const otherBasicActions = [
...RuleDataUtils.getActivationTypeBasicActions(Constants.ActivationTypeEnum.NO_ACTION, ruleData),
...RuleDataUtils.getActivationTypeBasicActions(Constants.ActivationTypeEnum.MINUTE, ruleData),
...RuleDataUtils.getActivationTypeBasicActions(Constants.ActivationTypeEnum.HOUR, ruleData),
...RuleDataUtils.getActivationTypeBasicActions(Constants.ActivationTypeEnum.SPECIAL, ruleData),
];
basicActionGroups[ActivationGroupKeyEnum.ACTIONS] = ActionsManager.sortBasicActions(actionBasicActions);
basicActionGroups[ActivationGroupKeyEnum.BONUS_ACTIONS] = ActionsManager.sortBasicActions(bonusBasicActions);
basicActionGroups[ActivationGroupKeyEnum.REACTIONS] = ActionsManager.sortBasicActions(reactionBasicActions);
basicActionGroups[ActivationGroupKeyEnum.OTHER] = ActionsManager.sortBasicActions(otherBasicActions);
return basicActionGroups;
}
static sortBasicActions(basicActions) {
return orderBy(basicActions, [(basicAction) => basicAction.name], ['asc']);
}
getActionGroups() {
const actionGroups = {};
const activatables = rulesEngineSelectors.getActivatables(this.state);
const activatableManagers = activatables.map((activatable) => getActivatableManager(Object.assign(Object.assign({}, this.params), { activatable })));
actionGroups[ActivationGroupKeyEnum.ACTIONS] = activatableManagers.filter((manager) => manager.isActionGroup());
actionGroups[ActivationGroupKeyEnum.BONUS_ACTIONS] = activatableManagers.filter((manager) => manager.isBonusActionGroup());
actionGroups[ActivationGroupKeyEnum.REACTIONS] = activatableManagers.filter((manager) => manager.isReactionGroup());
actionGroups[ActivationGroupKeyEnum.OTHER] = activatableManagers.filter((manager) => manager.isOtherGroup());
return actionGroups;
}
getRitualSpellGroups() {
const ritualSpellGroups = {};
const ritualSpells = rulesEngineSelectors.getRitualSpells(this.state);
ritualSpellGroups[ActivationGroupKeyEnum.ACTIONS] = [];
ritualSpellGroups[ActivationGroupKeyEnum.BONUS_ACTIONS] = [];
ritualSpellGroups[ActivationGroupKeyEnum.REACTIONS] = [];
ritualSpellGroups[ActivationGroupKeyEnum.OTHER] = ritualSpells;
return ritualSpellGroups;
}
}
@@ -0,0 +1,56 @@
import { ActionAccessors } from "../engine/Action";
import { ActivatableTypeEnum } from "../engine/Core";
import { ActionManager, Constants } from "../index";
import { BaseManager } from './BaseManager';
export const activatableManagerMap = new Map();
export const getActivatableManager = (params) => {
const { activatable } = params;
const key = activatable.key;
if (activatableManagerMap.has(key)) {
const activatableManager = activatableManagerMap.get(key);
if (!activatableManager) {
throw new Error(`ActivatableManager for activatable ${key} is null`);
}
if (activatableManager.activatable === activatable) {
return activatableManager;
}
}
const newActivatableManager = new ActivatableManager(params);
activatableManagerMap.set(key, newActivatableManager);
return newActivatableManager;
};
export class ActivatableManager extends BaseManager {
constructor(params) {
super(params);
// Accessors
this.getSortText = () => this.activatable.sortText;
this.getType = () => this.activatable.type;
this.getActivationType = () => this.activatable.activation.activationType;
this.getEntity = () => this.activatable.entity;
this.getEntityAsActionManager = () => {
const action = this.getEntity();
return ActionManager.getActionManager(ActionAccessors.getUniqueKey(action));
};
// Utils
this.isFeature = () => {
const type = this.getType();
return type !== ActivatableTypeEnum.CLASS_SPELL && type !== ActivatableTypeEnum.CHARACTER_SPELL;
};
this.isSpell = () => {
const type = this.getType();
return type === ActivatableTypeEnum.CLASS_SPELL || type === ActivatableTypeEnum.CHARACTER_SPELL;
};
this.isAction = () => this.getType() === ActivatableTypeEnum.ACTION;
this.isActionGroup = () => this.getActivationType() === Constants.ActivationTypeEnum.ACTION && !this.isSpell();
this.isBonusActionGroup = () => this.getActivationType() === Constants.ActivationTypeEnum.BONUS_ACTION;
this.isReactionGroup = () => this.getActivationType() === Constants.ActivationTypeEnum.REACTION;
this.isOtherGroup = () => {
const activationType = this.getActivationType();
return (activationType !== Constants.ActivationTypeEnum.ACTION &&
activationType !== Constants.ActivationTypeEnum.BONUS_ACTION &&
activationType !== Constants.ActivationTypeEnum.REACTION);
};
this.params = params;
this.activatable = params.activatable;
}
}
@@ -0,0 +1,107 @@
import { ActionAccessors } from "../engine/Action";
import { ItemAccessors, WeaponTypeEnum } from "../engine/Item";
import { RuleDataUtils } from "../engine/RuleData";
import { ActionManager, Constants, rulesEngineSelectors, } from "../index";
import { BaseManager } from './BaseManager';
export const attackManagerMap = new Map();
export const getAttackManager = (params) => {
const { attack } = params;
const key = attack.key;
if (attackManagerMap.has(key)) {
const attackManager = attackManagerMap.get(key);
if (!attackManager) {
throw new Error(`AttackManager for attack ${key} is null`);
}
if (attackManager.attack === attack) {
return attackManager;
}
}
const newAttackManager = new AttackManager(params);
attackManagerMap.set(key, newAttackManager);
return newAttackManager;
};
export class AttackManager extends BaseManager {
constructor(params) {
super(params);
// Accessors
this.getType = () => this.attack.type;
this.getActivation = () => this.attack.activation;
this.getData = () => this.attack.data;
this.getKey = () => this.attack.key;
// Utils
this.isActionGroup = () => this.getActivation() === Constants.ActivationTypeEnum.ACTION;
this.isBonusActionGroup = () => this.getActivation() === Constants.ActivationTypeEnum.BONUS_ACTION;
this.isReactionGroup = () => this.getActivation() === Constants.ActivationTypeEnum.REACTION;
this.isOtherGroup = () => {
const activationType = this.getActivation();
return (activationType !== Constants.ActivationTypeEnum.ACTION &&
activationType !== Constants.ActivationTypeEnum.BONUS_ACTION &&
activationType !== Constants.ActivationTypeEnum.REACTION);
};
this.getActionManager = () => {
const action = this.getData();
return ActionManager.getActionManager(ActionAccessors.getUniqueKey(action));
};
this.getLeveledSpellManager = (spellsManager) => {
const spell = this.getData();
return spellsManager.getLeveledSpellManagerBySpell(spell);
};
this.getItem = () => {
return this.getData();
};
this.getItemActionMeta = () => {
const item = this.getItem();
const combinedMetaItems = [];
const type = ItemAccessors.getType(item);
if (type === WeaponTypeEnum.AMMUNITION) {
combinedMetaItems.push('Ammunition');
}
else {
const isHexWeapon = ItemAccessors.isHexWeapon(item);
const isPactWeapon = ItemAccessors.isPactWeapon(item);
const isDedicatedWeapon = ItemAccessors.isDedicatedWeapon(item);
if (isHexWeapon || isPactWeapon || isDedicatedWeapon) {
if (isHexWeapon) {
combinedMetaItems.push('Hex Weapon');
}
if (isPactWeapon) {
combinedMetaItems.push('Pact Weapon');
}
if (isDedicatedWeapon) {
combinedMetaItems.push('Dedicated Weapon');
}
}
else {
const attackType = ItemAccessors.getAttackType(item);
let attackTypeName = '';
if (attackType) {
attackTypeName = RuleDataUtils.getAttackTypeRangeName(attackType);
}
combinedMetaItems.push(`${attackTypeName} Weapon`);
}
}
if (ItemAccessors.isOffhand(item)) {
combinedMetaItems.push('Dual Wield');
}
if (ItemAccessors.isAdamantine(item)) {
combinedMetaItems.push('Adamantine');
}
if (ItemAccessors.isSilvered(item)) {
combinedMetaItems.push('Silvered');
}
if (ItemAccessors.isCustomized(item)) {
combinedMetaItems.push('Customized');
}
if (ItemAccessors.getMasteryName(item)) {
combinedMetaItems.push('Mastery');
}
const metaItems = ItemAccessors.getMetaItems(item);
return [...combinedMetaItems, ...metaItems];
};
this.getWeaponSpellDamageGroups = () => {
return rulesEngineSelectors.getWeaponSpellDamageGroups(this.state);
};
this.params = params;
this.attack = params.attack;
}
}
@@ -0,0 +1,68 @@
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 { AbilityAccessors } from "../engine/Ability";
import { FeatureFlagEnum } from "../engine/FeatureFlagInfo";
import { characterSelectors, featureFlagInfoSelectors, rulesEngineSelectors } from "../selectors";
import { getAbilityManager } from './AbilityManager';
import { FeatureFlagManager } from './FeatureFlagManager';
// import { BlessingManager, getBlessingManager } from './FeatureManager';
import { FeaturesManager } from './FeaturesManager';
import { transformAbilityScores } from './utils/modifierTransformers';
export class AttributesManager extends FeaturesManager {
constructor(params) {
super(params);
this.getHighestAbilityScore = (abilities) => {
let orderedAbilities = orderBy(abilities, [(ability) => ability.getTotalScore(), (ability) => ability.getLabel()], ['desc', 'asc']);
return orderedAbilities[0];
};
this.params = params;
}
getAbilities() {
return __awaiter(this, void 0, void 0, function* () {
const abilities = rulesEngineSelectors.getAbilities(this.state);
const featureFlagsManager = new FeatureFlagManager(featureFlagInfoSelectors.getFeatureFlagInfo(this.state));
const hasAccessToBlessing = featureFlagsManager.getFlag(FeatureFlagEnum.RELEASE_GATE_GFS_BLESSINGS_UI);
if (!hasAccessToBlessing) {
return abilities.map((ability) => {
return getAbilityManager(Object.assign(Object.assign({}, this.params), { ability }));
});
}
else {
// TODO: GFS this is all the wrong place
// it should just be
// const abilities = getCharacter.abilities ...
const validGlobalModifiers = this.getValidGlobalModifiers();
const baseStats = characterSelectors.getStats(this.state);
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const { generatedFeature } = transformAbilityScores(validGlobalModifiers, baseStats, ruleData);
// TODO: GFS THERE IS SOME MORE TO DO HERE
const characterFeatureManagers = yield this.getCharacterFeatures();
// TODO: GFS this should be part of the process and I should just ask for the the object here.
const myAbilityState = this.processCharacter(generatedFeature);
return abilities.map((ability) => {
var _a, _b, _c, _d, _e, _f;
const name = (_b = (_a = AbilityAccessors.getLabel(ability)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : 'The ability with no name';
return getAbilityManager(Object.assign(Object.assign({}, this.params), { ability: Object.assign(Object.assign({}, ability), { maxStatScore: (_c = myAbilityState.attributes[name].max) !== null && _c !== void 0 ? _c : null, modifier: (_d = myAbilityState.attributes[name].modifier) !== null && _d !== void 0 ? _d : null, score: (_e = myAbilityState.attributes[name].value) !== null && _e !== void 0 ? _e : null, totalScore: (_f = myAbilityState.attributes[name].value) !== null && _f !== void 0 ? _f : null }) }));
});
}
});
}
getAbilitiesWithoutBlessings() {
const abilities = rulesEngineSelectors.getAbilities(this.state);
return abilities.map((ability) => {
return getAbilityManager(Object.assign(Object.assign({}, this.params), { ability }));
});
}
getValidGlobalModifiers() {
const validGlobalModifiers = rulesEngineSelectors.getValidGlobalModifiers(this.state);
return validGlobalModifiers;
}
}
@@ -0,0 +1,52 @@
import { getCurrentRulesEngineConfig } from '../config/utils';
export class BaseManager {
constructor(params = {}) {
this.randomId = 0; // helpful for debugging
this.setState = (newState) => {
var _a;
const state = newState || ((_a = getCurrentRulesEngineConfig().store) === null || _a === void 0 ? void 0 : _a.getState()) || null;
this.state = state;
};
this.getState = () => this.state || null;
this.setDispatch = (newDispatch) => {
var _a;
const dispatch = newDispatch || ((_a = getCurrentRulesEngineConfig().store) === null || _a === void 0 ? void 0 : _a.dispatch) || null;
if (dispatch) {
this.dispatch = dispatch;
}
};
this.getDispatch = () => this.dispatch;
this.setState(params.state);
this.setDispatch(params.dispatch);
this.subscribeToStateUpdates(function init() { });
// this random id can be used to help debug if there are more than one instance
this.randomId = Math.floor(Math.random() * 1000000);
this.name = 'BaseManager';
}
updateState() {
const store = getCurrentRulesEngineConfig().store;
if (store) {
this.setState(store.getState());
}
}
subscribeToStateUpdates(callback) {
if (getCurrentRulesEngineConfig().store) {
const store = getCurrentRulesEngineConfig().store;
if (this.unsubscribe) {
this.unsubscribe();
}
this.unsubscribe = store === null || store === void 0 ? void 0 : store.subscribe(() => {
this.setState(store.getState());
if (callback) {
callback();
}
});
return this.unsubscribe;
}
return () => {
if (this.unsubscribe) {
this.unsubscribe();
}
};
}
}
@@ -0,0 +1,186 @@
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);
}
}
@@ -0,0 +1,357 @@
import { characterActions, serviceDataActions } from '../actions';
import { ConfigUtils } from '../config';
import { rulesEngineConfig } from '../config/utils';
import { ContainerAccessors } from '../engine/Container';
import { CoreUtils, CURRENCY_VALUE } from '../engine/Core';
import { DefinitionHacks } from '../engine/Definition';
import { FormatUtils } from '../engine/Format';
import { HelperUtils } from '../engine/Helper';
import { NotificationTypeEnum } from '../notification';
import { rulesEngineSelectors } from '../selectors';
import { InventoryManager } from './InventoryManager';
export class CoinManager extends InventoryManager {
constructor(params) {
super(params);
//Coin Action Handlers
this.handleTransaction = ({ coin, containerDefinitionKey, multiplier }, onSuccess, onError) => {
var _a, _b;
const adjustedCurrency = CoreUtils.getCurrencyTransactionAdjustment(multiplier, coin);
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
const container = HelperUtils.lookupDataOrFallback(containerLookup, containerDefinitionKey);
const isEmpty = Object.keys(coin).length === 0;
if (!isEmpty && container) {
const destinationEntityTypeId = ContainerAccessors.getContainerType(container);
const destinationEntityId = ContainerAccessors.getMappingId(container);
const currentCoins = this.getContainerCoin(containerDefinitionKey);
if (multiplier === 1) {
const validAddCoin = this.calculateAddCoinPayload(currentCoins, coin.cp || 0, coin.ep || 0, coin.gp || 0, coin.pp || 0, coin.sp || 0);
if (!validAddCoin) {
(_a = rulesEngineConfig === null || rulesEngineConfig === void 0 ? void 0 : rulesEngineConfig.onNotification) === null || _a === void 0 ? void 0 : _a.call(rulesEngineConfig, 'Failed to add coins', `The max amount allowed for each currency type is ${FormatUtils.renderLocaleNumber(CURRENCY_VALUE.MAX)}`, NotificationTypeEnum.ERROR);
return;
}
}
else {
const validRemoveCoin = this.calculateRemoveCoinPayload(currentCoins, coin.cp || 0, coin.ep || 0, coin.gp || 0, coin.pp || 0, coin.sp || 0);
if (!validRemoveCoin) {
(_b = rulesEngineConfig === null || rulesEngineConfig === void 0 ? void 0 : rulesEngineConfig.onNotification) === null || _b === void 0 ? void 0 : _b.call(rulesEngineConfig, 'Failed to remove coins', `The min amount allowed for each currency type is ${FormatUtils.renderLocaleNumber(CURRENCY_VALUE.MIN)}`, NotificationTypeEnum.ERROR);
return;
}
}
this.dispatch(characterActions.currencyTransactionSet(Object.assign(Object.assign({}, adjustedCurrency), { destinationEntityId: destinationEntityId, destinationEntityTypeId: destinationEntityTypeId }), () => {
typeof onSuccess === 'function' && onSuccess();
}, () => {
typeof onError === 'function' && onError();
}));
}
};
this.handleCoinSet = ({ coin, containerDefinitionKey }, onSuccess, onError) => {
const destinationEntityId = DefinitionHacks.hack__getDefinitionKeyId(containerDefinitionKey);
const destinationEntityTypeId = DefinitionHacks.hack__getDefinitionKeyType(containerDefinitionKey);
if (destinationEntityId && destinationEntityTypeId) {
if (containerDefinitionKey === this.getCharacterContainerDefinitionKey()) {
this.dispatch(characterActions.currenciesSet(coin, destinationEntityTypeId, destinationEntityId));
}
else if (containerDefinitionKey === this.getPartyEquipmentContainerDefinitionKey()) {
this.dispatch(serviceDataActions.partyCurrenciesSet(coin, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
}
};
this.handleAmountSet = ({ key, amount, containerDefinitionKey }, onSuccess, onError) => {
switch (key) {
case 'pp':
this.handlePlatinumSet({ amount, containerDefinitionKey }, onSuccess, onError);
break;
case 'gp':
this.handleGoldSet({ amount, containerDefinitionKey }, onSuccess, onError);
break;
case 'sp':
this.handleSilverSet({ amount, containerDefinitionKey }, onSuccess, onError);
break;
case 'ep':
this.handleElectrumSet({ amount, containerDefinitionKey }, onSuccess, onError);
break;
case 'cp':
this.handleCopperSet({ amount, containerDefinitionKey }, onSuccess, onError);
break;
default:
//not implemented
}
};
this.handlePlatinumSet = ({ amount, containerDefinitionKey }, onSuccess, onError) => {
const destinationEntityId = DefinitionHacks.hack__getDefinitionKeyId(containerDefinitionKey);
const destinationEntityTypeId = DefinitionHacks.hack__getDefinitionKeyType(containerDefinitionKey);
if (destinationEntityId && destinationEntityTypeId) {
if (containerDefinitionKey === this.getCharacterContainerDefinitionKey()) {
this.dispatch(characterActions.currencyPlatinumSet(amount, destinationEntityTypeId, destinationEntityId));
}
else if (containerDefinitionKey === this.getPartyEquipmentContainerDefinitionKey()) {
this.dispatch(serviceDataActions.partyCurrencyPlatinumSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
const isShared = this.isSharedContainerDefinitionKey(containerDefinitionKey);
if (isShared) {
this.dispatch(serviceDataActions.partyItemCurrencyPlatinumSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
this.dispatch(characterActions.itemCurrencyPlatinumSet(amount, destinationEntityTypeId, destinationEntityId));
}
}
}
};
this.handleGoldSet = ({ amount, containerDefinitionKey }, onSuccess, onError) => {
const destinationEntityId = DefinitionHacks.hack__getDefinitionKeyId(containerDefinitionKey);
const destinationEntityTypeId = DefinitionHacks.hack__getDefinitionKeyType(containerDefinitionKey);
if (destinationEntityId && destinationEntityTypeId) {
if (containerDefinitionKey === this.getCharacterContainerDefinitionKey()) {
this.dispatch(characterActions.currencyGoldSet(amount, destinationEntityTypeId, destinationEntityId));
}
else if (containerDefinitionKey === this.getPartyEquipmentContainerDefinitionKey()) {
this.dispatch(serviceDataActions.partyCurrencyGoldSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
const isShared = this.isSharedContainerDefinitionKey(containerDefinitionKey);
if (isShared) {
this.dispatch(serviceDataActions.partyItemCurrencyGoldSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
this.dispatch(characterActions.itemCurrencyGoldSet(amount, destinationEntityTypeId, destinationEntityId));
}
}
}
};
this.handleElectrumSet = ({ amount, containerDefinitionKey }, onSuccess, onError) => {
const destinationEntityId = DefinitionHacks.hack__getDefinitionKeyId(containerDefinitionKey);
const destinationEntityTypeId = DefinitionHacks.hack__getDefinitionKeyType(containerDefinitionKey);
if (destinationEntityId && destinationEntityTypeId) {
if (containerDefinitionKey === this.getCharacterContainerDefinitionKey()) {
this.dispatch(characterActions.currencyElectrumSet(amount, destinationEntityTypeId, destinationEntityId));
}
else if (containerDefinitionKey === this.getPartyEquipmentContainerDefinitionKey()) {
this.dispatch(serviceDataActions.partyCurrencyElectrumSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
const isShared = this.isSharedContainerDefinitionKey(containerDefinitionKey);
if (isShared) {
this.dispatch(serviceDataActions.partyItemCurrencyElectrumSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
this.dispatch(characterActions.itemCurrencyElectrumSet(amount, destinationEntityTypeId, destinationEntityId));
}
}
}
};
this.handleSilverSet = ({ amount, containerDefinitionKey }, onSuccess, onError) => {
const destinationEntityId = DefinitionHacks.hack__getDefinitionKeyId(containerDefinitionKey);
const destinationEntityTypeId = DefinitionHacks.hack__getDefinitionKeyType(containerDefinitionKey);
if (destinationEntityId && destinationEntityTypeId) {
if (containerDefinitionKey === this.getCharacterContainerDefinitionKey()) {
this.dispatch(characterActions.currencySilverSet(amount, destinationEntityTypeId, destinationEntityId));
}
else if (containerDefinitionKey === this.getPartyEquipmentContainerDefinitionKey()) {
this.dispatch(serviceDataActions.partyCurrencySilverSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
const isShared = this.isSharedContainerDefinitionKey(containerDefinitionKey);
if (isShared) {
this.dispatch(serviceDataActions.partyItemCurrencySilverSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
this.dispatch(characterActions.itemCurrencySilverSet(amount, destinationEntityTypeId, destinationEntityId));
}
}
}
};
this.handleCopperSet = ({ amount, containerDefinitionKey }, onSuccess, onError) => {
const destinationEntityId = DefinitionHacks.hack__getDefinitionKeyId(containerDefinitionKey);
const destinationEntityTypeId = DefinitionHacks.hack__getDefinitionKeyType(containerDefinitionKey);
if (destinationEntityId && destinationEntityTypeId) {
if (containerDefinitionKey === this.getCharacterContainerDefinitionKey()) {
this.dispatch(characterActions.currencyCopperSet(amount, destinationEntityTypeId, destinationEntityId));
}
else if (containerDefinitionKey === this.getPartyEquipmentContainerDefinitionKey()) {
this.dispatch(serviceDataActions.partyCurrencyCopperSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
const isShared = this.isSharedContainerDefinitionKey(containerDefinitionKey);
if (isShared) {
this.dispatch(serviceDataActions.partyItemCurrencyCopperSet(amount, destinationEntityTypeId, destinationEntityId, () => {
typeof onSuccess === 'function' && onSuccess();
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}));
}
else {
this.dispatch(characterActions.itemCurrencyCopperSet(amount, destinationEntityTypeId, destinationEntityId));
}
}
}
};
// Validators
this.canAddCoin = (coin, containerDefinitionKey) => {
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
const container = HelperUtils.lookupDataOrFallback(containerLookup, containerDefinitionKey);
const isShared = container ? ContainerAccessors.isShared(container) : false;
if (isShared && this.isSharingTurnedDeleteOnly()) {
return false;
}
return true;
};
this.canRemoveCoin = (coin, containerDefinitionKey) => {
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
const container = HelperUtils.lookupDataOrFallback(containerLookup, containerDefinitionKey);
const isShared = container ? ContainerAccessors.isShared(container) : false;
if (isShared && this.isSharingTurnedDeleteOnly()) {
let hasNegatives = Object.values(coin).some((value) => value < 0);
return !hasNegatives;
}
return true;
};
this.canUseCointainers = () => {
const rulesEngineConfig = ConfigUtils.getCurrentRulesEngineConfig();
const preferences = rulesEngineSelectors.getPreferences(this.state);
return !!((preferences === null || preferences === void 0 ? void 0 : preferences.enableContainerCurrency) && (rulesEngineConfig === null || rulesEngineConfig === void 0 ? void 0 : rulesEngineConfig.canUseCurrencyContainers));
};
//Utils
this.getTotalContainerCoinInGold = (containerDefinitionKey) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const coin = this.getContainerCoin(containerDefinitionKey);
return coin ? CoreUtils.convertTotalCoinToGold(coin, ruleData) : 0;
};
this.getContainerCoin = (containerDefinitionKey) => {
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
const container = HelperUtils.lookupDataOrFallback(containerLookup, containerDefinitionKey);
return container ? ContainerAccessors.getCoin(container) : null;
};
this.getAllPartyCoin = () => {
const partyContainers = rulesEngineSelectors.getPartyInventoryContainers(this.state);
return partyContainers.reduce((acc, container) => {
const containerCoin = ContainerAccessors.getCoin(container);
return containerCoin
? {
cp: containerCoin.cp ? acc.cp + containerCoin.cp : acc.cp,
sp: containerCoin.sp ? acc.sp + containerCoin.sp : acc.sp,
ep: containerCoin.ep ? acc.ep + containerCoin.ep : acc.ep,
gp: containerCoin.gp ? acc.gp + containerCoin.gp : acc.gp,
pp: containerCoin.pp ? acc.pp + containerCoin.pp : acc.pp,
}
: acc;
}, { cp: 0, sp: 0, ep: 0, gp: 0, pp: 0 });
};
this.getAllCharacterCoin = () => {
const characterContainers = rulesEngineSelectors.getCharacterInventoryContainers(this.state);
return characterContainers.reduce((acc, container) => {
const containerCoin = ContainerAccessors.getCoin(container);
return containerCoin
? {
cp: containerCoin.cp ? acc.cp + containerCoin.cp : acc.cp,
sp: containerCoin.sp ? acc.sp + containerCoin.sp : acc.sp,
ep: containerCoin.ep ? acc.ep + containerCoin.ep : acc.ep,
gp: containerCoin.gp ? acc.gp + containerCoin.gp : acc.gp,
pp: containerCoin.pp ? acc.pp + containerCoin.pp : acc.pp,
}
: acc;
}, { cp: 0, sp: 0, ep: 0, gp: 0, pp: 0 });
};
}
calculateAddCoinPayload(currentCoins, copper, electrum, gold, platinum, silver) {
const cp = copper !== 0 ? this.calculateModifiedMaxValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.cp, copper) : 0;
const ep = electrum !== 0 ? this.calculateModifiedMaxValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.ep, electrum) : 0;
const gp = gold !== 0 ? this.calculateModifiedMaxValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.gp, gold) : 0;
const pp = platinum !== 0 ? this.calculateModifiedMaxValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.pp, platinum) : 0;
const sp = silver !== 0 ? this.calculateModifiedMaxValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.sp, silver) : 0;
return this.calculateCoinPayload(cp, ep, gp, pp, sp);
}
calculateRemoveCoinPayload(currentCoins, copper, electrum, gold, platinum, silver) {
const cp = copper !== 0 ? this.calculateModifiedMinValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.cp, copper) : 0;
const ep = electrum !== 0 ? this.calculateModifiedMinValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.ep, electrum) : 0;
const gp = gold !== 0 ? this.calculateModifiedMinValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.gp, gold) : 0;
const pp = platinum !== 0 ? this.calculateModifiedMinValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.pp, platinum) : 0;
const sp = silver !== 0 ? this.calculateModifiedMinValue(currentCoins === null || currentCoins === void 0 ? void 0 : currentCoins.sp, silver) : 0;
return this.calculateCoinPayload(cp, ep, gp, pp, sp);
}
calculateCoinPayload(cp, ep, gp, pp, sp) {
const coinPayload = {};
if (cp === null || ep === null || gp === null || pp === null || sp === null)
return null;
if (cp !== 0)
coinPayload.cp = cp;
if (ep !== 0)
coinPayload.ep = ep;
if (gp !== 0)
coinPayload.gp = gp;
if (pp !== 0)
coinPayload.pp = pp;
if (sp !== 0)
coinPayload.sp = sp;
return coinPayload;
}
calculateModifiedMinValue(baseValue, value) {
const result = (baseValue || 0) - value;
if (result >= CURRENCY_VALUE.MIN) {
return value;
}
else {
return null;
}
}
calculateModifiedMaxValue(baseValue, value) {
const result = (baseValue || 0) + value;
if (result <= CURRENCY_VALUE.MAX) {
return value;
}
else {
return null;
}
}
}
@@ -0,0 +1,163 @@
import { sortBy } from 'lodash';
import { SourceUtils } from "../engine/Source";
import { TypeScriptUtils } from "../utils";
import { ContainerAccessors, ContainerUtils, ContainerValidators } from '../engine/Container';
import { ItemUtils, ItemAccessors } from '../engine/Item';
import { rulesEngineSelectors } from '../selectors';
import { getItemManager } from './ItemManager';
import { MessageManager } from './MessageManager';
import * as FilterUtils from './utils/Filter';
const containerManagerMap = new Map();
export const getContainerManager = (params) => {
const { container } = params;
const containerId = ContainerAccessors.getDefinitionKey(container);
if (containerManagerMap.has(containerId)) {
const containerManager = containerManagerMap.get(containerId);
if (!containerManager) {
throw new Error(`ContainerManager for container ${containerId} is null`);
}
if (containerManager.container === container) {
return containerManager;
}
}
const newContainerManger = new ContainerManager(params);
containerManagerMap.set(containerId, newContainerManger);
return newContainerManger;
};
export class ContainerManager extends MessageManager {
constructor(params) {
super(params);
this.getContainerItem = () => {
const item = ContainerAccessors.getItem(this.container);
return item ? this.generateItemManager(item) : null;
};
this.getFilteredItems = (filterOptions, items) => {
const { filterTypes = [], filterQuery = '', filterProficient = false, filterBasic = false, filterMagic = false, filterContainer = false, filterSourceCategories = [], } = filterOptions;
return items.filter((item) => {
if (filterSourceCategories.length !== 0) {
const itemSourceCategories = ItemUtils.getAllSourceCategoryIds(item);
if (!filterSourceCategories.some((id) => itemSourceCategories.includes(id))) {
return false;
}
}
if (filterProficient &&
(ItemUtils.isWeaponContract(item) || ItemUtils.isArmorContract(item)) &&
!item.proficiency) {
return false;
}
let filterType = ItemAccessors.getDefintionFilterType(item);
if (filterTypes.length !== 0 && filterType !== null && !filterTypes.includes(filterType)) {
return false;
}
let searchTags = [];
if (ItemUtils.isGearContract(item)) {
let subType = ItemAccessors.getSubType(item);
if (subType) {
searchTags.push(subType);
}
}
if (filterQuery !== '' &&
!FilterUtils.doesQueryMatchData(filterQuery, ItemAccessors.getName(item), searchTags)) {
return false;
}
if (filterMagic && !filterBasic && !ItemAccessors.isMagic(item)) {
return false;
}
if (filterBasic && !filterMagic && ItemAccessors.isMagic(item)) {
return false;
}
if (filterContainer && !filterBasic && !filterMagic && !ItemAccessors.isContainer(item)) {
return false;
}
return true;
});
};
// get the items from a list of items...
// this logic needs to be consolidated in the incentory manager via
// the filter logic
/**
* TODO IMS: filtering and sorting as params then no need to inventory to be passed
* @returns Array<ItemManager>
*/
this.getInventoryItems = (controls) => {
const { filteredInventory = null, includeContainer = false, filterOptions = {
filterTypes: [],
filterQuery: '',
filterProficient: false,
filterBasic: false,
filterMagic: false,
filterContainer: false,
filterSourceCategories: [],
}, paginationOptions = {
currentPage: 0,
pageSize: null,
}, isShoppe, } = controls || {};
const { currentPage = 0, pageSize = null } = paginationOptions || {};
const allItems = rulesEngineSelectors.getAllInventoryItems(this.state);
const items = this.overrideItems
? this.overrideItems
: ContainerUtils.getInventoryItems(this.container, filteredInventory ? filteredInventory : allItems);
if (includeContainer) {
const containerItem = ContainerAccessors.getItem(this.container);
if (containerItem) {
items.push(containerItem);
}
}
let filteredItems = filterOptions ? this.getFilteredItems(filterOptions, items) : items;
if (isShoppe) {
filteredItems = sortBy(filteredItems, [
(item) => ItemUtils.getRarityLevel(item),
(item) => ItemAccessors.getName(item),
]);
}
else {
filteredItems = ItemUtils.sortInventoryItems(filteredItems, includeContainer);
}
let paginatedItems = pageSize === null ? filteredItems : filteredItems.slice(0, (currentPage + 1) * pageSize);
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const activeSourceCategories = rulesEngineSelectors.getActiveSourceCategories(this.state);
const itemDefinitions = items
.map((item) => ItemAccessors.getDefinition(item))
.filter(TypeScriptUtils.isNotNullOrUndefined);
const sourceCategories = SourceUtils.getSimpleSourceCategoriesData(itemDefinitions, ruleData, activeSourceCategories);
return {
items: paginatedItems.map((item) => this.generateItemManager(item)),
totalItems: filteredItems.length,
sourceCategories,
};
};
this.getInventoryItemsCount = () => {
return rulesEngineSelectors.getAllInventoryItems(this.state).length;
};
// TODO should we make a new one that has coin? or rework the current one?
// generateCoinManager = () => {
// return new CoinManager()
// }
// Accessors
this.getDefinitionKey = () => ContainerAccessors.getDefinitionKey(this.container);
this.getMappingId = () => ContainerAccessors.getMappingId(this.container);
this.getName = () => ContainerAccessors.getName(this.container);
this.isShared = () => ContainerAccessors.isShared(this.container);
this.getWeightInfo = () => ContainerAccessors.getWeightInfo(this.container);
this.isEquipped = () => ContainerAccessors.isEquipped(this.container);
this.hasInfusions = () => ContainerAccessors.hasInfusions(this.container);
this.getCoin = () => ContainerAccessors.getCoin(this.container);
// Validators
this.isCharacterContainer = () => ContainerValidators.isCharacterContainer(this.container);
this.isSharedOtherContainerDefinitionKey = (otherContainerDefinitionKey) => {
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
return ContainerValidators.validateIsShared(otherContainerDefinitionKey, containerLookup);
};
this.params = params;
this.container = params.container;
this.overrideItems = (params === null || params === void 0 ? void 0 : params.overrideItems) || null;
}
generateItemManager(item) {
return getItemManager(Object.assign(Object.assign({}, this.params), { container: this, item }));
}
}
// Static Helpers ... not sure I like this but this might be better?
ContainerManager.isSharedContainerDefinitionKey = (state, containerDefinitionKey) => {
const containerLookup = rulesEngineSelectors.getContainerLookup(state);
return ContainerValidators.validateIsShared(containerDefinitionKey, containerLookup);
};
@@ -0,0 +1,184 @@
import { CreatureSimulators } from "../engine/Creature";
import { DefinitionGenerators } from "../engine/Definition";
import { DefinitionPoolUtils } from "../engine/DefinitionPool";
import { HelperUtils } from "../engine/Helper";
import { RuleDataUtils } from "../engine/RuleData";
import { VehicleSimulators } from "../engine/Vehicle";
import { TypeScriptUtils } from "../utils";
import { characterActions, serviceDataActions } from '../actions';
import { ExtraAccessors, ExtraUtils } from '../engine/Extra';
import { rulesEngineSelectors, serviceDataSelectors } from '../selectors';
import { BaseManager } from './BaseManager';
import { getVehicleManager } from './VehicleManager';
const extraManagerMap = new Map();
export const getExtraManager = (params) => {
const { extra } = params;
const extraId = ExtraAccessors.getId(extra);
if (extraManagerMap.has(extraId)) {
const extraManager = extraManagerMap.get(extraId);
if (!extraManager) {
throw new Error(`ExtraManager for extra ${extraId} is null`);
}
if (extraManager.extra === extra) {
return extraManager;
}
}
const newExtraManger = new ExtraManager(params);
extraManagerMap.set(extraId, newExtraManger);
return newExtraManger;
};
export class ExtraManager extends BaseManager {
constructor(params) {
super(params);
//Handlers
this.handleAcceptOnSuccess = (onSuccess) => {
return () => {
typeof onSuccess === 'function' && onSuccess();
};
};
this.handleRejectOnError = (onError) => {
return () => {
typeof onError === 'function' && onError();
};
};
this.handleAdd = ({ quantity, selectedGroup }, onSuccess, onError) => {
var _a;
const extraId = this.getId();
if (this.isCreature()) {
if (selectedGroup && typeof extraId === 'number') {
let names = [];
if (quantity > 1) {
for (let i = 1; i <= quantity; i++) {
names.push(`${this.getName()} ${i}`);
}
}
else {
names.push(null);
}
this.dispatch(characterActions.creatureCreate(selectedGroup, extraId, names, this.handleAcceptOnSuccess(onSuccess)));
}
}
else if (this.isVehicle()) {
if (typeof extraId === 'string') {
this.dispatch(serviceDataActions.vehicleMappingCreate(extraId, (_a = this.getName()) !== null && _a !== void 0 ? _a : 'Vehicle', this.handleAcceptOnSuccess(onSuccess)));
}
}
};
this.handleRemove = (onSuccess, onError) => {
const mappingId = this.getMappingId();
if (this.isCreature()) {
this.dispatch(characterActions.creatureRemove(mappingId));
}
else if (this.isVehicle()) {
this.getExtraData().handleRemove();
}
};
this.handleSetActive = ({ isActive }, onSuccess, onError) => {
this.dispatch(characterActions.creatureActiveSet(this.getMappingId(), isActive));
};
this.getExtraData = () => {
if (this.isCreature()) {
//this lookup is only creatures mapped to the character
const creatureLookup = rulesEngineSelectors.getCreatureLookup(this.state);
return ExtraUtils.getTypedExtra(this.extra, creatureLookup);
}
else if (this.isVehicle()) {
//this lookup is only vehicles mapped to the character
const vehicleLookup = rulesEngineSelectors.getVehicleLookup(this.state);
const vehicle = ExtraUtils.getTypedExtra(this.extra, vehicleLookup);
if (vehicle === null) {
return null;
}
return getVehicleManager({ vehicle });
}
else {
return null;
}
};
//TODO this could probably use some clean up and not have to pass in the lookup etc. maybe make the lookup as a selector? or put creatures in the definitionPool?
this.simulateExtraData = (groupId, creatureLookup) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
if (this.isCreature()) {
const definition = HelperUtils.lookupDataOrFallback(creatureLookup !== null && creatureLookup !== void 0 ? creatureLookup : {}, this.getId());
if (!definition || groupId === null) {
return null;
}
return CreatureSimulators.simulateCreature(definition, groupId, ruleData);
}
else if (this.isVehicle()) {
const definitionPool = serviceDataSelectors.getDefinitionPool(this.state);
const type = this.getType();
if (!type) {
return null;
}
const vehicleDefinitionKey = DefinitionGenerators.generateDefinitionKey(type, String(this.getId()));
const vehicleDefinition = DefinitionPoolUtils.getVehicleDefinition(vehicleDefinitionKey, definitionPool);
if (!vehicleDefinition) {
return null;
}
const vehicle = VehicleSimulators.simulateVehicle(vehicleDefinition, definitionPool, ruleData);
if (vehicle === null) {
return null;
}
return getVehicleManager({
vehicle,
});
}
else {
return null;
}
};
//Utils
this.getSourceNames = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
//TODO could/should use SourceUtils.getSourceFullNames
const sources = this.getSources();
return sources
.map((sourceMapping) => RuleDataUtils.getSourceDataInfo(sourceMapping.sourceId, ruleData))
.filter(TypeScriptUtils.isNotNullOrUndefined)
.map((sourceDataInfo) => sourceDataInfo.description)
.filter(TypeScriptUtils.isNotNullOrUndefined);
};
this.getEnvironmentNames = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
let names = [];
this.getEnvironments().forEach((id) => {
const name = RuleDataUtils.getEnvironmentName(id, ruleData);
if (name !== '') {
names.push(name);
}
});
return names;
};
//Accessors
this.isCreature = () => ExtraUtils.isCreature(this.extra);
this.isVehicle = () => ExtraUtils.isVehicle(this.extra);
this.getName = () => ExtraAccessors.getName(this.extra);
this.getSources = () => ExtraAccessors.getSources(this.extra);
this.isHomebrew = () => ExtraAccessors.isHomebrew(this.extra);
this.getMetaText = () => ExtraAccessors.getMetaText(this.extra);
this.getUniqueKey = () => ExtraAccessors.getUniqueKey(this.extra);
this.getGroupId = () => ExtraAccessors.getGroupId(this.extra);
this.getId = () => ExtraAccessors.getId(this.extra);
this.getMappingId = () => ExtraAccessors.getMappingId(this.extra);
this.getTags = () => ExtraAccessors.getTags(this.extra);
this.getEnvironments = () => ExtraAccessors.getEnvironments(this.extra);
this.getMovementNames = () => ExtraAccessors.getMovementNames(this.extra);
this.getMovementInfo = () => ExtraAccessors.getMovementInfo(this.extra);
this.getFilterTypes = () => ExtraAccessors.getFilterTypes(this.extra);
this.getSearchTags = () => ExtraAccessors.getSearchTags(this.extra);
this.getSizeInfo = () => ExtraAccessors.getSizeInfo(this.extra);
this.getType = () => ExtraAccessors.getType(this.extra);
this.getHitPointInfo = () => ExtraAccessors.getHitPointInfo(this.extra);
this.getArmorClassInfo = () => ExtraAccessors.getArmorClassInfo(this.extra);
this.getAvatarUrl = () => ExtraAccessors.getAvatarUrl(this.extra);
this.getNoteComponents = () => ExtraAccessors.getNoteComponents(this.extra);
this.getExtraType = () => ExtraAccessors.getExtraType(this.extra);
this.isCustomized = () => ExtraAccessors.isCustomized(this.extra);
this.params = params;
this.extra = params.extra;
if (!this.extra) {
throw new Error('constructed without Extra');
}
}
}
@@ -0,0 +1,337 @@
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 { groupBy } from 'lodash';
import * as ApiAdapterUtils from "../apiAdapter/utils";
import { CreatureRuleUtils } from "../engine/CreatureRule";
import { DefinitionTypeEnum } from "../engine/Definition";
import { DefinitionPoolUtils } from "../engine/DefinitionPool";
import { TypeScriptUtils } from "../utils";
import { characterActions, serviceDataActions } from '../actions';
import { CreatureAccessors, CreatureGroupFlagEnum, CreatureSimulators, DB_STRING_GROUP_SIDEKICK, DB_STRING_TAG_SIDEKICK, } from '../engine/Creature';
import { ExtraAccessors, ExtraGenerators, ExtraUtils, HACK_VEHICLE_GROUP_ID, } from '../engine/Extra';
import { RuleDataGenerators, RuleDataUtils } from '../engine/RuleData';
import { VehicleSimulators } from '../engine/Vehicle';
import { apiCreatorSelectors, rulesEngineSelectors, serviceDataSelectors } from '../selectors';
import { getExtraManager } from './ExtraManager';
import { MessageManager } from './MessageManager';
export class ExtrasManager extends MessageManager {
constructor(params) {
super(params);
//Shoppe
//TODO optimize to not make api calls when updates are called (see FeatShoppe)
this.getExtrasShoppe = ({ groupId, onSuccess, additionalConfig, }) => __awaiter(this, void 0, void 0, function* () {
var _a;
const loadAvailableMonsters = apiCreatorSelectors.makeLoadAvailableMonsters(this.state);
const loadAvailableVehicles = apiCreatorSelectors.makeLoadAvailableVehicles(this.state);
const monsterResponse = (yield loadAvailableMonsters(additionalConfig));
const vehicleResponse = yield loadAvailableVehicles(additionalConfig);
// forced to do any because of stupid typings - pulled from ExtraManagePane - fix it eventually?
const monsterResponseDefinitions = (_a = ApiAdapterUtils.getResponseData(monsterResponse)) !== null && _a !== void 0 ? _a : [];
const vehicleResponseDefinitions = ApiAdapterUtils.getResponseData(vehicleResponse);
const transformedCreatures = groupId !== null ? this.transformLoadedCreatures(monsterResponseDefinitions, groupId) : [];
if (vehicleResponseDefinitions !== null && vehicleResponseDefinitions.definitionData.length > 0) {
this.dispatch(serviceDataActions.definitionPoolAdd(vehicleResponseDefinitions.definitionData, vehicleResponseDefinitions.accessTypes));
}
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const definitionPool = serviceDataSelectors.getDefinitionPool(this.state);
const vehicleDefinitions = DefinitionPoolUtils.getTypedDefinitionList(DefinitionTypeEnum.VEHICLE, definitionPool);
let vehicles = vehicleDefinitions
.map((vehicleDef) => VehicleSimulators.simulateVehicle(vehicleDef, definitionPool, ruleData))
.filter(TypeScriptUtils.isNotNullOrUndefined)
.map((vehicle) => this.generateVehicleExtraManager(vehicle));
//TODO should this be something we store somehwere other than the shoppe?
const creatureDefinitionLookup = monsterResponseDefinitions.reduce((lookup, definition) => {
const id = definition.id;
lookup[id] = definition;
return lookup;
}, {});
const shoppeState = {
creatureDefinitionLookup,
creatureDefinitions: monsterResponseDefinitions,
creatures: transformedCreatures.map((creature) => this.generateCreatureExtraManager(creature)),
vehicles,
};
onSuccess(shoppeState);
return shoppeState;
});
//Update Shoppe with a selected group
this.updateExtrasShoppe = ({ currentShoppe, groupId, onSuccess, }) => {
const transformedCreatures = groupId !== null ? this.transformLoadedCreatures(currentShoppe.creatureDefinitions, groupId) : [];
const newShoppeState = Object.assign(Object.assign({}, currentShoppe), { creatures: transformedCreatures.map((creature) => this.generateCreatureExtraManager(creature)) });
onSuccess(newShoppeState);
return newShoppeState;
};
this.transformLoadedCreatures = (creatureDefinitions, groupId) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
if (groupId === null) {
return [];
}
return creatureDefinitions.map((definition) => CreatureSimulators.simulateCreature(definition, groupId, ruleData));
};
//TODO this could use some cleanup?
this.getCreaturesFilteredByRules = (creatures, creatureDefinitionLookup, groupId) => {
const creatureRules = rulesEngineSelectors.getCreatureRules(this.state);
const creatureDatas = creatures.map((manager) => manager.simulateExtraData(groupId, creatureDefinitionLookup));
let groupCreatures = this.getCreaturesFilteredByGroupFlags(creatureDatas, groupId);
let filteredCreatures = [];
let unmatchedCreatures = [...groupCreatures];
let hasRules = false;
creatureRules.forEach((rule) => {
if (groupId === null || !CreatureRuleUtils.isRuleGroup(rule, groupId)) {
return;
}
let testableCreatures = [...unmatchedCreatures];
unmatchedCreatures = [];
testableCreatures.forEach((creature) => {
if (CreatureRuleUtils.isValidCreature(creature, rule)) {
filteredCreatures.push(creature);
}
else {
unmatchedCreatures.push(creature);
}
});
hasRules = true;
});
return hasRules
? filteredCreatures.map((creature) => this.generateCreatureExtraManager(creature))
: groupCreatures.map((creature) => this.generateCreatureExtraManager(creature));
};
//Handlers
this.handleAcceptOnSuccess = (onSuccess) => {
return () => {
typeof onSuccess === 'function' && onSuccess();
};
};
this.handleRejectOnError = (onError) => {
return () => {
typeof onError === 'function' && onError();
};
};
/**
* @deprecated
* should use ExtraManager
*/
this.handleAdd = ({ extra, quantity, selectedGroup }, onSuccess, onError) => {
var _a;
const extraId = ExtraAccessors.getId(extra);
if (ExtraUtils.isCreature(extra)) {
if (selectedGroup && typeof extraId === 'number') {
let names = [];
if (quantity > 1) {
for (let i = 1; i <= quantity; i++) {
names.push(`${ExtraAccessors.getName(extra)} ${i}`);
}
}
else {
names.push(null);
}
this.dispatch(characterActions.creatureCreate(selectedGroup, extraId, names, this.handleAcceptOnSuccess(onSuccess)));
}
}
else if (ExtraUtils.isVehicle(extra)) {
if (typeof extraId === 'string') {
this.dispatch(serviceDataActions.vehicleMappingCreate(extraId, (_a = ExtraAccessors.getName(extra)) !== null && _a !== void 0 ? _a : 'Vehicle', this.handleAcceptOnSuccess(onSuccess)));
}
}
};
/**
* @deprecated
* should use ExtraManager
*/
this.handleRemove = ({ extra }, onSuccess, onError) => {
const mappingId = ExtraAccessors.getMappingId(extra);
if (ExtraUtils.isCreature(extra)) {
this.dispatch(characterActions.creatureRemove(mappingId));
}
else if (ExtraUtils.isVehicle(extra)) {
this.dispatch(serviceDataActions.vehicleMappingRemove(mappingId));
}
};
/**
* @deprecated
* should use ExtraManager
*/
this.handleSetActive = ({ extra, isActive }, onSuccess, onError) => {
this.dispatch(characterActions.creatureActiveSet(ExtraAccessors.getMappingId(extra), isActive));
};
//Getters?
this.getCharacterExtras = () => {
return rulesEngineSelectors.getExtras(this.state);
};
this.getCharacterExtraManagers = () => {
return this.getCharacterExtras().map((extra) => getExtraManager(Object.assign(Object.assign({}, this.params), { extra })));
};
//START HERE!!!!!!!!!!!! Extras.tsx - manager :)
// getCharacterVehicles = () => {
// }
// getAllVehicles = () => {
// }
/**
* @deprecated
* should use ExtraManager
*/
this.getExtraData = (extra) => {
if (ExtraUtils.isCreature(extra)) {
const creatureLookup = rulesEngineSelectors.getCreatureLookup(this.state);
return ExtraUtils.getTypedExtra(extra, creatureLookup);
}
else if (ExtraUtils.isVehicle(extra)) {
const vehicleLookup = rulesEngineSelectors.getVehicleLookup(this.state);
return ExtraUtils.getTypedExtra(extra, vehicleLookup);
}
else {
return null;
}
};
//UTILS
//should this be in generators?
this.generateExtrasGroupsManagers = (extraManagers) => {
return groupBy(extraManagers, (extra) => extra.getGroupId());
};
//TODO these are probably managers - or use other filter utils
this.getExtrasGroups = (filteredExtras) => {
const extras = filteredExtras !== null && filteredExtras !== void 0 ? filteredExtras : this.getCharacterExtraManagers();
return this.generateExtrasGroupsManagers(extras);
};
this.getGroupInfosForExtras = (filteredExtras) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const extraManagers = filteredExtras !== null && filteredExtras !== void 0 ? filteredExtras : this.getCharacterExtraManagers();
const extras = extraManagers.map((manager) => manager.extra);
return ExtraGenerators.generateCurrentExtraGroupInfos(extras, ruleData);
};
/**
*
* @deprecated for getGroupInfosForExtras
*/
this.getCurrentGroupInfos = (filteredExtras) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const extras = filteredExtras !== null && filteredExtras !== void 0 ? filteredExtras : this.getCharacterExtras();
return ExtraGenerators.generateCurrentExtraGroupInfos(extras, ruleData);
};
this.getGroupOptions = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
let creatureGroupOptions = RuleDataGenerators.getCreatureGroupGroupedOptions(ruleData);
return [
{
optGroupLabel: 'Creature',
options: creatureGroupOptions,
},
{
optGroupLabel: 'Object',
options: [
{
label: 'Vehicle',
value: HACK_VEHICLE_GROUP_ID,
},
],
},
];
};
this.hack__isSidekickGroup = (groupId) => {
const creatureGroupInfo = this.getCreatureGroupInfo(groupId);
return !!creatureGroupInfo && creatureGroupInfo.name === DB_STRING_GROUP_SIDEKICK;
};
//TODO move to CreatureManager or ExtraManager
this.getCreatureGroupInfo = (groupId) => {
if (groupId === null) {
return null;
}
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return RuleDataUtils.getCreatureGroupInfo(groupId, ruleData);
};
//TODO move to CreatureManager or ExtraManager
this.getCreaturesFilteredByGroupFlags = (creatures, groupId) => {
if (groupId === null) {
return creatures;
}
let groupInfo = this.getCreatureGroupInfo(groupId);
if (groupInfo && groupInfo.flagInfoLookup[CreatureGroupFlagEnum.CANNOT_BE_SWARM]) {
creatures = creatures.filter((creature) => !CreatureAccessors.isSwarm(creature));
}
if (this.hack__isSidekickGroup(groupId)) {
creatures = creatures.filter((creature) => CreatureAccessors.getTags(creature).includes(DB_STRING_TAG_SIDEKICK));
}
return creatures;
};
this.getChallengeOptions = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return RuleDataGenerators.getChallengeOptions(ruleData);
};
this.generateVehicleExtraManager = (vehicle) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return getExtraManager(Object.assign(Object.assign({}, this.params), { extra: ExtraGenerators.generateVehicleExtra(vehicle, ruleData) }));
};
this.generateCreatureExtraManager = (creature) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return getExtraManager(Object.assign(Object.assign({}, this.params), { extra: ExtraGenerators.generateCreatureExtra(creature, ruleData) }));
};
/**
* @deprecated - for VehicleManager
*/
this.generateVehicleMeta = (vehicle) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return ExtraGenerators.generateVehicleMeta(vehicle, ruleData);
};
this.generateCreatureMeta = (creature) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return ExtraGenerators.generateCreatureMeta(creature, ruleData);
};
/**
* @deprecated
* should use ExtraManager
*/
this.getSourceNames = (extra) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const sources = this.getSources(extra);
return sources
.map((sourceMapping) => RuleDataUtils.getSourceDataInfo(sourceMapping.sourceId, ruleData))
.filter(TypeScriptUtils.isNotNullOrUndefined)
.map((sourceDataInfo) => sourceDataInfo.description)
.filter(TypeScriptUtils.isNotNullOrUndefined);
};
//Accessors
/**
* @deprecated
* should use ExtraManager
*/
this.isCreature = (extra) => ExtraUtils.isCreature(extra);
/**
* @deprecated
* should use ExtraManager
*/
this.isVehicle = (extra) => ExtraUtils.isVehicle(extra);
/**
* @deprecated
* should use ExtraManager
*/
this.getName = (extra) => ExtraAccessors.getName(extra);
/**
* @deprecated
* should use ExtraManager
*/
this.getSources = (extra) => ExtraAccessors.getSources(extra);
/**
* @deprecated
* should use ExtraManager
*/
this.isHomebrew = (extra) => ExtraAccessors.isHomebrew(extra);
/**
* @deprecated
* should use ExtraManager
*/
this.getMetaText = (extra) => ExtraAccessors.getMetaText(extra);
/**
* @deprecated
* should use ExtraManager
*/
this.getUniqueKey = (extra) => ExtraAccessors.getUniqueKey(extra);
this.params = params;
}
}
@@ -0,0 +1,126 @@
import { characterActions } from "../actions";
import { ChoiceUtils } from "../engine/Choice";
import { DisplayConfigurationTypeEnum } from "../engine/Core";
import { DataOriginTypeEnum } from "../engine/DataOrigin";
import { DefinitionHacks } from "../engine/Definition";
import { FeatAccessors, FeatUtils } from "../engine/Feat";
import { PrerequisiteUtils, PrerequisiteValidators } from "../engine/Prerequisite";
import { RuleDataUtils } from "../engine/RuleData";
import { SourceUtils } from "../engine/Source";
import { rulesEngineSelectors } from "../selectors";
import { BaseManager } from './BaseManager';
export const featDefinitionMap = new Map();
const featMangerMap = new Map();
export const getFeatManager = (params) => {
const { feat } = params;
const featId = FeatAccessors.getId(feat);
if (featMangerMap.has(featId)) {
const FeatManager = featMangerMap.get(featId);
if (!FeatManager) {
throw new Error(`FeatManager for feat ${featId} is null`);
}
if (FeatManager.params.feat === feat) {
return FeatManager;
}
}
const newFeatManager = new FeatManager(params);
featMangerMap.set(featId, newFeatManager);
return newFeatManager;
};
export class FeatManager extends BaseManager {
constructor(params) {
super(params);
this.handleAcceptOnSuccess = (onSuccess) => {
return () => {
typeof onSuccess === 'function' && onSuccess();
};
};
this.handleRejectOnError = (onError) => {
return () => {
typeof onError === 'function' && onError();
};
};
this.handleAdd = (onSuccess, onError) => {
return this.dispatch(characterActions.adhocFeatCreate(this.getId()));
};
this.handleRemove = (onSuccess, onError) => {
featMangerMap.delete(this.getId());
return this.dispatch(characterActions.adhocFeatRemove(this.getId()));
};
//Accessors
this.getId = () => FeatAccessors.getId(this.feat);
this.getEntityTypeId = () => FeatAccessors.getEntityTypeId(this.feat);
this.getName = () => FeatAccessors.getName(this.feat);
this.getDataOrigin = () => FeatAccessors.getDataOrigin(this.feat);
this.getDataOriginType = () => FeatAccessors.getDataOriginType(this.feat);
this.getActions = () => FeatAccessors.getActions(this.feat);
this.getChoices = () => FeatAccessors.getChoices(this.feat);
this.getOptions = () => FeatAccessors.getOptions(this.feat);
this.getSpells = () => FeatAccessors.getSpells(this.feat);
this.getPrerequisites = () => FeatAccessors.getPrerequisites(this.feat);
this.getPrerequisiteDescription = () => FeatAccessors.getPrerequisiteDescription(this.feat);
this.getDescription = () => FeatAccessors.getDescription(this.feat);
this.getSources = () => FeatAccessors.getSources(this.feat);
this.getSnippet = () => FeatAccessors.getSnippet(this.feat);
this.isRepeatable = () => FeatAccessors.isRepeatable(this.feat);
this.getRepeatableParentId = () => FeatAccessors.getRepeatableParentId(this.feat);
this.isHomebrew = () => FeatAccessors.isHomebrew(this.feat);
this.getDefinition = () => FeatAccessors.getDefinition(this.feat);
// Possible TODO for the future: We may add data to Entity Tags to distinguish
// user-facing categories from technical-only tags.
this.getCategories = () => FeatAccessors.getCategories(this.feat);
//Utils
this.getPrerequisiteFailures = () => {
const prerequisiteData = rulesEngineSelectors.getPrerequisiteData(this.state);
return PrerequisiteUtils.getPrerequisiteGroupingFailures(this.getPrerequisites(), prerequisiteData);
};
this.getRepeatableGroupId = () => FeatUtils.getRepeatableGroupId(this.feat);
this.isRepeatableFeatParent = () => this.isRepeatable() && this.getRepeatableParentId() === null;
this.getDefinitionKey = () => {
return DefinitionHacks.hack__generateDefinitionKey(this.getEntityTypeId(), this.getId());
};
this.getHelperText = () => {
return RuleDataUtils.getBuilderHelperTextByDefinitionKeys([this.getDefinitionKey()], rulesEngineSelectors.getRuleData(this.state), DisplayConfigurationTypeEnum.FEAT);
};
//validate that a feat meets its prereqs
this.canAdd = () => {
const prerequisiteData = rulesEngineSelectors.getPrerequisiteData(this.state);
const prereqs = this.getPrerequisites();
return PrerequisiteValidators.validatePrerequisiteGrouping(prereqs, prerequisiteData);
};
this.isHiddenFeat = () => FeatUtils.isHiddenFeat(this.feat);
this.isSimulatedRepeatableFeat = () => {
return this.isRepeatable() && this.getDataOriginType() === DataOriginTypeEnum.SIMULATED;
};
this.getPrimarySourceName = () => this.isHomebrew()
? 'Homebrew'
: SourceUtils.getSourceFullNames(this.getSources(), rulesEngineSelectors.getRuleData(this.state))[0];
this.getSourceCategory = () => {
var _a;
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const sources = this.getSources();
if (sources.length === 0)
return null;
const sourceData = RuleDataUtils.getSourceDataInfo(sources[0].sourceId, ruleData);
return (_a = sourceData === null || sourceData === void 0 ? void 0 : sourceData.sourceCategory) !== null && _a !== void 0 ? _a : null;
};
this.params = params;
this.feat = params.feat;
}
static getFeatManager(id) {
const featManager = featMangerMap.get(id);
if (!featManager) {
throw new Error(`featManager for feat ${id} is null`);
}
return featManager;
}
handleChoiceChange(featId, choiceType, choiceId, optionValue) {
this.dispatch(characterActions.featChoiceSetRequest(featId, choiceType, choiceId, optionValue));
}
hasUnfinishedChoices() {
return this.getUnfinishedChoices().length > 0;
}
getUnfinishedChoices() {
return this.getChoices().filter((choice) => ChoiceUtils.isTodo(choice));
}
}
@@ -0,0 +1,26 @@
import { DEFAULT_FEATURE_FLAG_INFO, FeatureFlagInfoUtils, FeatureFlagEnum, } from '../engine/FeatureFlagInfo';
export class FeatureFlagManager {
// TODO: let make this optional and just get it with the rules engine config...
constructor(featureFlagInfo) {
this.featureFlagInfo = DEFAULT_FEATURE_FLAG_INFO;
this.FLAGS = FeatureFlagEnum;
/**
*
* @param flag the name of a flag
* @returns its current value
*
* Try featureFlagManager.getFlag(featureFlagManager.NAME_OF_FLAG)
*/
this.getFlag = (flag) => {
return FeatureFlagInfoUtils.getFeatureFlagInfoValue(flag, this.featureFlagInfo);
};
/**
*
* @param featureFlagInfo the new feature flags
*/
this.update = (featureFlagInfo) => {
this.featureFlagInfo = featureFlagInfo;
};
this.featureFlagInfo = featureFlagInfo;
}
}
@@ -0,0 +1,46 @@
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 { ApiRequests } from "../api";
export class FeatureManager {
constructor(params) {
this.handleAcceptOnSuccess = (onSuccess) => {
typeof onSuccess === 'function' && onSuccess();
};
this.handleRejectOnError = (onError) => {
typeof onError === 'function' && onError();
};
this.handleAdd = (onSuccess, onError) => __awaiter(this, void 0, void 0, function* () {
yield ApiRequests.postCharacterFeature({ featureId: this.getId() });
this.bustCache();
this.handleAcceptOnSuccess(onSuccess);
this.runSubscriptions();
});
this.handleRemove = (onSuccess, onError) => __awaiter(this, void 0, void 0, function* () {
// featureManagerMap.delete(this.getId()); //TODO GFS do we still need to do it this way
yield ApiRequests.deleteCharacterFeature({ featureId: this.getId() });
this.bustCache();
this.handleAcceptOnSuccess(onSuccess);
this.runSubscriptions();
});
//Accessors
this.getId = () => this.feature.featureId;
this.getStatements = () => this.feature.statements;
this.getFeature = () => this.feature;
// Label accessors
// TODO what are the default/standard ones?
//TODO GFS: talk about role.toLowerCase
this.getName = () => { var _a, _b; return (_b = (_a = this.feature.labels.find((label) => label.role.toLowerCase() === 'name')) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 'FEATURE WITH NO NAME'; }; //TODO GFS ENUMS for all of these
this.getDescription = () => { var _a, _b; return (_b = (_a = this.feature.labels.find((label) => label.role.toLowerCase() === 'description')) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : ''; };
this.params = params;
this.feature = params.feature;
this.runSubscriptions = params.runSubscriptions;
this.bustCache = params.bustCache;
}
}
@@ -0,0 +1,166 @@
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 { generateCharacterState } from '@dndbeyond/character-gfs';
import { ApiRequests } from "../api";
import { ApiAdapterUtils } from "../apiAdapter";
import { getCurrentRulesEngineConfig } from "../config/utils";
import { HelperUtils } from "../engine/Helper";
import { apiCreatorSelectors, rulesEngineSelectors } from "../selectors";
import { TypeScriptUtils } from "../utils";
import { FeatureManager } from './FeatureManager';
import { MessageManager } from './MessageManager';
export const featureMap = new Map();
export const subscriptionsMap = new Map();
const featureManagerMap = new Map();
export const getFeatureManager = (params) => {
const { feature } = params;
const featureId = feature.featureId; //TODO GFS Accessors?
if (featureManagerMap.has(featureId)) {
const FeatureManager = featureManagerMap.get(featureId);
if (!FeatureManager) {
throw new Error(`FeatureManager for feature ${featureId} is null`);
}
if (FeatureManager.params.feature === feature) {
return FeatureManager;
}
}
const newFeatureManager = new FeatureManager(params);
featureManagerMap.set(featureId, newFeatureManager);
return newFeatureManager;
};
// we want this cached across all instances of the FeaturesManager
let rootCharacterFeatureManagers = null; // needs
let allCharacterFeatureManagers = null; // needs
let availableFeaturesResponseData = null; // not required to bust
export class FeaturesManager extends MessageManager {
constructor(params = {}) {
super(params);
// these values are used for caching and may need to be busted when the character changes
this.randomId = 0; // helpful for debugging
this.runSubscriptions = () => {
subscriptionsMap.forEach((cb) => cb());
};
this.transformLoadedFeatures = (features) => {
return features.map((feature) => {
featureMap.set(feature.featureId, feature);
return getFeatureManager({ feature, runSubscriptions: this.runSubscriptions, bustCache: this.bustCache });
});
};
this.getAvailableFeatures = (additionalConfig) => __awaiter(this, void 0, void 0, function* () {
var _a;
if (availableFeaturesResponseData) {
return availableFeaturesResponseData;
}
const loadFeatures = apiCreatorSelectors.makeLoadAvailableFeatures(this.state);
const response = yield loadFeatures(additionalConfig);
availableFeaturesResponseData = (_a = ApiAdapterUtils.getResponseData(response)) !== null && _a !== void 0 ? _a : [];
availableFeaturesResponseData.map((feature) => {
featureMap.set(feature.featureId, feature);
});
return availableFeaturesResponseData;
});
this.getFeaturesShoppe = (additionalConfig) => __awaiter(this, void 0, void 0, function* () {
let data = yield this.getAvailableFeatures(additionalConfig);
if (data) {
return this.transformLoadedFeatures(data);
}
return [];
});
this.getAllCharacterFeatures = (rootCharacterFeatureIds) => __awaiter(this, void 0, void 0, function* () {
var _b;
if (allCharacterFeatureManagers) {
return allCharacterFeatureManagers;
}
const allFeaturesAndSubFeatureResponse = yield ApiRequests.postFeaturesAndSubfeatures({
featureIds: rootCharacterFeatureIds,
});
const allFeaturesAndSubFeatureData = (_b = ApiAdapterUtils.getResponseData(allFeaturesAndSubFeatureResponse)) !== null && _b !== void 0 ? _b : [];
allFeaturesAndSubFeatureData.map((feature) => {
featureMap.set(feature.featureId, feature);
});
const allFeaturesAndSubFeatureIds = allFeaturesAndSubFeatureData.map((feature) => feature.featureId);
allCharacterFeatureManagers = allFeaturesAndSubFeatureIds
.map((id) => {
const feature = featureMap.get(id);
return feature
? getFeatureManager({ feature, runSubscriptions: this.runSubscriptions, bustCache: this.bustCache })
: null;
})
.filter(TypeScriptUtils.isNotNullOrUndefined);
return allCharacterFeatureManagers || [];
});
this.bustCache = () => __awaiter(this, void 0, void 0, function* () {
availableFeaturesResponseData = null;
rootCharacterFeatureManagers = null;
allCharacterFeatureManagers = null;
});
this.getCharacterFeatures = () => __awaiter(this, void 0, void 0, function* () {
var _c;
if (rootCharacterFeatureManagers) {
return rootCharacterFeatureManagers;
}
const characterId = rulesEngineSelectors.getId(this.state);
const characterFeatureResponse = yield ApiRequests.getCharacterFeatures({ characterId });
const characterFeatureData = (_c = ApiAdapterUtils.getResponseData(characterFeatureResponse)) !== null && _c !== void 0 ? _c : [];
const characterFeatureIds = characterFeatureData.map((feature) => feature.id);
if (characterFeatureIds.length > 0) {
yield this.getAvailableFeatures({
params: {
category: 'blessing',
},
});
yield this.getAllCharacterFeatures(characterFeatureIds);
}
rootCharacterFeatureManagers = characterFeatureIds
.map((id) => {
const feature = featureMap.get(id);
return feature
? getFeatureManager({ feature, runSubscriptions: this.runSubscriptions, bustCache: this.bustCache })
: null;
})
.filter(TypeScriptUtils.isNotNullOrUndefined);
return rootCharacterFeatureManagers;
});
this.processCharacter = (generatedFeature) => {
featureMap.set(generatedFeature.featureId, generatedFeature);
return generateCharacterState({
rootFeatureIds: [
generatedFeature.featureId,
...((rootCharacterFeatureManagers === null || rootCharacterFeatureManagers === void 0 ? void 0 : rootCharacterFeatureManagers.map((featureManager) => featureManager.getId())) || []),
],
featureMap,
});
};
this.randomId = Math.floor(Math.random() * 1000000);
}
}
// manage subscriptions
FeaturesManager.subscribeToUpdates = ({ onUpdate, shouldInit = true }) => {
// TODO: maybe from lib.
const mapKey = HelperUtils.generateGuid();
// connect to redux for updates
let unsubscribe;
if (getCurrentRulesEngineConfig().store) {
const store = getCurrentRulesEngineConfig().store;
unsubscribe = store === null || store === void 0 ? void 0 : store.subscribe(() => {
onUpdate();
});
}
subscriptionsMap.set(mapKey, onUpdate);
if (shouldInit) {
onUpdate();
}
return () => {
subscriptionsMap.delete(mapKey);
if (unsubscribe) {
unsubscribe();
}
};
};
@@ -0,0 +1,411 @@
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 * as ApiAdapterUtils from "../apiAdapter/utils";
import { characterActions } from '../actions';
import { ContainerAccessors, ContainerGenerators, ContainerTypeEnum, ContainerUtils, ContainerValidators, } from '../engine/Container';
import { EntityTypeEnum, DefaultCharacterName } from '../engine/Core';
import { DefinitionHacks } from '../engine/Definition';
import { HelperUtils } from '../engine/Helper';
import { InfusionAccessors } from '../engine/Infusion';
import { ItemAccessors, ItemSimulators, ItemUtils, ItemValidators } from '../engine/Item';
import { ValueHacks } from '../engine/Value';
import { apiCreatorSelectors, characterSelectors, rulesEngineSelectors } from '../selectors';
import { getContainerManager } from './ContainerManager';
import { PartyManager } from './PartyManager';
export class InventoryManager extends PartyManager {
constructor(params) {
super(params);
this.transformLoadedItems = (data) => {
const globalModifiers = rulesEngineSelectors.getValidGlobalModifiers(this.state);
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const valueLookupByType = rulesEngineSelectors.getCharacterValueLookupByType(this.state);
return data
.filter((item) => item.canBeAddedToInventory)
.map((definition) => ItemSimulators.simulateItem(definition, globalModifiers, valueLookupByType, ruleData, {
simulatedItemContract: {
id: definition.id,
},
}));
};
/**
*
* Below methods are being depricated in favor of
* moving the methods to container and item managers
*
*/
this.handleAcceptOnSuccess = (isShared, onSuccess) => {
return () => {
typeof onSuccess === 'function' && onSuccess();
if (isShared) {
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}
};
};
this.handleRejectOnError = (onError, isShared) => {
return () => {
typeof onError === 'function' && onError();
};
};
//Item Action Handlers
/**
* @deprecated
*/
this.handleAdd = ({ item, amount, containerDefinitionKey }, onSuccess, onError) => {
const isShared = this.isSharedContainerDefinitionKey(containerDefinitionKey);
this.dispatch(characterActions.itemCreate(item, amount, this.handleAcceptOnSuccess(isShared, onSuccess), this.handleRejectOnError(onError), containerDefinitionKey));
};
this.handleCustomAdd = ({ customItem, containerDefinitionKey }, onSuccess, onError) => {
var _a;
const isShared = this.isSharedContainerDefinitionKey(containerDefinitionKey);
this.dispatch(characterActions.customItemCreate((_a = customItem.name) !== null && _a !== void 0 ? _a : this.getDefaultCustomItemName(), customItem.description, customItem.cost, customItem.quantity, customItem.weight, customItem.notes, containerDefinitionKey, isShared ? this.getPartyId() : null, this.handleAcceptOnSuccess(isShared, onSuccess)));
};
/**
* @deprecated
*/
this.handleMove = ({ item, containerDefinitionKey }, onSuccess, onError) => {
const definitionKeyId = DefinitionHacks.hack__getDefinitionKeyId(containerDefinitionKey);
const definitionKeyType = DefinitionHacks.hack__getDefinitionKeyType(containerDefinitionKey);
if (definitionKeyId && definitionKeyType) {
const isShared = this.isSharedContainerDefinitionKey(containerDefinitionKey) || this.isShared(item);
this.dispatch(characterActions.itemMoveSet(ItemAccessors.getMappingId(item), definitionKeyId, definitionKeyType, this.handleAcceptOnSuccess(isShared, onSuccess)));
}
};
/**
* @deprecated
*/
this.handleRemove = ({ item }, onSuccess, onError) => {
const isCustomItem = ItemAccessors.isCustom(item);
const isShared = this.isShared(item);
if (isCustomItem) {
this.dispatch(characterActions.customItemDestroy(ItemAccessors.getDefinitionId(item), ItemAccessors.getMappingId(item), isShared ? this.getPartyId() : null, this.handleAcceptOnSuccess(isShared, onSuccess)));
}
else {
const isContainer = ItemAccessors.isContainer(item);
const infusion = ItemAccessors.getInfusion(item);
this.dispatch(characterActions.itemDestroy(ItemAccessors.getMappingId(item), isContainer && infusion ? false : true, this.handleAcceptOnSuccess(isShared, onSuccess)));
}
};
/**
* @deprecated
*/
this.handleEquip = ({ item }, onSuccess, onError) => {
const characterId = rulesEngineSelectors.getId(this.state);
const isShared = this.isShared(item);
this.dispatch(characterActions.itemEquippedSet(ItemAccessors.getMappingId(item), true, characterId, EntityTypeEnum.CHARACTER, this.handleAcceptOnSuccess(isShared, onSuccess)));
};
/**
* @deprecated
*/
this.handleUnequip = ({ item }, onSuccess, onError) => {
const isShared = this.isShared(item);
this.dispatch(characterActions.itemEquippedSet(ItemAccessors.getMappingId(item), false, null, null, this.handleAcceptOnSuccess(isShared, onSuccess)));
};
this.handleAttune = ({ item }, onSuccess, onError) => {
const isShared = this.isShared(item);
this.dispatch(characterActions.itemAttuneSet(ItemAccessors.getMappingId(item), true, this.handleAcceptOnSuccess(isShared, onSuccess)));
};
this.handleUnattune = ({ item }, onSuccess, onError) => {
const isShared = this.isShared(item);
this.dispatch(characterActions.itemAttuneSet(ItemAccessors.getMappingId(item), false, this.handleAcceptOnSuccess(isShared, onSuccess)));
};
this.handleQuantitySet = ({ item, quantity }, onSuccess, onError) => {
const originalQuantity = ItemAccessors.getQuantity(item);
if (originalQuantity === quantity) {
return;
}
const isShared = this.isShared(item);
const isCustomItem = ItemAccessors.isCustom(item);
if (isCustomItem) {
this.handleCustomEdit({ item, adjustments: { quantity } }, onSuccess, onError);
}
else {
this.dispatch(characterActions.itemQuantitySet(ItemAccessors.getMappingId(item), quantity, this.handleAcceptOnSuccess(isShared, onSuccess)));
}
};
/**
* @deprecated - moved to the item
* @param param0
* @param onSuccess
* @param onError
*/
this.handleChargesSet = ({ item, used }, onSuccess, onError) => {
const isShared = this.isShared(item);
this.dispatch(characterActions.itemChargesSet(ItemAccessors.getMappingId(item), used, this.handleAcceptOnSuccess(isShared, onSuccess)));
};
this.handleCustomEdit = ({ item, adjustments }, onSuccess, onError) => {
const isShared = this.isShared(item);
let originalContract = ItemAccessors.getOriginalContract(item);
if (originalContract !== null) {
let newProperties = Object.assign(Object.assign({}, originalContract), adjustments);
this.dispatch(characterActions.customItemSet(ItemAccessors.getDefinitionId(item), newProperties, ItemAccessors.getMappingId(item), isShared ? this.getPartyId() : null, this.handleAcceptOnSuccess(isShared, onSuccess)));
}
};
this.handleCustomizationSet = ({ item, adjustmentType, value }, onSuccess, onError) => {
const isShared = this.isShared(item);
this.dispatch(characterActions.valueSet(adjustmentType, value, null, ValueHacks.hack__toString(ItemAccessors.getMappingId(item)), ValueHacks.hack__toString(ItemAccessors.getMappingEntityTypeId(item)), null, null, isShared ? this.getPartyId() : null, this.handleAcceptOnSuccess(isShared, onSuccess)));
};
this.handleCustomizationsRemove = ({ item }, onSuccess, onError) => {
const isShared = this.isShared(item);
this.dispatch(characterActions.itemCustomizationsDelete(ItemAccessors.getMappingId(item), ItemAccessors.getMappingEntityTypeId(item), isShared ? this.getPartyId() : null, this.handleAcceptOnSuccess(isShared, onSuccess)));
};
// Validators
this.canAddToContainer = (container) => {
const isShared = ContainerAccessors.isShared(container);
return !isShared || this.isSharingTurnedOn();
};
/**
* @deprecated
*/
this.canEquipUnequipItem = (item) => {
const isShared = this.isShared(item);
const canEquip = ItemAccessors.canEquip(item);
const isEquipped = ItemAccessors.isEquipped(item);
if (isShared && canEquip) {
if (this.isSharingTurnedOn() || (this.isSharingTurnedDeleteOnly() && isEquipped)) {
return isEquipped ? this.isEquippedToCurrentCharacter(item) : true;
}
return false;
}
return canEquip;
};
this.canAttuneUnattuneItem = (item) => {
const hasMaxAttunedItems = rulesEngineSelectors.hasMaxAttunedItems(this.state);
const isShared = this.isShared(item);
const canAttune = ItemAccessors.canAttune(item);
const isAttuned = ItemAccessors.isAttuned(item);
if (!isAttuned) {
if (isShared) {
if (this.isSharingTurnedDeleteOnly()) {
return false;
}
return this.isEquippedToCurrentCharacter(item) && !hasMaxAttunedItems && canAttune;
}
return !hasMaxAttunedItems && canAttune;
}
else {
if (isShared) {
return this.isEquippedToCurrentCharacter(item);
}
return true;
}
};
this.canModifyQuantity = (item) => {
const isShared = this.isShared(item);
const isStackable = ItemAccessors.isStackable(item);
if (isShared && isStackable) {
return this.isSharingTurnedOn();
}
return isStackable;
};
this.canCustomizeItem = (item) => {
const isShared = this.isShared(item);
if (isShared) {
return this.isSharingTurnedOn();
}
return true;
};
this.canUseItemCharges = (item) => {
const isShared = this.isShared(item);
if (isShared) {
return this.isSharingTurnedOn();
}
return !!ItemAccessors.getLimitedUse(item);
};
this.canRemoveItem = (item) => {
const isShared = this.isShared(item);
if (isShared) {
const infusion = ItemAccessors.getInfusion(item);
if (infusion) {
const infusionCharacterId = InfusionAccessors.getCharacterId(infusion);
if (infusionCharacterId !== rulesEngineSelectors.getId(this.state)) {
return false;
}
}
return ItemAccessors.isEquipped(item)
? this.isSharingTurnedOnOrDeleteOnly() && this.isEquippedToCurrentCharacter(item)
: this.isSharingTurnedOnOrDeleteOnly();
}
return true;
};
this.canMoveItem = (item) => {
// If you are changing this, you may want to change the canRemoveItem above
const isShared = this.isShared(item);
if (isShared) {
const infusion = ItemAccessors.getInfusion(item);
if (infusion) {
const infusionCharacterId = InfusionAccessors.getCharacterId(infusion);
if (infusionCharacterId !== rulesEngineSelectors.getId(this.state)) {
return false;
}
}
return ItemAccessors.isEquipped(item)
? this.isSharingTurnedOnOrDeleteOnly() && this.isEquippedToCurrentCharacter(item)
: this.isSharingTurnedOnOrDeleteOnly();
}
else if (this.isSharingTurnedDeleteOnly()) {
const localContainers = rulesEngineSelectors.getCharacterInventoryContainers(this.state);
return localContainers.length > 1;
}
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
return Object.keys(containerLookup).length > 1;
};
this.canShareItem = (item) => {
const isContainer = ItemAccessors.isContainer(item);
if (!isContainer) {
return false;
}
const isShared = this.isShared(item);
return !isShared && this.isSharingTurnedOn();
};
this.canClaimItem = (item) => {
const isContainer = ItemAccessors.isContainer(item);
if (!isContainer || (ItemAccessors.isEquipped(item) && !this.isEquippedToCurrentCharacter(item))) {
return false;
}
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
const isShared = this.isShared(item);
if (isShared) {
// If container is shared and has items equipped to others, then it can't be claimed
const definitionKey = DefinitionHacks.hack__generateDefinitionKey(EntityTypeEnum.ITEM, ItemAccessors.getMappingId(item));
const container = HelperUtils.lookupDataOrFallback(containerLookup, definitionKey);
if (container) {
const infusion = ItemAccessors.getInfusion(item);
if (infusion) {
const infusionCharacterId = InfusionAccessors.getCharacterId(infusion);
if (infusionCharacterId !== rulesEngineSelectors.getId(this.state)) {
return false;
}
}
const inventory = rulesEngineSelectors.getAllInventoryItems(this.state);
const inventoryItems = ContainerUtils.getInventoryItems(container, inventory);
const itemsEquippedToOthers = inventoryItems.filter((item) => ItemAccessors.isEquipped(item) && !this.isEquippedToCurrentCharacter(item));
if (itemsEquippedToOthers.length) {
return false;
}
}
return this.isSharingTurnedOnOrDeleteOnly();
}
return false;
};
this.isSharedContainerDefinitionKey = (containerDefinitionKey) => {
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
return ContainerValidators.validateIsShared(containerDefinitionKey, containerLookup);
};
this.isShared = (item) => {
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
return ItemUtils.isShared(item, containerLookup);
};
/**
* @deprecated
*/
this.isEquippedToCurrentCharacter = (item) => {
const characterId = rulesEngineSelectors.getId(this.state);
return ItemValidators.isEquippedToCurrentCharacter(item, characterId);
};
this.isInfused = (item) => {
const infusion = ItemAccessors.getInfusion(item);
return !!infusion;
};
/**
* @deprecated
*/
this.getEquippedCharacterName = (item) => {
var _a;
const equippedEntityId = ItemAccessors.getEquippedEntityId(item);
if (this.isEquippedToCurrentCharacter(item)) {
return (_a = rulesEngineSelectors.getName(this.state)) !== null && _a !== void 0 ? _a : '';
}
if (equippedEntityId) {
return this.getPartyMemberName(equippedEntityId);
}
return DefaultCharacterName;
};
this.getPartyEquipmentContainerDefinitionKey = () => {
const partyId = this.getPartyId();
if (partyId) {
return ContainerUtils.getPartyContainerDefinitionKey(partyId);
}
return null;
};
this.getCharacterContainerDefinitionKey = () => {
const characterId = rulesEngineSelectors.getId(this.state);
return ContainerUtils.getCharacterContainerDefinitionKey(characterId);
};
this.getDefaultCustomItemName = () => {
const customItems = rulesEngineSelectors.getCustomItems(this.state);
return `Custom Item ${customItems.length + 1}`;
};
this.params = params;
}
getCharacterContainers() {
const characterContainers = rulesEngineSelectors.getCharacterInventoryContainers(this.state);
return this.generateContainerManagers(characterContainers);
}
getPartyContainers() {
const partyContainers = rulesEngineSelectors.getPartyInventoryContainers(this.state);
return this.generateContainerManagers(partyContainers);
}
getAllContainers() {
const containers = rulesEngineSelectors.getInventoryContainers(this.state);
return this.generateContainerManagers(containers);
}
getContainer(containerDefinitionKey) {
const containerLookup = rulesEngineSelectors.getContainerLookup(this.state);
const container = HelperUtils.lookupDataOrFallback(containerLookup, containerDefinitionKey);
return container ? getContainerManager(Object.assign(Object.assign({}, this.params), { container })) : null;
}
generateContainerManagers(containers) {
return containers.map((container) => getContainerManager(Object.assign(Object.assign({}, this.params), { container })));
}
// TODO: this can be cached sometimes? recall when pane closes and opens? needs a cache reset function?
getInventoryShoppe({ onSuccess, additionalApiConfig, }) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
const characterId = characterSelectors.getId(this.state);
const loadItems = apiCreatorSelectors.makeLoadAvailableItems(this.state);
//set up pagination params
let paginationParams = {
page: 0,
pageSize: 1000,
};
//set up vars to hold all response data and track totalItemsCount and totalPages
const allResponseDataItems = [];
let totalItemsCount = 0;
let totalPages = 0;
//request items until all items are loaded
while (paginationParams.page <= totalPages) {
const requestConfig = Object.assign(Object.assign({}, additionalApiConfig), { params: Object.assign(Object.assign({}, paginationParams), additionalApiConfig === null || additionalApiConfig === void 0 ? void 0 : additionalApiConfig.params) });
const response = yield loadItems(requestConfig);
//on first iteration - set total items count
if (paginationParams.page === 0) {
totalItemsCount = (_b = (_a = response.data.pagination) === null || _a === void 0 ? void 0 : _a.total) !== null && _b !== void 0 ? _b : 0;
//calculate how many more calls we need to make after the first page
totalPages = Math.ceil(totalItemsCount / paginationParams.pageSize) - 1;
}
const data = ApiAdapterUtils.getResponseData(response);
//if we don't get any data back, break out of the loop
if (!data || data.length === 0) {
break;
}
//add data to the all response data array and increment the page
allResponseDataItems.push(...data);
paginationParams.page += 1;
}
let transformedItems = this.transformLoadedItems(allResponseDataItems);
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const shoppeContainer = ContainerGenerators.generateContainer(-1, ContainerTypeEnum.SHOPPE, 'Item Shoppe', transformedItems, false, null, null, characterId, null, ruleData);
const shoppeManager = getContainerManager(Object.assign(Object.assign({}, this.params), { container: shoppeContainer, overrideItems: transformedItems }));
onSuccess(shoppeManager);
return shoppeManager;
});
}
}
@@ -0,0 +1,243 @@
import { characterActions } from "../actions";
import { ContainerAccessors, ContainerTypeEnum, ContainerValidators } from "../engine/Container";
import { LimitedUseAccessors } from "../engine/LimitedUse";
import { RuleDataUtils } from "../engine/RuleData";
import { rulesEngineSelectors } from "../selectors";
import { EntityTypeEnum, DefaultCharacterName } from '../engine/Core';
import { DefinitionHacks } from '../engine/Definition';
import { ItemAccessors, ItemNotes, ItemUtils, ItemValidators } from '../engine/Item';
import { ContainerManager } from './ContainerManager';
import { PartyManager } from './PartyManager';
const itemMangerMap = new Map();
export const getItem = (itemId) => {
const itemManager = itemMangerMap.get(itemId);
if (!itemManager) {
throw new Error(`ItemManager for item ${itemId} is null`);
}
return itemManager;
};
export const getItemManager = (params) => {
const { item } = params;
const itemId = ItemAccessors.getMappingId(item);
if (itemMangerMap.has(itemId)) {
const itemManager = itemMangerMap.get(itemId);
if (!itemManager) {
throw new Error(`ItemManager for item ${itemId} is null`);
}
if (itemManager.item === item) {
return itemManager;
}
}
const newItemManger = new ItemManager(params);
itemMangerMap.set(itemId, newItemManger);
return newItemManger;
};
export class ItemManager extends PartyManager {
constructor(params) {
super(params);
// Actions
this.handleAdd = ({ amount, containerDefinitionKey }, onSuccess, onError) => {
var _a;
// const container = this.requiresContainer();
let itemDestinationContainerDefinitionKey = containerDefinitionKey;
// TODO: IS THERE A SELECTOR FOR THIS? -> move to container manager?
const containers = rulesEngineSelectors.getInventoryContainers(this.state);
// Default to the character container if none is passed
if (!itemDestinationContainerDefinitionKey) {
itemDestinationContainerDefinitionKey = ContainerAccessors.getDefinitionKey((_a = containers.find(ContainerValidators.isCharacterContainer)) !== null && _a !== void 0 ? _a : containers[0]);
}
const isShared = ContainerManager.isSharedContainerDefinitionKey(this.state, itemDestinationContainerDefinitionKey);
this.dispatch(characterActions.itemCreate(this.item, amount, this.handleAcceptOnSuccess(isShared, onSuccess), this.handleRejectOnError(onError), containerDefinitionKey));
};
this.handleRemove = (onSuccess, onError) => {
const isShared = this.isShared();
if (this.isCustom()) {
this.dispatch(characterActions.customItemDestroy(this.getDefinitionId(), this.getMappingId(), isShared ? this.getPartyId() : null, this.handleAcceptOnSuccess(isShared, onSuccess)));
}
else {
const isContainer = this.isContainer();
const infusion = this.getInfusion();
this.dispatch(characterActions.itemDestroy(this.getMappingId(), isContainer && infusion ? false : true, this.handleAcceptOnSuccess(isShared, onSuccess)));
}
};
this.handleMove = ({ containerDefinitionKey }, onSuccess, onError) => {
const definitionKeyId = DefinitionHacks.hack__getDefinitionKeyId(containerDefinitionKey);
const definitionKeyType = DefinitionHacks.hack__getDefinitionKeyType(containerDefinitionKey);
if (definitionKeyId && definitionKeyType) {
const isShared = this.requiresContainer().isSharedOtherContainerDefinitionKey(containerDefinitionKey) || this.isShared();
this.dispatch(characterActions.itemMoveSet(this.getMappingId(), definitionKeyId, definitionKeyType, this.handleAcceptOnSuccess(isShared, onSuccess)));
}
};
this.handleEquip = (onSuccess, onError) => {
const characterId = rulesEngineSelectors.getId(this.state);
this.dispatch(characterActions.itemEquippedSet(ItemAccessors.getMappingId(this.item), true, characterId, EntityTypeEnum.CHARACTER, this.handleAcceptOnSuccess(this.isShared(), onSuccess)));
};
this.handleUnequip = (onSuccess, onError) => {
this.dispatch(characterActions.itemEquippedSet(ItemAccessors.getMappingId(this.item), false, null, null, this.handleAcceptOnSuccess(this.isShared(), onSuccess)));
};
this.handleItemLimitedUseSet = (uses) => {
this.dispatch(characterActions.itemChargesSet(this.getMappingId(), uses, this.handleAcceptOnSuccess(this.isShared())));
};
this.getItem = () => this.item;
// Accessors
this.getUniqueKey = () => `${ItemAccessors.getUniqueKey(this.item)}-${this.requiresContainer().getDefinitionKey()}`;
this.isEquipped = () => ItemAccessors.isEquipped(this.item);
this.getMappingId = () => ItemAccessors.getMappingId(this.item);
this.getType = () => ItemAccessors.getType(this.item);
this.isHexWeapon = () => ItemAccessors.isHexWeapon(this.item);
this.isPactWeapon = () => ItemAccessors.isPactWeapon(this.item);
this.isDedicatedWeapon = () => ItemAccessors.isDedicatedWeapon(this.item);
this.isOffhand = () => ItemAccessors.isOffhand(this.item);
this.getBaseArmorName = () => ItemAccessors.getBaseArmorName(this.item);
this.getSubType = () => ItemAccessors.getSubType(this.item);
this.getQuantity = () => ItemAccessors.getQuantity(this.item);
this.isStackable = () => ItemAccessors.isStackable(this.item);
this.getWeight = () => ItemAccessors.getWeight(this.item);
this.getCost = () => ItemAccessors.getCost(this.item);
this.getAvatarUrl = () => ItemAccessors.getAvatarUrl(this.item);
this.isContainer = () => ItemAccessors.isContainer(this.item);
this.getName = () => ItemAccessors.getName(this.item);
this.isCustom = () => ItemAccessors.isCustom(this.item);
this.getDefinitionId = () => ItemAccessors.getDefinitionId(this.item);
this.getInfusion = () => ItemAccessors.getInfusion(this.item);
this.getContainerDefinitionKey = () => ItemAccessors.getContainerDefinitionKey(this.item);
this.hasProficiency = () => ItemAccessors.hasProficiency(this.item);
this.isMagic = () => ItemAccessors.isMagic(this.item);
this.getDefintionFilterType = () => ItemAccessors.getDefintionFilterType(this.item);
this.isPack = () => ItemAccessors.isPack(this.item);
this.getBundleSize = () => ItemAccessors.getBundleSize(this.item);
this.getAttackType = () => ItemAccessors.getAttackType(this.item);
this.isAdamantine = () => ItemAccessors.isAdamantine(this.item);
this.isSilvered = () => ItemAccessors.isSilvered(this.item);
this.isCustomized = () => ItemAccessors.isCustomized(this.item);
this.getRange = () => ItemAccessors.getRange(this.item);
this.getLongRange = () => ItemAccessors.getLongRange(this.item);
this.getReach = () => ItemAccessors.getReach(this.item);
this.getToHit = () => ItemAccessors.getToHit(this.item);
this.getDamage = () => ItemAccessors.getDamage(this.item);
this.getVersatileDamage = () => ItemAccessors.getVersatileDamage(this.item);
this.getDamageType = () => ItemAccessors.getDamageType(this.item);
this.getMasteryName = () => ItemAccessors.getMasteryName(this.item);
this.isLegacy = () => ItemAccessors.isLegacy(this.item);
this.getPrimarySourceCategoryId = () => ItemAccessors.getPrimarySourceCategoryId(this.item);
this.getSecondarySourceCategoryIds = () => ItemAccessors.getSecondarySourceCategoryIds(this.item);
this.getAllSourceCategoryIds = () => ItemUtils.getAllSourceCategoryIds(this.item);
// Validator
this.isShared = () => this.requiresContainer().isShared();
this.isArmorContract = () => ItemUtils.isArmorContract(this.item);
this.isGearContract = () => ItemUtils.isGearContract(this.item);
this.isWeaponContract = () => ItemUtils.isWeaponContract(this.item);
this.canEquipUnequipItem = () => {
const isShared = this.isShared();
const canEquip = ItemAccessors.canEquip(this.item);
const isEquipped = ItemAccessors.isEquipped(this.item);
if (isShared && canEquip) {
if (this.isSharingTurnedOn() || (this.isSharingTurnedDeleteOnly() && isEquipped)) {
return isEquipped ? this.isEquippedToCurrentCharacter() : true;
}
return false;
}
return canEquip;
};
this.isEquippedToCurrentCharacter = () => {
const characterId = rulesEngineSelectors.getId(this.state);
return ItemValidators.isEquippedToCurrentCharacter(this.item, characterId);
};
// Utils
this.getRarityLevel = () => ItemUtils.getRarityLevel(this.item);
this.generateContainerDefinitionKey = () => {
return DefinitionHacks.hack__generateDefinitionKey(ContainerTypeEnum.ITEM, this.getMappingId());
};
this.getEquippedCharacterName = () => {
var _a;
const equippedEntityId = ItemAccessors.getEquippedEntityId(this.item);
if (this.isEquippedToCurrentCharacter()) {
return (_a = rulesEngineSelectors.getName(this.state)) !== null && _a !== void 0 ? _a : '';
}
if (equippedEntityId) {
return this.getPartyMemberName(equippedEntityId);
}
return DefaultCharacterName;
};
this.getNotes = () => {
const weaponSpellDamageGroups = rulesEngineSelectors.getWeaponSpellDamageGroups(this.state);
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const abilityLookup = rulesEngineSelectors.getAbilityLookup(this.state);
const proficiencyBonus = rulesEngineSelectors.getProficiencyBonus(this.state);
return ItemNotes.getNoteComponents(this.item, weaponSpellDamageGroups, ruleData, abilityLookup, proficiencyBonus);
};
this.getMetaText = () => {
let metaItems = [];
if (this.isLegacy()) {
metaItems.push('Legacy');
}
const type = this.getType();
if (type !== null) {
metaItems.push(type);
}
if (this.isHexWeapon()) {
metaItems.push('Hex Weapon');
}
if (this.isPactWeapon()) {
metaItems.push('Pact Weapon');
}
if (this.isDedicatedWeapon()) {
metaItems.push('Dedicated Weapon');
}
if (this.isOffhand()) {
metaItems.push('Dual Wield');
}
if (this.isArmorContract()) {
let baseArmorName = this.getBaseArmorName();
if (baseArmorName) {
metaItems.push(baseArmorName);
}
}
else if (this.isGearContract()) {
let subType = this.getSubType();
if (subType) {
metaItems.push(subType);
}
}
if (this.getMasteryName()) {
metaItems.push('Mastery');
}
if (this.isEquipped() && !this.isEquippedToCurrentCharacter()) {
metaItems.push(`Equipped by ${this.getEquippedCharacterName()}`);
}
return metaItems;
};
this.getNumberUsed = () => {
const limitedUse = ItemAccessors.getLimitedUse(this.item);
if (limitedUse !== null) {
return LimitedUseAccessors.getNumberUsed(limitedUse);
}
return null;
};
this.getAttackTypeName = () => {
const attackType = this.getAttackType();
let attackTypeName = '';
if (attackType) {
attackTypeName = RuleDataUtils.getAttackTypeRangeName(attackType);
}
return attackTypeName;
};
this.getMasteryAction = () => {
const actions = rulesEngineSelectors.getActions(this.state);
return ItemUtils.getMasteryAction(this.item, actions);
};
this.item = params.item;
this.container = params.container || null;
}
/**
*
* @description This method is used to throw an error incase the manager was not created with a container
*/
requiresContainer() {
if (this.container === null) {
throw new Error('Item does not have a container');
}
return this.container;
}
}
ItemManager.getItem = getItem;
@@ -0,0 +1,329 @@
import { characterActions } from "../actions";
import { ActivationAccessors } from "../engine/Activation";
import { ClassAccessors } from "../engine/Class";
import { ConditionAccessors } from "../engine/Condition";
import { DataOriginTypeEnum } from "../engine/DataOrigin";
import { DiceRenderers } from "../engine/Dice";
import { EntityUtils } from "../engine/Entity";
import { HelperUtils } from "../engine/Helper";
import { ItemAccessors } from "../engine/Item";
import { LimitedUseAccessors } from "../engine/LimitedUse";
import { ModifierAccessors, ModifierValidators } from "../engine/Modifier";
import { RuleDataUtils } from "../engine/RuleData";
import { SpellAccessors, SpellDerivers, SpellNotes, SpellUtils, SpellValidators } from "../engine/Spell";
import { FormatUtils } from "../index";
import { rulesEngineSelectors } from "../selectors";
import { ItemManager } from './ItemManager';
import { getSpellManager } from './SpellManager';
import { SpellsManager } from './SpellsManager';
export const leveledSpellManagerMap = new Map();
export const getLeveledSpellManager = (params) => {
const { spell } = params;
const spellManager = getSpellManager(params);
const leveledKnownKey = spellManager.deriveLeveledKnownKey(SpellAccessors.getCastLevel(spell));
if (leveledSpellManagerMap.has(leveledKnownKey)) {
const leveledSpellManager = leveledSpellManagerMap.get(leveledKnownKey);
if (!leveledSpellManager) {
throw new Error(`leveledSpellManager for spell ${leveledKnownKey} is null`);
}
if (leveledSpellManager.spell === spell) {
return leveledSpellManager;
}
}
const newLeveledSpellManager = new LeveledSpellManager(params);
leveledSpellManagerMap.set(leveledKnownKey, newLeveledSpellManager);
return newLeveledSpellManager;
};
export class LeveledSpellManager extends SpellsManager {
constructor(params) {
super(params);
// Handlers
this.onSpellLimitedUseSet = (uses) => {
const mappingId = this.getMappingId();
const mappingTypeId = this.getMappingEntityTypeId();
if (mappingId !== null && mappingTypeId !== null) {
this.dispatch(characterActions.spellUseSet(mappingId, mappingTypeId, uses, this.getDataOriginType()));
}
};
this.onUse = (useSpellSlot, usePactMagicSlot, dataOriginType, uses, mappingId, mappingTypeId) => {
if (useSpellSlot) {
this.handleSpellSlotChange(1, this.getCastLevel());
}
if (usePactMagicSlot) {
this.handlePactSlotChange(1, this.getCastLevel());
}
if (uses !== null && mappingId !== null && mappingTypeId !== null) {
switch (dataOriginType) {
case DataOriginTypeEnum.ITEM:
const item = ItemManager.getItem(mappingId);
item.handleItemLimitedUseSet(uses);
break;
default:
this.onSpellLimitedUseSet(uses);
}
}
};
this.handleSpellUse = (useSpellSlot, usePactMagicSlot) => {
let dataOrigin = this.getDataOrigin();
let dataOriginType = this.getDataOriginType();
let limitedUse = this.getLimitedUse();
let uses = null;
let mappingId = null;
let mappingTypeId = null;
if (limitedUse) {
let numberUsed = LimitedUseAccessors.getNumberUsed(limitedUse);
let minConsumed = LimitedUseAccessors.getMinNumberConsumed(limitedUse);
let consumedAmount = minConsumed + this.getScaledAmount();
if (dataOriginType === DataOriginTypeEnum.ITEM) {
uses = numberUsed + consumedAmount;
mappingId = ItemAccessors.getMappingId(dataOrigin.primary);
mappingTypeId = ItemAccessors.getMappingEntityTypeId(dataOrigin.primary);
}
else {
uses = numberUsed + 1;
mappingId = this.getMappingId();
mappingTypeId = this.getMappingEntityTypeId();
}
}
if (this.onUse) {
this.onUse(useSpellSlot, usePactMagicSlot, dataOriginType, uses, mappingId, mappingTypeId);
}
};
this.handleCastClick = (bothSlotsCallback) => {
let usesSpellSlot = this.getUsesSpellSlot();
let limitedUse = this.getLimitedUse();
let spellSlotLevel = this.getSpellSlotInfo();
let pactMagicLevel = this.getPactSlotInfo();
let doesSpellSlotExist = !!spellSlotLevel && spellSlotLevel.available > 0;
let doesPactSlotExist = !!pactMagicLevel && pactMagicLevel.available > 0;
let isSpellSlotAvailable = false;
if (doesSpellSlotExist && spellSlotLevel) {
isSpellSlotAvailable = spellSlotLevel.used < spellSlotLevel.available;
}
let isPactSlotAvailable = false;
if (doesPactSlotExist && pactMagicLevel) {
isPactSlotAvailable = pactMagicLevel.used < pactMagicLevel.available;
}
if (usesSpellSlot) {
if (limitedUse) {
const spellDataOrigin = this.getDataOrigin();
const spellDataOriginType = this.getDataOriginType();
if (spellDataOriginType === DataOriginTypeEnum.CLASS_FEATURE &&
ClassAccessors.isPactMagicActive(spellDataOrigin.parent)) {
if (isPactSlotAvailable) {
this.handleSpellUse(false, true);
}
}
else {
if (isSpellSlotAvailable) {
this.handleSpellUse(true, false);
}
}
}
else {
if (doesSpellSlotExist && doesPactSlotExist) {
if (!bothSlotsCallback) {
throw new Error('No callback passed for encountering both slots');
}
bothSlotsCallback();
}
else {
if (isSpellSlotAvailable) {
this.handleSpellUse(true, false);
}
else if (isPactSlotAvailable) {
this.handleSpellUse(false, true);
}
}
}
}
else {
this.handleSpellUse(false, false);
}
};
// Accessors
this.getId = () => SpellAccessors.getId(this.spell);
this.getDataOrigin = () => SpellAccessors.getDataOrigin(this.spell);
this.getDataOriginType = () => SpellAccessors.getDataOriginType(this.spell);
this.getLimitedUse = () => SpellAccessors.getLimitedUse(this.spell);
this.getScaledAmount = () => SpellAccessors.getScaledAmount(this.spell);
this.getScaleType = () => SpellAccessors.getScaleType(this.spell);
this.getMappingId = () => SpellAccessors.getMappingId(this.spell);
this.getMappingEntityTypeId = () => SpellAccessors.getMappingEntityTypeId(this.spell);
this.getUsesSpellSlot = () => SpellAccessors.getUsesSpellSlot(this.spell);
this.getName = () => SpellAccessors.getName(this.spell);
this.asPartOfWeaponAttack = () => SpellAccessors.asPartOfWeaponAttack(this.spell);
this.getRequiresAttackRoll = () => SpellAccessors.getRequiresAttackRoll(this.spell);
this.getRequiresSavingThrow = () => SpellAccessors.getRequiresSavingThrow(this.spell);
this.getToHit = () => SpellAccessors.getToHit(this.spell);
this.getAttackSaveValue = () => SpellAccessors.getAttackSaveValue(this.spell);
this.getSaveDcAbilityKey = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return SpellUtils.getSaveDcAbilityKey(this.spell, ruleData);
};
this.getRange = () => SpellAccessors.getRange(this.spell);
this.getActivation = () => SpellAccessors.getActivation(this.spell);
this.getCastLevel = () => SpellAccessors.getCastLevel(this.spell);
this.getLevel = () => SpellAccessors.getLevel(this.spell);
this.isAtWill = () => SpellAccessors.isAtWill(this.spell);
this.getMaxUses = () => SpellAccessors.getMaxUses(this.spell);
this.getConsumedUses = () => SpellAccessors.getConsumedUses(this.spell);
this.isLimitedUseAvailableAtScaledAmount = () => SpellAccessors.isLimitedUseAvailableAtScaledAmount(this.spell);
this.getLimitedUseNumberUsed = () => {
const limitedUse = this.getLimitedUse();
if (limitedUse) {
return LimitedUseAccessors.getNumberUsed(limitedUse);
}
else {
return null;
}
};
this.getExpandedDataOriginRef = () => SpellAccessors.getExpandedDataOriginRef(this.spell);
this.isCastAsRitual = () => SpellAccessors.isCastAsRitual(this.spell);
this.getNoteComponents = () => {
var _a;
const castLevel = this.getCastLevel();
const characterLevel = (_a = rulesEngineSelectors.getExperienceInfo(this.state)) === null || _a === void 0 ? void 0 : _a.currentLevel;
const abilityLookup = rulesEngineSelectors.getAbilityLookup(this.state);
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const spellScaledAmount = this.getScaledAmount();
const proficiencyBonus = rulesEngineSelectors.getProficiencyBonus(this.state);
return SpellNotes.getNoteComponents(this.spell, castLevel, characterLevel, abilityLookup, ruleData, spellScaledAmount, proficiencyBonus);
};
this.isCustomized = () => SpellAccessors.isCustomized(this.spell);
this.getConcentration = () => SpellAccessors.getConcentration(this.spell);
this.isRitual = () => SpellAccessors.isRitual(this.spell);
this.getCharacterLevel = () => SpellAccessors.getCharacterLevel(this.spell);
this.getModifiers = () => SpellAccessors.getModifiers(this.spell);
this.getTags = () => SpellAccessors.getTags(this.spell);
this.getConditions = () => SpellAccessors.getConditions(this.spell);
this.isLegacy = () => SpellAccessors.isLegacy(this.spell);
this.isValidToUsePactSlots = () => SpellValidators.isValidToUsePactSlots(this.spell);
// Utils
this.getActivationTime = () => ActivationAccessors.getTime(this.getActivation());
this.getActivationUnit = () => ActivationAccessors.getType(this.getActivation());
this.getFriendlySubtypeName = (modifier) => { var _a; return (_a = ModifierAccessors.getFriendlySubtypeName(modifier)) !== null && _a !== void 0 ? _a : ''; };
this.getPrimaryConditionName = () => {
const spellCondition = this.getConditions()[0];
const ruleData = rulesEngineSelectors.getRuleData(this.state);
let condition = RuleDataUtils.getCondition(spellCondition.conditionId, ruleData);
if (condition) {
return ConditionAccessors.getName(condition);
}
return null;
};
this.getDamageModifiers = (modifiers) => {
return modifiers.filter((modifier) => ModifierValidators.isSpellDamageModifier(modifier));
};
this.getHitPointHealingModifiers = (modifiers) => {
return modifiers.filter((modifier) => ModifierValidators.isSpellHealingHitPointsModifier(modifier));
};
this.getTempHitPointHealingModifiers = (modifiers) => {
return modifiers.filter((modifier) => ModifierValidators.isSpellHealingTempHitPointsModifier(modifier));
};
this.getEffectModifierDiceContract = (modifier) => {
const scaleType = this.getScaleType();
const points = this.getHigherLevelEntryContracts(modifier);
const atHigherDamage = SpellDerivers.getSpellScaledAtHigher(this.spell, scaleType, points, this.getCharacterLevel(), this.getCastLevel());
return SpellUtils.getSpellFinalScaledDie(this.spell, modifier, atHigherDamage);
};
this.getEffectRenderedDiceModifier = (modifier) => {
const scaledDamageDie = this.getEffectModifierDiceContract(modifier);
return DiceRenderers.renderDice(scaledDamageDie);
};
this.getHigherLevelEntryContracts = (modifier) => {
const atHigherLevels = ModifierAccessors.getAtHigherLevels(modifier);
let points = [];
if (atHigherLevels && atHigherLevels.points !== null) {
points = atHigherLevels.points;
}
return points;
};
this.getUsedSpellSlotLevelAmount = (changeAmount, spellSlots) => {
const foundSlotLevel = spellSlots.find((spellSlot) => spellSlot.level === this.getCastLevel());
if (!foundSlotLevel) {
return null;
}
const usedAmount = foundSlotLevel.used + changeAmount;
const maxAmount = foundSlotLevel.available;
return HelperUtils.clampInt(usedAmount, 0, maxAmount);
};
this.getSpellSlotInfo = () => {
const spellCasterInfo = rulesEngineSelectors.getSpellCasterInfo(this.state);
const { spellSlots } = spellCasterInfo;
let spellSlotInfo = spellSlots.find((spellSlotGroup) => spellSlotGroup.level === this.getCastLevel());
return spellSlotInfo ? spellSlotInfo : null;
};
this.getPactSlotInfo = () => {
const spellCasterInfo = rulesEngineSelectors.getSpellCasterInfo(this.state);
const { pactMagicSlots } = spellCasterInfo;
let spellSlotInfo = pactMagicSlots.find((pactSlotGroup) => pactSlotGroup.level === this.getCastLevel());
return spellSlotInfo ? spellSlotInfo : null;
};
this.getExpandedDataOriginRefName = () => {
const expandedDataOriginRef = this.getExpandedDataOriginRef();
if (expandedDataOriginRef) {
const dataOriginRefData = rulesEngineSelectors.getDataOriginRefData(this.state);
return EntityUtils.getDataOriginRefName(expandedDataOriginRef, dataOriginRefData);
}
else {
return null;
}
};
this.getDataOriginName = () => EntityUtils.getDataOriginName(this.getDataOrigin());
this.getClassId = () => {
if (this.getDataOriginType() === DataOriginTypeEnum.CLASS) {
return ClassAccessors.getMappingId(this.getDataOrigin().primary);
}
else {
return null;
}
};
this.getSchoolName = () => SpellAccessors.getSchool(this.spell);
this.getSchoolIconSlug = () => {
const school = this.getSchoolName();
return FormatUtils.slugify(school);
};
this.getActionMetaItems = () => {
const metaItems = [];
metaItems.push(FormatUtils.renderSpellLevelName(this.getLevel()));
const spellDataOrigin = this.getDataOrigin();
if (spellDataOrigin) {
const dataOriginName = this.getDataOriginName();
metaItems.push(dataOriginName);
}
let expandedDataOriginRefName = this.getExpandedDataOriginRefName();
if (expandedDataOriginRefName) {
metaItems.push(expandedDataOriginRefName);
}
if (this.getConcentration()) {
metaItems.push('Concentration');
}
if (this.isCustomized()) {
metaItems.push('Customized');
}
return metaItems;
};
this.deriveKnownKey = () => {
return SpellDerivers.deriveKnownKey(this.spell);
};
this.deriveLeveledKnownKey = (castLevel) => {
return SpellDerivers.deriveLeveledKnownKey(this.spell, castLevel);
};
this.params = params;
this.spell = params.spell;
}
static getLeveledSpellManager(knownLevelKey) {
const leveledSpellManager = leveledSpellManagerMap.get(knownLevelKey);
if (!leveledSpellManager) {
throw new Error(`SpellManager for spell ${knownLevelKey} is null`);
}
return leveledSpellManager;
}
static makeKnownKey(mappingId, mappingEntityTypeId) {
return SpellUtils.makeKnownKey(mappingId, mappingEntityTypeId);
}
static makeLeveledKnownKey(mappingId, mappingEntityTypeId, castLevel) {
return SpellUtils.makeLeveledKnownKey(mappingId, mappingEntityTypeId, castLevel);
}
}
@@ -0,0 +1,84 @@
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { getCurrentRulesEngineConfig } from '../config/utils';
import { generateGuid } from '../engine/Helper/utils';
import { characterSelectors } from '../selectors';
import { BaseManager } from './BaseManager';
const messageManagerGuid = generateGuid();
let isSubscribed = false;
let listeningFor = {};
export class MessageManager extends BaseManager {
constructor(params = {}) {
super(params);
this.setMessageBroker = (newMessageBroker) => {
const messageBroker = newMessageBroker || getCurrentRulesEngineConfig().messageBroker;
if (!messageBroker) {
if (process.env.NODE_ENV === 'development') {
console.warn('[MessageManager]: The message manager was not able to set the messageBroker');
}
this.messageBroker = null;
return;
}
this.messageBroker = messageBroker;
if (!isSubscribed) {
this.subscribeToMessages();
}
isSubscribed = true;
};
this.getMessageBroker = () => {
if (this.messageBroker === null) {
// This class may have been instantiated before the message broker is ready.
// If so, try getting it from the rules engine config, which may have updated.
this.setMessageBroker();
}
return this.messageBroker;
};
this.sendMessage = (_a) => {
var _b, _c, _d;
var { data, entityId, entityType, eventType, messageScope, messageTarget, persist } = _a, rest = __rest(_a, ["data", "entityId", "entityType", "eventType", "messageScope", "messageTarget", "persist"]);
if (!eventType) {
throw new Error(`[MessageManager]: The message manager send method needs a valid eventType - ${eventType}`);
}
(_b = this.getMessageBroker()) === null || _b === void 0 ? void 0 : _b.dispatch(Object.assign({ data: Object.assign({ messageManagerGuid: this.messageManagerGuid, shouldUpdate: false }, data), entityId: entityId || this.characterId, entityType: entityType || 'character',
//pending, fulfilled, or rejected
eventType, persist: persist || false, messageScope: messageScope || 'gameId', messageTarget: messageTarget ||
((_c = characterSelectors.getCampaign(this.state)) === null || _c === void 0 ? void 0 : _c.id.toString()) ||
((_d = this.getMessageBroker()) === null || _d === void 0 ? void 0 : _d.gameId) }, rest));
};
this.subscribeToMessages = () => {
var _a;
(_a = this.messageBroker) === null || _a === void 0 ? void 0 : _a.subscribe((msg) => {
try {
const { messageManagerGuid, shouldUpdate } = msg === null || msg === void 0 ? void 0 : msg.data;
listeningFor[msg.eventType](shouldUpdate || this.messageManagerGuid !== messageManagerGuid);
}
catch (error) {
if (process.env.NODE_ENV === 'development') {
console.warn(`[MessageManager]: The message manager recieved an event it does not handle(${msg.eventType})`);
}
}
});
};
this.addSubscriptions = (newSubs) => {
listeningFor = Object.assign(Object.assign({}, listeningFor), newSubs);
};
this.EVENT_TYPES = {
//pending, fulfilled, or rejected
//TODO: add more verbose actions? chat with team about it?
//if we add more here - we should strongly consider changing ITEM_SHARED_FULFILLED name to something related to "update"
ITEM_SHARED_FULFILLED: 'character-sheet/item-shared/fulfilled',
};
this.setMessageBroker(params.messageBroker);
this.messageManagerGuid = messageManagerGuid;
this.characterId = this.state ? characterSelectors.getId(this.state).toString() : '';
}
}
@@ -0,0 +1,56 @@
import { serviceDataActions } from '../actions';
import { CampaignAccessors, CampaignUtils } from '../engine/Campaign';
import { PartyInventorySharingStateEnum } from '../engine/Campaign/constants';
import { serviceDataSelectors } from '../selectors';
import { MessageManager } from './MessageManager';
export class PartyManager extends MessageManager {
constructor(params) {
super(params);
// Data Requests
this.updateSharedInventory = (shouldUpdate) => {
if (shouldUpdate) {
this.dispatch(serviceDataActions.partyInventoryRequest());
}
};
this.handleAcceptOnSuccess = (isShared, onSuccess) => {
return () => {
typeof onSuccess === 'function' && onSuccess();
if (isShared) {
const data = {};
const eventType = this.EVENT_TYPES.ITEM_SHARED_FULFILLED;
this.sendMessage({ data, eventType });
}
};
};
this.handleRejectOnError = (onError, isShared) => {
return () => {
typeof onError === 'function' && onError();
};
};
// Accessors
this.getSharingState = () => {
var _a;
const partyInfo = serviceDataSelectors.getPartyInfo(this.state);
return (_a = (partyInfo && CampaignAccessors.getSharingState(partyInfo))) !== null && _a !== void 0 ? _a : PartyInventorySharingStateEnum.OFF; // if we get null return off
};
this.getPartyId = () => {
const partyInfo = serviceDataSelectors.getPartyInfo(this.state);
return partyInfo ? CampaignAccessors.getId(partyInfo) : null;
};
this.getPartyMemberName = (equippedEntityId) => {
const partyInfo = serviceDataSelectors.getPartyInfo(this.state);
if (partyInfo) {
return CampaignUtils.getCharacterName(partyInfo, equippedEntityId);
}
return '';
};
// Validators
this.isSharingTurnedOn = () => this.getSharingState() === PartyInventorySharingStateEnum.ON;
this.isSharingTurnedOff = () => this.getSharingState() === PartyInventorySharingStateEnum.OFF;
this.isSharingTurnedDeleteOnly = () => this.getSharingState() === PartyInventorySharingStateEnum.DELETE_ONLY;
this.isSharingTurnedOnOrDeleteOnly = () => CampaignUtils.isSharingStateActive(this.getSharingState());
this.addSubscriptions({
[this.EVENT_TYPES.ITEM_SHARED_FULFILLED]: this.updateSharedInventory,
});
}
}
@@ -0,0 +1,120 @@
import { characterActions } from "../actions";
import { ClassAccessors } from "../engine/Class";
import { HelperUtils } from "../engine/Helper";
import { RuleDataAccessors } from "../engine/RuleData";
import { SpellAccessors, SpellDerivers, SpellUtils } from "../engine/Spell";
import { rulesEngineSelectors } from "../selectors";
import { TypeScriptUtils } from "../utils";
import { BaseManager } from './BaseManager';
export const spellManagerMap = new Map();
export const getSpellManager = (params) => {
const { spell, classId } = params;
const spellId = SpellDerivers.deriveKnownKey(spell);
let spellKey = spellId;
if (classId) {
spellKey = `${spellId}-${classId}`;
}
if (spellManagerMap.has(spellKey)) {
const spellManager = spellManagerMap.get(spellKey);
if (!spellManager) {
throw new Error(`SpellManager for spell ${spellId} is null`);
}
if (spellManager.spell === spell) {
return spellManager;
}
else {
spellManager.updateSpell(params);
spellManagerMap.set(spellKey, spellManager);
return spellManager;
}
}
const newSpellManager = new SpellManager(params);
spellManagerMap.set(spellKey, newSpellManager);
return newSpellManager;
};
export class SpellManager extends BaseManager {
constructor(params) {
super(params);
// Handlers
this.handlePrepare = ({ characterClassId }, onSuccess, onError) => {
this.bustCache();
this.dispatch(characterActions.spellPreparedSet(this.spell, characterClassId, true, onSuccess));
};
this.handleUnprepare = ({ characterClassId }, onSuccess, onError) => {
this.bustCache();
this.dispatch(characterActions.spellPreparedSet(this.spell, characterClassId, false, onSuccess));
};
this.handleRemove = ({ characterClassId }, onSuccess, onError) => {
this.bustCache();
this.dispatch(characterActions.spellRemove(this.spell, characterClassId, onSuccess));
};
this.handleAdd = ({ characterClassId }, onSuccess, onError) => {
this.bustCache();
this.dispatch(characterActions.spellCreate(this.spell, characterClassId, onSuccess));
};
// Accessors
this.getLevel = () => SpellAccessors.getLevel(this.spell);
this.getName = () => SpellAccessors.getName(this.spell);
this.getExpandedDataOriginRef = () => SpellAccessors.getExpandedDataOriginRef(this.spell);
this.getUniqueKey = () => SpellAccessors.getUniqueKey(this.spell);
this.getId = () => SpellAccessors.getId(this.spell);
this.getDataOrigin = () => SpellAccessors.getDataOrigin(this.spell);
this.isPrepared = () => SpellAccessors.isPrepared(this.spell);
this.isAlwaysPrepared = () => SpellAccessors.isAlwaysPrepared(this.spell);
this.isCantrip = () => SpellAccessors.isCantrip(this.spell);
this.canRemove = () => SpellAccessors.canRemove(this.spell);
this.canPrepare = () => SpellAccessors.canPrepare(this.spell);
this.canAdd = () => SpellAccessors.canAdd(this.spell);
this.isCustomized = () => SpellAccessors.isCustomized(this.spell);
this.getConcentration = () => SpellAccessors.getConcentration(this.spell);
this.isRitual = () => SpellAccessors.isRitual(this.spell);
this.isLegacy = () => SpellAccessors.isLegacy(this.spell);
this.getSources = () => SpellAccessors.getSources(this.spell);
this.getDefinition = () => SpellAccessors.getDefinition(this.spell);
this.getPrimarySourceCategoryId = () => {
var _a, _b;
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const sources = this.getSources()
.map((source) => HelperUtils.lookupDataOrFallback(RuleDataAccessors.getSourceDataLookup(ruleData), source.sourceId))
.filter(TypeScriptUtils.isNotNullOrUndefined);
return ((_b = (_a = sources[0]) === null || _a === void 0 ? void 0 : _a.sourceCategory) === null || _b === void 0 ? void 0 : _b.id) || 0;
};
/*
Temporary solution, until Spells and Spell Managers dependence get fixed
*/
this.getSpell = () => this.spell;
// Utils
this.isSpellbookCaster = () => {
return ClassAccessors.isSpellbookSpellcaster(this.getDataOrigin().primary);
};
this.deriveKnownKey = () => {
return SpellDerivers.deriveKnownKey(this.spell);
};
this.deriveLeveledKnownKey = (castLevel) => {
return SpellDerivers.deriveLeveledKnownKey(this.spell, castLevel);
};
this.params = params;
this.spell = params.spell;
this.bustCache = params.bustCache || function NOOP() { };
this.name = 'SpellManager';
}
updateSpell(params) {
this.params = params;
this.spell = params.spell;
this.bustCache = params.bustCache || function NOOP() { };
this.name = 'SpellManager';
}
static getSpellManager(knownKey) {
const spellManager = spellManagerMap.get(knownKey);
if (!spellManager) {
throw new Error(`SpellManager for spell ${knownKey} is null`);
}
return spellManager;
}
static makeKnownKey(mappingId, mappingEntityTypeId) {
return SpellUtils.makeKnownKey(mappingId, mappingEntityTypeId);
}
static makeLeveledKnownKey(mappingId, mappingEntityTypeId, castLevel) {
return SpellUtils.makeLeveledKnownKey(mappingId, mappingEntityTypeId, castLevel);
}
}
@@ -0,0 +1,256 @@
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 { characterActions, serviceDataActions } from "../actions";
import { ApiRequestHelpers } from "../api";
import { ApiAdapterUtils } from "../apiAdapter";
import { ClassAccessors } from "../engine/Class";
import { HelperUtils } from "../engine/Helper";
import { SourceUtils } from "../engine/Source";
import { SpellDerivers, SpellSimulators } from "../engine/Spell";
import { apiCreatorSelectors, rulesEngineSelectors } from "../selectors";
import { getOverallSpellInfo } from "../selectors/composite/engine";
import { TypeScriptUtils } from "../utils";
import { getLeveledSpellManager, LeveledSpellManager } from './LeveledSpellManager';
import { MessageManager } from './MessageManager';
import { getSpellManager, SpellManager } from './SpellManager';
let spellShoppe = null;
let apiResponses = {};
let spellsManager = null;
// TODO: this seems like a good pattern might want to implement else where
export function getSpellsManager(params) {
if (spellsManager) {
return spellsManager;
}
spellsManager = new SpellsManager(params);
return spellsManager;
}
export class SpellsManager extends MessageManager {
constructor(params) {
super(params);
this.handleSpellSlotSet = (used, castLevel) => {
const spellLevelSpellSlotRequestsDataKey = ApiRequestHelpers.getSpellLevelSpellSlotRequestsDataKey(castLevel);
if (spellLevelSpellSlotRequestsDataKey !== null) {
this.dispatch(characterActions.spellLevelSpellSlotsSet({
[spellLevelSpellSlotRequestsDataKey]: used,
}));
}
};
this.handleSpellSlotChange = (changeAmount, castLevel) => {
const spellSlots = rulesEngineSelectors.getSpellSlots(this.state);
const usedAmount = this.getUsedSpellSlotLevelAmount(changeAmount, spellSlots, castLevel);
if (usedAmount !== null) {
this.handleSpellSlotSet(usedAmount, castLevel);
}
};
this.handlePactSlotSet = (used, castLevel) => {
const spellLevelPactMagicRequestsDataKey = ApiRequestHelpers.getSpellLevelPactMagicRequestsDataKey(castLevel);
if (spellLevelPactMagicRequestsDataKey !== null) {
this.dispatch(characterActions.spellLevelPactMagicSlotsSet({
[spellLevelPactMagicRequestsDataKey]: used,
}));
}
};
this.handlePactSlotChange = (changeAmount, castLevel) => {
const pactMagicSlots = rulesEngineSelectors.getPactMagicSlots(this.state);
const usedAmount = this.getUsedSpellSlotLevelAmount(changeAmount, pactMagicSlots, castLevel);
if (usedAmount !== null) {
this.handlePactSlotSet(usedAmount, castLevel);
}
};
this.getUsedSpellSlotLevelAmount = (changeAmount, spellSlots, castLevel) => {
const foundSlotLevel = spellSlots.find((spellSlot) => spellSlot.level === castLevel);
if (!foundSlotLevel) {
return null;
}
const usedAmount = foundSlotLevel.used + changeAmount;
const maxAmount = foundSlotLevel.available;
return HelperUtils.clampInt(usedAmount, 0, maxAmount);
};
this.handleAlwaysKnownLoad = (spells, classId) => {
this.dispatch(serviceDataActions.classAlwaysKnownSpellsSet(spells, classId));
};
this.transformLoadedSpells = (data) => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const overallSpellInfo = getOverallSpellInfo(this.state);
return data.map((definition) => SpellSimulators.simulateSpell(definition, overallSpellInfo, ruleData, null, null));
};
this.params = params;
this.name = 'SpellsManager';
}
bustCache() {
spellShoppe = null;
}
getLevelSpells() {
const levelSpells = rulesEngineSelectors.getLevelSpells(this.state);
return levelSpells.map((spells) => {
return spells.map((spell) => getLeveledSpellManager(Object.assign(Object.assign({}, this.params), { spell })));
});
}
getSpellCasterInfo() {
return rulesEngineSelectors.getSpellCasterInfo(this.state);
}
getClassName(charClass) {
const name = ClassAccessors.getName(charClass);
return name ? name : '';
}
getCharacterClassId(classId) {
const charClasses = rulesEngineSelectors.getClasses(this.state);
const charClassFound = charClasses.find((clazz) => {
return ClassAccessors.getActiveId(clazz) === classId;
});
if (charClassFound) {
return ClassAccessors.getMappingId(charClassFound);
}
else {
return null;
}
}
getLeveledSpell(knownLevelKey) {
const leveledSpellManager = LeveledSpellManager.getLeveledSpellManager(knownLevelKey);
if (!leveledSpellManager) {
throw new Error(`leveledSpellManager for spell ${knownLevelKey} is null`);
}
return leveledSpellManager;
}
getLeveledSpellManagerBySpell(spell) {
const spellCasterInfo = rulesEngineSelectors.getSpellCasterInfo(this.state);
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const minCastLevel = SpellDerivers.getMinCastLevel(spell, spellCasterInfo, ruleData);
const leveledKnownKey = SpellDerivers.deriveLeveledKnownKey(spell, minCastLevel);
let result = null;
try {
result = this.getLeveledSpell(leveledKnownKey);
}
catch (error) {
// noop
}
return result;
}
getSpellManagers(spells, classId) {
const managers = spells.map((spell) => getSpellManager(Object.assign(Object.assign({}, this.params), { spell, bustCache: this.bustCache, classId })));
return managers.filter((manager, index) => managers.indexOf(manager) === index);
}
getSpell(knownKey) {
const spellManager = SpellManager.getSpellManager(knownKey);
if (!spellManager) {
throw new Error(`spellManager for spell ${knownKey} is null`);
}
return spellManager;
}
getSpellShoppe(shouldBustCache) {
return __awaiter(this, void 0, void 0, function* () {
if (!spellShoppe || shouldBustCache) {
const classKnownSpells = apiCreatorSelectors.makeLoadClassAlwaysKnownSpells(this.state);
const classRemainingSpells = apiCreatorSelectors.makeLoadClassRemainingSpells(this.state);
const ruleData = rulesEngineSelectors.getRuleData(this.state);
let classSpellLists = rulesEngineSelectors.getClassSpellLists(this.state);
const classSpells = {};
const availableSpellRequests = classSpellLists.map((classSpellList, i) => {
var _a;
const classId = ClassAccessors.getActiveId(classSpellList.charClass);
if (!shouldBustCache && ((_a = apiResponses === null || apiResponses === void 0 ? void 0 : apiResponses[classId]) === null || _a === void 0 ? void 0 : _a.availableSpells)) {
return Promise.resolve(apiResponses[classId].availableSpells);
}
const availableRequest = classRemainingSpells(classSpellList.charClass);
if (availableRequest) {
return availableRequest();
}
return Promise.resolve(null);
});
const knownSpellRequests = classSpellLists.map((classSpellList, i) => {
var _a;
const classId = ClassAccessors.getActiveId(classSpellList.charClass);
if (!shouldBustCache && ((_a = apiResponses === null || apiResponses === void 0 ? void 0 : apiResponses[classId]) === null || _a === void 0 ? void 0 : _a.knownSpells)) {
return Promise.resolve(apiResponses[classId].knownSpells);
}
const knownRequest = classKnownSpells(classSpellList.charClass);
if (knownRequest) {
return knownRequest();
}
return Promise.resolve(null);
});
const availableSpellResponses = yield Promise.all(availableSpellRequests);
const knownSpellResponses = yield Promise.all(knownSpellRequests);
classSpellLists.forEach((classSpellList, index) => {
// Dispatch any known spells into the store first
const classId = ClassAccessors.getActiveId(classSpellList.charClass);
apiResponses[classId] = (apiResponses === null || apiResponses === void 0 ? void 0 : apiResponses[classId]) ? apiResponses === null || apiResponses === void 0 ? void 0 : apiResponses[classId] : {};
const knownSpellResponse = knownSpellResponses[index];
if (knownSpellResponse) {
apiResponses[classId].knownSpells = knownSpellResponse;
const spellData = ApiAdapterUtils.getResponseData(knownSpellResponse);
if (spellData) {
this.dispatch(serviceDataActions.classAlwaysKnownSpellsSet(spellData, classId));
}
}
});
// Get new spelllists with properly generated spells
classSpellLists = rulesEngineSelectors.getClassSpellLists(this.state);
classSpellLists.forEach((classSpellList, index) => {
const knownSpellIds = classSpellList.knownSpellIds;
const availableSpellResponse = availableSpellResponses[index];
const classId = ClassAccessors.getActiveId(classSpellList.charClass);
apiResponses[classId] = (apiResponses === null || apiResponses === void 0 ? void 0 : apiResponses[classId]) ? apiResponses === null || apiResponses === void 0 ? void 0 : apiResponses[classId] : {};
let availableSpells = [];
if (availableSpellResponse) {
apiResponses[classId].availableSpells = availableSpellResponse;
const spellData = ApiAdapterUtils.getResponseData(availableSpellResponse);
const transformed = this.transformLoadedSpells(spellData || []);
const filtered = transformed.filter((spell) => !knownSpellIds.includes(SpellDerivers.deriveKnownKey(spell)));
availableSpells = this.getSpellManagers(filtered, classId);
}
const knownSpells = this.getSpellManagers(classSpellList.spells, classId);
const activeSpells = this.getSpellManagers(classSpellList.activeSpells, classId);
const availableSpellDefinitions = [...availableSpells, ...knownSpells, ...activeSpells]
.map((spellManager) => spellManager.getDefinition())
.filter(TypeScriptUtils.isNotNullOrUndefined);
const activeSourceCategories = rulesEngineSelectors.getActiveSourceCategories(this.state);
const sourceCategories = SourceUtils.getSimpleSourceCategoriesData(availableSpellDefinitions, ruleData, activeSourceCategories);
classSpells[classId] = {
availableSpells,
knownSpells,
activeSpells,
sourceCategories,
};
});
spellShoppe = classSpells;
return spellShoppe;
}
else {
return spellShoppe;
}
});
}
getSpellClasses() {
return rulesEngineSelectors.getSpellClasses(this.state);
}
getClassSpellLists() {
return rulesEngineSelectors.getClassSpellLists(this.state);
}
getCharClassByActiveId(activeId) {
var _a;
const spellClasses = this.getSpellClasses();
return (_a = spellClasses.find((charClass) => charClass.activeId === activeId)) !== null && _a !== void 0 ? _a : null;
}
getClassSpellInfoByActiveId(activeId) {
var _a;
const spellClasses = this.getClassSpellLists();
return (_a = spellClasses.find((classSpellInfo) => classSpellInfo.charClass.activeId === activeId)) !== null && _a !== void 0 ? _a : null;
}
}
SpellsManager.getUsedSpellSlotLevelAmount = (changeAmount, spellSlots, castLevel) => {
const foundSlotLevel = spellSlots.find((spellSlot) => spellSlot.level === castLevel);
if (!foundSlotLevel) {
return null;
}
const usedAmount = foundSlotLevel.used + changeAmount;
const maxAmount = foundSlotLevel.available;
return HelperUtils.clampInt(usedAmount, 0, maxAmount);
};
@@ -0,0 +1,173 @@
import { serviceDataActions } from "../actions";
import { ActionAccessors } from "../engine/Action";
import { CharacterUtils } from "../engine/Character";
import { HelperUtils } from "../engine/Helper";
import { RuleDataAccessors } from "../engine/RuleData";
import { VehicleConfigurationPrimaryComponentManageTypeEnum } from "../engine/Vehicle";
import { VehicleComponentAccessors } from "../engine/VehicleComponent";
import { rulesEngineSelectors } from "../selectors";
import { BaseManager } from './BaseManager';
const vehicleComponentManagerMap = new Map();
export const getVehicleComponentManager = (params) => {
var _a;
const { vehicleComponent } = params;
const vehicleId = (_a = VehicleComponentAccessors.getId(vehicleComponent)) !== null && _a !== void 0 ? _a : '';
if (vehicleComponentManagerMap.has(vehicleId)) {
const vehicleComponentManager = vehicleComponentManagerMap.get(vehicleId);
if (!vehicleComponentManager) {
throw new Error(`VehicleManager for vehicle ${vehicleId} is null`);
}
if (vehicleComponentManager.vehicleComponent === vehicleComponent) {
return vehicleComponentManager;
}
}
const newVehicleManger = new VehicleComponentManager(params);
vehicleComponentManagerMap.set(vehicleId, newVehicleManger);
return newVehicleManger;
};
export class VehicleComponentManager extends BaseManager {
constructor(params) {
super(params);
// Actions
this.handleHitPointAdjustment = ({ hitPointDiff }, onSuccess, onError) => {
const hitPointInfo = this.getHitPointInfo();
if (hitPointInfo === null) {
return;
}
let newHitPoints = CharacterUtils.calculateHitPoints(hitPointInfo, hitPointDiff);
this.dispatch(serviceDataActions.vehicleComponentMappingHitPointsSet(this.getMappingId(), newHitPoints.newRemovedHp));
};
// Accessors
this.getMappingId = () => VehicleComponentAccessors.getMappingId(this.vehicleComponent);
this.getHitPointInfo = () => VehicleComponentAccessors.getHitPointInfo(this.vehicleComponent);
this.getName = () => VehicleComponentAccessors.getName(this.vehicleComponent);
this.getTypeNames = () => VehicleComponentAccessors.getTypeNames(this.vehicleComponent);
this.getUniqueKey = () => VehicleComponentAccessors.getUniqueKey(this.vehicleComponent);
this.getArmorClassInfo = () => VehicleComponentAccessors.getArmorClassInfo(this.vehicleComponent);
this.getSpeedInfos = () => VehicleComponentAccessors.getSpeedInfos(this.vehicleComponent);
this.getCosts = () => VehicleComponentAccessors.getCosts(this.vehicleComponent);
this.getIsPrimary = () => VehicleComponentAccessors.getIsPrimary(this.vehicleComponent);
this.getActions = () => VehicleComponentAccessors.getActions(this.vehicleComponent);
this.getDisplayOrder = () => VehicleComponentAccessors.getDisplayOrder(this.vehicleComponent);
this.getIsRemovable = () => VehicleComponentAccessors.getIsRemovable(this.vehicleComponent);
this.getUniquenessFactor = () => VehicleComponentAccessors.getUniquenessFactor(this.vehicleComponent);
this.getCoverType = () => VehicleComponentAccessors.getCoverType(this.vehicleComponent);
this.getRequiredCrew = () => VehicleComponentAccessors.getRequiredCrew(this.vehicleComponent);
this.getDamageThreshold = () => VehicleComponentAccessors.getDamageThreshold(this.vehicleComponent);
this.getMishapThreshold = () => VehicleComponentAccessors.getMishapThreshold(this.vehicleComponent);
this.getHitPoints = () => VehicleComponentAccessors.getDefinitionHitPoints(this.vehicleComponent);
this.getHitPointSpeedAdjustments = () => VehicleComponentAccessors.getHitPointSpeedAdjustments(this.vehicleComponent);
this.getVehicleMappingId = () => VehicleComponentAccessors.getVehicleMappingId(this.vehicleComponent);
this.getDescription = () => VehicleComponentAccessors.getDescription(this.vehicleComponent);
// Utils
this.generateVehicleComponentCoverType = () => {
if (!this.vehicle.getEnableComponentCover()) {
return null;
}
const coverType = this.getCoverType();
if (coverType === null) {
return null;
}
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const coverTypeLookup = RuleDataAccessors.getCoverTypeLookup(ruleData);
const cover = HelperUtils.lookupDataOrFallback(coverTypeLookup, coverType);
return cover ? cover.name : null;
};
this.generateVehicleComponentRequiredCrew = () => {
const enableRequiredCrew = this.vehicle.getEnableComponentCrewRequirements();
return enableRequiredCrew ? this.getRequiredCrew() : null;
};
this.generateVehicleComponentArmorClassInfo = () => {
const enableArmorClass = this.vehicle.getEnableComponentArmorClass();
if (enableArmorClass && this.allowComponentProperty()) {
return this.getArmorClassInfo();
}
return null;
};
this.generateEnableComponentManagement = () => {
if (this.generateVehicleBlockComponentHitPointInfo() !== null) {
return true;
}
return false;
};
this.generateVehicleBlockComponentInfo = () => {
const uniqueKey = this.getUniqueKey();
return {
actions: this.getActions().map((action) => {
return {
name: ActionAccessors.getName(action),
description: ActionAccessors.getDescription(action),
key: `${ActionAccessors.getName(action)}-${ActionAccessors.getUniqueKey(action)}`,
ammo: ActionAccessors.getAmmunition(action),
};
}),
count: 1,
description: this.getDescription(),
displayOrder: this.getDisplayOrder(),
isPrimaryComponent: this.getIsPrimary(),
isRemovable: this.getIsRemovable(),
name: this.getName(),
typeNames: this.getTypeNames(),
coverType: this.generateVehicleComponentCoverType(),
requiredCrew: this.generateVehicleComponentRequiredCrew(),
key: uniqueKey,
uniqueKey,
uniquenessFactor: this.getUniquenessFactor(),
id: this.getMappingId(),
vehicleId: this.vehicle.getMappingId(),
displayType: this.vehicle.getDisplayType(),
armorClassInfo: this.generateVehicleComponentArmorClassInfo(),
speedInfos: this.generateVehicleComponentSpeedInfos(),
hitPointInfo: this.generateVehicleBlockComponentHitPointInfo(),
enableComponentManagement: this.generateEnableComponentManagement(),
costInfos: this.getCosts(),
width: this.vehicle.getWidth(),
length: this.vehicle.getLength(),
};
};
this.params = params;
this.vehicleComponent = params.vehicleComponent;
this.vehicle = params.vehicle;
}
// Validators
allowComponentProperty() {
const primaryManageType = this.vehicle.getPrimaryComponentManageType();
const isPrimaryComponent = this.getIsPrimary();
return !(primaryManageType === VehicleConfigurationPrimaryComponentManageTypeEnum.VEHICLE && isPrimaryComponent);
}
generateVehicleComponentSpeedInfos() {
const enableSpeeds = this.vehicle.getEnableComponentSpeeds();
if (enableSpeeds && this.allowComponentProperty()) {
return this.getSpeedInfos();
}
return [];
}
generateVehicleBlockComponentHitPointInfo() {
if (!this.allowComponentProperty()) {
return null;
}
const enableComponentHitPoints = this.vehicle.getEnableComponentHitPoints();
if (!enableComponentHitPoints) {
return null;
}
let damageThreshold = null;
if (this.vehicle.getEnableComponentDamageThreshold()) {
damageThreshold = this.getDamageThreshold();
}
let mishapThreshold = null;
if (this.vehicle.getEnableComponentMishapThreshold()) {
mishapThreshold = this.getMishapThreshold();
}
let componentHitPointInfo = this.getHitPointInfo();
const definitionHitPoints = this.getHitPoints();
return {
remainingHp: componentHitPointInfo !== null ? componentHitPointInfo.remainingHp : definitionHitPoints,
tempHp: componentHitPointInfo !== null ? componentHitPointInfo.tempHp : null,
totalHp: componentHitPointInfo !== null ? componentHitPointInfo.totalHp : definitionHitPoints,
removedHp: componentHitPointInfo !== null ? componentHitPointInfo.removedHp : 0,
hitPointSpeedAdjustments: this.getHitPointSpeedAdjustments(),
damageThreshold,
mishapThreshold,
};
}
}
@@ -0,0 +1,228 @@
import { AccessUtils } from "../engine/Access";
import { ActionValidators } from "../engine/Action";
import { ActivationTypeEnum } from "../engine/Activation";
import { ConditionAccessors } from "../engine/Condition";
import { ExtraGenerators } from "../engine/Extra";
import { RuleDataUtils } from "../engine/RuleData";
import { SourceUtils } from "../engine/Source";
import { TypeScriptUtils } from "../utils";
import { ActionUtils } from '..';
import { serviceDataActions } from '../actions';
import { VehicleAccessors, VehicleConfigurationDisplayTypeEnum, VehicleConfigurationKeyEnum, VehicleConfigurationPrimaryComponentManageTypeEnum, VehicleUtils, } from '../engine/Vehicle';
import { rulesEngineSelectors } from '../selectors';
import { BaseManager } from './BaseManager';
import { getVehicleComponentManager } from './VehicleComponentManager';
const vehicleManagerMap = new Map();
export const getVehicleManager = (params) => {
const { vehicle } = params;
const vehicleId = VehicleAccessors.getId(vehicle);
if (vehicleManagerMap.has(vehicleId)) {
const vehicleManager = vehicleManagerMap.get(vehicleId);
if (!vehicleManager) {
throw new Error(`VehicleManager for vehicle ${vehicleId} is null`);
}
if (vehicleManager.vehicle === vehicle) {
return vehicleManager;
}
}
const newVehicleManger = new VehicleManager(params);
vehicleManagerMap.set(vehicleId, newVehicleManger);
return newVehicleManger;
};
export class VehicleManager extends BaseManager {
constructor(params) {
super(params);
// Actions
this.handleRemove = (onSuccess, onError) => {
this.dispatch(serviceDataActions.vehicleMappingRemove(this.getMappingId()));
};
this.handleDataUpdate = ({ properties }, onSuccess, onError) => {
this.dispatch(serviceDataActions.vehicleMappingDataSet(this.getMappingId(), properties));
};
this.handleFuelChange = ({ remainingFuel }, onSuccess, onError) => {
this.dispatch(serviceDataActions.vehicleMappingRemainingFuelSet(this.getMappingId(), remainingFuel));
};
this.handleConditionLevelChange = (conditionId, newLevel, prevLevel) => {
const vehicleId = this.getMappingId();
if (!prevLevel && newLevel !== null) {
this.dispatch(serviceDataActions.vehicleMappingConditionAdd(vehicleId, conditionId, newLevel));
}
else if (!newLevel) {
this.dispatch(serviceDataActions.vehicleMappingConditionRemove(vehicleId, conditionId));
}
else {
this.dispatch(serviceDataActions.vehicleMappingConditionSet(vehicleId, conditionId, newLevel));
}
};
// Accessors
this.getAccessType = () => VehicleAccessors.getAccessType(this.vehicle);
this.getMappingId = () => VehicleAccessors.getMappingId(this.vehicle);
this.getPrimaryComponent = () => getVehicleComponentManager({
vehicle: this,
vehicleComponent: VehicleAccessors.getPrimaryComponent(this.vehicle),
});
this.getName = () => VehicleAccessors.getName(this.vehicle);
this.getDescription = () => VehicleAccessors.getDescription(this.vehicle);
this.getLargeAvatarUrl = () => VehicleAccessors.getLargeAvatarUrl(this.vehicle);
this.getDefinitionName = () => VehicleAccessors.getDefinitionName(this.vehicle);
this.getMovementNames = () => VehicleAccessors.getMovementNames(this.vehicle);
this.getDefinitionDescription = () => VehicleAccessors.getDefinitionDescription(this.vehicle);
this.getSources = () => VehicleAccessors.getSources(this.vehicle);
this.getFuelData = () => VehicleAccessors.getFuelData(this.vehicle);
this.getEnabledConditions = () => VehicleAccessors.getEnabledConditions(this.vehicle);
this.getActiveConditionLookup = () => VehicleAccessors.getActiveConditionLookup(this.vehicle);
this.getPrimaryComponentManageType = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.PRIMARY_COMPONENT_MANAGE_TYPE, this.vehicle);
this.getDisplayType = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.DISPLAY_TYPE, this.vehicle);
this.getEnableConditionTracking = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_CONDITIONS_TRACKING, this.vehicle);
this.getEnableFuelTracking = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_FUEL_TRACKING, this.vehicle);
this.getEnableActionStations = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_ACTION_STATIONS, this.vehicle);
this.getEnableTravelPace = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_TRAVEL_PACE, this.vehicle);
this.getEnableComponentCover = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_COMPONENT_COVER, this.vehicle);
this.getEnableComponentArmorClass = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_COMPONENT_ARMOR_CLASS, this.vehicle);
this.getEnableComponentSpeeds = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_COMPONENT_SPEEDS, this.vehicle);
this.getEnableComponentHitPoints = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_COMPONENT_HIT_POINTS, this.vehicle);
this.getEnableComponentDamageThreshold = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_COMPONENT_DAMAGE_THRESHOLD, this.vehicle);
this.getEnableComponentMishapThreshold = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_COMPONENT_MISHAP_THRESHOLD, this.vehicle);
this.getEnableComponents = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_COMPONENTS, this.vehicle);
this.getEnableComponentCrewRequirements = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_COMPONENT_CREW_REQUIREMENTS, this.vehicle);
this.getUrl = () => VehicleAccessors.getUrl(this.vehicle);
this.getUniqueKey = () => VehicleAccessors.getUniqueKey(this.vehicle);
this.getAvatarUrl = () => VehicleAccessors.getAvatarUrl(this.vehicle);
this.isHomebrew = () => VehicleAccessors.isHomebrew(this.vehicle);
this.getType = () => VehicleAccessors.getType(this.vehicle);
this.getComponents = () => {
return VehicleAccessors.getComponents(this.vehicle).map((vehicleComponent) => getVehicleComponentManager(Object.assign(Object.assign({}, this.params), { vehicle: this, vehicleComponent })));
};
this.getActionStations = () => {
return VehicleAccessors.getActionStations(this.vehicle).map((vehicleComponent) => getVehicleComponentManager(Object.assign(Object.assign({}, this.params), { vehicle: this, vehicleComponent })));
};
this.getEnableFeatures = () => VehicleUtils.getConfigurationValue(VehicleConfigurationKeyEnum.ENABLE_FEATURES, this.vehicle);
this.getFeatures = () => VehicleAccessors.getFeatures(this.vehicle);
this.getWeight = () => VehicleAccessors.getWeight(this.vehicle);
this.getWidth = () => VehicleAccessors.getWidth(this.vehicle);
this.getLength = () => VehicleAccessors.getLength(this.vehicle);
this.getSizeInfo = () => VehicleAccessors.getSizeInfo(this.vehicle);
this.getCargoCapacity = () => VehicleAccessors.getCargoCapacity(this.vehicle);
this.getCargoCapacityDescription = () => VehicleAccessors.getCargoCapacityDescription(this.vehicle);
this.getConditionImmunityInfos = () => VehicleAccessors.getConditionImmunityInfos(this.vehicle);
this.getCreatureCapacityDescriptions = () => VehicleAccessors.getCreatureCapacityDescriptions(this.vehicle);
this.getCreatureCapacity = () => VehicleAccessors.getCreatureCapacity(this.vehicle);
this.getDamageImmunityInfos = () => VehicleAccessors.getDamageImmunityInfos(this.vehicle);
this.getActionSummaries = () => VehicleAccessors.getActionSummaries(this.vehicle);
this.getActionsText = () => VehicleAccessors.getActionsText(this.vehicle);
this.getStats = () => VehicleAccessors.getStats(this.vehicle);
this.getTravelPace = () => VehicleAccessors.getTravelPace(this.vehicle);
this.getTravelPaceEffectiveHours = () => VehicleAccessors.getTravelPaceEffectiveHours(this.vehicle);
this.getAllActions = () => VehicleAccessors.getAllActions(this.vehicle);
// Validators
this.isAccessible = () => AccessUtils.isAccessible(this.getAccessType());
this.isSpelljammer = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.SPELLJAMMER;
this.isInfernalWarMachine = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.INFERNAL_WAR_MACHINE;
this.isShip = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.SHIP;
// Utils
this.generateVehicleMeta = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return ExtraGenerators.generateVehicleMeta(this.vehicle, ruleData);
};
this.getSourceNames = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return SourceUtils.getSourceFullNames(this.getSources(), ruleData);
};
this.getResolvedEnabledConditions = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return this.getEnabledConditions().map((id) => RuleDataUtils.getCondition(id, ruleData));
};
this.getVehicleSizeName = () => { var _a, _b; return (_b = (_a = this.getSizeInfo()) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : null; };
this.getVehicleTypeName = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
const type = this.getType();
if (type === null) {
return null;
}
return RuleDataUtils.getObjectTypeName(type, ruleData);
};
this.getVehicleCargoCapacityInfo = () => {
return {
weight: this.getCargoCapacity(),
description: this.getCargoCapacityDescription(),
};
};
this.generateVehicleConditionImmunityNames = () => {
return this.getConditionImmunityInfos().map((conditionInfo) => ConditionAccessors.getName(conditionInfo));
};
this.generateVehicleDamageImmunityNames = () => {
return this.getDamageImmunityInfos()
.map((immunityInfo) => immunityInfo.name)
.filter(TypeScriptUtils.isNotNullOrUndefined);
};
this.generateFeatures = () => {
return this.getEnableFeatures() ? this.getFeatures() : [];
};
this.generateVehicleComponentTravelPaceInfo = () => {
const enableTravelPace = this.getEnableTravelPace();
if (!enableTravelPace) {
return null;
}
const pace = this.getTravelPace();
const effectiveHours = this.getTravelPaceEffectiveHours();
return pace !== null
? {
pace,
effectiveHours,
}
: null;
};
this.params = params;
this.vehicle = params.vehicle;
}
generateVehicleBlockActionsSummaries() {
const ruleData = rulesEngineSelectors.getRuleData(this.state);
return this.getActionSummaries().map((action) => {
let sourceInfo = null;
if (action.sourceId !== null) {
sourceInfo = RuleDataUtils.getSourceDataInfo(action.sourceId, ruleData);
}
return Object.assign(Object.assign({}, action), { sourceName: sourceInfo ? sourceInfo.name : null, sourceFullName: sourceInfo ? sourceInfo.description : null });
});
}
generateVehicleActionsSummaries() {
return {
actionsText: this.getActionsText(),
actionsSummaries: this.generateVehicleBlockActionsSummaries(),
};
}
generateVehicleActionStationsInfo() {
return this.getActionStations().map((station) => station.generateVehicleBlockComponentInfo());
}
generateVehicleComponentsBlockInfo() {
return this.getComponents().map((component) => component.generateVehicleBlockComponentInfo());
}
generateVehicleActionInfo(action) {
return {
name: ActionUtils.getName(action),
description: ActionUtils.getDescription(action),
key: `${ActionUtils.getName(action)}-${ActionUtils.getUniqueKey(action)}`,
ammo: ActionUtils.getAmmunition(action),
};
}
generateVehicleBlockActions(activationType, actions) {
return actions
.filter((action) => ActionValidators.validateIsActivationType(action, activationType))
.map(this.generateVehicleActionInfo);
}
generateVehicleBlockActionsInfo() {
const allActions = this.getAllActions();
return {
reactions: this.generateVehicleBlockActions(ActivationTypeEnum.REACTION, allActions),
bonusActions: this.generateVehicleBlockActions(ActivationTypeEnum.BONUS_ACTION, allActions),
special: this.generateVehicleBlockActions(ActivationTypeEnum.SPECIAL, allActions),
};
}
generateVehicleBlockPrimaryHitPointInfo() {
const primaryManageType = this.getPrimaryComponentManageType();
if (primaryManageType === VehicleConfigurationPrimaryComponentManageTypeEnum.COMPONENT) {
return null;
}
return this.getPrimaryComponent().generateVehicleBlockComponentHitPointInfo();
}
}
@@ -0,0 +1,79 @@
/**
*
* @param input
* @param startIdx
* @param pieceIdx
* @param pieceCharIdx
* @param pieces
*/
export function checkQueryPiece(input, startIdx, pieceIdx, pieceCharIdx, pieces) {
let i;
let ch;
let length = input.length;
for (i = startIdx; i < length; i++) {
ch = input.charAt(i);
switch (ch) {
case ' ':
case '-':
case '.':
case '"':
continue; //ignore spaces, dashes, periods, and double quotes
default:
if (pieceIdx < pieces.length) {
let piece = pieces[pieceIdx];
if (pieceCharIdx < piece.length && ch === piece.charAt(pieceCharIdx)) {
//if current character matches, check next character in piece
if (checkQueryPiece(input, i + 1, pieceIdx, pieceCharIdx + 1, pieces)) {
return true;
}
//if path for next character doesn't match, try path from start of next piece
if (checkQueryPiece(input, i + 1, pieceIdx + 1, 0, pieces)) {
return true;
}
}
if (pieceCharIdx === 0) {
//if first character of piece doesn't match, allow skipping that piece
if (checkQueryPiece(input, i, pieceIdx + 1, 0, pieces)) {
return true;
}
}
}
return false; //no match if no path above reaches end of input string
}
}
return true; //match if reached end of input string
}
/**
*
* @param query
* @param primaryText
* @param tags
*/
export function doesQueryMatchData(query, primaryText, tags = []) {
let input = query.toLowerCase().trim();
if (!input) {
// if there is no query don't filter anything
return true;
}
let processedPrimaryText = primaryText ? primaryText.replace(/["+]/g, '').toLowerCase().trim() : '';
let processedTags = tags && tags.length ? tags.map((tag) => (tag ? tag.toLowerCase().trim() : tag)) : [];
let pieces = processedPrimaryText.split(/[ -]/);
if (checkQueryPiece(input, 0, 0, 0, pieces)) {
//don't filter out item if heading matches
return true;
}
// if heading isn't a match, see if input is an exact match for associated tags
if (!processedTags.length) {
// filter out item if no tags specified
return false;
}
let tagMatch = false;
processedTags.forEach((tag) => {
if (tagMatch || !tag) {
return;
}
let pieces = tag.split(/[ -]/);
tagMatch = checkQueryPiece(input, 0, 0, 0, pieces);
});
return tagMatch;
}
@@ -0,0 +1,148 @@
import { ExpressionOperators, StatementOperators } from '@dndbeyond/character-gfs';
import { ModifierAccessors, ModifierTypeEnum, ModifierValidators } from "../../../engine/Modifier";
import { RuleDataUtils } from "../../../engine/RuleData";
export function transformAbilityScores(validGlobalModifiers, baseStats, ruleData) {
const abilityScoreModifiers = validGlobalModifiers.filter((modifier) => {
return (ModifierValidators.isValidStatScoreModifier(modifier, 1) ||
ModifierValidators.isValidStatScoreModifier(modifier, 2) ||
ModifierValidators.isValidStatScoreModifier(modifier, 3) ||
ModifierValidators.isValidStatScoreModifier(modifier, 4) ||
ModifierValidators.isValidStatScoreModifier(modifier, 5) ||
ModifierValidators.isValidStatScoreModifier(modifier, 6));
});
const baseAbilityScores = baseStats.flatMap((stat) => {
var _a, _b, _c;
if (stat.id) {
const statName = (_b = (_a = RuleDataUtils.getStatNameById(stat.id, ruleData, true)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
stat.value = (_c = stat.value) !== null && _c !== void 0 ? _c : 0;
// Math.floor((stat.value - 10) / 2)
return [
{
precedence: 0.5,
target: ['attributes', statName, 'value'],
operator: StatementOperators.SET,
operand: {
operator: ExpressionOperators.STATIC_VALUE,
values: [stat.value],
operands: null,
},
},
{
precedence: 0.5,
target: ['attributes', statName, 'modifier'],
operator: StatementOperators.SET,
operand: {
operator: ExpressionOperators.FLOOR,
values: null,
operands: [
{
operator: ExpressionOperators.MULTIPLY,
values: null,
operands: [
{
operator: ExpressionOperators.STATIC_VALUE,
values: [0.5],
operands: null,
},
{
operator: ExpressionOperators.ADD,
values: null,
operands: [
{
operator: ExpressionOperators.STATIC_VALUE,
values: [-10],
operands: null,
},
{
operator: ExpressionOperators.DYNAMIC_VALUE,
values: ['attributes', statName, 'value'],
operands: null,
},
],
},
],
},
],
},
},
{
precedence: 0.5,
target: ['attributes', statName, 'max'],
operator: StatementOperators.SET,
operand: {
operator: ExpressionOperators.STATIC_VALUE,
values: [20],
operands: null,
},
},
];
}
return [];
});
const gfsAbilityScores = abilityScoreModifiers.map((abilityScoreModifier) => {
var _a, _b, _c, _d;
const modifierType = ModifierAccessors.getType(abilityScoreModifier);
switch (modifierType) {
case ModifierTypeEnum.SET:
return {
precedence: 0.5,
target: [
'attributes',
(_a = ModifierAccessors.getSubType(abilityScoreModifier)) === null || _a === void 0 ? void 0 : _a.replace('-score', ''),
'value',
],
operator: StatementOperators.SET,
operand: {
operator: ExpressionOperators.STATIC_VALUE,
values: [ModifierAccessors.getValue(abilityScoreModifier)],
},
};
case ModifierTypeEnum.BONUS:
default:
return {
precedence: 0.5,
target: [
'attributes',
(_b = ModifierAccessors.getSubType(abilityScoreModifier)) === null || _b === void 0 ? void 0 : _b.replace('-score', ''),
'value',
],
operator: StatementOperators.SET,
operand: {
operator: ExpressionOperators.ADD,
values: null,
operands: [
{
operator: ExpressionOperators.DYNAMIC_VALUE,
operands: null,
values: [
'attributes',
(_c = ModifierAccessors.getSubType(abilityScoreModifier)) === null || _c === void 0 ? void 0 : _c.replace('-score', ''),
'value',
],
},
{
operator: ExpressionOperators.STATIC_VALUE,
values: [(_d = ModifierAccessors.getValue(abilityScoreModifier)) !== null && _d !== void 0 ? _d : 0],
operands: null,
},
],
},
};
}
});
const generatedFeature = {
meta: {
name: 'Generated Ability Scores',
category: 'Generated',
},
statements: [...baseAbilityScores, ...gfsAbilityScores],
featureId: 'generated-ability-scores',
labels: [],
sources: [],
subfeatures: [],
acceptableInputs: {},
primarySource: null,
secondarySources: [],
};
return { baseAbilityScores, gfsAbilityScores, generatedFeature };
}