Skip to content

Commit

Permalink
feat(active_effects): very WIP active effect implementation
Browse files Browse the repository at this point in the history
* corrected stat going up each time actor sheet with AEs is closed
* synced AEs with "active" checkbox for modifiers
* add support for reducing encumbrance of equipped items

#1215
  • Loading branch information
wrycu committed May 23, 2023
1 parent 0457a55 commit a7c1f73
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 19 deletions.
21 changes: 20 additions & 1 deletion modules/actors/actor-sheet-ffg.js
Original file line number Diff line number Diff line change
Expand Up @@ -513,11 +513,12 @@ export class ActorSheetFFG extends ActorSheet {
});

// Toggle item equipped
html.find(".items .item a.toggle-equipped").click((ev) => {
html.find(".items .item a.toggle-equipped").click(async (ev) => {
const li = $(ev.currentTarget);
const item = this.actor.items.get(li.data("itemId"));
if (item) {
item.update({ ["system.equippable.equipped"]: !item.system.equippable.equipped });
await ModifierHelpers.updateAEsForEquip(this.actor, item, !item.system.equippable.equipped);
}
});

Expand Down Expand Up @@ -1297,6 +1298,24 @@ export class ActorSheetFFG extends ActorSheet {
return true;
}

async _onDropItem(event, data) {
let deny = false;
let item = game.items.get(data.uuid.split('.')[1]);
if (item) {
// don't allow dropping vehicle-only items onto a character
if (this.actor.type === "character" && ['criticaldamage', 'shipattachment', 'shipweapon'].includes(item.type)) {
deny = true;
}
// placeholder for vehicle denying
else if (this.actor.type === 'vehicle' && [].includes(item.type)) {
deny = true;
}
}
if (!deny) {
await super._onDropItem(event, data);
}
}

/**
* Drop Event function for transferring items between actors
*
Expand Down
16 changes: 10 additions & 6 deletions modules/helpers/actor-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import ModifierHelpers from "./modifiers.js";
export default class ActorHelpers {
static updateActor(event, formData) {
formData = expandObject(formData);
const ownedItems = this.actor.items;

// as of Foundry v10, saving an editor only submits the single entry for that editor
if (Object.keys(formData).length > 1) {
Expand All @@ -23,7 +22,8 @@ export default class ActorHelpers {
Object.keys(CONFIG.FFG.character_stats).forEach((k) => {
const key = CONFIG.FFG.character_stats[k].value;

let total = parseInt(ModifierHelpers.getActiveAEModifierValue(this.actor, key)); // value being added from AEs
let total = parseInt(ModifierHelpers.getActiveAEModifierValue(this.actor, key)); // value being added from AEs, needed for thresholds
if (key === 'Encumbrance') { console.log(`starting total: ${total}`)}
let statValue = 0; // value as it exists in the form
let isFormValueVisible = true;

Expand All @@ -44,6 +44,7 @@ export default class ActorHelpers {
statValue -= 5; // remove the extra 5 added
statValue -= parseInt(ModifierHelpers.getActiveAEModifierValue(this.actor, 'Brawn'));
total += parseInt(ModifierHelpers.getActiveAEModifierValue(this.actor, 'Brawn'))
formData.data.stats[k].value -= parseInt(ModifierHelpers.getActiveAEModifierValue(this.actor, key))
} else {
statValue = 0;
isFormValueVisible = false;
Expand Down Expand Up @@ -97,7 +98,7 @@ export default class ActorHelpers {
Object.keys(CONFIG.FFG.vehicle_stats).forEach((k) => {
const key = CONFIG.FFG.vehicle_stats[k].value;

let total = ModifierHelpers.getCalculateValueForAttribute(key, this.actor.system.attributes, ownedItems, "Stat");
let total = parseInt(ModifierHelpers.getActiveAEModifierValue(this.actor, key));

let statValue = 0;
let isFormValueVisible = true;
Expand Down Expand Up @@ -132,14 +133,16 @@ export default class ActorHelpers {
allowNegative = true;
}
let x = statValue - (isFormValueVisible ? total : 0);
let y = parseInt(formData.data.attributes[key].value, 10) + x;
if (y > 0 || allowNegative) {
formData.data.attributes[key].value = y;
if (x > 0 || allowNegative) {
formData.data.attributes[key].value = x;
} else {
formData.data.attributes[key].value = 0;
}
}
});
// subtract AE values for stats which can be increased by AEs so we don't add to them forever
formData.data.stats.encumbrance.value -= parseInt(ModifierHelpers.getActiveAEModifierValue(this.actor, 'Encumbrance'));
formData.data.stats.customizationHardPoints.value -= parseInt(ModifierHelpers.getActiveAEModifierValue(this.actor, 'customizationHardPoints'));
}
}
}
Expand All @@ -161,6 +164,7 @@ export default class ActorHelpers {

// recombine attributes to formData
formData.data.attributes = attributes;
console.log(formData)

// Update the Actor
setProperty(formData, `flags.starwarsffg.loaded`, false);
Expand Down
10 changes: 8 additions & 2 deletions modules/helpers/item-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,17 @@ export default class ItemHelpers {
let mod_type = formData.data.attributes[key].modtype;
let mod = formData.data.attributes[key].mod;
let mod_value = formData.data.attributes[key].value;
await ModifierHelpers.updateActiveEffect(this.item, active_effect_id, mod_type, mod, mod_value);
let mod_active = formData.data.attributes[key].active;
await ModifierHelpers.updateActiveEffect(this.item, active_effect_id, mod_type, mod, mod_value, mod_active);

} else {
// handle non-mod changes
let active_effect_id = key; // ex: Agility
let mod_type = formData.data.attributes[key].modtype;
let mod = formData.data.attributes[key].mod;
let mod_value = formData.data.attributes[key].value;
await ModifierHelpers.updateActiveEffect(this.item, active_effect_id, mod_type, mod, mod_value);
let mod_active = formData.data.attributes[key].active;
await ModifierHelpers.updateActiveEffect(this.item, active_effect_id, mod_type, mod, mod_value, mod_active);
}
}

Expand Down Expand Up @@ -314,6 +316,7 @@ export default class ItemHelpers {
modifier_data.modtype,
modifier_data.mod,
modifier_data.value,
modifier_data.active,
);
touched_modifiers.push(modifier_label);
}
Expand Down Expand Up @@ -380,6 +383,7 @@ export default class ItemHelpers {
modifier_data.modtype,
modifier_data.mod,
modifier_data.value,
modifier_data.active,
);
touched_modifiers.push(modifier_label);
}
Expand Down Expand Up @@ -427,6 +431,7 @@ export default class ItemHelpers {
modifier_data.modtype,
modifier_data.mod,
modifier_data.value,
modifier_data.active,
);
touched_modifiers.push(modifier_label);
}
Expand Down Expand Up @@ -477,6 +482,7 @@ export default class ItemHelpers {
modifier_data.modtype,
modifier_data.mod,
modifier_data.value,
modifier_data.active,
);
touched_modifiers.push(modifier_label);
}
Expand Down
111 changes: 101 additions & 10 deletions modules/helpers/modifiers.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ export default class ModifierHelpers {
const delete_id = $(li).attr('data-attribute');
// find the matching active effect
const to_delete = [];
if (this.id === 'popout-modifiers' && this.object.parent.type === 'forcepower' || this.object.parent.type === 'signatureability') {
if (this.id === 'popout-modifiers' && this.object?.parent?.type === 'forcepower' || this.object?.parent?.type === 'signatureability') {
this.object.parent.getEmbeddedCollection("ActiveEffect").filter(i => i.label === delete_id).forEach(function (item) {
to_delete.push(item.id);
});
Expand Down Expand Up @@ -457,7 +457,7 @@ export default class ModifierHelpers {
} else if (modifier_category === 'Stat' && Object.keys(stat_mods).includes(modifier)) {
// Stat mods
data['keys'] = [stat_mods[modifier]];
} else if (modifier_category === 'Encumbrance (Current)') {
} else if (modifier_category === 'Encumbrance (Current)' || modifier_category === 'Encumbrance (Equipped)') {
data['keys'] = ['system.stats.encumbrance.value'];
} else if (modifier_category === 'Hardpoints') {
data['keys'] = ['system.stats.customizationHardPoints.value'];
Expand All @@ -482,9 +482,10 @@ export default class ModifierHelpers {
* @param modifier_category - the "modifier type" selected
* @param modifier - the "modifier" selected
* @param modifier_value - the "value" selected (true/false for some mods)
* @param active - OPTIONAL. if the active effect should currently be supplied or not. if not supplied, assumed to be true.
* @returns {Promise<void>}
*/
static async updateActiveEffect(item, effect_id, modifier_category, modifier, modifier_value) {
static async updateActiveEffect(item, effect_id, modifier_category, modifier, modifier_value, active=null) {
// TODO: we really should check if this data has changed rather than forcing a full update each time
// TODO: refactor console.log statements into debug statements
let active_effect = item.getEmbeddedCollection("ActiveEffect").filter(i => i.label === effect_id);
Expand All @@ -503,6 +504,8 @@ export default class ModifierHelpers {
let data = ModifierHelpers.determineModifierKey(modifier_category, modifier);
console.log("found data:")
console.log(data)
console.log("new value")
console.log(modifier_value)
data['keys'].forEach(function (key) {
changes.push({
key: key,
Expand All @@ -516,8 +519,15 @@ export default class ModifierHelpers {
await game.actors.filter(i => i.name === 'ship')[0].effects.filter(i => i.id === effect.id)[0].update({changes: [{new: changes}]});
*/
await current_effect.update({
changes: changes
changes: changes,
});
if (active !== null) {
// if the caller specified active, set it
// (reversed because the property in Foundry is _disabled_ and we use _active_)
await current_effect.update({
disabled: !active,
});
}
console.log("resulting effect:")
console.log(current_effect)
}
Expand Down Expand Up @@ -593,6 +603,22 @@ export default class ModifierHelpers {
}]
}]
);
await item.createEmbeddedDocuments(
"ActiveEffect",
[{
label: "Encumbrance (Equipped)",
icon: "icons/svg/aura.svg",
origin: item.uuid,
disabled: false,
transfer: true,
changes: [{
// TODO: this mapping should be a global somewhere, not hidden away and reconstructed in various functions
key: 'system.stats.encumbrance.value',
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: '0',
}]
}]
);
} else if (item_type === 'shipattachment') {
await item.createEmbeddedDocuments(
"ActiveEffect",
Expand Down Expand Up @@ -664,18 +690,84 @@ export default class ModifierHelpers {
}
}

static async updateAEsForEquip(actor, item, equipped) {
// determine how much encumbrance our item is (across all of its AEs)
// update the "equipped" encumbrance to that value - 3 (to a minimum of 0)
// disable the "current" one and enable the "equipped" one
if (equipped) {
console.log("enabling equipped encumbrance")
let total = ModifierHelpers.getActiveAEModifierValue(item, 'Encumbrance');
await ModifierHelpers.updateEmbeddedActiveEffect(
actor,
'Encumbrance (Equipped)',
'Encumbrance (Equipped)',
'',
Math.max(total - 3, 0), // configure it to be current encumbrance - 3
true,
);
await ModifierHelpers.updateEmbeddedActiveEffect(
actor,
'Encumbrance (Current)',
'Encumbrance (Current)',
'',
total,
false,
);
} else {
console.log("disabling equipped encumbrance")
await ModifierHelpers.updateEmbeddedActiveEffect(
actor,
'Encumbrance (Equipped)',
'Encumbrance (Equipped)',
'',
0,
false,
);
let total = ModifierHelpers.getActiveAEModifierValue(item, 'Encumbrance');
await ModifierHelpers.updateEmbeddedActiveEffect(
actor,
'Encumbrance (Current)',
'Encumbrance (Current)',
'',
total,
true,
);
}
}

static async updateEmbeddedActiveEffect(actor, effect_id, modifier_category, modifier, value, active) {
let effect = actor.effects.filter(i => i.label === effect_id);
if (!effect) {
return;
}
effect = effect[0];
for (let change of effect.changes) {
let changes = [];
let data = ModifierHelpers.determineModifierKey(modifier_category, modifier);
data['keys'].forEach(function (key) {
changes.push({
key: key,
mode: data['mode'],
value: value,
});
});
await effect.update({changes: changes});
}
await effect.update({disabled: !active});
}

static getActiveAEModifierValue(actor, attribute) {
// TODO: this can probably be enhanced to return the source as well (to retain the feature... I asked for :p)
// TODO: refactor console.log statements into debug statements
// find (active) active effects
console.log(`Looking for AEs impacting ${attribute} on ${actor.name}`)
//console.log(`Looking for AEs impacting ${attribute} on ${actor.name}`)
const effects = actor.effects.contents.filter(i => i.disabled === false);
let value = 0;
effects.forEach(function (effect) {
// step through the changes
effect.changes.forEach(function (change) {
// check if the change key is the key for our attribute
if (change.key === `system.attributes.${attribute}.value` || change.key === testMap[attribute]) {
if (change.key === `system.attributes.${attribute}.value` || change.key === testMap[attribute] || change.key === `system.stats.${attribute.toLowerCase()}.value` || change.key === `system.stats.${attribute}.value`) {
if (change.mode === CONST.ACTIVE_EFFECT_MODES.ADD) {
value += parseInt(change.value);
} else if (change.mode === CONST.ACTIVE_EFFECT_MODES.SUBTRACT) {
Expand All @@ -688,7 +780,6 @@ export default class ModifierHelpers {
}
});
});
console.log(`Final value: ${value}`)
return value;
}

Expand All @@ -702,7 +793,7 @@ export default class ModifierHelpers {
// TODO: this can probably be enhanced to return the source as well (to retain the feature... I asked for :p)
// TODO: refactor console.log statements into debug statements
// find (active) active effects
console.log(`Looking for skill AEs impacting ${skill}/${modifier_type} on ${actor.name}`)
//console.log(`Looking for skill AEs impacting ${skill}/${modifier_type} on ${actor.name}`)
const effects = actor.effects.contents.filter(i => i.disabled === false);
let value = 0;
let sources = [];
Expand All @@ -711,7 +802,7 @@ export default class ModifierHelpers {
effect.changes.forEach(function (change) {
// check if the change key is the key for our attribute
if (change.key === `system.skills.${skill}.${testSkillModMap[modifier_type]}`) {
console.log(effect)
//console.log(effect)
// TODO: this code is brittle and should probably be refactored
sources.push(actor.items.filter(i => i.id === effect.origin.split('.')[3])[0].name)
if (isNaN(parseInt(change.value))) {
Expand All @@ -730,7 +821,7 @@ export default class ModifierHelpers {
}
});
});
console.log(`Final ${skill} value: ${value}, sources; ${sources}`)
//console.log(`Final ${skill} value: ${value}, sources; ${sources}`)
return {
'total': value,
'sources': sources,
Expand Down
6 changes: 6 additions & 0 deletions modules/items/item-ffg.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,15 @@ export class ItemFFG extends ItemBaseFFG {
});
}

console.log("found the following active modifiers")
console.log(active_modifiers)

data.soak.adjusted += ModifierHelpers.calculateValueFromModifiers(active_modifiers, "soak", "Armor Stat");
data.defence.adjusted += ModifierHelpers.calculateValueFromModifiers(active_modifiers, "defence", "Armor Stat");
console.log("encumbrance")
console.log(data.encumbrance.adjusted)
data.encumbrance.adjusted += ModifierHelpers.calculateValueFromModifiers(active_modifiers, "encumbrance", "Armor Stat");
console.log(data.encumbrance.adjusted)
data.price.adjusted += ModifierHelpers.calculateValueFromModifiers(active_modifiers, "price", "Armor Stat");
data.rarity.adjusted += ModifierHelpers.calculateValueFromModifiers(active_modifiers, "rarity", "Armor Stat");
console.log("before")
Expand Down

0 comments on commit a7c1f73

Please sign in to comment.