Skip to content

Commit d325f2d

Browse files
authored
Add RE for soul steal, add expression support for skill accuracy (#649)
* Add RE for soul steal, add expression support for skill accuracy * Render the DL used for the attribute check too.
1 parent a95c7f9 commit d325f2d

File tree

12 files changed

+152
-67
lines changed

12 files changed

+152
-67
lines changed

lang/en.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -957,8 +957,8 @@
957957
"ChatApplyDamagePrompt": "The followings characters have <strong>suffered {amount} {type} damage</strong> due to <strong>{source}</strong>.",
958958
"ChatResourceGainPrompt": "The followings characters have <strong>gained {amount} {type}</strong> due to <strong>{source}</strong>.",
959959
"ChatResourceLossPrompt": "The followings characters have <strong>lost {amount} {type}</strong> due to <strong>{source}</strong>.",
960-
"ChatResourceGainTooltip": "Gain <strong>{amount} {resource}</strong>.",
961-
"ChatResourceLossTooltip": "Lose <strong>{amount} {resource}</strong>.",
960+
"ChatResourceGainTooltip": "Gain {amount} {resource}",
961+
"ChatResourceLossTooltip": "Lose {amount} {resource}",
962962
"Increment": "Increment",
963963
"Decrement": "Decrement",
964964
"TotalAvailableSkills": "Total Available Skills",
@@ -1666,6 +1666,7 @@
16661666
"RulePredicateFactionRelation": "Faction Relation",
16671667
"RulePredicateEffect": "Effect",
16681668
"RulePredicateSpecies": "Species",
1669+
"RulePredicateRank": "Rank",
16691670
"RulePredicateWeapon": "Weapon",
16701671
"RulePredicateResource": "Resource",
16711672
"RulePredicateTargeting": "Targeting",

module/documents/effects/actions/message-rule-action.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { FUHooks } from '../../../hooks.mjs';
55
import { CommonSections } from '../../../checks/common-sections.mjs';
66
import { Flags } from '../../../helpers/flags.mjs';
77
import { Pipeline } from '../../../pipelines/pipeline.mjs';
8+
import { CHECK_DETAILS } from '../../../checks/default-section-order.mjs';
89

910
const { StringField } = foundry.data.fields;
1011

@@ -52,7 +53,7 @@ export class MessageRuleAction extends RuleActionDataModel {
5253
/** @type RenderCheckEvent **/
5354
const rce = context.event;
5455
const actor = rce.source.actor !== context.character.actor ? context.item.parent : null;
55-
CommonSections.itemText(rce.renderData, this.message, actor, context.item, flags);
56+
CommonSections.itemText(rce.renderData, this.message, actor, context.item, flags, CHECK_DETAILS);
5657
} else {
5758
const actor = context.character.actor;
5859
const content = await FoundryUtils.renderTemplate('chat/partials/chat-item-text', {

module/documents/effects/actions/update-resource-rule-action.mjs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { ExpressionContext, Expressions } from '../../../expressions/expressions
44
import { ResourcePipeline, ResourceRequest } from '../../../pipelines/resource-pipeline.mjs';
55
import { RuleActionDataModel } from './rule-action-data-model.mjs';
66
import { SETTINGS } from '../../../settings.js';
7-
import { FUHooks } from '../../../hooks.mjs';
87

98
const fields = foundry.data.fields;
9+
1010
/**
1111
* @property {FU.resources} resource
1212
* @property {String} amount
@@ -48,11 +48,9 @@ export class UpdateResourceRuleAction extends RuleActionDataModel {
4848
const request = new ResourceRequest(context.sourceInfo, targets, this.resource, amount);
4949
request.fromOrigin(context.origin);
5050

51-
if (context.eventType === FUHooks.INITIALIZE_CHECK_EVENT) {
52-
/** @type InitializeCheckEvent **/
53-
const ice = context.event;
51+
if (context.config) {
5452
const targetAction = ResourcePipeline.getTargetedAction(request);
55-
ice.config.addTargetedAction(targetAction);
53+
context.config.addTargetedAction(targetAction);
5654
} else {
5755
if (game.settings.get(SYSTEM, SETTINGS.automationUpdateResource)) {
5856
await ResourcePipeline.process(request);

module/documents/effects/active-effect-config.mjs

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -145,47 +145,49 @@ export class FUActiveEffectConfig extends foundry.applications.sheets.ActiveEffe
145145
case 'rules':
146146
{
147147
context.options = {
148+
...FU,
148149
ruleActions: RuleActionRegistry.instance.localizedEntries,
149150
ruleTriggers: RuleTriggerRegistry.instance.localizedEntries,
150-
combatEvent: FU.combatEvent,
151-
duration: FU.duration,
152-
damageTypes: FU.damageTypes,
153151
damageTypeOptions: FoundryUtils.getFormOptions(FU.damageTypes),
154-
itemGroup: FU.itemGroup,
155152
itemGroupOptions: FoundryUtils.getFormOptions(FU.itemGroup),
156-
damageSource: FU.damageSource,
157153
damageSourceOptions: FoundryUtils.getFormOptions(FU.damageSource),
158-
expenseSource: FU.expenseSource,
159-
checkTypes: FU.checkTypes,
160154
checkTypeOptions: FoundryUtils.getFormOptions(FU.checkTypes),
161-
dialogCheckTypes: FU.dialogCheckTypes,
162-
attributes: FU.attributes,
163-
resources: FU.resources,
164-
species: FU.species,
165-
weaponTypes: FU.weaponTypes,
166-
handedness: FU.handedness,
167-
weaponCategories: FU.weaponCategories,
155+
rankOptions: FoundryUtils.getFormOptions(FU.rank),
168156
weaponCategoryOptions: FoundryUtils.getFormOptions(FU.weaponCategories),
169157
consumableTraitOptions: FoundryUtils.getFormOptions(ConsumableTraits, (k, v) => k),
170-
targetingRules: FU.targetingRules,
171-
commandAction: FU.commandAction,
172-
statusEffects: FU.statusEffects,
173-
eventRelation: FU.eventRelation,
174-
factionRelation: FU.factionRelation,
175-
bondPredicate: FU.bondPredicate,
176-
targetSelector: FU.targetSelector,
177-
checkParity: FU.checkParity,
178-
checkOutcome: FU.checkOutcome,
179158
traits: TraitUtils.getOptions(Traits),
180-
changeSetMode: FU.changeSetMode,
181-
booleanOption: FU.booleanOption,
182-
collectionChange: FU.collectionChange,
183-
collectionRemovalRule: FU.collectionRemovalRule,
184-
scalarChange: FU.scalarChange,
185-
comparisonOperator: FU.comparisonOperator,
186-
targetingPredicate: FU.targetingPredicate,
187-
predicateQuantifier: FU.predicateQuantifier,
188-
modifyDamageVariant: FU.modifyDamageVariant,
159+
// combatEvent: FU.combatEvent,
160+
// duration: FU.duration,
161+
// damageTypes: FU.damageTypes,
162+
// itemGroup: FU.itemGroup,
163+
// damageSource: FU.damageSource,
164+
// expenseSource: FU.expenseSource,
165+
// checkTypes: FU.checkTypes,
166+
// dialogCheckTypes: FU.dialogCheckTypes,
167+
// attributes: FU.attributes,
168+
// resources: FU.resources,
169+
// species: FU.species,
170+
// weaponTypes: FU.weaponTypes,
171+
// handedness: FU.handedness,
172+
// weaponCategories: FU.weaponCategories,
173+
// targetingRules: FU.targetingRules,
174+
// commandAction: FU.commandAction,
175+
// statusEffects: FU.statusEffects,
176+
// eventRelation: FU.eventRelation,
177+
// factionRelation: FU.factionRelation,
178+
// bondPredicate: FU.bondPredicate,
179+
// targetSelector: FU.targetSelector,
180+
// checkParity: FU.checkParity,
181+
// checkOutcome: FU.checkOutcome,
182+
// changeSetMode: FU.changeSetMode,
183+
// booleanOption: FU.booleanOption,
184+
// collectionChange: FU.collectionChange,
185+
// collectionRemovalRule: FU.collectionRemovalRule,
186+
// scalarChange: FU.scalarChange,
187+
// comparisonOperator: FU.comparisonOperator,
188+
// targetingPredicate: FU.targetingPredicate,
189+
// predicateQuantifier: FU.predicateQuantifier,
190+
// modifyDamageVariant: FU.modifyDamageVariant,
189191
};
190192
}
191193
break;

module/documents/effects/predicates/check-rule-predicate.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ export class CheckRulePredicate extends RulePredicateDataModel {
9494
break;
9595

9696
default:
97+
if (context.config.isFumble()) {
98+
return false;
99+
}
97100
for (const target of context.event.targets) {
98101
switch (target.check) {
99102
case 'hit':
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { RulePredicateDataModel } from './rule-predicate-data-model.mjs';
2+
import { systemTemplatePath } from '../../../helpers/system-utils.mjs';
3+
4+
const fields = foundry.data.fields;
5+
6+
/**
7+
* @property {Set<FUAdversaryRank>} ranks
8+
*/
9+
export class RankRulePredicate extends RulePredicateDataModel {
10+
static {
11+
Object.defineProperty(this, 'TYPE', { value: 'rankRulePredicate' });
12+
}
13+
14+
static defineSchema() {
15+
return Object.assign(super.defineSchema(), {
16+
ranks: new fields.SetField(new fields.StringField()),
17+
});
18+
}
19+
20+
static get localization() {
21+
return 'FU.RulePredicateRank';
22+
}
23+
24+
static get template() {
25+
return systemTemplatePath('effects/predicates/rank-rule-predicate');
26+
}
27+
28+
/**
29+
* @override
30+
*/
31+
validateContext(context) {
32+
for (const target of context.targets) {
33+
/** @type NpcDataModel **/
34+
const targetData = target.actor.system;
35+
if (!this.ranks.has(targetData.rank.value)) {
36+
return false;
37+
}
38+
}
39+
return true;
40+
}
41+
}

module/documents/items/skill/skill-data-model.mjs

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,17 @@ Hooks.on(CheckHooks.renderCheck, onRenderAccuracyCheck);
6161
/**
6262
* @type RenderCheckHook
6363
*/
64-
let onRenderAttributeCheck = (sections, check, actor, item) => {
64+
let onRenderAttributeCheck = (sections, check, actor, item, flags) => {
6565
if (check.type === 'attribute' && check.additionalData[skillForAttributeCheck]) {
6666
const skill = fromUuidSync(check.additionalData[skillForAttributeCheck]);
67+
const inspector = CheckConfiguration.inspect(check);
6768
CommonSections.itemFlavor(sections, skill);
6869
CommonSections.tags(sections, getTags(skill), CHECK_DETAILS);
6970
if (skill.system.hasResource.value) {
7071
CommonSections.resource(sections, skill.system.rp, CHECK_DETAILS);
7172
}
7273
CommonSections.description(sections, skill.system.description, skill.system.summary.value, CHECK_DETAILS);
74+
CommonSections.actions(sections, actor, item, [], flags, inspector);
7375
}
7476
};
7577
Hooks.on(CheckHooks.renderCheck, onRenderAttributeCheck);
@@ -115,7 +117,7 @@ function getTags(skill) {
115117
* @property {string} class.value
116118
* @property {UseWeaponDataModelV2} useWeapon
117119
* @property {ItemAttributesDataModelV2} attributes
118-
* @property {number} accuracy
120+
* @property {String} accuracy A number or expression for the accuracy to be used.
119121
* @property {Defense} defense
120122
* @property {DamageDataModelV2} damage
121123
* @property {ResourceDataModel} resource
@@ -160,7 +162,7 @@ export class SkillDataModel extends FUStandardItemDataModel {
160162
secondary: 'ins',
161163
},
162164
}),
163-
accuracy: new NumberField({ initial: 0, integer: true, nullable: false }),
165+
accuracy: new StringField({ initial: '', blank: true, nullable: false }),
164166
defense: new StringField({ initial: 'def', choices: Object.keys(FU.defenses), blank: true }),
165167
damage: new EmbeddedDataField(DamageDataModelV2, {}),
166168
resource: new EmbeddedDataField(ResourceDataModel, {}),
@@ -212,7 +214,7 @@ export class SkillDataModel extends FUStandardItemDataModel {
212214
secondary: this.attributes.secondary,
213215
},
214216
this.parent,
215-
this.#initializeAttributeCheck(),
217+
this.#initializeAttributeCheck(modifiers),
216218
);
217219
}
218220
}
@@ -236,6 +238,44 @@ export class SkillDataModel extends FUStandardItemDataModel {
236238
};
237239
}
238240

241+
/**
242+
* @return {CheckCallback}
243+
*/
244+
#initializeAttributeCheck(modifiers) {
245+
return async (check, actor, item) => {
246+
/** @type SkillDataModel **/
247+
const skill = item.system;
248+
const config = CheckConfiguration.configure(check);
249+
const targets = config.getTargets();
250+
const context = ExpressionContext.fromTargetData(actor, item, targets);
251+
252+
if (skill.defense && targets.length === 1) {
253+
let dl;
254+
switch (skill.defense) {
255+
case 'def':
256+
dl = targets[0].def;
257+
break;
258+
259+
case 'mdef':
260+
dl = targets[0].mdef;
261+
break;
262+
}
263+
config.setDifficulty(dl);
264+
}
265+
266+
if (this.accuracy) {
267+
const calculatedAccuracyBonus = await Expressions.evaluateAsync(this.accuracy, context);
268+
if (calculatedAccuracyBonus > 0) {
269+
check.modifiers.push({
270+
label: 'FU.CheckBonus',
271+
value: calculatedAccuracyBonus,
272+
});
273+
}
274+
}
275+
check.additionalData[skillForAttributeCheck] = this.parent.uuid;
276+
};
277+
}
278+
239279
/**
240280
* @param {KeyboardModifiers} modifiers
241281
* @return {CheckCallback}
@@ -264,6 +304,8 @@ export class SkillDataModel extends FUStandardItemDataModel {
264304
/** @type SkillDataModel **/
265305
const skill = item.system;
266306
const config = CheckConfiguration.configure(check);
307+
const targets = config.getTargets();
308+
const context = ExpressionContext.fromTargetData(actor, item, targets);
267309

268310
config.addTraits('skill');
269311
config.addTraitsFromItemModel(this.traits);
@@ -273,10 +315,13 @@ export class SkillDataModel extends FUStandardItemDataModel {
273315
config.setWeaponTraits(config.getWeaponTraits());
274316

275317
if (this.accuracy) {
276-
check.modifiers.push({
277-
label: 'FU.CheckBonus',
278-
value: this.accuracy,
279-
});
318+
const calculatedAccuracyBonus = await Expressions.evaluateAsync(this.accuracy, context);
319+
if (calculatedAccuracyBonus > 0) {
320+
check.modifiers.push({
321+
label: 'FU.CheckBonus',
322+
value: calculatedAccuracyBonus,
323+
});
324+
}
280325
}
281326

282327
if (skill.resource.enabled) {
@@ -307,8 +352,6 @@ export class SkillDataModel extends FUStandardItemDataModel {
307352

308353
const onRoll = this.damage.onRoll;
309354
if (onRoll) {
310-
const targets = config.getTargets();
311-
const context = ExpressionContext.fromTargetData(actor, item, targets);
312355
const extraDamage = await Expressions.evaluateAsync(onRoll, context);
313356
if (extraDamage > 0) {
314357
config.addDamageBonus('FU.DamageOnRoll', extraDamage);
@@ -325,19 +368,6 @@ export class SkillDataModel extends FUStandardItemDataModel {
325368
};
326369
}
327370

328-
/**
329-
* @return {CheckCallback}
330-
*/
331-
#initializeAttributeCheck() {
332-
return (check) => {
333-
check.modifiers.push({
334-
label: 'FU.CheckBonus',
335-
value: this.accuracy,
336-
});
337-
check.additionalData[skillForAttributeCheck] = this.parent.uuid;
338-
};
339-
}
340-
341371
shouldApplyEffect(effect) {
342372
return this.level.value > 0;
343373
}

module/helpers/config.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,10 @@ FU.combatHudThemeTemplates = {
662662
'fu-pixel': 'combat-hud-pixel',
663663
};
664664

665+
/**
666+
* @typedef {'soldier', 'elite', 'champion'} FUAdversaryRank
667+
*/
668+
665669
FU.rank = {
666670
soldier: 'FU.Soldier',
667671
elite: 'FU.Elite',

module/pipelines/resource-pipeline.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@ function getTargetedAction(request) {
437437
})
438438
.requiresOwner()
439439
.setFlag(request.gain ? Flags.ChatMessage.ResourceGain : Flags.ChatMessage.ResourceLoss)
440+
.withLabel(tooltip)
440441
.withColor(request.gain ? 'var(--color-hp)' : 'var(--color-hp-crisis)')
441442
.withSelected();
442443
}

module/pipelines/rule-elements.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { PerformCheckRuleAction } from '../documents/effects/actions/perform-che
5353
import { ModifyResourceRuleAction } from '../documents/effects/actions/modify-resource-rule-action.mjs';
5454
import { CalculateResourceRuleTrigger } from '../documents/effects/triggers/calculate-resource-rule-trigger.mjs';
5555
import { OpenApplicationRuleAction } from '../documents/effects/actions/open-application-rule-action.mjs';
56+
import { RankRulePredicate } from '../documents/effects/predicates/rank-rule-predicate.mjs';
5657

5758
function register() {
5859
RuleTriggerRegistry.instance.register(systemId, CombatEventRuleTrigger.TYPE, CombatEventRuleTrigger);
@@ -94,6 +95,7 @@ function register() {
9495
RulePredicateRegistry.instance.register(systemId, FactionRelationRulePredicate.TYPE, FactionRelationRulePredicate);
9596
RulePredicateRegistry.instance.register(systemId, EffectRulePredicate.TYPE, EffectRulePredicate);
9697
RulePredicateRegistry.instance.register(systemId, SpeciesRulePredicate.TYPE, SpeciesRulePredicate);
98+
RulePredicateRegistry.instance.register(systemId, RankRulePredicate.TYPE, RankRulePredicate);
9799
RulePredicateRegistry.instance.register(systemId, WeaponRulePredicate.TYPE, WeaponRulePredicate);
98100
RulePredicateRegistry.instance.register(systemId, ResourceRulePredicate.TYPE, ResourceRulePredicate);
99101
RulePredicateRegistry.instance.register(systemId, TargetingRulePredicate.TYPE, TargetingRulePredicate);

0 commit comments

Comments
 (0)