Files
foil-cap-theories/generate_presentation.js
2026-06-30 13:44:04 +03:00

343 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const path = require('path');
const pptxgen = require('pptxgenjs');
const base = __dirname;
const outPath = path.join(base, 'prezentaciya-vampiricheskoe-tenevoe-pravitelstvo.pptx');
const assets = {
title: path.join(base, 'assets', 'generated', '01-title-food-chain.png'),
medieval: path.join(base, 'assets', 'generated', '02-medieval-origin.png'),
classes: path.join(base, 'assets', 'generated', '03-vampire-classes.png'),
failed: path.join(base, 'assets', 'generated', '04-failed-infection.png'),
recognize: path.join(base, 'assets', 'generated', '05-recognize-vampire.png'),
blood: path.join(base, 'assets', 'generated', '06-blood-economy.png'),
artificial: path.join(base, 'assets', 'generated', '07-artificial-blood.png'),
market: path.join(base, 'assets', 'generated', '08-hidden-donor-market.png'),
donorHouse: path.join(base, 'assets', 'generated', '09-donor-house.png'),
etiquette: path.join(base, 'assets', 'generated', '10-blood-etiquette.png'),
zones: path.join(base, 'assets', 'generated', '11-feeding-zones-map.png'),
chain: path.join(base, 'assets', 'generated', '12-food-chain-chart.png')
};
const pptx = new pptxgen();
pptx.layout = 'LAYOUT_WIDE';
pptx.author = 'Hermes Agent';
pptx.company = 'Nous Research';
pptx.subject = 'Псевдонаучная теория о гемозависимой элите';
pptx.title = 'Пищевая цепочка: от тебя до теневого правительства';
pptx.lang = 'ru-RU';
pptx.theme = {
headFontFace: 'Aptos Display',
bodyFontFace: 'Aptos',
lang: 'ru-RU'
};
const C = {
bgDark: '130B0D',
bgLight: 'F4EEE7',
card: 'FFF8F2',
red: '7A1623',
red2: 'A42A37',
text: '24181A',
cream: 'F5E6D3',
gold: 'C7A56A',
muted: '705A5D',
line: 'D8C2B8',
white: 'FFFFFF',
ink: '2A1A1D'
};
function baseLight(slide, title, tag = 'Псевдонаучная реконструкция') {
slide.background = { color: C.bgLight };
slide.addShape(pptx.ShapeType.rect, { x: 0, y: 0, w: 13.333, h: 0.45, fill: { color: C.red }, line: { color: C.red } });
slide.addText(title, { x: 0.55, y: 0.56, w: 9.2, h: 0.4, fontFace: 'Aptos Display', fontSize: 27, bold: true, color: C.ink, margin: 0 });
slide.addText(tag, { x: 10.2, y: 0.6, w: 2.4, h: 0.18, fontSize: 10, color: C.red2, italic: true, align: 'right', margin: 0 });
}
function baseDark(slide, title, subtitle = '') {
slide.background = { color: C.bgDark };
slide.addShape(pptx.ShapeType.rect, { x: 0, y: 0, w: 13.333, h: 7.5, fill: { color: C.bgDark }, line: { color: C.bgDark } });
slide.addText(title, { x: 0.9, y: 0.9, w: 8.8, h: 0.8, color: C.white, fontFace: 'Aptos Display', fontSize: 28, bold: true, margin: 0 });
if (subtitle) {
slide.addText(subtitle, { x: 0.92, y: 1.65, w: 7.7, h: 0.4, color: C.cream, fontSize: 17, italic: true, margin: 0 });
}
}
function footer(slide, n) {
slide.addText(String(n).padStart(2, '0'), { x: 12.45, y: 7.05, w: 0.35, h: 0.16, fontSize: 10, color: C.muted, align: 'right', margin: 0 });
}
function addBullets(slide, items, opts = {}) {
const text = [];
items.forEach((item) => text.push({ text: item, options: { bullet: { indent: 16 }, breakLine: true } }));
slide.addText(text, {
x: opts.x ?? 0.9,
y: opts.y ?? 1.6,
w: opts.w ?? 5.2,
h: opts.h ?? 4.4,
fontSize: opts.fontSize ?? 18,
color: opts.color ?? C.text,
paraSpaceAfterPt: opts.paraSpaceAfterPt ?? 8,
margin: 0.04,
valign: 'top'
});
}
function addCallout(slide, text, opts = {}) {
slide.addShape(pptx.ShapeType.roundRect, {
x: opts.x ?? 0.9,
y: opts.y ?? 5.8,
w: opts.w ?? 11.4,
h: opts.h ?? 0.6,
rectRadius: 0.05,
fill: { color: opts.fill ?? C.red },
line: { color: opts.fill ?? C.red }
});
slide.addText(text, {
x: (opts.x ?? 0.9) + 0.15,
y: (opts.y ?? 5.8) + 0.14,
w: (opts.w ?? 11.4) - 0.3,
h: 0.22,
align: 'center',
color: C.white,
fontSize: opts.fontSize ?? 21,
bold: true,
italic: opts.italic ?? true,
margin: 0
});
}
function addCard(slide, x, y, w, h, title, body, accent = C.red2) {
slide.addShape(pptx.ShapeType.roundRect, { x, y, w, h, rectRadius: 0.06, fill: { color: C.card }, line: { color: accent, pt: 1.2 } });
slide.addText(title, { x: x + 0.18, y: y + 0.16, w: w - 0.36, h: 0.24, fontSize: 17, bold: true, color: C.red, margin: 0 });
slide.addText(body, { x: x + 0.18, y: y + 0.52, w: w - 0.36, h: h - 0.7, fontSize: 14.6, color: C.text, margin: 0.02, valign: 'mid' });
}
// 1 Title
{
const s = pptx.addSlide();
s.background = { color: C.bgDark };
s.addImage({ path: assets.title, x: 0, y: 0, w: 13.333, h: 7.5 });
s.addShape(pptx.ShapeType.rect, { x: 0, y: 0, w: 13.333, h: 7.5, fill: { color: '120A0D', transparency: 34 }, line: { color: '120A0D', transparency: 100 } });
s.addShape(pptx.ShapeType.roundRect, { x: 0.72, y: 0.82, w: 6.95, h: 5.25, rectRadius: 0.06, fill: { color: '1B1114', transparency: 15 }, line: { color: C.red2, pt: 1.1, transparency: 20 } });
s.addText('Пищевая цепочка:\nот тебя до теневого правительства', { x: 1.0, y: 1.15, w: 6.3, h: 1.5, color: C.white, fontFace: 'Aptos Display', fontSize: 25, bold: true, margin: 0 });
s.addText('Расширенная версия: типы вампиров,\nкачественная кровь и скрытый рынок доноров', { x: 1.04, y: 2.95, w: 6.0, h: 0.75, color: C.cream, fontSize: 17.5, italic: true, margin: 0 });
s.addText('Клуб шапочек из фольги\nПсевдонаучный доклад в академическом стиле', { x: 1.04, y: 4.0, w: 4.9, h: 0.8, color: 'E0D0C4', fontSize: 16, margin: 0 });
footer(s, 1);
}
// 2 Hypothesis
{
const s = pptx.addSlide();
baseLight(s, 'Гипотеза исследования');
addCard(s, 0.88, 1.55, 5.35, 2.15, 'Базовая идея', 'Теневое правительство — это не просто политическая сеть, а долгоживущая гемозависимая каста, превратившая власть в механизм стабильного питания.');
addBullets(s, [
'вирусное происхождение касты;',
'долголетие как источник стратегического преимущества;',
'кровь как ресурс, валюта и предмет селекции;',
'власть как надстройка над пищевой цепочкой.'
], { x: 7.05, y: 1.72, w: 5.0, h: 3.6, fontSize: 18 });
addCallout(s, 'Ключевая формула: власть = контроль кормовой инфраструктуры');
footer(s, 2);
}
// 3 Virus
{
const s = pptx.addSlide();
baseLight(s, 'Sanguis Noctis Virus (SNV-12)');
addBullets(s, [
'латентная вирус-индуцированная вампиризация;',
'хроническая антропогематофагия;',
'гемозависимый постчеловеческий синдром;',
'обратимая фототолерантность после кормления.'
], { x: 0.88, y: 1.7, w: 5.0, h: 3.8, fontSize: 18 });
addCard(s, 6.8, 1.55, 5.3, 3.9, 'Ключевые эффекты', 'SNV-12 не делает человека мистическим существом мгновенно. Он перестраивает обменные пути, усиливает регенерацию, замедляет старение и формирует зависимость от человеческой крови как от носителя редких сигнальных молекул.');
addCallout(s, 'Вампиризм здесь — не проклятие, а вирусная адаптация', { fill: 'A42A37' });
footer(s, 3);
}
// 4 Middle Ages
{
const s = pptx.addSlide();
baseLight(s, 'Почему именно XII-XIV века');
s.addImage({ path: assets.medieval, x: 0.78, y: 1.28, w: 6.9, h: 4.85 });
addBullets(s, [
'войны и миграции ускоряют перенос патогена;',
'антисанитария скрывает аномальные случаи;',
'массовая смертность маскирует новый фенотип;',
'фольклор фиксирует ранние утечки в легенды.'
], { x: 8.0, y: 1.7, w: 4.2, h: 3.7, fontSize: 17.5 });
addCallout(s, 'Там, где высокая смертность, новый патоген легче принять за обычное бедствие', { y: 6.02, fontSize: 19 });
footer(s, 4);
}
// 5 Classes
{
const s = pptx.addSlide();
baseLight(s, 'Классы вампирической адаптации');
s.addImage({ path: assets.classes, x: 0.78, y: 1.35, w: 7.0, h: 5.1 });
addBullets(s, [
'молодой носитель: нестабилен и голоден;',
'старший адаптант: устойчив и дисциплинирован;',
'гемарх: древняя элита, пьёт редко, но требует лучшее;',
'ранний возраст заражения повышает шанс интеграции.'
], { x: 8.02, y: 1.7, w: 4.25, h: 3.9, fontSize: 16.8 });
addCallout(s, 'Древность в этой системе — не возраст, а политический капитал', { y: 6.0, w: 12.0, x: 0.65, fontSize: 20 });
footer(s, 5);
}
// 6 Failed infection
{
const s = pptx.addSlide();
baseLight(s, 'Не всякий укушенный становится вампиром');
s.addImage({ path: assets.failed, x: 0.78, y: 1.28, w: 6.9, h: 4.9 });
addBullets(s, [
'феррогемный каскад запускает перегрев крови;',
'летальная несовместимость отсеивает большинство;',
'успешная вампиризация биологически редка;',
'малочисленность касты имеет псевдонаучное объяснение.'
], { x: 8.0, y: 1.72, w: 4.2, h: 3.8, fontSize: 17 });
addCallout(s, 'Не всякий укушенный становится вампиром; большинство становятся статистикой', { y: 6.0, fontSize: 18.5 });
footer(s, 6);
}
// 7 Recognition
{
const s = pptx.addSlide();
baseLight(s, 'Как распознать вампира');
s.addImage({ path: assets.recognize, x: 0.78, y: 1.28, w: 6.85, h: 4.95 });
addBullets(s, [
'клыки чуть длиннее нормы;',
'странно медленное старение;',
'в голоде — тёмный расширенный зрачок;',
'закрытые клубы и слишком долгая карьера.'
], { x: 8.0, y: 1.7, w: 4.2, h: 3.6, fontSize: 17.3 });
addCallout(s, 'Ни один симптом не доказывает ничего сам по себе — работает только совокупность', { y: 6.0, w: 12.0, x: 0.65, fontSize: 18.3 });
footer(s, 7);
}
// 8 Blood economy
{
const s = pptx.addSlide();
baseLight(s, 'Экономика качественной крови');
s.addImage({ path: assets.blood, x: 0.78, y: 1.3, w: 7.0, h: 4.95 });
addBullets(s, [
'массовая кровь — для периферии;',
'стабильная кровь — от здоровых доноров;',
'пиковая кровь — от молодых и сильных;',
'династическая кровь — от особо ценных линий.'
], { x: 8.0, y: 1.65, w: 4.25, h: 3.8, fontSize: 16.9 });
addCallout(s, 'Чем древнее вампир, тем реже он питается, но тем дороже обходится каждый акт питания', { y: 6.0, x: 0.65, w: 12.0, fontSize: 17.8 });
footer(s, 8);
}
// 9 Artificial blood
{
const s = pptx.addSlide();
baseLight(s, 'Почему искусственная кровь им не подходит');
s.addImage({ path: assets.artificial, x: 0.78, y: 1.28, w: 7.0, h: 4.95 });
addBullets(s, [
'живая кровь несёт гормоны и стрессовые маркеры;',
'искусственный суррогат даёт только базовую функцию;',
'у гемархов не возникает полноценного насыщения;',
'фототолерантность и регенерация не восстанавливаются.'
], { x: 8.0, y: 1.67, w: 4.25, h: 3.8, fontSize: 16.8 });
addCallout(s, 'Искусственная кровь для них — суррогат, а не решение', { y: 6.0, fontSize: 19 });
footer(s, 9);
}
// 10 Donor market
{
const s = pptx.addSlide();
baseLight(s, 'Тайный рынок доноров');
s.addImage({ path: assets.market, x: 0.78, y: 1.28, w: 6.95, h: 4.95 });
addBullets(s, [
'частные клиники и закрытые биобанки;',
'элитные школы и клубы как фильтр линий;',
'донорские дома и фонды наблюдения;',
'качество для верхушки важнее объёма.'
], { x: 8.02, y: 1.72, w: 4.2, h: 3.5, fontSize: 17 });
addCallout(s, 'Настоящая роскошь для гемархов — не золото, а доступ к правильно отсортированным живым донорам', { y: 6.0, x: 0.65, w: 12.0, fontSize: 17 });
footer(s, 10);
}
// 11 Power capture
{
const s = pptx.addSlide();
baseLight(s, 'Почему захват власти рационален');
s.addImage({ path: assets.donorHouse, x: 0.78, y: 1.28, w: 6.95, h: 4.95 });
addBullets(s, [
'обеспечить безопасный доступ к крови;',
'скрыть своё существование;',
'сортировать кровь по качеству;',
'контролировать внутреннюю иерархию через снабжение.'
], { x: 8.0, y: 1.72, w: 4.2, h: 3.7, fontSize: 17.2 });
addCallout(s, 'Власть здесь не цель, а технология питания', { y: 6.0, fontSize: 22 });
footer(s, 11);
}
// 12 Internal discipline
{
const s = pptx.addSlide();
baseLight(s, 'Кровь как валюта и инструмент подчинения');
s.addImage({ path: assets.etiquette, x: 0.78, y: 1.28, w: 6.95, h: 4.95 });
addBullets(s, [
'доступ к качественной крови = статус;',
'плохое питание ломает самоконтроль низших носителей;',
'снабжение превращается в механизм дисциплины;',
'верхушка удерживает касту через монополию на лучший ресурс.'
], { x: 8.0, y: 1.68, w: 4.2, h: 3.8, fontSize: 16.9 });
addCallout(s, 'Кровь внутри системы — это одновременно пища, валюта и инструмент подчинения', { y: 6.0, x: 0.65, w: 12.0, fontSize: 18 });
footer(s, 12);
}
// 13 Geography
{
const s = pptx.addSlide();
baseLight(s, 'Геополитика кормовых зон');
s.addImage({ path: assets.zones, x: 0.78, y: 1.28, w: 7.0, h: 4.98 });
addBullets(s, [
'резервуары населения;',
'транспортные коридоры людей и биоресурсов;',
'убежища древних династий;',
'экспериментальные зоны селекции и наблюдения.'
], { x: 8.0, y: 1.7, w: 4.2, h: 3.7, fontSize: 17.1 });
addCallout(s, 'Геополитика = распределение кормовых контуров', { y: 6.02, fontSize: 21 });
footer(s, 13);
}
// 14 Chain
{
const s = pptx.addSlide();
baseLight(s, 'Пищевая цепочка: от тебя до теневого правительства');
s.addImage({ path: assets.chain, x: 0.78, y: 1.28, w: 7.0, h: 4.98 });
addBullets(s, [
'человек — исходный биоресурс;',
'системы учёта отсеивают пригодных доноров;',
'рынок качества поднимает ресурс к верхушке;',
'гемархи замыкают всю логистику на себе.'
], { x: 8.0, y: 1.72, w: 4.2, h: 3.7, fontSize: 17.1 });
addCallout(s, 'Человек включён в систему не как гражданин, а как элемент глобальной донорской инфраструктуры', { y: 6.0, x: 0.65, w: 12.0, fontSize: 17.6 });
footer(s, 14);
}
// 15 Conclusion
{
const s = pptx.addSlide();
baseDark(s, 'Вывод', 'Финальная формула псевдонаучной модели');
s.addText('Если теория верна, вершина мировой политики — это не кабинет и не трон, а хорошо организованная столовая премиум-класса, где человечество давно числится в меню.', { x: 0.95, y: 2.0, w: 8.15, h: 1.9, fontSize: 24, color: C.cream, margin: 0.02, valign: 'mid' });
s.addShape(pptx.ShapeType.roundRect, { x: 9.15, y: 1.6, w: 2.85, h: 3.4, rectRadius: 0.08, fill: { color: '2A1419' }, line: { color: C.gold, pt: 1.3 } });
s.addText('Ключевые\nформулы', { x: 9.72, y: 2.0, w: 1.75, h: 0.6, align: 'center', fontSize: 20, bold: true, color: C.white, margin: 0 });
s.addText('• власть = кормовая логистика\n• кровь = валюта\n• элита скрывает не власть, а рацион', { x: 9.45, y: 2.9, w: 2.25, h: 1.25, fontSize: 15.5, color: C.cream, margin: 0.02 });
s.addText('Жанр: академически поданная псевдонаука для клуба шапочек из фольги', { x: 1.0, y: 6.45, w: 7.4, h: 0.22, fontSize: 13, color: 'D2B8A5', italic: true, margin: 0 });
footer(s, 15);
}
async function main() {
await pptx.writeFile({ fileName: outPath });
}
main().catch((err) => {
console.error(err.stack || err.message || String(err));
process.exit(1);
});