shape change part 2

This commit is contained in:
Mike Bloy 2024-05-27 23:53:39 -05:00
parent 08d2be4ea0
commit e438fd36e8
3 changed files with 268 additions and 39 deletions

View File

@ -733,4 +733,171 @@ export class ActorFolderEffect extends PowerEffect {
}
return actors;
}
// eslint-disable-next-line no-unused-vars
actorValue(actor) {
return 0;
}
getActors() {
this.data.actors = this.prepActors();
const choices = {};
const effects = {};
const values = {};
Object.keys(this.data.actors)
.filter((k) => !k.includes('_template'))
.sort()
.forEach((key) => {
const id = this.data.actors[key].id;
choices[id] = key;
effects[id] = null;
values[id] = this.actorValue(this.data.actors[key]);
});
return { choices, effects, values };
}
get modifiers() {
const { choices, effects, values } = this.getActors();
return [
...super.modifiers,
{
name: 'Select Creature',
id: 'actorId',
type: 'select',
choices,
effects,
values,
epic: false,
effect: false,
},
];
}
get spawnUpdates() {
const updates = {
actor: {},
token: {
actorLink: false,
},
embedded: {
ActiveEffect: {},
Item: {},
},
};
return updates;
}
#documentFinder(documentType, oldDoc, newDoc) {
if (documentType === 'Item') {
return oldDoc.name.toLowerCase() === newDoc.name.toLowerCase() && oldDoc.type === newDoc.type;
}
return oldDoc.name.toLowerCase() === newDoc.name.toLowerCase();
}
async updateEmbedded(actor, newDocs) {
const adds = {};
const updates = {};
for (const documentType of Object.keys(newDocs ?? {})) {
const collection = actor.getEmbeddedCollection(documentType);
adds[documentType] = [];
updates[documentType] = [];
for (const newDocKey in newDocs[documentType]) {
const newDoc = newDocs[documentType][newDocKey];
const oldDoc = collection.find((doc) => this.#documentFinder(documentType, doc, newDoc));
if (oldDoc) {
const _id = oldDoc.id;
updates[documentType].push({ ...newDoc, _id });
} else {
adds[documentType].push(newDoc);
}
}
const updateOpts = {};
if (documentType === 'Item') {
updateOpts.renderSheet = null;
}
try {
if (adds[documentType].length > 0) {
actor.createEmbeddedDocuments(documentType, adds[documentType], updateOpts);
}
} catch (e) {
log('ERROR', e);
}
try {
if (updates[documentType].length > 0) {
actor.updateEmbeddedDocuments(documentType, updates[documentType], updateOpts);
}
} catch (e) {
log('ERROR', e);
}
}
}
async parseValues() {
await super.parseValues();
this.data.maintid = randomID();
this.targetActor = await game.actors.get(this.data.actorId);
this.targetTokenDoc = await this.targetActor.getTokenDocument();
const sourceUpdates = {
delta: {
ownership: {
[game.user.id]: CONST.DOCUMENT_PERMISSION_LEVELS.OWNER,
},
},
};
this.targetTokenDoc.updateSource(sourceUpdates);
}
async spawn() {
this.targetTokenDoc.updateSource({
x: this.source.x,
y: this.source.y,
elevation: this.source.elevation,
});
return this.source.scene.createEmbeddedDocuments('Token', [this.targetTokenDoc]);
}
async apply() {
this.data.spawned = await this.spawn();
const updates = this.spawnUpdates;
const secondaryDocs = await this.createSecondaryEffects(this.data.maintId);
const primaryDoc = await this.createPrimaryEffect(this.data.maintId);
const promises = [];
for (const token of this.data.spawned) {
if (updates?.token) {
promises.push(token.update(updates.token));
}
if (updates?.actor) {
promises.push(token.actor.update(updates.actor));
}
if (updates?.embedded) {
promises.push(this.updateEmbedded(token.actor, updates.embedded));
}
const activeEffects = await this.secondaryDocsForTarget(secondaryDocs, token);
activeEffects.push(await this.primaryDocForTarget(primaryDoc, token));
promises.push(token.actor.createEmbeddedDocuments('ActiveEffect', activeEffects));
}
const maintainDoc = await this.createMaintainEffect(this.data.maintId);
if (this.duration > 0) {
promises.push(this.applyActiveEffects(this.source, [maintainDoc]));
}
await Promise.all(promises);
}
async sideEffects() {
if (this.data.fatigue) {
for (const target of this.data.spawned) {
const actor = target.actor;
const update = {
system: {
fatigue: {
value: actor.system.fatigue.value + 1,
},
},
};
if (actor.system.fatigue.value < actor.system.fatigue.max) {
await actor.update(update);
}
}
}
}
}

View File

@ -1,3 +1,4 @@
import { moduleName } from '../globals.js';
import { ActorFolderEffect } from './basePowers.js';
export class ShapeChangeEffect extends ActorFolderEffect {
@ -17,6 +18,10 @@ export class ShapeChangeEffect extends ActorFolderEffect {
return true;
}
get oneTarget() {
return true;
}
get isRaisable() {
return true;
}
@ -33,48 +38,24 @@ export class ShapeChangeEffect extends ActorFolderEffect {
return 'Morphables';
}
getShapeChangeActors() {
this.data.actors = this.prepActors();
const choices = {};
const effects = {};
const values = {};
Object.keys(this.data.actors)
.filter((k) => !k.includes('_template'))
.sort()
.forEach((key) => {
const id = this.data.actors[key].id;
const size = this.data.actors[key].system.stats.size;
let value = 3;
if (size >= 5) {
value = 15;
} else if (size >= 3) {
value = 11;
} else if (size >= 1) {
value = 8;
} else if (size >= 0) {
value = 5;
}
choices[id] = key;
effects[id] = null;
values[id] = value;
});
return { choices, effects, values };
actorValue(actor) {
const size = actor.system.stats.size;
let value = 3;
if (size >= 5) {
value = 15;
} else if (size >= 3) {
value = 11;
} else if (size >= 1) {
value = 8;
} else if (size >= 0) {
value = 5;
}
return value;
}
get modifiers() {
const { choices, effects, values } = this.getShapeChangeActors();
return [
...super.modifiers,
{
name: 'Shape Change Into',
id: 'actorId',
type: 'select',
choices,
effects,
values,
epic: false,
effect: false,
},
{
name: 'Duration',
type: 'checkbox',
@ -84,7 +65,7 @@ export class ShapeChangeEffect extends ActorFolderEffect {
effect: false,
},
{
name: 'Transform',
name: 'Transform Other?',
type: 'select',
default: 'none',
id: 'transform',
@ -100,6 +81,87 @@ export class ShapeChangeEffect extends ActorFolderEffect {
];
}
async parseValues() {
await super.parseValues();
this.target = this?.targets?.[0] ?? this.source;
this.data.actorUpdates = {
name: `${this.target.actor.name} (${this.targetActor.name} form)`,
system: {
wildcard: this.target.actor.system.wildcard,
attributes: {},
},
};
for (const stat of ['smarts', 'spirit']) {
this.data.actorUpdates.system.attributes[stat] = {
die: this.target.actor.system.attributes[stat].die,
'wild-die': this.target.actor.system.attributes[stat]['wild-die'],
};
}
this.data.tokenUpdates = {
flags: {
[moduleName]: { 'shapeChange.srcTokenId': this.target.id },
},
actorLink: false,
name: `${this.target.name} (${this.targetActor.prototypeToken.name} form)`,
disposition: this.target.document.disposition,
sight: {
enabled: true,
},
};
this.data.embeddedUpdates = {
ActiveEffect: {},
Item: {},
};
for (const effect of this.target.actor.effects) {
const doc = await this.target.actor.getEmbeddedDocument('ActiveEffect', effect.id);
this.data.embeddedUpdates.ActiveEffect[effect.name] = doc;
}
for (const item of this.target.actor.items.filter(
(i) =>
(i.type === 'skill' && ['smarts', 'spirit'].includes(i.system.attribute)) ||
['power', 'edge', 'hindrance', 'action'].includes(i.type),
)) {
const doc = await this.target.actor.getEmbeddedDocument('Item', item.id);
this.data.embeddedUpdates.Item[item.name] = doc;
}
}
async spawn() {
this.targetTokenDoc.updateSource({
x: this.target.x,
y: this.target.y,
elevation: this.target.elevation,
});
return this.source.scene.createEmbeddedDocuments('Token', [this.targetTokenDoc]);
}
get effectName() {
return `Shape Change into ${this.targetActor.prototypeToken.name}`;
}
getPrimaryEffectChanges() {
const changes = super.getPrimaryEffectChanges();
if (this.data.raise) {
for (const stat of ['vigor', 'strength']) {
changes.push({
key: `system.attributes.${stat}.die.sides`,
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: 2,
priority: 0,
});
}
}
return changes;
}
get spawnUpdates() {
const updates = super.spawnUpdates;
mergeObject(updates.actor, this.data.actorUpdates);
mergeObject(updates.token, this.data.tokenUpdates);
mergeObject(updates.embedded, this.data.embeddedUpdates);
return updates;
}
get description() {
let desc = super.description;
return desc;

View File

@ -17,7 +17,7 @@
{{#if targets.length}}
<p>
<strong>Targets</strong>:
{{#each targets}}{{#if @index}}, {{/if}}{{this}}{{/each}}
{{#each targets}}{{#if @index}}, {{/if}}{{{this}}}{{/each}}
{{#if recipients.cost}}
<br>({{#if recipients.epic}}⭐ {{/if}}Additional Recipients
{{recipients.cost}}pp each × {{recipients.count}} = {{recipients.total}})