Merge branch 'master' into bug-reporter-support

This commit is contained in:
Johannes Loher 2021-02-18 14:51:01 +01:00
commit f0ba708e7f
14 changed files with 347 additions and 103 deletions

View file

@ -211,5 +211,8 @@
"DS4.ChatVisibilityRoll": "Alle",
"DS4.ChatVisibilityGmRoll": "Selbst & SL",
"DS4.ChatVisibilityBlindRoll": "Nur SL",
"DS4.ChatVisibilitySelfRoll": "Nur selbst"
"DS4.ChatVisibilitySelfRoll": "Nur selbst",
"DS4.TooltipBaseValue": "Basiswert",
"DS4.TooltipModifier": "Modifikator",
"DS4.TooltipEffects": "Effekte"
}

View file

@ -211,5 +211,8 @@
"DS4.ChatVisibilityRoll": "All",
"DS4.ChatVisibilityGmRoll": "Self & GM",
"DS4.ChatVisibilityBlindRoll": "GM only",
"DS4.ChatVisibilitySelfRoll": "Self only"
"DS4.ChatVisibilitySelfRoll": "Self only",
"DS4.TooltipBaseValue": "Base Value",
"DS4.TooltipModifier": "Modifier",
"DS4.TooltipEffects": "Effects"
}

View file

@ -1,4 +1,5 @@
import { ModifiableData } from "../common/common-data";
import { DS4 } from "../config";
import { DS4Item } from "../item/item";
import { ItemType } from "../item/item-data";
import { DS4ActorData } from "./actor-data";
@ -8,8 +9,22 @@ import { DS4ActorData } from "./actor-data";
*/
export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
/** @override */
prepareDerivedData(): void {
prepareData(): void {
this.data = duplicate(this._data) as DS4ActorData;
if (!this.data.img) this.data.img = CONST.DEFAULT_TOKEN;
if (!this.data.name) this.data.name = "New " + this.entity;
this.prepareBaseData();
this.prepareEmbeddedEntities();
this.applyActiveEffectsToBaseData();
this.prepareDerivedData();
this.applyActiveEffectsToDerivedData();
this.prepareFinalDerivedData();
}
/** @override */
prepareBaseData(): void {
const data = this.data;
const attributes = data.data.attributes;
Object.values(attributes).forEach(
(attribute: ModifiableData<number>) => (attribute.total = attribute.base + attribute.mod),
@ -17,10 +32,98 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
const traits = data.data.traits;
Object.values(traits).forEach((trait: ModifiableData<number>) => (trait.total = trait.base + trait.mod));
}
applyActiveEffectsToBaseData(): void {
// reset overrides because our variant of applying active effects does not set them, it only adds overrides
this.overrides = {};
this.applyActiveEffectsFiltered(
(change) =>
!this.derivedDataProperties.includes(change.key) &&
!this.finalDerivedDataProperties.includes(change.key),
);
}
applyActiveEffectsToDerivedData(): void {
this.applyActiveEffectsFiltered((change) => this.derivedDataProperties.includes(change.key));
}
/**
* Apply ActiveEffectChanges to the Actor data which are caused by ActiveEffects and satisfy the given predicate.
*
* @param predicate - The predicate that ActiveEffectChanges need to satisfy in order to be applied
*/
applyActiveEffectsFiltered(predicate: (change: ActiveEffectChange) => boolean): void {
const overrides: Record<string, unknown> = {};
// Organize non-disabled effects by their application priority
const changes = this.effects.reduce(
(changes: Array<ActiveEffectChange & { effect: ActiveEffect<DS4Actor> }>, e) => {
if (e.data.disabled) return changes;
const item = this._getOriginatingItemOfActiveEffect(e);
if (item?.isNonEquippedEuipable()) return changes;
const factor = item?.activeEffectFactor ?? 1;
return changes.concat(
e.data.changes.filter(predicate).flatMap((c) => {
const duplicatedChange = duplicate(c) as ActiveEffect.Change;
duplicatedChange.priority = duplicatedChange.priority ?? duplicatedChange.mode * 10;
return Array(factor).fill({
...duplicatedChange,
effect: e,
});
}),
);
},
[],
);
changes.sort((a, b) => a.priority - b.priority);
// Apply all changes
for (const change of changes) {
const result = change.effect.apply(this, change);
if (result !== null) overrides[change.key] = result;
}
// Expand the set of final overrides
this.overrides = expandObject({ ...flattenObject(this.overrides), ...overrides });
}
protected _getOriginatingItemOfActiveEffect(effect: ActiveEffect<DS4Actor>): DS4Item | undefined {
return this.items.find((item) => item.uuid === effect.data.origin) ?? undefined;
}
/**
* Apply transformations to the Actor data after effects have been applied to the base data.
* @override
*/
prepareDerivedData(): void {
this._prepareCombatValues();
}
/**
* The list of properties that are derived from others, given in dot notation.
*/
get derivedDataProperties(): Array<string> {
return Object.keys(DS4.i18n.combatValues).map((combatValue) => `data.combatValues.${combatValue}.total`);
}
/**
* Apply final transformations to the Actor data after all effects have been applied.
*/
prepareFinalDerivedData(): void {
this.data.data.combatValues.hitPoints.max = this.data.data.combatValues.hitPoints.total;
}
/**
* The list of properties that are completely derived (i.e. {@link ActiveEffect}s cannot be applied to them),
* given in dot notation.
*/
get finalDerivedDataProperties(): string[] {
return ["data.combatValues.hitPoints.max"];
}
/**
* The list of item types that can be owned by this actor.
*/
@ -57,12 +160,12 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
/**
* Prepares the combat values of the actor.
*/
private _prepareCombatValues(): void {
protected _prepareCombatValues(): void {
const data = this.data.data;
const armorValueOfEquippedItems = this._calculateArmorValueOfEquippedItems();
data.combatValues.hitPoints.base =
(data.attributes.body.total ?? 0) + (data.traits.constitution.total ?? 0) + 10;
data.combatValues.defense.base =
(data.attributes.body.total ?? 0) + (data.traits.constitution.total ?? 0) + armorValueOfEquippedItems;
data.combatValues.initiative.base = (data.attributes.mobility.total ?? 0) + (data.traits.agility.total ?? 0);
@ -78,14 +181,12 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
Object.values(data.combatValues).forEach(
(combatValue: ModifiableData<number>) => (combatValue.total = combatValue.base + combatValue.mod),
);
data.combatValues.hitPoints.max = data.combatValues.hitPoints.total;
}
/**
* Calculates the total armor value of all equipped items.
*/
private _calculateArmorValueOfEquippedItems(): number {
protected _calculateArmorValueOfEquippedItems(): number {
return this.items
.map((item) => {
if (item.data.type === "armor" || item.data.type === "shield") {

View file

@ -1,3 +1,4 @@
import { ModifiableMaybeData } from "../../common/common-data";
import { DS4 } from "../../config";
import { DS4Item } from "../../item/item";
import { DS4ItemData } from "../../item/item-data";
@ -50,7 +51,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
*/
async getData(): Promise<ActorSheet.Data<DS4Actor>> {
const data = {
...(await super.getData()),
...this._addTooltipsToData(await super.getData()),
// Add the localization config to the data:
config: DS4,
// Add the items explicitly sorted by type to the data:
@ -59,6 +60,24 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
return data;
}
protected _addTooltipsToData(data: ActorSheet.Data<DS4Actor>): ActorSheet.Data<DS4Actor> {
const valueGroups = [data.data.attributes, data.data.traits, data.data.combatValues];
valueGroups.forEach((valueGroup) => {
Object.values(valueGroup).forEach(
(attribute: ModifiableMaybeData<number | null> & { tooltip?: string }) => {
attribute.tooltip = this._getTooltipForValue(attribute);
},
);
});
return data;
}
protected _getTooltipForValue(value: ModifiableMaybeData<number | null>): string {
return `${value.base} (${game.i18n.localize("DS4.TooltipBaseValue")}) + ${value.mod} (${game.i18n.localize(
"DS4.TooltipModifier",
)}) ${game.i18n.localize("DS4.TooltipEffects")} ${value.total}`;
}
/** @override */
activateListeners(html: JQuery): void {
super.activateListeners(html);
@ -127,7 +146,6 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
*/
protected _onItemChange(ev: JQuery.ChangeEvent): void {
ev.preventDefault();
console.log("Current target:", $(ev.currentTarget).get(0)["name"]);
const el: HTMLFormElement = $(ev.currentTarget).get(0);
const id = $(ev.currentTarget).parents(".item").data("itemId");
const item = duplicate<DS4Item, "lenient">(this.actor.getOwnedItem(id));
@ -224,9 +242,13 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
/** @override */
protected async _onDropItem(
event: DragEvent,
data: { type: "Item" } & (DeepPartial<ActorSheet.OwnedItemData<DS4Actor>> | { pack: string } | { id: string }),
data: { type: "Item" } & (
| { data: DeepPartial<ActorSheet.OwnedItemData<DS4Actor>> }
| { pack: string }
| { id: string }
),
): Promise<boolean | undefined | ActorSheet.OwnedItemData<DS4Actor>> {
const item = ((await Item.fromDropData(data)) as unknown) as DS4Item;
const item = await DS4Item.fromDropData(data);
if (item && !this.actor.canOwnItemType(item.data.type)) {
ui.notifications?.warn(
game.i18n.format("DS4.WarningActorCannotOwnItem", {

View file

@ -32,8 +32,6 @@ type DS4LanguageData = DS4ItemDataHelper<DS4LanguageDataData, "language">;
type DS4AlphabetData = DS4ItemDataHelper<DS4AlphabetDataData, "alphabet">;
type DS4SpecialCreatureAbilityData = DS4ItemDataHelper<DS4SpecialCreatureAbilityDataData, "specialCreatureAbility">;
// types
interface DS4WeaponDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {
attackType: "melee" | "ranged" | "meleeRanged";
weaponBonus: number;

View file

@ -49,7 +49,6 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Data<DS4Item>> {
actor: this.item.actor,
isPhysical: isDS4ItemDataTypePhysical(this.item.data.data),
};
console.log(data);
return data;
}

View file

@ -18,4 +18,18 @@ export class DS4Item extends Item<DS4ItemData> {
data.rank.total = data.rank.base + data.rank.mod;
}
}
isNonEquippedEuipable(): boolean {
return "equipped" in this.data.data && !this.data.data.equipped;
}
/**
* The number of times that active effect changes originating from this item should be applied.
*/
get activeEffectFactor(): number | undefined {
if (this.data.type === "talent") {
return this.data.data.rank.total;
}
return 1;
}
}

View file

@ -18,6 +18,9 @@
grid-column: span 2;
line-height: $default-input-height;
}
.attribute-value-arrow {
padding: 0 5px;
}
}
}
.trait {
@ -41,6 +44,9 @@
grid-column: span 2;
line-height: $default-input-height;
}
.trait-value-arrow {
padding: 0 5px;
}
}
}
}

View file

@ -14,10 +14,13 @@
<div class="attribute flexrow"><label for="data.attributes.body.total"
class="attribute-label ">{{attribute-label}}</label>
<div class="attribute-value flex15 grid grid-8col"><input type="number"
name="data.attributes.{{attribute-key}}.base" value='{{attribute-data.base}}' data-dtype="Number" /><span> +
name="data.attributes.{{attribute-key}}.base" value='{{attribute-data.base}}' data-dtype="Number"
title="{{attribute-label}} {{localize 'DS4.TooltipBaseValue'}}" /><span> +
</span><input type="number" name="data.attributes.{{attribute-key}}.mod" value='{{attribute-data.mod}}'
data-dtype="Number" /><span> =
</span><span class="attribute-value-total">{{attribute-data.total}}</span></div>
data-dtype="Number" title="{{attribute-label}} {{localize 'DS4.TooltipModifier'}}" /><span
class="attribute-value-arrow">➞</span><span class="attribute-value-total"
title="{{attribute-label}}: {{attribute-data.tooltip}}">{{attribute-data.total}}</span>
</div>
</div>
{{/inline}}
@ -32,10 +35,11 @@
{{#*inline "trait"}}
<div class="trait flexrow"><label for="data.traits.strength.total" class="trait-label">{{trait-label}}</label>
<div class="trait-value flex15 grid grid-8col"><input type="number" name="data.traits.{{trait-key}}.base"
value='{{trait-data.base}}' data-dtype="Number" /><span> +
</span><input type="number" name="data.traits.{{trait-key}}.mod" value='{{trait-data.mod}}'
data-dtype="Number" /><span> =
</span><span class="trait-value-total">{{trait-data.total}}</span></div>
value='{{trait-data.base}}' data-dtype="Number"
title="{{trait-label}} {{localize 'DS4.TooltipBaseValue'}}" /><span> +
</span><input type="number" name="data.traits.{{trait-key}}.mod" value='{{trait-data.mod}}' data-dtype="Number"
title="{{trait-label}} {{localize 'DS4.TooltipModifier'}}" /><span class=" trait-value-arrow">➞</span><span
class="trait-value-total" title="{{trait-label}}: {{trait-data.tooltip}}">{{trait-data.total}}</span></div>
</div>
{{/inline}}

View file

@ -7,15 +7,18 @@
!--
!-- @param combat-value-key: The key of the combat value
!-- @param combat-value-data: The data for the attribute
!-- @param combat-value-label: The label for the attribute
--}}
{{#*inline "combat-value"}}
<div class="combat-value-with-formula">
<div class="combat-value {{combat-value-key}}"><span class="combat-value-total">{{combat-value-data.total}}</span>
<div class="combat-value {{combat-value-key}}" title="{{combat-value-label}}: {{combat-value-data.tooltip}}"><span
class="combat-value-total">{{combat-value-data.total}}</span>
</div>
<div class="combat-value-formula flexrow"><span class="combat-value-base">{{combat-value-data.base}}</span><span>+</span><input
<div class="combat-value-formula flexrow"><span class="combat-value-base"
title="{{combat-value-label}} {{localize 'DS4.TooltipBaseValue'}}">{{combat-value-data.base}}</span><span>+</span><input
type="number" name="data.combatValues.{{combat-value-key}}.mod" value='{{combat-value-data.mod}}'
data-dtype="Number" />
data-dtype="Number" title="{{combat-value-label}} {{localize 'DS4.TooltipModifier'}}" />
</div>
</div>
{{/inline}}
@ -25,6 +28,6 @@
<div class="combat-values flexrow flex-between">
{{#each config.i18n.combatValues as |combat-value-label combat-value-key|}}
{{> combat-value combat-value-key=combat-value-key combat-value-data=(lookup ../data.combatValues
combat-value-key)}}
combat-value-key) combat-value-label=combat-value-label}}
{{/each}}
</div>