Skip to content

Commit

Permalink
Use CRB language for creature identification info on NPC sheet (found…
Browse files Browse the repository at this point in the history
  • Loading branch information
stwlam authored Apr 13, 2023
1 parent ffc8127 commit b519224
Show file tree
Hide file tree
Showing 16 changed files with 265 additions and 258 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions src/module/actor/npc/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import { ModifierPF2e, StatisticModifier } from "@actor/modifiers.ts";
import { AbilityString, ActorAlliance, SaveType } from "@actor/types.ts";
import { MeleePF2e } from "@item";
import { Rarity, Size } from "@module/data.ts";
import { IdentifyCreatureData } from "@module/recall-knowledge.ts";

interface NPCSource extends BaseCreatureSource<"npc", NPCSystemSource> {
flags: DeepPartial<NPCFlags>;
Expand Down Expand Up @@ -190,8 +189,6 @@ interface NPCDetails extends NPCDetailsSource {
};

alliance: ActorAlliance;

identification: IdentifyCreatureData;
}

/** The full data for a NPC action (used primarily for strikes.) */
Expand Down
12 changes: 8 additions & 4 deletions src/module/actor/npc/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ItemPF2e, MeleePF2e } from "@item";
import { ItemType } from "@item/data/index.ts";
import { calculateDC } from "@module/dc.ts";
import { RollNotePF2e } from "@module/notes.ts";
import { identifyCreature } from "@module/recall-knowledge.ts";
import { CreatureIdentificationData, creatureIdentificationDCs } from "@module/recall-knowledge.ts";
import {
extractDegreeOfSuccessAdjustments,
extractModifierAdjustments,
Expand Down Expand Up @@ -60,6 +60,11 @@ class NPCPF2e<TParent extends TokenDocumentPF2e | null = TokenDocumentPF2e | nul
return this.attributes.adjustment === "weak";
}

get identificationDCs(): CreatureIdentificationData {
const proficiencyWithoutLevel = game.settings.get("pf2e", "proficiencyVariant") === "ProficiencyWithoutLevel";
return creatureIdentificationDCs(this, { proficiencyWithoutLevel });
}

/** Users with limited permission can loot a dead NPC */
override canUserModify(user: User, action: UserAction): boolean {
if (action === "update" && this.isLootable) {
Expand Down Expand Up @@ -127,9 +132,6 @@ class NPCPF2e<TParent extends TokenDocumentPF2e | null = TokenDocumentPF2e | nul
details.alliance = this.hasPlayerOwner ? "party" : "opposition";
}

const proficiencyWithoutLevel = game.settings.get("pf2e", "proficiencyVariant") === "ProficiencyWithoutLevel";
details.identification = identifyCreature(this, { proficiencyWithoutLevel });

// Ensure undead have negative healing
attributes.hp.negativeHealing = systemData.traits.value.includes("undead");

Expand All @@ -143,6 +145,8 @@ class NPCPF2e<TParent extends TokenDocumentPF2e | null = TokenDocumentPF2e | nul
this.rollOptions.all[`self:level:${level.value}`] = true;

attributes.classDC = ((): { value: number } => {
const proficiencyWithoutLevel =
game.settings.get("pf2e", "proficiencyVariant") === "ProficiencyWithoutLevel";
const levelBasedDC = calculateDC(level.base, { proficiencyWithoutLevel, rarity: this.rarity });
const adjusted = this.isElite ? levelBasedDC + 2 : this.isWeak ? levelBasedDC - 2 : levelBasedDC;
return { value: adjusted };
Expand Down
95 changes: 50 additions & 45 deletions src/module/actor/npc/sheet.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import { NPCPF2e } from "@actor";
import { Abilities, AbilityData, SkillAbbreviation } from "@actor/creature/data.ts";
import { CreatureSheetPF2e } from "@actor/creature/sheet.ts";
import { CreatureSheetData } from "@actor/creature/types.ts";
import { ALIGNMENT_TRAITS } from "@actor/creature/values.ts";
import { NPCPF2e } from "@actor";
import { NPCSkillsEditor } from "@actor/npc/skills-editor.ts";
import { RecallKnowledgePopup } from "@actor/sheet/popups/recall-knowledge-popup.ts";
import { AbilityString } from "@actor/types.ts";
import { ABILITY_ABBREVIATIONS, SAVE_TYPES, SKILL_DICTIONARY } from "@actor/values.ts";
import { Size } from "@module/data.ts";
import { createTagifyTraits } from "@module/sheet/helpers.ts";
import { DicePF2e } from "@scripts/dice.ts";
import { eventToRollParams } from "@scripts/sheet-util.ts";
import { getActionGlyph, getActionIcon, objectHasKey, setHasElement, tagify } from "@util";
import { RecallKnowledgePopup } from "../sheet/popups/recall-knowledge-popup.ts";
import {
ErrorPF2e,
getActionGlyph,
getActionIcon,
htmlQuery,
htmlQueryAll,
localizeList,
objectHasKey,
setHasElement,
tagify,
} from "@util";
import { NPCConfig } from "./config.ts";
import { NPCSkillData } from "./data.ts";
import {
NPCActionSheetData,
NPCIdentificationSheetData,
NPCSheetData,
NPCSkillSheetData,
NPCSpellcastingSheetData,
Expand Down Expand Up @@ -72,7 +82,6 @@ class NPCSheetPF2e<TActor extends NPCPF2e> extends CreatureSheetPF2e<TActor> {
*/
override async prepareItems(sheetData: NPCSheetData<TActor>): Promise<void> {
this.#prepareAbilities(sheetData.data.abilities);
this.#prepareSize(sheetData.data);
this.#prepareAlignment(sheetData.data);
this.#prepareSkills(sheetData.data);
this.#prepareSaves(sheetData.data);
Expand All @@ -94,25 +103,29 @@ class NPCSheetPF2e<TActor extends NPCPF2e> extends CreatureSheetPF2e<TActor> {
const actorTraits = sheetData.data.traits;
actorTraits.value = actorTraits.value.filter((t: string) => !alignmentTraits.has(t));

// recall knowledge DCs
const identifyCreatureData = (sheetData.identifyCreatureData = sheetData.data.details.identification);
sheetData.identifySkillDC = identifyCreatureData.skill.dc;
sheetData.identifySkillAdjustment = CONFIG.PF2E.dcAdjustments[identifyCreatureData.skill.start];
sheetData.identifySkillProgression = identifyCreatureData.skill.progression.join("/");
sheetData.identificationSkills = Array.from(sheetData.identifyCreatureData.skills)
.sort()
.map((skill) => CONFIG.PF2E.skillList[skill]);

sheetData.specificLoreDC = identifyCreatureData.specificLoreDC.dc;
sheetData.specificLoreAdjustment = CONFIG.PF2E.dcAdjustments[identifyCreatureData.specificLoreDC.start];
sheetData.specificLoreProgression = identifyCreatureData.specificLoreDC.progression.join("/");

sheetData.unspecificLoreDC = identifyCreatureData.unspecificLoreDC.dc;
sheetData.unspecificLoreAdjustment = CONFIG.PF2E.dcAdjustments[identifyCreatureData.unspecificLoreDC.start];
sheetData.unspecificLoreProgression = identifyCreatureData.unspecificLoreDC.progression.join("/");

sheetData.isNotCommon = sheetData.data.traits.rarity !== "common";
sheetData.actorSize = CONFIG.PF2E.actorSizes[sheetData.data.traits.size.value as Size];
// Identification DCs
sheetData.identificationDCs = ((): NPCIdentificationSheetData => {
const data = this.actor.identificationDCs;
const skills =
data.skills.length > 0
? localizeList(data.skills.map((s) => game.i18n.localize(CONFIG.PF2E.skillList[s])))
: null;
return {
standard: skills
? game.i18n.format("PF2E.Actor.NPC.Identification.Skills.Label", {
skills,
dc: data.standard.dc,
adjustment: game.i18n.localize(CONFIG.PF2E.dcAdjustments[data.standard.start]),
})
: null,
lore: game.i18n.format("PF2E.Actor.NPC.Identification.Lore.Label", {
dc1: data.lore[0].dc,
adjustment1: game.i18n.localize(CONFIG.PF2E.dcAdjustments[data.lore[0].start]),
dc2: data.lore[1].dc,
adjustment2: game.i18n.localize(CONFIG.PF2E.dcAdjustments[data.lore[1].start]),
}),
};
})();

// Shield
const { heldShield } = this.actor;
Expand Down Expand Up @@ -193,15 +206,26 @@ class NPCSheetPF2e<TActor extends NPCPF2e> extends CreatureSheetPF2e<TActor> {
tagify(traitsEl, { whitelist: CONFIG.PF2E.monsterTraits });
}

const mainPanel = htmlQuery(html, ".tab[data-tab=main]");
if (!mainPanel) throw ErrorPF2e("Unexpected failure while renderin NPC sheet");

// Creature identification
for (const identificationDC of htmlQueryAll(mainPanel, ".recall-knowledge .identification-skills")) {
$(identificationDC).tooltipster({ position: "bottom", maxWidth: 350, theme: "crb-hover" });
}

htmlQuery(mainPanel, ".recall-knowledge button.breakdown")?.addEventListener("click", () => {
new RecallKnowledgePopup({}, this.actor.identificationDCs).render(true);
});

// Subscribe to roll events
const rollables = ["a.rollable", ".rollable a", ".item-icon.rollable"].join(", ");
$html.find(rollables).on("click", (event) => this.#onClickRollable(event));

// Don't subscribe to edit buttons it the sheet is NOT editable
if (!this.options.editable) return;

$html.find(".trait-edit").on("click", (event) => this.openTagSelector(event));
$html.find(".skills-edit").on("click", () => {
htmlQuery(mainPanel, ".skills-edit")?.addEventListener("click", () => {
new NPCSkillsEditor(this.actor).render(true);
});

Expand All @@ -220,12 +244,6 @@ class NPCSheetPF2e<TActor extends NPCPF2e> extends CreatureSheetPF2e<TActor> {
.find<HTMLInputElement | HTMLSelectElement>(".attack-input, .dc-input, .ability-score select")
.on("change", (event) => this.#onChangeSpellcastingEntry(event));

$html.find(".recall-knowledge button.breakdown").on("click", (event) => {
event.preventDefault();
const identifyCreatureData = this.actor.system.details.identification;
new RecallKnowledgePopup({}, identifyCreatureData).render(true);
});

$html.find(".item-control[data-action=generate-attack]").on("click", async (event) => {
const { actor } = this;
const itemId = event.currentTarget.closest<HTMLElement>(".item")?.dataset.itemId ?? "";
Expand Down Expand Up @@ -271,14 +289,6 @@ class NPCSheetPF2e<TActor extends NPCPF2e> extends CreatureSheetPF2e<TActor> {
}
}

#prepareSize(sheetSystemData: NPCSystemSheetData): void {
const size = sheetSystemData.traits.size.value;
const localizationKey = this.#getSizeLocalizedKey(size);
const localizedName = game.i18n.localize(localizationKey);

sheetSystemData.traits.size.localizedName = localizedName;
}

#prepareAlignment(sheetSystemData: NPCSystemSheetData): void {
const alignmentCode = sheetSystemData.details.alignment.value;
const localizedName = game.i18n.localize(`PF2E.Alignment${alignmentCode}`);
Expand Down Expand Up @@ -403,11 +413,6 @@ class NPCSheetPF2e<TActor extends NPCPF2e> extends CreatureSheetPF2e<TActor> {
sheetData.actions = actions;
}

#getSizeLocalizedKey(size: string): string {
const actorSizes = CONFIG.PF2E.actorSizes;
return objectHasKey(actorSizes, size) ? actorSizes[size] : "";
}

// ROLLS

async #rollPerception(event: JQuery.ClickEvent): Promise<void> {
Expand Down
27 changes: 8 additions & 19 deletions src/module/actor/npc/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import { SaveType, SkillAbbreviation } from "@actor/types.ts";
import { ActionItemPF2e, EffectPF2e, ItemPF2e } from "@item";
import { SpellcastingSheetData } from "@item/spellcasting-entry/index.ts";
import { ZeroToFour } from "@module/data.ts";
import { IdentifyCreatureData } from "@module/recall-knowledge.ts";
import { TraitTagifyEntry } from "@module/sheet/helpers.ts";
import { NPCPF2e, NPCStrike } from "./index.ts";
import { NPCArmorClass, NPCAttributes, NPCSaveData, NPCSkillData, NPCSystemData, NPCTraitsData } from "./data.ts";
import { NPCArmorClass, NPCAttributes, NPCSaveData, NPCSkillData, NPCSystemData } from "./data.ts";

interface ActionsDetails {
label: string;
Expand Down Expand Up @@ -57,11 +56,6 @@ interface NPCSystemSheetData extends NPCSystemData {
sortedSkills: Record<SkillAbbreviation, NPCSkillSheetData>;
saves: Record<SaveType, NPCSaveData & WithAdjustments & WithRank & { labelShort?: string }>;
skills: Record<SkillAbbreviation, NPCSkillSheetData>;
traits: NPCTraitsData & {
size: {
localizedName?: string;
};
};
}

interface NPCStrikeSheetData extends NPCStrike {
Expand All @@ -82,18 +76,7 @@ interface NPCSheetData<TActor extends NPCPF2e> extends CreatureSheetData<TActor>
effectItems: EffectPF2e[];
spellcastingEntries: SpellcastingSheetData[];
orphanedSpells: boolean;
identifyCreatureData: IdentifyCreatureData;
identifySkillDC?: number;
identifySkillAdjustment?: string;
identifySkillProgression?: string;
identificationSkills?: string[];
identificationSkillList?: string;
specificLoreDC?: number;
specificLoreAdjustment?: string;
specificLoreProgression?: string;
unspecificLoreDC?: number;
unspecificLoreAdjustment?: string;
unspecificLoreProgression?: string;
identificationDCs: NPCIdentificationSheetData;
isNotCommon?: boolean;
actorSize?: string;
isWeak?: boolean;
Expand Down Expand Up @@ -132,8 +115,14 @@ type NPCSheetItemData<TItem extends ItemPF2e<NPCPF2e>> = RawObject<TItem> & {
hasAura: boolean;
};

interface NPCIdentificationSheetData {
standard: string | null;
lore: string;
}

export {
NPCActionSheetData,
NPCIdentificationSheetData,
NPCSheetData,
NPCSheetItemData,
NPCSkillSheetData,
Expand Down
2 changes: 1 addition & 1 deletion src/module/actor/sheet/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ abstract class ActorSheetPF2e<TActor extends ActorPF2e> extends ActorSheet<TActo
strike?.[method]?.({ getFormula: true, altUsage }).then((formula) => {
if (!formula) return;
button.title = formula.toString();
$(button).tooltipster({ position: "bottom", theme: "crb-hover" });
$(button).tooltipster({ position: "top", theme: "crb-hover" });
});
}

Expand Down
59 changes: 32 additions & 27 deletions src/module/actor/sheet/popups/recall-knowledge-popup.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,49 @@
import { IdentifyCreatureData } from "@module/recall-knowledge.ts";
import { padArray } from "@util";
import { CreatureIdentificationData } from "@module/recall-knowledge.ts";
import { localizeList, padArray } from "@util";

export class RecallKnowledgePopup extends Application {
static override get defaultOptions(): ApplicationOptions {
const options = super.defaultOptions;
options.id = "recall-knowledge-breakdown";
options.classes = [];
options.title = game.i18n.localize("PF2E.RecallKnowledge.BreakdownTitle");
options.template = "systems/pf2e/templates/actors/recall-knowledge.hbs";
options.width = 630;
return options;
}
#identificationData: CreatureIdentificationData;

constructor(options: Partial<ApplicationOptions>, private data: IdentifyCreatureData) {
constructor(options: Partial<ApplicationOptions>, data: CreatureIdentificationData) {
super(options);
this.#identificationData = data;
}

static override get defaultOptions(): ApplicationOptions {
return {
...super.defaultOptions,
id: "recall-knowledge-breakdown",
classes: [],
title: game.i18n.localize("PF2E.RecallKnowledge.BreakdownTitle"),
template: "systems/pf2e/templates/actors/recall-knowledge.hbs",
width: 630,
};
}

override async getData(): Promise<{
specificLoreAttempts: string[];
unspecificLoreAttempts: string[];
skills: { name: string; attempts: string[] }[];
}> {
const data = this.data;
override async getData(): Promise<PopupData> {
const identificationData = this.#identificationData;

return {
specificLoreAttempts: this.padAttempts(data.specificLoreDC.progression),
unspecificLoreAttempts: this.padAttempts(data.unspecificLoreDC.progression),
skills: Array.from(data.skills)
.sort()
.map((skill) => ({
name: CONFIG.PF2E.skillList[skill],
attempts: this.padAttempts(data.skill.progression),
})),
standard: {
label: localizeList(identificationData.skills.map((s) => game.i18n.localize(CONFIG.PF2E.skillList[s]))),
attempts: this.#padAttempts(identificationData.standard.progression),
},
loreEasy: this.#padAttempts(identificationData.lore[0].progression),
loreVeryEasy: this.#padAttempts(identificationData.lore[1].progression),
};
}

private padAttempts(attempts: number[]): string[] {
#padAttempts(attempts: number[]): string[] {
return padArray(
attempts.map((attempt) => attempt.toString()),
6,
"-"
);
}
}

interface PopupData {
standard: { label: string; attempts: string[] };
loreEasy: string[];
loreVeryEasy: string[];
}
Loading

0 comments on commit b519224

Please sign in to comment.