- Profils raciaux appliqués automatiquement - DsN opératonnel - Gestion plus fine des fils/orbes

This commit is contained in:
2026-05-04 08:09:27 +02:00
parent 320b2941dc
commit 3534bdf181
68 changed files with 2199 additions and 24 deletions
+114
View File
@@ -4,6 +4,7 @@ import { LesOubliesRolls } from "./les-oublies-rolls.js"
export class LesOubliesActor extends Actor {
static CREATION_ITEM_TYPES = new Set(["race", "tribu", "metier"])
static THREAD_RESOURCE_KEYS = new Set(["songesThreads", "cauchemarThreads", "emptyGlobes"])
prepareDerivedData() {
super.prepareDerivedData()
@@ -22,6 +23,18 @@ export class LesOubliesActor extends Actor {
system.cauchemar.max = totals.cauchemarPoints
system.songes.points = Math.clamp(Number(system.songes.points ?? totals.songesPoints), 0, totals.songesPoints)
system.cauchemar.points = Math.clamp(Number(system.cauchemar.points ?? totals.cauchemarPoints), 0, totals.cauchemarPoints)
system.reserves.songesThreads = Math.max(Number(system.reserves?.songesThreads ?? 0), 0)
system.reserves.cauchemarThreads = Math.max(Number(system.reserves?.cauchemarThreads ?? 0), 0)
system.reserves.emptyGlobes = Math.max(Number(system.reserves?.emptyGlobes ?? 0), 0)
return
}
if (this.type === "compagnie") {
const system = this.system
system.power.sharedDreamPoints = Math.max(Number(system.power?.sharedDreamPoints ?? 0), 0)
system.reserves.songesThreads = Math.max(Number(system.reserves?.songesThreads ?? 0), 0)
system.reserves.cauchemarThreads = Math.max(Number(system.reserves?.cauchemarThreads ?? 0), 0)
system.reserves.emptyGlobes = Math.max(Number(system.reserves?.emptyGlobes ?? 0), 0)
return
}
@@ -62,6 +75,7 @@ export class LesOubliesActor extends Actor {
async assignCreationItem(sourceItem) {
if (!sourceItem || !LesOubliesActor.CREATION_ITEM_TYPES.has(sourceItem.type)) return null
const previousItem = this.getCreationItem(sourceItem.type)
const itemData = sourceItem.toObject()
delete itemData._id
@@ -77,12 +91,19 @@ export class LesOubliesActor extends Actor {
[`system.references.${sourceItem.type}Id`]: createdItem.id,
})
if (sourceItem.type === "race") {
await this.syncRaceProfiles({ currentRace: createdItem })
await this.syncRaceDomains({ currentRace: createdItem, previousRace: previousItem })
}
return createdItem
}
async clearCreationItem(type) {
if (!LesOubliesActor.CREATION_ITEM_TYPES.has(type)) return
const previousItem = this.getCreationItem(type)
const existingIds = this.getEmbeddedItems(type).map((item) => item.id)
if (existingIds.length) {
await this.deleteEmbeddedDocuments("Item", existingIds, { renderSheet: false })
@@ -91,6 +112,11 @@ export class LesOubliesActor extends Actor {
await this.update({
[`system.references.${type}Id`]: "",
})
if (type === "race") {
await this.syncRaceProfiles({ currentRace: null })
await this.syncRaceDomains({ currentRace: null, previousRace: previousItem })
}
}
getCompagnie() {
@@ -98,10 +124,94 @@ export class LesOubliesActor extends Actor {
return compagnieId ? game.actors.get(compagnieId) ?? null : null
}
getThreadReserveOwner(source = "actor") {
if (source === "company" || source === "compagnie") return this.getCompagnie()
return this
}
getThreadReserves(source = "actor") {
const owner = this.getThreadReserveOwner(source)
return {
owner,
songesThreads: Math.max(Number(owner?.system?.reserves?.songesThreads ?? 0), 0),
cauchemarThreads: Math.max(Number(owner?.system?.reserves?.cauchemarThreads ?? 0), 0),
emptyGlobes: Math.max(Number(owner?.system?.reserves?.emptyGlobes ?? 0), 0),
}
}
async transferThreadReserve(resourceKey, amount, direction = "toCompany") {
if (!LesOubliesActor.THREAD_RESOURCE_KEYS.has(resourceKey)) return false
const company = this.getCompagnie()
if (!company) return false
const transferAmount = Math.max(Math.trunc(Number(amount ?? 0)), 0)
if (transferAmount < 1) return false
const fromActor = direction === "toCompany" ? this : company
const toActor = direction === "toCompany" ? company : this
const current = Math.max(Number(fromActor.system?.reserves?.[resourceKey] ?? 0), 0)
if (current < transferAmount) return false
const path = `system.reserves.${resourceKey}`
const targetCurrent = Math.max(Number(toActor.system?.reserves?.[resourceKey] ?? 0), 0)
await fromActor.update({ [path]: current - transferAmount })
await toActor.update({ [path]: targetCurrent + transferAmount })
return true
}
getCompetenceByKey(skillKey) {
return this.getEmbeddedItems("competence").find((item) => item.system.key === skillKey) ?? null
}
getRaceLanguageDomains(race = this.getCreationItem("race")) {
return LesOubliesUtility.uniqueStrings(race?.system?.languageDomains ?? [])
}
getRaceProfiles(race = this.getCreationItem("race")) {
const profiles = LesOubliesUtility.createEmptyProfiles()
for (const key of Object.keys(profiles)) {
profiles[key] = Math.trunc(Number(race?.system?.profiles?.[key] ?? 0))
}
return profiles
}
async syncRaceProfiles({ currentRace = this.getCreationItem("race") } = {}) {
if (this.type !== "personnage") return false
const profiles = this.getRaceProfiles(currentRace)
const updateData = Object.fromEntries(
Object.entries(profiles).map(([key, value]) => [`system.profils.${key}`, value]),
)
await this.update(updateData)
if (currentRace) {
ui.notifications.info(`Profils raciaux appliqués depuis ${currentRace.name}.`)
}
return true
}
async syncRaceDomains({ currentRace = this.getCreationItem("race"), previousRace = null } = {}) {
if (this.type !== "personnage") return false
const competence = this.getCompetenceByKey("langues")
if (!competence) return false
const currentAutoDomains = LesOubliesUtility.uniqueStrings(competence.system.fixedDomains ?? [])
const previousRaceDomains = previousRace
? this.getRaceLanguageDomains(previousRace)
: currentAutoDomains
const autoDomainsToReplace = currentAutoDomains.length ? currentAutoDomains : previousRaceDomains
const nextAutoDomains = this.getRaceLanguageDomains(currentRace)
const manualDomains = LesOubliesUtility.uniqueStrings(
(competence.system.domains ?? []).filter((domain) => !autoDomainsToReplace.includes(domain)),
)
await competence.update({
"system.fixedDomains": nextAutoDomains,
"system.domains": LesOubliesUtility.uniqueStrings([...manualDomains, ...nextAutoDomains]),
})
return true
}
getSkillScoreByKey(skillKey) {
const competence = this.getCompetenceByKey(skillKey)
return competence ? this.computeSkillValue(competence) : 0
@@ -119,6 +229,8 @@ export class LesOubliesActor extends Actor {
item,
finalValue: this.computeSkillValue(item),
profileLabel: LESOUBLIES_CONFIG.profileLabels[item.system.profileKey] ?? item.system.profileKey,
domains: LesOubliesUtility.uniqueStrings(item.system.domains ?? []),
fixedDomains: LesOubliesUtility.uniqueStrings(item.system.fixedDomains ?? []),
}))
}
@@ -146,6 +258,8 @@ export class LesOubliesActor extends Actor {
cauchemarMax: this.system.cauchemar?.max ?? this.system.cauchemar?.points ?? 0,
songesPoints: this.system.songes?.points ?? 0,
cauchemarPoints: this.system.cauchemar?.points ?? 0,
reserves: this.getThreadReserves(),
companyReserves: this.getThreadReserves("company"),
race: this.getCreationItem("race"),
tribu: this.getCreationItem("tribu"),
metier: this.getCreationItem("metier"),