mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-11 19:25:21 +01:00
[PR][Feature] Actor Sizes (#1433)
* Added support for adversary actor sizes * . * . * Finished token implementation * Fixed token-config * Updated SRD adversaries * . * Added size to Beastform tokenData * Fixed sizing for evolved beastforms * Beastform compendium update * .
This commit is contained in:
parent
7926c614e3
commit
8178fa5738
176 changed files with 1198 additions and 203 deletions
|
|
@ -53,6 +53,8 @@ CONFIG.Canvas.rulerClass = placeables.DhRuler;
|
||||||
CONFIG.Canvas.layers.templates.layerClass = placeables.DhTemplateLayer;
|
CONFIG.Canvas.layers.templates.layerClass = placeables.DhTemplateLayer;
|
||||||
CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate;
|
CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate;
|
||||||
|
|
||||||
|
CONFIG.Scene.documentClass = documents.DhScene;
|
||||||
|
|
||||||
CONFIG.Token.documentClass = documents.DhToken;
|
CONFIG.Token.documentClass = documents.DhToken;
|
||||||
CONFIG.Token.prototypeSheetClass = applications.sheetConfigs.DhPrototypeTokenConfig;
|
CONFIG.Token.prototypeSheetClass = applications.sheetConfigs.DhPrototypeTokenConfig;
|
||||||
CONFIG.Token.objectClass = placeables.DhTokenPlaceable;
|
CONFIG.Token.objectClass = placeables.DhTokenPlaceable;
|
||||||
|
|
|
||||||
20
lang/en.json
20
lang/en.json
|
|
@ -611,6 +611,9 @@
|
||||||
"insufficientHope": "The initiating character doesn't have enough hope",
|
"insufficientHope": "The initiating character doesn't have enough hope",
|
||||||
"createTagTeam": "Create TagTeam Roll",
|
"createTagTeam": "Create TagTeam Roll",
|
||||||
"chatMessageRollTitle": "Roll"
|
"chatMessageRollTitle": "Roll"
|
||||||
|
},
|
||||||
|
"TokenConfig": {
|
||||||
|
"actorSizeUsed": "Actor size is set, determining the dimensions"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CLASS": {
|
"CLASS": {
|
||||||
|
|
@ -1147,6 +1150,14 @@
|
||||||
"rect": "Rectangle",
|
"rect": "Rectangle",
|
||||||
"ray": "Ray"
|
"ray": "Ray"
|
||||||
},
|
},
|
||||||
|
"TokenSize": {
|
||||||
|
"tiny": "Tiny",
|
||||||
|
"small": "Small",
|
||||||
|
"medium": "Medium",
|
||||||
|
"large": "Large",
|
||||||
|
"huge": "Huge",
|
||||||
|
"gargantuan": "Gargantuan"
|
||||||
|
},
|
||||||
"Traits": {
|
"Traits": {
|
||||||
"agility": {
|
"agility": {
|
||||||
"name": "Agility",
|
"name": "Agility",
|
||||||
|
|
@ -2168,6 +2179,7 @@
|
||||||
"plural": "Targets"
|
"plural": "Targets"
|
||||||
},
|
},
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
|
"tokenSize": "Token Size",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"traitModifier": "Trait Modifier",
|
"traitModifier": "Trait Modifier",
|
||||||
"true": "True",
|
"true": "True",
|
||||||
|
|
@ -2224,6 +2236,7 @@
|
||||||
"tokenRingImg": { "label": "Subject Texture" },
|
"tokenRingImg": { "label": "Subject Texture" },
|
||||||
"tokenSize": {
|
"tokenSize": {
|
||||||
"placeholder": "Using character dimensions",
|
"placeholder": "Using character dimensions",
|
||||||
|
"disabledPlaceholder": "Set by character size",
|
||||||
"height": { "label": "Height" },
|
"height": { "label": "Height" },
|
||||||
"width": { "label": "Width" }
|
"width": { "label": "Width" }
|
||||||
},
|
},
|
||||||
|
|
@ -2249,7 +2262,9 @@
|
||||||
"hybridizeFeatureTitle": "Hybrid Features",
|
"hybridizeFeatureTitle": "Hybrid Features",
|
||||||
"hybridizeDrag": "Drag a form here to hybridize it.",
|
"hybridizeDrag": "Drag a form here to hybridize it.",
|
||||||
"mainTrait": "Main Trait",
|
"mainTrait": "Main Trait",
|
||||||
"traitBonus": "Trait Bonus"
|
"traitBonus": "Trait Bonus",
|
||||||
|
"evolvedTokenHint": "An evolved beastform's token is based on that of the form you evolve",
|
||||||
|
"evolvedImagePlaceholder": "The image for the form selected for evolution will be used"
|
||||||
},
|
},
|
||||||
"Class": {
|
"Class": {
|
||||||
"hopeFeatures": "Hope Features",
|
"hopeFeatures": "Hope Features",
|
||||||
|
|
@ -2802,7 +2817,8 @@
|
||||||
"companionPartnerLevelBlock": "The companion needs an assigned partner to level up.",
|
"companionPartnerLevelBlock": "The companion needs an assigned partner to level up.",
|
||||||
"configureAttribution": "Configure Attribution",
|
"configureAttribution": "Configure Attribution",
|
||||||
"deleteItem": "Delete Item",
|
"deleteItem": "Delete Item",
|
||||||
"immune": "Immune"
|
"immune": "Immune",
|
||||||
|
"tokenSize": "The token size used on the canvas"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
deleteAdversaryType: this.deleteAdversaryType,
|
deleteAdversaryType: this.deleteAdversaryType,
|
||||||
selectAdversaryType: this.selectAdversaryType,
|
selectAdversaryType: this.selectAdversaryType,
|
||||||
save: this.save,
|
save: this.save,
|
||||||
|
resetTokenSizes: this.resetTokenSizes,
|
||||||
reset: this.reset
|
reset: this.reset
|
||||||
},
|
},
|
||||||
form: { handler: this.updateData, submitOnChange: true }
|
form: { handler: this.updateData, submitOnChange: true }
|
||||||
|
|
@ -424,6 +425,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async resetTokenSizes() {
|
||||||
|
await this.settings.updateSource({
|
||||||
|
tokenSizes: this.settings.schema.fields.tokenSizes.initial
|
||||||
|
});
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
static async reset() {
|
static async reset() {
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
window: {
|
window: {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,18 @@
|
||||||
export default class DhPrototypeTokenConfig extends foundry.applications.sheets.PrototypeTokenConfig {
|
export default class DhPrototypeTokenConfig extends foundry.applications.sheets.PrototypeTokenConfig {
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
tabs: super.PARTS.tabs,
|
||||||
|
identity: super.PARTS.identity,
|
||||||
|
appearance: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets-settings/token-config/appearance.hbs',
|
||||||
|
scrollable: ['']
|
||||||
|
},
|
||||||
|
vision: super.PARTS.vision,
|
||||||
|
light: super.PARTS.light,
|
||||||
|
resources: super.PARTS.resources,
|
||||||
|
footer: super.PARTS.footer
|
||||||
|
};
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
async _prepareResourcesTab() {
|
async _prepareResourcesTab() {
|
||||||
const token = this.token;
|
const token = this.token;
|
||||||
|
|
@ -17,4 +31,11 @@ export default class DhPrototypeTokenConfig extends foundry.applications.sheets.
|
||||||
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
|
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _prepareAppearanceTab() {
|
||||||
|
const context = await super._prepareAppearanceTab();
|
||||||
|
context.actorSizeUsed = this.token.actor ? Boolean(this.token.actor.system.size) : false;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,18 @@
|
||||||
export default class DhTokenConfig extends foundry.applications.sheets.TokenConfig {
|
export default class DhTokenConfig extends foundry.applications.sheets.TokenConfig {
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
tabs: super.PARTS.tabs,
|
||||||
|
identity: super.PARTS.identity,
|
||||||
|
appearance: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets-settings/token-config/appearance.hbs',
|
||||||
|
scrollable: ['']
|
||||||
|
},
|
||||||
|
vision: super.PARTS.vision,
|
||||||
|
light: super.PARTS.light,
|
||||||
|
resources: super.PARTS.resources,
|
||||||
|
footer: super.PARTS.footer
|
||||||
|
};
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
async _prepareResourcesTab() {
|
async _prepareResourcesTab() {
|
||||||
const token = this.token;
|
const token = this.token;
|
||||||
|
|
@ -17,4 +31,11 @@ export default class DhTokenConfig extends foundry.applications.sheets.TokenConf
|
||||||
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
|
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _prepareAppearanceTab() {
|
||||||
|
const context = await super._prepareAppearanceTab();
|
||||||
|
context.actorSizeUsed = this.token.actor ? Boolean(this.token.actor.system.size) : false;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ export default class BeastformSheet extends DHBaseItemSheet {
|
||||||
name: context.document.system.advantageOn[key].value
|
name: context.document.system.advantageOn[key].value
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
context.dimensionsDisabled = context.document.system.tokenSize.size !== 'custom';
|
||||||
break;
|
break;
|
||||||
case 'effects':
|
case 'effects':
|
||||||
context.effects.actives = context.effects.actives.map(effect => {
|
context.effects.actives = context.effects.actives.map(effect => {
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,30 @@ export default class DhActorDirectory extends foundry.applications.sidebar.tabs.
|
||||||
: null;
|
: null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
_onDragStart(event) {
|
||||||
|
let actor;
|
||||||
|
const { entryId } = event.currentTarget.dataset;
|
||||||
|
if (entryId) {
|
||||||
|
actor = this.collection.get(entryId);
|
||||||
|
if (!actor?.visible) return false;
|
||||||
|
}
|
||||||
|
super._onDragStart(event);
|
||||||
|
|
||||||
|
// Create the drag preview.
|
||||||
|
if (actor && canvas.ready) {
|
||||||
|
const img = event.currentTarget.querySelector('img');
|
||||||
|
const pt = actor.prototypeToken;
|
||||||
|
const usesSize = actor.system.metadata.usesSize;
|
||||||
|
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||||
|
const width = usesSize ? tokenSizes[actor.system.size] : pt.width;
|
||||||
|
const height = usesSize ? tokenSizes[actor.system.size] : pt.height;
|
||||||
|
|
||||||
|
const w = width * canvas.dimensions.size * Math.abs(pt.texture.scaleX) * canvas.stage.scale.x;
|
||||||
|
const h = height * canvas.dimensions.size * Math.abs(pt.texture.scaleY) * canvas.stage.scale.y;
|
||||||
|
const preview = foundry.applications.ux.DragDrop.implementation.createDragImage(img, w, h);
|
||||||
|
event.dataTransfer.setDragImage(preview, w / 2, h / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,44 @@ export const adversaryTraits = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const tokenSize = {
|
||||||
|
custom: {
|
||||||
|
id: 'custom',
|
||||||
|
value: 0,
|
||||||
|
label: 'DAGGERHEART.GENERAL.custom'
|
||||||
|
},
|
||||||
|
tiny: {
|
||||||
|
id: 'tiny',
|
||||||
|
value: 1,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.tiny'
|
||||||
|
},
|
||||||
|
small: {
|
||||||
|
id: 'small',
|
||||||
|
value: 2,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.small'
|
||||||
|
},
|
||||||
|
medium: {
|
||||||
|
id: 'medium',
|
||||||
|
value: 3,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.medium'
|
||||||
|
},
|
||||||
|
large: {
|
||||||
|
id: 'large',
|
||||||
|
value: 4,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.large'
|
||||||
|
},
|
||||||
|
huge: {
|
||||||
|
id: 'huge',
|
||||||
|
value: 5,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.huge'
|
||||||
|
},
|
||||||
|
gargantuan: {
|
||||||
|
id: 'gargantuan',
|
||||||
|
value: 6,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.gargantuan'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const levelChoices = {
|
export const levelChoices = {
|
||||||
attributes: {
|
attributes: {
|
||||||
name: 'attributes',
|
name: 'attributes',
|
||||||
|
|
|
||||||
|
|
@ -65,20 +65,30 @@ export default class BeastformEffect extends BaseEffect {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateToken = token => ({
|
const updateToken = token => {
|
||||||
...baseUpdate,
|
const { x, y } = game.system.api.documents.DhToken.getSnappedPositionInSquareGrid(
|
||||||
'texture': {
|
token.object.scene.grid,
|
||||||
enabled: this.characterTokenData.usesDynamicToken,
|
{ x: token.x, y: token.y, elevation: token.elevation },
|
||||||
src: token.flags.daggerheart?.beastformTokenImg ?? this.characterTokenData.tokenImg
|
baseUpdate.width,
|
||||||
},
|
baseUpdate.height
|
||||||
'ring': {
|
);
|
||||||
subject: {
|
return {
|
||||||
texture:
|
...baseUpdate,
|
||||||
token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg
|
x,
|
||||||
}
|
y,
|
||||||
},
|
'texture': {
|
||||||
'flags.daggerheart': { '-=beastformTokenImg': null, '-=beastformSubjectTexture': null }
|
enabled: this.characterTokenData.usesDynamicToken,
|
||||||
});
|
src: token.flags.daggerheart?.beastformTokenImg ?? this.characterTokenData.tokenImg
|
||||||
|
},
|
||||||
|
'ring': {
|
||||||
|
subject: {
|
||||||
|
texture:
|
||||||
|
token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'flags.daggerheart': { '-=beastformTokenImg': null, '-=beastformSubjectTexture': null }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
await updateActorTokens(this.parent.parent, update, updateToken);
|
await updateActorTokens(this.parent.parent, update, updateToken);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
label: 'TYPES.Actor.adversary',
|
label: 'TYPES.Actor.adversary',
|
||||||
type: 'adversary',
|
type: 'adversary',
|
||||||
settingSheet: DHAdversarySettings,
|
settingSheet: DHAdversarySettings,
|
||||||
hasAttribution: true
|
hasAttribution: true,
|
||||||
|
usesSize: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,7 +143,7 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
}
|
}
|
||||||
|
|
||||||
isItemValid(source) {
|
isItemValid(source) {
|
||||||
return source.type === "feature";
|
return source.type === 'feature';
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preUpdate(changes, options, user) {
|
async _preUpdate(changes, options, user) {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
settingSheet: null,
|
settingSheet: null,
|
||||||
hasResistances: true,
|
hasResistances: true,
|
||||||
hasAttribution: false,
|
hasAttribution: false,
|
||||||
hasLimitedView: true
|
hasLimitedView: true,
|
||||||
|
usesSize: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,6 +78,13 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
'DAGGERHEART.GENERAL.DamageResistance.magicalReduction'
|
'DAGGERHEART.GENERAL.DamageResistance.magicalReduction'
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
if (this.metadata.usesSize)
|
||||||
|
schema.size = new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
nullable: false,
|
||||||
|
choices: CONFIG.DH.ACTOR.tokenSize,
|
||||||
|
initial: CONFIG.DH.ACTOR.tokenSize.custom.id
|
||||||
|
});
|
||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export default class DhParty extends BaseDataActor {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
isItemValid(source) {
|
isItemValid(source) {
|
||||||
return ["weapon", "armor", "consumable", "loot"].includes(source.type);
|
return ['weapon', 'armor', 'consumable', 'loot'].includes(source.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareBaseData() {
|
prepareBaseData() {
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,18 @@ export default class BeastformField extends fields.SchemaField {
|
||||||
|
|
||||||
beastformEffect.changes = [...beastformEffect.changes, ...evolvedForm.changes];
|
beastformEffect.changes = [...beastformEffect.changes, ...evolvedForm.changes];
|
||||||
formData.system.features = [...formData.system.features, ...selectedForm.system.features.map(x => x.uuid)];
|
formData.system.features = [...formData.system.features, ...selectedForm.system.features.map(x => x.uuid)];
|
||||||
|
|
||||||
|
const baseSize = evolvedData.form.system.tokenSize.size;
|
||||||
|
const evolvedSize =
|
||||||
|
baseSize === 'custom'
|
||||||
|
? 'custom'
|
||||||
|
: (Object.keys(CONFIG.DH.ACTOR.tokenSize).find(
|
||||||
|
x => CONFIG.DH.ACTOR.tokenSize[x].value === CONFIG.DH.ACTOR.tokenSize[baseSize].value + 1
|
||||||
|
) ?? baseSize);
|
||||||
|
formData.system.tokenSize = {
|
||||||
|
...evolvedData.form.system.tokenSize,
|
||||||
|
size: evolvedSize
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedForm.system.beastformType === CONFIG.DH.ITEM.beastformTypes.hybrid.id) {
|
if (selectedForm.system.beastformType === CONFIG.DH.ITEM.beastformTypes.hybrid.id) {
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,12 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
base64: false
|
base64: false
|
||||||
}),
|
}),
|
||||||
tokenSize: new fields.SchemaField({
|
tokenSize: new fields.SchemaField({
|
||||||
|
size: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
nullable: false,
|
||||||
|
choices: CONFIG.DH.ACTOR.tokenSize,
|
||||||
|
initial: CONFIG.DH.ACTOR.tokenSize.custom.id
|
||||||
|
}),
|
||||||
height: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }),
|
height: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }),
|
||||||
width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true })
|
width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true })
|
||||||
}),
|
}),
|
||||||
|
|
@ -190,9 +196,18 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
|
|
||||||
await this.parent.parent.createEmbeddedDocuments('ActiveEffect', [beastformEffect.toObject()]);
|
await this.parent.parent.createEmbeddedDocuments('ActiveEffect', [beastformEffect.toObject()]);
|
||||||
|
|
||||||
|
const autoTokenSize =
|
||||||
|
this.tokenSize.size !== 'custom'
|
||||||
|
? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes[
|
||||||
|
this.tokenSize.size
|
||||||
|
]
|
||||||
|
: null;
|
||||||
|
const width = autoTokenSize ?? this.tokenSize.width;
|
||||||
|
const height = autoTokenSize ?? this.tokenSize.height;
|
||||||
|
|
||||||
const prototypeTokenUpdate = {
|
const prototypeTokenUpdate = {
|
||||||
height: this.tokenSize.height,
|
height,
|
||||||
width: this.tokenSize.width,
|
width,
|
||||||
texture: {
|
texture: {
|
||||||
src: this.tokenImg
|
src: this.tokenImg
|
||||||
},
|
},
|
||||||
|
|
@ -202,16 +217,25 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const tokenUpdate = token => {
|
||||||
const tokenUpdate = token => ({
|
const { x, y } = game.system.api.documents.DhToken.getSnappedPositionInSquareGrid(
|
||||||
...prototypeTokenUpdate,
|
token.object.scene.grid,
|
||||||
flags: {
|
{ x: token.x, y: token.y, elevation: token.elevation },
|
||||||
daggerheart: {
|
width ?? token.width,
|
||||||
beastformTokenImg: token.texture.src,
|
height ?? token.height
|
||||||
beastformSubjectTexture: token.ring.subject.texture
|
);
|
||||||
|
return {
|
||||||
|
...prototypeTokenUpdate,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
flags: {
|
||||||
|
daggerheart: {
|
||||||
|
beastformTokenImg: token.texture.src,
|
||||||
|
beastformSubjectTexture: token.ring.subject.texture
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
});
|
};
|
||||||
|
|
||||||
await updateActorTokens(this.parent.parent, prototypeTokenUpdate, tokenUpdate);
|
await updateActorTokens(this.parent.parent, prototypeTokenUpdate, tokenUpdate);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,38 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
traitArray: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }), {
|
traitArray: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }), {
|
||||||
initial: () => [2, 1, 1, 0, 0, -1]
|
initial: () => [2, 1, 1, 0, 0, -1]
|
||||||
}),
|
}),
|
||||||
|
tokenSizes: new fields.SchemaField({
|
||||||
|
tiny: new fields.NumberField({
|
||||||
|
integer: false,
|
||||||
|
initial: 0.5,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.tiny'
|
||||||
|
}),
|
||||||
|
small: new fields.NumberField({
|
||||||
|
integer: false,
|
||||||
|
initial: 0.8,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.small'
|
||||||
|
}),
|
||||||
|
medium: new fields.NumberField({
|
||||||
|
integer: false,
|
||||||
|
initial: 1,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.medium'
|
||||||
|
}),
|
||||||
|
large: new fields.NumberField({
|
||||||
|
integer: false,
|
||||||
|
initial: 2,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.large'
|
||||||
|
}),
|
||||||
|
huge: new fields.NumberField({
|
||||||
|
integer: false,
|
||||||
|
initial: 3,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.huge'
|
||||||
|
}),
|
||||||
|
gargantuan: new fields.NumberField({
|
||||||
|
integer: false,
|
||||||
|
initial: 4,
|
||||||
|
label: 'DAGGERHEART.CONFIG.TokenSize.gargantuan'
|
||||||
|
})
|
||||||
|
}),
|
||||||
currency: new fields.SchemaField({
|
currency: new fields.SchemaField({
|
||||||
title: new fields.StringField({
|
title: new fields.StringField({
|
||||||
required: true,
|
required: true,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ export { default as DhpCombat } from './combat.mjs';
|
||||||
export { default as DHCombatant } from './combatant.mjs';
|
export { default as DHCombatant } from './combatant.mjs';
|
||||||
export { default as DhActiveEffect } from './activeEffect.mjs';
|
export { default as DhActiveEffect } from './activeEffect.mjs';
|
||||||
export { default as DhChatMessage } from './chatMessage.mjs';
|
export { default as DhChatMessage } from './chatMessage.mjs';
|
||||||
|
export { default as DhScene } from './scene.mjs';
|
||||||
export { default as DhToken } from './token.mjs';
|
export { default as DhToken } from './token.mjs';
|
||||||
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
||||||
export { default as DhTemplateManager } from './templateManager.mjs';
|
export { default as DhTemplateManager } from './templateManager.mjs';
|
||||||
|
|
|
||||||
|
|
@ -74,16 +74,27 @@ export default class DhpActor extends Actor {
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
if ((await super._preCreate(data, options, user)) === false) return false;
|
if ((await super._preCreate(data, options, user)) === false) return false;
|
||||||
|
const update = {};
|
||||||
|
|
||||||
|
// Set default token size. Done here as we do not want to set a datamodel default, since that would apply the sizing to third party actor modules that aren't set up with the size system.
|
||||||
|
if (this.system.metadata.usesSize && !data.system?.size) {
|
||||||
|
Object.assign(update, {
|
||||||
|
system: {
|
||||||
|
size: CONFIG.DH.ACTOR.tokenSize.medium.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Configure prototype token settings
|
// Configure prototype token settings
|
||||||
const prototypeToken = {};
|
|
||||||
if (['character', 'companion', 'party'].includes(this.type))
|
if (['character', 'companion', 'party'].includes(this.type))
|
||||||
Object.assign(prototypeToken, {
|
Object.assign(update, {
|
||||||
sight: { enabled: true },
|
prototypeToken: {
|
||||||
actorLink: true,
|
sight: { enabled: true },
|
||||||
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
actorLink: true,
|
||||||
|
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.updateSource({ prototypeToken });
|
this.updateSource(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onUpdate(changes, options, userId) {
|
_onUpdate(changes, options, userId) {
|
||||||
|
|
|
||||||
40
module/documents/scene.mjs
Normal file
40
module/documents/scene.mjs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import DHToken from './token.mjs';
|
||||||
|
|
||||||
|
export default class DhScene extends Scene {
|
||||||
|
/** A map of `TokenDocument` IDs embedded in this scene long with new dimensions from actor size-category changes */
|
||||||
|
#sizeSyncBatch = new Map();
|
||||||
|
|
||||||
|
/** Synchronize a token's dimensions with its actor's size category. */
|
||||||
|
syncTokenDimensions(tokenDoc, tokenSize) {
|
||||||
|
if (!tokenDoc.parent?.tokens.has(tokenDoc.id)) return;
|
||||||
|
const prototype = tokenDoc.actor?.prototypeToken ?? tokenDoc;
|
||||||
|
this.#sizeSyncBatch.set(tokenDoc.id, {
|
||||||
|
size: tokenSize,
|
||||||
|
prototypeSize: { width: prototype.width, height: prototype.height },
|
||||||
|
position: { x: tokenDoc.x, y: tokenDoc.y, elevation: tokenDoc.elevation }
|
||||||
|
});
|
||||||
|
this.#processSyncBatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Retrieve size and clear size-sync batch, make updates. */
|
||||||
|
#processSyncBatch = foundry.utils.debounce(() => {
|
||||||
|
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||||
|
const entries = this.#sizeSyncBatch
|
||||||
|
.entries()
|
||||||
|
.toArray()
|
||||||
|
.map(([_id, { size, prototypeSize, position }]) => {
|
||||||
|
const tokenSize = tokenSizes[size];
|
||||||
|
const width = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.width;
|
||||||
|
const height = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.height;
|
||||||
|
const updatedPosition = DHToken.getSnappedPositionInSquareGrid(this.grid, position, width, height);
|
||||||
|
return {
|
||||||
|
_id,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
...updatedPosition
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.#sizeSyncBatch.clear();
|
||||||
|
this.updateEmbeddedDocuments('Token', entries, { animation: { movementSpeed: 1.5 } });
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export default class DHToken extends TokenDocument {
|
export default class DHToken extends CONFIG.Token.documentClass {
|
||||||
/**
|
/**
|
||||||
* Inspect the Actor data model and identify the set of attributes which could be used for a Token Bar.
|
* Inspect the Actor data model and identify the set of attributes which could be used for a Token Bar.
|
||||||
* @param {object} attributes The tracked attributes which can be chosen from
|
* @param {object} attributes The tracked attributes which can be chosen from
|
||||||
|
|
@ -100,4 +100,440 @@ export default class DHToken extends TokenDocument {
|
||||||
}
|
}
|
||||||
super.deleteCombatants(tokens, combat ?? {});
|
super.deleteCombatants(tokens, combat ?? {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**@inheritdoc */
|
||||||
|
static async _preCreateOperation(documents, operation, user) {
|
||||||
|
const allowed = await super._preCreateOperation(documents, operation, user);
|
||||||
|
if (allowed === false) return false;
|
||||||
|
|
||||||
|
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||||
|
for (const document of documents) {
|
||||||
|
const actor = document.actor;
|
||||||
|
if (actor?.system.metadata.usesSize) {
|
||||||
|
const tokenSize = tokenSizes[actor.system.size];
|
||||||
|
if (tokenSize && actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
||||||
|
document.updateSource({
|
||||||
|
width: tokenSize,
|
||||||
|
height: tokenSize
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**@inheritdoc */
|
||||||
|
_onRelatedUpdate(update = {}, operation = {}) {
|
||||||
|
super._onRelatedUpdate(update, operation);
|
||||||
|
|
||||||
|
if (!this.actor?.isOwner) return;
|
||||||
|
|
||||||
|
const updates = Array.isArray(update) ? update : [update];
|
||||||
|
const activeGM = game.users.activeGM; // Let the active GM take care of updates if available
|
||||||
|
for (let update of updates) {
|
||||||
|
if (
|
||||||
|
this.actor.system.metadata.usesSize &&
|
||||||
|
update.system?.size &&
|
||||||
|
activeGM &&
|
||||||
|
game.user.id === activeGM.id
|
||||||
|
) {
|
||||||
|
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||||
|
const tokenSize = tokenSizes[update.system.size];
|
||||||
|
if (tokenSize !== this.width || tokenSize !== this.height) {
|
||||||
|
this.parent?.syncTokenDimensions(this, update.system.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**@inheritdoc */
|
||||||
|
getSnappedPosition(data = {}) {
|
||||||
|
const grid = this.parent?.grid ?? BaseScene.defaultGrid;
|
||||||
|
const x = data.x ?? this.x;
|
||||||
|
const y = data.y ?? this.y;
|
||||||
|
let elevation = data.elevation ?? this.elevation;
|
||||||
|
const unsnapped = { x, y, elevation };
|
||||||
|
|
||||||
|
// Gridless grid
|
||||||
|
if (grid.isGridless) return unsnapped;
|
||||||
|
|
||||||
|
// Get position and elevation
|
||||||
|
elevation = Math.round(elevation / grid.distance) * grid.distance;
|
||||||
|
|
||||||
|
let width = data.width ?? this.width;
|
||||||
|
let height = data.height ?? this.height;
|
||||||
|
|
||||||
|
if (this.actor?.system.metadata.usesSize) {
|
||||||
|
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||||
|
const tokenSize = tokenSizes[this.actor.system.size];
|
||||||
|
if (tokenSize && this.actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
||||||
|
width = tokenSize ?? width;
|
||||||
|
height = tokenSize ?? height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round width and height to nearest multiple of 0.5 if not small
|
||||||
|
width = width < 1 ? width : Math.round(width * 2) / 2;
|
||||||
|
height = height < 1 ? height : Math.round(height * 2) / 2;
|
||||||
|
const shape = data.shape ?? this.shape;
|
||||||
|
|
||||||
|
// Square grid
|
||||||
|
let snapped;
|
||||||
|
if (grid.isSquare) snapped = DHToken.getSnappedPositionInSquareGrid(grid, unsnapped, width, height);
|
||||||
|
// Hexagonal grid
|
||||||
|
else snapped = DHToken.getSnappedPositionInHexagonalGrid(grid, unsnapped, width, height, shape);
|
||||||
|
return { x: snapped.x, y: snapped.y, elevation };
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSnappedPositionInSquareGrid(grid, position, width, height) {
|
||||||
|
const M = CONST.GRID_SNAPPING_MODES;
|
||||||
|
// Small tokens snap to any vertex of the subgrid with resolution 4
|
||||||
|
// where the token is fully contained within the grid space
|
||||||
|
const isTiny = (width === 0.5 && height <= 1) || (width <= 1 && height === 0.5);
|
||||||
|
if (isTiny) {
|
||||||
|
let x = position.x / grid.size;
|
||||||
|
let y = position.y / grid.size;
|
||||||
|
if (width === 1) x = Math.round(x);
|
||||||
|
else {
|
||||||
|
x = Math.floor(x * 8);
|
||||||
|
const k = ((x % 8) + 8) % 8;
|
||||||
|
if (k >= 6) x = Math.ceil(x / 8);
|
||||||
|
else if (k === 5) x = Math.floor(x / 8) + 0.5;
|
||||||
|
else x = Math.round(x / 2) / 4;
|
||||||
|
}
|
||||||
|
if (height === 1) y = Math.round(y);
|
||||||
|
else {
|
||||||
|
y = Math.floor(y * 8);
|
||||||
|
const k = ((y % 8) + 8) % 8;
|
||||||
|
if (k >= 6) y = Math.ceil(y / 8);
|
||||||
|
else if (k === 5) y = Math.floor(y / 8) + 0.5;
|
||||||
|
else y = Math.round(y / 2) / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
x *= grid.size;
|
||||||
|
y *= grid.size;
|
||||||
|
|
||||||
|
return { x, y };
|
||||||
|
} else if (width < 1 && height < 1) {
|
||||||
|
// isSmall
|
||||||
|
let xGrid = Math.round(position.x / grid.size);
|
||||||
|
let yGrid = Math.round(position.y / grid.size);
|
||||||
|
|
||||||
|
const x = xGrid * grid.size + grid.size / 2 - (width * grid.size) / 2;
|
||||||
|
const y = yGrid * grid.size + grid.size / 2 - (height * grid.size) / 2;
|
||||||
|
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
|
|
||||||
|
const modeX = Number.isInteger(width) ? M.VERTEX : M.VERTEX | M.EDGE_MIDPOINT | M.CENTER;
|
||||||
|
const modeY = Number.isInteger(height) ? M.VERTEX : M.VERTEX | M.EDGE_MIDPOINT | M.CENTER;
|
||||||
|
|
||||||
|
if (modeX === modeY) return grid.getSnappedPoint(position, { mode: modeX });
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: grid.getSnappedPoint(position, { mode: modeX }).x,
|
||||||
|
y: grid.getSnappedPoint(position, { mode: modeY }).y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//#region CopyPasta for mean private methods that have to be duplicated
|
||||||
|
static getSnappedPositionInHexagonalGrid(grid, position, width, height, shape) {
|
||||||
|
// Hexagonal shape
|
||||||
|
const hexagonalShape = DHToken.#getHexagonalShape(width, height, shape, grid.columns);
|
||||||
|
if (hexagonalShape) {
|
||||||
|
const offsetX = hexagonalShape.anchor.x * grid.sizeX;
|
||||||
|
const offsetY = hexagonalShape.anchor.y * grid.sizeY;
|
||||||
|
position = grid.getCenterPoint({ x: position.x + offsetX, y: position.y + offsetY });
|
||||||
|
position.x -= offsetX;
|
||||||
|
position.y -= offsetY;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rectagular shape
|
||||||
|
const M = CONST.GRID_SNAPPING_MODES;
|
||||||
|
return grid.getSnappedPoint(position, { mode: M.CENTER | M.VERTEX | M.CORNER | M.SIDE_MIDPOINT });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cache of hexagonal shapes.
|
||||||
|
* @type {Map<string, DeepReadonly<TokenHexagonalShapeData>>}
|
||||||
|
*/
|
||||||
|
static #hexagonalShapes = new Map();
|
||||||
|
|
||||||
|
static #getHexagonalShape(width, height, shape, columns) {
|
||||||
|
if (!Number.isInteger(width * 2) || !Number.isInteger(height * 2)) return null;
|
||||||
|
|
||||||
|
// TODO: can we set a max of 2^13 on width and height so that we may use an integer key?
|
||||||
|
const key = `${width},${height},${shape}${columns ? 'C' : 'R'}`;
|
||||||
|
let data = DHToken.#hexagonalShapes.get(key);
|
||||||
|
if (data) return data;
|
||||||
|
|
||||||
|
// Hexagon symmetry
|
||||||
|
if (columns) {
|
||||||
|
const rowData = BaseToken.#getHexagonalShape(height, width, shape, false);
|
||||||
|
if (!rowData) return null;
|
||||||
|
|
||||||
|
// Transpose the offsets/points of the shape in row orientation
|
||||||
|
const offsets = { even: [], odd: [] };
|
||||||
|
for (const { i, j } of rowData.offsets.even) offsets.even.push({ i: j, j: i });
|
||||||
|
for (const { i, j } of rowData.offsets.odd) offsets.odd.push({ i: j, j: i });
|
||||||
|
offsets.even.sort(({ i: i0, j: j0 }, { i: i1, j: j1 }) => j0 - j1 || i0 - i1);
|
||||||
|
offsets.odd.sort(({ i: i0, j: j0 }, { i: i1, j: j1 }) => j0 - j1 || i0 - i1);
|
||||||
|
const points = [];
|
||||||
|
for (let i = rowData.points.length; i > 0; i -= 2) {
|
||||||
|
points.push(rowData.points[i - 1], rowData.points[i - 2]);
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
offsets,
|
||||||
|
points,
|
||||||
|
center: { x: rowData.center.y, y: rowData.center.x },
|
||||||
|
anchor: { x: rowData.anchor.y, y: rowData.anchor.x }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Small hexagon
|
||||||
|
else if (width === 0.5 && height === 0.5) {
|
||||||
|
data = {
|
||||||
|
offsets: { even: [{ i: 0, j: 0 }], odd: [{ i: 0, j: 0 }] },
|
||||||
|
points: [0.25, 0.0, 0.5, 0.125, 0.5, 0.375, 0.25, 0.5, 0.0, 0.375, 0.0, 0.125],
|
||||||
|
center: { x: 0.25, y: 0.25 },
|
||||||
|
anchor: { x: 0.25, y: 0.25 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal hexagon
|
||||||
|
else if (width === 1 && height === 1) {
|
||||||
|
data = {
|
||||||
|
offsets: { even: [{ i: 0, j: 0 }], odd: [{ i: 0, j: 0 }] },
|
||||||
|
points: [0.5, 0.0, 1.0, 0.25, 1, 0.75, 0.5, 1.0, 0.0, 0.75, 0.0, 0.25],
|
||||||
|
center: { x: 0.5, y: 0.5 },
|
||||||
|
anchor: { x: 0.5, y: 0.5 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hexagonal ellipse or trapezoid
|
||||||
|
else if (shape <= CONST.TOKEN_SHAPES.TRAPEZOID_2) {
|
||||||
|
data = DHToken.#createHexagonalEllipseOrTrapezoid(width, height, shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hexagonal rectangle
|
||||||
|
else if (shape <= CONST.TOKEN_SHAPES.RECTANGLE_2) {
|
||||||
|
data = DHToken.#createHexagonalRectangle(width, height, shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the shape
|
||||||
|
if (data) {
|
||||||
|
foundry.utils.deepFreeze(data);
|
||||||
|
DHToken.#hexagonalShapes.set(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static #createHexagonalEllipseOrTrapezoid(width, height, shape) {
|
||||||
|
if (!Number.isInteger(width) || !Number.isInteger(height)) return null;
|
||||||
|
const points = [];
|
||||||
|
let top;
|
||||||
|
let bottom;
|
||||||
|
switch (shape) {
|
||||||
|
case CONST.TOKEN_SHAPES.ELLIPSE_1:
|
||||||
|
if (height >= 2 * width) return null;
|
||||||
|
top = Math.floor(height / 2);
|
||||||
|
bottom = Math.floor((height - 1) / 2);
|
||||||
|
break;
|
||||||
|
case CONST.TOKEN_SHAPES.ELLIPSE_2:
|
||||||
|
if (height >= 2 * width) return null;
|
||||||
|
top = Math.floor((height - 1) / 2);
|
||||||
|
bottom = Math.floor(height / 2);
|
||||||
|
break;
|
||||||
|
case CONST.TOKEN_SHAPES.TRAPEZOID_1:
|
||||||
|
if (height > width) return null;
|
||||||
|
top = height - 1;
|
||||||
|
bottom = 0;
|
||||||
|
break;
|
||||||
|
case CONST.TOKEN_SHAPES.TRAPEZOID_2:
|
||||||
|
if (height > width) return null;
|
||||||
|
top = 0;
|
||||||
|
bottom = height - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const offsets = { even: [], odd: [] };
|
||||||
|
for (let i = bottom; i > 0; i--) {
|
||||||
|
for (let j = 0; j < width - i; j++) {
|
||||||
|
offsets.even.push({ i: bottom - i, j: j + (((bottom & 1) + i + 1) >> 1) });
|
||||||
|
offsets.odd.push({ i: bottom - i, j: j + (((bottom & 1) + i) >> 1) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i <= top; i++) {
|
||||||
|
for (let j = 0; j < width - i; j++) {
|
||||||
|
offsets.even.push({ i: bottom + i, j: j + (((bottom & 1) + i + 1) >> 1) });
|
||||||
|
offsets.odd.push({ i: bottom + i, j: j + (((bottom & 1) + i) >> 1) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let x = 0.5 * bottom;
|
||||||
|
let y = 0.25;
|
||||||
|
for (let k = width - bottom; k--; ) {
|
||||||
|
points.push(x, y);
|
||||||
|
x += 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
x += 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
}
|
||||||
|
points.push(x, y);
|
||||||
|
for (let k = bottom; k--; ) {
|
||||||
|
y += 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x += 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
y += 0.5;
|
||||||
|
for (let k = top; k--; ) {
|
||||||
|
points.push(x, y);
|
||||||
|
x -= 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
y += 0.5;
|
||||||
|
}
|
||||||
|
for (let k = width - top; k--; ) {
|
||||||
|
points.push(x, y);
|
||||||
|
x -= 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
x -= 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
}
|
||||||
|
points.push(x, y);
|
||||||
|
for (let k = top; k--; ) {
|
||||||
|
y -= 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x -= 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
y -= 0.5;
|
||||||
|
for (let k = bottom; k--; ) {
|
||||||
|
points.push(x, y);
|
||||||
|
x += 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
y -= 0.5;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
offsets,
|
||||||
|
points,
|
||||||
|
// We use the centroid of the polygon for ellipse and trapzoid shapes
|
||||||
|
center: foundry.utils.polygonCentroid(points),
|
||||||
|
anchor: bottom % 2 ? { x: 0.0, y: 0.5 } : { x: 0.5, y: 0.5 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the row-based hexagonal rectangle given the type, width, and height.
|
||||||
|
* @param {number} width The width of the Token (positive)
|
||||||
|
* @param {number} height The height of the Token (positive)
|
||||||
|
* @param {TokenShapeType} shape The shape type (must be RECTANGLE_1 or RECTANGLE_2)
|
||||||
|
* @returns {TokenHexagonalShapeData|null} The hexagonal shape or null if there is no shape
|
||||||
|
* for the given combination of arguments
|
||||||
|
*/
|
||||||
|
static #createHexagonalRectangle(width, height, shape) {
|
||||||
|
if (width < 1 || !Number.isInteger(height)) return null;
|
||||||
|
if (width === 1 && height > 1) return null;
|
||||||
|
if (!Number.isInteger(width) && height === 1) return null;
|
||||||
|
|
||||||
|
const even = shape === CONST.TOKEN_SHAPES.RECTANGLE_1 || height === 1;
|
||||||
|
const offsets = { even: [], odd: [] };
|
||||||
|
for (let i = 0; i < height; i++) {
|
||||||
|
const j0 = even ? 0 : (i + 1) & 1;
|
||||||
|
const j1 = ((width + (i & 1) * 0.5) | 0) - (even ? i & 1 : 0);
|
||||||
|
for (let j = j0; j < j1; j++) {
|
||||||
|
offsets.even.push({ i, j: j + (i & 1) });
|
||||||
|
offsets.odd.push({ i, j });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let x = even ? 0.0 : 0.5;
|
||||||
|
let y = 0.25;
|
||||||
|
const points = [x, y];
|
||||||
|
while (x + 1 <= width) {
|
||||||
|
x += 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
x += 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
if (x !== width) {
|
||||||
|
y += 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x += 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
while (y + 1.5 <= 0.75 * height) {
|
||||||
|
y += 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x -= 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
y += 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x += 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
if (y + 0.75 < 0.75 * height) {
|
||||||
|
y += 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x -= 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
y += 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
while (x - 1 >= 0) {
|
||||||
|
x -= 0.5;
|
||||||
|
y += 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
x -= 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
if (x !== 0) {
|
||||||
|
y -= 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x -= 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
while (y - 1.5 > 0) {
|
||||||
|
y -= 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x += 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
y -= 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x -= 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
if (y - 0.75 > 0) {
|
||||||
|
y -= 0.5;
|
||||||
|
points.push(x, y);
|
||||||
|
x += 0.5;
|
||||||
|
y -= 0.25;
|
||||||
|
points.push(x, y);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
offsets,
|
||||||
|
points,
|
||||||
|
// We use center of the rectangle (and not the centroid of the polygon) for the rectangle shapes
|
||||||
|
center: {
|
||||||
|
x: width / 2,
|
||||||
|
y: (0.75 * Math.floor(height) + 0.5 * (height % 1) + 0.25) / 2
|
||||||
|
},
|
||||||
|
anchor: even ? { x: 0.5, y: 0.5 } : { x: 0.0, y: 0.5 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,8 @@
|
||||||
"experiences": {
|
"experiences": {
|
||||||
"pe7OIoJsqlpMXEvs": {
|
"pe7OIoJsqlpMXEvs": {
|
||||||
"name": "Tremor Sense",
|
"name": "Tremor Sense",
|
||||||
"value": 2
|
"value": 2,
|
||||||
|
"description": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bonuses": {
|
"bonuses": {
|
||||||
|
|
@ -148,7 +149,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 75,
|
"page": 75,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"ownership": {
|
"ownership": {
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 91,
|
"page": 91,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "gargantuan"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "G7jiltRjgvVhZewm",
|
"_id": "G7jiltRjgvVhZewm",
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 84,
|
"page": 84,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "vNIbYQ4YSzNf0WPE",
|
"_id": "vNIbYQ4YSzNf0WPE",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 97,
|
"page": 97,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "WPEOIGfclNJxWb87",
|
"_id": "WPEOIGfclNJxWb87",
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,8 @@
|
||||||
"experiences": {
|
"experiences": {
|
||||||
"Gtr9I2G39GcXT2Si": {
|
"Gtr9I2G39GcXT2Si": {
|
||||||
"name": "Local Knowledge",
|
"name": "Local Knowledge",
|
||||||
"value": 3
|
"value": 3,
|
||||||
|
"description": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bonuses": {
|
"bonuses": {
|
||||||
|
|
@ -116,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 77,
|
"page": 77,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "JRhrrEg5UroURiAD",
|
"_id": "JRhrrEg5UroURiAD",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 84,
|
"page": 84,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "0ts6CGd93lLqGZI5",
|
"_id": "0ts6CGd93lLqGZI5",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 84,
|
"page": 84,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "h5RuhzGL17dW5FBT",
|
"_id": "h5RuhzGL17dW5FBT",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 85,
|
"page": 85,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "dgH3fW9FTYLaIDvS",
|
"_id": "dgH3fW9FTYLaIDvS",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 75,
|
"page": 75,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "71qKDLKO3CsrNkdy",
|
"_id": "71qKDLKO3CsrNkdy",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 77,
|
"page": 77,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "B4LZcGuBAHzyVdzy",
|
"_id": "B4LZcGuBAHzyVdzy",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 83,
|
"page": 83,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "2UeZ0tEe7AzgSJNd",
|
"_id": "2UeZ0tEe7AzgSJNd",
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 75,
|
"page": 75,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "8Zkqk1jU09nKL2fy",
|
"_id": "8Zkqk1jU09nKL2fy",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 85,
|
"page": 85,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "tiny"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "jDmHqGvzg5wjgmxE",
|
"_id": "jDmHqGvzg5wjgmxE",
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 85,
|
"page": 85,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "99TqczuQipBmaB8i",
|
"_id": "99TqczuQipBmaB8i",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 75,
|
"page": 75,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "uOP5oT9QzXPlnf3p",
|
"_id": "uOP5oT9QzXPlnf3p",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 85,
|
"page": 85,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "ZxWaWPdzFIUPNC62",
|
"_id": "ZxWaWPdzFIUPNC62",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 76,
|
"page": 76,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "CBBuEXAlLKFMJdjg",
|
"_id": "CBBuEXAlLKFMJdjg",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 85,
|
"page": 85,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "0NxCSugvKQ4W8OYZ",
|
"_id": "0NxCSugvKQ4W8OYZ",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 86,
|
"page": 86,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "tyBOpLfigAhI9bU3",
|
"_id": "tyBOpLfigAhI9bU3",
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 86,
|
"page": 86,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "zx99sOGTXicP4SSD",
|
"_id": "zx99sOGTXicP4SSD",
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 76,
|
"page": 76,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "9x2xY9zwc3xzbXo5",
|
"_id": "9x2xY9zwc3xzbXo5",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 91,
|
"page": 91,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "pnyjIGxxvurcWmTv",
|
"_id": "pnyjIGxxvurcWmTv",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 92,
|
"page": 92,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "kE4dfhqmIQpNd44e",
|
"_id": "kE4dfhqmIQpNd44e",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 92,
|
"page": 92,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "2VN3BftageoTTIzu",
|
"_id": "2VN3BftageoTTIzu",
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 92,
|
"page": 92,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "SxSOkM4bcVOFyjbo",
|
"_id": "SxSOkM4bcVOFyjbo",
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 92,
|
"page": 92,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "5lphJAgzoqZI3VoG",
|
"_id": "5lphJAgzoqZI3VoG",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 86,
|
"page": 86,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "NoRZ1PqB8N5wcIw0",
|
"_id": "NoRZ1PqB8N5wcIw0",
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 93,
|
"page": 93,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "tBWHW00epmMnkawe",
|
"_id": "tBWHW00epmMnkawe",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 76,
|
"page": 76,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "wNzeuQLfLUMvgHlQ",
|
"_id": "wNzeuQLfLUMvgHlQ",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 93,
|
"page": 93,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "wR7cFKrHvRzbzhBT",
|
"_id": "wR7cFKrHvRzbzhBT",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 86,
|
"page": 86,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "TLzY1nDw0Bu9Ud40",
|
"_id": "TLzY1nDw0Bu9Ud40",
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 93,
|
"page": 93,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "tiny"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "P7h54ZePFPHpYwvB",
|
"_id": "P7h54ZePFPHpYwvB",
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 86,
|
"page": 86,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"ownership": {
|
"ownership": {
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 86,
|
"page": 86,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "ChwwVqowFw8hJQwT",
|
"_id": "ChwwVqowFw8hJQwT",
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 97,
|
"page": 97,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "OsLG2BjaEdTZUJU9",
|
"_id": "OsLG2BjaEdTZUJU9",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 97,
|
"page": 97,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "PELRry1vqjBzSAlr",
|
"_id": "PELRry1vqjBzSAlr",
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 97,
|
"page": 97,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"prototypeToken": {
|
"prototypeToken": {
|
||||||
"name": "Fallen Warlord: Realm Breaker",
|
"name": "Fallen Warlord: Realm Breaker",
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 98,
|
"page": 98,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"prototypeToken": {
|
"prototypeToken": {
|
||||||
"name": "Fallen Warlord: Undefeated Champion",
|
"name": "Fallen Warlord: Undefeated Champion",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 87,
|
"page": 87,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "8VZIgU12cB3cvlyH",
|
"_id": "8VZIgU12cB3cvlyH",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 87,
|
"page": 87,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "YnObCleGjPT7yqEc",
|
"_id": "YnObCleGjPT7yqEc",
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 87,
|
"page": 87,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"ownership": {
|
"ownership": {
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 76,
|
"page": 76,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "IIWV4ysJPFPnTP7W",
|
"_id": "IIWV4ysJPFPnTP7W",
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 76,
|
"page": 76,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "small"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "4PfLnaCrOcMdb4dK",
|
"_id": "4PfLnaCrOcMdb4dK",
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 87,
|
"page": 87,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "5s8wSvpyC5rxY5aD",
|
"_id": "5s8wSvpyC5rxY5aD",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 76,
|
"page": 76,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "fmfntuJ8mHRCAktP",
|
"_id": "fmfntuJ8mHRCAktP",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 77,
|
"page": 77,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "8KWVLWXFhlY2kYx0",
|
"_id": "8KWVLWXFhlY2kYx0",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 88,
|
"page": 88,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "8mJYMpbLTb8qIOrr",
|
"_id": "8mJYMpbLTb8qIOrr",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 93,
|
"page": 93,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "gargantuan"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "dsfB3YhoL5SudvS2",
|
"_id": "dsfB3YhoL5SudvS2",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 93,
|
"page": 93,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "gargantuan"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "xIICT6tEdnA7dKDV",
|
"_id": "xIICT6tEdnA7dKDV",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 80,
|
"page": 80,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "SHXedd9zZPVfUgUa",
|
"_id": "SHXedd9zZPVfUgUa",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 98,
|
"page": 98,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "kabueAo6BALApWqp",
|
"_id": "kabueAo6BALApWqp",
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 98,
|
"page": 98,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "VENwg7xEFcYObjmT",
|
"_id": "VENwg7xEFcYObjmT",
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 77,
|
"page": 77,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "uRtghKE9mHlII4rs",
|
"_id": "uRtghKE9mHlII4rs",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 77,
|
"page": 77,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "mK3A5FTx6k8iPU3F",
|
"_id": "mK3A5FTx6k8iPU3F",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 95,
|
"page": 95,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "i2UNbRvgyoSs07M6",
|
"_id": "i2UNbRvgyoSs07M6",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 98,
|
"page": 98,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "r1mbfSSwKWdcFdAU",
|
"_id": "r1mbfSSwKWdcFdAU",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 94,
|
"page": 94,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "6hbqmxDXFOzZJDk4",
|
"_id": "6hbqmxDXFOzZJDk4",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 94,
|
"page": 94,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "huge"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "MI126iMOOobQ1Obn",
|
"_id": "MI126iMOOobQ1Obn",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 77,
|
"page": 77,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "5Lh1T0zaT8Pkr2U2",
|
"_id": "5Lh1T0zaT8Pkr2U2",
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 78,
|
"page": 78,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "MbBPIOxaxXYNApXz",
|
"_id": "MbBPIOxaxXYNApXz",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 78,
|
"page": 78,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "CBKixLH3yhivZZuL",
|
"_id": "CBKixLH3yhivZZuL",
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 78,
|
"page": 78,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "C0OMQqV7pN6t7ouR",
|
"_id": "C0OMQqV7pN6t7ouR",
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 78,
|
"page": 78,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "aTljstqteGoLpCBq",
|
"_id": "aTljstqteGoLpCBq",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 78,
|
"page": 78,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "XF4tYTq9nPJAy2ox",
|
"_id": "XF4tYTq9nPJAy2ox",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 78,
|
"page": 78,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "1zuyof1XuIfi3aMG",
|
"_id": "1zuyof1XuIfi3aMG",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 88,
|
"page": 88,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "MYXmTx2FHcIjdfYZ",
|
"_id": "MYXmTx2FHcIjdfYZ",
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 88,
|
"page": 88,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "7ai2opemrclQe3VF",
|
"_id": "7ai2opemrclQe3VF",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 99,
|
"page": 99,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "gargantuan"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "4nqv3ZwJGjnmic8j",
|
"_id": "4nqv3ZwJGjnmic8j",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 88,
|
"page": 88,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "niBpVU7yeo5ccskE",
|
"_id": "niBpVU7yeo5ccskE",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 84,
|
"page": 84,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "dNta0cUzr96xcFhf",
|
"_id": "dNta0cUzr96xcFhf",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 79,
|
"page": 79,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "Al3w2CgjfdT3p9ma",
|
"_id": "Al3w2CgjfdT3p9ma",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 88,
|
"page": 88,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "Vy02IhGhkJLuezu4",
|
"_id": "Vy02IhGhkJLuezu4",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 79,
|
"page": 79,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "sRn4bqerfARvhgSV",
|
"_id": "sRn4bqerfARvhgSV",
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 79,
|
"page": 79,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "3tqCjDwJAQ7JKqMb",
|
"_id": "3tqCjDwJAQ7JKqMb",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 79,
|
"page": 79,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "DscWkNVoHak6P4hh",
|
"_id": "DscWkNVoHak6P4hh",
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 80,
|
"page": 80,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "G62k4oSkhkoXEs2D",
|
"_id": "G62k4oSkhkoXEs2D",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 89,
|
"page": 89,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "rM9qCIYeWg9I0B4l",
|
"_id": "rM9qCIYeWg9I0B4l",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 94,
|
"page": 94,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "yx0vK2yfNVZKWUUi",
|
"_id": "yx0vK2yfNVZKWUUi",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 89,
|
"page": 89,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "medium"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "mVV7a7KQAORoPMgZ",
|
"_id": "mVV7a7KQAORoPMgZ",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@
|
||||||
"page": 95,
|
"page": 95,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
},
|
},
|
||||||
"motivesAndTactics": "Hide in plain sight, preserve the forest, root down, swing branches"
|
"motivesAndTactics": "Hide in plain sight, preserve the forest, root down, swing branches",
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "XK78QUfY8c8Go8Uv",
|
"_id": "XK78QUfY8c8Go8Uv",
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 99,
|
"page": 99,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"size": "large"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_id": "befIqd5IYKg6eUz2",
|
"_id": "befIqd5IYKg6eUz2",
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue