diff --git a/assets/caster-silhouette.v1.svg b/assets/caster-silhouette.v1.svg index ed60c3e..9b53fcc 100644 --- a/assets/caster-silhouette.v1.svg +++ b/assets/caster-silhouette.v1.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/langs/en-ca.json b/langs/en-ca.json index 65bb80d..c40e853 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -18,6 +18,7 @@ "AllItemsSheetV1": "RipCrypt Item Sheet", "CombinedHeroSheet": "Hero Sheet", "HeroSummaryCardV1": "Hero Stat Card", + "HeroCraftCardV1": "Hero Craft Card", "HeroSkillsCardV1": "Hero Skill Card" }, "common": { @@ -51,6 +52,7 @@ "fract": "Fract", "focus": "Focus" }, + "aura": "Aura", "cost": "Cost", "currency": { "gold": "Gold", @@ -60,6 +62,7 @@ "damage": "Damage", "delete": "Delete", "description": "Description", + "details": "Details", "difficulties": { "easy": "Easy", "normal": "Normal", @@ -75,6 +78,7 @@ "equipped": "Equipped", "fate": "Fate", "gear": "Gear", + "glimcraft": "Glimcraft", "glory": "Glory", "guts": "Guts", "location": "Location", @@ -149,7 +153,6 @@ } }, "Apps": { - "move-run": "@RipCrypt.common.move • @RipCrypt.common.run", "traits-range": "@RipCrypt.common.traits • @RipCrypt.common.range", "grit-skills": "@RipCrypt.common.abilities.grit Skills", "gait-skills": "@RipCrypt.common.abilities.gait Skills", @@ -188,7 +191,11 @@ "set-fate-to": "Set Fate to {ordinal}", "current-tour": "Current Delve Tour", "next-tour": "Next Delve Tour", - "prev-tour": "Previous Delve Tour" + "prev-tour": "Previous Delve Tour", + "auras": { + "normal": "The distance of your aura normally", + "heavy": "The distance of your aura when using Heavycraft" + } } } } diff --git a/module/Apps/ActorSheets/HeroCraftCardV1.mjs b/module/Apps/ActorSheets/HeroCraftCardV1.mjs index 7d78029..66d78d4 100644 --- a/module/Apps/ActorSheets/HeroCraftCardV1.mjs +++ b/module/Apps/ActorSheets/HeroCraftCardV1.mjs @@ -8,6 +8,7 @@ import { Logger } from "../../utils/Logger.mjs"; const { HandlebarsApplicationMixin } = foundry.applications.api; const { ActorSheetV2 } = foundry.applications.sheets; const { ContextMenu } = foundry.applications.ui; +const { deepClone } = foundry.utils; export class HeroCraftCardV1 extends GenericAppMixin(HandlebarsApplicationMixin(ActorSheetV2)) { @@ -79,12 +80,18 @@ export class HeroCraftCardV1 extends GenericAppMixin(HandlebarsApplicationMixin( ctx = await super._preparePartContext(partId, ctx, opts); ctx.actor = this.document; + ctx = await HeroCraftCardV1.prepareAura(ctx); ctx = await HeroCraftCardV1.prepareCraft(ctx); Logger.debug(`Context:`, ctx); return ctx; }; + static async prepareAura(ctx) { + ctx.aura = deepClone(ctx.actor.system.aura); + return ctx; + }; + static async prepareCraft(ctx) { ctx.craft = {}; const aspects = Object.values(gameTerms.Aspects); diff --git a/module/api.mjs b/module/api.mjs index d2e50ac..a56f7b1 100644 --- a/module/api.mjs +++ b/module/api.mjs @@ -8,6 +8,7 @@ import { RichEditor } from "./Apps/RichEditor.mjs"; // Util imports import { distanceBetweenFates, nextFate, previousFate } from "./utils/fates.mjs"; import { documentSorter } from "./consts.mjs"; +import { rankToInteger } from "./utils/rank.mjs"; const { deepFreeze } = foundry.utils; @@ -28,6 +29,7 @@ Object.defineProperty( distanceBetweenFates, nextFate, previousFate, + rankToInteger, }, }), writable: false, diff --git a/module/data/Actor/Hero.mjs b/module/data/Actor/Hero.mjs index 2f0dc15..6f589d6 100644 --- a/module/data/Actor/Hero.mjs +++ b/module/data/Actor/Hero.mjs @@ -1,5 +1,6 @@ import { derivedMaximumBar } from "../helpers.mjs"; import { gameTerms } from "../../gameTerms.mjs"; +import { rankToInteger } from "../../utils/rank.mjs"; import { sumReduce } from "../../utils/sumReduce.mjs"; const { fields } = foundry.data; @@ -122,6 +123,13 @@ export class HeroData extends foundry.abstract.TypeDataModel { prepareBaseData() { super.prepareBaseData(); + // Calculate the person's base Crafting aura + const rank = rankToInteger(this.level.rank); + this.aura = { + normal: ( rank + 1 ) * 2, + heavy: ( rank + 2 ) * 2, + }; + this.guts.max = 0; // The limitations imposed on things like inventory spaces and equipped diff --git a/module/hooks/init.mjs b/module/hooks/init.mjs index 547ea3e..48c3171 100644 --- a/module/hooks/init.mjs +++ b/module/hooks/init.mjs @@ -2,6 +2,7 @@ import { AllItemSheetV1 } from "../Apps/ItemSheets/AllItemSheetV1.mjs"; import { CombinedHeroSheet } from "../Apps/ActorSheets/CombinedHeroSheet.mjs"; import { DelveDiceHUD } from "../Apps/DelveDiceHUD.mjs"; +import { HeroCraftCardV1 } from "../Apps/ActorSheets/HeroCraftCardV1.mjs"; import { HeroSkillsCardV1 } from "../Apps/ActorSheets/HeroSkillsCardV1.mjs"; import { HeroSummaryCardV1 } from "../Apps/ActorSheets/HeroSummaryCardV1.mjs"; import { RipCryptCombatTracker } from "../Apps/sidebar/CombatTracker.mjs"; @@ -91,6 +92,11 @@ Hooks.once(`init`, () => { label: `RipCrypt.sheet-names.HeroSkillsCardV1`, themes: HeroSkillsCardV1.themes, }); + Actors.registerSheet(game.system.id, HeroCraftCardV1, { + types: [`hero`], + label: `RipCrypt.sheet-names.HeroCraftCardV1`, + themes: HeroCraftCardV1.themes, + }); // #endregion // #region Items diff --git a/module/utils/rank.mjs b/module/utils/rank.mjs new file mode 100644 index 0000000..81f6b59 --- /dev/null +++ b/module/utils/rank.mjs @@ -0,0 +1,13 @@ +import { gameTerms } from "../gameTerms.mjs"; + +/** + * Converts a rank's name into an integer form for use in mathematical calculations + * that rely on rank. + * + * @param {Novice|Adept|Expert|Master} rankName The rank to convert into an integer + * @returns An integer between 1 and 4 + */ +export function rankToInteger(rankName) { + return Object.values(gameTerms.Rank) + .findIndex(r => r === rankName) + 1; +}; diff --git a/system.json b/system.json index 9924bfe..68d0f25 100644 --- a/system.json +++ b/system.json @@ -1,7 +1,7 @@ { "id": "ripcrypt", "title": "RipCrypt", - "description": "", + "description": "A dungeon sprint RPG. Faster than an arrow to the eye. Smoother than a clean blade. Compact with consequences.", "version": "0.0.1", "compatibility": { "minimum": 13, diff --git a/templates/Apps/HeroCraftCardV1/content.hbs b/templates/Apps/HeroCraftCardV1/content.hbs new file mode 100644 index 0000000..e30ee69 --- /dev/null +++ b/templates/Apps/HeroCraftCardV1/content.hbs @@ -0,0 +1,101 @@ +
+
+ {{rc-i18n "RipCrypt.common.glimcraft"}} +
+
+
10
+
8
+
6
+
4
+ +
+
+ +
+ {{aura.normal}} + + {{aura.heavy}} +
+
+
+
+ +
+ {{rc-i18n "RipCrypt.common.aspectNames.focus"}} + {{rc-i18n "RipCrypt.common.details"}} +
+
    + {{#each craft.focus as | craft |}} + {{#if craft}} +
  1. + {{ craft.name }} + {{#if craft.use}} + + {{/if}} +
  2. + {{else}} +
  3. + {{/if}} + {{/each}} +
+ +
+ {{rc-i18n "RipCrypt.common.aspectNames.flect"}} + {{rc-i18n "RipCrypt.common.details"}} +
+
    + {{#each craft.flect as | craft |}} + {{#if craft}} +
  1. + {{ craft.name }} + {{#if craft.use}} + + {{/if}} +
  2. + {{else}} +
  3. + {{/if}} + {{/each}} +
+ +
+ {{rc-i18n "RipCrypt.common.aspectNames.fract"}} + {{rc-i18n "RipCrypt.common.details"}} +
+
    + {{#each craft.fract as | craft |}} + {{#if craft}} +
  1. + {{ craft.name }} + {{#if craft.use}} + + {{/if}} +
  2. + {{else}} +
  3. + {{/if}} + {{/each}} +
+
diff --git a/templates/Apps/HeroCraftCardV1/style.css b/templates/Apps/HeroCraftCardV1/style.css new file mode 100644 index 0000000..cff3266 --- /dev/null +++ b/templates/Apps/HeroCraftCardV1/style.css @@ -0,0 +1,137 @@ +.ripcrypt .HeroCraftCardV1 { + --col-gap: 8px; + + display: grid; + column-gap: var(--col-gap); + grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-rows: repeat(15, minmax(0, 1fr)); + + background: var(--base-background); + color: var(--base-text); + + .col-header { + display: flex; + flex-direction: row; + align-items: center; + background: var(--section-header-background); + color: var(--section-header-text); + padding: 2px 4px; + border-radius: 999px; + } + + label, .label { + box-sizing: border-box; + padding: 2px 4px; + text-transform: uppercase; + font-size: var(--font-size-14); + overflow: hidden; + text-overflow: ellipsis; + font-weight: bold; + } + + .aura-container { + grid-column: 1 / -1; + grid-row: 2 / span 4; + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + grid-template-rows: minmax(0, 1fr); + position: relative; + } + + .circle-fragment, .full-circle { + display: flex; + justify-content: center; + align-items: center; + } + + .circle-fragment { + border-top-left-radius: 24% 100%; + border-bottom-left-radius: 25% 100%; + border-left: 2px dashed var(--accent-3); + margin-right: -5%; + } + + .full-circle { + border: 2px dashed var(--accent-3); + flex-grow: 0; + border-radius: 999px; + width: 80%; + aspect-ratio: 1; + align-self: center; + justify-self: center; + grid-row: 1; + grid-column: 4; + } + + .caster-silhouette { + grid-column: 4 / span 4; + grid-row: 1; + position: absolute; + left: 2rem; + width: 70%; + bottom: -10px; + } + + .aura-values { + grid-row: 1; + grid-column: -3 / -1; + display: flex; + justify-content: center; + align-items: center; + z-index: 3; + + .dual-pill { + border-radius: 999px; + background: var(--accent-1); + display: flex; + flex-direction: row; + gap: 0.25rem; + align-items: center; + padding-left: 8px; + margin-left: 1rem; + margin-bottom: 1.2rem; + } + + .values { + border-radius: 999px; + margin: 2px; + background: var(--base-background); + color: var(--base-text); + padding: 0.125rem 0.5rem; + display: flex; + flex-direction: row; + gap: 0.5rem; + --slash-color: var(--accent-1); + } + } + + .craft-list { + display: grid; + grid-template-rows: subgrid; + + > :nth-child(even) { + background: var(--alt-row-background); + color: var(--alt-row-text); + } + } + + span.name { + flex-grow: 1; + } + + [data-aspect="focus"] { --row: 6; --col: 1; } + [data-aspect="flect"] { --row: 6; --col: 2; } + [data-aspect="fract"] { --row: 11; --col: 1; } + + [data-aspect] { + &.aspect-header { + z-index: 1; + grid-row: var(--row); + grid-column: var(--col); + } + &.craft-list { + grid-row: calc(var(--row) + 1) / span 4; + grid-column: var(--col); + } + } +} diff --git a/templates/Apps/HeroSummaryCardV1/content.hbs b/templates/Apps/HeroSummaryCardV1/content.hbs index 23b84c8..beefb7d 100644 --- a/templates/Apps/HeroSummaryCardV1/content.hbs +++ b/templates/Apps/HeroSummaryCardV1/content.hbs @@ -351,7 +351,7 @@ {{speed.run}} diff --git a/templates/Apps/apps.css b/templates/Apps/apps.css index 9f6a4f6..2ec33cc 100644 --- a/templates/Apps/apps.css +++ b/templates/Apps/apps.css @@ -1,8 +1,8 @@ @import url("./AllItemSheetV1/style.css"); @import url("./CombinedHeroSheet/style.css"); -@import url("./CryptApp/style.css"); @import url("./DelveDiceHUD/style.css"); @import url("./DicePool/style.css"); +@import url("./HeroCraftCardV1/style.css"); @import url("./HeroSummaryCardV1/style.css"); @import url("./HeroSkillsCardV1/style.css"); @import url("./RichEditor/style.css"); diff --git a/templates/css/common.css b/templates/css/common.css index f11d646..cb8c9f0 100644 --- a/templates/css/common.css +++ b/templates/css/common.css @@ -21,7 +21,9 @@ } .HeroSummaryCardV1, - .HeroSkillsCardV1 { + .HeroSkillsCardV1, + .HeroCraftCardV1 { + padding: 8px; /* height: 270px; */ width: 680px; --col-gap: 2px; diff --git a/templates/css/elements/lists.css b/templates/css/elements/lists.css index 2940b37..b55f34f 100644 --- a/templates/css/elements/lists.css +++ b/templates/css/elements/lists.css @@ -15,6 +15,7 @@ counter-increment: list-index 1; position: relative; padding: 0 4px; + border-radius: 999px; } &.num-before > li::before, diff --git a/templates/css/elements/span.css b/templates/css/elements/span.css index b4a2cef..85a099b 100644 --- a/templates/css/elements/span.css +++ b/templates/css/elements/span.css @@ -10,6 +10,13 @@ overflow: hidden; } + &.slash { + width: 2px; + background: var(--slash-color, currentColor); + border-radius: 999px; + transform: rotate(var(--slash-rotation, 15deg)); + } + /* Makes it so that spans are never less than the font size */ &:empty::before { content: "\200b";