diff --git a/adventures-with-emmy.mjs b/adventures-with-emmy.mjs index fe1d8ac..05de539 100644 --- a/adventures-with-emmy.mjs +++ b/adventures-with-emmy.mjs @@ -42,7 +42,9 @@ Hooks.once("init", async function () { background: models.AwEBackground, kit: models.AwEKit, weapon: models.AwEWeapon, - equipment: models.AwEEquipment + equipment: models.AwEEquipment, + skill: models.AwESkill, + effect: models.AwEEffect } // Register actor sheets @@ -80,6 +82,12 @@ Hooks.once("init", async function () { foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwEEquipmentSheet, { types: ["equipment"], makeDefault: true }) + foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwESkillSheet, { + types: ["skill"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwEEffectSheet, { + types: ["effect"], makeDefault: true + }) CONFIG.ChatMessage.documentClass = documents.AwEChatMessage CONFIG.Dice.rolls.push(documents.AwERoll) diff --git a/lang/en.json b/lang/en.json index 44be259..7b0a6fc 100644 --- a/lang/en.json +++ b/lang/en.json @@ -7,6 +7,8 @@ "TYPES.Item.kit": "Kit", "TYPES.Item.weapon": "Weapon", "TYPES.Item.equipment": "Equipment", + "TYPES.Item.skill": "Skill", + "TYPES.Item.effect": "Effect", "TYPES.Actor.character": "Hero", "TYPES.Actor.creature": "Creature", "AWEMMY.Actor.Character": "Hero", @@ -31,7 +33,6 @@ "AWEMMY.Condition.Slowed": "Slowed", "AWEMMY.Condition.Vulnerable": "Vulnerable", "AWEMMY.Condition.Panel": "Conditions", - "AWEMMY.Roll.ConditionBonus": "Condition", "AWEMMY.Item.Description": "Description", "AWEMMY.Item.RollBonus": "Roll Bonus", "AWEMMY.Error.TraitPasteFailed": "Failed to update traits — please try again.", @@ -55,6 +56,10 @@ "AWEMMY.Item.Kit": "Kit", "AWEMMY.Item.Weapon": "Weapon", "AWEMMY.Item.Equipment": "Equipment", + "AWEMMY.Item.Skill": "Skill", + "AWEMMY.Skill.Label": "Skills", + "AWEMMY.Item.Effect": "Effect", + "AWEMMY.Effect.Label": "Active Effects", "AWEMMY.Roll.CriticalSuccess": "Critical Success", "AWEMMY.Roll.Success": "Success", "AWEMMY.Roll.Failure": "Failure", @@ -76,6 +81,7 @@ "AWEMMY.Sheet.Tab.Main": "Main", "AWEMMY.Sheet.Tab.Biography": "Biography", "AWEMMY.Sheet.Tab.Equipment": "Equipment", + "AWEMMY.Sheet.Tab.Inventory": "Inventory", "AWEMMY.Sheet.Tab.Description": "Description", "AWEMMY.Sheet.Tab.Eureka": "Eureka", "AWEMMY.Sheet.EditMode": "Edit", @@ -91,14 +97,12 @@ "AWEMMY.Character.Mod": "MOD", "AWEMMY.Character.DC": "DC", "AWEMMY.Character.Bonus": "+/−", - "AWEMMY.Character.Field": "Field", "AWEMMY.Character.Attribute": "Attribute", "AWEMMY.Character.Attributes": "Attributes", "AWEMMY.Character.Identity": "Identity", "AWEMMY.Character.Description": "Description", "AWEMMY.Character.Notes": "Notes", "AWEMMY.Character.Pronouns": "Pronouns", - "AWEMMY.Character.Specialization": "Specialization", "AWEMMY.Character.DropField": "Drag & drop a Field item here", "AWEMMY.Character.DropSpecialization": "Drag & drop a Specialization item here", "AWEMMY.Character.DropArchetype": "Drag & drop Archetype items here", @@ -106,13 +110,10 @@ "AWEMMY.Specialization.FieldName": "Parent Field", "AWEMMY.Specialization.KeyAttributeOverride": "Key Attribute Override", "AWEMMY.Specialization.Traits": "Traits", - "AWEMMY.Creature.EurekaRubric": "Eureka Rubric", "AWEMMY.Creature.Claims": "Claims", "AWEMMY.Creature.Evidence": "Evidence", "AWEMMY.Creature.Hints": "Hints", - "AWEMMY.Creature.Threshold1": "Threshold 1", - "AWEMMY.Creature.Threshold2": "Threshold 2", - "AWEMMY.Creature.Threshold3": "Threshold 3", + "AWEMMY.Creature.Thresholds": "Thresholds", "AWEMMY.Ability.CostLabel": "Cost", "AWEMMY.Ability.Frequency": "Frequency", "AWEMMY.Ability.Requirements": "Requirements", @@ -141,8 +142,6 @@ "AWEMMY.Weapon.CriticalHit": "Critical Hit!", "AWEMMY.Field.KeyAttribute": "Key Attribute", "AWEMMY.Field.KeyAttribute2": "Secondary Key Attribute", - "AWEMMY.Field.Specializations": "Specializations", - "AWEMMY.Field.AddSpecialization": "Add specialization...", "AWEMMY.Field.KnowledgeBonus": "Knowledge Bonus", "AWEMMY.Background.AttributeBoosts": "Attribute Boosts", "AWEMMY.Kit.Field": "Field", @@ -159,7 +158,6 @@ "AWEMMY.Rest.HPRestored": "Recovered {amount} HP (now {max}/{max})", "AWEMMY.Rest.AbilitiesReset": "{count} daily ability(ies) reset", "AWEMMY.Rest.AlreadyRested": "Already at full health — no recovery needed.", - "AWEMMY.TAH.Stats": "Stats", "AWEMMY.TAH.Combat": "Combat", "AWEMMY.TAH.Items": "Items", diff --git a/module/applications/_module.mjs b/module/applications/_module.mjs index 4511915..5295a19 100644 --- a/module/applications/_module.mjs +++ b/module/applications/_module.mjs @@ -8,3 +8,5 @@ export { default as AwEBackgroundSheet } from "./sheets/background-sheet.mjs" export { default as AwEKitSheet } from "./sheets/kit-sheet.mjs" export { default as AwEWeaponSheet } from "./sheets/weapon-sheet.mjs" export { default as AwEEquipmentSheet } from "./sheets/equipment-sheet.mjs" +export { default as AwESkillSheet } from "./sheets/skill-sheet.mjs" +export { default as AwEEffectSheet } from "./sheets/effect-sheet.mjs" diff --git a/module/applications/sheets/character-sheet.mjs b/module/applications/sheets/character-sheet.mjs index 03ceb20..2b55951 100644 --- a/module/applications/sheets/character-sheet.mjs +++ b/module/applications/sheets/character-sheet.mjs @@ -14,6 +14,8 @@ export default class AwECharacterSheet extends AwEActorSheet { }, actions: { createAbility: AwECharacterSheet.#onCreateAbility, + createSkill: AwECharacterSheet.#onCreateSkill, + createEffect: AwECharacterSheet.#onCreateEffect, createWeapon: AwECharacterSheet.#onCreateWeapon, createKit: AwECharacterSheet.#onCreateKit, createEquipment: AwECharacterSheet.#onCreateEquipment, @@ -94,6 +96,20 @@ export default class AwECharacterSheet extends AwEActorSheet { usedToday: item.system.usedToday })) context.hasUsedAbilities = context.abilities.some(a => a.usedToday) + context.skills = doc.itemTypes.skill.map(item => ({ + id: item.id, + uuid: item.uuid, + name: item.name, + img: item.img, + system: item.system + })) + context.effects = doc.itemTypes.effect.map(item => ({ + id: item.id, + uuid: item.uuid, + name: item.name, + img: item.img, + system: item.system + })) context.conditions = Object.values(SYSTEM.CONDITIONS).map(c => ({ ...c, label: game.i18n.localize(c.label), @@ -188,6 +204,26 @@ export default class AwECharacterSheet extends AwEActorSheet { this.document.createEmbeddedDocuments("Item", [{ name: CONFIG.Item.documentClass.defaultName({ type }), type }]) } + /** + * Create a new skill item. + * @param {Event} event - The triggering event. + * @param {HTMLElement} target - The target element. + */ + static #onCreateSkill(event, target) { + const type = "skill" + this.document.createEmbeddedDocuments("Item", [{ name: CONFIG.Item.documentClass.defaultName({ type }), type }]) + } + + /** + * Create a new effect item. + * @param {Event} event - The triggering event. + * @param {HTMLElement} target - The target element. + */ + static #onCreateEffect(event, target) { + const type = "effect" + this.document.createEmbeddedDocuments("Item", [{ name: CONFIG.Item.documentClass.defaultName({ type }), type }]) + } + /** * Create a new weapon item. * @param {Event} event - The triggering event. diff --git a/module/applications/sheets/creature-sheet.mjs b/module/applications/sheets/creature-sheet.mjs index 9b2134d..48554bd 100644 --- a/module/applications/sheets/creature-sheet.mjs +++ b/module/applications/sheets/creature-sheet.mjs @@ -1,4 +1,5 @@ import AwEActorSheet from "./base-actor-sheet.mjs" +import { SYSTEM } from "../../config/system.mjs" export default class AwECreatureSheet extends AwEActorSheet { /** @override */ @@ -10,6 +11,16 @@ export default class AwECreatureSheet extends AwEActorSheet { }, window: { contentClasses: ["creature-content"] + }, + actions: { + createAbility: AwECreatureSheet.#onCreateAbility, + createSkill: AwECreatureSheet.#onCreateSkill, + createEffect: AwECreatureSheet.#onCreateEffect, + createWeapon: AwECreatureSheet.#onCreateWeapon, + createKit: AwECreatureSheet.#onCreateKit, + createEquipment: AwECreatureSheet.#onCreateEquipment, + rollWeapon: AwECreatureSheet.#onRollWeapon, + rollDamage: AwECreatureSheet.#onRollDamage } } @@ -24,6 +35,9 @@ export default class AwECreatureSheet extends AwEActorSheet { main: { template: "systems/fvtt-adventures-with-emmy/templates/creature-main.hbs" }, + inventory: { + template: "systems/fvtt-adventures-with-emmy/templates/creature-inventory.hbs" + }, description: { template: "systems/fvtt-adventures-with-emmy/templates/creature-description.hbs" }, @@ -39,9 +53,10 @@ export default class AwECreatureSheet extends AwEActorSheet { #getTabs() { const tabs = { - main: { id: "main", group: "sheet", icon: "fa-solid fa-dragon", label: "AWEMMY.Sheet.Tab.Main" }, - description: { id: "description", group: "sheet", icon: "fa-solid fa-scroll", label: "AWEMMY.Sheet.Tab.Description" }, - eureka: { id: "eureka", group: "sheet", icon: "fa-solid fa-lightbulb", label: "AWEMMY.Sheet.Tab.Eureka" } + main: { id: "main", group: "sheet", icon: "fa-solid fa-dragon", label: "AWEMMY.Sheet.Tab.Main" }, + inventory: { id: "inventory", group: "sheet", icon: "fa-solid fa-backpack", label: "AWEMMY.Sheet.Tab.Inventory" }, + description: { id: "description", group: "sheet", icon: "fa-solid fa-scroll", label: "AWEMMY.Sheet.Tab.Description" }, + eureka: { id: "eureka", group: "sheet", icon: "fa-solid fa-lightbulb", label: "AWEMMY.Sheet.Tab.Eureka" } } for (const v of Object.values(tabs)) { v.active = this.tabGroups[v.group] === v.id @@ -65,6 +80,34 @@ export default class AwECreatureSheet extends AwEActorSheet { switch (partId) { case "main": context.tab = context.tabs.main + context.abilities = this.document.itemTypes.ability.map(item => ({ + id: item.id, + uuid: item.uuid, + name: item.name, + img: item.img, + system: item.system, + costLabel: game.i18n.localize(SYSTEM.ABILITY_COST[item.system.cost]?.label ?? item.system.cost) + })) + context.skills = this.document.itemTypes.skill.map(item => ({ + id: item.id, + uuid: item.uuid, + name: item.name, + img: item.img, + system: item.system + })) + context.effects = this.document.itemTypes.effect.map(item => ({ + id: item.id, + uuid: item.uuid, + name: item.name, + img: item.img, + system: item.system + })) + break + case "inventory": + context.tab = context.tabs.inventory + context.kits = this.document.itemTypes.kit + context.weapons = this.document.itemTypes.weapon + context.equipments = this.document.itemTypes.equipment break case "description": context.tab = context.tabs.description @@ -75,4 +118,64 @@ export default class AwECreatureSheet extends AwEActorSheet { } return context } + + /** @override */ + async _onDrop(event) { + if (!this.isEditable) return + const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event) + if (data.type === "Item") { + const item = await fromUuid(data.uuid) + return this._onDropItem(item) + } + } + + /** @override */ + async _onDropItem(item) { + if (!item) return + return super._onDropItem(item) + } + + static #onCreateAbility(event, target) { + const type = "ability" + this.document.createEmbeddedDocuments("Item", [{ name: CONFIG.Item.documentClass.defaultName({ type }), type }]) + } + + static #onCreateSkill(event, target) { + const type = "skill" + this.document.createEmbeddedDocuments("Item", [{ name: CONFIG.Item.documentClass.defaultName({ type }), type }]) + } + + static #onCreateEffect(event, target) { + const type = "effect" + this.document.createEmbeddedDocuments("Item", [{ name: CONFIG.Item.documentClass.defaultName({ type }), type }]) + } + + static #onCreateWeapon(event, target) { + const type = "weapon" + this.document.createEmbeddedDocuments("Item", [{ name: CONFIG.Item.documentClass.defaultName({ type }), type }]) + } + + static #onCreateKit(event, target) { + const type = "kit" + this.document.createEmbeddedDocuments("Item", [{ name: CONFIG.Item.documentClass.defaultName({ type }), type }]) + } + + static #onCreateEquipment(event, target) { + const type = "equipment" + this.document.createEmbeddedDocuments("Item", [{ name: CONFIG.Item.documentClass.defaultName({ type }), type }]) + } + + static async #onRollWeapon(event, target) { + const itemId = target.closest("[data-item-id]")?.dataset.itemId + const item = this.document.items.get(itemId) + if (!item) return + await this.document.rollWeapon(item) + } + + static async #onRollDamage(event, target) { + const itemId = target.closest("[data-item-id]")?.dataset.itemId + const item = this.document.items.get(itemId) + if (!item) return + await this.document.rollDamage(item) + } } diff --git a/module/applications/sheets/effect-sheet.mjs b/module/applications/sheets/effect-sheet.mjs new file mode 100644 index 0000000..9b70159 --- /dev/null +++ b/module/applications/sheets/effect-sheet.mjs @@ -0,0 +1,17 @@ +import AwEItemSheet from "./base-item-sheet.mjs" + +export default class AwEEffectSheet extends AwEItemSheet { + /** @override */ + static DEFAULT_OPTIONS = { + classes: ["effect"], + position: { width: 500 }, + window: { contentClasses: ["effect-content"] } + } + + /** @override */ + static PARTS = { + main: { + template: "systems/fvtt-adventures-with-emmy/templates/effect.hbs" + } + } +} diff --git a/module/applications/sheets/skill-sheet.mjs b/module/applications/sheets/skill-sheet.mjs new file mode 100644 index 0000000..5051b1f --- /dev/null +++ b/module/applications/sheets/skill-sheet.mjs @@ -0,0 +1,17 @@ +import AwEItemSheet from "./base-item-sheet.mjs" + +export default class AwESkillSheet extends AwEItemSheet { + /** @override */ + static DEFAULT_OPTIONS = { + classes: ["skill"], + position: { width: 500 }, + window: { contentClasses: ["skill-content"] } + } + + /** @override */ + static PARTS = { + main: { + template: "systems/fvtt-adventures-with-emmy/templates/skill.hbs" + } + } +} diff --git a/module/models/_module.mjs b/module/models/_module.mjs index 1f14d69..cdd1767 100644 --- a/module/models/_module.mjs +++ b/module/models/_module.mjs @@ -8,3 +8,5 @@ export { default as AwEBackground } from "./background.mjs" export { default as AwEKit } from "./kit.mjs" export { default as AwEWeapon } from "./weapon.mjs" export { default as AwEEquipment } from "./equipment.mjs" +export { default as AwESkill } from "./skill.mjs" +export { default as AwEEffect } from "./effect.mjs" diff --git a/module/models/creature.mjs b/module/models/creature.mjs index b686418..acc0671 100644 --- a/module/models/creature.mjs +++ b/module/models/creature.mjs @@ -37,11 +37,16 @@ export default class AwECreature extends foundry.abstract.TypeDataModel { // Eureka Rubric schema.eurekaClaims = new fields.StringField({ initial: "", required: false, nullable: true }) schema.eurekaEvidence = new fields.StringField({ initial: "", required: false, nullable: true }) - schema.eurekaThreshold1 = new fields.StringField({ initial: "", required: false, nullable: true }) - schema.eurekaThreshold2 = new fields.StringField({ initial: "", required: false, nullable: true }) - schema.eurekaThreshold3 = new fields.StringField({ initial: "", required: false, nullable: true }) + schema.eurekaThresholds = new fields.StringField({ initial: "", required: false, nullable: true }) schema.eurekaHints = new fields.StringField({ initial: "", required: false, nullable: true }) return schema } + + prepareDerivedData() { + super.prepareDerivedData() + for (const attr of Object.values(this.attributes)) { + attr.dc = 10 + attr.mod + } + } } diff --git a/module/models/effect.mjs b/module/models/effect.mjs new file mode 100644 index 0000000..90ce517 --- /dev/null +++ b/module/models/effect.mjs @@ -0,0 +1,10 @@ +export default class AwEEffect extends foundry.abstract.TypeDataModel { + static defineSchema() { + const fields = foundry.data.fields + const schema = {} + + schema.description = new fields.HTMLField({ required: true, textSearch: true }) + + return schema + } +} diff --git a/module/models/skill.mjs b/module/models/skill.mjs new file mode 100644 index 0000000..b0b2913 --- /dev/null +++ b/module/models/skill.mjs @@ -0,0 +1,10 @@ +export default class AwESkill extends foundry.abstract.TypeDataModel { + static defineSchema() { + const fields = foundry.data.fields + const schema = {} + + schema.description = new fields.HTMLField({ required: true, textSearch: true }) + + return schema + } +} diff --git a/system.json b/system.json index 4b04cc5..a88d50a 100644 --- a/system.json +++ b/system.json @@ -25,7 +25,9 @@ "background": { "htmlFields": ["description"] }, "kit": { "htmlFields": ["description"] }, "weapon": { "htmlFields": ["description"] }, - "equipment": { "htmlFields": ["description"] } + "equipment": { "htmlFields": ["description"] }, + "skill": { "htmlFields": ["description"] }, + "effect": { "htmlFields": ["description"] } } }, "grid": { "distance": 1, "units": "m" }, diff --git a/templates/character-main.hbs b/templates/character-main.hbs index 642f5c8..c4b14ef 100644 --- a/templates/character-main.hbs +++ b/templates/character-main.hbs @@ -84,6 +84,52 @@ + {{!-- Skills --}} +
+ + {{!-- Active Effects --}} + + {{!-- Conditions --}}