Files
fvtt-machine-gods-noxian-ex…/module/config/system.mjs
T
uberwald 12bf771e9d
Release Creation / build (release) Successful in 44s
Fixes around character sheets and buttons
2026-05-19 13:30:05 +02:00

302 lines
15 KiB
JavaScript

export const SYSTEM_ID = "fvtt-machine-gods-noxian-expanse"
export const ASCII = `
Machine Gods of the Noxian Expanse
`
const dieChoiceLabels = values => Object.fromEntries(values.map(value => [value, value === "depleted" ? "Depleted" : value.toUpperCase()]))
const keyedNameChoices = (items, key) => Object.fromEntries(items.map(item => [item[key], item.name]))
const toIconSlug = name => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")
const itemIcon = slug => `systems/${SYSTEM_ID}/assets/icons/${slug}.svg`
const withItemIcons = items => items.map(item => ({ ...item, img: itemIcon(toIconSlug(item.name)) }))
let localized = false
function localizeEntryLabels(entries, prefix) {
for (const [key, entry] of Object.entries(entries)) {
entry.label = game.i18n.localize(`${prefix}.${key}`)
}
}
function localizeChoiceLabels(entries, prefix) {
for (const key of Object.keys(entries)) {
entries[key] = game.i18n.localize(`${prefix}.${key}`)
}
}
export const SYSTEM = {
id: SYSTEM_ID,
actorTypes: {
character: { id: "character", label: "Character" },
creature: { id: "creature", label: "Creature" },
companion: { id: "companion", label: "Companion" },
party: { id: "party", label: "Party" },
},
itemTypes: {
weapon: { id: "weapon", label: "Weapon", icon: itemIcon("weapon") },
armor: { id: "armor", label: "Armor", icon: itemIcon("armor") },
shield: { id: "shield", label: "Shield", icon: itemIcon("shield") },
equipment: { id: "equipment", label: "Equipment", icon: itemIcon("equipment") },
"resonance-core": { id: "resonance-core", label: "Resonance Core", icon: itemIcon("resonance-core") },
artifact: { id: "artifact", label: "Artifact", icon: itemIcon("artifact") },
feature: { id: "feature", label: "Feature", icon: itemIcon("feature") },
"creature-trait": { id: "creature-trait", label: "Creature Trait", icon: itemIcon("creature-trait") },
},
abilities: {
agility: { id: "agility", label: "Agility" },
presence: { id: "presence", label: "Presence" },
strength: { id: "strength", label: "Strength" },
toughness: { id: "toughness", label: "Toughness" },
},
abilityOrder: ["agility", "presence", "strength", "toughness"],
conditions: {
bleeding: { id: "bleeding", label: "Bleeding", hasValue: true },
blinded: { id: "blinded", label: "Blinded" },
burning: { id: "burning", label: "Burning" },
fatigued: { id: "fatigued", label: "Fatigued", hasValue: true },
infected: { id: "infected", label: "Infected" },
poisoned: { id: "poisoned", label: "Poisoned" },
prone: { id: "prone", label: "Prone" },
restrained: { id: "restrained", label: "Restrained" },
starved: { id: "starved", label: "Starved" },
stunned: { id: "stunned", label: "Stunned", hasValue: true },
},
usageDice: ["d12", "d10", "d8", "d6", "d4", "depleted"],
usageDieChoices: dieChoiceLabels(["d12", "d10", "d8", "d6", "d4", "depleted"]),
armorDice: ["d12", "d10", "d8", "d6", "d4", "d2", "0"],
armorDieChoices: dieChoiceLabels(["d12", "d10", "d8", "d6", "d4", "d2", "0"]),
omenDice: ["d2", "d4", "d6", "d8"],
omenDieChoices: dieChoiceLabels(["d2", "d4", "d6", "d8"]),
weightCategories: {
trivial: "Trivial",
light: "Light",
normal: "Normal",
heavy: "Heavy",
},
weaponCategories: {
melee: "Melee",
ranged: "Ranged",
},
weaponProperties: {
ammo: { label: "Ammo", hint: "Requires ammunition. Improvised in melee or without ammo." },
awkward: { label: "Awkward", hint: "+1 DR to attacks." },
binding: { label: "Binding", hint: "Beat DR by 4+ on same-size or smaller target: inflict Restrained." },
durant: { label: "Durant", hint: "Durability die is D8." },
finesse: { label: "Finesse", hint: "Attacks can use either Presence or Strength." },
fling: { label: "Fling", hint: "Can make ranged attacks. Roll D4 Usage Die to avoid losing it." },
fragile: { label: "Fragile", hint: "Durability die is D4." },
glinting: { label: "Glinting", hint: "After attacking, next attack vs same target this combat gains -1 DR." },
overbearing:{ label: "Overbearing",hint: "+2 damage." },
parrying: { label: "Parrying", hint: "-1 DR to avoiding melee attacks while wielding." },
precise: { label: "Precise", hint: "-1 DR to hit." },
razored: { label: "Razored", hint: "Beat DR by 4+: inflict Bleeding (1)." },
ringing: { label: "Ringing", hint: "Beat DR by 4+ and target not Stunned: inflict Stunned (1)." },
"two-handed":{ label: "Two-Handed",hint: "Requires both hands." },
unwieldy: { label: "Unwieldy", hint: "Cannot attack more than once per Round." },
versatile: { label: "Versatile", hint: "Two-handed: -1 DR to hit and +1 damage." },
},
get weaponPropertyLabels() {
return Object.fromEntries(Object.entries(this.weaponProperties).map(([k, v]) => [k, v.label]))
},
get weaponPropertyHints() {
return Object.fromEntries(Object.entries(this.weaponProperties).map(([k, v]) => [k, v.hint]))
},
resonanceList: {
accelerate: "Accelerate",
blast: "Blast",
"breathe-water": "Breathe Water",
cauterize: "Cauterize",
"create-illusion": "Create Illusion",
distract: "Distract",
"eagle-eye": "Eagle Eye",
"empower-weapon": "Empower Weapon",
fireball: "Fireball",
hover: "Hover",
"influence-mind": "Influence Mind",
"knit-flesh": "Knit Flesh",
"light-construct": "Light Construct",
mirage: "Mirage",
"negate-injury": "Negate Injury",
paralyze: "Paralyze",
shield: "Shield",
shock: "Shock",
shroud: "Shroud",
"summon-mist": "Summon Mist",
},
starterWeapons: withItemIcons([
{ name: "Club", damage: "1d4", category: "melee", range: "Touch" },
{ name: "Dagger", damage: "1d4", category: "melee", range: "Touch" },
{ name: "Handaxe", damage: "1d6", category: "melee", range: "Touch" },
{ name: "Quarterstaff", damage: "1d4", category: "melee", range: "Touch" },
{ name: "Whip", damage: "1d4", category: "melee", range: "Near" },
{ name: "Shortbow", damage: "1d6", category: "ranged", range: "Near/Far" },
{ name: "Spear", damage: "1d6", category: "melee", range: "Near" },
{ name: "Longsword", damage: "1d8", category: "melee", range: "Touch" },
{ name: "Heavy Crossbow", damage: "1d10", category: "ranged", range: "Far" },
{ name: "Rapier", damage: "1d6", category: "melee", range: "Touch" },
{ name: "Halberd", damage: "1d10", category: "melee", range: "Near" },
{ name: "Maul", damage: "1d10", category: "melee", range: "Touch" },
]),
starterArmor: withItemIcons([
{ name: "Clothing (Average)", type: "armor", armorDie: "d2", penalty: 0 },
{ name: "Helm", type: "armor", armorDie: "d2", penalty: 0 },
{ name: "Medium Shield", type: "shield", armorDie: "d4", penalty: 0 },
{ name: "Gambeson", type: "armor", armorDie: "d4", penalty: 0 },
{ name: "Padded Leather", type: "armor", armorDie: "d4", penalty: 1 },
{ name: "Chain Shirt", type: "armor", armorDie: "d6", penalty: 1 },
{ name: "Half Plate", type: "armor", armorDie: "d8", penalty: 2 },
{ name: "Full Plate", type: "armor", armorDie: "d10", penalty: 2 },
]),
starterEquipment: withItemIcons([
{ name: "Rations", type: "equipment", subtype: "consumable", quantity: 1, usageDie: "d8", consumable: true },
{ name: "Waterskin", type: "equipment", subtype: "travel", quantity: 1, usageDie: "d8", consumable: true },
{ name: "Sack", type: "equipment", subtype: "container", quantity: 1, usageDie: "depleted", consumable: false },
{ name: "Backpack", type: "equipment", subtype: "container", quantity: 1, usageDie: "depleted", consumable: false },
{ name: "Rope", type: "equipment", subtype: "tool", quantity: 1, usageDie: "depleted", consumable: false },
{ name: "Medical Supplies", type: "equipment", subtype: "tool", quantity: 1, usageDie: "d6", consumable: true },
]),
equipmentSubtypes: {
gear: "Gear",
consumable: "Consumable",
travel: "Travel",
container: "Container",
tool: "Tool",
},
starterCores: withItemIcons([
{ name: "Accelerate Core", type: "resonance-core", resonationId: "accelerate", usageDie: "d6" },
{ name: "Blast Core", type: "resonance-core", resonationId: "blast", usageDie: "d6" },
{ name: "Cauterize Core", type: "resonance-core", resonationId: "cauterize", usageDie: "d6" },
{ name: "Empower Weapon Core", type: "resonance-core", resonationId: "empower-weapon", usageDie: "d6" },
]),
sampleArtifacts: withItemIcons([
{ name: "Shiver Lens", type: "artifact", artifactId: "shiver-lens", usageDie: "d6", description: "A cracked lens that reveals hidden heat signatures." },
{ name: "Processional Halo", type: "artifact", artifactId: "processional-halo", usageDie: "d6", description: "A floating crown of light that amplifies solemn commands." },
{ name: "Null Forge Spike", type: "artifact", artifactId: "null-forge-spike", usageDie: "d4", description: "A ritual spike used to jam malfunctioning relics into silence." },
{ name: "Lux Relay Idol", type: "artifact", artifactId: "lux-relay-idol", usageDie: "d6", description: "A palm-sized relay that stores and redirects ambient luminance." },
]),
artifactChoices: keyedNameChoices([
{ name: "Shiver Lens", artifactId: "shiver-lens" },
{ name: "Processional Halo", artifactId: "processional-halo" },
{ name: "Null Forge Spike", artifactId: "null-forge-spike" },
{ name: "Lux Relay Idol", artifactId: "lux-relay-idol" },
], "artifactId"),
sampleFeatures: withItemIcons([
{ name: "Akimbo Hit Priest", type: "feature", featureId: "akimbo-hit-priest", description: "Once per round after hitting with an attack, suffer Fatigued (1) to attack again." },
{ name: "Ambivalent Slouch", type: "feature", featureId: "ambivalent-slouch", description: "Gain +1 carrying capacity and reduce armor or shield Agility penalties by 1." },
{ name: "Avaricious Gubbingrifter", type: "feature", featureId: "avaricious-gubbingrifter", description: "Gain three random resonance cores and -1 DR to invoking resonations." },
{ name: "Backbiter", type: "feature", featureId: "backbiter", description: "When you win initiative, first-round attacks bypass 2d6 armor." },
{ name: "Chart Reading Maniac", type: "feature", featureId: "chart-reading-maniac", description: "Gain an extra exploration turn per day and reroll one exploration or ruin table per day." },
{ name: "Liturgicantal Blesswell", type: "feature", featureId: "liturgicantal-blesswell", description: "Restore d4 HP d4 times per full rest and repair one usage die by a step once per day." },
]),
featureChoices: keyedNameChoices([
{ name: "Akimbo Hit Priest", featureId: "akimbo-hit-priest" },
{ name: "Ambivalent Slouch", featureId: "ambivalent-slouch" },
{ name: "Avaricious Gubbingrifter", featureId: "avaricious-gubbingrifter" },
{ name: "Backbiter", featureId: "backbiter" },
{ name: "Chart Reading Maniac", featureId: "chart-reading-maniac" },
{ name: "Liturgicantal Blesswell", featureId: "liturgicantal-blesswell" },
], "featureId"),
companions: {
noble: {
name: "Beguiled Noble",
hp: 6,
morale: 9,
armorDie: "d4",
attackLabel: "Blade",
attackDamage: "1d6",
},
mercenary: {
name: "Dustland Mercenary",
hp: 8,
morale: 7,
armorDie: "d4",
attackLabel: "Cleaver",
attackDamage: "1d4+2",
},
pickpocket: {
name: "Scrapling Pickpocket",
hp: 3,
morale: 4,
armorDie: "0",
attackLabel: "Folding Knife",
attackDamage: "1d2",
},
cantor: {
name: "Silicon Cantor",
hp: 4,
morale: 6,
armorDie: "0",
attackLabel: "Book Thump",
attackDamage: "1",
},
},
tables: {
eucatastrophe: [
"Recover d4 HP.",
"Invoke the resonation again immediately.",
"Synchronize one additional artifact for the day.",
"Gain -1 DR to invoking resonations for five minutes.",
"Increase daily resonation uses by 2 for the day.",
"Gain +1 carrying capacity for the day.",
"Gain -4 DR to your next check within five minutes.",
"Gain +1 max HP for the day.",
"Recover one omen.",
"Borrow a random nearby feature or increase a random ability by 1 until end of day."
],
catastrophe: [
"You are reduced to 0 HP.",
"The resonation targets the wrong creature.",
"A random nearby artifact explodes.",
"The resonation is cast twice at random targets.",
"You become Restrained for five minutes.",
"You are knocked Prone and suffer Stunned (2).",
"Your worn armor is consumed.",
"Suffer Fatigued (1), doubling if repeated the same day.",
"Discard one omen.",
"All artifacts desynchronize and you cannot invoke resonations for the day."
],
triumphs: [
"Discover a useful resource.",
"Gain -3 DR to your next check within five minutes.",
"Discard a condition or recover d2 HP.",
"Grant a nearby ally -2 DR on their next check.",
"Ignore all penalties on your next check.",
"Discard Prone and Restrained, then move immediately.",
"Restore a weapon or armor usage die by one step.",
"Take an immediate action or find 3d10 coins.",
"Take an immediate action or gain -2 DR to your next check.",
"Move one point from one ability to another for one hour."
],
mishaps: [
"Downgrade involved equipment usage dice or suffer d4 damage bypassing armor.",
"A hostile random creature investigates.",
"Start an uncontrolled fire or suffer d4 damage bypassing armor.",
"Suffer Infected.",
"Suffer +1 DR on checks until you fully rest.",
"You cannot invoke resonations for d8 hours.",
"Suffer +3 DR on your next check this hour.",
"Suffer d6 damage bypassing armor, minimum 1 HP remaining.",
"Suffer Bleeding (1) in combat or d4 damage bypassing armor outside combat.",
"Roll twice on the mishaps table and take both results."
],
breakResults: {
1: "Unconscious for d4 rounds, then wake with d4 HP.",
2: "As above, and a random working limb becomes useless until treated.",
3: "As above, and without emergency treatment you die in d2 hours.",
4: "Immediate death."
}
}
}
export function localizeSystemConfig() {
if (localized) return
localized = true
localizeEntryLabels(SYSTEM.actorTypes, "MGNE.ActorTypes")
localizeEntryLabels(SYSTEM.itemTypes, "MGNE.ItemTypes")
localizeEntryLabels(SYSTEM.abilities, "MGNE.Abilities")
localizeEntryLabels(SYSTEM.conditions, "MGNE.Conditions")
localizeChoiceLabels(SYSTEM.weaponCategories, "MGNE.WeaponCategories")
localizeChoiceLabels(SYSTEM.resonanceList, "MGNE.Resonations")
localizeChoiceLabels(SYSTEM.equipmentSubtypes, "MGNE.EquipmentSubtypes")
SYSTEM.usageDieChoices.depleted = game.i18n.localize("MGNE.Common.Depleted")
}