Skip to content

Commit 6c585a7

Browse files
committed
WIP - fix effects
1 parent 9937f29 commit 6c585a7

217 files changed

Lines changed: 1938 additions & 944 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/lang/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@
817817
"anima.permissions.modifyDiceFormulasPermission.hint": "Allows the user to modify the dice settings used by the actors.",
818818
"customHotbar.damageCalculator": "Damage calculator",
819819
"customHotbar.sendAttack": "Send attack",
820-
"anima.ui.tooltips.base": "Final",
820+
"anima.ui.tooltips.base": "Base",
821821
"anima.ui.tooltips.special": "Special",
822822
"anima.ui.tooltips.final": "Final",
823823
"anima.ui.tooltips.mod": "Modifier",

src/lang/es.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,7 @@
819819
"anima.permissions.modifyDiceFormulasPermission.hint": "Permite al usuario cambiar las fórmulas de dados usadas por los actores.",
820820
"customHotbar.damageCalculator": "Calculadora de daño",
821821
"customHotbar.sendAttack": "Enviar ataque",
822-
"anima.ui.tooltips.base": "Final",
822+
"anima.ui.tooltips.base": "Base",
823823
"anima.ui.tooltips.special": "Especial",
824824
"anima.ui.tooltips.final": "Final",
825825
"anima.ui.tooltips.mod": "Modificador",

src/lang/fr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@
537537
"anima.ui.tabs.secondaries": "Secondaires",
538538
"anima.ui.tabs.effects": "Efectos",
539539
"anima.ui.titleSection.title": "Titres",
540-
"anima.ui.tooltips.base": "Final",
540+
"anima.ui.tooltips.base": "Base",
541541
"anima.ui.tooltips.final": "Final",
542542
"anima.ui.tooltips.special": "Spécial",
543543
"anima.ui.tooltips.mod": "Modifier",

src/module/actor/ABFActor.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ export class ABFActor extends Actor {
5151
overwrite: false
5252
});
5353

54-
// Inflate typed nodes from __type and remove markers
55-
data.system = inflateSystemFromTypeMarkers(data.system);
56-
5754
return super._preCreate(data, options, user);
5855
}
5956

@@ -68,10 +65,38 @@ export class ABFActor extends Actor {
6865
this.system = inflateSystemFromTypeMarkers(this.system);
6966

7067
buildTypedNodes(this, TYPED_PATHS);
68+
this._applyDefaultKeysToTypedNodes();
7169
await prepareActor(this);
7270
// applyTypedDerived(this);
7371
}
7472

73+
/**
74+
* Ensure BaseType.key is persisted when empty.
75+
* Derives it from the last segment of the system path.
76+
*/
77+
_applyDefaultKeysToTypedNodes() {
78+
const updates = {};
79+
80+
// typedNodes: Map<path, BaseTypeInstance>
81+
for (const [path, node] of this.typedNodes ?? []) {
82+
// Only if the type actually has a "key" field in its defaults/common defaults
83+
// (BaseType.commonDefaults includes key)
84+
85+
const current = foundry.utils.getProperty(this.system, `${path}.key`);
86+
87+
if (current == null || String(current) === '') {
88+
const parts = String(path).split('.');
89+
const derivedKey = parts.length ? parts[parts.length - 1] : '';
90+
if (derivedKey) updates[`${path}.key`] = derivedKey;
91+
}
92+
}
93+
94+
if (Object.keys(updates).length) {
95+
// Persist without triggering another full update cycle
96+
this.updateSource(updates);
97+
}
98+
}
99+
75100
getRollData() {
76101
const data = super.getRollData();
77102

src/module/actor/ABFActorSheet.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ABFDialogs } from '../dialogs/ABFDialogs';
1111
import { Logger } from '../../utils';
1212
import { ABFSettingsKeys } from '../../utils/registerSettings';
1313
import { createClickHandlers } from './utils/createClickHandlers';
14+
import { TypeEditorRegistry } from './types/TypeEditorRegistry.js';
1415

1516
/** @typedef {import('./constants').TActorData} TData */
1617
/** @typedef {typeof FormApplication<FormApplicationOptions, TData, TData>} TFormApplication */
@@ -129,6 +130,8 @@ export default class ABFActorSheet extends ActorSheet {
129130
// Everything below here is only needed if the sheet is editable
130131
if (!this.options.editable) return;
131132

133+
this._activateBaseTypeContextMenu(html);
134+
132135
const handler = ev => this._onDragStart(ev);
133136

134137
// Find all items on the character sheet.
@@ -180,6 +183,29 @@ export default class ABFActorSheet extends ActorSheet {
180183
html.find('.effect-control').click(this._onEffectControl.bind(this));
181184
}
182185

186+
_activateBaseTypeContextMenu(html) {
187+
new ContextMenu(html, '.base-type-row', [
188+
{
189+
name: game.i18n.localize('contextualMenu.common.options.edit') ?? 'Edit…',
190+
icon: '<i class="fas fa-edit fa-fw"></i>',
191+
callback: target => this._openBaseTypeEditor(target[0])
192+
}
193+
]);
194+
}
195+
196+
_openBaseTypeEditor(el) {
197+
const path = el?.dataset?.path;
198+
if (!path) return;
199+
200+
const node = this.actor.typedNodes?.get(path) ?? null;
201+
if (!node) return;
202+
203+
const type = node.constructor.type;
204+
205+
const app = TypeEditorRegistry.create(type, this.actor, { path });
206+
app?.render(true);
207+
}
208+
183209
async _onEffectControl(event) {
184210
event.preventDefault();
185211
const a = event.currentTarget;

src/module/actor/constants.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export const INITIAL_ACTOR_DATA = {
9191
}
9292
},
9393
extraDamage: {
94-
value: 0
94+
__type: '{"type":"NumericalValue"}'
9595
}
9696
},
9797
destinyPoints: {
@@ -973,15 +973,7 @@ export const INITIAL_ACTOR_DATA = {
973973
armors: [],
974974
supernaturalShields: [],
975975
damageReduction: {
976-
base: {
977-
value: 0
978-
},
979-
special: {
980-
value: 0
981-
},
982-
final: {
983-
value: 0
984-
}
976+
__type: '{"type":"NumericalValue"}'
985977
}
986978
},
987979

src/module/actor/types/BaseType.js

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,39 @@ export class BaseType {
1010
this.systemPath = systemPath;
1111
}
1212

13+
// ---- Instance extra deps (per derived spec id) ----
14+
_instanceExtraDeps = new Map();
15+
16+
/** @param {string} specId @param {string[]} deps */
17+
setInstanceDeps(specId, deps) {
18+
const arr = Array.isArray(deps) ? deps.filter(Boolean) : [];
19+
this._instanceExtraDeps.set(specId, arr);
20+
}
21+
22+
/** @param {string} specId */
23+
clearInstanceDeps(specId) {
24+
this._instanceExtraDeps.delete(specId);
25+
}
26+
27+
/** @param {string} specId @returns {string[]} */
28+
getInstanceDeps(specId) {
29+
return this._instanceExtraDeps.get(specId) ?? [];
30+
}
31+
32+
/**
33+
* Merge instance deps into the provided specs.
34+
* Call this at the end of getDerivedFlowSpecs() in subclasses.
35+
*
36+
* @param {Array<{id:string,deps:string[],mods:string[],compute:Function}>} specs
37+
*/
38+
_mergeInstanceDeps(specs) {
39+
return (specs ?? []).map(s => {
40+
const extra = this.getInstanceDeps(s.id);
41+
if (!extra.length) return s;
42+
return { ...s, deps: [...(s.deps ?? []), ...extra] };
43+
});
44+
}
45+
1346
_get(relPath, fallback = undefined) {
1447
return (
1548
foundry.utils.getProperty(this.actor, `${this.systemPath}.${relPath}`) ?? fallback
@@ -44,7 +77,12 @@ export class BaseType {
4477
throw new Error(`${this.constructor.name}.applyDerived() not implemented.`);
4578
}
4679

47-
/** Structure defaults used for migrations */
80+
/** Common fields shared by all typed nodes */
81+
static commonDefaults() {
82+
return { key: '' };
83+
}
84+
85+
/** Type-specific defaults used for migrations */
4886
static defaults() {
4987
return {};
5088
}
@@ -63,13 +101,18 @@ export class BaseType {
63101

64102
const normalized = this.normalizeInflateInput(combined);
65103

66-
const merged = foundry.utils.mergeObject(this.defaults(), normalized, {
104+
const base = foundry.utils.mergeObject(BaseType.commonDefaults(), this.defaults(), {
105+
inplace: false,
106+
insertKeys: true,
107+
insertValues: true
108+
});
109+
110+
const merged = foundry.utils.mergeObject(base, normalized, {
67111
inplace: false,
68112
insertKeys: true,
69113
insertValues: true
70114
});
71115

72-
// IMPORTANT: remove legacy keys like "value" or "__type"
73116
return this.pruneToDefaults(merged);
74117
}
75118

@@ -82,7 +125,11 @@ export class BaseType {
82125
static pruneToDefaults(obj) {
83126
if (!obj || typeof obj !== 'object') return obj;
84127

85-
const allowed = new Set(Object.keys(this.defaults()));
128+
const allowed = new Set([
129+
...Object.keys(BaseType.commonDefaults()),
130+
...Object.keys(this.defaults())
131+
]);
132+
86133
for (const k of Object.keys(obj)) {
87134
if (!allowed.has(k)) delete obj[k];
88135
}
@@ -96,11 +143,102 @@ export class BaseType {
96143
* @returns {object}
97144
*/
98145
static sanitize(data) {
99-
const merged = foundry.utils.mergeObject(this.defaults(), data ?? {}, {
146+
const base = foundry.utils.mergeObject(BaseType.commonDefaults(), this.defaults(), {
100147
inplace: false,
101148
insertKeys: true,
102149
insertValues: true
103150
});
151+
152+
const merged = foundry.utils.mergeObject(base, data ?? {}, {
153+
inplace: false,
154+
insertKeys: true,
155+
insertValues: true
156+
});
157+
104158
return this.pruneToDefaults(merged);
105159
}
160+
161+
getByKey(type, key) {
162+
return this.actor.typedRepo?.get(type)?.get(key) ?? null;
163+
}
164+
165+
getValueByKey(type, key, relPath) {
166+
const node = this.getByKey(type, key);
167+
if (!node) return undefined;
168+
return foundry.utils.getProperty(this.actor, `${node.systemPath}.${relPath}`);
169+
}
170+
171+
get key() {
172+
// If key is not stored, default to the last segment of the system path
173+
const stored = this._get('key', '');
174+
if (stored) return stored;
175+
176+
const parts = String(this.systemPath ?? '').split('.');
177+
return parts.length ? parts[parts.length - 1] : '';
178+
}
179+
180+
set key(v) {
181+
this._set('key', String(v ?? ''));
182+
}
183+
184+
/**
185+
* Persist the default key if it's missing or empty.
186+
* This writes into the actor's system data (in-memory). Persisting to the document
187+
* should be done by the caller (e.g. ABFActor.updateSource in prepareDerivedData).
188+
*
189+
* @returns {string} The key after ensuring.
190+
*/
191+
ensureDefaultKey() {
192+
const current = this._get('key', undefined);
193+
if (current == null || current === '') {
194+
const derived = BaseType.deriveKeyFromSystemPath(this.systemPath);
195+
if (derived) this._set('key', derived);
196+
return derived;
197+
}
198+
return String(current);
199+
}
200+
201+
/**
202+
* Static helper to persist a default key on raw system data, without needing an instance.
203+
* Useful if you prefer to patch this.system directly in ABFActor.
204+
*
205+
* @param {object} systemRoot Usually actor.system
206+
* @param {string} systemPath Path to the node (e.g. "system.characteristics.primaries.agility")
207+
* @returns {boolean} True if it changed something
208+
*/
209+
static ensureDefaultKeyOnNodeData(systemRoot, systemPath) {
210+
if (!systemRoot || typeof systemRoot !== 'object') return false;
211+
if (!systemPath || typeof systemPath !== 'string') return false;
212+
213+
const keyPath = `${systemPath}.key`;
214+
const current = foundry.utils.getProperty(systemRoot, keyPath);
215+
216+
if (current == null || current === '') {
217+
const derived = BaseType.deriveKeyFromSystemPath(systemPath);
218+
if (!derived) return false;
219+
foundry.utils.setProperty(systemRoot, keyPath, derived);
220+
return true;
221+
}
222+
223+
return false;
224+
}
225+
226+
static deriveKeyFromSystemPath(systemPath) {
227+
const parts = String(systemPath ?? '').split('.');
228+
return parts.length ? parts[parts.length - 1] : '';
229+
}
230+
231+
static editorConfig() {
232+
return {
233+
readonly: ['key'],
234+
hidden: [],
235+
labels: { key: 'Key' },
236+
order: [],
237+
overrides: {
238+
// Example:
239+
// key: { readonly: false },
240+
// 'someField.value': { hidden: true }
241+
}
242+
};
243+
}
106244
}

0 commit comments

Comments
 (0)