diff --git a/css/style.min.css b/css/style.min.css index f2853193..e72ab15b 100644 --- a/css/style.min.css +++ b/css/style.min.css @@ -14007,7 +14007,8 @@ template { z-index: 4; width: 100%; position: absolute; - pointer-events: none; } + pointer-events: none; + margin-top: 20px; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root section.sheet-footer .roll-sheet-float-block.roll-effects-block:not(.inactive-mod-block) { flex-direction: column; width: unset; @@ -14023,12 +14024,34 @@ template { width: 100%; border-bottom-left-radius: 30px; border-bottom-right-radius: 30px; + overflow: hidden; + position: relative; } + :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root section.sheet-footer .roll-sheet-float-block.roll-button .roll-odds-strip .roll-odds-section-container { + height: 500%; + width: 100%; + position: absolute; + z-index: 5; + display: flex; + justify-content: stretch; + align-items: stretch; + flex-wrap: nowrap; + filter: blur(50px); } + :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root section.sheet-footer .roll-sheet-float-block.roll-button .roll-odds-strip .roll-odds-section-container > * { + flex-grow: 1; + flex-shrink: 1; } + :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root section.sheet-footer .roll-sheet-float-block.roll-button .roll-odds-label-container { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; text-align: center; line-height: 28px; color: var(--blades-gold-dark); font-family: var(--font-emphasis); font-size: 18px; - text-shadow: 1.5px 1.5px 0px var(--blades-black-dark); } + text-shadow: 1.5px 1.5px 0px var(--blades-black-dark); + z-index: 5; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root section.sheet-footer .roll-sheet-float-block.roll-button .roll-button { display: block; padding: 0; @@ -14048,7 +14071,8 @@ template { transform-origin: 50% 50%; transition: 0.25s; filter: drop-shadow(3px 3px 5px var(--blades-black)); - opacity: 1; } + opacity: 1; + z-index: 5; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root section.sheet-footer .roll-sheet-float-block.roll-button .roll-button:hover { scale: 1.2; color: var(--blades-gold-bright); diff --git a/module/BladesRollCollab.js b/module/BladesRollCollab.js index 106b5502..8b0ae52f 100644 --- a/module/BladesRollCollab.js +++ b/module/BladesRollCollab.js @@ -405,50 +405,20 @@ class BladesRollMod { }); } get tooltip() { - if (this.sideString) { - return this._tooltip - .replace(/%COLON%/g, ":") - .replace(/%DOC_NAME%/g, this.sideString); - } - return this._tooltip.replace(/%COLON%/g, ":"); + return this._tooltip.replace(/%COLON%/g, ":") + .replace(/%DOC_NAME%/g, this.sideString ?? "an Ally") + .replace(/@OPPOSITION_NAME@/g, this.rollInstance.rollOpposition?.rollOppName ?? "Your Opposition"); } get sideString() { if (this._sideString) { return this._sideString; } - switch (this.category) { - case RollModSection.roll: { - if (this.name === "Assist") { - const docID = this.rollInstance.document.getFlag("eunos-blades", "rollCollab.docSelections.roll.Assist"); - if (!docID) { - return undefined; - } - return (game.actors.get(docID) ?? game.items.get(docID))?.name ?? undefined; - } - return undefined; - } - case RollModSection.position: { - if (this.name === "Setup") { - const docID = this.rollInstance.document.getFlag("eunos-blades", "rollCollab.docSelections.position.Setup"); - if (!docID) { - return undefined; - } - return (game.actors.get(docID) ?? game.items.get(docID))?.name ?? undefined; - } - return undefined; - } - case RollModSection.effect: { - if (this.name === "Setup") { - const docID = this.rollInstance.document.getFlag("eunos-blades", "rollCollab.docSelections.effect.Setup"); - if (!docID) { - return undefined; - } - return (game.actors.get(docID) ?? game.items.get(docID))?.name ?? undefined; - } - return undefined; - } - default: return undefined; + const rollParticipantCategoryData = this.rollInstance.rollParticipants?.[this.category]; + if (rollParticipantCategoryData && this.name in rollParticipantCategoryData) { + const rollParticipant = rollParticipantCategoryData[this.name]; + return rollParticipant.rollParticipantName; } + return undefined; } get allFlagData() { return this.rollInstance.document.getFlag("eunos-blades", "rollCollab"); @@ -592,7 +562,10 @@ class BladesRollPrimary { this.rollPrimaryType = this.rollPrimaryDoc.rollPrimaryType; this.rollPrimaryImg = rollPrimaryImg ?? this.rollPrimaryDoc.rollPrimaryImg ?? ""; this._rollModsData = rollModsData ?? []; - this.rollFactors = Object.assign(this.rollPrimaryDoc.rollFactors, rollFactors ?? {}); + this.rollFactors = { + ...this.rollPrimaryDoc.rollFactors, + ...rollFactors ?? {} + }; } else { if (!rollPrimaryName) { @@ -1106,6 +1079,7 @@ class BladesRollCollab extends DocumentSheet { await rollInst._render(true); } static RenderRollCollab(rollID) { + BladesRollCollab.Current[rollID]?.prepareRollParticipantData(); BladesRollCollab.Current[rollID]?.render(); } static async CloseRollCollab(rollID) { @@ -1358,7 +1332,6 @@ class BladesRollCollab extends DocumentSheet { } async addRollParticipant(participant) { await participant.updateRollFlags(); - this.prepareRollParticipantData(); socketlib.system.executeForEveryone("renderRollCollab", this.rollID); } get rollType() { return this.flagData.rollType; } @@ -1490,126 +1463,85 @@ class BladesRollCollab extends DocumentSheet { return this._roll; } get rollFactors() { - const sourceFactors = Object.fromEntries(Object.entries(this.rollPrimary.rollFactors) - .map(([factor, factorData]) => [ - factor, - { - ...factorData, - ...this.flagData.rollFactorToggles.source[factor] ?? [] - } - ])); - Object.entries(this.flagData.rollFactorToggles.source).forEach(([factor, factorData]) => { - if (!(factor in sourceFactors)) { - sourceFactors[factor] = { - name: factor, - value: 0, - max: 0, - baseVal: 0, - cssClasses: "factor-gold", - isActive: factorData.isActive ?? false, - isPrimary: factorData.isPrimary ?? (factor === Factor.tier), - isDominant: factorData.isDominant ?? false, - highFavorsPC: factorData.highFavorsPC ?? true - }; - } - }); - Object.keys(sourceFactors) - .filter(isFactor) - .forEach(factor => { - const factorData = sourceFactors[factor]; - if (!factorData) { - return; - } - factorData.value ??= 0; - factorData.value += - (this.flagData.GMBoosts[factor] ?? 0) - + (this.tempGMBoosts[factor] ?? 0); - }); - Object.keys(sourceFactors) - .filter(isFactor) - .forEach(factor => { - const factorData = sourceFactors[factor]; - if (!factorData) { - return; - } - factorData.value ??= 0; - factorData.value += this.flagData.GMOppBoosts[factor] ?? 0; - if (factor === Factor.tier) { - factorData.display = U.romanizeNum(factorData.value); - } - else { - factorData.display = `${factorData.value}`; - } - }); - const rollOppFactors = this.rollOpposition?.rollFactors - ?? Object.fromEntries(([ - Factor.tier, - Factor.quality, - Factor.scale, - Factor.magnitude - ]).map(factor => [ - factor, - { - name: factor, - value: 0, - max: 0, - baseVal: 0, - cssClasses: "factor-gold", - isActive: false, - isPrimary: factor === Factor.tier, - isDominant: false, - highFavorsPC: true - } - ])); - const oppFactors = {}; - Object.entries(rollOppFactors) - .forEach(([factor, factorData]) => { - if (!isFactor(factor)) { - return; - } - oppFactors[factor] = { - ...factorData, - ...this.flagData.rollFactorToggles.opposition[factor] ?? [] - }; - }); - Object.entries(this.flagData.rollFactorToggles.opposition) - .forEach(([factor, factorData]) => { - if (!isFactor(factor)) { - return; - } - if (!(factor in oppFactors)) { - oppFactors[factor] = { - name: factor, - value: 0, - max: 0, - baseVal: 0, - cssClasses: "factor-gold", - isActive: factorData.isActive ?? false, - isPrimary: factorData.isPrimary ?? (factor === Factor.tier), - isDominant: factorData.isDominant ?? false, - highFavorsPC: factorData.highFavorsPC ?? true - }; - } - }); - Object.keys(oppFactors).forEach(factor => { - if (!isFactor(factor)) { - return; - } - const factorData = oppFactors[factor]; - if (!factorData) { - return; - } - factorData.value += this.flagData.GMOppBoosts[factor] ?? 0; - if (factor === Factor.tier) { - factorData.display = U.romanizeNum(factorData.value); - } - else { - factorData.display = `${factorData.value}`; + const defaultFactors = { + [Factor.tier]: { + name: "Tier", + value: 0, + max: 0, + baseVal: 0, + display: "?", + isActive: false, + isPrimary: true, + isDominant: false, + highFavorsPC: true, + cssClasses: "factor-gold" + }, + [Factor.quality]: { + name: "Quality", + value: 0, + max: 0, + baseVal: 0, + display: "?", + isActive: false, + isPrimary: false, + isDominant: false, + highFavorsPC: true, + cssClasses: "factor-gold" + }, + [Factor.scale]: { + name: "Scale", + value: 0, + max: 0, + baseVal: 0, + display: "?", + isActive: false, + isPrimary: false, + isDominant: false, + highFavorsPC: true, + cssClasses: "factor-gold" + }, + [Factor.magnitude]: { + name: "Magnitude", + value: 0, + max: 0, + baseVal: 0, + display: "?", + isActive: false, + isPrimary: false, + isDominant: false, + highFavorsPC: true, + cssClasses: "factor-gold" } - }); + }; + const mergedSourceFactors = U.objMerge(U.objMerge(defaultFactors, this.rollPrimary.rollFactors, { isMutatingOk: false }), this.flagData.rollFactorToggles.source, { isMutatingOk: false }); + const mergedOppFactors = this.rollOpposition + ? U.objMerge(U.objMerge(defaultFactors, this.rollPrimary.rollFactors, { isMutatingOk: false }), this.flagData.rollFactorToggles.opposition, { isMutatingOk: false }) + : {}; return { - source: sourceFactors, - opposition: oppFactors + source: Object.fromEntries(Object.entries(mergedSourceFactors) + .map(([factor, factorData]) => { + factorData.value += + (this.flagData.GMBoosts[factor] ?? 0) + + (this.tempGMBoosts[factor] ?? 0); + if (factor === Factor.tier) { + factorData.display = U.romanizeNum(factorData.value); + } + else { + factorData.display = `${factorData.value}`; + } + return [factor, factorData]; + })), + opposition: Object.fromEntries(Object.entries(mergedOppFactors) + .map(([factor, factorData]) => { + factorData.value += this.flagData.GMOppBoosts[factor] ?? 0; + if (factor === Factor.tier) { + factorData.display = U.romanizeNum(factorData.value); + } + else { + factorData.display = `${factorData.value}`; + } + return [factor, factorData]; + })) }; } initRollMods(modsData) { @@ -1896,6 +1828,7 @@ class BladesRollCollab extends DocumentSheet { rollTraitOptions, diceTotal: finalDicePool, rollOpposition: this.rollOpposition, + rollParticipants: this.rollParticipants, rollEffects: Object.values(Effect), teamworkDocs: game.actors.filter(actor => BladesActor.IsType(actor, BladesActorType.pc)), rollTraitValOverride: this.rollTraitValOverride, @@ -1906,7 +1839,7 @@ class BladesRollCollab extends DocumentSheet { .map(cat => [cat, this.getRollMods(cat, "negative")])), hasInactiveConditionals: this.calculateHasInactiveConditionalsData(), rollFactors, - oddsGradient: this.calculateOddsGradient(finalDicePool, finalResult), + ...this.calculateOddsHTML(finalDicePool, finalResult), costData: this.parseCostsHTML(this.getStressCosts(rollCosts), this.getSpecArmorCost(rollCosts)) }; const rollPositionData = this.calculatePositionData(finalPosition); @@ -2001,6 +1934,43 @@ class BladesRollCollab extends DocumentSheet { `${oddsColors.success} ${gradientStops.success}%`, `${oddsColors.crit})` ].join(", "); + } + calculateOddsHTML(diceTotal, finalResult) { + const oddsColors = { + crit: "var(--blades-gold)", + success: "var(--blades-white-bright)", + partial: "var(--blades-grey)", + fail: "var(--blades-black-dark)" + }; + const odds = { ...C.DiceOdds[diceTotal] }; + if (finalResult < 0) { + for (let i = finalResult; i < 0; i++) { + oddsColors.crit = oddsColors.success; + oddsColors.success = oddsColors.partial; + oddsColors.partial = oddsColors.fail; + } + } + else if (finalResult > 0) { + for (let i = 0; i < finalResult; i++) { + oddsColors.fail = oddsColors.partial; + oddsColors.partial = oddsColors.success; + oddsColors.success = oddsColors.crit; + } + } + const resultElements = []; + Object.entries(odds).reverse().forEach(([result, chance]) => { + if (chance === 0) { + return; + } + resultElements.push(`
The cohort is terrifying in aspect and reputation.
", - "Independent": "The cohort can be trusted to make good decisions and act on their own initiative in the absence of direct orders.
", - "Loyal": "The cohort can't be bribed or turned against you.
", - "Tenacious": "The cohort won't be deterred from a task.
", - "Nimble": "The vehicle handles easily. Consider this an assist for tricky maneuvers.
", - "Simple": "The vehicle is easy to repair. Remove all of its Harm during downtime
", - "Sturdy": "The vehicle keeps operating even when Broken.
", + Fearsome: "The cohort is terrifying in aspect and reputation.
", + Independent: "The cohort can be trusted to make good decisions and act on their own initiative in the absence of direct orders.
", + Loyal: "The cohort can't be bribed or turned against you.
", + Tenacious: "The cohort won't be deterred from a task.
", + Nimble: "The vehicle handles easily. Consider this an assist for tricky maneuvers.
", + Simple: "The vehicle is easy to repair. Remove all of its Harm during downtime
", + Sturdy: "The vehicle keeps operating even when Broken.
", "Arrow-Swift": "Your pet gains Potency when tracking or fighting the supernatural.
It can move extremely quickly, outpacing any other creature or vehicle.
", "Ghost Form": "Your pet gains Potency when tracking or fighting the supernatural.
It can transform into electroplasmic vapor as if it were a spirit.
", "Mind Link": "Your pet gains Potency when tracking or fighting the supernatural.
You and your pet can share senses and thoughts telepathically.
" @@ -982,7 +982,17 @@ const C = { [AttributeTrait.resolve]: [ActionTrait.attune, ActionTrait.command, ActionTrait.consort, ActionTrait.sway] }, Vices: [ - Vice.Faith, Vice.Gambling, Vice.Luxury, Vice.Obligation, Vice.Pleasure, Vice.Stupor, Vice.Weird, Vice.Worship, Vice.Living_Essence, Vice.Life_Essence, Vice.Electroplasmic_Power + Vice.Faith, + Vice.Gambling, + Vice.Luxury, + Vice.Obligation, + Vice.Pleasure, + Vice.Stupor, + Vice.Weird, + Vice.Worship, + Vice.Living_Essence, + Vice.Life_Essence, + Vice.Electroplasmic_Power ] }; export const Randomizers = { @@ -4225,7 +4235,7 @@ export const SVGDATA = { } }, teeth: { - "tall": { + tall: { viewBox: "0 0 512 1540", paths: { frame: "M0,0v1540l512-244.2V0H0z M451,1263.5l-390,186V61h390V1263.5z", @@ -4233,14 +4243,14 @@ export const SVGDATA = { full: "M0,0v1540l512-244.2V0H0z" } }, - "med": { + med: { viewBox: "0 0 512 1540", paths: { frame: "M0,0v1388l512-395.6V0H0z M458,965.7L54,1278V53h404V965.7z", full: "M0,0v1540l512-244.2V0H0z" } }, - "short": { + short: { viewBox: "0 0 512 1540", paths: { frame: "M0,0v991l511.4-247L512,0H0z M470.5,715.2L41,922.6V40h430L470.5,715.2z", diff --git a/module/documents/actors/BladesPC.js b/module/documents/actors/BladesPC.js index a08d7d7c..2a2f7987 100644 --- a/module/documents/actors/BladesPC.js +++ b/module/documents/actors/BladesPC.js @@ -37,13 +37,17 @@ class BladesPC extends BladesActor { async clearLoadout() { await this.update({ "system.loadout.selected": "" }); this.updateEmbeddedDocuments("Item", [ - ...this.activeSubItems.filter(item => BladesItem.IsType(item, BladesItemType.gear) && !item.hasTag(Tag.System.Archived)) + ...this.activeSubItems + .filter(item => BladesItem.IsType(item, BladesItemType.gear) + && !item.hasTag(Tag.System.Archived)) .map(item => ({ _id: item.id, "system.tags": [...item.tags, Tag.System.Archived], "system.uses_per_score.value": 0 })), - ...this.activeSubItems.filter(item => BladesItem.IsType(item, BladesItemType.ability) && item.system.uses_per_score.max) + ...this.activeSubItems + .filter(item => BladesItem.IsType(item, BladesItemType.ability) + && item.system.uses_per_score.max) .map(item => ({ _id: item.id, "system.uses_per_score.value": 0 @@ -100,28 +104,37 @@ class BladesPC extends BladesActor { return this.activeSubItems.find(item => item.type === BladesItemType.vice); } get crew() { - return this.activeSubActors.find((subActor) => BladesActor.IsType(subActor, BladesActorType.crew)); + return this.activeSubActors + .find((subActor) => BladesActor.IsType(subActor, BladesActorType.crew)); } get abilities() { if (!this.playbook) { return []; } - return this.activeSubItems.filter(item => [BladesItemType.ability, BladesItemType.crew_ability].includes(item.type)); + return this.activeSubItems + .filter(item => [BladesItemType.ability, BladesItemType.crew_ability].includes(item.type)); } get playbookName() { return this.playbook?.name; } get playbook() { - return this.activeSubItems.find((item) => item.type === BladesItemType.playbook); + return this.activeSubItems + .find((item) => item.type === BladesItemType.playbook); } get attributes() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return undefined; } return { - insight: Object.values(this.system.attributes.insight).filter(({ value }) => value > 0).length + this.system.resistance_bonus.insight, - prowess: Object.values(this.system.attributes.prowess).filter(({ value }) => value > 0).length + this.system.resistance_bonus.prowess, - resolve: Object.values(this.system.attributes.resolve).filter(({ value }) => value > 0).length + this.system.resistance_bonus.resolve + insight: Object.values(this.system.attributes.insight) + .filter(({ value }) => value > 0).length + + this.system.resistance_bonus.insight, + prowess: Object.values(this.system.attributes.prowess) + .filter(({ value }) => value > 0).length + + this.system.resistance_bonus.prowess, + resolve: Object.values(this.system.attributes.resolve) + .filter(({ value }) => value > 0).length + + this.system.resistance_bonus.resolve }; } get actions() { @@ -153,14 +166,17 @@ class BladesPC extends BladesActor { .length; } get traumaList() { - return BladesActor.IsType(this, BladesActorType.pc) ? Object.keys(this.system.trauma.active).filter(key => this.system.trauma.active[key]) : []; + return BladesActor.IsType(this, BladesActorType.pc) + ? Object.keys(this.system.trauma.active).filter(key => this.system.trauma.active[key]) + : []; } get activeTraumaConditions() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return {}; } return U.objFilter(this.system.trauma.checked, - (_v, traumaName) => Boolean(traumaName in this.system.trauma.active && this.system.trauma.active[traumaName])); + (_v, traumaName) => Boolean(traumaName in this.system.trauma.active + && this.system.trauma.active[traumaName])); } get currentLoad() { if (!BladesActor.IsType(this, BladesActorType.pc)) { @@ -176,7 +192,8 @@ class BladesPC extends BladesActor { if (!this.system.loadout.selected) { return 0; } - const maxLoad = this.system.loadout.levels[game.i18n.localize(this.system.loadout.selected.toString()).toLowerCase()]; + const maxLoad = this.system.loadout.levels[game.i18n.localize(this.system.loadout.selected.toString()) + .toLowerCase()]; return Math.max(0, maxLoad - this.currentLoad); } async addStash(amount) { @@ -217,7 +234,10 @@ class BladesPC extends BladesActor { get rollPrimaryImg() { return this.img; } get rollModsData() { const rollModsData = BladesRollMod.ParseDocRollMods(this); - [[/1d/, RollModSection.roll], [/Less Effect/, RollModSection.effect]].forEach(([effectPat, effectCat]) => { + [ + [/1d/, RollModSection.roll], + [/Less Effect/, RollModSection.effect] + ].forEach(([effectPat, effectCat]) => { const { one: harmConditionOne, two: harmConditionTwo } = Object.values(this.system.harm) .find(harmData => effectPat.test(harmData.effect)) ?? {}; const harmString = U.objCompact([harmConditionOne, harmConditionTwo === "" ? null : harmConditionTwo]).join(" & "); @@ -240,7 +260,8 @@ class BladesPC extends BladesActor { }); } }); - const { one: harmCondition } = Object.values(this.system.harm).find(harmData => /Need Help/.test(harmData.effect)) ?? {}; + const { one: harmCondition } = Object.values(this.system.harm) + .find(harmData => /Need Help/.test(harmData.effect)) ?? {}; if (harmCondition && harmCondition.trim() !== "") { rollModsData.push({ id: "Push-negative-roll", diff --git a/scss/sheets/_roll-collab-sheet.scss b/scss/sheets/_roll-collab-sheet.scss index 875cbb44..237105a0 100644 --- a/scss/sheets/_roll-collab-sheet.scss +++ b/scss/sheets/_roll-collab-sheet.scss @@ -1457,6 +1457,7 @@ width: 100%; position: absolute; pointer-events: none; + margin-top: 20px; &:not(.inactive-mod-block) { flex-direction: column; @@ -1477,12 +1478,43 @@ width: 100%; border-bottom-left-radius: 30px; border-bottom-right-radius: 30px; + overflow: hidden; + position: relative; + + .roll-odds-section-container { + height: 500%; + width: 100%; + position: absolute; + z-index: 5; + + display: flex; + justify-content: stretch; + align-items: stretch; + flex-wrap: nowrap; + + filter: blur(50px); + + > * { + flex-grow: 1; + flex-shrink: 1; + } + + } + } + + .roll-odds-label-container { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; text-align: center; line-height: 28px; color: var(--blades-gold-dark); font-family: var(--font-emphasis); font-size: 18px; text-shadow: 1.5px 1.5px 0px var(--blades-black-dark); + z-index: 5; } .roll-button { @@ -1505,6 +1537,7 @@ transition: 0.25s; filter: drop-shadow(3px 3px 5px var(--blades-black)); opacity: 1; + z-index: 5; &:hover { scale: 1.2; diff --git a/templates/components/slide-out-controls.hbs b/templates/components/slide-out-controls.hbs index 9361edb9..45fa2a08 100644 --- a/templates/components/slide-out-controls.hbs +++ b/templates/components/slide-out-controls.hbs @@ -7,7 +7,7 @@