Add rules-based squad tactics checks
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
const { missionHelpers, pool } = require('../mariadb');
|
||||
|
||||
const SQUAD_TACTICS_CHECK = {
|
||||
name: 'Squad Tactics',
|
||||
skill: 'Tactics',
|
||||
characteristic: 'Int',
|
||||
target: 45,
|
||||
modifier: 0,
|
||||
rulesNote: 'Core: Tactics is an Advanced Intelligence Skill with groups such as Assault Doctrine, Defensive Doctrine, Orbital Drop Procedures, and Recon and Stealth. It gives tactical reading/positioning; it does not grant free Cohesion or Squad Mode abilities.',
|
||||
reward: 'On success, the Kill-team identifies the best tactical approach for this scene; extra Degrees of Success may reveal a safer route, priority threat, ambush vector, or better position.',
|
||||
};
|
||||
|
||||
function hasSquadTactics(scene) {
|
||||
return (scene.checks || []).some(check => String(check.name || '').toLowerCase() === 'squad tactics');
|
||||
}
|
||||
|
||||
function addSquadTactics(scene) {
|
||||
if (hasSquadTactics(scene)) return scene;
|
||||
return {
|
||||
...scene,
|
||||
checks: [SQUAD_TACTICS_CHECK, ...(Array.isArray(scene.checks) ? scene.checks : [])],
|
||||
};
|
||||
}
|
||||
|
||||
(async function main() {
|
||||
const missions = await missionHelpers.getAll();
|
||||
const targets = missions.filter(mission => mission.name === 'Vesalius Echo: Theta-92' || mission.is_active);
|
||||
const results = [];
|
||||
|
||||
for (const mission of targets) {
|
||||
const scenes = Array.isArray(mission.scenes) ? mission.scenes : [];
|
||||
const nextScenes = scenes.map(addSquadTactics);
|
||||
const changed = JSON.stringify(scenes) !== JSON.stringify(nextScenes);
|
||||
if (!changed) {
|
||||
results.push({ id: mission.id, name: mission.name, changed: false });
|
||||
continue;
|
||||
}
|
||||
|
||||
const ok = await missionHelpers.update(mission.id, {
|
||||
name: mission.name,
|
||||
theme: mission.theme,
|
||||
sceneCount: nextScenes.length,
|
||||
enemyCount: mission.enemy_count || 0,
|
||||
threatLevel: mission.threat_level || 'Medium',
|
||||
playerCount: mission.player_count || 4,
|
||||
scenes: nextScenes,
|
||||
});
|
||||
results.push({ id: mission.id, name: mission.name, changed: ok });
|
||||
}
|
||||
|
||||
console.log(JSON.stringify({ updated: results }, null, 2));
|
||||
await pool.end();
|
||||
})().catch(async error => {
|
||||
console.error(error);
|
||||
try { await pool.end(); } catch {}
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,14 +1,30 @@
|
||||
const { missionHelpers, pool } = require('../mariadb');
|
||||
|
||||
const squadTacticsCheck = {
|
||||
name: 'Squad Tactics',
|
||||
skill: 'Tactics',
|
||||
characteristic: 'Int',
|
||||
target: 45,
|
||||
modifier: 0,
|
||||
rulesNote: 'Core: Tactics is an Advanced Intelligence Skill with groups such as Assault Doctrine, Defensive Doctrine, Orbital Drop Procedures, and Recon and Stealth. It gives tactical reading/positioning; it does not grant free Cohesion or Squad Mode abilities.',
|
||||
reward: 'On success, the Kill-team identifies the best tactical approach for this scene; extra Degrees of Success may reveal a safer route, priority threat, ambush vector, or better position.',
|
||||
};
|
||||
|
||||
function withSquadTactics(checks) {
|
||||
const list = Array.isArray(checks) ? checks : [];
|
||||
if (list.some(check => String(check.name || '').toLowerCase() === 'squad tactics')) return list;
|
||||
return [squadTacticsCheck, ...list];
|
||||
}
|
||||
|
||||
function rebalanceScene(scene) {
|
||||
if (scene.title === 'The Living Vault') {
|
||||
return {
|
||||
...scene,
|
||||
checks: [
|
||||
checks: withSquadTactics([
|
||||
{ name: 'Anchor the psychic lock', skill: 'Psyniscience', characteristic: 'Wp', target: 45, modifier: 10, reward: 'Malachiel steadies the vault before the signal tears free.' },
|
||||
{ name: 'Read psychic carrier wave', skill: 'Psyniscience', characteristic: 'Per', target: 45, modifier: 10, reward: 'Confirm the signal is warp-amplified.' },
|
||||
{ name: 'Copy datapulse checksum', skill: 'Tech-Use', characteristic: 'Int', target: 45, modifier: 0, reward: 'Recover THETA-92 coordinates without needing a perfect decode.' }
|
||||
],
|
||||
]),
|
||||
complications: [
|
||||
'2 successes gives full coordinates; 1 success gives partial coordinates with a bad omen.',
|
||||
'On 2+ failures, the vault tags one marine with a bio-psychic echo.',
|
||||
@@ -20,11 +36,11 @@ function rebalanceScene(scene) {
|
||||
if (scene.title === 'Prisoner Signal') {
|
||||
return {
|
||||
...scene,
|
||||
checks: [
|
||||
checks: withSquadTactics([
|
||||
{ name: 'Open stasis cages safely', skill: 'Tech-Use', characteristic: 'Int', target: 45, modifier: 10, reward: 'Release prisoners without killing support systems.' },
|
||||
{ name: 'Triage movable prisoners', skill: 'Medicae', characteristic: 'Int', target: 45, modifier: 10, reward: 'Save the prisoners who can survive extraction.' },
|
||||
{ name: 'Spot biomorphic trap', skill: 'Awareness', characteristic: 'Per', target: 45, modifier: 10, reward: 'Detect the altered prisoner before it wakes.' }
|
||||
],
|
||||
]),
|
||||
enemies: [
|
||||
{ name: 'Gene-Warped Prisoner', type: 'mutant trap', count: 1, wounds: 12, ws: 35, bs: 0, ap: 2, damage: '1d10+4', agBonus: 4 }
|
||||
]
|
||||
@@ -34,12 +50,12 @@ function rebalanceScene(scene) {
|
||||
if (scene.title === 'Engine Shrine Sabotage') {
|
||||
return {
|
||||
...scene,
|
||||
checks: [
|
||||
checks: withSquadTactics([
|
||||
{ name: 'Place demolition charges', skill: 'Demolitions', characteristic: 'Int', target: 45, modifier: 10, reward: 'Arm the charges in the right sequence.' },
|
||||
{ name: 'Sense the living machine-spirit', skill: 'Psyniscience', characteristic: 'Wp', target: 45, modifier: 0, reward: 'Malachiel predicts the reactor pulse before it vents.' },
|
||||
{ name: 'Rip open armour panels', skill: 'Strength', characteristic: 'S', target: 45, modifier: 10, reward: 'Expose the core without losing time.' },
|
||||
{ name: 'Avoid plasma venting', skill: 'Dodge', characteristic: 'Ag', target: 45, modifier: 10, reward: 'Avoid 1d10+4 Energy Pen 3.' }
|
||||
],
|
||||
]),
|
||||
enemies: [
|
||||
{ name: 'Gland Hound', type: 'bio-hound', count: 1, wounds: 14, ws: 40, bs: 0, ap: 2, damage: '1d10+5', agBonus: 5 }
|
||||
]
|
||||
@@ -50,11 +66,11 @@ function rebalanceScene(scene) {
|
||||
return {
|
||||
...scene,
|
||||
type: 'briefing',
|
||||
checks: [
|
||||
checks: withSquadTactics([
|
||||
{ name: 'Hold the transmission in memory', skill: 'Willpower', characteristic: 'Wp', target: 45, modifier: 10, reward: 'Preserve the coordinates despite Bile’s psychic static.' },
|
||||
{ name: 'Lock coordinates', skill: 'Navigation', characteristic: 'Int', target: 45, modifier: 10, reward: '+10 to first infiltration/navigation test next mission.' },
|
||||
{ name: 'Frame strategic report', skill: 'Command', characteristic: 'Fel', target: 45, modifier: 10, reward: 'Turn the discovery into actionable Deathwatch orders.' }
|
||||
],
|
||||
]),
|
||||
complications: [
|
||||
'Intel priority choices: enemy intel, facility map, sample data, or prisoner testimony.',
|
||||
'If Sample extracted, quarantine becomes a next-session pressure.',
|
||||
@@ -64,7 +80,7 @@ function rebalanceScene(scene) {
|
||||
};
|
||||
}
|
||||
|
||||
return scene;
|
||||
return { ...scene, checks: withSquadTactics(scene.checks) };
|
||||
}
|
||||
|
||||
(async function main() {
|
||||
|
||||
@@ -34,6 +34,22 @@ const canon = {
|
||||
}
|
||||
};
|
||||
|
||||
const squadTacticsCheck = {
|
||||
name: 'Squad Tactics',
|
||||
skill: 'Tactics',
|
||||
characteristic: 'Int',
|
||||
target: 45,
|
||||
modifier: 0,
|
||||
rulesNote: 'Core: Tactics is an Advanced Intelligence Skill with groups such as Assault Doctrine, Defensive Doctrine, Orbital Drop Procedures, and Recon and Stealth. It gives tactical reading/positioning; it does not grant free Cohesion or Squad Mode abilities.',
|
||||
reward: 'On success, the Kill-team identifies the best tactical approach for this scene; extra Degrees of Success may reveal a safer route, priority threat, ambush vector, or better position.',
|
||||
};
|
||||
|
||||
function withSquadTactics(scene) {
|
||||
const checks = Array.isArray(scene.checks) ? scene.checks : [];
|
||||
if (checks.some(check => String(check.name || '').toLowerCase() === 'squad tactics')) return scene;
|
||||
return { ...scene, checks: [squadTacticsCheck, ...checks] };
|
||||
}
|
||||
|
||||
const scenes = [
|
||||
{
|
||||
title: 'The Living Vault',
|
||||
@@ -417,7 +433,7 @@ const scenes = [
|
||||
enemyCount: 4,
|
||||
threatLevel: 'High',
|
||||
playerCount: existing.player_count || 4,
|
||||
scenes: scenes.map(scene => ({ ...scene, missionCanon: canon, missionStoryFlags: storyFlags }))
|
||||
scenes: scenes.map(withSquadTactics).map(scene => ({ ...scene, missionCanon: canon, missionStoryFlags: storyFlags }))
|
||||
});
|
||||
|
||||
if (!ok) throw new Error(`Failed to update mission ${existing.id}`);
|
||||
|
||||
Reference in New Issue
Block a user