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:
@@ -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;
|
||||
}
|
||||
+148
@@ -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 };
|
||||
}
|
||||
Reference in New Issue
Block a user