Various items fixes and enhancements

This commit is contained in:
2024-12-09 16:02:40 +01:00
parent 9775ec9fa1
commit 1de2927d93
51 changed files with 2102 additions and 574 deletions

View File

@ -9,4 +9,6 @@ export { default as LethalFantasyArmorSheet } from "./sheets/armor-sheet.mjs"
export { default as LethalFantasySpellSheet } from "./sheets/spell-sheet.mjs"
export { default as LethalFantasyEquipmentSheet } from "./sheets/equipment-sheet.mjs"
export { default as LethalFantasyShieldSheet } from "./sheets/shield-sheet.mjs"
export { default as LethalFantasyMiracleSheet } from "./sheets/miracle-sheet.mjs"
export { default as LethalFantasyManager } from "./manager.mjs"

View File

@ -23,10 +23,16 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
template: "systems/fvtt-lethal-fantasy/templates/character-main.hbs",
},
tabs: {
template: "systems/fvtt-lethal-fantasy/templates/generic/tab-navigation.hbs",
template: "templates/generic/tab-navigation.hbs",
},
items: {
template: "systems/fvtt-lethal-fantasy/templates/character-items.hbs",
skills: {
template: "systems/fvtt-lethal-fantasy/templates/character-skills.hbs",
},
weapons: {
template: "systems/fvtt-lethal-fantasy/templates/character-weapons.hbs",
},
spells: {
template: "systems/fvtt-lethal-fantasy/templates/character-spells.hbs",
},
biography: {
template: "systems/fvtt-lethal-fantasy/templates/character-biography.hbs",
@ -35,7 +41,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
/** @override */
tabGroups = {
sheet: "items",
sheet: "skills",
}
/**
@ -44,8 +50,10 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
*/
#getTabs() {
const tabs = {
items: { id: "items", group: "sheet", icon: "fa-solid fa-shapes", label: "LETHALFANTASY.Character.Label.details" },
biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "LETHALFANTASY.Character.Label.biography" },
skills: { id: "skills", group: "sheet", icon: "fa-solid fa-shapes", label: "LETHALFANTASY.Label.skills" },
weapons: { id: "weapons", group: "sheet", icon: "fa-solid fa-swords", label: "LETHALFANTASY.Label.weapons" },
spells: { id: "spells", group: "sheet", icon: "fa-sharp-duotone fa-solid fa-wand-magic-sparkles", label: "LETHALFANTASY.Label.spells" },
biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "LETHALFANTASY.Label.biography" },
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
@ -78,15 +86,24 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
const doc = this.document
switch (partId) {
case "main":
context.enrichedBiens = await TextEditor.enrichHTML(doc.system.biens, { async: true })
break
case "items":
context.tab = context.tabs.items
context.weapons = doc.itemTypes.weapon
context.armors = doc.itemTypes.armor
case "skills":
context.tab = context.tabs.skills
context.skills = doc.itemTypes.skill
context.gifts = doc.itemTypes.gifts
context.vulnerabilities = doc.itemTypes.vulnerability
break
case "spells":
context.tab = context.tabs.spells
context.spells = doc.itemTypes.spell
context.hasSpells = context.spells.length > 0
break
case "weapons":
context.tab = context.tabs.weapons
context.weapons = doc.itemTypes.weapon
context.armors = doc.itemTypes.armor
context.equipments = doc.itemTypes.equipment
break
case "biography":
context.tab = context.tabs.biography
context.enrichedDescription = await TextEditor.enrichHTML(doc.system.description, { async: true })
@ -111,11 +128,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
switch (data.type) {
case "Item":
const item = await fromUuid(data.uuid)
if (!["path", "weapon", "armor", "spell"].includes(item.type)) return
if (item.type === "path") return this.#onDropPathItem(item)
if (item.type === "weapon") return super._onDropItem(item)
if (item.type === "armor") return this._onDropItem(item)
if (item.type === "spell") return this._onDropItem(item)
return this._onDropItem(item)
}
}

View File

@ -0,0 +1,21 @@
import LethalFantasyItemSheet from "./base-item-sheet.mjs"
export default class LethalFantasyMiracleSheet extends LethalFantasyItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["miracle"],
position: {
width: 450,
},
window: {
contentClasses: ["miracle-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-lethal-fantasy/templates/miracle.hbs",
},
}
}

View File

@ -1,7 +1,7 @@
export const CHARACTERISTICS = Object.freeze({
str: {
id: "str",
label: "LETHALFANTASY.Character.str.label"
label: "LETHALFANTASY.Label.str"
},
int: {
id: "int",
@ -23,6 +23,10 @@ export const CHARACTERISTICS = Object.freeze({
id: "cha",
label: "LETHALFANTASY.Character.cha.label"
},
luc: {
id: "luc",
label: "LETHALFANTASY.Character.luc.label"
},
app: {
id: "app",
label: "LETHALFANTASY.Character.app.label"

File diff suppressed because it is too large Load Diff

View File

@ -4,20 +4,11 @@ import * as ARMOR from "./armor.mjs"
import * as SPELL from "./spell.mjs"
import * as SKILL from "./skill.mjs"
import * as EQUIPMENT from "./equipment.mjs"
import * as CHARACTERISTICS from "./characteristic-tables.mjs"
export const SYSTEM_ID = "fvtt-lethal-fantasy"
export const DEV_MODE = false
export const DICE_VALUE = Object.freeze({
D4: "d4",
D6: "d6",
D8: "d8",
D10: "d10",
D12: "d12",
})
export const DICE_VALUES = ["0", "d4", "d6", "d8", "d10", "d12"]
export const ROLL_TYPE = Object.freeze({
SAVE: "save",
RESOURCE: "resource",
@ -77,15 +68,15 @@ export const ASCII = `
export const SYSTEM = {
id: SYSTEM_ID,
CHARACTERISTICS: CHARACTER.CHARACTERISTICS,
RESOURCES: CHARACTER.RESOURCES,
CHARACTERISTICS_TABLES: CHARACTERISTICS.TABLES,
CHARACTERISTICS_MAJOR: CHARACTERISTICS.MAJOR,
SAVES: CHARACTER.SAVES,
SKILL_CATEGORY: SKILL.CATEGORY,
WEAPON_CATEGORY: WEAPON.CATEGORY,
WEAPON_DAMAGE: WEAPON.DAMAGE,
ARMOR_TYPE: ARMOR.TYPE,
EQUIPMENT_CATEGORY: EQUIPMENT.CATEGORY,
SPELL_RANGE: SPELL.RANGE,
WEAPON_TYPE: WEAPON.WEAPON_TYPE,
WEAPON_CLASS: WEAPON.WEAPON_CLASS,
MONEY,
ASCII,
ROLL_TYPE,

View File

@ -1,35 +1,20 @@
export const CATEGORY = Object.freeze({
mains: {
id: "mains",
label: "LETHALFANTASY.Weapon.Category.main",
},
improvisee: {
id: "improvisee",
label: "LETHALFANTASY.Weapon.Category.improvisee",
},
courte: {
id: "courte",
label: "LETHALFANTASY.Weapon.Category.courte",
},
longue: {
id: "longue",
label: "LETHALFANTASY.Weapon.Category.longue",
},
lourde: {
id: "lourde",
label: "LETHALFANTASY.Weapon.Category.lourde",
},
})
export const WEAPON_TYPE = {
"melee": "LETHALFANTASY.Weapon.WeaponType.melee",
"ranged": "LETHALFANTASY.Weapon.WeaponType.ranged"
}
export const DAMAGE = Object.freeze({
UN: "1",
D4: "d4",
D6: "d6",
D8: "d8",
D10: "d10",
})
export const WEAPON_CLASS = {
"longblade": "LETHALFANTASY.Weapon.WeaponClass.longblade",
"shortblade": "LETHALFANTASY.Weapon.WeaponClass.shortblade",
"mediumblade": "LETHALFANTASY.Weapon.WeaponClass.mediumblade",
"axe": "LETHALFANTASY.Weapon.WeaponClass.axe",
"hammer": "LETHALFANTASY.Weapon.WeaponClass.hammer",
"mace": "LETHALFANTASY.Weapon.WeaponClass.mace",
"flail": "LETHALFANTASY.Weapon.WeaponClass.flail",
"bow": "LETHALFANTASY.Weapon.WeaponClass.bow",
"sling": "LETHALFANTASY.Weapon.WeaponClass.sling",
"thrown": "LETHALFANTASY.Weapon.WeaponClass.thrown",
"polearm": "LETHALFANTASY.Weapon.WeaponClass.polearm",
"unarmed" : "LETHALFANTASY.Weapon.WeaponClass.unarmed"
}

View File

@ -1 +1,19 @@
export default class LethalFantasyItem extends Item {}
export const defaultItemImg = {
weapon: "systems/fvtt-lethal-fantasy/assets/icons/icon_weapon.webp",
armor: "systems/fvtt-lethal-fantasy/assets/icons/icon_armor.webp",
equipment: "systems/fvtt-lethal-fantasy/assets/icons/icon_equipment.webp",
skill: "systems/fvtt-lethal-fantasy/assets/icons/icon_skill.webp",
gift: "systems/fvtt-lethal-fantasy/assets/icons/icon_gift.webp",
invulnerability: "systems/fvtt-lethal-fantasy/assets/icons/icon_invulnerability.webp",
shield: "systems/fvtt-lethal-fantasy/assets/icons/icon_shield.webp",
spell: "systems/fvtt-lethal-fantasy/assets/icons/icon_spell.webp"
}
export default class LethalFantasyItem extends Item {
constructor(data, context) {
if (!data.img) {
data.img = defaultItemImg[data.type];
}
super(data, context);
}
}

View File

@ -9,3 +9,4 @@ export { default as LethalFantasyGift } from "./gift.mjs"
export { default as LethalFantasyVulnerability } from "./vulnerability.mjs"
export { default as LethalFantasySave } from "./save.mjs"
export { default as LethalFantasyEquipment } from "./equipment.mjs"
export { default as LethalFantasyMiracle } from "./miracle.mjs"

View File

@ -52,6 +52,7 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
bonus: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.grit = new fields.SchemaField({
starting: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
earned: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
@ -87,6 +88,17 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
/** @override */
static LOCALIZATION_PREFIXES = ["LETHALFANTASY.Character"]
prepareDerivedData() {
super.prepareDerivedData();
let grit = 0
for (let c in this.characteristics) {
if (SYSTEM.CHARACTERISTICS_MAJOR[c.id]) {
grit += this.characteristics[c].value
}
}
this.grit.starting = Math.round(grit / 6)
}
/**
* Rolls a dice for a character.
* @param {("save"|"resource|damage")} rollType The type of the roll.

View File

@ -1,5 +1,5 @@
import { SYSTEM } from "../config/system.mjs"
import { CATEGORY } from "../config/weapon.mjs"
export default class LethalFantasyEquipment extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields

38
module/models/miracle.mjs Normal file
View File

@ -0,0 +1,38 @@
import { SYSTEM } from "../config/system.mjs"
export default class LethalFantasyMiracle extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
const schema = {}
schema.description = new fields.HTMLField({
required: false,
blank: true,
initial: "",
textSearch: true,
})
schema.level = new fields.NumberField({
...requiredInteger,
initial: 1,
min: 1,
max: 20,
})
schema.components = new fields.SchemaField({
verbal: new fields.BooleanField(),
somatic: new fields.BooleanField(),
material: new fields.BooleanField(),
catalyst: new fields.BooleanField(),
religious: new fields.BooleanField()
})
schema.prayerTime = new fields.StringField({ required: true, initial: "" })
schema.miracleRange = new fields.StringField({ required: true, initial: "" })
schema.areaAffected = new fields.StringField({ required: true, initial: "" })
schema.duration = new fields.StringField({ required: true, initial: "" })
schema.savingThrow = new fields.StringField({ required: true, initial: "" })
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["LETHALFANTASY.Miracle"]
}

View File

@ -12,6 +12,13 @@ export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
schema.bonus = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger,required: true, initial: 0, min: 0 })
schema.weaponClass = new fields.StringField({ required: true, initial: "shortblade", choices: SYSTEM.WEAPON_CLASS })
schema.weaponBonus = new fields.SchemaField({
attack: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
defense: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
damage: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
})
return schema
}
@ -21,10 +28,30 @@ export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
get skillCategory() {
return game.i18n.localize(CATEGORY[this.category].label)
}
validate(options) {
let isError = super.validate(options)
console.log(this)
let bonus = this._source.weaponBonus.attack + this._source.weaponBonus.defense + this._source.weaponBonus.damage
console.log(bonus, this._source.skillTotal)
if ( bonus > Math.floor(this._source.skillTotal / 10) ) {
ui.notifications.error(game.i18n.localize("LETHALFANTASY.Skill.error.weaponBonus"))
isError = true
}
return isError
}
prepareDerivedData() {
super.prepareDerivedData();
this.skillTotal = this.computeBase();
if( this.category === "weapon" ) {
this.totalBonus = this.weaponBonus.attack + this.weaponBonus.defense + this.weaponBonus.damage;
if ( Number(this.skillTotal) ) {
this.availableBonus = Math.max( Math.floor(this.skillTotal / 10) - 1, 0 )
} else {
this.availableBonus = "N/A"
}
}
}
computeBase() {
@ -47,7 +74,7 @@ export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
maxStat = statValue;
}
}
return maxStat;
return maxStat + this.bonus
} else {
// Split with + calculate the total
baseSplit = base.split("+");
@ -59,9 +86,9 @@ export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
const statValue = actor.system.characteristics[stat.toLowerCase()]?.value || 0;
total += statValue;
}
return total
return total + this.bonus
}
}
return `${this.base } + ${ String(this.bonus)}`;
return `${this.base} + ${String(this.bonus)}`;
}
}

View File

@ -25,7 +25,7 @@ export default class LethalFantasySpell extends foundry.abstract.TypeDataModel {
material: new fields.BooleanField(),
})
schema.castingTime = new fields.StringField({ required: true, initial: "" })
schema.range = new fields.StringField({ required: true, initial: "" })
schema.spellRange = new fields.StringField({ required: true, initial: "" })
schema.areaAffected = new fields.StringField({ required: true, initial: "" })
schema.duration = new fields.StringField({ required: true, initial: "" })
schema.savingThrow = new fields.StringField({ required: true, initial: "" })

View File

@ -1,5 +1,5 @@
import { SYSTEM } from "../config/system.mjs"
import { CATEGORY } from "../config/weapon.mjs"
export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
@ -8,6 +8,8 @@ export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.weaponType = new fields.StringField({ required: true, initial: "melee", choices: SYSTEM.WEAPON_TYPE })
schema.weaponClass = new fields.StringField({ required: true, initial: "shortblade", choices: SYSTEM.WEAPON_CLASS })
schema.damageType = new fields.SchemaField({
typeP: new fields.BooleanField(),
typeB: new fields.BooleanField(),
@ -29,7 +31,7 @@ export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
})
schema.defense = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.range = new fields.SchemaField({
schema.weaponRange = new fields.SchemaField({
pointBlank: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
short: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
medium: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
@ -49,6 +51,6 @@ export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
static LOCALIZATION_PREFIXES = ["LETHALFANTASY.Weapon"]
get weaponCategory() {
return game.i18n.localize(CATEGORY[this.category].label)
return game.i18n.localize(CATEGORY[this.weaponType].label)
}
}

View File

@ -1,23 +1,13 @@
import { DICE_VALUES } from "./config/system.mjs"
export default class LethalFantasyUtils {
// Return the maximum damage limited by the maximum damage of the character
static maxDamage(damage, damageMax) {
const damageIndex = DICE_VALUES.indexOf(damage)
const damageMaxIndex = DICE_VALUES.indexOf(damageMax)
// If damage exceeds damageMax, return damageMax
if (damageIndex > damageMaxIndex) {
return damageMax
}
// Otherwise, return damage (as it is less than or equal to damageMax)
return damage
return 0
}
// Used when a ressource is lost to find the next lower dice
static findLowerDice(dice) {
let index = DICE_VALUES.indexOf(dice)
return DICE_VALUES[index - 1]
return 0
}
}