2PlayerGame/chapter1/case.js
2026-07-23 17:03:26 +03:00

155 lines
22 KiB
JavaScript
Raw Permalink 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.

// КЕЙС №1 — "Nightshade Protocol". Всі дані та логіка розв'язання — детерміновані, без AI.
const CASE = {
title: 'Nightshade Protocol',
intro: 'Меридіан, 2087. Маркус Рейз, 44, брокер даних Halcyon Dynamics, знайдений мертвим під балконом свого пентхауса — падіння з 40-го поверху. Офіційна версія: нещасний випадок. Біометричний лог нейроімпланта (незламний) зафіксував смерть о 23:14. У вас одна ніч, щоб довести протилежне.',
roles: {
field: { name: 'Польовий детектив', desc: 'Ти працюєш ногами: місце злочину, фізичні докази, допити віч-на-віч. Цифрові системи для тебе закриті — те, що бачить аналітик, ти дізнаєшся тільки з розмови.' },
analyst: { name: 'Детектив-аналітик', desc: 'Ти працюєш з терміналу: логи доступу, камери, транзакції, біометрія. На місце злочину тебе не пускають — фізичні знахідки напарника ти дізнаєшся тільки з розмови.' }
},
howto: 'Знайдені докази закріплюйте на спільній дошці (📌) — тільки закріплені докази бачить напарник, і тільки вони розблоковують нові питання в допитах та нові запити в системах. Говоріть уголос: жодна роль сама не бачить повної картини.',
suspects: [
{ id: 'elena', name: 'Елена Восс', role: 'Партнерка Рейза по бізнесу', desc: 'Співзасновниця напряму в Halcyon. Холодна, бездоганна, кар\'єра понад усе.' },
{ id: 'kaito', name: 'Кайто Морі', role: 'Екс-коханець', desc: 'Фрилансер-хакер з Нижніх Доків. Розрив із Рейзом був гучним.' },
{ id: 'sable', name: 'Д-р Сейбл', role: 'Біотех-конкурент', desc: 'Голова R&D у конкурентів. Публічно звинувачував Рейза у крадіжці досліджень.' },
{ id: 'rook', name: 'Рук', role: 'Вуличний фіксер, кредитор', desc: 'Тримає майстерню в Доках. Позичає гроші тим, кому банки не позичать.' },
{ id: 'ivy', name: 'Айві Чен', role: 'Асистентка Рейза', desc: 'Повний доступ до пентхауса і графіка жертви. Тиха, виконавча, нервова.' }
],
methods: [
{ id: 'push', label: 'Вимкнення силового поля балкона через інженерний override і поштовх' },
{ id: 'poison', label: 'Отруєння нейротоксином' },
{ id: 'hack', label: 'Злам нейроімпланта (примусова зупинка)' },
{ id: 'drone', label: 'Удар найманого бойового дрона' },
{ id: 'accident', label: 'Нещасний випадок / самогубство' }
],
// tag: що доводить. requires: докази, які мають бути НА ДОШЦІ, щоб це стало доступним.
evidence: {
// — фізичні (польовий) —
fabric: { role: 'field', title: 'Клапоть тканини', tag: 'контакт', detail: 'Темно-сірий клапоть ділового піджака, зачепився за поручень балкона. Дорога корпоративна тканина — таку носять у Шпилі, не в Доках.' },
struggle: { role: 'field', title: 'Сліди боротьби', tag: 'контакт', detail: 'Перекинутий столик, подряпини на покритті балкона біля поручня. Рейз не стояв там сам — його штовхали, і він опирався.' },
fingerprint: { role: 'field', title: 'Частковий відбиток', tag: 'спосіб', detail: 'Частковий відбиток пальця на панелі інженерного override. Потрібна цифрова звірка — закріпи на дошці, щоб аналітик міг прогнати по базі.' },
guard: { role: 'field', title: 'Свідчення охоронця', tag: 'алібі', detail: 'Нічний охоронець Halcyon: «Пані Восс вийшла о 22:15 — бейдж пікнув, я записав. Назад не бачив, після 22:30 я був на обході».' },
ledger: { role: 'field', title: 'Тіньовий гросбух', tag: 'мотив', detail: 'У схованці за панеллю в кабінеті Рейза: роздруківки тіньових рахунків з позначками «Е.Ві нотатка Рейза: «Рада. 9:00. Все їм показати».' },
elena_lie: { role: 'field', title: 'Зізнання Елени', tag: 'алібі', detail: 'Під тиском доказів Елена визнала, що була в пентхаусі Рейза вночі. Стверджує, що пішла, коли він був ще живий.' },
// — цифрові (аналітик) —
override_log:{ role: 'analyst', title: 'Лог override панелі', tag: 'спосіб', detail: 'Інженерний override силового поля балкона, 23:10. Авторизація: ID Елени Восс (пропуск-чіп часів її стосунків з Рейзом досі активний).' },
drone_rec: { role: 'analyst', title: 'Запис патрульного дрона', tag: 'присутність', detail: 'Резервний архів: 22:50, силует на балконі пентхауса. Обличчя не розібрати — зріст і статура дорослої людини в діловому одязі.' },
entry_log: { role: 'analyst', title: 'Лог входу в будівлю Рейза', tag: 'алібі', detail: 'Бейдж Елени Восс: вхід у будівлю Рейза о 22:47. Сама вона каже, що «була в офісі допізна».' },
calendar: { role: 'analyst', title: 'Запрошення в календарі', tag: 'мотив', detail: 'Рейз створив подію на 9:00 ранку: «Термінова рада — комплаєнс Е.В.». Розсилка: рада директорів Halcyon.' },
kaito_wifi: { role: 'analyst', title: 'Мережевий лог Кайто', tag: 'зняття підозри', detail: 'Імплант Кайто Морі підключений до wifi бару "The Hollow Note" безперервно 20:1023:30. На момент смерті (23:14) він був у барі.' },
rook_manifest:{ role: 'analyst', title: 'Маніфест доставки', tag: 'зняття підозри', detail: 'Вантажний під\'їзд будівлі Рейза: Рук, 21:3021:45, накладна на серводеталі для консьєржа. Виїхав за півтори години до смерті.' },
ivy_cam: { role: 'analyst', title: 'Камера лобі', tag: 'зняття підозри', detail: 'Айві Чен виходить з лобі о 22:30, збігається з логом бейджа. До 23:14 не поверталась.' },
sable_conf: { role: 'analyst', title: 'Реєстрація конференції', tag: 'зняття підозри', detail: 'Д-р Сейбл: чекін на конференції «Женева-2», виступ 22:4023:20, сотні свідків і трансляція. Залізне алібі.' },
biometric: { role: 'analyst', title: 'Біометрія імпланта Рейза', tag: 'факт', detail: 'Смерть: 23:14. Сплеск адреналіну 23:1223:14 — боротьба або падіння при свідомості. Це не тихий нещасний випадок.' },
print_match: { role: 'analyst', title: 'Збіг відбитка', tag: 'спосіб', requires: ['fingerprint'], detail: 'Частковий відбиток з панелі override: збіг 87% — Елена Восс.' },
shadow_tx: { role: 'analyst', title: 'Тіньові транзакції', tag: 'мотив', requires: ['ledger'], detail: 'Рахунки з гросбуха: систематичне виведення коштів Halcyon на офшор. Транзакції підписані корпоративним ключем Е.В. Рейз це знайшов.' }
},
// Польовий детектив
locations: [
{ id: 'penthouse', name: 'Пентхаус Рейза (40-й поверх)', desc: 'Місце смерті. Балкон над прірвою Нижніх Доків, кабінет із заблокованим терміналом.',
spots: [
{ id: 's1', label: 'Оглянути поручень балкона', ev: 'fabric' },
{ id: 's2', label: 'Оглянути балкон', ev: 'struggle' },
{ id: 's3', label: 'Оглянути панель override', ev: 'fingerprint' },
{ id: 's4', label: 'Обшукати кабінет', ev: 'ledger' },
{ id: 's5', label: 'Робочий термінал Рейза', flavor: 'Термінал заблоковано корпоративним шифруванням. Цифрові сліди — робота аналітика.' }
], people: [] },
{ id: 'lobby', name: 'Лобі будівлі Рейза', desc: 'Мармур, дрони-консьєржі, камери в кожному куті.',
spots: [ { id: 's6', label: 'Стійка охорони', flavor: 'Журнал відвідувачів повністю цифровий. Хай аналітик перевірить логи доступу будівлі.' } ],
people: ['ivy'] },
{ id: 'halcyon', name: 'Офіс Halcyon Dynamics', desc: 'Верхній Шпиль. Скло, тиша, нічна зміна охорони.',
spots: [], people: ['elena', 'guard', 'sable'] },
{ id: 'bar', name: 'Бар "The Hollow Note"', desc: 'Нижні Доки. Димно, гучно, ніхто нічого не бачив — офіційно.',
spots: [], people: ['kaito', 'barman'] },
{ id: 'workshop', name: 'Майстерня Рука', desc: 'Гараж у Доках, завалений дронами й запчастинами.',
spots: [ { id: 's7', label: 'Оглянути дрони', flavor: 'Вантажні мули для доставки. Жодних слідів бойових модифікацій.' } ],
people: ['rook'] }
],
// Аналітик
systems: [
{ id: 'access', name: 'Логи доступу', queries: [
{ id: 'q1', label: 'Будівля Рейза: входи 20:0000:00', ev: 'entry_log' },
{ id: 'q2', label: 'Панель безпеки пентхауса', ev: 'override_log' },
{ id: 'q3', label: 'Вантажний під\'їзд будівлі', ev: 'rook_manifest' },
{ id: 'q4', label: 'Офіс Halcyon: нічні логи', flavor: 'Нічний архів Halcyon недоступний — планове обслуговування серверів. Лишається усний журнал охорони на місці.' }
]},
{ id: 'video', name: 'Відеоархіви', queries: [
{ id: 'q5', label: 'Камери лобі будівлі Рейза', ev: 'ivy_cam' },
{ id: 'q6', label: 'Патрульні дрони: резервний архів', ev: 'drone_rec' },
{ id: 'q7', label: 'Трансляція конференції «Женева-2»', ev: 'sable_conf' }
]},
{ id: 'net', name: 'Мережа і біометрія', queries: [
{ id: 'q8', label: 'Біометричний лог імпланта жертви', ev: 'biometric' },
{ id: 'q9', label: 'Мережева активність імпланта Кайто Морі', ev: 'kaito_wifi' },
{ id: 'q10', label: 'Звірити відбиток з панелі по базі', ev: 'print_match' }
]},
{ id: 'finance', name: 'Фінанси і записи', queries: [
{ id: 'q11', label: 'Календар жертви', ev: 'calendar' },
{ id: 'q12', label: 'Рахунки жертви', flavor: 'Регулярні перекази на рахунок, пов\'язаний з Руком: борг ~40 000 кредитів, останній платіж прострочений.' },
{ id: 'q13', label: 'Простежити рахунки з гросбуха', ev: 'shadow_tx' }
]}
],
// Допити (веде польовий). requires: id доказів на дошці.
people: {
elena: { name: 'Елена Восс', suspect: true, topics: [
{ id: 't1', q: 'Де ви були цієї ночі?', a: '«Була в офісі допізна. Квартальний звіт. Пішла десь після опівночі — охорона підтвердить.» Дивиться прямо, не кліпає. Занадто рівно.' },
{ id: 't2', q: 'Ваші стосунки з Рейзом?', a: '«Партнери. Дванадцять років. Останнім часом він став… параноїком. Всюди бачив змови.» На слові «параноїком» — коротка пауза.' },
{ id: 't3', q: 'Що за термінова рада о 9:00?', requires: ['calendar'], a: '«Звичайна процедурна рада. Комплаєнс. Нічого особливого.» Пальці стискають планшет так, що біліють кісточки.' },
{ id: 't4', q: 'Охоронець каже: ваш бейдж вийшов о 22:15.', requires: ['guard'], a: '«Виходила подихати. Повернулась і працювала далі. Це не злочин, детективе.» Погляд уперше йде вбік.' },
{ id: 't5', q: 'Лог будівлі Рейза: ваш бейдж, вхід о 22:47.', requires: ['guard', 'entry_log'], gives: 'elena_lie', a: 'Довга пауза. «…Гаразд. Я була в нього. Хотіла поговорити до ради. Ми посперечались, і я пішла. Він був живий, чуєте? Живий.» Голос зривається — вперше за розмову.' }
]},
kaito: { name: 'Кайто Морі', suspect: true, topics: [
{ id: 't6', q: 'Де ви були о 23:14?', a: '«Тут. Весь вечір, до пів на дванадцяту. Спитайте бармена — чи мій імплант, якщо вмієте читати логи.»' },
{ id: 't7', q: 'Рейз злив ваші генетичні дані.', a: 'Стискає склянку. «Продав мою ДНК як рекламну базу. Я його ненавидів. Але між ненавистю і сорока поверхами — велика різниця.»' },
{ id: 't8', q: 'Мережа підтверджує: ви були тут.', requires: ['kaito_wifi'], a: 'Видихає. «Нарешті хоч хтось перевірив. Слухайте… Рейз останній місяць скаржився на Елену. Казав: „вона рахує, що я сліпий". Не знаю, що він знайшов, але воно його зжирало.»' }
]},
sable: { name: 'Д-р Сейбл (відеозв\'язок)', suspect: true, topics: [
{ id: 't9', q: 'Де ви були цієї ночі?', a: '«На „Женеві-2". Виступ о 22:40, чотириста свідків і трансляція. Перевірте — заощадите нам обом час.»' },
{ id: 't10', q: 'Рейз украв ваші дослідження.', a: '«Рейз — падальник, і я не шкодуватиму за ним. Але вбивство — інструмент тих, у кого закінчились юристи. У мене вони не закінчились.»' }
]},
rook: { name: 'Рук', suspect: true, topics: [
{ id: 't11', q: 'Вас бачили біля будівлі Рейза.', a: '«Був поруч. Проїздом. Нічого не бачив, нікого не чіпав.» Дивиться повз тебе, витирає руки ганчіркою, яка чистіша за його відповіді.' },
{ id: 't12', q: 'Рейз був винен вам гроші.', a: '«Сорок штук. І що? Мертвий він мені не заплатить. Подумай головою, детективе: я кредитор, а не гробар.»' },
{ id: 't13', q: 'Маніфест: доставка о 21:3021:45.', requires: ['rook_manifest'], a: '«Ну так, завозив серводеталі консьєржу. Поїхав о 21:45. Задоволений?» Пауза. «Одне скажу: асистентка його виходила пізніше — нервова, як дріт під струмом.»' }
]},
ivy: { name: 'Айві Чен', suspect: true, topics: [
{ id: 't14', q: 'Коли ви пішли від Рейза?', a: '«О 22:30, як завжди. Камера в лобі підтвердить.» Відповідає миттєво — наче репетирувала.' },
{ id: 't15', q: 'Яким він був останні тижні?', a: '«Напруженим. Замкнув частину файлів навіть від мене. Я… я думала, він щось підозрює.» Затинається на слові «підозрює».' },
{ id: 't16', q: 'Камера підтверджує. Але чому ви так нервуєте?', requires: ['ivy_cam'], a: 'Довго мовчить. «Я передавала документи Сейблу. За гроші. Але я не вбивала! Рейз готував на ранок якесь викриття — не про мене. Він сказав: „це її поховає". Її.»' }
]},
guard: { name: 'Нічний охоронець Halcyon', suspect: false, topics: [
{ id: 't17', q: 'Хто виходив з офісу ввечері?', gives: 'guard', a: '«Пані Восс вийшла о 22:15 — бейдж пікнув, я записав у журнал. Назад не бачив: після 22:30 я на обході, а нічні логи в них на обслуговуванні.»' }
]},
barman: { name: 'Бармен "The Hollow Note"', suspect: false, topics: [
{ id: 't18', q: 'Кайто Морі був тут уночі?', a: '«Весь вечір, до пів на дванадцяту точно. Сидів он там і пив так, наче хотів забути чиєсь ім\'я.»' }
]}
},
finale: {
suspect: 'elena', method: 'push', maxEvidence: 4,
recap: 'Елена Восс роками виводила гроші Halcyon через тіньові рахунки. Рейз знайшов гросбух і призначив на 9:00 раду, яка б її поховала. О 22:15 вона вийшла з офісу — охоронець це бачив. О 22:47 увійшла в будівлю Рейза старим пропуском. О 23:10 інженерним override вимкнула силове поле балкона. О 23:14 — після короткої боротьби, що лишила клапоть її піджака на поручні, — Маркус Рейз упав з 40-го поверху. Справу закрито.'
}
};
// Перевірка звинувачення. Повертає {ok, hint}.
function checkAccusation(suspect, method, evs) {
const has = id => evs.includes(id);
if (suspect !== CASE.finale.suspect)
return { ok: false, hint: 'Не сходиться сам підозрюваний: його алібі на момент смерті підтверджується доказом, який у вас уже є або який можна знайти. Перевірте, кого докази виключають.' };
if (method !== CASE.finale.method)
return { ok: false, hint: 'Підозрюваний правильний, але спосіб не відповідає слідам на місці смерті. Що саме відкрило вбивці шлях до жертви?' };
if (!(has('override_log') || has('print_match')))
return { ok: false, hint: 'Ланка «спосіб/доступ» не доведена: потрібен доказ, ЧИМ вбивця відкрив шлях до жертви.' };
if (!(has('elena_lie') || (has('guard') && has('entry_log'))))
return { ok: false, hint: 'Ланка «алібі» не зламана: потрібно довести, що підозрюваний бреше про те, де був. Одного доказу тут мало — потрібна суперечність.' };
if (!(has('fabric') || has('struggle')))
return { ok: false, hint: 'Ланка «фізичний контакт» не доведена: що пов\'язує вбивцю з боротьбою на балконі?' };
return { ok: true };
}
if (typeof module !== 'undefined') module.exports = { CASE, checkAccusation };