Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Add Item data models #263

Merged
merged 8 commits into from
Dec 10, 2022
11 changes: 6 additions & 5 deletions src/e2e/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
export type QuenchMethods = {
[s: string]: any
}

import characterTests, {
key as characterKey,
options as characterOptions
Expand Down Expand Up @@ -37,6 +33,10 @@ import dataModelCharacterTests, {
options as dataModelCharacterOptions
} from '../module/actor/data-model-character.test.js';

export type QuenchMethods = {
[s: string]: any
}

type Quench = {
registerBatch: (
key: string,
Expand All @@ -54,4 +54,5 @@ Hooks.on('quenchReady', async (quench: Quench) => {
quench.registerBatch(dataModelCharacterEncumbranceKey, dataModelCharacterEncumbranceTests, dataModelCharacterEncumbranceOptions);
quench.registerBatch(dataModelCharacterMoveKey, dataModelCharacterMoveTests, dataModelCharacterMoveOptions);
quench.registerBatch(dataModelCharacterKey, dataModelCharacterTests, dataModelCharacterOptions);
});
});

50 changes: 21 additions & 29 deletions src/module/actor/actor-sheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,24 +266,25 @@ export class OseActorSheet extends ActorSheet {
})
);
}
async _onDropItem(event, data) {
async _onDropItem(event, data){
const item = await Item.implementation.fromDropData(data);
const itemContainer = this.object.items.get(item?.system?.containerId);
const isContainer = this.actor.items.get(item.system.containerId);

if (isContainer)
return this._onContainerItemRemove(item, isContainer);

const itemData = item.toObject();
const targetEl = event.target.closest(".item");
const targetItem = this.object.items.get(targetEl?.dataset?.itemId);
let targetIsContainer = targetItem?.type == "container" ? true : false;
const exists = this.object.items.get(item.id) ? true : false;
const {itemId: targetId} = event.target.closest('.item').dataset;
const targetItem = this.actor.items.get(targetId)
const targetIsContainer = targetItem?.type === 'container'

if (targetIsContainer) {
if (targetIsContainer)
return this._onContainerItemAdd(item, targetItem);
} else if (itemContainer) {
this._onContainerItemRemove(item, itemContainer);
} else {
if (!exists) {
return this._onDropItemCreate([itemData]);
}
}

const exists = !!this.actor.items.get(item.id);

if (!exists)
return this._onDropItemCreate([itemData]);
}
async _onContainerItemRemove(item, container) {
const newList = container.system.itemIds.filter((s) => s != item.id);
Expand Down Expand Up @@ -371,17 +372,10 @@ export class OseActorSheet extends ActorSheet {
event.preventDefault();
const header = event.currentTarget;
const type = header.dataset.type;

// item creation helper func
const createItem = function (type, name) {
const itemData = {
name: name ? name : `New ${type.capitalize()}`,
type: type,
data: header.dataset,
};
delete itemData.data["type"];
return itemData;
};
const createItem = (type, name) => ({
name: name ? name : `New ${type.capitalize()}`,
type: type,
});

// Getting back to main logic
if (type === "choice") {
Expand All @@ -390,10 +384,8 @@ export class OseActorSheet extends ActorSheet {
const itemData = createItem(dialogInput.type, dialogInput.name);
this.actor.createEmbeddedDocuments("Item", [itemData], {});
});
} else {
const itemData = createItem(type);
return this.actor.createEmbeddedDocuments("Item", [itemData], {});
}
} else
return this.actor.createEmbeddedDocuments("Item", [createItem(type)], {});
}

async _updateItemQuantity(event) {
Expand Down
29 changes: 2 additions & 27 deletions src/module/actor/data-model-character.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,38 +121,13 @@ export default class OseDataModelCharacter extends foundry.abstract.DataModel {
}

get containers() {
const containerContent = this.parent.items
.filter(({ system: { containerId } }) => containerId)
.reduce((obj, item) => {
const { containerId } = item.system;

return {
...obj,
[containerId]: obj[containerId] ? [...obj[containerId], item] : [item]
}
}, {});

const containers = getItemsOfActorOfType(
return getItemsOfActorOfType(
this.parent,
'container',
({ system: { containerId } }) => !containerId
);

const reducedWeight = (acc, { system: { weight, quantity } }) => (
acc + weight * (quantity?.value || 1)
);

const mapItemsToContainer = (container, key) => ({
...container,
system: {
...container.system,
itemIds: containerContent[container.id] || [],
totalWeight: containerContent[container.id]?.reduce(reducedWeight, 0)
}
});

return containers.map(mapItemsToContainer);
}

get treasures() {
return getItemsOfActorOfType(
this.parent,
Expand Down
32 changes: 31 additions & 1 deletion src/module/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type OseConfig = {
armor: Record<Armor, string>;
colors: Record<Color, string>;
languages: string[];
auto_tags: {[n: string]: {label: string, icon: string}};
tags: Record<InventoryItemTag, string>;
tag_images: Record<InventoryItemTag, string>;
monster_saves: Record<
Expand Down Expand Up @@ -177,12 +178,41 @@ export const OSE: OseConfig = {
reload: "OSE.items.Reload",
charge: "OSE.items.Charge",
},
auto_tags: {
get melee() {
return ({ label: CONFIG.OSE.tags.melee, image: `${CONFIG.OSE.assetsPath}/melee.png`,icon: 'fa-sword', })
},
get missile() {
return ({ label: CONFIG.OSE.tags.missile, image: `${CONFIG.OSE.assetsPath}/missile.png`,icon: 'fa-bow-arrow' })
},
get slow() {
return ({ label: CONFIG.OSE.tags.slow, image: `${CONFIG.OSE.assetsPath}/slow.png`,icon: 'fa-weight-hanging' })
},
get twohanded() {
return ({ label: CONFIG.OSE.tags.twohanded, image: `${CONFIG.OSE.assetsPath}/twohanded.png`,icon: 'fa-hands-holding' })
},
get blunt() {
return ({ label: CONFIG.OSE.tags.blunt, image: `${CONFIG.OSE.assetsPath}/blunt.png`,icon: 'fa-hammer-crash' })
},
get brace() {
return ({ label: CONFIG.OSE.tags.brace, image: `${CONFIG.OSE.assetsPath}/brace.png`,icon: 'fa-block-brick' })
},
get splash() {
return ({ label: CONFIG.OSE.tags.splash, image: `${CONFIG.OSE.assetsPath}/splash.png`,icon: 'fa-burst' })
},
get reload() {
return ({ label: CONFIG.OSE.tags.reload, image: `${CONFIG.OSE.assetsPath}/reload.png`,icon: 'fa-gear' })
},
get charge() {
return ({ label: CONFIG.OSE.tags.charge, image: `${CONFIG.OSE.assetsPath}/charge.png`,icon: 'fa-person-running' })
}
},
tag_images: {
get melee() {
return `${OSE.assetsPath}/melee.png`;
},
get missile() {
return `${OSE.assetsPath}/missile.png`;
return `fa-bow-arrow`;
},
get slow() {
return `${OSE.assetsPath}/slow.png`;
Expand Down
49 changes: 49 additions & 0 deletions src/module/item/data-model-ability.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
export default class OseDataModelAbility extends foundry.abstract.DataModel {
static defineSchema() {
const { SchemaField, StringField, NumberField, BooleanField, ArrayField, ObjectField } = foundry.data.fields;
return {
save: new StringField(),
pattern: new StringField(),
requirements: new StringField(),
roll: new StringField(),
rollType: new StringField(),
rollTarget: new NumberField(),
blindroll: new BooleanField(),
description: new StringField(),
tags: new ArrayField(new ObjectField()),
};
}

get #rollTag() {
if (!this.roll) return null;

const rollTarget = this.rollTarget === undefined
? ''
: ` ${CONFIG.OSE.roll_type[this.rollType]}${this.rollTarget}`

return {
label: `${game.i18n.localize('OSE.items.Roll')} ${this.roll}${rollTarget}`
}
}

get #saveTag() {
if (!this.save) return null;

return {
label: CONFIG.OSE.saves_long[this.save],
icon: 'fa-skull'
}
}

get manualTags() {
return this.tags || [];
}

get autoTags() {
return [
...this.requirements.split(',').map(req => ({ label: req.trim() })),
this.#rollTag,
this.#saveTag,
].filter(t => !!t);
}
}
65 changes: 65 additions & 0 deletions src/module/item/data-model-armor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
export default class OseDataModelArmor extends foundry.abstract.DataModel {
static ArmorTypes = {
unarmored: 'OSE.armor.unarmored',
light: 'OSE.armor.light',
heavy: 'OSE.armor.heavy',
shield: 'OSE.armor.shield'
}

static defineSchema() {
const { SchemaField, StringField, NumberField, BooleanField, ArrayField, ObjectField } = foundry.data.fields;
return {
type: new StringField({
initial: 'light',
choices: Object.keys(OseDataModelArmor.ArmorTypes)
}),
ac: new SchemaField({
value: new NumberField({
initial: 9
})
}),
aac: new SchemaField({
value: new NumberField({
initial: 10
})
}),
description: new StringField(),
tags: new ArrayField(new ObjectField()),
equipped: new BooleanField(),
cost: new NumberField({ min: 0, initial: 0 }),
containerId: new StringField(),
quantity: new SchemaField({
value: new NumberField({ min: 0, initial: 0 }),
max: new NumberField({ min: 0, initial: 0 }),
}),
weight: new NumberField({ min: 0, initial: 0 })
};
}

get manualTags() {
if (!this.tags) return null;

const tagNames = Object.values(CONFIG.OSE.auto_tags).map(({ label }) => label);
return this.tags.filter(({ value }) =>
!tagNames.includes(value)
).map(({ title, value }) => ({
title,
value,
label: value
}))
}

get autoTags() {
const tagNames = Object.values(CONFIG.OSE.auto_tags)

const autoTags = this.tags.map(({ value }) =>
tagNames.find(({ label }) => value === label)
)

return [
{ label: OseDataModelArmor.ArmorTypes[this.type], icon: 'fa-tshirt' },
...autoTags,
...this.manualTags
].flat().filter(t => !!t)
}
}
54 changes: 54 additions & 0 deletions src/module/item/data-model-container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
export default class OseDataModelContainer extends foundry.abstract.DataModel {
static defineSchema() {
const { SchemaField, StringField, NumberField, BooleanField, ArrayField, ObjectField } = foundry.data.fields;
return {
itemIds: new ArrayField(new StringField),
description: new StringField(),
tags: new ArrayField(new ObjectField()),
cost: new NumberField({ min: 0, initial: 0 }),
containerId: new StringField(),
quantity: new SchemaField({
value: new NumberField({ min: 0, initial: 0 }),
max: new NumberField({ min: 0, initial: 0 }),
}),
weight: new NumberField({ min: 0, initial: 0 })
};
}

get contents() {
if (!this.itemIds) return null;
if (!this?.parent?.parent?.items) return null;
const { id } = this.parent;
return this.parent.parent.items.filter(
({system: {containerId}}) => id === containerId
);
}

get totalWeight() {
return this.contents.reduce((acc, { system: { weight, quantity }}) => (
acc + weight * (quantity?.value || 1)
), 0)
}

get manualTags() {
if (!this.tags) return null;

const tagNames = Object.values(CONFIG.OSE.auto_tags).map(({ label }) => label);
return this.tags.filter(({ value }) =>
!tagNames.includes(value)
).map(({ title, value }) => ({ title, value, label: value }))
}

get autoTags() {
const tagNames = Object.values(CONFIG.OSE.auto_tags)

const autoTags = this.tags.map(({ value }) =>
tagNames.find(({ label }) => value === label)
)

return [
...autoTags,
...this.manualTags
].flat().filter(t => !!t)
}
}
Loading