Skip to content

Commit

Permalink
fix: Improves behavior of additive and reductive traits (close #456, c…
Browse files Browse the repository at this point in the history
…lose #421)
  • Loading branch information
valentine195 committed Sep 19, 2024
1 parent 6d14ce3 commit 12df383
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 137 deletions.
2 changes: 1 addition & 1 deletion src/layouts/layout.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const StatblockItemTypes = [
"action"
] as const;

export const TypeNames: Array<[(typeof StatblockItemTypes)[number], string]> = [
export const TypeNames: Array<[(typeof StatblockItemTypes)[number] | null, string]> = [
["group", "Group"],
["inline", "Inline Group"],
["ifelse", "If/Else"],
Expand Down
133 changes: 52 additions & 81 deletions src/view/statblock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ type RendererParameters = {
);

export default class StatBlockRenderer extends MarkdownRenderChild {
topBar: HTMLDivElement;
bottomBar: HTMLDivElement;
topBar!: HTMLDivElement;
bottomBar!: HTMLDivElement;
loaded: boolean = false;
statblockEl: HTMLDivElement;
contentEl: HTMLDivElement;
statblockEl!: HTMLDivElement;
contentEl!: HTMLDivElement;
container: HTMLElement;
monster: Monster;
monster!: Monster;
plugin: StatBlockPlugin;
params: Partial<StatblockParameters>;
params!: Partial<StatblockParameters>;
context: string;
layout: Layout;
layout!: Layout;
constructor(
public rendererParameters: RendererParameters,
public icons = true
Expand Down Expand Up @@ -166,86 +166,57 @@ export default class StatBlockRenderer extends MarkdownRenderChild {
}
switch (block.type) {
case "traits": {
/**
* Traits can be defined directly, as additive (+) or subtractive (-).
*
* Directly defined traits can be overidden by name up the extension tree.
* Parameters > `creature` > `extends`
* Directly defined parameter traits are *always shown*.
*
* Additive traits are *always* displayed, no matter where they originate.
*
* Subtractive traits are *always* removed, unless the trait is directly defined in the parameters.
* Subtractive traits only work on directly defined traits.
*
*/
const $TRAIT_MAP: Map<string, Trait> = new Map();
let $ADDITIVE_TRAITS: Trait[] = [];
let $DELETE_TRAITS: Set<string> = new Set();
/** Add traits from the extensions group first. */
for (const extension of extensions) {
let traits = getTraitsList(property, extension);
for (const trait of traits) {
$TRAIT_MAP.set(trait.name, trait);
}
const $ADDITIVE_TRAITS: Trait[] = [];

traits = getTraitsList(
`${property}+` as keyof Monster,
extension
);
for (const trait of traits) {
$ADDITIVE_TRAITS.push(trait);
}
traits = getTraitsList(
/**
* Resolve extension traits first.
*/
for (const creature of [...extensions]) {
/**
* Deleted traits. These are always removed.
*/
for (const trait of getTraitsList(
`${property}-` as keyof Monster,
extension
);
for (const trait of traits) {
$DELETE_TRAITS.add(trait.name);
}
}
//next, underlying monster object
let traits = getTraitsList(property, built);
for (const trait of traits) {
if (!(property in this.params)) {
creature
)) {
$TRAIT_MAP.delete(trait.name);
$ADDITIVE_TRAITS.push(trait);
} else {
}
/**
* Directly defined traits.
*
* Because these can be overridden, they go into a map by name.
*/
for (const trait of getTraitsList(
property,
creature
)) {
$TRAIT_MAP.set(trait.name, trait);
}
}
traits = getTraitsList(
`${property}+` as keyof Monster,
built
);
for (const trait of traits) {
$ADDITIVE_TRAITS.push(trait);
}
traits = getTraitsList(
`${property}-` as keyof Monster,
built
);
for (const trait of traits) {
$DELETE_TRAITS.add(trait.name);
}

/** Remove these traits first, so you don't get hit by the params */
traits = getTraitsList(
`${property}-` as keyof Monster,
this.params
);
for (const trait of traits) {
$DELETE_TRAITS.add(trait.name);
}
for (const trait of $DELETE_TRAITS) {
$TRAIT_MAP.delete(trait);
$ADDITIVE_TRAITS = $ADDITIVE_TRAITS.filter(
(t) => t.name !== trait
);
}
//finally, the parameters should always be added
traits = getTraitsList(property, this.params);
for (const trait of traits) {
$TRAIT_MAP.delete(trait.name);
$ADDITIVE_TRAITS.push(trait);
}

traits = getTraitsList(
`${property}+` as keyof Monster,
this.params
);

for (const trait of traits) {
$ADDITIVE_TRAITS.push(trait);
/**
* Additive traits. These traits are always shown.
*/
for (const trait of getTraitsList(
`${property}+` as keyof Monster,
creature
)) {
$ADDITIVE_TRAITS.push(trait);
}
}

Object.assign(built, {
[property]: [
...$TRAIT_MAP.values(),
Expand Down Expand Up @@ -398,7 +369,7 @@ export default class StatBlockRenderer extends MarkdownRenderChild {
}
}

$ui: Statblock;
$ui!: Statblock;
async init() {
this.containerEl.empty();
this.monster = (await this.build()) as Monster;
Expand Down Expand Up @@ -434,7 +405,7 @@ export default class StatBlockRenderer extends MarkdownRenderChild {
this.$ui.$on("export", () => {
this.plugin.exportAsPng(
this.monster.name,
this.containerEl.firstElementChild
this.containerEl.firstElementChild!
);
});

Expand Down
105 changes: 52 additions & 53 deletions src/view/ui/ColumnContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
container: target,
classes: item.cls
? [...(classes ?? []), item.cls]
: classes ?? []
: (classes ?? [])
});
targets.push(...element);
Expand Down Expand Up @@ -231,7 +231,7 @@
"statblock-item-inline",
...(item.cls
? [...(classes ?? []), item.cls]
: classes ?? [])
: (classes ?? []))
]
});
for (const nested of item.nested ?? []) {
Expand Down Expand Up @@ -266,7 +266,7 @@
type: "group",
nested: layout.blocks,
id: item.layout,
properties: null
properties: []
},
{
classes: [
Expand Down Expand Up @@ -306,58 +306,55 @@
item.properties[0]
] as Spell[];
if (!Array.isArray(blocks) || !blocks.length) return;
let spellBlocks: Array<SpellBlock> = blocks.reduce(
(acc, current) => {
if (
typeof current === "string" &&
(current.charAt(current.length - 1) == ":" ||
!current.includes(":"))
) {
const newBlock: SpellBlock = {
header: ensureColon(current),
spells: []
};
acc.push(newBlock);
return acc;
}
const lastBlock: SpellBlock = acc[acc.length - 1];
let spell: Spell;
if (typeof current == "string") {
if (!Array.isArray(blocks) || !blocks.length) return [];
let spellBlocks: Array<SpellBlock> = blocks.reduce<
SpellBlock[]
>((acc, current) => {
if (
typeof current === "string" &&
(current.charAt(current.length - 1) == ":" ||
!current.includes(":"))
) {
const newBlock: SpellBlock = {
header: ensureColon(current),
spells: []
};
acc.push(newBlock);
return acc;
}
const lastBlock: SpellBlock = acc[acc.length - 1];
let spell: Spell;
if (typeof current == "string") {
spell = {
spells: Linkifier.linkifySpells(
current,
context.get("context") as string
)
};
} else {
try {
spell = {
level: Object.keys(current).shift(),
spells: Linkifier.linkifySpells(
current,
stringify(Object.values(current).shift()!),
context.get("context") as string
)
};
} else {
try {
spell = {
level: Object.keys(current).shift(),
spells: Linkifier.linkifySpells(
stringify(
Object.values(current).shift()
),
context.get("context") as string
)
};
} catch (e) {
return acc;
}
}
if (lastBlock) {
lastBlock.spells.push(spell);
} else {
const missingHeaderBlock: SpellBlock = {
header: `${monster.name} knows the following spells:`,
spells: [spell]
};
acc.push(missingHeaderBlock);
} catch (e) {
return acc;
}
return acc;
},
[]
);
}
if (lastBlock) {
lastBlock.spells.push(spell);
} else {
const missingHeaderBlock: SpellBlock = {
header: `${monster.name} knows the following spells:`,
spells: [spell]
};
acc.push(missingHeaderBlock);
}
return acc;
}, []);
for (
let blockIndex = 0;
Expand All @@ -371,7 +368,7 @@
props: {
name:
blockIndex == 0
? item.heading ?? "Spellcasting"
? (item.heading ?? "Spellcasting")
: "",
property: item.properties[0],
desc: block.header,
Expand Down Expand Up @@ -515,10 +512,12 @@
}
}
} catch (e) {
console.error(e);
return [];
}
break;
}
}
if ("hasRule" in item && item.hasRule) {
const rule = createDiv(
Expand All @@ -535,7 +534,7 @@
return targets.filter((el) => el.hasChildNodes());
};
$: maxHeight =
!isNaN(Number(monster.columnHeight)) && monster.columnHeight > 0
!isNaN(Number(monster.columnHeight)) && monster.columnHeight! > 0
? monster.columnHeight
: Infinity;
Expand Down Expand Up @@ -582,7 +581,7 @@
}
});
contentContainer.$on("built", () => {
const columnEl = temp.querySelector(".column");
const columnEl = temp.querySelector(".column")!;
for (let target of targets) {
heights.push(target.scrollHeight);
}
Expand All @@ -597,7 +596,7 @@
} else {
split = Math.max(
600,
Math.min(columnEl.scrollHeight / columns, maxHeight)
Math.min(columnEl.scrollHeight / columns, maxHeight!)
);
}
Expand Down
5 changes: 3 additions & 2 deletions src/view/ui/MarkdownHolder.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import { Linkifier } from "src/parser/linkify";
import { parseForDice } from "src/parser/dice-parsing";
import type { Writable } from "svelte/store";
import { stringify } from "src/util/util";
export let property: string;
Expand Down Expand Up @@ -38,7 +39,7 @@
) {
split = [{ text: monster[item.diceProperty] as string }];
} else {
const parsed = parseForDice(layout, property, monster);
const parsed = parseForDice(layout, stringify(property), monster);
if (Array.isArray(parsed)) {
split = parsed;
} else {
Expand All @@ -62,7 +63,7 @@
new Notice(
`There was an error executing the provided dice callback for [${item.properties.join(
", "
)}]\n\n${e.message}`
)}]\n\n${(e as Error).message}`
);
console.error(e);
}
Expand Down

0 comments on commit 12df383

Please sign in to comment.