111 lines
6.2 KiB
JavaScript
111 lines
6.2 KiB
JavaScript
const { playerHelpers, pool } = require('../mariadb');
|
||
|
||
const talentDescriptions = {
|
||
'Ambidextrous': 'Reduces the penalty for using the off-hand and supports two-weapon fighting.',
|
||
'Astartes Weapon Training': 'May use standard Space Marine weapon groups without the normal untrained penalty.',
|
||
'Bulging Biceps': 'Can fire heavy weapons more effectively and benefits from massive Astartes strength.',
|
||
'Deathwatch Training': 'Against xenos, may confirm Righteous Fury more reliably and apply Deathwatch combat doctrine.',
|
||
'Heightened Senses (Hearing, Sight)': '+10 bonus to relevant Awareness tests using hearing or sight.',
|
||
'Killing Strike': 'May spend Fate or use focused attack timing to make a melee attack harder to defend against.',
|
||
'Nerves of Steel': 'May re-roll failed tests to avoid or recover from Pinning.',
|
||
'Psy Rating 3': 'Counts as Psy Rating 3 for psychic power strength, range, and force weapon effects.',
|
||
'Quick Draw': 'Can ready a weapon as a Free Action once per round.',
|
||
'Resistance (Psychic Powers)': '+10 bonus to resist psychic powers and warp effects.',
|
||
'True Grit': 'Reduces incoming Critical Damage, making the Marine much harder to kill outright.',
|
||
'Unarmed Master': 'Unarmed attacks are dangerous Astartes attacks rather than weak improvised strikes.',
|
||
'Unnatural Strength (x2)': 'Doubles Strength Bonus for lifting, melee damage, and opposed Strength uses.',
|
||
'Unnatural Toughness (x2)': 'Doubles Toughness Bonus for resisting damage and physical punishment.',
|
||
'Bolter Mastery': 'Improves effectiveness with bolter weapons, often represented as extra damage or reliability.',
|
||
'Combat Master': 'Enemies do not gain the normal ganging-up bonus against this character in melee.',
|
||
'Melee Weapon Training': 'Can use the listed melee weapon type without the untrained penalty.',
|
||
'Pistol Training': 'Can use the listed pistol type without the untrained penalty.',
|
||
'Swift Attack': 'Can make multiple melee attacks as a Full Action.',
|
||
'Lightning Attack': 'Can make even more rapid melee attacks than Swift Attack.',
|
||
'Fearless': 'Immune to Fear and Pinning, but may be unable to retreat from threats.',
|
||
'Jaded': 'Immune or resistant to mundane horror and many shocking sights.',
|
||
'Hatred': '+10 Weapon Skill against the hated foe and must often pursue them aggressively.',
|
||
'Rapid Reload': 'Reduces reload time for ranged weapons.',
|
||
'Die Hard': 'Harder to die from blood loss and severe injury.',
|
||
'Crushing Blow': 'Adds extra melee damage from brutal force.',
|
||
'Two Weapon Wielder': 'Allows more effective fighting with two weapons when prerequisites are met.',
|
||
'Unrelenting Devastation': 'Devastator-style heavy fire discipline. When using heavy weapons against massed enemies, the Marine can turn sustained fire into extra Horde pressure or area control.',
|
||
'Air of Authority': 'Bonus/effect: On a successful Command Test, affects 1d10 + Fellowship Bonus NPC targets. May Command NPCs not under your authority at -10. No effect on hostile targets.',
|
||
'Iron Discipline': 'Followers and allies under the character’s command are steadier under fire and less likely to break, panic, or ignore orders.',
|
||
'Hatred (Xenos)': '+10 Weapon Skill against xenos enemies; the character is driven to engage hated aliens aggressively.',
|
||
'Wings of Angels': 'Assault Marine jump-pack mastery. Improves control and impact when charging, leaping, or striking from above with a jump pack.'
|
||
};
|
||
|
||
const psychicDescriptions = {
|
||
'Psy Rating 3': 'Power level 3. Increases psychic effect strength and force weapon impact.',
|
||
'Smite': 'Focused psychic attack. Use Willpower/Focus Power; on success it deals direct warp lightning damage.',
|
||
'Inspire': 'Sustained support power that strengthens allies against Fear, Pinning, and morale collapse.',
|
||
'Reading': 'Reads aura and surface impressions to reveal corruption, psychic traces, wounds, and emotional state.',
|
||
'Augury': 'Seeks omens and likely outcomes; useful for mission planning and danger prediction.',
|
||
'Avenger': 'Violent psychic flame/force attack for area damage.',
|
||
'Short Range Telepathy': 'Silent mind-to-mind communication at short range.'
|
||
};
|
||
|
||
const psychicNoteDescriptions = {
|
||
'Using psychic powers risks Psychic Phenomena depending on power level and roll.': 'When a Librarian manifests powers, some rolls can trigger Psychic Phenomena or worse warp side effects. Push powers only when the risk is worth it.',
|
||
'Force weapon damage and penetration increase by Psy Rating when wielded by this Librarian.': 'A force weapon channels the Librarian’s Psy Rating. For Malachiel, add Psy Rating 3 to relevant force weapon damage and penetration when activated as allowed by the rules.'
|
||
};
|
||
|
||
function lines(value) {
|
||
return String(value || '').split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
||
}
|
||
|
||
function nameFromLine(line) {
|
||
return String(line || '').replace(/:.*/, '').trim();
|
||
}
|
||
|
||
function descriptionsFor(value, source) {
|
||
const out = {};
|
||
for (const line of lines(value)) {
|
||
const name = nameFromLine(line);
|
||
if (source[name]) out[name] = source[name];
|
||
}
|
||
return out;
|
||
}
|
||
|
||
(async function main() {
|
||
const players = await playerHelpers.getAll();
|
||
const updated = [];
|
||
|
||
for (const player of players) {
|
||
if (player.name === 'gm') continue;
|
||
const tabInfo = player.tabInfo || {};
|
||
const nextTabInfo = {
|
||
...tabInfo,
|
||
talentDescriptions: {
|
||
...(tabInfo.talentDescriptions || {}),
|
||
...descriptionsFor(tabInfo.talents, talentDescriptions)
|
||
},
|
||
psychicDescriptions: {
|
||
...(tabInfo.psychicDescriptions || {}),
|
||
...descriptionsFor(tabInfo.psychic, { ...psychicDescriptions, ...psychicNoteDescriptions })
|
||
}
|
||
};
|
||
|
||
await playerHelpers.update(player.name, {
|
||
rollerInfo: player.rollerInfo || {},
|
||
shopInfo: player.shopInfo || {},
|
||
tabInfo: nextTabInfo,
|
||
pw: player.pw || '',
|
||
pwHash: player.pwHash || ''
|
||
});
|
||
updated.push({
|
||
name: player.name,
|
||
talents: Object.keys(nextTabInfo.talentDescriptions).length,
|
||
psychic: Object.keys(nextTabInfo.psychicDescriptions).length
|
||
});
|
||
}
|
||
|
||
console.log(JSON.stringify({ updated }, null, 2));
|
||
await new Promise(resolve => setTimeout(resolve, 250));
|
||
await pool.end();
|
||
})().catch(async error => {
|
||
console.error(error);
|
||
try { await pool.end(); } catch {}
|
||
process.exit(1);
|
||
});
|