@optolith/entity-descriptions 0.13.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/lib/creator.d.ts +4 -11
  3. package/lib/creator.js +58 -2
  4. package/lib/entities/activatable.d.ts +5 -2
  5. package/lib/entities/activatable.js +29 -15
  6. package/lib/entities/alternativeRule.d.ts +1 -1
  7. package/lib/entities/attribute.d.ts +1 -3
  8. package/lib/entities/combatTechnique.d.ts +2 -2
  9. package/lib/entities/condition.d.ts +2 -6
  10. package/lib/entities/culture.d.ts +2 -1
  11. package/lib/entities/culture.js +3 -3
  12. package/lib/entities/curriculum.d.ts +1 -1
  13. package/lib/entities/curriculum.js +1 -1
  14. package/lib/entities/derivedCharacteristic.d.ts +1 -1
  15. package/lib/entities/disease.d.ts +1 -1
  16. package/lib/entities/elixir.d.ts +1 -1
  17. package/lib/entities/elixir.js +1 -1
  18. package/lib/entities/equipment.d.ts +4 -6
  19. package/lib/entities/equipment.js +517 -213
  20. package/lib/entities/equipmentPackage.d.ts +1 -1
  21. package/lib/entities/experienceLevel.d.ts +1 -3
  22. package/lib/entities/focusRule.d.ts +1 -1
  23. package/lib/entities/influence.d.ts +1 -1
  24. package/lib/entities/liturgicalChant.d.ts +3 -3
  25. package/lib/entities/optionalRule.d.ts +1 -3
  26. package/lib/entities/partial/adventurePointsValue.js +8 -7
  27. package/lib/entities/partial/dice.js +2 -2
  28. package/lib/entities/partial/enhancements.js +2 -2
  29. package/lib/entities/partial/herbary.js +1 -1
  30. package/lib/entities/partial/prerequisites/single/personalityTrait.js +1 -1
  31. package/lib/entities/partial/prerequisites/single/ratedSum.js +1 -1
  32. package/lib/entities/partial/profession/options/combatTechniquesForAdventurePoints.d.ts +16 -0
  33. package/lib/entities/partial/profession/options/combatTechniquesForAdventurePoints.js +43 -0
  34. package/lib/entities/partial/profession/options/numberOfSpellworksTotalingAdventurePoints.d.ts +16 -0
  35. package/lib/entities/partial/profession/options/numberOfSpellworksTotalingAdventurePoints.js +39 -0
  36. package/lib/entities/partial/profession/packages.d.ts +67 -0
  37. package/lib/entities/partial/profession/packages.js +84 -0
  38. package/lib/entities/partial/profession/variants.d.ts +21 -0
  39. package/lib/entities/partial/profession/variants.js +31 -0
  40. package/lib/entities/partial/rated/activatable/cost.js +11 -9
  41. package/lib/entities/partial/rated/activatable/effect.js +2 -2
  42. package/lib/entities/partial/reader.d.ts +25 -1
  43. package/lib/entities/partial/reader.js +20 -0
  44. package/lib/entities/partial/units/energy.js +2 -1
  45. package/lib/entities/partial/units/length.js +6 -3
  46. package/lib/entities/partial/units/simple.d.ts +55 -0
  47. package/lib/entities/partial/units/simple.js +56 -0
  48. package/lib/entities/partial/units/timeSpan.js +11 -7
  49. package/lib/entities/partial/unknown.d.ts +4 -0
  50. package/lib/entities/partial/unknown.js +4 -0
  51. package/lib/entities/personalityTrait.d.ts +1 -1
  52. package/lib/entities/poison.d.ts +1 -1
  53. package/lib/entities/poison.js +1 -1
  54. package/lib/entities/profession.d.ts +2 -1
  55. package/lib/entities/profession.js +44 -62
  56. package/lib/entities/race.d.ts +2 -2
  57. package/lib/entities/sexPractice.d.ts +1 -3
  58. package/lib/entities/skill.d.ts +1 -1
  59. package/lib/entities/spell.d.ts +17 -16
  60. package/lib/entities/state.d.ts +1 -3
  61. package/lib/entities/tradeSecret.d.ts +4 -0
  62. package/lib/entities/tradeSecret.js +77 -0
  63. package/lib/env.d.ts +9 -2
  64. package/lib/helpers/locale.d.ts +4 -0
  65. package/lib/index.d.ts +320 -293
  66. package/lib/index.js +9 -1
  67. package/lib/references/index.d.ts +2 -3
  68. package/lib/references/index.js +11 -16
  69. package/lib/references/page.d.ts +4 -2
  70. package/lib/references/page.js +6 -4
  71. package/lib/references/pageRange.d.ts +3 -3
  72. package/lib/references/pageRange.js +6 -4
  73. package/lib/tsconfig.tsbuildinfo +1 -1
  74. package/package.json +2 -2
@@ -1,17 +1,21 @@
1
1
  import { ensureNonEmpty, isNotEmpty } from "@elyukai/utils/array/nonEmpty";
2
2
  import { on } from "@elyukai/utils/function";
3
3
  import { isNotNullish } from "@elyukai/utils/nullable";
4
+ import { omitKeys, sortObjectKeysByIndex } from "@elyukai/utils/object";
4
5
  import { compareNumber } from "@elyukai/utils/ordering";
6
+ import { Reader } from "@elyukai/utils/reader";
5
7
  import { romanize } from "@elyukai/utils/roman";
6
8
  import { sign } from "@elyukai/utils/string/number";
7
9
  import { assertExhaustive } from "@elyukai/utils/typeSafety";
8
10
  import { mapNullable } from "@optolith/helpers/nullable";
9
11
  import { createEntityDescriptionCreator } from "../creator.js";
10
12
  import { renderDice, renderDiceAndFlat } from "./partial/dice.js";
11
- import { attributedName, attributedNameFromInstance } from "./partial/markdown.js";
13
+ import { attributedName, attributedNameFromInstance, attributedNameFromText, } from "./partial/markdown.js";
12
14
  import { additionFormatter, subtractionFormatter } from "./partial/mathOperation.js";
13
15
  import { parensIf } from "./partial/rated/activatable/parensIf.js";
16
+ import { attributedCustomNameR, formatNumber, getInstanceByIdR, localeSortR, nameR, sequence, translateMapR, translateR, } from "./partial/reader.js";
14
17
  import { ResponsiveTextSize } from "./partial/responsiveText.js";
18
+ import { adjustWeight, formatAdjustedWeight, formatArbitrarySilverthalers, formatArbitraryWeight, formatSilverthalers, } from "./partial/units/simple.js";
15
19
  import { formatTimeSpan } from "./partial/units/timeSpan.js";
16
20
  import { MISSING_VALUE, UNHANDLED_VALUE } from "./partial/unknown.js";
17
21
  /**
@@ -229,25 +233,22 @@ const renderRangedDamage = (translate) => (damage) => {
229
233
  return assertExhaustive(damage);
230
234
  }
231
235
  };
232
- const renderComplexity = (translate, complexity) => mapNullable(complexity, c => ({
233
- label: translate("Complexity"),
234
- value: (() => {
235
- switch (c.kind) {
236
- case "Primitive":
237
- return translate("Primitive");
238
- case "Simple":
239
- return translate("Simple");
240
- case "Complex":
241
- return `${translate("Complex")} (${translate("{$value} AP", {
242
- value: c.Complex.ap_value,
243
- })})`;
244
- case "Various":
245
- return translate("Various");
246
- default:
247
- return assertExhaustive(c);
248
- }
249
- })(),
250
- }));
236
+ const renderComplexity = (complexity) => mapNullable(complexity, c => {
237
+ switch (c.kind) {
238
+ case "Primitive":
239
+ return translateR("Primitive");
240
+ case "Simple":
241
+ return translateR("Simple");
242
+ case "Complex":
243
+ return sequence `${translateR("Complex")} (${translateR("{$value} AP", {
244
+ value: c.Complex.ap_value.toFixed(),
245
+ })})`;
246
+ case "Various":
247
+ return translateR("Various");
248
+ default:
249
+ return assertExhaustive(c);
250
+ }
251
+ }) ?? Reader.of("—");
251
252
  const renderBlessedTraditionRestriction = (translate, translateMap, localeJoin, getInstanceById, name, restriction) => {
252
253
  const getName = (traditionId) => translateMap(getInstanceById("BlessedTradition", traditionId)?.translations)?.name;
253
254
  if (restriction.isSanctifiedBy) {
@@ -335,103 +336,80 @@ const renderNote = (translate, translateMap, getInstanceById, localeJoin, melee_
335
336
  : renderMagicalTraditionRestriction(translate, translateMap, getInstanceById, localeJoin, name, restrictedTo.magicalTraditions),
336
337
  note,
337
338
  ].filter(isNotNullish))?.join("; ");
338
- const renderWeight = (translate, formatNumber, measurements, weight) => {
339
+ const renderWeightLabel = (entityName) => {
340
+ if (entityName === "Jewelry") {
341
+ return translateR("Weight (Bronze/Silver/Gold)");
342
+ }
343
+ else {
344
+ return translateR("Weight");
345
+ }
346
+ };
347
+ const renderWeightValue = (weight) => {
348
+ if (weight === undefined) {
349
+ return Reader.of("—");
350
+ }
339
351
  if (typeof weight === "number") {
340
- return {
341
- label: translate("Weight"),
342
- value: translate(".input {$value :number} {{{$value} pounds}}", {
343
- value: weight * measurements.stonesMultiplier,
344
- }),
345
- };
352
+ return formatAdjustedWeight(weight);
346
353
  }
347
354
  else {
348
- return {
349
- label: translate("Weight (Bronze/Silver/Gold)"),
350
- value: translate("{$value} pounds", {
351
- value: [weight.bronze, weight.silver, weight.gold]
352
- .map(value => formatNumber(value * measurements.stonesMultiplier))
353
- .join("/"),
354
- }),
355
- };
355
+ return Reader.traverse([weight.bronze, weight.silver, weight.gold], adjustWeight).thenW(values => formatArbitraryWeight(values.join("/")));
356
356
  }
357
357
  };
358
- const renderCost = (translate, translateMap, formatNumber, cost) => {
358
+ const renderCostLabel = (entityName) => {
359
+ if (entityName === "Jewelry") {
360
+ return translateR("Cost (Bronze/Silver/Gold)");
361
+ }
362
+ else if (entityName === "GemOrPreciousStone") {
363
+ return translateR("Cost/10 carats");
364
+ }
365
+ else {
366
+ return translateR("Cost");
367
+ }
368
+ };
369
+ const renderCost = (cost) => {
370
+ if (cost === undefined) {
371
+ return Reader.of("—");
372
+ }
359
373
  const renderBookCostVariant = (bookCostVariant) => {
360
374
  switch (bookCostVariant.kind) {
361
- case "Definite": {
362
- const translation = translateMap(bookCostVariant.Definite.translations);
363
- return (renderCost(translate, translateMap, formatNumber, bookCostVariant.Definite.cost).value +
364
- parensIf(translation?.label));
365
- }
366
- case "Indefinite": {
367
- const translation = translateMap(bookCostVariant.Indefinite.translations);
368
- return (translation?.description ?? MISSING_VALUE) + parensIf(translation?.label);
369
- }
375
+ case "Definite":
376
+ return translateMapR(bookCostVariant.Definite.translations).thenW(translation => renderCost(bookCostVariant.Definite.cost).map(value => value + parensIf(translation?.label)));
377
+ case "Indefinite":
378
+ return translateMapR(bookCostVariant.Indefinite.translations).map(translation => (translation?.description ?? MISSING_VALUE) + parensIf(translation?.label));
370
379
  default:
371
380
  return assertExhaustive(bookCostVariant);
372
381
  }
373
382
  };
374
383
  if ("bronze" in cost) {
375
- return {
376
- label: translate("Cost (Bronze/Silver/Gold)"),
377
- value: translate("{$value} silverthalers", {
378
- value: [cost.bronze, cost.silver, cost.gold].map(formatNumber).join("/"),
379
- }),
380
- };
384
+ return Reader.traverse([cost.bronze, cost.silver, cost.gold], formatNumber).thenW(values => formatArbitrarySilverthalers(values.join("/")));
381
385
  }
382
386
  else {
383
- return {
384
- label: translate("Cost"),
385
- value: (() => {
386
- switch (cost.kind) {
387
- case "Free":
388
- return translate("free");
389
- case "Various":
390
- return translate("various");
391
- case "Invaluable":
392
- return translate("invaluable");
393
- case "Fixed": {
394
- const translation = translateMap(cost.Fixed.translations);
395
- const main = translate(".input {$value :number} {{{$value} silverthalers}}", {
396
- value: cost.Fixed.value,
397
- });
398
- if (translation?.wrap_in_text === undefined) {
399
- return main;
400
- }
401
- return `${UNHANDLED_VALUE} ${translate(".input {$value :number} {{{$value} silverthalers}}", {
402
- value: cost.Fixed.value,
403
- })}`;
404
- }
405
- case "Range":
406
- return translate(".input {$from :number} .input {$to :number} {{{$from}–{$to} silverthalers}}", {
407
- from: cost.Range.from,
408
- to: cost.Range.to,
409
- });
410
- case "Single":
411
- return renderBookCostVariant(cost.Single);
412
- case "Multiple":
413
- return cost.Multiple.map(renderBookCostVariant).join(", ");
414
- default:
415
- return assertExhaustive(cost);
416
- }
417
- })(),
418
- };
387
+ switch (cost.kind) {
388
+ case "Free":
389
+ return translateR("free");
390
+ case "Various":
391
+ return translateR("various");
392
+ case "Invaluable":
393
+ return translateR("invaluable");
394
+ case "Fixed": {
395
+ return translateMapR(cost.Fixed.translations).thenW(translation => formatSilverthalers(cost.Fixed.value).thenW(main => translation?.wrap_in_text === undefined
396
+ ? Reader.of(main)
397
+ : sequence `${UNHANDLED_VALUE} ${main}`));
398
+ }
399
+ case "Range":
400
+ return translateR(".input {$from :number} .input {$to :number} {{{$from}–{$to} silverthalers}}", {
401
+ from: cost.Range.from,
402
+ to: cost.Range.to,
403
+ });
404
+ case "Single":
405
+ return renderBookCostVariant(cost.Single);
406
+ case "Multiple":
407
+ return Reader.traverse(cost.Multiple, renderBookCostVariant).map(list => list.join(", "));
408
+ default:
409
+ return assertExhaustive(cost);
410
+ }
419
411
  }
420
412
  };
421
- const renderArmorValues = (translate, translateMap, localeCompare, getInstanceById, idMap, values) => [
422
- { label: translate("Protection"), value: values.protection.toFixed() },
423
- { label: translate("Encumbrance"), value: values.encumbrance.toFixed() },
424
- {
425
- label: translate("Additional Penalties"),
426
- value: values.has_additional_penalties
427
- ? [idMap.DerivedCharacteristic.Movement, idMap.DerivedCharacteristic.Initiative]
428
- .map(dcId => `${sign(-1)} ${translateMap(getInstanceById("DerivedCharacteristic", dcId)?.translations)
429
- ?.abbreviation ?? MISSING_VALUE}`)
430
- .toSorted(localeCompare)
431
- .join(", ")
432
- : "—",
433
- },
434
- ];
435
413
  const normalizeCombatValues = (entity) => {
436
414
  switch (entity.entity) {
437
415
  case "Weapon":
@@ -620,17 +598,261 @@ const renderBookRules = (translate, rules) => {
620
598
  return assertExhaustive(rules);
621
599
  }
622
600
  };
601
+ const renderStructurePoints = (structurePoints) => structurePoints !== undefined && isNotEmpty(structurePoints)
602
+ ? structurePoints.length === 1
603
+ ? translateR(".input {$value :number} {{{$value} Structure Points}}", {
604
+ value: structurePoints[0].points,
605
+ })
606
+ : translateR("{$value} Structure Points", {
607
+ value: structurePoints.map(elem => elem.points).join("/"),
608
+ })
609
+ : Reader.of("—");
610
+ const meleeWeaponColumns = {
611
+ name: translateR("Name"),
612
+ damagePoints: translateR("DP"),
613
+ primaryAttributeDamageThreshold: translateR("P+T"),
614
+ attackParryModifier: translateR("AT/PA Mod"),
615
+ reach: translateR("RE"),
616
+ weight: translateR("Weight"),
617
+ length: translateR("Length"),
618
+ cost: translateR("Cost"),
619
+ complexity: translateR("Complexity"),
620
+ };
621
+ const rangedWeaponColumns = {
622
+ name: translateR("Name"),
623
+ damagePoints: translateR("DP"),
624
+ reloadTime: translateR("RT"),
625
+ range: translateR("RA"),
626
+ ammunition: translateR("Ammunition"),
627
+ weight: translateR("Weight"),
628
+ length: translateR("Length"),
629
+ cost: translateR("Cost"),
630
+ complexity: translateR("Complexity"),
631
+ };
632
+ const armorColumns = {
633
+ name: translateR("Name"),
634
+ protection: translateR("PRO"),
635
+ encumbrance: translateR("ENC"),
636
+ additionalPenalties: translateR("Additional Penalties"),
637
+ weight: translateR("Weight"),
638
+ cost: translateR("Cost"),
639
+ complexity: translateR("Complexity"),
640
+ };
641
+ const createMeleeWeaponTableEntry = (name, entityName, instance, instanceTranslation, combatTechniqueId, use) => {
642
+ const combatTechnique = getInstanceByIdR("CloseCombatTechnique", combatTechniqueId);
643
+ const combatTechniqueName = nameR("CloseCombatTechnique", combatTechniqueId);
644
+ const attributedCombatTechniqueName = combatTechniqueName.map(ctName => ctName === undefined
645
+ ? MISSING_VALUE
646
+ : attributedNameFromText(ctName, "equipment-table", "CloseCombatTechnique", combatTechniqueId));
647
+ return {
648
+ category: {
649
+ label: attributedCombatTechniqueName,
650
+ value: combatTechniqueName.map(ctName => `1-${ctName ?? MISSING_VALUE}`),
651
+ },
652
+ labels: sortObjectKeysByIndex({
653
+ ...meleeWeaponColumns,
654
+ weight: renderWeightLabel(entityName),
655
+ cost: renderCostLabel(entityName),
656
+ }, Object.keys(meleeWeaponColumns)),
657
+ values: {
658
+ name: Reader.of(name),
659
+ damagePoints: Reader.asks(env => renderMeleeDamage(env.translate)(use.damage)),
660
+ primaryAttributeDamageThreshold: combatTechnique.thenW(ct => Reader.asks(env => renderPrimaryAttributeAndDamageThreshold(env.translateMap, env.getInstanceById, ct, use.damage_threshold))),
661
+ attackParryModifier: Reader.of(renderAttackParryModifier(use.attackModifier, use.parryModifier)),
662
+ reach: Reader.asks(env => renderReach(env.translateMap, env.localeJoin, env.getInstanceById, use.reach)),
663
+ weight: renderWeightValue(instance.weight),
664
+ length: Reader.asks(env => renderLength(env.translate, env.measurementAdjustments, use.length)),
665
+ cost: renderCost(instance.cost),
666
+ complexity: renderComplexity(instance.complexity),
667
+ },
668
+ additionalInformation: [
669
+ instanceTranslation.note === undefined
670
+ ? undefined
671
+ : {
672
+ label: translateR("Note"),
673
+ id: "note",
674
+ value: Reader.of(instanceTranslation.note),
675
+ },
676
+ instanceTranslation.rules === undefined
677
+ ? undefined
678
+ : {
679
+ label: translateR("Rules"),
680
+ id: "rules",
681
+ value: Reader.of(instanceTranslation.rules),
682
+ },
683
+ instanceTranslation.advantage === undefined
684
+ ? undefined
685
+ : {
686
+ label: translateR("Weapon Advantage"),
687
+ id: "advantage",
688
+ value: Reader.of(instanceTranslation.advantage),
689
+ },
690
+ instanceTranslation.disadvantage === undefined
691
+ ? undefined
692
+ : {
693
+ label: translateR("Weapon Disadvantage"),
694
+ id: "disadvantage",
695
+ value: Reader.of(instanceTranslation.disadvantage),
696
+ },
697
+ ],
698
+ };
699
+ };
700
+ const createRangedWeaponTableEntry = (name, entityName, instance, instanceTranslation, combatTechniqueId, use) => {
701
+ const combatTechniqueName = nameR("RangedCombatTechnique", combatTechniqueId);
702
+ const attributedCombatTechniqueName = combatTechniqueName.map(ctName => ctName === undefined
703
+ ? MISSING_VALUE
704
+ : attributedNameFromText(ctName, "equipment-table", "RangedCombatTechnique", combatTechniqueId));
705
+ return {
706
+ category: {
707
+ label: attributedCombatTechniqueName,
708
+ value: combatTechniqueName.map(ctName => `2-${ctName ?? MISSING_VALUE}`),
709
+ },
710
+ labels: sortObjectKeysByIndex({
711
+ ...rangedWeaponColumns,
712
+ weight: renderWeightLabel(entityName),
713
+ cost: renderCostLabel(entityName),
714
+ }, Object.keys(rangedWeaponColumns)),
715
+ values: {
716
+ name: Reader.of(name),
717
+ damagePoints: Reader.asks(env => renderRangedDamage(env.translate)(use.damage)),
718
+ reloadTime: Reader.asks(env => renderReloadTime(env.translate, env.translateMap, env.format, use.reload_time)),
719
+ range: Reader.of(renderRangeBrackets(use.range)),
720
+ ammunition: Reader.asks(env => renderAmmunition(env.translateMap, env.getInstanceById, use.ammunition)),
721
+ weight: renderWeightValue(instance.weight),
722
+ length: Reader.asks(env => renderLength(env.translate, env.measurementAdjustments, use.length)),
723
+ cost: renderCost(instance.cost),
724
+ complexity: renderComplexity(instance.complexity),
725
+ },
726
+ additionalInformation: [
727
+ {
728
+ label: translateR("Note"),
729
+ id: "note",
730
+ value: Reader.asks(env => renderNote(env.translate, env.translateMap, env.getInstanceById, env.localeJoin, undefined, instance.restrictedTo, name, instanceTranslation.note)),
731
+ },
732
+ {
733
+ label: translateR("Rules"),
734
+ id: "rules",
735
+ value: Reader.of(instanceTranslation.rules),
736
+ },
737
+ {
738
+ label: translateR("Weapon Advantage"),
739
+ id: "advantage",
740
+ value: Reader.of(instanceTranslation.advantage),
741
+ },
742
+ {
743
+ label: translateR("Weapon Disadvantage"),
744
+ id: "disadvantage",
745
+ value: Reader.of(instanceTranslation.disadvantage),
746
+ },
747
+ ],
748
+ };
749
+ };
750
+ const createArmorTableEntry = (name, entityName, instance, instanceTranslation, values) => ({
751
+ labels: sortObjectKeysByIndex({ ...armorColumns, weight: renderWeightLabel(entityName), cost: renderCostLabel(entityName) }, Object.keys(armorColumns)),
752
+ values: {
753
+ name: Reader.of(name),
754
+ protection: Reader.of(values.protection.toFixed()),
755
+ encumbrance: Reader.of(values.encumbrance.toFixed()),
756
+ additionalPenalties: values.has_additional_penalties
757
+ ? Reader.asks((env) => [
758
+ env.idMap.DerivedCharacteristic.Movement,
759
+ env.idMap.DerivedCharacteristic.Initiative,
760
+ ])
761
+ .thenW(dcIds => Reader.traverse(dcIds, id => attributedCustomNameR("equipment-table", t => t.abbreviation, "DerivedCharacteristic", id).map(dcName => `${sign(-1)} ${dcName ?? MISSING_VALUE}`)))
762
+ .thenW(localeSortR)
763
+ .map(names => names.join(", "))
764
+ : Reader.of("—"),
765
+ weight: renderWeightValue(instance.weight),
766
+ cost: renderCost(instance.cost),
767
+ complexity: renderComplexity(instance.complexity),
768
+ },
769
+ additionalInformation: [
770
+ {
771
+ label: translateR("Note"),
772
+ id: "note",
773
+ value: Reader.asks(env => renderNote(env.translate, env.translateMap, env.getInstanceById, env.localeJoin, undefined, instance.restrictedTo, name, instanceTranslation.note)),
774
+ },
775
+ {
776
+ label: translateR("Rules"),
777
+ id: "rules",
778
+ value: Reader.of(instanceTranslation.rules),
779
+ },
780
+ {
781
+ label: translateR("Armor Advantage"),
782
+ id: "advantage",
783
+ value: Reader.of(instanceTranslation.advantage),
784
+ },
785
+ {
786
+ label: translateR("Armor Disadvantage"),
787
+ id: "disadvantage",
788
+ value: Reader.of(instanceTranslation.disadvantage),
789
+ },
790
+ ],
791
+ });
792
+ const createGemOrPreciousStoneTableEntry = (name, instance, instanceTranslation) => ({
793
+ labels: {
794
+ name: translateR("Name"),
795
+ color: translateR("Color"),
796
+ cost: renderCostLabel("GemOrPreciousStone"),
797
+ },
798
+ values: {
799
+ name: Reader.of(name),
800
+ color: Reader.of(instanceTranslation?.color ?? "—"),
801
+ cost: renderCost(instance.cost),
802
+ },
803
+ additionalInformation: [
804
+ {
805
+ label: translateR("Note"),
806
+ id: "note",
807
+ value: Reader.asks(env => renderNote(env.translate, env.translateMap, env.getInstanceById, env.localeJoin, undefined, undefined, name, instanceTranslation?.note)),
808
+ },
809
+ {
810
+ label: translateR("Rules"),
811
+ id: "rules",
812
+ value: Reader.of(instanceTranslation?.rules),
813
+ },
814
+ ],
815
+ });
816
+ const allSimpleKeys = ["name", "weight", "structurePoints", "cost", "complexity"];
817
+ const createSimpleTableEntry = (name, entityName, useKeys, instance, instanceTranslation) => ({
818
+ labels: omitKeys({
819
+ name: translateR("Name"),
820
+ weight: renderWeightLabel(entityName),
821
+ structurePoints: translateR("Structure Points"),
822
+ cost: renderCostLabel(entityName),
823
+ complexity: translateR("Complexity"),
824
+ }, ...allSimpleKeys.filter(key => !(key in useKeys))),
825
+ values: omitKeys({
826
+ name: Reader.of(name),
827
+ structurePoints: renderStructurePoints(instance.structure_points),
828
+ weight: renderWeightValue(instance.weight),
829
+ cost: renderCost(instance.cost),
830
+ complexity: renderComplexity(instance.complexity),
831
+ }, ...allSimpleKeys.filter(key => !(key in useKeys))),
832
+ additionalInformation: [
833
+ {
834
+ label: translateR("Note"),
835
+ id: "note",
836
+ value: Reader.asks(env => renderNote(env.translate, env.translateMap, env.getInstanceById, env.localeJoin, undefined, instance.restrictedTo, name, instanceTranslation?.note)),
837
+ },
838
+ {
839
+ label: translateR("Rules"),
840
+ id: "rules",
841
+ value: Reader.of(instanceTranslation?.rules),
842
+ },
843
+ ],
844
+ });
623
845
  /**
624
846
  * Get a JSON representation of the rules text for equipment.
625
847
  */
626
- export const getEquipmentEntityDescription = createEntityDescriptionCreator(({ getInstanceById, idMap }, locale, entry) => {
627
- const { translate, translateMap, format } = locale;
848
+ export const getEquipmentEntityDescription = createEntityDescriptionCreator((env, locale, entry) => {
849
+ const { translate, translateMap } = locale;
850
+ const name = getEquipmentName(translate, translateMap, env.getInstanceById, entry);
628
851
  if (entry.entity === "Book") {
629
852
  const translation = translateMap(entry.content.translations);
630
853
  if (translation === undefined) {
631
854
  return undefined;
632
855
  }
633
- const name = getEquipmentName(translate, translateMap, getInstanceById, entry);
634
856
  return {
635
857
  title: name,
636
858
  className: "equipment",
@@ -644,7 +866,7 @@ export const getEquipmentEntityDescription = createEntityDescriptionCreator(({ g
644
866
  },
645
867
  {
646
868
  label: translate("Type"),
647
- value: renderBookTypes(translate, translateMap, locale.join, locale.compare, getInstanceById, entry.content.types),
869
+ value: renderBookTypes(translate, translateMap, locale.join, locale.compare, env.getInstanceById, entry.content.types),
648
870
  },
649
871
  translation.language !== undefined || translation.script !== undefined
650
872
  ? {
@@ -674,7 +896,10 @@ export const getEquipmentEntityDescription = createEntityDescriptionCreator(({ g
674
896
  }
675
897
  })(),
676
898
  },
677
- mapNullable(entry.content.cost, cost => renderCost(translate, translateMap, locale.formatNumber, cost)),
899
+ mapNullable(entry.content.cost, cost => ({
900
+ label: translate("Cost"),
901
+ value: renderCost(cost).run(env),
902
+ })),
678
903
  translation.note === undefined
679
904
  ? undefined
680
905
  : {
@@ -718,118 +943,197 @@ export const getEquipmentEntityDescription = createEntityDescriptionCreator(({ g
718
943
  }
719
944
  const baseItem = entry.content;
720
945
  const baseItemTranslation = translation;
721
- const name = getEquipmentName(translate, translateMap, getInstanceById, entry);
722
946
  const combatValues = normalizeCombatValues(entry);
723
947
  const combatTranslation = translateMap(combatValues?.values.translations);
724
- return {
948
+ const baseProperties = {
949
+ type: "tabular",
725
950
  title: name,
726
951
  className: "equipment",
727
- body: [
728
- ...(combatValues?.type === "Weapon"
729
- ? Object.entries(combatValues.values.melee_uses ?? {}).map(([combatTechniqueId, use]) => renderMeleeWeapon(translate, translateMap, locale.join, getInstanceById, locale.measurementAdjustments, renderMeleeDamage(translate), combatTechniqueId, use))
730
- : []),
731
- ...(combatValues?.type === "Weapon"
732
- ? Object.entries(combatValues.values.ranged_uses ?? {}).map(([combatTechniqueId, use]) => renderRangedWeapon(translate, translateMap, format, getInstanceById, locale.measurementAdjustments, renderRangedDamage(translate), combatTechniqueId, use))
733
- : []),
734
- {
735
- type: "definitionList",
736
- items: [
737
- renderComplexity(translate, baseItem.complexity),
738
- ...(combatValues?.type === "Armor"
739
- ? renderArmorValues(translate, translateMap, locale.compare, getInstanceById, idMap, combatValues.values)
740
- : []),
741
- mapNullable(baseItem.burning_time, burningTime => ({
742
- label: translate("Burning Time"),
743
- value: burningTime.kind === "Unlimited"
744
- ? translate("unlimited")
745
- : formatTimeSpan(translate, translateMap, format, ResponsiveTextSize.Full, burningTime.Limited.unit, burningTime.Limited.value),
746
- })),
747
- mapNullable(baseItemTranslation?.color, color => ({
748
- label: translate("Color"),
749
- value: color,
750
- })),
751
- baseItem.structure_points !== undefined && isNotEmpty(baseItem.structure_points)
752
- ? {
753
- label: translate("Structure Points"),
754
- value: baseItem.structure_points.length === 1
755
- ? translate(".input {$value :number} {{{$value} Structure Points}}", {
756
- value: baseItem.structure_points[0].points,
757
- })
758
- : translate("{$value} Structure Points", {
759
- value: baseItem.structure_points.map(elem => elem.points).join("/"),
760
- }),
761
- }
762
- : undefined,
763
- mapNullable(baseItem.weight, weight => renderWeight(translate, locale.formatNumber, locale.measurementAdjustments, weight)),
764
- mapNullable(baseItem.cost, cost => renderCost(translate, translateMap, locale.formatNumber, cost)),
765
- mapNullable(renderNote(translate, translateMap, getInstanceById, locale.join, combatValues?.type === "Weapon" ? combatValues.values.melee_uses : undefined, combatValues?.values.restrictedTo, name, baseItemTranslation?.note), note => ({
766
- label: translate("Note"),
767
- value: note,
768
- })),
769
- baseItemTranslation?.rules !== undefined
770
- ? {
771
- label: translate("Rules"),
772
- value: baseItemTranslation.rules,
773
- }
774
- : undefined,
775
- combatValues?.type === "Weapon" && combatTranslation?.advantage !== undefined
776
- ? {
777
- label: translate("Weapon Advantage"),
778
- value: combatTranslation.advantage,
779
- }
780
- : undefined,
781
- combatValues?.type === "Weapon" && combatTranslation?.disadvantage !== undefined
782
- ? {
783
- label: translate("Weapon Disadvantage"),
784
- value: combatTranslation.disadvantage,
785
- }
786
- : undefined,
787
- combatValues?.type === "Armor" && combatTranslation?.advantage !== undefined
788
- ? {
789
- label: translate("Armor Advantage"),
790
- value: combatTranslation.advantage,
791
- }
792
- : undefined,
793
- combatValues?.type === "Armor" && combatTranslation?.disadvantage !== undefined
794
- ? {
795
- label: translate("Armor Disadvantage"),
796
- value: combatTranslation.disadvantage,
797
- }
798
- : undefined,
799
- baseItemTranslation?.placeOfPublication !== undefined
800
- ? {
801
- label: translate("Place of Publication"),
802
- value: baseItemTranslation.placeOfPublication,
803
- }
804
- : undefined,
805
- baseItemTranslation?.topics !== undefined
806
- ? {
807
- label: translate("Topics"),
808
- value: baseItemTranslation.topics.toSorted(locale.compare).join(", "),
809
- }
810
- : undefined,
811
- baseItemTranslation?.appearance !== undefined
812
- ? {
813
- label: translate("Appearance"),
814
- value: baseItemTranslation.appearance,
815
- }
816
- : undefined,
817
- baseItemTranslation?.components !== undefined
818
- ? {
819
- label: translate("Components"),
820
- value: baseItemTranslation.components,
821
- }
822
- : undefined,
823
- baseItemTranslation?.use !== undefined
824
- ? {
825
- label: translate("Use"),
826
- value: baseItemTranslation.use,
827
- }
828
- : undefined,
829
- ],
830
- },
831
- ],
832
952
  errata: baseItemTranslation?.errata,
833
953
  references: entry.content.src,
834
954
  };
955
+ return [
956
+ ...(baseItemTranslation !== undefined && combatValues?.type === "Weapon"
957
+ ? Object.entries(combatValues.values.melee_uses ?? {}).map(([combatTechniqueId, use]) => ({
958
+ ...baseProperties,
959
+ ...createMeleeWeaponTableEntry(name, entry.entity, baseItem, { ...combatTranslation, ...baseItemTranslation }, combatTechniqueId, use),
960
+ }))
961
+ : []),
962
+ ...(baseItemTranslation !== undefined && combatValues?.type === "Weapon"
963
+ ? Object.entries(combatValues.values.ranged_uses ?? {}).map(([combatTechniqueId, use]) => ({
964
+ ...baseProperties,
965
+ ...createRangedWeaponTableEntry(name, entry.entity, baseItem, { ...combatTranslation, ...baseItemTranslation }, combatTechniqueId, use),
966
+ }))
967
+ : []),
968
+ ...(baseItemTranslation !== undefined && combatValues?.type === "Armor"
969
+ ? [
970
+ {
971
+ ...baseProperties,
972
+ ...createArmorTableEntry(name, entry.entity, baseItem, { ...combatTranslation, ...baseItemTranslation }, combatValues.values),
973
+ },
974
+ ]
975
+ : []),
976
+ ...(entry.entity === "Weapon" || entry.entity === "Armor"
977
+ ? []
978
+ : entry.entity === "GemOrPreciousStone"
979
+ ? [
980
+ {
981
+ ...baseProperties,
982
+ ...createGemOrPreciousStoneTableEntry(name, entry.content, translateMap(entry.content.translations)),
983
+ },
984
+ ]
985
+ : entry.entity === "MusicalInstrument" ||
986
+ entry.entity === "Animal" ||
987
+ entry.entity === "AnimalCare" ||
988
+ entry.entity === "Vehicle"
989
+ ? [
990
+ {
991
+ ...baseProperties,
992
+ ...createSimpleTableEntry(name, entry.entity, {
993
+ name: null,
994
+ weight: null,
995
+ cost: null,
996
+ }, baseItem, baseItemTranslation),
997
+ },
998
+ ]
999
+ : [
1000
+ {
1001
+ ...baseProperties,
1002
+ ...createSimpleTableEntry(name, entry.entity, {
1003
+ name: null,
1004
+ weight: null,
1005
+ structurePoints: null,
1006
+ cost: null,
1007
+ complexity: null,
1008
+ }, baseItem, baseItemTranslation),
1009
+ },
1010
+ ]),
1011
+ // {
1012
+ // title: name,
1013
+ // className: "equipment",
1014
+ // body: [
1015
+ // {
1016
+ // type: "definitionList",
1017
+ // items: [
1018
+ // renderComplexity(translate, baseItem.complexity),
1019
+ // mapNullable(baseItem.burning_time, burningTime => ({
1020
+ // label: translate("Burning Time"),
1021
+ // value:
1022
+ // burningTime.kind === "Unlimited"
1023
+ // ? translate("unlimited")
1024
+ // : formatTimeSpan(
1025
+ // translate,
1026
+ // translateMap,
1027
+ // format,
1028
+ // ResponsiveTextSize.Full,
1029
+ // burningTime.Limited.unit,
1030
+ // burningTime.Limited.value,
1031
+ // ),
1032
+ // })),
1033
+ // mapNullable(baseItemTranslation?.color, color => ({
1034
+ // label: translate("Color"),
1035
+ // value: color,
1036
+ // })),
1037
+ // baseItem.structure_points !== undefined && isNotEmpty(baseItem.structure_points)
1038
+ // ? {
1039
+ // label: translate("Structure Points"),
1040
+ // value:
1041
+ // baseItem.structure_points.length === 1
1042
+ // ? translate(".input {$value :number} {{{$value} Structure Points}}", {
1043
+ // value: baseItem.structure_points[0].points,
1044
+ // })
1045
+ // : translate("{$value} Structure Points", {
1046
+ // value: baseItem.structure_points.map(elem => elem.points).join("/"),
1047
+ // }),
1048
+ // }
1049
+ // : undefined,
1050
+ // mapNullable(baseItem.weight, weight =>
1051
+ // renderWeight(translate, locale.formatNumber, locale.measurementAdjustments, weight),
1052
+ // ),
1053
+ // mapNullable(baseItem.cost, cost =>
1054
+ // renderCost(translate, translateMap, locale.formatNumber, cost),
1055
+ // ),
1056
+ // mapNullable(
1057
+ // renderNote(
1058
+ // translate,
1059
+ // translateMap,
1060
+ // getInstanceById,
1061
+ // locale.join,
1062
+ // combatValues?.type === "Weapon" ? combatValues.values.melee_uses : undefined,
1063
+ // combatValues?.values.restrictedTo,
1064
+ // name,
1065
+ // baseItemTranslation?.note,
1066
+ // ),
1067
+ // note => ({
1068
+ // label: translate("Note"),
1069
+ // value: note,
1070
+ // }),
1071
+ // ),
1072
+ // baseItemTranslation?.rules !== undefined
1073
+ // ? {
1074
+ // label: translate("Rules"),
1075
+ // value: baseItemTranslation.rules,
1076
+ // }
1077
+ // : undefined,
1078
+ // combatValues?.type === "Weapon" && combatTranslation?.advantage !== undefined
1079
+ // ? {
1080
+ // label: translate("Weapon Advantage"),
1081
+ // value: combatTranslation.advantage,
1082
+ // }
1083
+ // : undefined,
1084
+ // combatValues?.type === "Weapon" && combatTranslation?.disadvantage !== undefined
1085
+ // ? {
1086
+ // label: translate("Weapon Disadvantage"),
1087
+ // value: combatTranslation.disadvantage,
1088
+ // }
1089
+ // : undefined,
1090
+ // combatValues?.type === "Armor" && combatTranslation?.advantage !== undefined
1091
+ // ? {
1092
+ // label: translate("Armor Advantage"),
1093
+ // value: combatTranslation.advantage,
1094
+ // }
1095
+ // : undefined,
1096
+ // combatValues?.type === "Armor" && combatTranslation?.disadvantage !== undefined
1097
+ // ? {
1098
+ // label: translate("Armor Disadvantage"),
1099
+ // value: combatTranslation.disadvantage,
1100
+ // }
1101
+ // : undefined,
1102
+ // baseItemTranslation?.placeOfPublication !== undefined
1103
+ // ? {
1104
+ // label: translate("Place of Publication"),
1105
+ // value: baseItemTranslation.placeOfPublication,
1106
+ // }
1107
+ // : undefined,
1108
+ // baseItemTranslation?.topics !== undefined
1109
+ // ? {
1110
+ // label: translate("Topics"),
1111
+ // value: baseItemTranslation.topics.toSorted(locale.compare).join(", "),
1112
+ // }
1113
+ // : undefined,
1114
+ // baseItemTranslation?.appearance !== undefined
1115
+ // ? {
1116
+ // label: translate("Appearance"),
1117
+ // value: baseItemTranslation.appearance,
1118
+ // }
1119
+ // : undefined,
1120
+ // baseItemTranslation?.components !== undefined
1121
+ // ? {
1122
+ // label: translate("Components"),
1123
+ // value: baseItemTranslation.components,
1124
+ // }
1125
+ // : undefined,
1126
+ // baseItemTranslation?.use !== undefined
1127
+ // ? {
1128
+ // label: translate("Use"),
1129
+ // value: baseItemTranslation.use,
1130
+ // }
1131
+ // : undefined,
1132
+ // ],
1133
+ // },
1134
+ // ] as (RawEntityDescriptionSection | undefined)[],
1135
+ // errata: baseItemTranslation?.errata,
1136
+ // references: entry.content.src,
1137
+ // },
1138
+ ];
835
1139
  });