Merge branch '068-enable-strict-mode' into 050-basic-active-effects
This commit is contained in:
commit
b74ee5ec7c
45 changed files with 3088 additions and 2119 deletions
|
@ -12,6 +12,8 @@ module.exports = {
|
|||
"plugin:prettier/recommended", // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
|
||||
],
|
||||
|
||||
plugins: ["@typescript-eslint"],
|
||||
|
||||
rules: {
|
||||
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
|
||||
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
|
||||
|
|
21
README.md
21
README.md
|
@ -3,9 +3,8 @@
|
|||
An implementation of the Dungeonslayers 4 game system for [Foundry Virtual
|
||||
Tabletop](http://foundryvtt.com).
|
||||
|
||||
This system provides character sheet support for Actors and Items and mechanical
|
||||
support for dice and rules necessary to
|
||||
play games of Dungeponslayers 4.
|
||||
This system provides sheet support for Actors and Items and mechanical support
|
||||
for dice and rules necessary to play games of Dungeonslayers 4.
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -17,7 +16,7 @@ https://git.f3l.de/dungeonslayers/ds4/-/raw/latest/src/system.json?inline=false
|
|||
|
||||
## Development
|
||||
|
||||
### Prerequisits
|
||||
### Prerequisites
|
||||
|
||||
In order to build this system, recent versions of `node` and `npm` are required.
|
||||
We recommend using the latest lts version of `node`, which is `v14.15.4` at the
|
||||
|
@ -83,16 +82,18 @@ npm test
|
|||
## Contributing
|
||||
|
||||
Code and content contributions are accepted. Please feel free to submit issues
|
||||
to the issue tracker or submit merge requests for code changes. To create an issue send a mail to [git+dungeonslayers-ds4-155-issue-@git.f3l.de](mailto:git+dungeonslayers-ds4-155-issue-@git.f3l.de).
|
||||
to the issue tracker or submit merge requests for code changes. To create an
|
||||
issue, send a mail to [git+dungeonslayers-ds4-155-issue-@git.f3l.de](mailto:git+dungeonslayers-ds4-155-issue-@git.f3l.de).
|
||||
|
||||
## Licensing
|
||||
|
||||
[Dungeonslayers](http://dungeonslayers.de/) (© Christian Kennig) is licensed under [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/de/deed.en).
|
||||
[Dungeonslayers](http://dungeonslayers.de/) (© Christian Kennig) is licensed
|
||||
under [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/de/deed.en).
|
||||
|
||||
The icons in [src/assets/icons/official](src/assets/icons/official) are slightly modifed
|
||||
versions of original Dungeonslayers icons, which have also been published under
|
||||
CC BY-NC-SA 3.0. Hence the modified icons are also published under this
|
||||
license. A copy of this license can be found under
|
||||
The icons in [src/assets/icons/official](src/assets/icons/official) are slightly
|
||||
modified versions of original Dungeonslayers icons, which have also been
|
||||
published under CC BY-NC-SA 3.0. Hence the modified icons are also published
|
||||
under this license. A copy of this license can be found under
|
||||
[src/assets/icons/official/LICENSE](src/assets/icons/official/LICENSE).
|
||||
|
||||
Similarly, the compendium packs found in [src/packs](src/packs) are based on
|
||||
|
|
3458
package-lock.json
generated
3458
package-lock.json
generated
File diff suppressed because it is too large
Load diff
26
package.json
26
package.json
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"private": true,
|
||||
"name": "test",
|
||||
"name": "dungeonslayers4",
|
||||
"description": "An implementation of the Dungeonslayers 4 game system for Foundry Virtual Tabletop.",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.f3l.de/dungeonslayers/ds4",
|
||||
"repository": {
|
||||
|
@ -35,7 +35,7 @@
|
|||
"build:watch": "gulp watch",
|
||||
"link": "gulp link",
|
||||
"clean": "gulp clean && gulp link --clean",
|
||||
"update": "npm install --save-dev git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
|
||||
"update": "npm install --save-dev github:League-of-Foundry-Developers/foundry-vtt-types#foundry-0.7.9",
|
||||
"updateManifest": "gulp updateManifest",
|
||||
"lint": "eslint 'src/**/*.ts' --cache",
|
||||
"lint:fix": "eslint 'src/**/*.ts' --cache --fix",
|
||||
|
@ -45,28 +45,28 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^9.0.6",
|
||||
"@types/jasmine": "^3.6.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.14.0",
|
||||
"@typescript-eslint/parser": "^4.14.0",
|
||||
"@types/jasmine": "^3.6.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.14.2",
|
||||
"@typescript-eslint/parser": "^4.14.2",
|
||||
"archiver": "^5.2.0",
|
||||
"chalk": "^4.1.0",
|
||||
"eslint": "^7.18.0",
|
||||
"eslint-config-prettier": "^7.1.0",
|
||||
"eslint": "^7.19.0",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"foundry-pc-types": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
|
||||
"fs-extra": "^9.0.1",
|
||||
"foundry-vtt-types": "github:League-of-Foundry-Developers/foundry-vtt-types#foundry-0.7.9",
|
||||
"fs-extra": "^9.1.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-git": "^2.10.1",
|
||||
"gulp-less": "^4.0.1",
|
||||
"gulp-sass": "^4.1.0",
|
||||
"gulp-typescript": "^6.0.0-alpha.1",
|
||||
"husky": "^4.3.8",
|
||||
"jasmine": "^3.6.3",
|
||||
"jasmine": "^3.6.4",
|
||||
"jasmine-xml-reporter": "^1.2.1",
|
||||
"json-stringify-pretty-compact": "^2.0.0",
|
||||
"lint-staged": "^10.5.3",
|
||||
"lint-staged": "^10.5.4",
|
||||
"prettier": "^2.2.1",
|
||||
"sass": "^1.32.4",
|
||||
"sass": "^1.32.6",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.1.3",
|
||||
"yargs": "^16.2.0"
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
"DS4.UserInteractionAddItem": "Neu",
|
||||
"DS4.UserInteractionEditItem": "Bearbeiten",
|
||||
"DS4.UserInteractionDeleteItem": "Löschen",
|
||||
"DS4.UserInteractionAddEffect": "Neuer Effekt",
|
||||
"DS4.UserInteractionEditEffect": "Effekt bearbeiten",
|
||||
"DS4.UserInteractionDeleteEffect": "Effekt löschen",
|
||||
"DS4.NotOwned": "Nicht besessen",
|
||||
"DS4.HeadingBiography": "Biografie",
|
||||
"DS4.HeadingDetails": "Details",
|
||||
|
@ -12,11 +15,11 @@
|
|||
"DS4.HeadingSpells": "Zaubersprüche",
|
||||
"DS4.HeadingDescription": "Beschreibung",
|
||||
"DS4.HeadingSpecialCreatureAbilites": "Besondere Fähigkeiten",
|
||||
"DS4.AttackType": "Angriffs Typ",
|
||||
"DS4.AttackType": "Angriffstyp",
|
||||
"DS4.AttackTypeAbbr": "AT",
|
||||
"DS4.WeaponBonus": "Waffen Bonus",
|
||||
"DS4.WeaponBonus": "Waffenbonus",
|
||||
"DS4.WeaponBonusAbbr": "WB",
|
||||
"DS4.OpponentDefense": "Gegner Abwehr",
|
||||
"DS4.OpponentDefense": "Gegnerabwehr",
|
||||
"DS4.OpponentDefenseAbbr": "GA",
|
||||
"DS4.AttackTypeMelee": "Schlagen",
|
||||
"DS4.AttackTypeRanged": "Schießen",
|
||||
|
@ -61,9 +64,9 @@
|
|||
"DS4.ItemTypeSpecialCreatureAbilityPlural": "Besondere Kreaturenfähigkeiten",
|
||||
"DS4.ArmorType": "Panzerungstyp",
|
||||
"DS4.ArmorTypeAbbr": "PAT",
|
||||
"DS4.ArmorMaterialType": "Material Typ",
|
||||
"DS4.ArmorMaterialType": "Materialtyp",
|
||||
"DS4.ArmorMaterialTypeAbbr": "Mat.",
|
||||
"DS4.ArmorValue": "Panzerungs Wert",
|
||||
"DS4.ArmorValue": "Panzerungswert",
|
||||
"DS4.ArmorValueAbbr": "PA",
|
||||
"DS4.ArmorTypeBody": "Körper",
|
||||
"DS4.ArmorTypeBodyAbbr": "Körper",
|
||||
|
@ -125,7 +128,7 @@
|
|||
"DS4.CombatValuesTargetedSpellcasting": "Zielzaubern",
|
||||
"DS4.CharacterBaseInfoRace": "Volk",
|
||||
"DS4.CharacterBaseInfoClass": "Klasse",
|
||||
"DS4.CharacterBaseInfoHeroClass": "Helden Klasse",
|
||||
"DS4.CharacterBaseInfoHeroClass": "Heldenklasse",
|
||||
"DS4.CharacterBaseInfoCulture": "Kultur",
|
||||
"DS4.CharacterProgressionLevel": "Stufe",
|
||||
"DS4.CharacterProgressionExperiencePoints": "Erfahrungspunkte",
|
||||
|
@ -144,9 +147,9 @@
|
|||
"DS4.CharacterProfileBirthday": "Geburtstag",
|
||||
"DS4.CharacterProfileBirthplace": "Geburtsort",
|
||||
"DS4.CharacterProfileAge": "Alter",
|
||||
"DS4.CharacterProfileHeight": "Größe",
|
||||
"DS4.CharacterProfileHeight": "Größe [cm]",
|
||||
"DS4.CharacterProfileHairColor": "Haarfarbe",
|
||||
"DS4.CharacterProfileWeight": "Gewicht",
|
||||
"DS4.CharacterProfileWeight": "Gewicht [kg]",
|
||||
"DS4.CharacterProfileEyeColor": "Augenfarbe",
|
||||
"DS4.CharacterProfileSpecialCharacteristics": "Besondere Eigenschaften",
|
||||
"DS4.CharacterCurrencyGold": "Gold",
|
||||
|
@ -193,9 +196,10 @@
|
|||
"DS4.UnitCustom": "individuell",
|
||||
"DS4.UnitCustomAbbr": " ",
|
||||
"DS4.RollDialogDefaultTitle": "Proben-Optionen",
|
||||
"DS4.RollDialogOkButton": "Ok",
|
||||
"DS4.RollDialogOkButton": "OK",
|
||||
"DS4.RollDialogCancelButton": "Abbrechen",
|
||||
"DS4.ErrorUnexpectedHtmlType": "Typfehler: Erwartet wurde {exType}, tatsächlich erhalten wurde {realType}",
|
||||
"DS4.ErrorUnexpectedHtmlType": "Typfehler: Erwartet wurde '{exType}', tatsächlich erhalten wurde '{realType}'.",
|
||||
"DS4.ErrorCouldNotFindForm": "Konnte HTML Element '{htmlElement}' nicht finden.",
|
||||
"DS4.RollDialogTargetLabel": "Probenwert",
|
||||
"DS4.RollDialogModifierLabel": "SL-Modifikator",
|
||||
"DS4.RollDialogCoupLabel": "Immersieg bis",
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
"DS4.UserInteractionAddItem": "Add item",
|
||||
"DS4.UserInteractionEditItem": "Edit item",
|
||||
"DS4.UserInteractionDeleteItem": "Delete item",
|
||||
"DS4.UserInteractionAddEffect": "Add Effect",
|
||||
"DS4.UserInteractionEditEffect": "Edit Effect",
|
||||
"DS4.UserInteractionDeleteEffect": "Delete Effect",
|
||||
"DS4.NotOwned": "No owner",
|
||||
"DS4.HeadingBiography": "Biography",
|
||||
"DS4.HeadingDetails": "Details",
|
||||
|
@ -144,9 +147,9 @@
|
|||
"DS4.CharacterProfileBirthday": "Birthday",
|
||||
"DS4.CharacterProfileBirthplace": "Birthplace",
|
||||
"DS4.CharacterProfileAge": "Age",
|
||||
"DS4.CharacterProfileHeight": "Height",
|
||||
"DS4.CharacterProfileHeight": "Height [m]",
|
||||
"DS4.CharacterProfileHairColor": "Hair Color",
|
||||
"DS4.CharacterProfileWeight": "Weight",
|
||||
"DS4.CharacterProfileWeight": "Weight [kg]",
|
||||
"DS4.CharacterProfileEyeColor": "Eye Color",
|
||||
"DS4.CharacterProfileSpecialCharacteristics": "Special Characteristics",
|
||||
"DS4.CharacterCurrencyGold": "Gold",
|
||||
|
@ -195,7 +198,8 @@
|
|||
"DS4.RollDialogDefaultTitle": "Roll Options",
|
||||
"DS4.RollDialogOkButton": "Ok",
|
||||
"DS4.RollDialogCancelButton": "Cancel",
|
||||
"DS4.ErrorUnexpectedHtmlType": "Type Error: Expected {exType}, got {realType}",
|
||||
"DS4.ErrorUnexpectedHtmlType": "Type Error: Expected '{exType}' but got '{realType}'.",
|
||||
"DS4.ErrorCouldNotFindForm": "Could not find html element '{htmlElement}'.",
|
||||
"DS4.RollDialogTargetLabel": "Check Target Number",
|
||||
"DS4.RollDialogModifierLabel": "Game Master Modifier",
|
||||
"DS4.RollDialogCoupLabel": "Coup to",
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
import { ModifiableData, ResourceData, UsableResource } from "../common/common-data";
|
||||
import { DS4 } from "../config";
|
||||
import { DS4ItemData } from "../item/item-data";
|
||||
|
||||
export type DS4ActorDataType = DS4ActorDataCharacter | DS4ActorDataCreature;
|
||||
export type DS4ActorData = DS4CharacterData | DS4CreatureData;
|
||||
|
||||
interface DS4ActorDataBase {
|
||||
attributes: DS4ActorDataAttributes;
|
||||
traits: DS4ActorDataTraits;
|
||||
combatValues: DS4ActorDataCombatValues;
|
||||
type ActorType = keyof typeof DS4.i18n.actorTypes;
|
||||
|
||||
interface DS4ActorDataHelper<T, U extends ActorType> extends Actor.Data<T, DS4ItemData> {
|
||||
type: U;
|
||||
}
|
||||
|
||||
interface DS4ActorDataAttributes {
|
||||
type DS4CharacterData = DS4ActorDataHelper<DS4CharacterDataData, "character">;
|
||||
type DS4CreatureData = DS4ActorDataHelper<DS4CreatureDataData, "creature">;
|
||||
|
||||
interface DS4ActorDataDataBase {
|
||||
attributes: DS4ActorDataDataAttributes;
|
||||
traits: DS4ActorDataDataTraits;
|
||||
combatValues: DS4ActorDataDataCombatValues;
|
||||
}
|
||||
|
||||
interface DS4ActorDataDataAttributes {
|
||||
body: ModifiableData<number>;
|
||||
mobility: ModifiableData<number>;
|
||||
mind: ModifiableData<number>;
|
||||
}
|
||||
|
||||
interface DS4ActorDataTraits {
|
||||
interface DS4ActorDataDataTraits {
|
||||
strength: ModifiableData<number>;
|
||||
constitution: ModifiableData<number>;
|
||||
agility: ModifiableData<number>;
|
||||
|
@ -23,7 +34,7 @@ interface DS4ActorDataTraits {
|
|||
aura: ModifiableData<number>;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCombatValues {
|
||||
interface DS4ActorDataDataCombatValues {
|
||||
hitPoints: ResourceData<number>;
|
||||
defense: ModifiableData<number>;
|
||||
initiative: ModifiableData<number>;
|
||||
|
@ -34,34 +45,32 @@ interface DS4ActorDataCombatValues {
|
|||
targetedSpellcasting: ModifiableData<number>;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCharacter extends DS4ActorDataBase {
|
||||
baseInfo: DS4ActorDataCharacterBaseInfo;
|
||||
progression: DS4ActorDataCharacterProgression;
|
||||
language: DS4ActorDataCharacterLanguage;
|
||||
profile: DS4ActorDataCharacterProfile;
|
||||
currency: DS4ActorDataCharacterCurrency;
|
||||
interface DS4CharacterDataData extends DS4ActorDataDataBase {
|
||||
baseInfo: DS4CharacterDataDataBaseInfo;
|
||||
progression: DS4CharacterDataDataProgression;
|
||||
language: DS4CharacterDataDataLanguage;
|
||||
profile: DS4CharacterDataDataProfile;
|
||||
currency: DS4CharacterDataDataCurrency;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCharacterBaseInfo {
|
||||
interface DS4CharacterDataDataBaseInfo {
|
||||
race: string;
|
||||
class: string;
|
||||
heroClass: string;
|
||||
culture: string;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCharacterProgression {
|
||||
interface DS4CharacterDataDataProgression {
|
||||
level: number;
|
||||
experiencePoints: number;
|
||||
talentPoints: UsableResource<number>;
|
||||
progressPoints: UsableResource<number>;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCharacterLanguage {
|
||||
interface DS4CharacterDataDataLanguage {
|
||||
languages: string;
|
||||
alphabets: string;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCharacterProfile {
|
||||
interface DS4CharacterDataDataProfile {
|
||||
biography: string;
|
||||
gender: string;
|
||||
birthday: string;
|
||||
|
@ -74,21 +83,21 @@ interface DS4ActorDataCharacterProfile {
|
|||
specialCharacteristics: string;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCharacterCurrency {
|
||||
interface DS4CharacterDataDataCurrency {
|
||||
gold: number;
|
||||
silver: number;
|
||||
copper: number;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCreature extends DS4ActorDataBase {
|
||||
baseInfo: DS4ActorDataCreatureBaseInfo;
|
||||
interface DS4CreatureDataData extends DS4ActorDataDataBase {
|
||||
baseInfo: DS4CreatureDataDataBaseInfo;
|
||||
}
|
||||
|
||||
type CreatureType = "animal" | "construct" | "humanoid" | "magicalEntity" | "plantBeing" | "undead";
|
||||
|
||||
type SizeCategory = "tiny" | "small" | "normal" | "large" | "huge" | "colossal";
|
||||
|
||||
interface DS4ActorDataCreatureBaseInfo {
|
||||
interface DS4CreatureDataDataBaseInfo {
|
||||
loot: string;
|
||||
foeFactor: number;
|
||||
creatureType: CreatureType;
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { ModifiableData } from "../common/common-data";
|
||||
import { DS4 } from "../config";
|
||||
import { DS4Item } from "../item/item";
|
||||
import { DS4Armor, DS4EquippableItemDataType, DS4ItemDataType, DS4Shield, ItemType } from "../item/item-data";
|
||||
import { DS4ActorDataType } from "./actor-data";
|
||||
import { DS4ItemData, ItemType } from "../item/item-data";
|
||||
import { DS4ActorData } from "./actor-data";
|
||||
|
||||
type DS4ActiveEffect = ActiveEffect<DS4ActorDataType, DS4ItemDataType, DS4Actor, DS4Item>;
|
||||
|
||||
export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item> {
|
||||
/**
|
||||
* The Actor class for DS4
|
||||
*/
|
||||
export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
|
||||
/** @override */
|
||||
prepareData(): void {
|
||||
this.data = duplicate(this._data);
|
||||
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();
|
||||
|
@ -45,20 +46,23 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item>
|
|||
* @param predicate The predicate that ActiveEffectChanges need to satisfy in order to be applied
|
||||
*/
|
||||
applyActiveEffectsFiltered(predicate: (change: ActiveEffectChange) => boolean): void {
|
||||
const overrides = {};
|
||||
const overrides: Record<string, unknown> = {};
|
||||
|
||||
// Organize non-disabled effects by their application priority
|
||||
const changes = this.effects.reduce((changes: Array<ActiveEffectChange & { effect: DS4ActiveEffect }>, e) => {
|
||||
const changes = this.effects.reduce(
|
||||
(changes: Array<ActiveEffectChange & { effect: ActiveEffect<DS4Actor> }>, e) => {
|
||||
if (e.data.disabled) return changes;
|
||||
|
||||
return changes.concat(
|
||||
e.data.changes.filter(predicate).map((c) => {
|
||||
c = duplicate(c);
|
||||
c.priority = c.priority ?? c.mode * 10;
|
||||
return { ...c, effect: e };
|
||||
const duplicatedChange = duplicate(c) as ActiveEffect.Change;
|
||||
duplicatedChange.priority = duplicatedChange.priority ?? duplicatedChange.mode * 10;
|
||||
return { ...duplicatedChange, effect: e };
|
||||
}),
|
||||
);
|
||||
}, []);
|
||||
},
|
||||
[],
|
||||
);
|
||||
changes.sort((a, b) => a.priority - b.priority);
|
||||
|
||||
// Apply all changes
|
||||
|
@ -68,7 +72,7 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item>
|
|||
}
|
||||
|
||||
// Expand the set of final overrides
|
||||
this["overrides"] = expandObject({ ...flattenObject(this["overrides"] ?? {}), ...overrides });
|
||||
this.overrides = expandObject({ ...flattenObject(this.overrides ?? {}), ...overrides });
|
||||
}
|
||||
|
||||
/** @override */
|
||||
|
@ -78,7 +82,7 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item>
|
|||
|
||||
/** The list of properties that are derived from others, given in dot notation */
|
||||
get derivedDataProperties(): Array<string> {
|
||||
return Object.keys(DS4.combatValues)
|
||||
return Object.keys(DS4.i18n.combatValues)
|
||||
.map((combatValue) => `data.combatValues.${combatValue}.total`)
|
||||
.concat("data.combatValues.hitPoints.max");
|
||||
}
|
||||
|
@ -110,7 +114,7 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item>
|
|||
|
||||
/**
|
||||
* Checks whether or not the given item type can be owned by the actor.
|
||||
* @param itemType the item type to check
|
||||
* @param itemType - The item type to check
|
||||
*/
|
||||
canOwnItemType(itemType: ItemType): boolean {
|
||||
return this.ownableItemTypes.includes(itemType);
|
||||
|
@ -149,10 +153,13 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item>
|
|||
*/
|
||||
private _calculateArmorValueOfEquippedItems(): number {
|
||||
return this.items
|
||||
.filter((item) => ["armor", "shield"].includes(item.type))
|
||||
.map((item) => item.data.data as DS4Armor | DS4Shield)
|
||||
.filter((itemData) => itemData.equipped)
|
||||
.map((itemData) => itemData.armorValue)
|
||||
.map((item) => {
|
||||
if (item.data.type === "armor" || item.data.type === "shield") {
|
||||
return item.data.data.equipped ? item.data.data.armorValue : 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
})
|
||||
.reduce((a, b) => a + b, 0);
|
||||
}
|
||||
|
||||
|
@ -161,7 +168,7 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item>
|
|||
* This only differs from the base implementation by also allowing negative values.
|
||||
* @override
|
||||
*/
|
||||
async modifyTokenAttribute(attribute: string, value: number, isDelta = false, isBar = true): Promise<DS4Actor> {
|
||||
async modifyTokenAttribute(attribute: string, value: number, isDelta = false, isBar = true): Promise<this> {
|
||||
const current = getProperty(this.data.data, attribute);
|
||||
|
||||
// Determine the updates to make to the actor data
|
||||
|
@ -180,59 +187,77 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item>
|
|||
}
|
||||
|
||||
/** @override */
|
||||
// TODO(types): Improve typing once it's fixed in upstream (arrays can be passed!)
|
||||
createEmbeddedEntity(
|
||||
embeddedName: string,
|
||||
createData: Record<string, unknown> | Array<Record<string, unknown>>,
|
||||
embeddedName: "OwnedItem",
|
||||
data: DeepPartial<DS4ItemData>,
|
||||
options?: Record<string, unknown>,
|
||||
): Promise<this> {
|
||||
): Promise<DS4ItemData>;
|
||||
createEmbeddedEntity(
|
||||
embeddedName: "ActiveEffect",
|
||||
data: DeepPartial<DS4ItemData>,
|
||||
options?: Record<string, unknown>,
|
||||
): Promise<ActiveEffect.Data>;
|
||||
createEmbeddedEntity(
|
||||
embeddedName: "OwnedItem" | "ActiveEffect",
|
||||
data: DeepPartial<DS4ItemData> | DeepPartial<ActiveEffect.Data>,
|
||||
options?: Record<string, unknown>,
|
||||
): Promise<DS4ItemData> | Promise<ActiveEffect.Data> {
|
||||
if (embeddedName === "OwnedItem") {
|
||||
this._preCreateOwnedItem((createData as unknown) as ItemData<DS4ItemDataType>);
|
||||
this._preCreateOwnedItem(data);
|
||||
return super.createEmbeddedEntity(embeddedName, data, options);
|
||||
}
|
||||
return super.createEmbeddedEntity(embeddedName, createData, options);
|
||||
return super.createEmbeddedEntity(embeddedName, data, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the item that is going to be created is equippable, set it to be non equipped and disable all ActiveEffects
|
||||
* If the item that is going to be created is equipable, set it to be non equipped and disable all ActiveEffects
|
||||
* contained in the item
|
||||
* @param itemData The data of the item to be created
|
||||
*/
|
||||
private _preCreateOwnedItem(itemData: ItemData<DS4ItemDataType>): void {
|
||||
if ("equipped" in itemData.data) {
|
||||
itemData.effects = itemData.effects.map((effect) => ({ ...effect, disabled: true }));
|
||||
const equippableUpdateData = itemData as ItemData<DS4EquippableItemDataType>;
|
||||
equippableUpdateData.data.equipped = false;
|
||||
protected _preCreateOwnedItem(itemData: DeepPartial<DS4ItemData>): void {
|
||||
if (itemData.data && "equipped" in itemData.data) {
|
||||
itemData.effects = itemData.effects?.map((effect) => ({ ...effect, disabled: true }));
|
||||
itemData.data.equipped = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
// TODO(types): Improve typing once it's fixed in upstream
|
||||
updateEmbeddedEntity(embeddedName: string, data: unknown[], options?: Entity.UpdateOptions): Promise<unknown[]>;
|
||||
updateEmbeddedEntity(embeddedName: string, data: unknown, options?: Entity.UpdateOptions): Promise<unknown>;
|
||||
updateEmbeddedEntity(
|
||||
embeddedName: string,
|
||||
updateData: Record<string, unknown> | Array<Record<string, unknown>>,
|
||||
updateData: unknown | unknown[],
|
||||
options?: Record<string, unknown>,
|
||||
): Promise<this> {
|
||||
): Promise<unknown> {
|
||||
if (embeddedName === "OwnedItem") {
|
||||
this._preUpdateOwnedItem(updateData as Partial<ItemData<DS4ItemDataType>>);
|
||||
this._preUpdateOwnedItem(updateData as DeepPartial<DS4ItemData> | Array<DeepPartial<DS4ItemData>>);
|
||||
}
|
||||
return super.updateEmbeddedEntity(embeddedName, updateData, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the equipped flag of an item changed, update all ActiveEffects originating from that item accordingly.
|
||||
* @param updateData The change that is going to be applied to the owned item
|
||||
* If the equipped flag of one or more items changed, update all ActiveEffects originating from those items
|
||||
* accordingly.
|
||||
* @param updateData The change that is going to be applied to the owned item(s)
|
||||
*/
|
||||
private _preUpdateOwnedItem(updateData: Partial<ItemData<DS4ItemDataType>>): void {
|
||||
if ("equipped" in updateData.data) {
|
||||
const equippableUpdateData = updateData as Partial<ItemData<DS4EquippableItemDataType>>;
|
||||
const origin = `Actor.${this.id}.OwnedItem.${updateData._id}`;
|
||||
private _preUpdateOwnedItem(updateData: DeepPartial<DS4ItemData> | Array<DeepPartial<DS4ItemData>>): void {
|
||||
const dataArray = updateData instanceof Array ? updateData : [updateData];
|
||||
dataArray.forEach((data) => {
|
||||
if (data.data && "equipped" in data.data) {
|
||||
const equipped = data.data.equipped;
|
||||
const origin = `Actor.${this.id}.OwnedItem.${data._id}`;
|
||||
const effects = this.effects
|
||||
.filter((e) => e.data.origin === origin)
|
||||
.map((e) => {
|
||||
const data = duplicate(e.data);
|
||||
data.disabled = !equippableUpdateData.data.equipped;
|
||||
return data;
|
||||
const effectData = duplicate(e.data);
|
||||
effectData.disabled = !(equipped ?? true);
|
||||
return effectData;
|
||||
});
|
||||
if (effects.length > 0)
|
||||
this.updateEmbeddedEntity("ActiveEffect", (effects as unknown) as Record<string, unknown>);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,38 @@
|
|||
import { DS4 } from "../../config";
|
||||
import { DS4Item } from "../../item/item";
|
||||
import { DS4ItemDataType, ItemType } from "../../item/item-data";
|
||||
import { DS4ItemData } from "../../item/item-data";
|
||||
import { DS4Actor } from "../actor";
|
||||
import { DS4ActorDataType } from "../actor-data";
|
||||
|
||||
/**
|
||||
* Extend the basic ActorSheet with some very simple modifications
|
||||
* @extends {ActorSheet}
|
||||
* The base Sheet class for all DS4 Actors
|
||||
*/
|
||||
export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4ItemDataType> {
|
||||
export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
|
||||
// TODO(types): Improve mergeObject in upstream so that it isn't necessary to provide all parameters (see https://github.com/League-of-Foundry-Developers/foundry-vtt-types/issues/272)
|
||||
/** @override */
|
||||
static get defaultOptions(): FormApplicationOptions {
|
||||
return mergeObject(super.defaultOptions, {
|
||||
static get defaultOptions(): BaseEntitySheet.Options {
|
||||
const superDefaultOptions = super.defaultOptions;
|
||||
return mergeObject(superDefaultOptions, {
|
||||
classes: ["ds4", "sheet", "actor"],
|
||||
width: 745,
|
||||
height: 600,
|
||||
scrollY: [".sheet-body"],
|
||||
template: superDefaultOptions.template,
|
||||
viewPermission: superDefaultOptions.viewPermission,
|
||||
closeOnSubmit: superDefaultOptions.closeOnSubmit,
|
||||
submitOnChange: superDefaultOptions.submitOnChange,
|
||||
submitOnClose: superDefaultOptions.submitOnClose,
|
||||
editable: superDefaultOptions.editable,
|
||||
baseApplication: superDefaultOptions.baseApplication,
|
||||
top: superDefaultOptions.top,
|
||||
left: superDefaultOptions.left,
|
||||
popOut: superDefaultOptions.popOut,
|
||||
minimizable: superDefaultOptions.minimizable,
|
||||
resizable: superDefaultOptions.resizable,
|
||||
id: superDefaultOptions.id,
|
||||
dragDrop: superDefaultOptions.dragDrop,
|
||||
filters: superDefaultOptions.filters,
|
||||
title: superDefaultOptions.title,
|
||||
tabs: superDefaultOptions.tabs,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -24,27 +42,23 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
return `${path}/${this.actor.data.type}-sheet.hbs`;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* This method returns the data for the template of the actor sheet.
|
||||
* It explicitly adds the items of the object sorted by type in the
|
||||
* object itemsByType.
|
||||
* @returns the data fed to the template of the actor sheet
|
||||
* @returns The data fed to the template of the actor sheet
|
||||
*/
|
||||
getData(): ActorSheetData<DS4ActorDataType, DS4Actor> {
|
||||
async getData(): Promise<ActorSheet.Data<DS4Actor>> {
|
||||
const data = {
|
||||
...super.getData(),
|
||||
...(await super.getData()),
|
||||
// Add the localization config to the data:
|
||||
config: CONFIG.DS4,
|
||||
config: DS4,
|
||||
// Add the items explicitly sorted by type to the data:
|
||||
itemsByType: this.actor.itemTypes,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
activateListeners(html: JQuery): void {
|
||||
super.activateListeners(html);
|
||||
|
@ -59,7 +73,7 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
html.find(".item-edit").on("click", (ev) => {
|
||||
const li = $(ev.currentTarget).parents(".item");
|
||||
const item = this.actor.getOwnedItem(li.data("itemId"));
|
||||
item.sheet.render(true);
|
||||
item.sheet?.render(true);
|
||||
});
|
||||
|
||||
// Delete Inventory Item
|
||||
|
@ -75,20 +89,16 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
html.find(".rollable").click(this._onRoll.bind(this));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @private
|
||||
* @param event - The originating click event
|
||||
*/
|
||||
private _onItemCreate(event: JQuery.ClickEvent): Promise<Item> {
|
||||
protected _onItemCreate(event: JQuery.ClickEvent): Promise<DS4ItemData> {
|
||||
event.preventDefault();
|
||||
const header = event.currentTarget;
|
||||
// Get the type of item to create.
|
||||
const type = header.dataset.type;
|
||||
// Grab any data associated with this control.
|
||||
const data = duplicate(header.dataset);
|
||||
const { type, ...data } = duplicate(header.dataset);
|
||||
// Initialize a default name.
|
||||
const name = `New ${type.capitalize()}`;
|
||||
// Prepare the item object.
|
||||
|
@ -97,8 +107,6 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
type: type,
|
||||
data: data,
|
||||
};
|
||||
// Remove the type from the dataset since it's in the itemData.type prop.
|
||||
delete itemData.data.type;
|
||||
|
||||
// Finally, create the item!
|
||||
return this.actor.createOwnedItem(itemData);
|
||||
|
@ -108,15 +116,14 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
* Handle changes to properties of an Owned Item from within character sheet.
|
||||
* Can currently properly bind: see getValue().
|
||||
* Assumes the item property is given as the value of the HTML element property 'data-property'.
|
||||
* @param {JQuery.ChangeEvent<HTMLFormElement>} ev The originating change event
|
||||
* @private
|
||||
* @param ev - The originating change event
|
||||
*/
|
||||
private _onItemChange(ev: JQuery.ChangeEvent<HTMLFormElement>): void {
|
||||
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(this.actor.getOwnedItem(id)); // getOwnedItem is typed incorrectly, it actually returns a ItemData<DS4ItemDataType>, not an Item
|
||||
const item = duplicate<DS4Item, "lenient">(this.actor.getOwnedItem(id));
|
||||
const property: string | undefined = $(ev.currentTarget).data("property");
|
||||
|
||||
// Early return:
|
||||
|
@ -139,7 +146,7 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
* - Checkbox: boolean
|
||||
* - Text input: string
|
||||
* - Number: number
|
||||
* @param el the input element to collect the value of
|
||||
* @param el - The input element to collect the value of
|
||||
*/
|
||||
private getValue(el: HTMLFormElement): boolean | string | number {
|
||||
// One needs to differentiate between e.g. checkboxes (value="on") and select boxes etc.
|
||||
|
@ -190,10 +197,9 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
|
||||
/**
|
||||
* Handle clickable rolls.
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @private
|
||||
* @param event - The originating click event
|
||||
*/
|
||||
private _onRoll(event: JQuery.ClickEvent): void {
|
||||
protected _onRoll(event: JQuery.ClickEvent): void {
|
||||
event.preventDefault();
|
||||
const element = event.currentTarget;
|
||||
const dataset = element.dataset;
|
||||
|
@ -209,10 +215,13 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
}
|
||||
|
||||
/** @override */
|
||||
async _onDropItem(event: DragEvent, data: Parameters<typeof DS4Item.fromDropData>[0]): Promise<unknown> {
|
||||
const item = await Item.fromDropData(data);
|
||||
if (item && !this.actor.canOwnItemType(item.data.type as ItemType)) {
|
||||
ui.notifications.warn(
|
||||
protected async _onDropItem(
|
||||
event: DragEvent,
|
||||
data: { type: "Item" } & (DeepPartial<ActorSheet.OwnedItemData<DS4Actor>> | { pack: string } | { id: string }),
|
||||
): Promise<boolean | undefined | ActorSheet.OwnedItemData<DS4Actor>> {
|
||||
const item = ((await Item.fromDropData(data)) as unknown) as DS4Item;
|
||||
if (item && !this.actor.canOwnItemType(item.data.type)) {
|
||||
ui.notifications?.warn(
|
||||
game.i18n.format("DS4.WarningActorCannotOwnItem", {
|
||||
actorName: this.actor.name,
|
||||
actorType: this.actor.data.type,
|
||||
|
|
|
@ -1,11 +1,34 @@
|
|||
import { DS4ActorSheet } from "./actor-sheet";
|
||||
|
||||
/**
|
||||
* The Sheet class for DS4 Character Actors
|
||||
*/
|
||||
export class DS4CharacterActorSheet extends DS4ActorSheet {
|
||||
/** @override */
|
||||
static get defaultOptions(): FormApplicationOptions {
|
||||
return mergeObject(super.defaultOptions, {
|
||||
static get defaultOptions(): BaseEntitySheet.Options {
|
||||
const superDefaultOptions = super.defaultOptions;
|
||||
return mergeObject(superDefaultOptions, {
|
||||
classes: ["ds4", "sheet", "actor", "character"],
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "inventory" }],
|
||||
template: superDefaultOptions.template,
|
||||
viewPermission: superDefaultOptions.viewPermission,
|
||||
closeOnSubmit: superDefaultOptions.closeOnSubmit,
|
||||
submitOnChange: superDefaultOptions.submitOnChange,
|
||||
submitOnClose: superDefaultOptions.submitOnClose,
|
||||
editable: superDefaultOptions.editable,
|
||||
baseApplication: superDefaultOptions.baseApplication,
|
||||
top: superDefaultOptions.top,
|
||||
left: superDefaultOptions.left,
|
||||
popOut: superDefaultOptions.popOut,
|
||||
minimizable: superDefaultOptions.minimizable,
|
||||
resizable: superDefaultOptions.resizable,
|
||||
id: superDefaultOptions.id,
|
||||
dragDrop: superDefaultOptions.dragDrop,
|
||||
filters: superDefaultOptions.filters,
|
||||
title: superDefaultOptions.title,
|
||||
width: superDefaultOptions.width,
|
||||
height: superDefaultOptions.height,
|
||||
scrollY: superDefaultOptions.scrollY,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,34 @@
|
|||
import { DS4ActorSheet } from "./actor-sheet";
|
||||
|
||||
/**
|
||||
* The Sheet class for DS4 Creature Actors
|
||||
*/
|
||||
export class DS4CreatureActorSheet extends DS4ActorSheet {
|
||||
/** @override */
|
||||
static get defaultOptions(): FormApplicationOptions {
|
||||
return mergeObject(super.defaultOptions, {
|
||||
static get defaultOptions(): BaseEntitySheet.Options {
|
||||
const superDefaultOptions = super.defaultOptions;
|
||||
return mergeObject(superDefaultOptions, {
|
||||
classes: ["ds4", "sheet", "actor", "creature"],
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "inventory" }],
|
||||
template: superDefaultOptions.template,
|
||||
viewPermission: superDefaultOptions.viewPermission,
|
||||
closeOnSubmit: superDefaultOptions.closeOnSubmit,
|
||||
submitOnChange: superDefaultOptions.submitOnChange,
|
||||
submitOnClose: superDefaultOptions.submitOnClose,
|
||||
editable: superDefaultOptions.editable,
|
||||
baseApplication: superDefaultOptions.baseApplication,
|
||||
top: superDefaultOptions.top,
|
||||
left: superDefaultOptions.left,
|
||||
popOut: superDefaultOptions.popOut,
|
||||
minimizable: superDefaultOptions.minimizable,
|
||||
resizable: superDefaultOptions.resizable,
|
||||
id: superDefaultOptions.id,
|
||||
dragDrop: superDefaultOptions.dragDrop,
|
||||
filters: superDefaultOptions.filters,
|
||||
title: superDefaultOptions.title,
|
||||
width: superDefaultOptions.width,
|
||||
height: superDefaultOptions.height,
|
||||
scrollY: superDefaultOptions.scrollY,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
37
src/module/common/utils.ts
Normal file
37
src/module/common/utils.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Partition an array into two, following a predicate.
|
||||
* @param input - The Array to split.
|
||||
* @param predicate - The predicate by which to split.
|
||||
* @returns A tuple of two arrays, the first one containing all elements from `input` that match the predicate, the second one containing those that do not.
|
||||
*/
|
||||
export function partition<T>(input: Array<T>, predicate: (v: T) => boolean): [T[], T[]] {
|
||||
return input.reduce(
|
||||
(p: [Array<T>, Array<T>], cur: T) => {
|
||||
if (predicate(cur)) {
|
||||
p[0].push(cur);
|
||||
} else {
|
||||
p[1].push(cur);
|
||||
}
|
||||
return p;
|
||||
},
|
||||
[[], []],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zips two Arrays to an array of pairs of elements with corresponding indices. Excessive elements are dropped.
|
||||
* @param a1 - First array to zip.
|
||||
* @param a2 - Second array to zip.
|
||||
*
|
||||
* @typeParam T - Type of elements contained in `a1`.
|
||||
* @typeParam U - Type of elements contained in `a2`.
|
||||
*
|
||||
* @returns The array of pairs that had the same index in their source array.
|
||||
*/
|
||||
export function zip<T, U>(a1: Array<T>, a2: Array<U>): Array<[T, U]> {
|
||||
if (a1.length <= a2.length) {
|
||||
return a1.map((e1, i) => [e1, a2[i]]);
|
||||
} else {
|
||||
return a2.map((e2, i) => [a1[i], e2]);
|
||||
}
|
||||
}
|
|
@ -8,6 +8,12 @@ export const DS4 = {
|
|||
|____/ \___/|_| \_|\____|_____\___/|_| \_|____/|_____/_/ \_\_| |_____|_| \_\____/ |_|
|
||||
=============================================================================================`,
|
||||
|
||||
/**
|
||||
* A dictionary of dictionaries each mapping keys to localized strings
|
||||
* resp. their localization keys.
|
||||
* The localization is assumed to take place on each reload.
|
||||
*/
|
||||
i18n: {
|
||||
/**
|
||||
* Define the set of acttack types that can be performed with weapon items
|
||||
*/
|
||||
|
@ -17,23 +23,6 @@ export const DS4 = {
|
|||
meleeRanged: "DS4.AttackTypeMeleeRanged",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the file paths to icon images
|
||||
*/
|
||||
attackTypesIcons: {
|
||||
melee: "systems/ds4/assets/icons/official/combat-values/melee-attack.png",
|
||||
meleeRanged: "systems/ds4/assets/icons/official/combat-values/melee-ranged-attack.png",
|
||||
ranged: "systems/ds4/assets/icons/official/combat-values/ranged-attack.png",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the file paths to icon images
|
||||
*/
|
||||
spellTypesIcons: {
|
||||
spellcasting: "systems/ds4/assets/icons/official/combat-values/spellcasting.png",
|
||||
targetedSpellcasting: "systems/ds4/assets/icons/official/combat-values/targeted-spellcasting.png",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the set of item availabilties
|
||||
*/
|
||||
|
@ -87,7 +76,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Define the set of armor materials, used to determine if a characer may wear the armor without additional penalties
|
||||
* Define the set of armor materials, used to determine if a character may wear the armor without additional penalties
|
||||
*/
|
||||
armorMaterialTypes: {
|
||||
cloth: "DS4.ArmorMaterialTypeCloth",
|
||||
|
@ -209,23 +198,6 @@ export const DS4 = {
|
|||
eyeColor: "DS4.CharacterProfileEyeColor",
|
||||
specialCharacteristics: "DS4.CharacterProfileSpecialCharacteristics",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the profile info types for handlebars of a character
|
||||
*/
|
||||
characterProfileDTypes: {
|
||||
biography: "String",
|
||||
gender: "String",
|
||||
birthday: "String",
|
||||
birthplace: "String",
|
||||
age: "Number",
|
||||
height: "Number",
|
||||
hairColor: "String",
|
||||
weight: "Number",
|
||||
eyeColor: "String",
|
||||
specialCharacteristics: "String",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define currency elements of a character
|
||||
*/
|
||||
|
@ -319,4 +291,43 @@ export const DS4 = {
|
|||
blindroll: "DS4.ChatVisibilityBlindRoll",
|
||||
selfroll: "DS4.ChatVisibilitySelfRoll",
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* A dictionary of dictionaries mapping keys to icon file paths.
|
||||
*/
|
||||
icons: {
|
||||
/**
|
||||
* Define the file paths to icon images
|
||||
*/
|
||||
attackTypes: {
|
||||
melee: "systems/ds4/assets/icons/official/combat-values/melee-attack.png",
|
||||
meleeRanged: "systems/ds4/assets/icons/official/combat-values/melee-ranged-attack.png",
|
||||
ranged: "systems/ds4/assets/icons/official/combat-values/ranged-attack.png",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the file paths to icon images
|
||||
*/
|
||||
spellTypes: {
|
||||
spellcasting: "systems/ds4/assets/icons/official/combat-values/spellcasting.png",
|
||||
targetedSpellcasting: "systems/ds4/assets/icons/official/combat-values/targeted-spellcasting.png",
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the profile info types for handlebars of a character
|
||||
*/
|
||||
characterProfileDTypes: {
|
||||
biography: "String",
|
||||
gender: "String",
|
||||
birthday: "String",
|
||||
birthplace: "String",
|
||||
age: "Number",
|
||||
height: "Number",
|
||||
hairColor: "String",
|
||||
weight: "Number",
|
||||
eyeColor: "String",
|
||||
specialCharacteristics: "String",
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// Import Modules
|
||||
import { DS4Actor } from "./actor/actor";
|
||||
import { DS4Item } from "./item/item";
|
||||
import { DS4ItemSheet } from "./item/item-sheet";
|
||||
|
@ -10,7 +9,7 @@ import { createCheckRoll } from "./rolls/check-factory";
|
|||
import { registerSystemSettings } from "./settings";
|
||||
import { migration } from "./migrations";
|
||||
|
||||
Hooks.once("init", async function () {
|
||||
Hooks.once("init", async () => {
|
||||
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
||||
|
||||
game.ds4 = {
|
||||
|
@ -21,29 +20,19 @@ Hooks.once("init", async function () {
|
|||
migration,
|
||||
};
|
||||
|
||||
// Record configuration
|
||||
CONFIG.DS4 = DS4;
|
||||
|
||||
// Define custom Entity classes
|
||||
CONFIG.Actor.entityClass = DS4Actor as typeof Actor;
|
||||
CONFIG.Item.entityClass = DS4Item as typeof Item;
|
||||
CONFIG.Actor.entityClass = DS4Actor;
|
||||
CONFIG.Item.entityClass = DS4Item;
|
||||
|
||||
// Define localized type labels
|
||||
CONFIG.Actor.typeLabels = DS4.actorTypes;
|
||||
CONFIG.Item.typeLabels = DS4.itemTypes;
|
||||
CONFIG.Actor.typeLabels = DS4.i18n.actorTypes;
|
||||
CONFIG.Item.typeLabels = DS4.i18n.itemTypes;
|
||||
|
||||
// Configure Dice
|
||||
CONFIG.Dice.types = [Die, DS4Check];
|
||||
CONFIG.Dice.terms = {
|
||||
c: Coin,
|
||||
d: Die,
|
||||
s: DS4Check,
|
||||
};
|
||||
CONFIG.Dice.types.push(DS4Check);
|
||||
CONFIG.Dice.terms.s = DS4Check;
|
||||
|
||||
// Register system settings
|
||||
registerSystemSettings();
|
||||
|
||||
// Register sheet application classes
|
||||
Actors.unregisterSheet("core", ActorSheet);
|
||||
Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true });
|
||||
Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true });
|
||||
|
@ -69,67 +58,59 @@ async function registerHandlebarsPartials() {
|
|||
"systems/ds4/templates/actor/partials/combat-values.hbs",
|
||||
"systems/ds4/templates/actor/partials/profile.hbs",
|
||||
"systems/ds4/templates/actor/partials/character-progression.hbs",
|
||||
"systems/ds4/templates/actor/partials/special-creature-abilites-overview.hbs",
|
||||
"systems/ds4/templates/actor/partials/special-creature-abilities-overview.hbs",
|
||||
"systems/ds4/templates/actor/partials/character-inventory.hbs",
|
||||
"systems/ds4/templates/actor/partials/creature-inventory.hbs",
|
||||
];
|
||||
return loadTemplates(templatePaths);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Foundry VTT Setup */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
||||
*/
|
||||
Hooks.once("setup", function () {
|
||||
// Localize CONFIG objects once up-front
|
||||
const toLocalize = [
|
||||
"attackTypes",
|
||||
"itemAvailabilities",
|
||||
"itemTypes",
|
||||
"armorTypes",
|
||||
"armorTypesAbbr",
|
||||
"armorMaterialTypes",
|
||||
"armorMaterialTypesAbbr",
|
||||
"armorMaterialTypes",
|
||||
"spellTypes",
|
||||
"spellCategories",
|
||||
"attributes",
|
||||
"traits",
|
||||
"combatValues",
|
||||
"characterBaseInfo",
|
||||
"characterProgression",
|
||||
"characterLanguage",
|
||||
"characterProfile",
|
||||
"characterCurrency",
|
||||
"creatureTypes",
|
||||
"creatureSizeCategories",
|
||||
"creatureBaseInfo",
|
||||
"temporalUnits",
|
||||
"temporalUnitsAbbr",
|
||||
"distanceUnits",
|
||||
"distanceUnitsAbbr",
|
||||
"chatVisibilities",
|
||||
];
|
||||
|
||||
// Exclude some from sorting where the default order matters
|
||||
const noSort = ["attributes", "traits", "combatValues", "creatureSizeCategories"];
|
||||
|
||||
// Localize and sort CONFIG objects
|
||||
for (const o of toLocalize) {
|
||||
const localized = Object.entries(CONFIG.DS4[o]).map((e) => {
|
||||
return [e[0], game.i18n.localize(e[1] as string)];
|
||||
});
|
||||
if (!noSort.includes(o)) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
CONFIG.DS4[o] = localized.reduce((obj, e) => {
|
||||
obj[e[0]] = e[1];
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
Hooks.once("setup", () => {
|
||||
localizeAndSortConfigObjects();
|
||||
});
|
||||
|
||||
Hooks.once("ready", function () {
|
||||
Hooks.once("ready", () => {
|
||||
migration.migrate();
|
||||
});
|
||||
|
||||
/**
|
||||
* Select the text of input elements in given sheets via onfocus listener.
|
||||
* The hook names are of the form "render"+sheet_superclassname and are called within
|
||||
* the render() method of the foundry Application class.
|
||||
* Note: The render hooks of all classes in the class hierarchy are called,
|
||||
* so e.g. for a Dialog, both "renderDialog" and "renderApplication" are called
|
||||
* (in this order).
|
||||
*/
|
||||
["renderApplication", "renderActorSheet", "renderItemSheet"].forEach((hookName: string) => {
|
||||
Hooks.on(hookName, (app: Dialog, html: JQueryStatic) => {
|
||||
$(html)
|
||||
.find("input")
|
||||
.on("focus", (ev: JQuery.FocusEvent<HTMLInputElement>) => {
|
||||
ev.currentTarget.select();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Localizes all objects in {@link DS4.i18n} and sorts them unless they are explicitly excluded.
|
||||
*/
|
||||
function localizeAndSortConfigObjects() {
|
||||
const noSort = ["attributes", "traits", "combatValues", "creatureSizeCategories"];
|
||||
|
||||
const localizeObject = <T extends { [s: string]: string }>(obj: T, sort = true): T => {
|
||||
const localized = Object.entries(obj).map(([key, value]) => {
|
||||
return [key, game.i18n.localize(value)];
|
||||
});
|
||||
if (sort) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
return Object.fromEntries(localized);
|
||||
};
|
||||
|
||||
DS4.i18n = Object.fromEntries(
|
||||
Object.entries(DS4.i18n).map(([key, value]) => {
|
||||
return [key, localizeObject(value, !noSort.includes(key))];
|
||||
}),
|
||||
) as typeof DS4.i18n;
|
||||
}
|
||||
|
|
|
@ -1,37 +1,53 @@
|
|||
import { ModifiableData } from "../common/common-data";
|
||||
import { DS4 } from "../config";
|
||||
|
||||
export type ItemType = keyof typeof DS4.itemTypes;
|
||||
export type ItemType = keyof typeof DS4.i18n.itemTypes;
|
||||
|
||||
export type DS4ItemDataType =
|
||||
| DS4Weapon
|
||||
| DS4Armor
|
||||
| DS4Shield
|
||||
| DS4Spell
|
||||
| DS4Trinket
|
||||
| DS4Equipment
|
||||
| DS4Talent
|
||||
| DS4RacialAbility
|
||||
| DS4Language
|
||||
| DS4Alphabet
|
||||
| DS4SpecialCreatureAbility;
|
||||
export type DS4ItemData =
|
||||
| DS4WeaponData
|
||||
| DS4ArmorData
|
||||
| DS4ShieldData
|
||||
| DS4SpellData
|
||||
| DS4TrinketData
|
||||
| DS4EquipmentData
|
||||
| DS4TalentData
|
||||
| DS4RacialAbilityData
|
||||
| DS4LanguageData
|
||||
| DS4AlphabetData
|
||||
| DS4SpecialCreatureAbilityData;
|
||||
|
||||
export type DS4EquippableItemDataType = DS4Weapon | DS4Armor | DS4Shield | DS4Trinket;
|
||||
interface DS4ItemDataHelper<T, U extends ItemType> extends Item.Data<T> {
|
||||
type: U;
|
||||
}
|
||||
|
||||
// types
|
||||
type DS4WeaponData = DS4ItemDataHelper<DS4WeaponDataData, "weapon">;
|
||||
type DS4ArmorData = DS4ItemDataHelper<DS4ArmorDataData, "armor">;
|
||||
type DS4ShieldData = DS4ItemDataHelper<DS4ShieldDataData, "shield">;
|
||||
type DS4SpellData = DS4ItemDataHelper<DS4SpellDataData, "spell">;
|
||||
type DS4TrinketData = DS4ItemDataHelper<DS4TrinketDataData, "trinket">;
|
||||
type DS4EquipmentData = DS4ItemDataHelper<DS4EquipmentDataData, "equipment">;
|
||||
type DS4TalentData = DS4ItemDataHelper<DS4TalentDataData, "talent">;
|
||||
type DS4RacialAbilityData = DS4ItemDataHelper<DS4RacialAbilityDataData, "racialAbility">;
|
||||
type DS4LanguageData = DS4ItemDataHelper<DS4LanguageDataData, "language">;
|
||||
type DS4AlphabetData = DS4ItemDataHelper<DS4AlphabetDataData, "alphabet">;
|
||||
type DS4SpecialCreatureAbilityData = DS4ItemDataHelper<DS4SpecialCreatureAbilityDataData, "specialCreatureAbility">;
|
||||
|
||||
interface DS4Weapon extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable {
|
||||
interface DS4WeaponDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {
|
||||
attackType: "melee" | "ranged" | "meleeRanged";
|
||||
weaponBonus: number;
|
||||
opponentDefense: number;
|
||||
}
|
||||
|
||||
export interface DS4Armor extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable, DS4ItemProtective {
|
||||
interface DS4ArmorDataData
|
||||
extends DS4ItemDataDataBase,
|
||||
DS4ItemDataDataPhysical,
|
||||
DS4ItemDataDataEquipable,
|
||||
DS4ItemDataDataProtective {
|
||||
armorMaterialType: "cloth" | "leather" | "chain" | "plate";
|
||||
armorType: "body" | "helmet" | "vambrace" | "greaves" | "vambraceGreaves";
|
||||
}
|
||||
|
||||
export interface DS4Talent extends DS4ItemBase {
|
||||
interface DS4TalentDataData extends DS4ItemDataDataBase {
|
||||
rank: DS4TalentRank;
|
||||
}
|
||||
|
||||
|
@ -39,7 +55,7 @@ interface DS4TalentRank extends ModifiableData<number> {
|
|||
max: number;
|
||||
}
|
||||
|
||||
interface DS4Spell extends DS4ItemBase, DS4ItemEquipable {
|
||||
interface DS4SpellDataData extends DS4ItemDataDataBase, DS4ItemDataDataEquipable {
|
||||
spellType: "spellcasting" | "targetedSpellcasting";
|
||||
bonus: string;
|
||||
spellCategory:
|
||||
|
@ -59,37 +75,41 @@ interface DS4Spell extends DS4ItemBase, DS4ItemEquipable {
|
|||
scrollPrice: number;
|
||||
}
|
||||
|
||||
export interface DS4Shield extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable, DS4ItemProtective {}
|
||||
interface DS4Trinket extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable {}
|
||||
interface DS4Equipment extends DS4ItemBase, DS4ItemPhysical {}
|
||||
type DS4RacialAbility = DS4ItemBase;
|
||||
type DS4Language = DS4ItemBase;
|
||||
type DS4Alphabet = DS4ItemBase;
|
||||
interface DS4SpecialCreatureAbility extends DS4ItemBase {
|
||||
interface DS4ShieldDataData
|
||||
extends DS4ItemDataDataBase,
|
||||
DS4ItemDataDataPhysical,
|
||||
DS4ItemDataDataEquipable,
|
||||
DS4ItemDataDataProtective {}
|
||||
interface DS4TrinketDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {}
|
||||
interface DS4EquipmentDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical {}
|
||||
type DS4RacialAbilityDataData = DS4ItemDataDataBase;
|
||||
type DS4LanguageDataData = DS4ItemDataDataBase;
|
||||
type DS4AlphabetDataData = DS4ItemDataDataBase;
|
||||
interface DS4SpecialCreatureAbilityDataData extends DS4ItemDataDataBase {
|
||||
experiencePoints: number;
|
||||
}
|
||||
|
||||
// templates
|
||||
|
||||
interface DS4ItemBase {
|
||||
interface DS4ItemDataDataBase {
|
||||
description: string;
|
||||
}
|
||||
interface DS4ItemPhysical {
|
||||
interface DS4ItemDataDataPhysical {
|
||||
quantity: number;
|
||||
price: number;
|
||||
availability: "hamlet" | "village" | "city" | "elves" | "dwarves" | "nowhere" | "unset";
|
||||
storageLocation: string;
|
||||
}
|
||||
|
||||
export function isDS4ItemDataTypePhysical(input: DS4ItemDataType): boolean {
|
||||
export function isDS4ItemDataTypePhysical(input: DS4ItemData["data"]): boolean {
|
||||
return "quantity" in input && "price" in input && "availability" in input && "storageLocation" in input;
|
||||
}
|
||||
|
||||
interface DS4ItemEquipable {
|
||||
interface DS4ItemDataDataEquipable {
|
||||
equipped: boolean;
|
||||
}
|
||||
|
||||
interface DS4ItemProtective {
|
||||
interface DS4ItemDataDataProtective {
|
||||
armorValue: number;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,36 @@
|
|||
import { DS4 } from "../config";
|
||||
import { DS4Item } from "./item";
|
||||
import { DS4ItemDataType, isDS4ItemDataTypePhysical } from "./item-data";
|
||||
import { isDS4ItemDataTypePhysical } from "./item-data";
|
||||
|
||||
/**
|
||||
* Extend the basic ItemSheet with some very simple modifications
|
||||
* @extends {ItemSheet}
|
||||
* The Sheet class for DS4 Items
|
||||
*/
|
||||
export class DS4ItemSheet extends ItemSheet<DS4ItemDataType, DS4Item> {
|
||||
export class DS4ItemSheet extends ItemSheet<ItemSheet.Data<DS4Item>> {
|
||||
/** @override */
|
||||
static get defaultOptions(): FormApplicationOptions {
|
||||
return mergeObject(super.defaultOptions, {
|
||||
static get defaultOptions(): BaseEntitySheet.Options {
|
||||
const superDefaultOptions = super.defaultOptions;
|
||||
return mergeObject(superDefaultOptions, {
|
||||
width: 530,
|
||||
height: 400,
|
||||
classes: ["ds4", "sheet", "item"],
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
|
||||
scrollY: [".sheet-body"],
|
||||
template: superDefaultOptions.template,
|
||||
viewPermission: superDefaultOptions.viewPermission,
|
||||
closeOnSubmit: superDefaultOptions.closeOnSubmit,
|
||||
submitOnChange: superDefaultOptions.submitOnChange,
|
||||
submitOnClose: superDefaultOptions.submitOnClose,
|
||||
editable: superDefaultOptions.editable,
|
||||
baseApplication: superDefaultOptions.baseApplication,
|
||||
top: superDefaultOptions.top,
|
||||
left: superDefaultOptions.left,
|
||||
popOut: superDefaultOptions.popOut,
|
||||
minimizable: superDefaultOptions.minimizable,
|
||||
resizable: superDefaultOptions.resizable,
|
||||
id: superDefaultOptions.id,
|
||||
dragDrop: superDefaultOptions.dragDrop,
|
||||
filters: superDefaultOptions.filters,
|
||||
title: superDefaultOptions.title,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -23,13 +40,11 @@ export class DS4ItemSheet extends ItemSheet<DS4ItemDataType, DS4Item> {
|
|||
return `${path}/${this.item.data.type}-sheet.hbs`;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
getData(): ItemSheetData<DS4ItemDataType, DS4Item> {
|
||||
async getData(): Promise<ItemSheet.Data<DS4Item>> {
|
||||
const data = {
|
||||
...super.getData(),
|
||||
config: CONFIG.DS4,
|
||||
...(await super.getData()),
|
||||
config: DS4,
|
||||
isOwned: this.item.isOwned,
|
||||
actor: this.item.actor,
|
||||
isPhysical: isDS4ItemDataTypePhysical(this.item.data.data),
|
||||
|
@ -37,10 +52,8 @@ export class DS4ItemSheet extends ItemSheet<DS4ItemDataType, DS4Item> {
|
|||
return data;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
setPosition(options: ApplicationPosition = {}): ApplicationPosition {
|
||||
setPosition(options: Partial<Application.Position> = {}): Application.Position {
|
||||
const position = super.setPosition(options);
|
||||
if ("find" in this.element) {
|
||||
const sheetBody = this.element.find(".sheet-body");
|
||||
|
@ -52,8 +65,6 @@ export class DS4ItemSheet extends ItemSheet<DS4ItemDataType, DS4Item> {
|
|||
return position;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
activateListeners(html: JQuery): void {
|
||||
super.activateListeners(html);
|
||||
|
@ -65,13 +76,13 @@ export class DS4ItemSheet extends ItemSheet<DS4ItemDataType, DS4Item> {
|
|||
|
||||
/**
|
||||
* Handle management of ActiveEffects.
|
||||
* @param {Event} event The originating click event
|
||||
* @param event - he originating click event
|
||||
*/
|
||||
private async _onManageActiveEffect(event: JQuery.ClickEvent): Promise<unknown> {
|
||||
protected async _onManageActiveEffect(event: JQuery.ClickEvent): Promise<unknown> {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.item.isOwned) {
|
||||
return ui.notifications.warn(game.i18n.localize("DS4.WarningManageActiveEffectOnOwnedItem"));
|
||||
return ui.notifications?.warn(game.i18n.localize("DS4.WarningManageActiveEffectOnOwnedItem"));
|
||||
}
|
||||
const a = event.currentTarget;
|
||||
const li = $(a).parents(".effect");
|
||||
|
@ -81,7 +92,7 @@ export class DS4ItemSheet extends ItemSheet<DS4ItemDataType, DS4Item> {
|
|||
return this._createActiveEffect();
|
||||
case "edit":
|
||||
const effect = this.item.effects.get(li.data("effectId"));
|
||||
return effect.sheet.render(true);
|
||||
return effect?.sheet.render(true);
|
||||
case "delete": {
|
||||
return this.item.deleteEmbeddedEntity("ActiveEffect", li.data("effectId"));
|
||||
}
|
||||
|
@ -91,7 +102,7 @@ export class DS4ItemSheet extends ItemSheet<DS4ItemDataType, DS4Item> {
|
|||
/**
|
||||
* Create a new ActiveEffect for the item using default data.
|
||||
*/
|
||||
private async _createActiveEffect(): Promise<unknown> {
|
||||
protected async _createActiveEffect(): Promise<ActiveEffect.Data> {
|
||||
const label = `New Effect`;
|
||||
|
||||
const createData = {
|
||||
|
@ -101,7 +112,7 @@ export class DS4ItemSheet extends ItemSheet<DS4ItemDataType, DS4Item> {
|
|||
transfer: true,
|
||||
};
|
||||
|
||||
const effect = await ActiveEffect.create(createData, this.item);
|
||||
const effect = ActiveEffect.create(createData, this.item);
|
||||
return effect.create({});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,20 @@
|
|||
import { DS4Actor } from "../actor/actor";
|
||||
import { DS4ActorDataType } from "../actor/actor-data";
|
||||
import { DS4ItemDataType, DS4Talent } from "./item-data";
|
||||
import { DS4ItemData } from "./item-data";
|
||||
|
||||
/**
|
||||
* Extend the basic Item with some very simple modifications.
|
||||
* @extends {Item}
|
||||
* The Item class for DS4
|
||||
*/
|
||||
export class DS4Item extends Item<DS4ItemDataType, DS4ActorDataType, DS4Actor> {
|
||||
export class DS4Item extends Item<DS4ItemData> {
|
||||
/**
|
||||
* Augment the basic Item data model with additional dynamic data.
|
||||
* @override
|
||||
*/
|
||||
prepareData(): void {
|
||||
super.prepareData();
|
||||
this.prepareDerivedData();
|
||||
|
||||
// Get the Item's data
|
||||
// const itemData = this.data;
|
||||
// const actorData = this.actor ? this.actor.data : {};
|
||||
// const data = itemData.data;
|
||||
}
|
||||
|
||||
prepareDerivedData(): void {
|
||||
if (this.type === "talent") {
|
||||
const data = this.data.data as DS4Talent;
|
||||
if (this.data.type === "talent") {
|
||||
const data = this.data.data;
|
||||
data.rank.total = data.rank.base + data.rank.mod;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { migrate as migrate001 } from "./migrations/001";
|
||||
|
||||
async function migrate(): Promise<void> {
|
||||
if (!game.user.isGM) {
|
||||
if (!game.user?.isGM) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -18,14 +18,14 @@ async function migrate(): Promise<void> {
|
|||
}
|
||||
|
||||
async function migrateFromTo(oldMigrationVersion: number, targetMigrationVersion: number): Promise<void> {
|
||||
if (!game.user.isGM) {
|
||||
if (!game.user?.isGM) {
|
||||
return;
|
||||
}
|
||||
|
||||
const migrationsToExecute = migrations.slice(oldMigrationVersion, targetMigrationVersion);
|
||||
|
||||
if (migrationsToExecute.length > 0) {
|
||||
ui.notifications.info(
|
||||
ui.notifications?.info(
|
||||
game.i18n.format("DS4.InfoSystemUpdateStart", {
|
||||
currentVersion: oldMigrationVersion,
|
||||
targetVersion: targetMigrationVersion,
|
||||
|
@ -40,7 +40,7 @@ async function migrateFromTo(oldMigrationVersion: number, targetMigrationVersion
|
|||
await migration();
|
||||
game.settings.set("ds4", "systemMigrationVersion", currentMigrationVersion);
|
||||
} catch (err) {
|
||||
ui.notifications.error(
|
||||
ui.notifications?.error(
|
||||
game.i18n.format("DS4.ErrorDuringMigration", {
|
||||
currentVersion: oldMigrationVersion,
|
||||
targetVersion: targetMigrationVersion,
|
||||
|
@ -54,7 +54,7 @@ async function migrateFromTo(oldMigrationVersion: number, targetMigrationVersion
|
|||
}
|
||||
}
|
||||
|
||||
ui.notifications.info(
|
||||
ui.notifications?.info(
|
||||
game.i18n.format("DS4.InfoSystemUpdateCompleted", {
|
||||
currentVersion: oldMigrationVersion,
|
||||
targetVersion: targetMigrationVersion,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export async function migrate(): Promise<void> {
|
||||
for (const a of game.actors.entities) {
|
||||
for (const a of game.actors?.entities ?? []) {
|
||||
const updateData = getActorUpdateData();
|
||||
console.log(`Migrating actor ${a.name}`);
|
||||
await a.update(updateData, { enforceTypes: false });
|
||||
|
@ -18,7 +18,7 @@ function getActorUpdateData(): Record<string, unknown> {
|
|||
"rangedAttack",
|
||||
"spellcasting",
|
||||
"targetedSpellcasting",
|
||||
].reduce((acc, curr) => {
|
||||
].reduce((acc: Partial<Record<string, { "-=base": null }>>, curr) => {
|
||||
acc[curr] = { "-=base": null };
|
||||
return acc;
|
||||
}, {}),
|
||||
|
|
|
@ -4,13 +4,13 @@ import { DS4 } from "../config";
|
|||
* Provides default values for all arguments the `CheckFactory` expects.
|
||||
*/
|
||||
class DefaultCheckOptions implements DS4CheckFactoryOptions {
|
||||
maxCritSuccess = 1;
|
||||
minCritFailure = 20;
|
||||
useSlayingDice = false;
|
||||
rollMode: DS4RollMode = "roll";
|
||||
readonly maxCritSuccess = 1;
|
||||
readonly minCritFailure = 20;
|
||||
readonly useSlayingDice = false;
|
||||
readonly rollMode: DS4RollMode = "roll";
|
||||
|
||||
mergeWith(other: Partial<DS4CheckFactoryOptions>): DS4CheckFactoryOptions {
|
||||
return { ...this, ...other } as DS4CheckFactoryOptions;
|
||||
return { ...this, ...other };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,13 +28,13 @@ class CheckFactory {
|
|||
private gmModifier: number,
|
||||
passedOptions: Partial<DS4CheckFactoryOptions> = {},
|
||||
) {
|
||||
this.checkOptions = new DefaultCheckOptions().mergeWith(passedOptions);
|
||||
this.checkOptions = defaultCheckOptions.mergeWith(passedOptions);
|
||||
}
|
||||
|
||||
private checkOptions: DS4CheckFactoryOptions;
|
||||
|
||||
async execute(): Promise<ChatMessage | unknown> {
|
||||
const rollCls: typeof Roll = CONFIG.Dice.rolls[0];
|
||||
const rollCls = CONFIG.Dice.rolls[0];
|
||||
|
||||
const formula = [
|
||||
"ds",
|
||||
|
@ -45,7 +45,6 @@ class CheckFactory {
|
|||
const roll = new rollCls(formula);
|
||||
|
||||
const rollModeTemplate = this.checkOptions.rollMode;
|
||||
console.log(rollModeTemplate);
|
||||
return roll.toMessage({}, { rollMode: rollModeTemplate, create: true });
|
||||
}
|
||||
|
||||
|
@ -76,8 +75,8 @@ class CheckFactory {
|
|||
|
||||
/**
|
||||
* Asks the user for all unknown/necessary information and passes them on to perform a roll.
|
||||
* @param targetValue {number} The Check Target Number ("CTN")
|
||||
* @param options {Partial<DS4CheckFactoryOptions>} Options changing the behaviour of the roll and message.
|
||||
* @param targetValue - The Check Target Number ("CTN")
|
||||
* @param options - Options changing the behavior of the roll and message.
|
||||
*/
|
||||
export async function createCheckRoll(
|
||||
targetValue: number,
|
||||
|
@ -86,6 +85,8 @@ export async function createCheckRoll(
|
|||
// Ask for additional required data;
|
||||
const gmModifierData = await askGmModifier(targetValue, options);
|
||||
|
||||
const newTargetValue = gmModifierData.checkTargetValue ?? targetValue;
|
||||
const gmModifier = gmModifierData.gmModifier ?? 0;
|
||||
const newOptions: Partial<DS4CheckFactoryOptions> = {
|
||||
maxCritSuccess: gmModifierData.maxCritSuccess ?? options.maxCritSuccess ?? undefined,
|
||||
minCritFailure: gmModifierData.minCritFailure ?? options.minCritFailure ?? undefined,
|
||||
|
@ -94,7 +95,7 @@ export async function createCheckRoll(
|
|||
};
|
||||
|
||||
// Create Factory
|
||||
const cf = new CheckFactory(gmModifierData.checkTargetValue, gmModifierData.gmModifier, newOptions);
|
||||
const cf = new CheckFactory(newTargetValue, gmModifier, newOptions);
|
||||
|
||||
// Possibly additional processing
|
||||
|
||||
|
@ -108,13 +109,13 @@ export async function createCheckRoll(
|
|||
* @notes
|
||||
* At the moment, this asks for more data than it will do after some iterations.
|
||||
*
|
||||
* @returns {Promise<IntermediateGmModifierData>} The data given by the user.
|
||||
* @returns The data given by the user.
|
||||
*/
|
||||
async function askGmModifier(
|
||||
targetValue: number,
|
||||
options: Partial<DS4CheckFactoryOptions> = {},
|
||||
{ template, title }: { template?: string; title?: string } = {},
|
||||
): Promise<IntermediateGmModifierData> {
|
||||
): Promise<Partial<IntermediateGmModifierData>> {
|
||||
// Render model interface and return value
|
||||
const usedTemplate = template ?? "systems/ds4/templates/roll/roll-options.hbs";
|
||||
const usedTitle = title ?? game.i18n.localize("DS4.RollDialogDefaultTitle");
|
||||
|
@ -124,7 +125,7 @@ async function askGmModifier(
|
|||
checkTargetValue: targetValue,
|
||||
maxCritSuccess: options.maxCritSuccess ?? defaultCheckOptions.maxCritSuccess,
|
||||
minCritFailure: options.minCritFailure ?? defaultCheckOptions.minCritFailure,
|
||||
rollModes: rollModes,
|
||||
rollMode: options.rollMode,
|
||||
config: DS4,
|
||||
};
|
||||
const renderedHtml = await renderTemplate(usedTemplate, templateData);
|
||||
|
@ -133,14 +134,12 @@ async function askGmModifier(
|
|||
new Dialog(
|
||||
{
|
||||
title: usedTitle,
|
||||
close: () => {
|
||||
// Don't do anything
|
||||
},
|
||||
content: renderedHtml,
|
||||
buttons: {
|
||||
ok: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: game.i18n.localize("DS4.RollDialogOkButton"),
|
||||
callback: (html: HTMLElement | JQuery) => {
|
||||
callback: (html) => {
|
||||
if (!("jquery" in html)) {
|
||||
throw new Error(
|
||||
game.i18n.format("DS4.ErrorUnexpectedHtmlType", {
|
||||
|
@ -150,39 +149,40 @@ async function askGmModifier(
|
|||
);
|
||||
} else {
|
||||
const innerForm = html[0].querySelector("form");
|
||||
if (!innerForm) {
|
||||
throw new Error(
|
||||
game.i18n.format("DS4.ErrorCouldNotFindHtmlElement", { htmlElement: "form" }),
|
||||
);
|
||||
}
|
||||
resolve(innerForm);
|
||||
}
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
label: game.i18n.localize("DS4.RollDialogCancelButton"),
|
||||
callback: () => {
|
||||
// Don't do anything
|
||||
},
|
||||
},
|
||||
},
|
||||
default: "ok",
|
||||
},
|
||||
{},
|
||||
{ jQuery: true },
|
||||
).render(true);
|
||||
});
|
||||
const dialogForm = await dialogPromise;
|
||||
return parseDialogFormData(dialogForm, targetValue);
|
||||
return parseDialogFormData(dialogForm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts Dialog data from the returned DOM element.
|
||||
* @param formData {HTMLFormElement} The filed dialog
|
||||
* @param targetValue {number} The previously known target value (slated for removal once data automation is available)
|
||||
* @param formData - The filed dialog
|
||||
*/
|
||||
function parseDialogFormData(formData: HTMLFormElement, targetValue: number): IntermediateGmModifierData {
|
||||
function parseDialogFormData(formData: HTMLFormElement): Partial<IntermediateGmModifierData> {
|
||||
return {
|
||||
checkTargetValue: parseInt(formData["ctv"]?.value) ?? targetValue,
|
||||
gmModifier: parseInt(formData["gmmod"]?.value) ?? 0,
|
||||
maxCritSuccess: parseInt(formData["maxcoup"]?.value) ?? defaultCheckOptions.maxCritSuccess,
|
||||
minCritFailure: parseInt(formData["minfumble"]?.value) ?? defaultCheckOptions.minCritFailure,
|
||||
useSlayingDice: false,
|
||||
rollMode: formData["visibility"]?.value ?? defaultCheckOptions.rollMode,
|
||||
checkTargetValue: parseInt(formData["ctv"]?.value),
|
||||
gmModifier: parseInt(formData["gmmod"]?.value),
|
||||
maxCritSuccess: parseInt(formData["maxcoup"]?.value),
|
||||
minCritFailure: parseInt(formData["minfumble"]?.value),
|
||||
rollMode: formData["visibility"]?.value,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -56,8 +56,8 @@ export class DS4Check extends DiceTerm {
|
|||
}
|
||||
}
|
||||
|
||||
success = null;
|
||||
failure = null;
|
||||
success: boolean | null = null;
|
||||
failure: boolean | null = null;
|
||||
targetValue = DS4Check.DEFAULT_TARGET_VALUE;
|
||||
minCritFailure = DS4Check.DEFAULT_MIN_CRIT_FAILURE;
|
||||
maxCritSuccess = DS4Check.DEFAULT_MAX_CRIT_SUCCESS;
|
||||
|
@ -93,16 +93,11 @@ export class DS4Check extends DiceTerm {
|
|||
}
|
||||
}
|
||||
|
||||
/** Term Modifiers */
|
||||
noop(): this {
|
||||
return this;
|
||||
}
|
||||
|
||||
// DS4 only allows recursive explosions
|
||||
explode(modifier: string): this {
|
||||
explode(modifier: string): void {
|
||||
const rgx = /[xX]/;
|
||||
const match = modifier.match(rgx);
|
||||
if (!match) return this;
|
||||
if (!match) return;
|
||||
|
||||
this.results = (this.results as Array<RollResult>)
|
||||
.map((r) => {
|
||||
|
@ -110,7 +105,7 @@ export class DS4Check extends DiceTerm {
|
|||
|
||||
let checked = 0;
|
||||
while (checked < intermediateResults.length) {
|
||||
const r = (intermediateResults as Array<RollResult>)[checked];
|
||||
const r = intermediateResults[checked];
|
||||
checked++;
|
||||
if (!r.active) continue;
|
||||
|
||||
|
@ -135,7 +130,7 @@ export class DS4Check extends DiceTerm {
|
|||
static DENOMINATION = "s";
|
||||
static MODIFIERS = {
|
||||
x: "explode",
|
||||
c: "noop", // Modifier is consumed in constructor for target value
|
||||
v: "noop", // Modifier is consumed in constructor for target value
|
||||
c: (): void => undefined, // Modifier is consumed in constructor for crit
|
||||
v: (): void => undefined, // Modifier is consumed in constructor for target value
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export class DefaultRollOptions implements RollOptions {
|
|||
public slayingDiceRepetition = false;
|
||||
|
||||
mergeWith(other: Partial<RollOptions>): RollOptions {
|
||||
return { ...this, ...other } as RollOptions;
|
||||
return { ...this, ...other };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ import { calculateRollResult, isDiceSwapNecessary, isSlayingDiceRepetition, sepa
|
|||
|
||||
/**
|
||||
* Performs a roll against a check target number, e.g. for usage in battle, but not for herbs.
|
||||
* @param {number} checkTargetValue the final CTN, including all static modifiers.
|
||||
* @param {Partial<RollOptions>} rollOptions optional, final option override that affect the checks outcome, e.g. different values for crits or whether slaying dice are used.
|
||||
* @param {Array<number>} dice optional, pass already thrown dice that are used instead of rolling new ones.
|
||||
* @param checkTargetValue - the final CTN, including all static modifiers.
|
||||
* @param rollOptions - optional, final option override that affect the checks outcome, e.g. different values for crits or whether slaying dice are used.
|
||||
* @param dice - optional, pass already thrown dice that are used instead of rolling new ones.
|
||||
*/
|
||||
export function ds4roll(
|
||||
checkTargetValue: number,
|
||||
rollOptions: Partial<RollOptions> = {},
|
||||
dice: Array<number> = null,
|
||||
dice: Array<number> = [],
|
||||
): RollResult {
|
||||
if (checkTargetValue <= 20) {
|
||||
return rollCheckSingleDie(checkTargetValue, rollOptions, dice);
|
||||
|
@ -27,20 +27,20 @@ export function ds4roll(
|
|||
* This is not intended for direct usage. Use
|
||||
* {@link ds4roll | the function that is not bound to an amount of Dice} instead.
|
||||
*
|
||||
* @param {number} checkTargetValue - The target value to check against.
|
||||
* @param {RollOptions} rollOptions - Options that affect the checks outcome, e.g. different values for crits or whether slaying dice are used.
|
||||
* @param {Array<number>} dice optional, pass already thrown dice that are used instead of rolling new ones.
|
||||
* @param checkTargetValue - The target value to check against.
|
||||
* @param rollOptions - Options that affect the checks outcome, e.g. different values for crits or whether slaying dice are used.
|
||||
* @param dice - optional, pass already thrown dice that are used instead of rolling new ones.
|
||||
*
|
||||
* @returns {RollResult} An object containing detailed information on the roll result.
|
||||
* @returns An object containing detailed information on the roll result.
|
||||
*/
|
||||
export function rollCheckSingleDie(
|
||||
checkTargetValue: number,
|
||||
rollOptions: Partial<RollOptions>,
|
||||
dice: Array<number> = null,
|
||||
dice: Array<number> = [],
|
||||
): RollResult {
|
||||
const usedOptions = new DefaultRollOptions().mergeWith(rollOptions);
|
||||
|
||||
if (dice?.length != 1) {
|
||||
if (dice.length != 1) {
|
||||
dice = [new DS4RollProvider().getNextRoll()];
|
||||
}
|
||||
const usedDice = dice;
|
||||
|
@ -66,22 +66,22 @@ export function rollCheckSingleDie(
|
|||
* This is not intended for direct usage. Use
|
||||
* {@link ds4roll | the function that is not bound to an amount of Dice} instead.
|
||||
*
|
||||
* @param {number} targetValue- - The target value to check against.
|
||||
* @param {RollOptions} rollOptions - Options that affect the checks outcome, e.g. different values for crits or whether slaying dice are used.
|
||||
* @param {Array<number>} dice - Optional array of dice values to consider instead of rolling new ones.
|
||||
* @param targetValue - The target value to check against.
|
||||
* @param rollOptions - Options that affect the checks outcome, e.g. different values for crits or whether slaying dice are used.
|
||||
* @param dice - Optional array of dice values to consider instead of rolling new ones.
|
||||
*
|
||||
* @returns {RollResult} An object containing detailed information on the roll result.
|
||||
* @returns An object containing detailed information on the roll result.
|
||||
*/
|
||||
export function rollCheckMultipleDice(
|
||||
targetValue: number,
|
||||
rollOptions: Partial<RollOptions>,
|
||||
dice: Array<number> = null,
|
||||
dice: Array<number> = [],
|
||||
): RollResult {
|
||||
const usedOptions = new DefaultRollOptions().mergeWith(rollOptions);
|
||||
const remainderTargetValue = targetValue % 20;
|
||||
const numberOfDice = Math.ceil(targetValue / 20);
|
||||
|
||||
if (!dice || dice.length != numberOfDice) {
|
||||
if (dice.length != numberOfDice) {
|
||||
dice = new DS4RollProvider().getNextRolls(numberOfDice);
|
||||
}
|
||||
const usedDice = dice;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { partition, zip } from "../common/utils";
|
||||
import { RollOptions } from "./roll-data";
|
||||
|
||||
/**
|
||||
|
@ -8,9 +9,9 @@ import { RollOptions } from "./roll-data";
|
|||
* @private_remarks
|
||||
* This uses an internal implementation of a `partition` method. Don't let typescript fool you, it will tell you that a partition method is available for Arrays, but that one's imported globally from foundry's declarations and not available during the test stage!
|
||||
*
|
||||
* @param {Array<number>} dice - The dice values.
|
||||
* @param {RollOptions} usedOptions - Options that affect the check's behaviour.
|
||||
* @returns {[Array<number>, Array<number>]} A tuple containing two arrays of dice values, the first one containing all critical hits, the second one containing all others. Both arrays are sorted descendingby value.
|
||||
* @param dice - The dice values.
|
||||
* @param usedOptions - Options that affect the check's behavior.
|
||||
* @returns A tuple containing two arrays of dice values, the first one containing all critical hits, the second one containing all others. Both arrays are sorted descending by value.
|
||||
*/
|
||||
export function separateCriticalHits(dice: Array<number>, usedOptions: RollOptions): CritsAndNonCrits {
|
||||
const [critSuccesses, otherRolls] = partition(dice, (v: number) => {
|
||||
|
@ -25,40 +26,19 @@ export function separateCriticalHits(dice: Array<number>, usedOptions: RollOptio
|
|||
*/
|
||||
type CritsAndNonCrits = [Array<number>, Array<number>];
|
||||
|
||||
/**
|
||||
* Partition an array into two, following a predicate.
|
||||
* @param {Array<T>} input The Array to split.
|
||||
* @param {(T) => boolean} predicate The predicate by which to split.
|
||||
* @returns A tuple of two arrays, the first one containing all elements from `input` that matched the predicate, the second one containing those that don't.
|
||||
*/
|
||||
// TODO: Move to generic utils method?
|
||||
function partition<T>(input: Array<T>, predicate: (v: T) => boolean) {
|
||||
return input.reduce(
|
||||
(p: [Array<T>, Array<T>], cur: T) => {
|
||||
if (predicate(cur)) {
|
||||
p[0].push(cur);
|
||||
} else {
|
||||
p[1].push(cur);
|
||||
}
|
||||
return p;
|
||||
},
|
||||
[[], []],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates if a critical success should be moved to the last position in order to maximize the check's result.
|
||||
*
|
||||
* @example
|
||||
* With regular dice rolling rules and a check target number of 31, the two dice 1 and 19 can get to a check result of 30.
|
||||
* This method would be called as follows:
|
||||
* ```
|
||||
* ```ts
|
||||
* isDiceSwapNecessary([[1], [19]], 11)
|
||||
* ```
|
||||
*
|
||||
* @param {[Array<number>, Array<number>]} critsAndNonCrits the dice values thrown. It is assumed that both critical successes and other rolls are sorted descending.
|
||||
* @param {number} remainingTargetValue the target value for the last dice, that is the only one that can be less than 20.
|
||||
* @returns {boolean} Bool indicating whether a critical success has to be used as the last dice.
|
||||
* @param critsAndNonCrits - The dice values thrown. It is assumed that both critical successes and other rolls are sorted descending.
|
||||
* @param remainingTargetValue - The target value for the last dice, that is the only one that can be less than 20.
|
||||
* @returns Bool indicating whether a critical success has to be used as the last dice.
|
||||
*/
|
||||
export function isDiceSwapNecessary(
|
||||
[critSuccesses, otherRolls]: CritsAndNonCrits,
|
||||
|
@ -81,7 +61,7 @@ export function isDiceSwapNecessary(
|
|||
*
|
||||
* @internal
|
||||
*
|
||||
* @param {RollOptions} opts the roll options to check against
|
||||
* @param opts - The roll options to check against
|
||||
*/
|
||||
export function isSlayingDiceRepetition(opts: RollOptions): boolean {
|
||||
return opts.useSlayingDice && opts.slayingDiceRepetition;
|
||||
|
@ -92,9 +72,9 @@ export function isSlayingDiceRepetition(opts: RollOptions): boolean {
|
|||
*
|
||||
* @internal
|
||||
*
|
||||
* @param assignedRollResults The dice values in the order of usage.
|
||||
* @param remainderTargetValue Target value for the last dice (the only one differing from `20`).
|
||||
* @param rollOptions Config object containing options that change the way dice results are handled.
|
||||
* @param assignedRollResults - The dice values in the order of usage.
|
||||
* @param remainderTargetValue - Target value for the last dice (the only one differing from `20`).
|
||||
* @param rollOptions - Config object containing options that change the way dice results are handled.
|
||||
*
|
||||
* @returns {number} The total check value.
|
||||
*/
|
||||
|
@ -118,22 +98,3 @@ export function calculateRollResult(
|
|||
.map(([v]) => v)
|
||||
.reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
// TODO: Move to generic utils method?
|
||||
/**
|
||||
* Zips two Arrays to an array of pairs of elements with corresponding indices. Excessive elements are dropped.
|
||||
* @param {Array<T>} a1 First array to zip.
|
||||
* @param {Array<U>} a2 Second array to zip.
|
||||
*
|
||||
* @typeParam T - Type of elements contained in `a1`.
|
||||
* @typeParam U - Type of elements contained in `a2`.
|
||||
*
|
||||
* @returns {Array<[T,U]>} The array of pairs that had the same index in their source array.
|
||||
*/
|
||||
function zip<T, U>(a1: Array<T>, a2: Array<U>): Array<[T, U]> {
|
||||
if (a1.length <= a2.length) {
|
||||
return a1.map((e1, i) => [e1, a2[i]]);
|
||||
} else {
|
||||
return a2.map((e2, i) => [a1[i], e2]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "ds4",
|
||||
"title": "Dungeonslayers 4",
|
||||
"description": "The Dungeonslayers 4 system for FoundryVTT. Dungeonslayers (© Christian Kennig) is licensed under CC BY-NC-SA 3.0 (https://creativecommons.org/licenses/by-nc-sa/3.0/de/deed.en).",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"minimumCoreVersion": "0.7.9",
|
||||
"compatibleCoreVersion": "0.7.9",
|
||||
"templateVersion": 2,
|
||||
|
@ -43,7 +43,7 @@
|
|||
"primaryTokenAttribute": "combatValues.hitPoints",
|
||||
"url": "https://git.f3l.de/dungeonslayers/ds4",
|
||||
"manifest": "https://git.f3l.de/dungeonslayers/ds4/-/raw/latest/src/system.json?inline=false",
|
||||
"download": "https://git.f3l.de/dungeonslayers/ds4/-/jobs/artifacts/0.2.0/download?job=build",
|
||||
"download": "https://git.f3l.de/dungeonslayers/ds4/-/jobs/artifacts/0.2.1/download?job=build",
|
||||
"license": "MIT",
|
||||
"initiative": "@combatValues.initiative.total"
|
||||
}
|
||||
|
|
|
@ -13,20 +13,20 @@
|
|||
<div class="flexrow basic-properties">
|
||||
<div class="basic-property">
|
||||
<label class="basic-property-label"
|
||||
for="data.baseInfo.race">{{config.characterBaseInfo.race}}</label>
|
||||
for="data.baseInfo.race">{{config.i18n.characterBaseInfo.race}}</label>
|
||||
<input type="text" name="data.baseInfo.race" id="data.baseInfo.race" value="{{data.baseInfo.race}}"
|
||||
data-dtype="String" />
|
||||
</div>
|
||||
<div class="basic-property">
|
||||
<label class="basic-property-label"
|
||||
for="data.baseInfo.culture">{{config.characterBaseInfo.culture}}</label>
|
||||
for="data.baseInfo.culture">{{config.i18n.characterBaseInfo.culture}}</label>
|
||||
<input id="data.baseInfo.culture" type="text" name="data.baseInfo.culture"
|
||||
value="{{data.baseInfo.culture}}"
|
||||
data-dtype="String" />
|
||||
</div>
|
||||
<div class="basic-property flex125">
|
||||
<label class="basic-property-label"
|
||||
for="data.progression.progressPoints.used">{{config.characterProgression.progressPoints}}</label>
|
||||
for="data.progression.progressPoints.used">{{config.i18n.characterProgression.progressPoints}}</label>
|
||||
<div class="flexrow">
|
||||
<input id="data.progression.progressPoints.used" type="number"
|
||||
name="data.progression.progressPoints.used"
|
||||
|
@ -43,7 +43,7 @@
|
|||
</div>
|
||||
<div class="basic-property flex125">
|
||||
<label class="basic-property-label"
|
||||
for="data.progression.talentPoints.used">{{config.characterProgression.talentPoints}}</label>
|
||||
for="data.progression.talentPoints.used">{{config.i18n.characterProgression.talentPoints}}</label>
|
||||
<div class="flexrow">
|
||||
<input type="number" name="data.progression.talentPoints.used"
|
||||
id="data.progression.talentPoints.used"
|
||||
|
@ -56,13 +56,13 @@
|
|||
</div>
|
||||
<div class="basic-property">
|
||||
<label class="basic-property-label"
|
||||
for="data.baseInfo.class">{{config.characterBaseInfo.class}}</label>
|
||||
for="data.baseInfo.class">{{config.i18n.characterBaseInfo.class}}</label>
|
||||
<input type="text" id="data.baseInfo.class" name="data.baseInfo.class"
|
||||
value="{{data.baseInfo.class}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="basic-property">
|
||||
<label class="basic-property-label"
|
||||
for="data.baseInfo.heroClass">{{config.characterBaseInfo.heroClass}}</label>
|
||||
for="data.baseInfo.heroClass">{{config.i18n.characterBaseInfo.heroClass}}</label>
|
||||
<input type="text" id="data.baseInfo.heroClass" name="data.baseInfo.heroClass"
|
||||
value="{{data.baseInfo.heroClass}}"
|
||||
data-dtype="String" />
|
||||
|
@ -84,7 +84,9 @@
|
|||
<a class="item" data-tab="biography">{{localize 'DS4.HeadingBiography'}}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Sheet Body --}}
|
||||
<!-- beautify ignore:start -->
|
||||
<!-- prettier-ignore-start -->
|
||||
{{!-- Sheet Body (remove indentation to avoid annoying Handlebars auto-indent) --}}
|
||||
<section class="sheet-body">
|
||||
{{!-- Items Tab --}}
|
||||
{{> systems/ds4/templates/actor/partials/character-inventory.hbs}}
|
||||
|
@ -104,4 +106,6 @@
|
|||
editable=editable}}
|
||||
</div>
|
||||
</section>
|
||||
<!-- prettier-ignore-end -->
|
||||
<!-- beautify ignore:end -->
|
||||
</form>
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name" /></h1>
|
||||
<div class="flexrow basic-properties">
|
||||
<div class="basic-property">
|
||||
<label>{{config.creatureBaseInfo.creatureType}}</label>
|
||||
<label>{{config.i18n.creatureBaseInfo.creatureType}}</label>
|
||||
<select name="data.baseInfo.creatureType" data-type="String">
|
||||
{{#select data.baseInfo.creatureType}}
|
||||
{{#each config.creatureTypes as |value key|}}
|
||||
{{#each config.i18n.creatureTypes as |value key|}}
|
||||
<option value="{{key}}">{{value}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
|
@ -17,20 +17,20 @@
|
|||
</div>
|
||||
<div class="basic-property">
|
||||
<label class="basic-property-label"
|
||||
for="data.baseInfo.loot">{{config.creatureBaseInfo.loot}}</label>
|
||||
for="data.baseInfo.loot">{{config.i18n.creatureBaseInfo.loot}}</label>
|
||||
<input type="text" name="data.baseInfo.loot" value="{{data.baseInfo.loot}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="basic-property">
|
||||
<label class="basic-property-label"
|
||||
for="data.baseInfo.foeFactor">{{config.creatureBaseInfo.foeFactor}}</label>
|
||||
for="data.baseInfo.foeFactor">{{config.i18n.creatureBaseInfo.foeFactor}}</label>
|
||||
<input type="text" name="data.baseInfo.foeFactor" value="{{data.baseInfo.foeFactor}}"
|
||||
data-dtype="Number" />
|
||||
</div>
|
||||
<div class="basic-property">
|
||||
<label>{{config.creatureBaseInfo.sizeCategory}}</label>
|
||||
<label>{{config.i18n.creatureBaseInfo.sizeCategory}}</label>
|
||||
<select name="data.baseInfo.sizeCategory" data-type="String">
|
||||
{{#select data.baseInfo.sizeCategory}}
|
||||
{{#each config.creatureSizeCategories as |value key|}}
|
||||
{{#each config.i18n.creatureSizeCategories as |value key|}}
|
||||
<option value="{{key}}">{{value}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
|
@ -38,7 +38,7 @@
|
|||
</div>
|
||||
<div class="basic-property">
|
||||
<label class="basic-property-label"
|
||||
for="data.baseInfo.experiencePoints">{{config.creatureBaseInfo.experiencePoints}}</label>
|
||||
for="data.baseInfo.experiencePoints">{{config.i18n.creatureBaseInfo.experiencePoints}}</label>
|
||||
<input type="text" name="data.baseInfo.experiencePoints" value="{{data.baseInfo.experiencePoints}}"
|
||||
data-dtype="Number" />
|
||||
</div>
|
||||
|
@ -64,7 +64,7 @@
|
|||
{{> systems/ds4/templates/actor/partials/creature-inventory.hbs}}
|
||||
|
||||
{{!-- Special Creature Abilities Tab --}}
|
||||
{{> systems/ds4/templates/actor/partials/special-creature-abilites-overview.hbs}}
|
||||
{{> systems/ds4/templates/actor/partials/special-creature-abilities-overview.hbs}}
|
||||
|
||||
{{!-- Spells Tab --}}
|
||||
{{> systems/ds4/templates/actor/partials/spells-overview.hbs}}
|
||||
|
|
|
@ -42,11 +42,11 @@
|
|||
{{!-- ======================================================================== --}}
|
||||
|
||||
<div class="attributes-traits grid grid-3col">
|
||||
{{#each config.attributes as |attribute-label attribute-key|}}
|
||||
{{#each config.i18n.attributes as |attribute-label attribute-key|}}
|
||||
{{> attribute attribute-label=attribute-label attribute-key=attribute-key attribute-data=(lookup ../data.attributes
|
||||
attribute-key)}}
|
||||
{{/each}}
|
||||
{{#each config.traits as |trait-label trait-key|}}
|
||||
{{#each config.i18n.traits as |trait-label trait-key|}}
|
||||
{{> trait trait-label=trait-label trait-key=trait-key trait-data=(lookup ../data.traits trait-key)}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
<h4 class="items-list-title">{{localize 'DS4.CharacterCurrency'}}</h4>
|
||||
<ol class="items-list">
|
||||
<li class="item flexrow item-header">
|
||||
<label for="data.currency.gold" class="flex05">{{config.characterCurrency.gold}}</label>
|
||||
<label for="data.currency.gold" class="flex05">{{config.i18n.characterCurrency.gold}}</label>
|
||||
<input class="flex3 item-num-val item-change" type="number" min="0" step="1" name="data.currency.gold"
|
||||
id="data.currency.gold" value="{{data.currency.gold}}" data-dtype="Number" />
|
||||
<label for="data.currency.silver" class="flex05">{{config.characterCurrency.silver}}</label>
|
||||
<label for="data.currency.silver" class="flex05">{{config.i18n.characterCurrency.silver}}</label>
|
||||
<input class="flex3 item-num-val item-change" type="number" min="0" step="1" name="data.currency.silver"
|
||||
id="data.currency.silver" value="{{data.currency.silver}}" data-dtype="Number" />
|
||||
<label for="data.currency.copper" class="flex05">{{config.characterCurrency.copper}}</label>
|
||||
<label for="data.currency.copper" class="flex05">{{config.i18n.characterCurrency.copper}}</label>
|
||||
<input class="flex3 item-num-val item-change" type="number" min="0" step="1" name="data.currency.copper"
|
||||
id="data.currency.copper" value="{{data.currency.copper}}" data-dtype="Number" />
|
||||
</li>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="progression flexrow">
|
||||
<div class="progression-entry">
|
||||
<h2 class="progression-label"><label for="data.progression.level">{{config.characterProgression.level}}</label>
|
||||
<h2 class="progression-label"><label for="data.progression.level">{{config.i18n.characterProgression.level}}</label>
|
||||
</h2>
|
||||
<label for="data.progression.level" class="hidden">Progression Level</label>
|
||||
<input class="progression-value" type="number" name="data.progression.level" id="data.progression.level" value="{{data.progression.level}}"
|
||||
|
@ -8,7 +8,7 @@
|
|||
</div>
|
||||
<div class="progression-entry">
|
||||
<h2 class="progression-label"><label
|
||||
for="data.progression.experiencePoints">{{config.characterProgression.experiencePoints}}</label>
|
||||
for="data.progression.experiencePoints">{{config.i18n.characterProgression.experiencePoints}}</label>
|
||||
</h2>
|
||||
<label for="data.progression.experiencePoints" class="hidden">Experience Points</label>
|
||||
<input class="progression-value" type="number" name="data.progression.experiencePoints" id="data.progression.experiencePoints"
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
{{!-- ======================================================================== --}}
|
||||
|
||||
<div class="combat-values flexrow flex-between">
|
||||
{{#each config.combatValues as |combat-value-label combat-value-key|}}
|
||||
{{#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)}}
|
||||
{{/each}}
|
||||
|
|
|
@ -15,9 +15,8 @@
|
|||
{{#*inline "ifHasItemOfType"}}
|
||||
{{#if (and (ne itemsArray undefined) (gt itemsArray.length 0))}}
|
||||
{{> @partial-block}}
|
||||
{{else}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
|
||||
{{/if}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
|
||||
{{/inline}}
|
||||
|
||||
|
||||
|
@ -48,8 +47,8 @@
|
|||
{{> @partial-block }}
|
||||
{{!-- description --}}
|
||||
<div class="flex4">{{localize 'DS4.Description'}}</div>
|
||||
{{!-- add button --}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
|
||||
{{!-- control buttons placeholder --}}
|
||||
<div></div>
|
||||
</li>
|
||||
{{/inline}}
|
||||
|
||||
|
@ -110,8 +109,8 @@
|
|||
{{#each itemsByType.weapon as |item id|}}
|
||||
{{#> itemListEntry item=item}}
|
||||
<div class="flex05 item-image">
|
||||
<img src="{{lookup ../../config.attackTypesIcons item.data.data.attackType}}"
|
||||
title="{{lookup ../../config.attackTypes item.data.data.attackType}}" width="24" height="24" />
|
||||
<img src="{{lookup ../../config.icons.attackTypes item.data.data.attackType}}"
|
||||
title="{{lookup ../../config.i18n.attackTypes item.data.data.attackType}}" width="24" height="24" />
|
||||
</div>
|
||||
<div class="flex05 item-num-val">{{ item.data.data.weaponBonus}}</div>
|
||||
<div class="flex05 item-num-val">{{ item.data.data.opponentDefense}}</div>
|
||||
|
@ -135,11 +134,11 @@
|
|||
{{/itemListHeader}}
|
||||
{{#each itemsByType.armor as |item id|}}
|
||||
{{#> itemListEntry item=item }}
|
||||
<div title="{{lookup ../../config.armorMaterialTypes item.data.data.armorMaterialType}}">
|
||||
{{lookup ../../config.armorMaterialTypesAbbr item.data.data.armorMaterialType}}
|
||||
<div title="{{lookup ../../config.i18n.armorMaterialTypes item.data.data.armorMaterialType}}">
|
||||
{{lookup ../../config.i18n.armorMaterialTypesAbbr item.data.data.armorMaterialType}}
|
||||
</div>
|
||||
<div title="{{lookup ../../config.armorTypes item.data.data.armorType}}">
|
||||
{{lookup ../../config.armorTypesAbbr item.data.data.armorType}}
|
||||
<div title="{{lookup ../../config.i18n.armorTypes item.data.data.armorType}}">
|
||||
{{lookup ../../config.i18n.armorTypesAbbr item.data.data.armorType}}
|
||||
</div>
|
||||
<div class="flex05 item-num-val">{{ item.data.data.armorValue}}</div>
|
||||
{{/itemListEntry}}
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
!-- The current item is defined by the data-item-id HTML property of the parent li element.
|
||||
--}}
|
||||
<div class="item-controls">
|
||||
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
|
||||
<a class="item-control item-edit" title="{{localize 'DS4.UserInteractionEditItem'}}"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-control item-delete" title="{{localize 'DS4.UserInteractionDeleteItem'}}"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
<div class="tab profile" data-group="primary" data-tab="profile">
|
||||
<div class="grid grid-2col">
|
||||
{{#each data.profile as |profile-data-value profile-data-key|}}
|
||||
{{#if (ne profile-data-key 'biography')}}
|
||||
{{#if (and (ne profile-data-key 'biography') (ne profile-data-key 'specialCharacteristics'))}}
|
||||
<div class="profile-entry">
|
||||
<label for="data.profile.{{profile-data-key}}">
|
||||
{{lookup ../config.characterProfile profile-data-key}}
|
||||
{{lookup ../config.i18n.characterProfile profile-data-key}}
|
||||
</label>
|
||||
<input type="text" name="data.profile.{{profile-data-key}}" value="{{profile-data-value}}"
|
||||
data-dtype="{{lookup ../config/characterProfileDTypes profile-data-key}}" />
|
||||
data-dtype="{{lookup ../config.i18n.characterProfileDTypes profile-data-key}}" />
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
<div>
|
||||
<label for="data.profile.specialCharacteristics">
|
||||
{{lookup config.i18n.characterProfile 'specialCharacteristics'}}
|
||||
</label>
|
||||
<textarea name="data.profile.specialCharacteristics" data-dtype="String" rows="4">{{data.profile.specialCharacteristics}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -42,8 +42,8 @@
|
|||
<div class="flex1 item-name">{{localize 'DS4.ItemName'}}</div>
|
||||
{{!-- description --}}
|
||||
<div class="flex3">{{localize 'DS4.Description'}}</div>
|
||||
{{!-- add button --}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
|
||||
{{!-- control buttons placeholder --}}
|
||||
<div></div>
|
||||
</li>
|
||||
{{/inline}}
|
||||
|
||||
|
@ -58,4 +58,5 @@
|
|||
{{> baseItemListEntry item=item}}
|
||||
{{/each}}
|
||||
</ol>
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType='specialCreatureAbility' }}
|
||||
</div>
|
|
@ -13,7 +13,7 @@
|
|||
<div class="unit-data-pair item-num-val"
|
||||
title="{{localize localizationString}} [{{lookup unitNames unitDatum.unit}}]" >
|
||||
{{#if unitDatum.value }}
|
||||
{{unitDatum.value}}{{lookup unitAbbrs unitDatum.unit}}
|
||||
{{unitDatum.value}} {{lookup unitAbbrs unitDatum.unit}}
|
||||
{{else}}-{{/if}}
|
||||
</div>
|
||||
{{/inline}}
|
||||
|
@ -23,11 +23,11 @@
|
|||
!-- directly handing over the latter two.
|
||||
--}}
|
||||
{{#*inline "temporalUnit"}}
|
||||
{{> unit unitNames=config.temporalUnits unitAbbrs=config.temporalUnitsAbbr unitDatum=unitDatum localizationString=localizationString}}
|
||||
{{> unit unitNames=config.i18n.temporalUnits unitAbbrs=config.i18n.temporalUnitsAbbr unitDatum=unitDatum localizationString=localizationString}}
|
||||
{{/inline}}
|
||||
|
||||
{{#*inline "distanceUnit"}}
|
||||
{{> unit unitNames=config.distanceUnits unitAbbrs=config.distanceUnitsAbbr unitDatum=unitDatum localizationString=localizationString}}
|
||||
{{> unit unitNames=config.i18n.distanceUnits unitAbbrs=config.i18n.distanceUnitsAbbr unitDatum=unitDatum localizationString=localizationString}}
|
||||
{{/inline}}
|
||||
|
||||
|
||||
|
@ -55,8 +55,8 @@
|
|||
<div class="item-num-val" title="{{localize 'DS4.SpellCooldownDuration'}}"><i class="fas fa-hourglass-half"></i></div>
|
||||
{{!-- description --}}
|
||||
{{!-- <div class="flex3">{{localize 'DS4.Description'}}</div> --}}
|
||||
{{!-- add button --}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType='spell' }}
|
||||
{{!-- control buttons placeholder --}}
|
||||
<div></div>
|
||||
</li>
|
||||
{{#each itemsByType.spell as |item id|}}
|
||||
<li class="item flexrow" data-item-id="{{item._id}}">
|
||||
|
@ -71,8 +71,8 @@
|
|||
data-property="name" title="{{localize 'DS4.ItemName'}}" />
|
||||
{{!-- spell type --}}
|
||||
<div class="flex05 item-image">
|
||||
<img src="{{lookup ../config.spellTypesIcons item.data.data.spellType}}"
|
||||
title="{{lookup ../config.spellTypes item.data.data.spellType}}" width="24" height="24" />
|
||||
<img src="{{lookup ../config.icons.spellTypes item.data.data.spellType}}"
|
||||
title="{{lookup ../config.i18n.spellTypes item.data.data.spellType}}" width="24" height="24" />
|
||||
</div>
|
||||
{{!-- spell bonus --}}
|
||||
<input class="item-num-val item-change" type="text" data-dtype="String"
|
||||
|
@ -90,4 +90,6 @@
|
|||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
{{!-- add button --}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType='spell' }}
|
||||
</div>
|
|
@ -15,9 +15,8 @@
|
|||
{{#*inline "ifHasItemOfType"}}
|
||||
{{#if (and (ne itemsArray undefined) (gt itemsArray.length 0))}}
|
||||
{{> @partial-block}}
|
||||
{{else}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
|
||||
{{/if}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
|
||||
{{/inline}}
|
||||
|
||||
|
||||
|
@ -98,7 +97,7 @@
|
|||
{{/inline}}
|
||||
|
||||
{{!--
|
||||
!-- Render a list header for a base item list entries from a given item.
|
||||
!-- Render a list header for a base item list entry from a given item.
|
||||
!-- The partial assumes a variable dataType to be given in the context.
|
||||
!--
|
||||
!-- @param dataType: the string item type for the list
|
||||
|
@ -111,8 +110,8 @@
|
|||
<div class="flex1 item-name">{{localize 'DS4.ItemName'}}</div>
|
||||
{{!-- description --}}
|
||||
<div class="flex3">{{localize 'DS4.Description'}}</div>
|
||||
{{!-- add button --}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
|
||||
{{!-- control buttons placeholder --}}
|
||||
<div></div>
|
||||
</li>
|
||||
{{/inline}}
|
||||
|
||||
|
@ -133,8 +132,8 @@
|
|||
<div class="flex3">{{localize 'DS4.TalentRank'}}</div>
|
||||
{{!-- description --}}
|
||||
<div class="flex4">{{localize 'DS4.Description'}}</div>
|
||||
{{!-- add button --}}
|
||||
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType='talent' }}
|
||||
{{!-- control buttons placeholder --}}
|
||||
<div></div>
|
||||
</li>
|
||||
{{#each itemsByType.talent as |item id|}}
|
||||
{{> talentListEntry item=item}}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<label>{{localize "DS4.ArmorType"}}</label>
|
||||
<select name="data.armorType" data-type="String">
|
||||
{{#select data.armorType}}
|
||||
{{#each config.armorTypes as |value key|}}
|
||||
{{#each config.i18n.armorTypes as |value key|}}
|
||||
<option value="{{key}}">{{value}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
|
@ -15,7 +15,7 @@
|
|||
<label for="data.armorMaterialType">{{localize "DS4.ArmorMaterialType"}}</label>
|
||||
<select name="data.armorMaterialType" data-type="String">
|
||||
{{#select data.armorMaterialType}}
|
||||
{{#each config.armorMaterialTypes as |value key|}}
|
||||
{{#each config.i18n.armorMaterialTypes as |value key|}}
|
||||
<option value="{{key}}">{{value}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<label for="data.availability">{{localize "DS4.ItemAvailability"}}</label>
|
||||
<select name="data.availability" data-type="String">
|
||||
{{#select data.availability}}
|
||||
{{#each config.itemAvailabilities as |value key|}}
|
||||
{{#each config.i18n.itemAvailabilities as |value key|}}
|
||||
<option value="{{key}}">{{value}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
|
|
|
@ -5,16 +5,18 @@
|
|||
<div class="effect-image"></div>
|
||||
<div class="effect-name">Name</div>
|
||||
<div class="effect-controls">
|
||||
<a class="effect-control" data-action="create" title="Create Effect"><i class="fas fa-plus"></i> Add
|
||||
effect</a>
|
||||
<a class="effect-control" data-action="create" title="{{localize 'DS4.UserInteractionAddEffect'}}">
|
||||
<i class="fas fa-plus"></i> {{localize 'DS4.UserInteractionAddEffect'}}</a>
|
||||
</div>
|
||||
</li>
|
||||
{{#each item.effects as |effect id|}}
|
||||
<li class="effect flexrow" data-effect-id="{{effect._id}}">
|
||||
<h4 class="effect-name">{{effect.label}}</h4>
|
||||
<div class="effect-controls">
|
||||
<a class="effect-control" data-action="edit" title="Edit Effect"><i class="fas fa-edit"></i></a>
|
||||
<a class="effect-control" data-action="delete" title="Delete Effect"><i class="fas fa-trash"></i></a>
|
||||
<a class="effect-control" data-action="edit" title="{{localize 'DS4.UserInteractionEditEffect'}}">
|
||||
<i class="fas fa-edit"></i></a>
|
||||
<a class="effect-control" data-action="delete" title="{{localize 'DS4.UserInteractionDeleteEffect'}}">
|
||||
<i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
|
||||
<div class="header-fields flexrow">
|
||||
<h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeholder="Name" /></h1>
|
||||
<h2 class="item-type">{{localize (lookup config.itemTypes item.type)}}</h2>
|
||||
<h2 class="item-type">{{lookup config.i18n.itemTypes item.type}}</h2>
|
||||
{{> @partial-block}}
|
||||
</div>
|
||||
</header>
|
|
@ -12,9 +12,9 @@
|
|||
<select name="data.{{property}}.unit" data-type="String">
|
||||
{{#select (lookup (lookup data property) 'unit')}}
|
||||
{{#if (eq unitType 'temporal')}}
|
||||
{{#each (lookup config 'temporalUnitsAbbr') as |value key|}}<option value="{{key}}">{{value}}</option>{{/each}}
|
||||
{{#each (lookup config.i18n 'temporalUnitsAbbr') as |value key|}}<option value="{{key}}">{{value}}</option>{{/each}}
|
||||
{{else}}
|
||||
{{#each (lookup config 'distanceUnitsAbbr') as |value key|}}<option value="{{key}}">{{value}}</option>{{/each}}
|
||||
{{#each (lookup config.i18n 'distanceUnitsAbbr') as |value key|}}<option value="{{key}}">{{value}}</option>{{/each}}
|
||||
{{/if}}
|
||||
{{/select}}
|
||||
</select>
|
||||
|
@ -33,7 +33,7 @@
|
|||
<label for="data.spellType">{{localize "DS4.SpellType"}}</label>
|
||||
<select id="data.spellType" name="data.spellType" data-type="String">
|
||||
{{#select data.spellType}}
|
||||
{{#each config.spellTypes as |value key|}}
|
||||
{{#each config.i18n.spellTypes as |value key|}}
|
||||
<option value="{{key}}">{{value}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
|
@ -52,7 +52,7 @@
|
|||
<label for="data.spellCategory">{{localize "DS4.SpellCategory"}}</label>
|
||||
<select id="data.spellCategory" name="data.spellCategory" data-type="String">
|
||||
{{#select data.spellCategory}}
|
||||
{{#each config.spellCategories as |value key|}}
|
||||
{{#each config.i18n.spellCategories as |value key|}}
|
||||
<option value="{{key}}">{{value}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<label>{{localize "DS4.AttackType"}}</label>
|
||||
<select name="data.attackType" data-type="String">
|
||||
{{#select data.attackType}}
|
||||
{{#each config.attackTypes as |value key|}}
|
||||
{{#each config.i18n.attackTypes as |value key|}}
|
||||
<option value="{{key}}">{{value}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
|
|
|
@ -2,15 +2,17 @@
|
|||
<label for="ctv">{{localize "DS4.RollDialogTargetLabel"}}</label>
|
||||
<input id="ctv" data-type="Number" type="number" name="ctv" value="{{checkTargetValue}}" />
|
||||
<label for="gmmod">{{localize "DS4.RollDialogModifierLabel"}}</label>
|
||||
<input id="gmmod" data-type="Number" type="number" name="gmmod" value="0" />
|
||||
<input id="gmmod" data-type="Number" type="number" name="gmmod" value="0" autofocus />
|
||||
<label for="maxcoup">{{localize "DS4.RollDialogCoupLabel"}}</label>
|
||||
<input id="maxcoup" data-type="Number" type="number" name="maxcoup" value="{{maxCritSuccess}}" />
|
||||
<label for="minfumble">{{localize "DS4.RollDialogFumbleLabel"}}</label>
|
||||
<input id="minfumble" data-type="Number" type="number" name="minfumble" value="{{minCritFailure}}" />
|
||||
<label for="visibility">{{localize "DS4.RollDialogVisibilityLabel"}}</label>
|
||||
<select id="visibility" data-type="String">
|
||||
{{#each rollModes as |rollMode|}}
|
||||
<option value="{{rollMode}}">{{lookup ../config.chatVisibilities rollMode}}</option>
|
||||
{{#select rollMode}}
|
||||
{{#each config.i18n.chatVisibilities as |rollModeValue rollModeKey|}}
|
||||
<option value="{{rollModeKey}}">{{rollModeValue}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</form>
|
|
@ -1,8 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["DOM", "ES6", "ES2017"],
|
||||
"types": ["foundry-pc-types"],
|
||||
"esModuleInterop": true
|
||||
}
|
||||
"target": "ES2020",
|
||||
"lib": ["DOM", "ES2020"],
|
||||
"types": ["foundry-vtt-types"],
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue