This commit is contained in:
kyoung5seo
2026-07-09 23:20:31 +09:00
20 changed files with 783 additions and 273 deletions

View File

@@ -74,9 +74,14 @@ Phase 5 [ ] 비주얼 폴리싱 & UX ← 그레이박스 테스트 완
**Phase 4 완료 시 남은 주의사항:** 엔딩 도감 localStorage 키를 `ss_endings_v2`로 교체했다
(기존 `ss_endings`는 제목 문자열 기반이라 취약했음). 이전 플레이테스트 기록은 새 키와
호환되지 않는다. 내러티브 태그는 모든 카드가 아니라 선별된 카드(22/63장)만 키워드를 주는
방식으로 재설계했고, 페이즈 전환 문턱도 4/8 → 2/4로 낮췄다. 자세한 내용은 `ROADMAP.md`
Phase 4 참고.
호환되지 않는다. 내러티브 태그는 재사용 가능한 태그 풀(`narrative_tags.json`, 10종) 방식으로,
선별된 카드의 좌/우 선택지에서만 얻는다. 페이즈 전환 문턱은 "고유 태그 3종/6종 획득". 자세한
내용은 `ROADMAP.md` Phase 4 참고.
**⚠️ 2026-07-09 병렬 브랜치 병합:** 이 폴더가 유일한 작업 사본이 아니라면, 작업 시작 전
`git fetch``git log origin/main..HEAD``git log HEAD..origin/main`으로 분기 여부를
반드시 확인할 것. 과거에 다른 세션이 origin에 푸시한 커밋을 이 로컬이 받지 못한 채 Phase 2~4를
독립적으로 재구현해 대형 충돌이 났던 적이 있다. 자세한 경위는 `ROADMAP.md` 상단 박스 참고.
Phase 0~4 그레이박스 구간이 모두 완료됐다. 다음은 외부 플레이어 그레이박스 테스트이며,
Phase 5(비주얼 폴리싱)는 그 피드백 반영 후에만 착수한다.

View File

@@ -42,6 +42,19 @@ Phase 5 [ ] 비주얼 폴리싱 & UX ← 그레이박스 테스트 완
---
## ⚠️ 2026-07-09: 병렬 작업 브랜치 병합 (중요 — 반드시 읽을 것)
이 로컬 작업 폴더는 2026-06-24~25에 **다른 세션에서 origin/main에 푸시된 4개 커밋**을
받아오지 못한 채로 Phase 2~4 작업을 진행했다. 그 커밋들에는 이 로컬에서 독립적으로
다시 구현한 것과 겹치는 기능(파산 문책, 내러티브 태그, 오염 카드, 자산)이 서로 다른
방식으로 이미 존재했고, `git pull` 시점에 6개 파일에서 충돌이 나서 origin 쪽 구현을
기준으로 병합했다. 원인·과정·잔여 이슈는 아래 각 Phase 절의 "병합 참고" 박스에 정리했다.
**이 폴더가 유일한 작업 사본이 아니라면, 다음 세션을 시작하기 전에 반드시 `git fetch` +
`git log origin/main..HEAD` / `git log HEAD..origin/main`으로 분기 여부를 먼저 확인할 것.**
---
## 다음 단계
**Phase 0~4 그레이박스 작업이 모두 완료됐다.** 이제 외부 플레이어 그레이박스 테스트를 진행할 차례다.
@@ -108,7 +121,14 @@ Phase 5(비주얼 폴리싱)는 그 피드백을 반영한 뒤에만 착수한
> **참고:** 오염 카드 콘텐츠(`cards.csv`의 신화/조직저항/사회공황/이사회압박 16종)는 등급별 1종씩만 채운 **초안**이다.
> 밸런스·문체 검토 후 디자이너가 등급별로 추가 카드를 늘리는 작업이 별도로 필요하다.
> 또한 A급 이상 신화 카드가 '뉴스 이벤트' 카드를 추가하는 연쇄 규칙(기획서 92-95행)은 이번 범위에 포함하지 않았다.
> node/npm이 설치되지 않은 환경이라 `npm run dev` 실기 검증은 못 했고, 코드 리뷰로만 확인했다 — 실행 확인 필요.
> **병합 참고 (2026-07-09):** origin/main에 미리 푸시되어 있던 다른 세션의 파산 문책 구현(정례회의
> 라운드가 늘수록 결재 건수가 8→6→4→2→0으로 줄어들고, 파산 카운터 5회 이상이면 의회에서 전 파라미터를
> 강제로 올리는 에스컬레이션 포함)이 더 완성도 높고 실제 사용자 피드백을 직접 반영한 것이었다. 그 버전을
> 기준으로 병합하면서 상태명을 `bankruptcyCounter/bankruptcyWarned` → `bankruptcyCount/bankruptcyWarning`으로
> 통일했다. 자산은 origin에 `assets.json`(이름 있는 개별 아이템 4종: 전임 국장의 만년필 등)이 준비돼 있었지만
> 실제 효과 로직이 연결돼 있지 않아서, 이 로컬에서 만든 "성질 변환/시한부 격리/물리적 영구 소각/결재권 강제
> 행사" 4종 기능 로직을 그대로 유지했다 — `assets.json`의 이름 있는 아이템 플레이버는 아직 미사용 상태다.
### 핵심 참조 문서
- `시스템 기획서` — 결재 단계, 덱 오염, 덱 압축, 자산 슬롯
@@ -130,18 +150,20 @@ Phase 5(비주얼 폴리싱)는 그 피드백을 반영한 뒤에만 착수한
- [x] 연대기(Chronicle) 팝업
- [x] **파벌 우호도 세부 조건** — 12개 글로벌 지부장 파벌이 각자 다른 공식으로 우호도를 계산한다(`GameScreen.jsx` `GLOBAL_FACTIONS`). 게임 시작 시 12개 중 5개를 무작위 선발(`factionRoster`), 매 의회마다 실시간 상태(토큰/태그/파라미터/자산/카운터)로 점수를 산출해 동맹/우호/중립/적대/파국 5단계 티어로 환산, 투표와 국장 개입 면역 여부를 결정한다(`CouncilScreen.jsx`).
- [x] **파산 카운터 → 의회 문책 연동** — Phase 2 작업으로 완료됨 (`CouncilScreen.jsx``bankruptcyCounter`/`bankruptcyWarned`를 직접 참조)
- [x] **스토리 페이징 카드 풀 전환** — 초기 덱은 일상/위기만 포함되고, 내러티브 태그 4개 도달 시 작전/극비 프로젝트 카드 풀이 실시간으로 덱에 개방된다(`GameScreen.jsx` `phase2Pool`/`phase2Unlocked`).
- [x] **스토리 페이징 카드 풀 전환** — 초기 덱은 일상/위기만 포함되고, 고유 내러티브 태그 3종 도달 시 작전/극비 프로젝트 카드 풀이 실시간으로 덱에 개방된다(`GameScreen.jsx` `phase2Pool`/`phase2Unlocked`).
### 완료 기준 (Done Criteria)
- [x] 글로벌 지부장 12개 파벌의 우호도가 각자 다른 조건으로 개별 계산된다.
- [x] 파산 카운터가 의회 문책 로직에 연동된다.
- [x] 내러티브 태그 4개 도달 시 등장 카드 풀이 실시간으로 전환된다.
- [x] 내러티브 태그 누적 시 등장 카드 풀이 실시간으로 전환된다.
> **참고 및 남은 과제:**
> - 상임 이사회 8명의 투표는 여전히 `params.risk` 기반 확률 처리다. 기획서상 이들은 국장의 '성향 태그'에 반응해야 하는데, 이 게임에는 아직 성향 태그 시스템 자체가 없어 범위 밖으로 남겨뒀다(§4.1 대응).
> - 태그 8개(페이즈3) 시점의 카드 풀은 기획서상 페이즈2와 동일한 유형(작전/극비/오염)이라 별도 전환 이벤트를 추가하지 않았다. 대신 오염 카드 등급 가중치가 파라미터 상승에 따라 자연스럽게 심해지는 방식으로 "압박 루프" 강도를 구현했다.
> - Playwright로 실제 브라우저에서 플레이 검증 중 **`params.risk >= 5`가 직접 게임오버를 발동시키지만 `EndingScreen`에 해당 분기가 없어 "미확인 종말" 폴백으로 빠지는 버그**를 발견해 수정했다(PRD상 risk는 탄핵 기준치일 뿐 직접 붕괴 조건이 아님).
> **병합 참고 (2026-07-09):** 내러티브 태그 시스템 자체를 origin 버전(재사용 가능한 태그 풀 방식, 아래 참고)으로
> 교체하면서 페이징 문턱도 "고유 태그 4개/8개" → "고유 태그 3개/6개"로 재조정했다(풀 크기 10종 기준).
### 핵심 참조 문서
- `스토리 바이블` — §4.2 글로벌 지부장 12개 파벌 상세
- `시스템 기획서` — 주간 의회 단계, 토큰 경제
@@ -169,8 +191,9 @@ Phase 5(비주얼 폴리싱)는 그 피드백을 반영한 뒤에만 착수한
- [x] **변주된 붕괴 엔딩** — 자원 태그 변주 3종(쿠데타/은폐된 멸망/자본의 폭주) + 토큰 변주 3종(야반도주/정보 암흑/긴축의 폭동)
- [x] **극단적 파라미터 조기 엔딩** — 단일 결재에서 위험 파라미터가 3단계 이상 급증하며 5단계에 도달하면 "돌이킬 수 없는 순간" 즉각 붕괴 엔딩 발동
- [x] **내러티브 연쇄 엔딩** — 오염 카드 4종을 지정 순서로 수집하면 해금, 이후 탄핵/붕괴 시 최우선 적용
- [x] **도감 UI**`EndingCodex.jsx` 신규 컴포넌트. 엔딩 화면에서 "📖 엔딩 도감 보기"로 열람, 해금 15종 중 몇 종 확인했는지 표시, 미해금은 "???"로 잠금 처리, `localStorage`(`ss_endings_v2`)에 엔딩 id 기준으로 영구 저장
- [x] **연대기 팝업 완성** — 좌측 세로선 + 턴별 점(dot)으로 타임라인 형태 시각화(`ChroniclePopup.css`)
- [x] **임기 만료 엔딩** (병합으로 추가) — 라운드가 거듭될수록 정례회의까지의 결재 건수가 8→6→4→2→0으로 줄어들다 0이 되면 발동
- [x] **도감 UI**`EndingCodex.jsx` 신규 컴포넌트. 엔딩 화면에서 "📖 엔딩 도감 보기"로 열람, 해금 16종 중 몇 종 확인했는지 표시, 미해금은 "???"로 잠금 처리, `localStorage`(`ss_endings_v2`)에 엔딩 id 기준으로 영구 저장
- [x] **연대기 팝업 완성** — 턴별 타임라인 + 새로 획득한 내러티브 태그를 아이콘/설명과 함께 인라인으로 강조 표시, 상단에 수집한 태그 전체를 칩으로 요약(`ChroniclePopup.jsx/css`)
### 완료 기준 (Done Criteria)
- [x] 파라미터 5단계 도달 시 우선순위 판별기가 정확한 엔딩을 출력한다. (Playwright로 "군사 쿠데타" 변주 엔딩과 "정치적 탄핵" 기본 엔딩을 각각 실제로 트리거해 확인)
@@ -179,16 +202,18 @@ Phase 5(비주얼 폴리싱)는 그 피드백을 반영한 뒤에만 착수한
- [x] 연대기 팝업에서 이번 회차 내러티브 히스토리를 확인할 수 있다.
> **참고:** 엔딩 도감 localStorage 키를 `ss_endings`(제목 문자열 기반, 취약)에서 `ss_endings_v2`(고유 id 기반)로 교체했다.
> 이전에 수동 플레이테스트로 쌓인 `ss_endings` 데이터는 새 키와 호환되지 않아 브라우저에서 도감이 0/15로 보일 수 있다 —
> 이전에 수동 플레이테스트로 쌓인 `ss_endings` 데이터는 새 키와 호환되지 않아 브라우저에서 도감이 0/16으로 보일 수 있다 —
> 프로토타입 단계라 마이그레이션 로직은 만들지 않았다.
> **내러티브 태그 시스템 재설계 (디자이너 피드백 반영):** 최초 구현은 결재 카드 한 장당 무조건 +1 되는
> 카운터였는데, 이는 "내러티브 태그는 항상 얻는 것이 아니다"라는 기획 의도와 맞지 않았다. `cards.csv`에
> `left_narrative` / `right_narrative` 컬럼을 추가해 선별된 카드(위기/작전/극비 22종 + 오염 카드 A·S등급
> 8종, 총 22장/63장)의 좌/우 선택지에만 짧은 키워드(예: "은폐 공작 실행", "쿠데타 조짐")를 부여하도록
> 바꿨다. 수집한 키워드는 연대기 팝업 상단과 엔딩 화면에서 칩(chip) 형태로 확인할 수 있다(기존 턴별
> 결재 로그는 그대로 유지). 동시에 페이즈 전환 문턱을 4/8 → 2/4로 낮췄다(키워드가 희소해진 만큼, 그리고
> 기존 4/8도 실제 플레이에서 길게 느껴진다는 피드백 반영).
> **병합 참고 (2026-07-09) — 내러티브 태그 시스템 최종본:** 이 Phase에서 한 번(카드당 자유 텍스트 키워드
> `left_narrative`/`right_narrative`) 재설계했었는데, 이후 origin/main에 병합할 다른 세션의 구현을 발견했다.
> 그 버전은 `narrative_tags.json`(재사용 가능한 태그 10종 — 아이콘+이름+설명, 예: "쿠데타의 그림자" 🗡️)과
> `cards.csv`의 `left_tag`/`right_tag` 컬럼(태그 id 참조) 조합으로, 처음 획득하는 태그일 때만 토스트 알림이
> 뜨고 진행도에 반영되는 방식이었다 — 사용자 피드백을 더 직접 반영한 더 성숙한 설계라 최종적으로 이 방식을
> 채택하고 자유 텍스트 방식은 폐기했다. 다만 origin의 `cards.csv`에는 태그 컬럼 자체가 비어 있어 실제로는
> 작동하지 않는 상태였으므로, `update_csv_tags.js`의 기존 11개 매핑을 참고해 새로 채워 넣고 태그 풀도
> 4종(쿠데타의 그림자/군중의 광기/정치적 압박/희생의 대가) 추가했다. 페이징 문턱도 "태그 풀 10종 중 고유
> 3종/6종 획득"으로 재조정했다.
> **이 Phase 완료 = 그레이박스 테스트 준비 완료.**
> Phase 5는 테스트 피드백 반영이 끝난 후 착수한다.

View File

@@ -4,7 +4,7 @@
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
background: rgba(0, 0, 0, 0.85);
z-index: 300;
display: flex;
align-items: center;
@@ -14,14 +14,14 @@
.chronicle-popup {
background: var(--color-surface);
width: 90%;
max-width: 400px;
max-height: 80%;
max-width: 500px;
max-height: 85%;
border-radius: 16px;
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow: 0 10px 50px rgba(0, 0, 0, 0.9);
}
.chronicle-header {
@@ -29,14 +29,23 @@
align-items: center;
justify-content: space-between;
padding: 20px;
background: rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0.6);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.chronicle-header h2 {
margin: 0;
font-family: var(--font-doc-title);
font-size: 20px;
font-size: 22px;
}
.chronicle-tags-summary {
background: rgba(251, 191, 36, 0.2);
color: #fbbf24;
padding: 4px 12px;
border-radius: 12px;
font-size: 14px;
font-weight: bold;
}
.close-btn {
@@ -45,6 +54,7 @@
color: #fff;
font-size: 28px;
cursor: pointer;
line-height: 1;
}
.keyword-section {
@@ -85,7 +95,7 @@
}
.chronicle-content {
padding: 20px;
padding: 24px;
overflow-y: auto;
flex: 1;
}
@@ -96,50 +106,106 @@
font-style: italic;
}
.chronicle-list {
list-style: none;
padding: 0 0 0 14px;
margin: 0;
border-left: 2px solid rgba(255, 255, 255, 0.1);
.chronicle-timeline {
display: flex;
flex-direction: column;
gap: 20px;
}
.chronicle-item {
position: relative;
margin-bottom: 20px;
.chronicle-entry {
display: flex;
gap: 16px;
padding-bottom: 20px;
padding-left: 16px;
border-bottom: 1px dashed rgba(255, 255, 255, 0.1);
}
.chronicle-item::before {
content: '';
position: absolute;
left: -21px;
top: 4px;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--color-token-info);
box-shadow: 0 0 0 3px var(--color-surface);
}
.chronicle-item:last-child {
.chronicle-entry:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.turn-number {
font-size: 12px;
color: var(--color-token-info);
.entry-turn {
font-size: 14px;
font-weight: bold;
display: block;
margin-bottom: 5px;
color: #a1a1aa;
min-width: 60px;
padding-top: 2px;
}
.chronicle-item p {
margin: 0;
font-size: 14px;
line-height: 1.5;
color: #ddd;
.entry-body {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
}
.entry-context {
display: flex;
gap: 8px;
align-items: center;
}
.entry-type {
font-size: 12px;
color: var(--color-token-info);
font-family: var(--font-typewriter);
}
.entry-keyword {
font-size: 15px;
font-weight: bold;
color: #fbbf24;
}
.entry-text {
font-size: 14px;
color: #d4d4d8;
line-height: 1.5;
}
.entry-decision {
font-size: 14px;
color: #4ade80;
font-weight: bold;
}
.entry-tag-highlight {
margin-top: 12px;
background: rgba(251, 191, 36, 0.15);
border-left: 4px solid #fbbf24;
border-radius: 4px;
padding: 12px;
display: flex;
align-items: center;
gap: 12px;
}
.tag-icon {
font-size: 32px;
}
.tag-details {
display: flex;
flex-direction: column;
gap: 4px;
}
.tag-name {
font-size: 16px;
font-weight: bold;
color: #fbbf24;
}
.tag-desc {
font-size: 13px;
color: #fff;
line-height: 1.4;
}
.has-tag {
background: linear-gradient(90deg, rgba(251,191,36,0.05) 0%, transparent 100%);
border-radius: 8px;
padding: 12px;
border-bottom: none;
margin-bottom: 4px;
}

View File

@@ -1,23 +1,26 @@
import React from 'react';
import './ChroniclePopup.css';
export default function ChroniclePopup({ chronicle, keywords = [], onClose }) {
export default function ChroniclePopup({ chronicle, acquiredTags = [], onClose }) {
return (
<div className="chronicle-overlay" onClick={onClose}>
<div className="chronicle-popup" onClick={e => e.stopPropagation()}>
<div className="chronicle-header">
<h2>국장의 연대기</h2>
<div className="chronicle-tags-summary">
획득한 내러티브: {acquiredTags.length}
</div>
<button className="close-btn" onClick={onClose}>&times;</button>
</div>
<div className="keyword-section">
<span className="keyword-label">수집한 키워드</span>
{keywords.length === 0 ? (
{acquiredTags.length === 0 ? (
<span className="keyword-empty">아직 수집한 키워드가 없습니다.</span>
) : (
<div className="keyword-chips">
{keywords.map((k, idx) => (
<span key={idx} className="keyword-chip" title={`Turn ${k.turn}`}>{k.word}</span>
{acquiredTags.map((t) => (
<span key={t.id} className="keyword-chip" title={t.description}>{t.icon} {t.name}</span>
))}
</div>
)}
@@ -27,14 +30,31 @@ export default function ChroniclePopup({ chronicle, keywords = [], onClose }) {
{chronicle.length === 0 ? (
<p className="empty-text">아직 기록된 역사가 없습니다.</p>
) : (
<ul className="chronicle-list">
<div className="chronicle-timeline">
{chronicle.map((entry, idx) => (
<li key={idx} className="chronicle-item">
<span className="turn-number">Turn {entry.turn}</span>
<p>{entry.text}</p>
</li>
<div key={idx} className={`chronicle-entry ${entry.tag ? 'has-tag' : ''}`}>
<div className="entry-turn">Turn {entry.turn}</div>
<div className="entry-body">
<div className="entry-context">
<span className="entry-type">[{entry.cardType}]</span>
{entry.keyword && <span className="entry-keyword">{entry.keyword}</span>}
</div>
<div className="entry-text">{entry.text}</div>
{entry.decision && <div className="entry-decision">👉 결정: {entry.decision}</div>}
{entry.tag && (
<div className="entry-tag-highlight">
<div className="tag-icon">{entry.tag.icon}</div>
<div className="tag-details">
<div className="tag-name">내러티브 획득: {entry.tag.name}</div>
<div className="tag-desc">{entry.tag.description}</div>
</div>
</div>
)}
</div>
</div>
))}
</ul>
</div>
)}
</div>
</div>

View File

@@ -149,32 +149,19 @@
margin-bottom: 20px;
}
.bankruptcy-warning {
margin-top: 12px;
.bankruptcy-warning-box {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 20px;
}
.penalty-text {
color: var(--color-risk-critical);
font-family: var(--font-ui);
font-size: 13px;
font-size: 16px;
font-weight: bold;
}
.council-screen.bankruptcy-termination {
justify-content: center;
align-items: center;
text-align: center;
gap: 16px;
}
.council-screen.bankruptcy-termination h2 {
font-family: var(--font-doc-title);
color: var(--color-risk-critical);
}
.bankruptcy-quote {
font-style: italic;
opacity: 0.8;
}
.council-screen.bankruptcy-termination .fat-btn {
max-width: 280px;
margin-top: 20px;
line-height: 1.6;
}

View File

@@ -1,23 +1,41 @@
import React, { useState, useEffect } from 'react';
import './CouncilScreen.css';
const getBankruptcyTier = (counter) => {
if (counter >= 8) return { label: '최종 경고', text: '이사회 최종 경고: 다음에도 장부가 마이너스라면, 당신의 자리는 없습니다.' };
if (counter >= 4) return { label: '공식 문책', text: '인사위원회가 소환장을 보냈습니다. 법인카드는 잠시 압수하겠습니다.' };
return { label: '가벼운 경고', text: '회계팀이 슬쩍 눈치를 주기 시작했습니다. 영수증 좀 잘 챙기시죠.' };
};
export default function CouncilScreen({ params, setParams, tokens, setTokens, tags, setTags, bankruptcyCounter, bankruptcyWarned, globalFactions = [], onBribe, onResolve }) {
export default function CouncilScreen({ params, setParams, tokens, setTokens, tags, setTags, bankruptcyCount, bankruptcyWarning, globalFactions = [], onBribe, onResolve }) {
const [members, setMembers] = useState([]);
const [agenda, setAgenda] = useState('');
const [impeachment, setImpeachment] = useState(false);
const [actionTaken, setActionTaken] = useState(false);
const [result, setResult] = useState(null);
const isNegative = tokens.budget < 0 || tokens.personnel < 0 || tokens.info < 0;
const bankruptcyTermination = isNegative && bankruptcyWarned;
const [isBankruptcyWarning, setIsBankruptcyWarning] = useState(false);
const [bankruptPenalty, setBankruptPenalty] = useState('');
useEffect(() => {
const isNegative = tokens.budget < 0 || tokens.personnel < 0 || tokens.info < 0;
if (isNegative) {
if (bankruptcyWarning) {
setAgenda('재무 건전성 악화 및 횡령 배임 혐의');
setResult('당신은 파산했고 자금 횡령으로 체포되었습니다. 감옥의 식사는 다행히 무료입니다.');
return;
}
setIsBankruptcyWarning(true);
setAgenda('파산 문책: 재무 경고');
if (bankruptcyCount >= 5) {
setBankruptPenalty(`파산 카운터 ${bankruptcyCount}회: 의회가 매우 분노했습니다. 모든 위험도 파라미터가 상승합니다.`);
setParams(prev => ({
resistance: Math.min(5, prev.resistance + 1),
entropy: Math.min(5, prev.entropy + 1),
panic: Math.min(5, prev.panic + 1),
risk: Math.min(5, prev.risk + 1),
}));
} else {
setBankruptPenalty(`파산 카운터 ${bankruptcyCount}회: 의회에서 강력한 경고가 나왔습니다. 다음 의회까지 복구하지 못하면 해임됩니다.`);
}
return;
}
// 상임 이사회 8명: 위험 수용 계수 기반 무작위 (성향 태그 세부 연동은 Phase 3 범위 밖)
const boardMembers = Array.from({ length: 8 }).map((_, i) => {
const rand = Math.random() * 100;
@@ -43,15 +61,15 @@ export default function CouncilScreen({ params, setParams, tokens, setTokens, ta
const randAgenda = Math.random() > 0.5 ? '이득 규약 법안 상정' : '표적 규약 법안 상정';
setAgenda(randAgenda);
}
// 의회는 마운트 시 1회만 초기화한다 (params.risk를 deps에 넣으면 매수 액션의 risk 상승이
// 이 effect를 재실행시켜 방금 강제 전환한 투표를 덮어써버리는 버그가 있었다)
// 의회는 마운트 시 1회만 초기화한다 (deps에 tokens/params.risk를 넣으면 매수 액션 등으로
// 바뀔 때마다 재실행되어 방금 강제 전환한 투표 결과를 덮어써버리는 버그가 있었다)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleAction = (type) => {
if (actionTaken) return;
const newMembers = [...members];
if (type === 'bribe' && tokens.budget >= 30) {
setTokens(prev => ({ ...prev, budget: prev.budget - 30 }));
setParams(prev => ({ ...prev, risk: Math.min(5, prev.risk + 1) }));
@@ -85,23 +103,32 @@ export default function CouncilScreen({ params, setParams, tokens, setTokens, ta
} else {
return; // Not enough tokens
}
setMembers(newMembers);
setActionTaken(true);
};
const finalize = () => {
if (result && result.includes('파산')) {
onResolve({ bankrupted: true });
return;
}
if (result) {
onResolve({ impeachment: false, bankruptcy: false, bankruptcyWarning: isNegative });
onResolve({ impeachment: false });
return;
}
if (isBankruptcyWarning) {
onResolve({ impeachment: false, bankruptcyWarning: true });
return;
}
const againstCount = members.filter(m => m.vote === '반대').length;
if (impeachment) {
if (againstCount >= 9) {
onResolve({ impeachment: true, bankruptcy: false, bankruptcyWarning: isNegative });
onResolve({ impeachment: true });
} else {
onResolve({ impeachment: false, bankruptcy: false, bankruptcyWarning: isNegative });
onResolve({ impeachment: false, bankruptcyWarning: false });
}
} else {
const supportCount = members.filter(m => m.vote === '찬성').length;
@@ -112,27 +139,15 @@ export default function CouncilScreen({ params, setParams, tokens, setTokens, ta
setTags(prev => ({ ...prev, wealth: Math.max(0, prev.wealth - 1) }));
}
}
onResolve({ impeachment: false, bankruptcy: false, bankruptcyWarning: isNegative });
// 토큰이 정상 범위로 돌아왔다면 파산 경고를 해제한다
onResolve({ impeachment: false, bankruptcyWarning: false });
}
};
if (bankruptcyTermination) {
return (
<div className="council-screen bankruptcy-termination">
<h2>🚨 재정 파탄 특별 청문회</h2>
<p>지난 경고에도 불구하고 장부는 여전히 마이너스입니다. 이사회는 이상 변명을 듣지 않기로 했습니다.</p>
<p className="bankruptcy-quote">"숫자는 거짓말을 하지 않습니다. 당신의 자리도 이제 안녕입니다."</p>
<button className="fat-btn" onClick={() => onResolve({ impeachment: false, bankruptcy: true, bankruptcyWarning: true })}>해임 통보 수령</button>
</div>
);
}
const tier = getBankruptcyTier(bankruptcyCounter);
return (
<div className="council-screen">
<div className="council-header">
<h2>[📜 이번 상정: {agenda}]</h2>
<h2>[📜 이번 정기 상정: {agenda}]</h2>
<div className="risk-bar-container">
<span>위험 수용 계수:</span>
<div className="risk-bar">
@@ -140,51 +155,58 @@ export default function CouncilScreen({ params, setParams, tokens, setTokens, ta
</div>
</div>
{impeachment && <div className="warning-blink">[🚨 정치적 탄핵안 상정!]</div>}
{isNegative && !bankruptcyWarned && (
<div className="bankruptcy-warning"> [{tier.label}] {tier.text}</div>
)}
</div>
<div className="members-grid">
{members.map(m => (
<div key={m.id} className={`member-icon ${m.type}`} title={m.label}>
<span className="member-avatar">👤</span>
<span className="member-badge">
{m.vote === '찬성' ? '✅' : m.vote === '반대' ? '❌' : '❓'}
</span>
</div>
))}
</div>
{isBankruptcyWarning ? (
<div className="bankruptcy-warning-box">
<p className="penalty-text">{bankruptPenalty}</p>
</div>
) : (
<div className="members-grid">
{members.map(m => (
<div key={m.id} className={`member-icon ${m.type}`} title={m.label}>
<span className="member-avatar">👤</span>
<span className="member-badge">
{m.vote === '찬성' ? '✅' : m.vote === '반대' ? '❌' : '❓'}
</span>
</div>
))}
</div>
)}
{result ? (
<div className="council-result">
<h3>결과: {result}</h3>
<button className="fat-btn" onClick={finalize}>의회 종료</button>
</div>
) : isBankruptcyWarning ? (
<div className="council-actions">
<button className="fat-btn action-proceed" onClick={finalize}>경고 수용 의회 종료</button>
</div>
) : (
<div className="council-actions">
<button
className={`fat-btn ${tokens.budget < 30 ? 'disabled' : ''}`}
<button
className={`fat-btn ${tokens.budget < 30 ? 'disabled' : ''}`}
onClick={() => handleAction('bribe')}
disabled={actionTaken || tokens.budget < 30}
>
💰 매수 (예산 30 소모 / 임원 찬성)
</button>
<button
className={`fat-btn ${tokens.info < 30 ? 'disabled' : ''}`}
<button
className={`fat-btn ${tokens.info < 30 ? 'disabled' : ''}`}
onClick={() => handleAction('manipulate')}
disabled={actionTaken || tokens.info < 30}
>
👁 장부 조작 (정보 30 소모 / 안건 이월)
</button>
<button
className={`fat-btn ${tokens.personnel < 30 ? 'disabled' : ''}`}
<button
className={`fat-btn ${tokens.personnel < 30 ? 'disabled' : ''}`}
onClick={() => handleAction('audit')}
disabled={actionTaken || tokens.personnel < 30}
>
🩸 내부 감사 (인력 30 소모 / 기권 유도)
</button>
{actionTaken && (
<button className="fat-btn action-proceed" onClick={finalize}>표결 진행</button>
)}

View File

@@ -52,6 +52,13 @@
border: 1px solid rgba(255, 255, 255, 0.1);
}
.round-info {
font-family: var(--font-typewriter);
font-size: 12px;
color: #999;
white-space: nowrap;
}
.parameters {
display: flex;
gap: 15px;
@@ -227,3 +234,43 @@
background: var(--color-text);
color: var(--color-bg);
}
.tag-notification {
position: absolute;
top: 100px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.9);
border: 1px solid #fbbf24;
border-radius: 12px;
padding: 12px 24px;
display: flex;
align-items: center;
gap: 16px;
box-shadow: 0 4px 20px rgba(251, 191, 36, 0.4);
z-index: 100;
animation: slideDownFadeOut 3s forwards;
}
.tag-noti-icon {
font-size: 32px;
}
.tag-noti-info h4 {
margin: 0;
color: #fbbf24;
font-size: 14px;
}
.tag-noti-info p {
margin: 4px 0 0 0;
color: #fff;
font-weight: bold;
font-size: 18px;
}
@keyframes slideDownFadeOut {
0% { opacity: 0; transform: translate(-50%, -20px); }
10% { opacity: 1; transform: translate(-50%, 0); }
80% { opacity: 1; transform: translate(-50%, 0); }
100% { opacity: 0; transform: translate(-50%, -20px); }
}

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import Papa from 'papaparse';
import cardsCsvRaw from '../data/cards.csv?raw';
import narrativeTagsData from '../data/narrative_tags.json';
import { BookOpen, HandCoins, Droplet, Eye, Shield, Building2, EyeOff } from 'lucide-react';
import SwipeCard from './SwipeCard';
import CouncilScreen from './CouncilScreen';
@@ -38,9 +39,10 @@ const pickGrade = (level) => {
return 'C';
};
// 내러티브 키워드 누적치에 따른 페이즈 전환 문턱 (기존 4/8은 실제 플레이에서 너무 길게 느껴져 하향 조정)
const PHASE2_TAG_THRESHOLD = 2;
const PHASE3_TAG_THRESHOLD = 4;
// 내러티브 태그는 narrative_tags.json 풀(10종)에서 "처음 획득하는 태그"만 진행도에 반영된다.
// 문턱은 풀 크기 대비 낮게 잡는다 (기존 4/8은 실제 플레이에서 길게 느껴진다는 피드백 반영).
const PHASE2_TAG_THRESHOLD = 3;
const PHASE3_TAG_THRESHOLD = 6;
const ASSET_META = {
transform: { icon: '🧪', label: '성질 변환' },
@@ -58,7 +60,7 @@ const GLOBAL_FACTIONS = [
{ id: 'spindoctor', name: '여론 통제국', computeAffinity: (c) => c.tags.surveillance * 18 - 30 },
{ id: 'union', name: '현장 요원 노조', computeAffinity: (c) => (2 - c.params.resistance) * 30 },
{ id: 'risk', name: '리스크 관리반', computeAffinity: (c) => (2 - c.params.risk) * 30 },
{ id: 'occult', name: '오컬트 수집가 협회', computeAffinity: (c) => c.mythicCount * 35 + c.narrativeTags * 3 - 20 },
{ id: 'occult', name: '오컬트 수집가 협회', computeAffinity: (c) => c.mythicCount * 35 + c.tagCount * 3 - 20 },
{ id: 'blackops', name: '극비 프로젝트 연구소', computeAffinity: (c) => c.assetsAcquiredCount * 25 - 30 },
{ id: 'scavenger', name: '잔해 청소부 길드', computeAffinity: (c) => c.purgeCount * 35 - 20 },
{ id: 'entropy', name: '엔트로피 관측소', computeAffinity: (c) => (2 - c.params.entropy) * 30 },
@@ -78,13 +80,15 @@ const TIER_IMMUNE = new Set(['동맹', '파국']);
const buildCardFromRow = (row) => ({
id: row.id,
type: row.type,
keyword: row.keyword,
question: row.question,
text: row.text,
flavor: row.flavor,
grade: row.grade || null,
contaminated: CONTAM_TYPES.has(row.type),
left: {
text: row.left_text,
narrativeTag: row.left_narrative || null,
tag: row.left_tag ? narrativeTagsData.find(t => t.id === row.left_tag) : null,
stats: {
res: row.left_res || 0, ent: row.left_ent || 0, pan: row.left_pan || 0, rsk: row.left_rsk || 0,
bud: row.left_bud || 0, per: row.left_per || 0, inf: row.left_inf || 0,
@@ -93,7 +97,7 @@ const buildCardFromRow = (row) => ({
},
right: {
text: row.right_text,
narrativeTag: row.right_narrative || null,
tag: row.right_tag ? narrativeTagsData.find(t => t.id === row.right_tag) : null,
stats: {
res: row.right_res || 0, ent: row.right_ent || 0, pan: row.right_pan || 0, rsk: row.right_rsk || 0,
bud: row.right_bud || 0, per: row.right_per || 0, inf: row.right_inf || 0,
@@ -117,8 +121,8 @@ export function GameScreen() {
const [params, setParams] = useState({ resistance: 0, entropy: 0, panic: 0, risk: 0 });
const [tokens, setTokens] = useState({ budget: 50, personnel: 50, info: 50 });
const [tags, setTags] = useState({ wealth: 0, force: 0, surveillance: 0 });
const [narrativeTags, setNarrativeTags] = useState(0);
const [keywords, setKeywords] = useState([]);
const [acquiredTags, setAcquiredTags] = useState([]);
const [newTagNotification, setNewTagNotification] = useState(null);
const [phase, setPhase] = useState(1);
const [gameOver, setGameOver] = useState(false);
const [cards, setCards] = useState([]);
@@ -133,9 +137,13 @@ export function GameScreen() {
const [showChronicle, setShowChronicle] = useState(false);
const [assets, setAssets] = useState([]);
// 라운드 구조: 라운드가 증가할수록 정례회의까지 남은 결재 건수가 줄어든다 (시간 압박 시각화)
const [roundCount, setRoundCount] = useState(1);
const [approvalsRemaining, setApprovalsRemaining] = useState(10);
// 파산 문책 시스템
const [bankruptcyCounter, setBankruptcyCounter] = useState(0);
const [bankruptcyWarned, setBankruptcyWarned] = useState(false);
const [bankruptcyCount, setBankruptcyCount] = useState(0);
const [bankruptcyWarning, setBankruptcyWarning] = useState(false);
// 자산 효과 상태
const [mulliganShields, setMulliganShields] = useState(0);
@@ -179,9 +187,9 @@ export function GameScreen() {
setChairmanAxis(CHAIRMAN_AXES[Math.floor(Math.random() * CHAIRMAN_AXES.length)]);
}, []);
// 내러티브 키워드가 PHASE2_TAG_THRESHOLD 도달 시 페이즈2 카드 풀(작전/극비) 개방 — 스토리 페이징 카드 풀 전환
// 내러티브 태그가 PHASE2_TAG_THRESHOLD 도달 시 페이즈2 카드 풀(작전/극비) 개방
useEffect(() => {
if (phase2Unlocked || phase2Pool.length === 0 || narrativeTags < PHASE2_TAG_THRESHOLD) return;
if (phase2Unlocked || phase2Pool.length === 0 || acquiredTags.length < PHASE2_TAG_THRESHOLD) return;
setPhase2Unlocked(true);
setCards(prev => {
const shuffledPool = [...phase2Pool].sort(() => Math.random() - 0.5);
@@ -190,12 +198,12 @@ export function GameScreen() {
next.splice(insertAt, 0, ...shuffledPool);
return next;
});
setChronicle(prev => [...prev, { turn: turns, text: '[페이징] 페이즈 2 진입 - 작전 / 극비 프로젝트 카드 풀이 개방되었습니다.' }]);
}, [narrativeTags, phase2Pool, phase2Unlocked, currentCardIndex, turns]);
setChronicle(prev => [...prev, { turn: turns, cardType: '페이징', keyword: null, text: '페이즈 2 진입 - 작전 / 극비 프로젝트 카드 풀이 개방되었습니다.', decision: '', tag: null }]);
}, [acquiredTags, phase2Pool, phase2Unlocked, currentCardIndex, turns]);
useEffect(() => {
// 위험 수용 계수(risk)는 PRD상 탄핵/법안 상정의 기준치일 뿐, 직접적인 게임오버 트리거가 아니다.
if (!gameOver && (params.resistance >= 5 || params.entropy >= 5 || params.panic >= 5)) {
// 위험 수용 계수(risk)는 PRD상 탄핵/법안 상정의 기준치일 뿐, 직접적인 게임오버 트리거가 아니다.
const ending = resolveEnding({
trigger: 'collapse', params, tags, tokens,
lastDelta: lastParamDelta, narrativeChainUnlocked,
@@ -203,9 +211,9 @@ export function GameScreen() {
setFinalEnding(ending);
setGameOver(true);
}
if (narrativeTags >= PHASE2_TAG_THRESHOLD && narrativeTags < PHASE3_TAG_THRESHOLD) setPhase(2);
else if (narrativeTags >= PHASE3_TAG_THRESHOLD) setPhase(3);
}, [params, narrativeTags]);
if (acquiredTags.length >= PHASE2_TAG_THRESHOLD && acquiredTags.length < PHASE3_TAG_THRESHOLD) setPhase(2);
else if (acquiredTags.length >= PHASE3_TAG_THRESHOLD) setPhase(3);
}, [params, acquiredTags]);
// 시한부 격리된 오염 카드 복귀
useEffect(() => {
@@ -214,7 +222,7 @@ export function GameScreen() {
if (ready.length > 0) {
setCards(prev => [...prev, ...ready.map(({ returnTurn, ...rest }) => rest)]);
setQuarantineZone(prev => prev.filter(c => c.returnTurn > turns));
setChronicle(prev => [...prev, { turn: turns, text: `[자산] 격리했던 오염 카드 ${ready.length}건이 덱으로 복귀했습니다.` }]);
setChronicle(prev => [...prev, { turn: turns, cardType: '자산', keyword: null, text: `격리했던 오염 카드 ${ready.length}건이 덱으로 복귀했습니다.`, decision: '', tag: null }]);
}
}, [turns]);
@@ -244,6 +252,8 @@ export function GameScreen() {
let outcomeText = '';
const side = direction === 'left' ? card.left : direction === 'right' ? card.right : null;
let isNewTag = false;
let gainedTag = null;
if (side) {
const shouldNullify = card.contaminated && mulliganShields > 0;
@@ -255,7 +265,7 @@ export function GameScreen() {
const nextPersonnel = tokens.personnel + side.stats.per;
const nextInfo = tokens.info + side.stats.inf;
if (nextBudget < 0 || nextPersonnel < 0 || nextInfo < 0) {
setBankruptcyCounter(c => c + 1);
setBankruptcyCount(c => c + 1);
}
// 극단적 파라미터 조기 엔딩 판별용: 이번 결재에서 가장 크게 증가한 위험 파라미터 추적
@@ -268,10 +278,13 @@ export function GameScreen() {
if (maxParam) setLastParamDelta({ param: maxParam, amount: maxVal });
}
// 내러티브 키워드: 모든 카드가 아니라 선택된 일부 카드/선택지만 키워드를 부여한다
if (side.narrativeTag) {
setKeywords(prev => [...prev, { turn: turns + 1, word: side.narrativeTag }]);
setNarrativeTags(n => n + 1);
// 내러티브 태그: 재사용 가능한 태그 풀에서 처음 획득하는 태그일 때만 진행도에 반영
gainedTag = side.tag;
if (gainedTag && !acquiredTags.find(t => t.id === gainedTag.id)) {
isNewTag = true;
setAcquiredTags(prev => [...prev, gainedTag]);
setNewTagNotification(gainedTag);
setTimeout(() => setNewTagNotification(null), 3000);
}
// 내러티브 연쇄 엔딩: 오염 카드 4종이 정해진 순서로 등장했는지 추적
@@ -280,7 +293,6 @@ export function GameScreen() {
setContaminationSequence(nextSequence);
if (!narrativeChainUnlocked && isNarrativeChainComplete(nextSequence)) {
setNarrativeChainUnlocked(true);
setChronicle(prev => [...prev, { turn: turns + 1, text: '[내러티브] 사건들의 연쇄가 완성되었습니다 - 진실에 가까워졌습니다.' }]);
}
}
@@ -293,7 +305,10 @@ export function GameScreen() {
}
if (outcomeText) {
setChronicle(prev => [...prev, { turn: turns + 1, text: `[${card.type}] ${card.text} -> 결정: ${outcomeText}` }]);
setChronicle(prev => [...prev, {
turn: turns + 1, cardType: card.type, keyword: card.keyword, text: card.text,
decision: outcomeText, tag: isNewTag ? gainedTag : null,
}]);
}
// 극비 프로젝트 성공 시 자산 획득 (4종 중 랜덤)
@@ -307,11 +322,11 @@ export function GameScreen() {
}
setCurrentCardIndex(idx => idx + 1);
setTurns(t => t + 1);
const newTurns = turns + 1;
setTurns(newTurns);
if (newTurns > 0 && newTurns % 10 === 0) {
const newRemaining = approvalsRemaining - 1;
setApprovalsRemaining(newRemaining);
if (newRemaining <= 0) {
setShowCouncil(true);
}
};
@@ -336,21 +351,19 @@ export function GameScreen() {
next.splice(insertAt, 0, ...newCards);
return next;
});
setChronicle(prev => [...prev, { turn: turns, text: `[오염] 의회 종료 후 신규 오염 카드 ${newCards.length}건이 덱에 스며들었습니다.` }]);
setChronicle(prev => [...prev, { turn: turns, cardType: '오염', keyword: null, text: `정례회의 종료 후 신규 오염 카드 ${newCards.length}건이 덱에 스며들었습니다.`, decision: '', tag: null }]);
}
};
const handleCouncilResolve = (result) => {
setShowCouncil(false);
if (result.bankruptcy) {
if (result.bankrupted) {
setFinalEnding(getEndingById('bankruptcy'));
setGameOver(true);
return;
}
setBankruptcyWarned(result.bankruptcyWarning);
if (result.impeachment) {
const ending = resolveEnding({ trigger: 'impeachment', tags, chairmanAxis, narrativeChainUnlocked });
setFinalEnding(ending);
@@ -358,7 +371,28 @@ export function GameScreen() {
return;
}
if (typeof result.bankruptcyWarning === 'boolean') {
setBankruptcyWarning(result.bankruptcyWarning);
}
injectContaminationCards();
// 라운드 진행: 라운드가 증가할수록 다음 정례회의까지의 결재 건수가 줄어든다
const nextRound = roundCount + 1;
let nextApprovals;
if (roundCount === 1) nextApprovals = 8;
else if (roundCount === 2) nextApprovals = 6;
else if (roundCount === 3) nextApprovals = 4;
else if (roundCount === 4) nextApprovals = 2;
else nextApprovals = 0;
setRoundCount(nextRound);
setApprovalsRemaining(nextApprovals);
if (nextApprovals <= 0) {
setFinalEnding(getEndingById('term_expired'));
setGameOver(true);
}
};
// 자산 효과 4종 발동
@@ -366,7 +400,7 @@ export function GameScreen() {
if (asset.type === 'mulligan') {
setMulliganShields(s => s + 1);
setAssets(prev => prev.filter(a => a.id !== asset.id));
setChronicle(prev => [...prev, { turn: turns, text: '[자산] 결재권 강제 행사 방어막을 전개했습니다. 다음 오염 카드의 페널티가 무효화됩니다.' }]);
setChronicle(prev => [...prev, { turn: turns, cardType: '자산', keyword: null, text: '결재권 강제 행사 방어막을 전개했습니다. 다음 오염 카드의 페널티가 무효화됩니다.', decision: '', tag: null }]);
return;
}
@@ -374,7 +408,7 @@ export function GameScreen() {
cards.forEach((c, i) => { if (c.contaminated) contaminatedIdx.push(i); });
if (contaminatedIdx.length === 0) {
setChronicle(prev => [...prev, { turn: turns, text: `[자산] ${asset.label}을(를) 발동했지만 대상이 될 오염 카드가 없습니다.` }]);
setChronicle(prev => [...prev, { turn: turns, cardType: '자산', keyword: null, text: `${asset.label}을(를) 발동했지만 대상이 될 오염 카드가 없습니다.`, decision: '', tag: null }]);
setAssets(prev => prev.filter(a => a.id !== asset.id));
return;
}
@@ -383,17 +417,17 @@ export function GameScreen() {
const target = contaminatedIdx[Math.floor(Math.random() * contaminatedIdx.length)];
setCards(prev => prev.filter((_, i) => i !== target));
setPurgeCount(c => c + 1);
setChronicle(prev => [...prev, { turn: turns, text: '[자산] 오염 카드를 물리적으로 영구 소각했습니다.' }]);
setChronicle(prev => [...prev, { turn: turns, cardType: '자산', keyword: null, text: '오염 카드를 물리적으로 영구 소각했습니다.', decision: '', tag: null }]);
} else if (asset.type === 'quarantine') {
const targetSet = new Set(contaminatedIdx.slice(0, 2));
const quarantined = cards.filter((c, i) => targetSet.has(i));
setCards(prev => prev.filter((_, i) => !targetSet.has(i)));
setQuarantineZone(prev => [...prev, ...quarantined.map(c => ({ ...c, returnTurn: turns + 5 }))]);
setChronicle(prev => [...prev, { turn: turns, text: `[자산] 오염 카드 ${quarantined.length}건을 시한부 격리했습니다. (5턴 후 덱으로 복귀)` }]);
setChronicle(prev => [...prev, { turn: turns, cardType: '자산', keyword: null, text: `오염 카드 ${quarantined.length}건을 시한부 격리했습니다. (5턴 후 덱으로 복귀)`, decision: '', tag: null }]);
} else if (asset.type === 'transform') {
const target = contaminatedIdx[Math.floor(Math.random() * contaminatedIdx.length)];
setCards(prev => prev.map((c, i) => (i === target ? transformCard(c) : c)));
setChronicle(prev => [...prev, { turn: turns, text: '[자산] 오염 카드의 성질을 영구적으로 순화했습니다. (독을 약으로)' }]);
setChronicle(prev => [...prev, { turn: turns, cardType: '자산', keyword: null, text: '오염 카드의 성질을 영구적으로 순화했습니다. (독을 약으로)', decision: '', tag: null }]);
}
setAssets(prev => prev.filter(a => a.id !== asset.id));
@@ -409,8 +443,8 @@ export function GameScreen() {
return (
<EndingScreen
ending={finalEnding}
narrativeTags={narrativeTags}
keywords={keywords}
narrativeTags={acquiredTags.length}
keywords={acquiredTags.map(t => ({ word: t.name }))}
onRestart={() => window.location.reload()}
/>
);
@@ -422,7 +456,7 @@ export function GameScreen() {
tags, tokens, params,
assetsCount: assets.length,
mythicCount: cards.filter(c => c.type === '신화').length,
narrativeTags, assetsAcquiredCount, purgeCount, bribeCount,
tagCount: acquiredTags.length, assetsAcquiredCount, purgeCount, bribeCount,
};
const globalFactions = factionRoster.map(f => {
const score = f.computeAffinity(affinityCtx);
@@ -435,8 +469,8 @@ export function GameScreen() {
params={params} setParams={setParams}
tokens={tokens} setTokens={setTokens}
tags={tags} setTags={setTags}
bankruptcyCounter={bankruptcyCounter}
bankruptcyWarned={bankruptcyWarned}
bankruptcyCount={bankruptcyCount}
bankruptcyWarning={bankruptcyWarning}
globalFactions={globalFactions}
onBribe={() => setBribeCount(c => c + 1)}
onResolve={handleCouncilResolve}
@@ -456,7 +490,10 @@ export function GameScreen() {
{/* Top Bar */}
<div className="top-bar">
<div className="narrative-badge" onClick={() => setShowChronicle(true)} style={{cursor: 'pointer'}}>
<BookOpen size={16} /> <span>{narrativeTags}{nextTagThreshold !== null ? `/${nextTagThreshold}` : ''}</span>
<BookOpen size={16} /> <span>{acquiredTags.length}{nextTagThreshold !== null ? `/${nextTagThreshold}` : ''}</span>
</div>
<div className="round-info">
{roundCount} 정례회의 상정까지: 잔여 {approvalsRemaining}
</div>
<div className="parameters">
<div className={`param ${params.resistance >= 4 ? 'critical' : ''}`} style={{'--param-color': 'var(--color-resistance-critical)'}}>
@@ -518,10 +555,20 @@ export function GameScreen() {
</div>
</div>
{newTagNotification && (
<div className="tag-notification">
<div className="tag-noti-icon">{newTagNotification.icon}</div>
<div className="tag-noti-info">
<h4>새로운 내러티브 획득!</h4>
<p>[{newTagNotification.name}]</p>
</div>
</div>
)}
{showChronicle && (
<ChroniclePopup
chronicle={chronicle}
keywords={keywords}
acquiredTags={acquiredTags}
onClose={() => setShowChronicle(false)}
/>
)}

View File

@@ -50,10 +50,28 @@
.card-category {
font-family: var(--font-typewriter);
font-size: 18px;
font-size: 14px;
font-weight: 500;
color: var(--color-risk-base);
letter-spacing: 2px;
display: block;
margin-bottom: 8px;
}
.card-keyword {
font-family: var(--font-doc-title);
font-size: 20px;
color: #fff;
margin: 0 0 8px 0;
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
}
.card-question {
font-family: var(--font-ui);
font-size: 18px;
color: #fbbf24;
margin: 0;
font-weight: bold;
}
.card-body {
@@ -64,12 +82,12 @@
}
.card-text {
font-family: var(--font-doc-title);
font-size: 24px;
line-height: 1.5;
font-family: var(--font-ui);
font-size: 15px;
line-height: 1.6;
text-align: center;
color: var(--color-text);
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
color: rgba(255, 255, 255, 0.85);
text-shadow: 0 1px 2px rgba(0,0,0,0.8);
}
.swipe-intent {
@@ -106,26 +124,31 @@
.swipe-intent .intent-title {
background: rgba(0, 0, 0, 0.8);
padding: 8px 16px;
padding: 10px 20px;
border-radius: 8px;
border: 2px solid currentColor;
display: inline-block;
margin-bottom: 8px;
font-size: 24px;
font-weight: 900;
}
.card-flavor-container {
margin-top: auto;
padding-top: 15px;
border-top: 1px dashed rgba(255, 255, 255, 0.2);
padding: 12px;
background: rgba(0, 0, 0, 0.4);
border-radius: 8px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.card-flavor {
font-family: var(--font-ui);
font-size: 14px;
font-family: var(--font-typewriter);
font-size: 13px;
font-style: italic;
color: rgba(255,255,255,0.6);
color: #a1a1aa;
text-align: center;
line-height: 1.4;
margin: 0;
}
.stats-preview {
@@ -145,10 +168,10 @@
.stat-item {
display: flex;
align-items: center;
justify-content: space-between;
justify-content: center;
width: 100%;
font-family: var(--font-ui);
font-size: 14px;
font-size: 18px;
font-weight: bold;
}
@@ -156,17 +179,17 @@
display: flex;
align-items: center;
justify-content: center;
width: 20px;
width: 24px;
font-size: 18px;
}
.stat-icon svg {
width: 18px;
height: 18px;
}
.stat-label {
flex: 1;
text-align: left;
margin-left: 6px;
font-size: 12px;
text-transform: none;
letter-spacing: 0;
color: #fff;
display: none;
}
.stat-val {
@@ -180,3 +203,27 @@
.stat-bad {
color: #f87171;
}
.intent-tag-reward {
background: rgba(251, 191, 36, 0.9);
color: #000;
padding: 8px 16px;
border-radius: 8px;
display: inline-flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
font-weight: bold;
font-family: var(--font-ui);
box-shadow: 0 0 15px rgba(251, 191, 36, 0.5);
animation: pulseTag 1.5s infinite;
}
.intent-tag-icon {
font-size: 20px;
}
@keyframes pulseTag {
0% { transform: scale(1); box-shadow: 0 0 15px rgba(251, 191, 36, 0.5); }
50% { transform: scale(1.05); box-shadow: 0 0 25px rgba(251, 191, 36, 0.8); }
100% { transform: scale(1); box-shadow: 0 0 15px rgba(251, 191, 36, 0.5); }
}

View File

@@ -95,6 +95,8 @@ export default function SwipeCard({ card, onSwipe, phase }) {
<div className="card-content">
<div className="card-header">
<span className="card-category">[{card.type}]</span>
{card.keyword && <h3 className="card-keyword">{card.keyword}</h3>}
{card.question && <h2 className="card-question">{card.question}</h2>}
</div>
<div className="card-body">
<p className="card-text">{card.text}</p>
@@ -109,10 +111,22 @@ export default function SwipeCard({ card, onSwipe, phase }) {
{/* Swipe Intents */}
<motion.div className="swipe-intent left-intent" style={{ opacity: leftOpacity }}>
<span className="intent-title">{card.left?.text}</span>
{card.left?.tag && (
<div className="intent-tag-reward">
<span className="intent-tag-icon">{card.left.tag.icon}</span>
<span className="intent-tag-name">{card.left.tag.name}</span>
</div>
)}
{renderStats(card.left?.stats)}
</motion.div>
<motion.div className="swipe-intent right-intent" style={{ opacity: rightOpacity }}>
<span className="intent-title">{card.right?.text}</span>
{card.right?.tag && (
<div className="intent-tag-reward">
<span className="intent-tag-icon">{card.right.tag.icon}</span>
<span className="intent-tag-name">{card.right.tag.name}</span>
</div>
)}
{renderStats(card.right?.stats)}
</motion.div>
{card.type === '극비' && (

View File

@@ -0,0 +1,30 @@
[
{
"id": "asset_01",
"name": "전임 국장의 만년필",
"icon": "🖋️",
"description": "피로 쓰여진 듯한 잉크가 나옵니다. 예산 관리에 소소한 도움을 줍니다.",
"flavor": "\"예산 전용 동의서에 서명하기 아주 좋은 펜이군.\""
},
{
"id": "asset_02",
"name": "비밀 스위스 은행 계좌",
"icon": "🏦",
"description": "마이너스 예산을 한 번 막아줄 수 있습니다.",
"flavor": "\"우리 기구가 파산해도 내 노후는 안전해야지.\""
},
{
"id": "asset_03",
"name": "기억 소거 장치",
"icon": "🔦",
"description": "불리한 결재 건을 한 번 취소할 수 있습니다.",
"flavor": "\"자, 여기 불빛을 보세요. 당신은 방금 아무것도 보지 못했습니다.\""
},
{
"id": "asset_04",
"name": "방탄 커피 머신",
"icon": "☕",
"description": "요원들의 불만을 약간 억제합니다.",
"flavor": "\"아무리 세상이 멸망해도 모닝 커피는 포기 못하지.\""
}
]

View File

@@ -1,49 +1,48 @@
id,type,text,flavor,left_text,left_res,left_ent,left_pan,left_rsk,left_bud,left_per,left_inf,left_wea,left_for,left_sur,right_text,right_res,right_ent,right_pan,right_rsk,right_bud,right_per,right_inf,right_wea,right_for,right_sur,grade,left_narrative,right_narrative
1,일상,B구역 하급 요원들이 예산 삭감에 반발해 장비 반납 시위를 하고 있습니다.,우리가 세상을 구하는데 월급이 깎인다고요? 촉수에 맞아 죽으나 굶어 죽으나 똑같습니다!,무력 진압,1,0,0,0,0,-10,0,0,0,0,요구 수용,0,0,0,0,-20,0,0,0,0,0
2,위기,지하 4층 격리실의 문은 닫혀 있습니다만 요원 12명이 남긴 체액의 총량이 인간 12명의 부피를 초과했습니다.,보고서 첨부: 액체화된 직원은 산재 처리가 안 됩니다.,네이팜 소각 강행,0,0,0,0,0,-30,0,0,0,0,조사팀 파견,0,1,0,1,0,0,0,0,0,0,,증거 인멸 소각,진상 조사 착수
3,일상,7구역에 촉수 괴물이 출현했습니다. 작전팀이 법인카드 영수증 제출을 누락해서 출동 승인이 반려된 상태입니다.,규정은 규정입니다. 영수증 없이 세계를 구하는 건 용납할 수 없습니다.,내역 위조 후 출동,0,0,0,1,0,0,-10,0,0,0,규정대로 반려,1,1,0,0,0,0,0,0,0,0
4,극비,진화 미래당이 제안합니다. 요원들에게 괴수의 DNA를 소량 이식하면 작전 효율이 300% 상승합니다. 예산은 우리가 대죠.,인간의 한계는 명확합니다. 조금 징그러워지는 것쯤은 감수해야죠.,실험 승인,0,2,0,0,40,40,0,0,0,0,제안 거절,1,0,0,0,0,0,0,0,0,0,,인체 실험 승인,진화 계획 거절
5,작전,철의 수호당이 민간인 구역에 전술 폭격을 가해 S급 변칙 개체를 제거하자고 건의했습니다.,희생 없는 승리는 없습니다. 소를 위해 대를... 아니 대를 위해 소를 희생하죠.,궤도 폭격 승인,0,0,0,2,0,0,0,0,2,0,작전 기각,1,0,0,0,0,0,0,0,0,0,,민간인 희생 묵인,온건 노선 고수
6,일상,차원 무역당 소속 브로커가 처리된 괴수의 부산물을 암시장에 팔아치우자고 속삭입니다.,이 이빨 하나면 국장님 연봉의 10배입니다. 눈 한 번만 딱 감으시죠.,매각 허가,0,0,0,1,30,0,0,0,0,0,원칙대로 소각,0,0,0,0,-10,0,0,0,0,0
7,작전,대중 매체를 장악하여 I.S.C.O의 존재를 서서히 양지화하는 프로젝트입니다. 막대한 정보 토큰이 소모됩니다.,우리가 이 행성의 진짜 지배자임을 알릴 때가 되었습니다.,감시망 구축,0,0,0,0,0,0,-40,0,0,2,프로젝트 보류,1,0,0,0,0,0,0,0,0,0,,조직 양지화 착수,은둔 노선 유지
8,극비,고대 언어 해독을 위해 대규모 도서관 건립이 필요합니다. 지식 보존당이 학자들을 동원할 것입니다.,이해할 수 없는 것을 죽이는 건 야만인이나 하는 짓입니다.,예산 배정,0,0,0,0,-40,0,40,0,0,0,예산 전액 삭감,1,0,0,1,0,0,0,0,0,0
9,위기,기술 원리주의자들이 마녀사냥을 시작했습니다. 기구 내 마법 사용 요원들을 이단으로 규정하고 색출 중입니다.,더러운 마법과 변이에 의존해선 안 됩니다. 오직 순수한 인간의 힘으로!,적당히 묵인한다,1,0,0,0,0,-30,0,0,0,0,군사력으로 제압,0,0,0,1,0,0,-20,0,0,0
10,일상,한 유튜버가 변칙 현상을 촬영해 업로드했습니다. 침묵의 장막당이 즉각적인 물리적 제거와 기억 소거를 요구합니다.,대중은 진실을 감당할 지능이 없습니다. 무덤까지 비밀을 덮으십시오.,제거 절차 승인,0,0,0,-1,0,0,-20,0,0,0,영상만 삭제,0,1,0,0,0,0,0,0,0,0
11,위기,특정 구역의 사이비 종교가 고대 신을 소환하기 위해 시민 100명을 인신 공양하려 합니다. 공존당은 이를 방관하라 합니다.,위대한 그분들이 배가 고프다 하십니다. 조금만 양보하면 평화롭습니다.,군사 개입 지시,1,0,1,0,0,-40,0,0,0,0,의식 방관,0,2,0,0,0,0,0,0,0,0
12,위기,의회에서 국장의 예산 전용 의혹을 제기하며 긴급 감사를 통보했습니다. 장부를 조작해야 합니다!,세상을 구하는 데 돈 좀 쓴 게 죄입니까? 빨리 덮을 정보팀을 부르십시오.,은폐 공작 실행,0,0,0,1,0,0,-40,0,0,0,횡령 사실 인정,0,0,0,2,0,0,0,0,0,0,,은폐 공작 실행,횡령 자백
13,일상,연구 부서에서 초과 근무로 인한 집단 피로를 호소하며 휴가와 특별 보너스를 요구하고 있습니다.,저희도 사람입니다. 괴수 해부하다가 과로사하긴 싫어요.,특별 보너스 지급,-1,0,0,0,-30,0,0,0,0,0,요구 전면 묵살,1,0,0,0,0,0,0,0,0,0
14,작전,이계와의 무역로를 임시 개방하여 자원을 조달하자는 안건이 상정되었습니다. 미지의 오염이 우려됩니다.,저 너머의 세계엔 우리가 상상도 못 할 자원이 널려있습니다. 황금알을 낳는 거위죠.,게이트 강제 개방,0,1,0,0,50,0,0,0,0,0,폐쇄 상태 유지,1,0,0,0,0,0,0,0,0,0,,이계 무역로 개방,고립주의 선택
15,극비,전임 국장이 남긴 비밀 금고를 발견했습니다. 여는 데 특수 장비와 전문 인력이 필요합니다.,전임 국장님이 숙청당하기 전 남긴 비자금 장부일지도 모릅니다.,강제 개방 지시,0,0,0,0,20,-30,0,1,0,0,영구 봉인,0,0,0,0,0,0,0,0,0,0,,비자금 장부 발견,과거를 봉인
16,작전,글로벌 메가코프를 협박해 I.S.C.O의 비자금을 세탁하고 대규모 자금을 확보할 기회입니다.,그들이 우리 덕분에 안전하게 사업하는 겁니다. 보호세 좀 걷는 게 뭐 어떻습니까.,작전 실행,0,0,0,0,0,0,-30,2,0,0,작전 취소,1,0,0,0,0,0,0,0,0,0
17,위기,제7구역이 S급 변칙 개체에 의해 완전히 붕괴될 위기입니다. 민간인을 포기하고 융단 폭격을 해야 할까요?,시민 10만 명 구출 비용보다 유가족 합의금이 예산을 15% 더 아낄 수 있습니다.,시민 포기 및 폭격,0,-1,0,2,0,0,0,0,0,0,결사 방어 명령,0,0,0,0,-40,-40,0,0,0,0,,민간인 희생 승인,결사 방어 명령
18,일상,새로운 회식비 규정이 너무 짜다며 행정부에서 파업을 예고했습니다. 괴수 사체 처리 지연으로 악취가 진동합니다.,기구의 절반은 서류 작업으로 돌아갑니다. 그들을 화나게 해선 안 됩니다.,예산 전용으로 무마,0,0,0,1,-20,0,0,0,0,0,파업 강경 대응,1,0,0,0,0,0,0,0,0,0
19,극비,비밀 연구소에서 인공지능과 죽은 요원의 뇌를 결합한 불침번 요원을 개발했습니다.,잠도 안 자고 월급도 안 받습니다. 윤리적 문제만 빼면 완벽한 자산이죠.,실전 즉각 배치,0,1,0,0,0,0,20,0,1,0,즉각 폐기 처분,1,0,0,0,-10,0,0,0,0,0
20,일상,기구 내부에 스파이가 있다는 첩보가 들어왔습니다. 대대적인 사상 검증이 필요할지도 모릅니다.,누군가 정보를 빼돌려 의회에 넘기고 있습니다. 잡히면 믹서기에 넣어야 합니다.,대규모 사찰 시작,2,0,0,0,0,0,30,0,0,0,증거 부족으로 덮음,0,0,0,1,0,0,0,0,0,0,,공포 정치 시작,의혹 은폐
21,작전,순수 인간당이 변이의 기미가 보이는 요원들을 격리 수용할 대규모 시설 건립을 제안했습니다.,괴물과 싸우다 보면 괴물이 되는 법. 그들이 선을 넘기 전에 가둬야 합니다.,수용소 건립 승인,0,0,0,0,-40,0,0,0,1,0,제안 단호히 거절,1,0,0,1,0,0,0,0,0,0,,변이체 강제 수용,차별 정책 거부
22,극비,고대 공존당의 사제들이 심해의 유물을 인양했습니다. 만지는 자의 정신을 갉아먹지만 막대한 지식을 줍니다.,약간의 두통만 참으면 이계의 비밀을 모두 알 수 있습니다. 요원 하나쯤 미쳐도 괜찮습니다.,유물 집중 연구,0,1,0,0,0,0,50,0,0,0,유물 파괴 명령,1,0,0,1,0,0,0,0,0,0
23,위기,얇아진 장막 너머로 거대한 눈동자가 며칠째 지구를 주시하고 있습니다. 대중의 공포가 확산되고 있습니다.,하늘을 보지 마십시오. 아니 차라리 눈을 감고 살게 하십시오.,인터넷 전면 차단,2,0,1,0,0,0,0,0,0,2,진정제 무상 배포,0,0,0,0,-50,0,0,0,0,0
24,일상,요원들이 괴수 진압 후 심각한 트라우마에 시달리고 있습니다. 심리 치료 지원이 시급합니다.,밤마다 촉수가 목을 조르는 악몽을 꾼답니다. 차라리 그 부분 기억을 지워버릴까요?,정신과 치료 지원,0,0,0,0,-20,10,0,0,0,0,트라우마 기억 소거,0,1,0,1,0,0,0,0,0,0
25,극비,의회 의원 중 한 명이 기구의 비리를 폭로하려 합니다. 침묵의 장막당이 사고사 처리를 제안합니다.,국장님 계단에서 구르거나 심장마비가 오는 건 아주 흔한 자연의 섭리입니다.,사고사 위장 지시,0,0,0,-1,0,0,-30,0,0,0,폭로전 정면 돌파,0,0,0,2,0,0,0,0,0,0,,내부 고발자 암살,비리 자진 시인
26,위기,차원 붕괴 폭탄의 타이머가 오작동을 일으켰습니다! 당장 끄지 않으면 7구역이 통째로 증발합니다.,최저가 입찰로 부품을 사서 폭탄을 만든 결과입니다. 누군가는 책임져야 합니다.,특수 해체팀 투입,0,0,0,0,0,-40,0,0,0,0,폭발 방관 및 대피,0,2,0,0,0,0,0,0,0,0,,목숨 건 해체 작전,구역 포기 결정
27,작전,군부와 결탁하여 민간 군사 기업(PMC)을 I.S.C.O의 통제 하에 두려는 작전입니다.,돈으로 충성심을 사는 건 가장 안전한 방법입니다. 물론 돈이 많을 때 이야기죠.,결탁 및 흡수 합병,0,0,0,0,-40,0,0,0,2,0,독자적 노선 유지,1,0,0,0,0,0,0,0,0,0
28,작전,변칙 개체 관찰을 위해 1구역을 돔으로 덮고 거대한 실험장으로 만들자는 제안이 올라왔습니다.,시민들이 좀 불편해하겠지만 과학적 진보를 위해 그 정도는 감수해야죠.,돔 건설 강행,0,1,0,0,-50,0,0,0,0,0,윤리적 이유로 기각,0,0,0,1,0,0,0,0,0,0
29,극비,이계의 군주가 외교 사절을 보내왔습니다. 요원 10명을 노예로 주면 최고급 이계 무기를 주겠다고 합니다.,요원 10명의 생명 가치보다 이계 무기가 가져올 파급력이 월등히 높습니다.,거래 즉시 성사,0,0,0,0,0,-40,0,0,2,0,사절단 전원 사살,0,1,0,1,0,0,0,0,0,0,,요원을 노예로 상납,외교 사절 몰살
30,일상,국장님 믹스 커피가 다 떨어졌습니다. 이게 무슨 세상의 종말보다 더 큰 위기인 줄 아십니까? 행정부서의 불만이 폭주합니다.,종말을 결재하기 전엔 커피 한 잔의 여유가 필요한 법입니다. 안 그렇습니까?,최고급 원두 지원,-1,0,0,0,-10,0,0,0,0,0,헛소리 금지령,1,0,0,0,0,0,0,0,0,0
31,위기,신화적 존재가 깨어나려 합니다. 하늘이 피처럼 붉게 물들었고 요원들은 미쳐가고 있습니다!,최종 결재의 시간입니다. 세상의 물리 법칙을 비틀 것입니까 아니면 피로 막을 것입니까?,현실 조작기 가동,0,2,0,0,0,0,-50,0,0,0,모든 예비병력 투입,0,0,0,1,0,-60,0,0,0,0,,현실 왜곡 감행,총력전 돌입
32,신화,격리된 실험체 하나가 벽을 긁으며 알아들을 수 없는 노래를 흥얼거립니다.,직원들 사이에서 그 노래가 귓가에 맴돈다는 소문이 돕니다.,진정제 투여 및 격리 강화,0,0,0,0,-15,0,0,0,0,0,그냥 무시하고 넘어간다,0,1,0,0,0,0,0,0,0,0,C
33,신화,지하 수조에서 건져올린 조각상이 자정마다 미세하게 위치를 바꿉니다.,CCTV 확인 결과 아무도 없었는데 조각상은 분명 돌아서 있었습니다.,석관에 봉인 후 콘크리트 매립,0,0,0,0,-30,-10,0,0,0,0,일단 창고에 방치,0,1,1,0,0,0,0,0,0,0,B
34,신화,심해 탐사팀이 인양한 알 하나가 부화 징후를 보입니다. 안에서 무언가 박동합니다.,생물학팀은 저것은 알이 아니라 심장이라고 보고했습니다.,즉각 냉동 처리 및 소각,0,0,0,0,-50,-20,0,0,0,0,부화 과정 관찰 지속,0,2,1,0,0,0,0,0,0,0,A,미지의 알 폐기,금단의 부화 관찰
35,신화,장막 너머의 존재가 마침내 자신의 진짜 이름을 발음하기 시작했습니다. 듣는 요원들의 코피가 멈추지 않습니다.,언어병리학자는 이름의 세 음절만 듣고도 실어증에 걸렸습니다.,전 요원 청각 차단 및 격리 봉쇄,0,1,0,0,-70,-40,0,0,0,0,이름을 끝까지 듣고 기록한다,0,3,2,0,0,0,0,0,0,0,S,금단의 이름 봉인,금단의 이름 기록
36,조직저항,현장 요원 셋이 초과근무수당 미지급에 항의하며 보고서 제출을 하루 미뤘습니다.,정시 퇴근이 인권이라는 팻말을 봤습니다.,밀린 수당 즉시 지급,0,0,0,0,-15,0,0,0,0,0,규정 운운하며 묵살,1,0,0,0,0,0,0,0,0,0,C
37,조직저항,중간관리자 다섯이 연명으로 국장의 승인 절차에 대한 항명 성명을 냈습니다.,서명자 명단에 낯익은 이름이 보입니다. 승진 대상자였는데요.,일대일 면담으로 무마,0,0,0,0,0,-25,0,0,0,0,주동자 색출 및 좌천,2,0,0,0,0,0,0,0,0,0,B
38,조직저항,격리동 경비 인력 절반이 동시에 병가를 제출했습니다. 명백한 태업입니다.,다들 같은 날 같은 감기에 걸렸다고 합니다. 참으로 공교롭죠.,대체 인력 긴급 투입,0,0,0,0,-30,-15,0,0,0,0,복귀할 때까지 방치,2,0,1,0,0,0,0,0,0,0,A,태업 진압,조직 와해 방관
39,조직저항,전 부서장급 열두 명이 동시 사표를 제출하며 내부 기밀을 언론에 흘리겠다 협박합니다.,이것은 항명이 아니라 쿠데타입니다.,전원 요구 수용 및 대규모 숙청,1,0,0,0,-60,-50,0,0,0,0,협박을 무시하고 강경 대응,3,1,0,0,0,0,0,0,0,0,S,대숙청 단행,쿠데타 조짐
40,사회공황,동네 커뮤니티에 하늘에서 이상한 빛을 봤다는 글이 올라와 조회수가 치솟고 있습니다.,댓글 반응은 반반입니다. 반은 조작설 반은 목격담 추가.,여론 조작팀 투입해 진화,0,0,0,0,0,0,-15,0,0,0,며칠 지나면 묻힐 겁니다,0,0,1,0,0,0,0,0,0,0,C
41,사회공황,한 지역 방송국이 괴수 목격담을 특집 뉴스로 다뤘습니다. 시청률이 세 배 뛰었습니다.,미스터리 특종이라는 자막이 하루 종일 반복 재생됩니다.,방송국 인수 후 정정 보도,0,0,0,0,-30,0,0,0,0,0,허위 정보로 규정하고 방치,0,0,2,0,0,0,0,0,0,0,B
42,사회공황,대형 마트마다 생필품 사재기 행렬이 이어지고 있습니다. 폭동 조짐이 보입니다.,화장지 한 팩을 두고 몸싸움이 벌어졌다는 신고가 열 건째입니다.,비상 물자 방출 및 진정,0,0,0,0,-40,0,-15,0,0,0,질서 유지는 각자 알아서,1,0,2,0,0,0,0,0,0,0,A,비상 물자 방출,치안 붕괴 방관
43,사회공황,종말론에 심취한 신도 수천 명이 본부 앞으로 행진하며 심판을 요구합니다.,그들의 팻말에는 이상하게도 정확한 좌표가 적혀 있었습니다.,전면 계엄 수준의 통제 발동,0,1,1,0,-50,-30,0,0,0,0,군중 앞에서 진실을 공개한다,0,1,3,0,0,0,0,0,0,0,S,계엄 선포,진실 공개 선언
44,이사회압박,감사실에서 지난달 접대비 영수증 몇 장을 문제 삼기 시작했습니다.,법인카드로 산 초밥이 감사 보고서 제목이 됐습니다.,영수증 재발급 후 해명,0,0,0,0,-15,0,0,0,0,0,바쁘다는 핑계로 무시,0,0,0,1,0,0,0,0,0,0,C
45,이사회압박,이사회가 예산 집행 내역 전수 조사를 예고했습니다.,회계팀 막내가 벌써부터 사직서 초안을 쓰고 있습니다.,회계 자료 사전 정리,0,0,0,0,0,-25,0,0,0,0,조사에 순순히 응한다,0,0,0,2,0,0,0,0,0,0,B
46,이사회압박,긴급 임시 이사회가 소집되어 국장의 결재권 일부 정지를 논의합니다.,의제 제목이 국장의 독단 경영에 대하여입니다.,핵심 임원 개별 설득,0,0,0,0,-40,0,-15,0,0,0,정면으로 맞선다,1,0,0,2,0,0,0,0,0,0,A,막후 로비,이사회와 정면 충돌
47,이사회압박,청문회 소환장이 도착했습니다. 결재권 자체를 박탈하겠다는 초강경 안건입니다.,출석하지 않으면 그 자체로 유죄로 간주하겠다는 문구가 적혀 있습니다.,총력을 다해 청문회 방어,0,0,0,1,-60,-30,-20,0,0,0,청문회를 보이콧한다,1,1,0,3,0,0,0,0,0,0,S,청문회 사수,청문회 보이콧
,,,,,,,,,,,,,,,,,,,,,,,,,
id,type,keyword,question,text,flavor,left_text,left_tag,left_res,left_ent,left_pan,left_rsk,left_bud,left_per,left_inf,left_wea,left_for,left_sur,right_text,right_tag,right_res,right_ent,right_pan,right_rsk,right_bud,right_per,right_inf,right_wea,right_for,right_sur,grade
1,일상,요원들의 시위 발생,시위를 어떻게 처리하시겠습니까?,B구역 하급 요원들이 예산 삭감에 반발해 장비 반납 시위를 하고 있습니다.,우리가 세상을 구하는데 월급이 깎인다고요? 촉수에 맞아 죽으나 굶어 죽으나 똑같습니다!,무력 진압,tag_ruthless,1,0,0,0,0,-10,0,0,0,0,요구 수용,,0,0,0,0,-20,0,0,0,0,0,
2,위기,격리실 오염 발생,오염을 어떻게 정화하시겠습니까?,지하 4층 격리실의 문은 닫혀 있습니다만 요원 12명이 남긴 체액의 총량이 인간 12명의 부피를 초과했습니다.,보고서 첨부: 액체화된 직원은 산재 처리가 안 됩니다.,네이팜 소각 강행,tag_coverup,0,0,0,0,0,-30,0,0,0,0,조사팀 파견,,0,1,0,1,0,0,0,0,0,0,
3,일상,촉수 괴물 출현,출동 승인하시겠습니까?,7구역에 촉수 괴물이 출현했습니다. 작전팀이 법인카드 영수증 제출을 누락해서 출동 승인이 반려된 상태입니다.,규정은 규정입니다. 영수증 없이 세계를 구하는 건 용납할 수 없습니다.,내역 위조 후 출동,,0,0,0,1,0,0,-10,0,0,0,규정대로 반려,,1,1,0,0,0,0,0,0,0,0,
4,극비,변이 DNA 이식 제안,실험을 승인하시겠습니까?,진화 미래당이 제안합니다. 요원들에게 괴수의 DNA를 소량 이식하면 작전 효율이 300% 상승합니다. 예산은 우리가 대죠.,인간의 한계는 명확합니다. 조금 징그러워지는 것쯤은 감수해야죠.,실험 승인,tag_madness,0,2,0,0,40,40,0,0,0,0,제안 거절,,1,0,0,0,0,0,0,0,0,0,
5,작전,전술 폭격 건의,폭격을 승인하시겠습니까?,철의 수호당이 민간인 구역에 전술 폭격을 가해 S급 변칙 개체를 제거하자고 건의했습니다.,희생 없는 승리는 없습니다. 소를 위해 대를... 아니 대를 위해 소를 희생하죠.,궤도 폭격 승인,tag_sacrifice,0,0,0,2,0,0,0,0,2,0,작전 기각,,1,0,0,0,0,0,0,0,0,0,
6,일상,부산물 암시장 매각,매각을 허가하시겠습니까?,차원 무역당 소속 브로커가 처리된 괴수의 부산물을 암시장에 팔아치우자고 속삭입니다.,이 이빨 하나면 국장님 연봉의 10배입니다. 눈 한 번만 딱 감으시죠.,매각 허가,tag_corruption,0,0,0,1,30,0,0,0,0,0,원칙대로 소각,,0,0,0,0,-10,0,0,0,0,0,
7,작전,존재 양지화 프로젝트,프로젝트를 승인하시겠습니까?,대중 매체를 장악하여 I.S.C.O의 존재를 서서히 양지화하는 프로젝트입니다. 막대한 정보 토큰이 소모됩니다.,우리가 이 행성의 진짜 지배자임을 알릴 때가 되었습니다.,감시망 구축,tag_coverup,0,0,0,0,0,0,-40,0,0,2,프로젝트 보류,,1,0,0,0,0,0,0,0,0,0,
8,극비,대규모 도서관 건립,예산을 배정하시겠습니까?,고대 언어 해독을 위해 대규모 도서관 건립이 필요합니다. 지식 보존당이 학자들을 동원할 것입니다.,이해할 수 없는 것을 죽이는 건 야만인이나 하는 짓입니다.,예산 배정,,0,0,0,0,-40,0,40,0,0,0,예산 전액 삭감,,1,0,0,1,0,0,0,0,0,0,
9,위기,이단 색출 마녀사냥,어떻게 개입하시겠습니까?,기술 원리주의자들이 마녀사냥을 시작했습니다. 기구 내 마법 사용 요원들을 이단으로 규정하고 색출 중입니다.,더러운 마법과 변이에 의존해선 안 됩니다. 오직 순수한 인간의 힘으로!,적당히 묵인한다,,1,0,0,0,0,-30,0,0,0,0,군사력으로 제압,,0,0,0,1,0,0,-20,0,0,0,
10,일상,변칙 현상 영상 유출,어떻게 처리하시겠습니까?,한 유튜버가 변칙 현상을 촬영해 업로드했습니다. 침묵의 장막당이 즉각적인 물리적 제거와 기억 소거를 요구합니다.,대중은 진실을 감당할 지능이 없습니다. 무덤까지 비밀을 덮으십시오.,제거 절차 승인,tag_coverup,0,0,0,-1,0,0,-20,0,0,0,영상만 삭제,,0,1,0,0,0,0,0,0,0,0,
11,위기,사이비 종교 인신 공양,어떻게 대응하시겠습니까?,특정 구역의 사이비 종교가 고대 신을 소환하기 위해 시민 100명을 인신 공양하려 합니다. 공존당은 이를 방관하라 합니다.,위대한 그분들이 배가 고프다 하십니다. 조금만 양보하면 평화롭습니다.,군사 개입 지시,,1,0,1,0,0,-40,0,0,0,0,의식 방관,tag_cult,0,2,0,0,0,0,0,0,0,0,
12,위기,의회의 긴급 감사,어떻게 대처하시겠습니까?,의회에서 국장의 예산 전용 의혹을 제기하며 긴급 감사를 통보했습니다. 장부를 조작해야 합니다!,세상을 구하는 데 돈 좀 쓴 게 죄입니까? 빨리 덮을 정보팀을 부르십시오.,은폐 공작 실행,tag_corruption,0,0,0,1,0,0,-40,0,0,0,횡령 사실 인정,,0,0,0,2,0,0,0,0,0,0,
13,일상,연구 부서 파업 조짐,요구를 수용하시겠습니까?,연구 부서에서 초과 근무로 인한 집단 피로를 호소하며 휴가와 특별 보너스를 요구하고 있습니다.,저희도 사람입니다. 괴수 해부하다가 과로사하긴 싫어요.,특별 보너스 지급,,-1,0,0,0,-30,0,0,0,0,0,요구 전면 묵살,,1,0,0,0,0,0,0,0,0,0,
14,작전,이계 무역로 개방,무역로를 개방하시겠습니까?,이계와의 무역로를 임시 개방하여 자원을 조달하자는 안건이 상정되었습니다. 미지의 오염이 우려됩니다.,저 너머의 세계엔 우리가 상상도 못 할 자원이 널려있습니다. 황금알을 낳는 거위죠.,게이트 강제 개방,tag_alien_contact,0,1,0,0,50,0,0,0,0,0,폐쇄 상태 유지,,1,0,0,0,0,0,0,0,0,0,
15,극비,전임 국장 비밀 금고,금고를 개방하시겠습니까?,전임 국장이 남긴 비밀 금고를 발견했습니다. 여는 데 특수 장비와 전문 인력이 필요합니다.,전임 국장님이 숙청당하기 전 남긴 비자금 장부일지도 모릅니다.,강제 개방 지시,tag_corruption,0,0,0,0,20,-30,0,1,0,0,영구 봉인,,0,0,0,0,0,0,0,0,0,0,
16,작전,메가코프 협박 작전,작전을 실행하시겠습니까?,글로벌 메가코프를 협박해 I.S.C.O의 비자금을 세탁하고 대규모 자금을 확보할 기회입니다.,그들이 우리 덕분에 안전하게 사업하는 겁니다. 보호세 좀 걷는 게 뭐 어떻습니까.,작전 실행,,0,0,0,0,0,0,-30,2,0,0,작전 취소,,1,0,0,0,0,0,0,0,0,0,
17,위기,전술 폭격 건의,폭격을 승인하시겠습니까?,제7구역이 S급 변칙 개체에 의해 완전히 붕괴될 위기입니다. 민간인을 포기하고 융단 폭격을 해야 할까요?,시민 10만 명 구출 비용보다 유가족 합의금이 예산을 15% 더 아낄 수 있습니다.,시민 포기 및 폭격,tag_ruthless,0,-1,0,2,0,0,0,0,0,0,결사 방어 명령,,0,0,0,0,-40,-40,0,0,0,0,
18,일상,행정부 파업 예고,어떻게 대응하시겠습니까?,새로운 회식비 규정이 너무 짜다며 행정부에서 파업을 예고했습니다. 괴수 사체 처리 지연으로 악취가 진동합니다.,기구의 절반은 서류 작업으로 돌아갑니다. 그들을 화나게 해선 안 됩니다.,예산 전용으로 무마,,0,0,0,1,-20,0,0,0,0,0,파업 강경 대응,,1,0,0,0,0,0,0,0,0,0,
19,극비,불침번 요원 개발,배치하시겠습니까?,비밀 연구소에서 인공지능과 죽은 요원의 뇌를 결합한 불침번 요원을 개발했습니다.,잠도 안 자고 월급도 안 받습니다. 윤리적 문제만 빼면 완벽한 자산이죠.,실전 즉각 배치,,0,1,0,0,0,0,20,0,1,0,즉각 폐기 처분,,1,0,0,0,-10,0,0,0,0,0,
20,일상,내부 스파이 첩보,어떻게 조사하시겠습니까?,기구 내부에 스파이가 있다는 첩보가 들어왔습니다. 대대적인 사상 검증이 필요할지도 모릅니다.,누군가 정보를 빼돌려 의회에 넘기고 있습니다. 잡히면 믹서기에 넣어야 합니다.,대규모 사찰 시작,tag_political_pressure,2,0,0,0,0,0,30,0,0,0,증거 부족으로 덮음,,0,0,0,1,0,0,0,0,0,0,
21,작전,비정상적 위기 발생,국장님 결단이 필요합니다,순수 인간당이 변이의 기미가 보이는 요원들을 격리 수용할 대규모 시설 건립을 제안했습니다.,괴물과 싸우다 보면 괴물이 되는 법. 그들이 선을 넘기 전에 가둬야 합니다.,수용소 건립 승인,tag_ruthless,0,0,0,0,-40,0,0,0,1,0,제안 단호히 거절,,1,0,0,1,0,0,0,0,0,0,
22,극비,이계의 유물 인양,유물을 어떻게 처리하시겠습니까?,고대 공존당의 사제들이 심해의 유물을 인양했습니다. 만지는 자의 정신을 갉아먹지만 막대한 지식을 줍니다.,약간의 두통만 참으면 이계의 비밀을 모두 알 수 있습니다. 요원 하나쯤 미쳐도 괜찮습니다.,유물 집중 연구,tag_alien_contact,0,1,0,0,0,0,50,0,0,0,유물 파괴 명령,,1,0,0,1,0,0,0,0,0,0,
23,위기,이계의 주시자 출현,대중을 어떻게 통제하시겠습니까?,얇아진 장막 너머로 거대한 눈동자가 며칠째 지구를 주시하고 있습니다. 대중의 공포가 확산되고 있습니다.,하늘을 보지 마십시오. 아니 차라리 눈을 감고 살게 하십시오.,인터넷 전면 차단,,2,0,1,0,0,0,0,0,0,2,진정제 무상 배포,,0,0,0,0,-50,0,0,0,0,0,
24,일상,요원 집단 트라우마,어떻게 지원하시겠습니까?,요원들이 괴수 진압 후 심각한 트라우마에 시달리고 있습니다. 심리 치료 지원이 시급합니다.,밤마다 촉수가 목을 조르는 악몽을 꾼답니다. 차라리 그 부분 기억을 지워버릴까요?,정신과 치료 지원,,0,0,0,0,-20,10,0,0,0,0,트라우마 기억 소거,,0,1,0,1,0,0,0,0,0,0,
25,극비,내부 고발자 발생,어떻게 막으시겠습니까?,의회 의원 중 한 명이 기구의 비리를 폭로하려 합니다. 침묵의 장막당이 사고사 처리를 제안합니다.,국장님 계단에서 구르거나 심장마비가 오는 건 아주 흔한 자연의 섭리입니다.,사고사 위장 지시,tag_coverup,0,0,0,-1,0,0,-30,0,0,0,폭로전 정면 돌파,,0,0,0,2,0,0,0,0,0,0,
26,위기,차원 붕괴 폭탄 오작동,어떻게 대응하시겠습니까?,차원 붕괴 폭탄의 타이머가 오작동을 일으켰습니다! 당장 끄지 않으면 7구역이 통째로 증발합니다.,최저가 입찰로 부품을 사서 폭탄을 만든 결과입니다. 누군가는 책임져야 합니다.,특수 해체팀 투입,tag_sacrifice,0,0,0,0,0,-40,0,0,0,0,폭발 방관 및 대피,,0,2,0,0,0,0,0,0,0,0,
27,작전,PMC 흡수 합병,결탁하시겠습니까?,군부와 결탁하여 민간 군사 기업(PMC)을 I.S.C.O의 통제 하에 두려는 작전입니다.,돈으로 충성심을 사는 건 가장 안전한 방법입니다. 물론 돈이 많을 때 이야기죠.,결탁 및 흡수 합병,,0,0,0,0,-40,0,0,0,2,0,독자적 노선 유지,,1,0,0,0,0,0,0,0,0,0,
28,작전,대규모 거주 구역 실험장화,제안을 승인하시겠습니까?,변칙 개체 관찰을 위해 1구역을 돔으로 덮고 거대한 실험장으로 만들자는 제안이 올라왔습니다.,시민들이 좀 불편해하겠지만 과학적 진보를 위해 그 정도는 감수해야죠.,돔 건설 강행,,0,1,0,0,-50,0,0,0,0,0,윤리적 이유로 기각,,0,0,0,1,0,0,0,0,0,0,
29,극비,이계 군주의 외교,제안을 수락하시겠습니까?,이계의 군주가 외교 사절을 보내왔습니다. 요원 10명을 노예로 주면 최고급 이계 무기를 주겠다고 합니다.,요원 10명의 생명 가치보다 이계 무기가 가져올 파급력이 월등히 높습니다.,거래 즉시 성사,tag_alien_contact,0,0,0,0,0,-40,0,0,2,0,사절단 전원 사살,,0,1,0,1,0,0,0,0,0,0,
30,일상,믹스 커피 재고 고갈,어떻게 해결하시겠습니까?,국장님 믹스 커피가 다 떨어졌습니다. 이게 무슨 세상의 종말보다 더 큰 위기인 줄 아십니까? 행정부서의 불만이 폭주합니다.,종말을 결재하기 전엔 커피 한 잔의 여유가 필요한 법입니다. 안 그렇습니까?,최고급 원두 지원,,-1,0,0,0,-10,0,0,0,0,0,헛소리 금지령,,1,0,0,0,0,0,0,0,0,0,
31,위기,신화적 존재 강림,어떻게 대적하시겠습니까?,신화적 존재가 깨어나려 합니다. 하늘이 피처럼 붉게 물들었고 요원들은 미쳐가고 있습니다!,최종 결재의 시간입니다. 세상의 물리 법칙을 비틀 것입니까 아니면 피로 막을 것입니까?,현실 조작기 가동,tag_madness,0,2,0,0,0,0,-50,0,0,0,모든 예비병력 투입,tag_sacrifice,0,0,0,1,0,-60,0,0,0,0,
32,신화,실험체의 노래,어떻게 조치하시겠습니까?,격리된 실험체 하나가 벽을 긁으며 알아들을 수 없는 노래를 흥얼거립니다.,직원들 사이에서 그 노래가 귓가에 맴돈다는 소문이 돕니다.,진정제 투여 및 격리 강화,,0,0,0,0,-15,0,0,0,0,0,그냥 무시하고 넘어간다,,0,1,0,0,0,0,0,0,0,0,C
33,신화,움직이는 조각상,어떻게 처리하시겠습니까?,지하 수조에서 건져올린 조각상이 자정마다 미세하게 위치를 바꿉니다.,CCTV 확인 결과 아무도 없었는데 조각상은 분명 돌아서 있었습니다.,석관에 봉인 후 콘크리트 매립,,0,0,0,0,-30,-10,0,0,0,0,일단 창고에 방치,,0,1,1,0,0,0,0,0,0,0,B
34,신화,심해의 알,부화를 막으시겠습니까?,심해 탐사팀이 인양한 알 하나가 부화 징후를 보입니다. 안에서 무언가 박동합니다.,생물학팀은 저것은 알이 아니라 심장이라고 보고했습니다.,즉각 냉동 처리 및 소각,,0,0,0,0,-50,-20,0,0,0,0,부화 과정 관찰 지속,tag_alien_contact,0,2,1,0,0,0,0,0,0,0,A
35,신화,장막 너머의 이름,이름을 들으시겠습니까?,장막 너머의 존재가 마침내 자신의 진짜 이름을 발음하기 시작했습니다. 듣는 요원들의 코피가 멈추지 않습니다.,언어병리학자는 이름의 세 음절만 듣고도 실어증에 걸렸습니다.,전 요원 청각 차단 및 격리 봉쇄,tag_madness,0,1,0,0,-70,-40,0,0,0,0,이름을 끝까지 듣고 기록한다,tag_alien_contact,0,3,2,0,0,0,0,0,0,0,S
36,조직저항,초과근무 항의,어떻게 무마하시겠습니까?,현장 요원 셋이 초과근무수당 미지급에 항의하며 보고서 제출을 하루 미뤘습니다.,정시 퇴근이 인권이라는 팻말을 봤습니다.,밀린 수당 즉시 지급,,0,0,0,0,-15,0,0,0,0,0,규정 운운하며 묵살,,1,0,0,0,0,0,0,0,0,0,C
37,조직저항,항명 성명,어떻게 대응하시겠습니까?,중간관리자 다섯이 연명으로 국장의 승인 절차에 대한 항명 성명을 냈습니다.,서명자 명단에 낯익은 이름이 보입니다. 승진 대상자였는데요.,일대일 면담으로 무마,,0,0,0,0,0,-25,0,0,0,0,주동자 색출 및 좌천,,2,0,0,0,0,0,0,0,0,0,B
38,조직저항,경비 태업,어떻게 수습하시겠습니까?,격리동 경비 인력 절반이 동시에 병가를 제출했습니다. 명백한 태업입니다.,다들 같은 날 같은 감기에 걸렸다고 합니다. 참으로 공교롭죠.,대체 인력 긴급 투입,,0,0,0,0,-30,-15,0,0,0,0,복귀할 때까지 방치,tag_coup,2,0,1,0,0,0,0,0,0,0,A
39,조직저항,대규모 사표,어떻게 진압하시겠습니까?,전 부서장급 열두 명이 동시 사표를 제출하며 내부 기밀을 언론에 흘리겠다 협박합니다.,이것은 항명이 아니라 쿠데타입니다.,전원 요구 수용 및 대규모 숙청,tag_ruthless,1,0,0,0,-60,-50,0,0,0,0,협박을 무시하고 강경 대응,tag_coup,3,1,0,0,0,0,0,0,0,0,S
40,사회공황,괴이한 목격담,어떻게 진화하시겠습니까?,동네 커뮤니티에 하늘에서 이상한 빛을 봤다는 글이 올라와 조회수가 치솟고 있습니다.,댓글 반응은 반반입니다. 반은 조작설 반은 목격담 추가.,여론 조작팀 투입해 진화,,0,0,0,0,0,0,-15,0,0,0,며칠 지나면 묻힐 겁니다,,0,0,1,0,0,0,0,0,0,0,C
41,사회공황,특집 뉴스 보도,어떻게 대응하시겠습니까?,한 지역 방송국이 괴수 목격담을 특집 뉴스로 다뤘습니다. 시청률이 세 배 뛰었습니다.,미스터리 특종이라는 자막이 하루 종일 반복 재생됩니다.,방송국 인수 후 정정 보도,,0,0,0,0,-30,0,0,0,0,0,허위 정보로 규정하고 방치,,0,0,2,0,0,0,0,0,0,0,B
42,사회공황,생필품 사재기,어떻게 진정시키시겠습니까?,대형 마트마다 생필품 사재기 행렬이 이어지고 있습니다. 폭동 조짐이 보입니다.,화장지 한 팩을 두고 몸싸움이 벌어졌다는 신고가 열 건째입니다.,비상 물자 방출 및 진정,,0,0,0,0,-40,0,-15,0,0,0,질서 유지는 각자 알아서,tag_panic_mob,1,0,2,0,0,0,0,0,0,0,A
43,사회공황,종말론 행진,어떻게 통제하시겠습니까?,종말론에 심취한 신도 수천 명이 본부 앞으로 행진하며 심판을 요구합니다.,그들의 팻말에는 이상하게도 정확한 좌표가 적혀 있었습니다.,전면 계엄 수준의 통제 발동,tag_ruthless,0,1,1,0,-50,-30,0,0,0,0,군중 앞에서 진실을 공개한다,tag_panic_mob,0,1,3,0,0,0,0,0,0,0,S
44,이사회압박,접대비 감사,어떻게 해명하시겠습니까?,감사실에서 지난달 접대비 영수증 몇 장을 문제 삼기 시작했습니다.,법인카드로 산 초밥이 감사 보고서 제목이 됐습니다.,영수증 재발급 후 해명,,0,0,0,0,-15,0,0,0,0,0,바쁘다는 핑계로 무시,,0,0,0,1,0,0,0,0,0,0,C
45,이사회압박,예산 전수조사,어떻게 대비하시겠습니까?,이사회가 예산 집행 내역 전수 조사를 예고했습니다.,회계팀 막내가 벌써부터 사직서 초안을 쓰고 있습니다.,회계 자료 사전 정리,,0,0,0,0,0,-25,0,0,0,0,조사에 순순히 응한다,,0,0,0,2,0,0,0,0,0,0,B
46,이사회압박,결재권 정지 논의,어떻게 방어하시겠습니까?,긴급 임시 이사회가 소집되어 국장의 결재권 일부 정지를 논의합니다.,의제 제목이 국장의 독단 경영에 대하여입니다.,핵심 임원 개별 설득,tag_corruption,0,0,0,0,-40,0,-15,0,0,0,정면으로 맞선다,,1,0,0,2,0,0,0,0,0,0,A
47,이사회압박,청문회 소환,어떻게 대응하시겠습니까?,청문회 소환장이 도착했습니다. 결재권 자체를 박탈하겠다는 초강경 안건입니다.,출석하지 않으면 그 자체로 유죄로 간주하겠다는 문구가 적혀 있습니다.,총력을 다해 청문회 방어,,0,0,0,1,-60,-30,-20,0,0,0,청문회를 보이콧한다,tag_political_pressure,1,1,0,3,0,0,0,0,0,0,S
1 id,type,text,flavor,left_text,left_res,left_ent,left_pan,left_rsk,left_bud,left_per,left_inf,left_wea,left_for,left_sur,right_text,right_res,right_ent,right_pan,right_rsk,right_bud,right_per,right_inf,right_wea,right_for,right_sur,grade,left_narrative,right_narrative id type keyword question text flavor left_text left_tag left_res left_ent left_pan left_rsk left_bud left_per left_inf left_wea left_for left_sur right_text right_tag right_res right_ent right_pan right_rsk right_bud right_per right_inf right_wea right_for right_sur grade
2 1,일상,B구역 하급 요원들이 예산 삭감에 반발해 장비 반납 시위를 하고 있습니다.,우리가 세상을 구하는데 월급이 깎인다고요? 촉수에 맞아 죽으나 굶어 죽으나 똑같습니다!,무력 진압,1,0,0,0,0,-10,0,0,0,0,요구 수용,0,0,0,0,-20,0,0,0,0,0 1 일상 요원들의 시위 발생 시위를 어떻게 처리하시겠습니까? B구역 하급 요원들이 예산 삭감에 반발해 장비 반납 시위를 하고 있습니다. 우리가 세상을 구하는데 월급이 깎인다고요? 촉수에 맞아 죽으나 굶어 죽으나 똑같습니다! 무력 진압 tag_ruthless 1 0 0 0 0 -10 0 0 0 0 요구 수용 0 0 0 0 -20 0 0 0 0 0
3 2,위기,지하 4층 격리실의 문은 닫혀 있습니다만 요원 12명이 남긴 체액의 총량이 인간 12명의 부피를 초과했습니다.,보고서 첨부: 액체화된 직원은 산재 처리가 안 됩니다.,네이팜 소각 강행,0,0,0,0,0,-30,0,0,0,0,조사팀 파견,0,1,0,1,0,0,0,0,0,0,,증거 인멸 소각,진상 조사 착수 2 위기 격리실 오염 발생 오염을 어떻게 정화하시겠습니까? 지하 4층 격리실의 문은 닫혀 있습니다만 요원 12명이 남긴 체액의 총량이 인간 12명의 부피를 초과했습니다. 보고서 첨부: 액체화된 직원은 산재 처리가 안 됩니다. 네이팜 소각 강행 tag_coverup 0 0 0 0 0 -30 0 0 0 0 조사팀 파견 0 1 0 1 0 0 0 0 0 0
4 3,일상,7구역에 촉수 괴물이 출현했습니다. 작전팀이 법인카드 영수증 제출을 누락해서 출동 승인이 반려된 상태입니다.,규정은 규정입니다. 영수증 없이 세계를 구하는 건 용납할 수 없습니다.,내역 위조 후 출동,0,0,0,1,0,0,-10,0,0,0,규정대로 반려,1,1,0,0,0,0,0,0,0,0 3 일상 촉수 괴물 출현 출동 승인하시겠습니까? 7구역에 촉수 괴물이 출현했습니다. 작전팀이 법인카드 영수증 제출을 누락해서 출동 승인이 반려된 상태입니다. 규정은 규정입니다. 영수증 없이 세계를 구하는 건 용납할 수 없습니다. 내역 위조 후 출동 0 0 0 1 0 0 -10 0 0 0 규정대로 반려 1 1 0 0 0 0 0 0 0 0
5 4,극비,진화 미래당이 제안합니다. 요원들에게 괴수의 DNA를 소량 이식하면 작전 효율이 300% 상승합니다. 예산은 우리가 대죠.,인간의 한계는 명확합니다. 조금 징그러워지는 것쯤은 감수해야죠.,실험 승인,0,2,0,0,40,40,0,0,0,0,제안 거절,1,0,0,0,0,0,0,0,0,0,,인체 실험 승인,진화 계획 거절 4 극비 변이 DNA 이식 제안 실험을 승인하시겠습니까? 진화 미래당이 제안합니다. 요원들에게 괴수의 DNA를 소량 이식하면 작전 효율이 300% 상승합니다. 예산은 우리가 대죠. 인간의 한계는 명확합니다. 조금 징그러워지는 것쯤은 감수해야죠. 실험 승인 tag_madness 0 2 0 0 40 40 0 0 0 0 제안 거절 1 0 0 0 0 0 0 0 0 0
6 5,작전,철의 수호당이 민간인 구역에 전술 폭격을 가해 S급 변칙 개체를 제거하자고 건의했습니다.,희생 없는 승리는 없습니다. 소를 위해 대를... 아니 대를 위해 소를 희생하죠.,궤도 폭격 승인,0,0,0,2,0,0,0,0,2,0,작전 기각,1,0,0,0,0,0,0,0,0,0,,민간인 희생 묵인,온건 노선 고수 5 작전 전술 폭격 건의 폭격을 승인하시겠습니까? 철의 수호당이 민간인 구역에 전술 폭격을 가해 S급 변칙 개체를 제거하자고 건의했습니다. 희생 없는 승리는 없습니다. 소를 위해 대를... 아니 대를 위해 소를 희생하죠. 궤도 폭격 승인 tag_sacrifice 0 0 0 2 0 0 0 0 2 0 작전 기각 1 0 0 0 0 0 0 0 0 0
7 6,일상,차원 무역당 소속 브로커가 처리된 괴수의 부산물을 암시장에 팔아치우자고 속삭입니다.,이 이빨 하나면 국장님 연봉의 10배입니다. 눈 한 번만 딱 감으시죠.,매각 허가,0,0,0,1,30,0,0,0,0,0,원칙대로 소각,0,0,0,0,-10,0,0,0,0,0 6 일상 부산물 암시장 매각 매각을 허가하시겠습니까? 차원 무역당 소속 브로커가 처리된 괴수의 부산물을 암시장에 팔아치우자고 속삭입니다. 이 이빨 하나면 국장님 연봉의 10배입니다. 눈 한 번만 딱 감으시죠. 매각 허가 tag_corruption 0 0 0 1 30 0 0 0 0 0 원칙대로 소각 0 0 0 0 -10 0 0 0 0 0
8 7,작전,대중 매체를 장악하여 I.S.C.O의 존재를 서서히 양지화하는 프로젝트입니다. 막대한 정보 토큰이 소모됩니다.,우리가 이 행성의 진짜 지배자임을 알릴 때가 되었습니다.,감시망 구축,0,0,0,0,0,0,-40,0,0,2,프로젝트 보류,1,0,0,0,0,0,0,0,0,0,,조직 양지화 착수,은둔 노선 유지 7 작전 존재 양지화 프로젝트 프로젝트를 승인하시겠습니까? 대중 매체를 장악하여 I.S.C.O의 존재를 서서히 양지화하는 프로젝트입니다. 막대한 정보 토큰이 소모됩니다. 우리가 이 행성의 진짜 지배자임을 알릴 때가 되었습니다. 감시망 구축 tag_coverup 0 0 0 0 0 0 -40 0 0 2 프로젝트 보류 1 0 0 0 0 0 0 0 0 0
9 8,극비,고대 언어 해독을 위해 대규모 도서관 건립이 필요합니다. 지식 보존당이 학자들을 동원할 것입니다.,이해할 수 없는 것을 죽이는 건 야만인이나 하는 짓입니다.,예산 배정,0,0,0,0,-40,0,40,0,0,0,예산 전액 삭감,1,0,0,1,0,0,0,0,0,0 8 극비 대규모 도서관 건립 예산을 배정하시겠습니까? 고대 언어 해독을 위해 대규모 도서관 건립이 필요합니다. 지식 보존당이 학자들을 동원할 것입니다. 이해할 수 없는 것을 죽이는 건 야만인이나 하는 짓입니다. 예산 배정 0 0 0 0 -40 0 40 0 0 0 예산 전액 삭감 1 0 0 1 0 0 0 0 0 0
10 9,위기,기술 원리주의자들이 마녀사냥을 시작했습니다. 기구 내 마법 사용 요원들을 이단으로 규정하고 색출 중입니다.,더러운 마법과 변이에 의존해선 안 됩니다. 오직 순수한 인간의 힘으로!,적당히 묵인한다,1,0,0,0,0,-30,0,0,0,0,군사력으로 제압,0,0,0,1,0,0,-20,0,0,0 9 위기 이단 색출 마녀사냥 어떻게 개입하시겠습니까? 기술 원리주의자들이 마녀사냥을 시작했습니다. 기구 내 마법 사용 요원들을 이단으로 규정하고 색출 중입니다. 더러운 마법과 변이에 의존해선 안 됩니다. 오직 순수한 인간의 힘으로! 적당히 묵인한다 1 0 0 0 0 -30 0 0 0 0 군사력으로 제압 0 0 0 1 0 0 -20 0 0 0
11 10,일상,한 유튜버가 변칙 현상을 촬영해 업로드했습니다. 침묵의 장막당이 즉각적인 물리적 제거와 기억 소거를 요구합니다.,대중은 진실을 감당할 지능이 없습니다. 무덤까지 비밀을 덮으십시오.,제거 절차 승인,0,0,0,-1,0,0,-20,0,0,0,영상만 삭제,0,1,0,0,0,0,0,0,0,0 10 일상 변칙 현상 영상 유출 어떻게 처리하시겠습니까? 한 유튜버가 변칙 현상을 촬영해 업로드했습니다. 침묵의 장막당이 즉각적인 물리적 제거와 기억 소거를 요구합니다. 대중은 진실을 감당할 지능이 없습니다. 무덤까지 비밀을 덮으십시오. 제거 절차 승인 tag_coverup 0 0 0 -1 0 0 -20 0 0 0 영상만 삭제 0 1 0 0 0 0 0 0 0 0
12 11,위기,특정 구역의 사이비 종교가 고대 신을 소환하기 위해 시민 100명을 인신 공양하려 합니다. 공존당은 이를 방관하라 합니다.,위대한 그분들이 배가 고프다 하십니다. 조금만 양보하면 평화롭습니다.,군사 개입 지시,1,0,1,0,0,-40,0,0,0,0,의식 방관,0,2,0,0,0,0,0,0,0,0 11 위기 사이비 종교 인신 공양 어떻게 대응하시겠습니까? 특정 구역의 사이비 종교가 고대 신을 소환하기 위해 시민 100명을 인신 공양하려 합니다. 공존당은 이를 방관하라 합니다. 위대한 그분들이 배가 고프다 하십니다. 조금만 양보하면 평화롭습니다. 군사 개입 지시 1 0 1 0 0 -40 0 0 0 0 의식 방관 tag_cult 0 2 0 0 0 0 0 0 0 0
13 12,위기,의회에서 국장의 예산 전용 의혹을 제기하며 긴급 감사를 통보했습니다. 장부를 조작해야 합니다!,세상을 구하는 데 돈 좀 쓴 게 죄입니까? 빨리 덮을 정보팀을 부르십시오.,은폐 공작 실행,0,0,0,1,0,0,-40,0,0,0,횡령 사실 인정,0,0,0,2,0,0,0,0,0,0,,은폐 공작 실행,횡령 자백 12 위기 의회의 긴급 감사 어떻게 대처하시겠습니까? 의회에서 국장의 예산 전용 의혹을 제기하며 긴급 감사를 통보했습니다. 장부를 조작해야 합니다! 세상을 구하는 데 돈 좀 쓴 게 죄입니까? 빨리 덮을 정보팀을 부르십시오. 은폐 공작 실행 tag_corruption 0 0 0 1 0 0 -40 0 0 0 횡령 사실 인정 0 0 0 2 0 0 0 0 0 0
14 13,일상,연구 부서에서 초과 근무로 인한 집단 피로를 호소하며 휴가와 특별 보너스를 요구하고 있습니다.,저희도 사람입니다. 괴수 해부하다가 과로사하긴 싫어요.,특별 보너스 지급,-1,0,0,0,-30,0,0,0,0,0,요구 전면 묵살,1,0,0,0,0,0,0,0,0,0 13 일상 연구 부서 파업 조짐 요구를 수용하시겠습니까? 연구 부서에서 초과 근무로 인한 집단 피로를 호소하며 휴가와 특별 보너스를 요구하고 있습니다. 저희도 사람입니다. 괴수 해부하다가 과로사하긴 싫어요. 특별 보너스 지급 -1 0 0 0 -30 0 0 0 0 0 요구 전면 묵살 1 0 0 0 0 0 0 0 0 0
15 14,작전,이계와의 무역로를 임시 개방하여 자원을 조달하자는 안건이 상정되었습니다. 미지의 오염이 우려됩니다.,저 너머의 세계엔 우리가 상상도 못 할 자원이 널려있습니다. 황금알을 낳는 거위죠.,게이트 강제 개방,0,1,0,0,50,0,0,0,0,0,폐쇄 상태 유지,1,0,0,0,0,0,0,0,0,0,,이계 무역로 개방,고립주의 선택 14 작전 이계 무역로 개방 무역로를 개방하시겠습니까? 이계와의 무역로를 임시 개방하여 자원을 조달하자는 안건이 상정되었습니다. 미지의 오염이 우려됩니다. 저 너머의 세계엔 우리가 상상도 못 할 자원이 널려있습니다. 황금알을 낳는 거위죠. 게이트 강제 개방 tag_alien_contact 0 1 0 0 50 0 0 0 0 0 폐쇄 상태 유지 1 0 0 0 0 0 0 0 0 0
16 15,극비,전임 국장이 남긴 비밀 금고를 발견했습니다. 여는 데 특수 장비와 전문 인력이 필요합니다.,전임 국장님이 숙청당하기 전 남긴 비자금 장부일지도 모릅니다.,강제 개방 지시,0,0,0,0,20,-30,0,1,0,0,영구 봉인,0,0,0,0,0,0,0,0,0,0,,비자금 장부 발견,과거를 봉인 15 극비 전임 국장 비밀 금고 금고를 개방하시겠습니까? 전임 국장이 남긴 비밀 금고를 발견했습니다. 여는 데 특수 장비와 전문 인력이 필요합니다. 전임 국장님이 숙청당하기 전 남긴 비자금 장부일지도 모릅니다. 강제 개방 지시 tag_corruption 0 0 0 0 20 -30 0 1 0 0 영구 봉인 0 0 0 0 0 0 0 0 0 0
17 16,작전,글로벌 메가코프를 협박해 I.S.C.O의 비자금을 세탁하고 대규모 자금을 확보할 기회입니다.,그들이 우리 덕분에 안전하게 사업하는 겁니다. 보호세 좀 걷는 게 뭐 어떻습니까.,작전 실행,0,0,0,0,0,0,-30,2,0,0,작전 취소,1,0,0,0,0,0,0,0,0,0 16 작전 메가코프 협박 작전 작전을 실행하시겠습니까? 글로벌 메가코프를 협박해 I.S.C.O의 비자금을 세탁하고 대규모 자금을 확보할 기회입니다. 그들이 우리 덕분에 안전하게 사업하는 겁니다. 보호세 좀 걷는 게 뭐 어떻습니까. 작전 실행 0 0 0 0 0 0 -30 2 0 0 작전 취소 1 0 0 0 0 0 0 0 0 0
18 17,위기,제7구역이 S급 변칙 개체에 의해 완전히 붕괴될 위기입니다. 민간인을 포기하고 융단 폭격을 해야 할까요?,시민 10만 명 구출 비용보다 유가족 합의금이 예산을 15% 더 아낄 수 있습니다.,시민 포기 및 폭격,0,-1,0,2,0,0,0,0,0,0,결사 방어 명령,0,0,0,0,-40,-40,0,0,0,0,,민간인 희생 승인,결사 방어 명령 17 위기 전술 폭격 건의 폭격을 승인하시겠습니까? 제7구역이 S급 변칙 개체에 의해 완전히 붕괴될 위기입니다. 민간인을 포기하고 융단 폭격을 해야 할까요? 시민 10만 명 구출 비용보다 유가족 합의금이 예산을 15% 더 아낄 수 있습니다. 시민 포기 및 폭격 tag_ruthless 0 -1 0 2 0 0 0 0 0 0 결사 방어 명령 0 0 0 0 -40 -40 0 0 0 0
19 18,일상,새로운 회식비 규정이 너무 짜다며 행정부에서 파업을 예고했습니다. 괴수 사체 처리 지연으로 악취가 진동합니다.,기구의 절반은 서류 작업으로 돌아갑니다. 그들을 화나게 해선 안 됩니다.,예산 전용으로 무마,0,0,0,1,-20,0,0,0,0,0,파업 강경 대응,1,0,0,0,0,0,0,0,0,0 18 일상 행정부 파업 예고 어떻게 대응하시겠습니까? 새로운 회식비 규정이 너무 짜다며 행정부에서 파업을 예고했습니다. 괴수 사체 처리 지연으로 악취가 진동합니다. 기구의 절반은 서류 작업으로 돌아갑니다. 그들을 화나게 해선 안 됩니다. 예산 전용으로 무마 0 0 0 1 -20 0 0 0 0 0 파업 강경 대응 1 0 0 0 0 0 0 0 0 0
20 19,극비,비밀 연구소에서 인공지능과 죽은 요원의 뇌를 결합한 불침번 요원을 개발했습니다.,잠도 안 자고 월급도 안 받습니다. 윤리적 문제만 빼면 완벽한 자산이죠.,실전 즉각 배치,0,1,0,0,0,0,20,0,1,0,즉각 폐기 처분,1,0,0,0,-10,0,0,0,0,0 19 극비 불침번 요원 개발 배치하시겠습니까? 비밀 연구소에서 인공지능과 죽은 요원의 뇌를 결합한 불침번 요원을 개발했습니다. 잠도 안 자고 월급도 안 받습니다. 윤리적 문제만 빼면 완벽한 자산이죠. 실전 즉각 배치 0 1 0 0 0 0 20 0 1 0 즉각 폐기 처분 1 0 0 0 -10 0 0 0 0 0
21 20,일상,기구 내부에 스파이가 있다는 첩보가 들어왔습니다. 대대적인 사상 검증이 필요할지도 모릅니다.,누군가 정보를 빼돌려 의회에 넘기고 있습니다. 잡히면 믹서기에 넣어야 합니다.,대규모 사찰 시작,2,0,0,0,0,0,30,0,0,0,증거 부족으로 덮음,0,0,0,1,0,0,0,0,0,0,,공포 정치 시작,의혹 은폐 20 일상 내부 스파이 첩보 어떻게 조사하시겠습니까? 기구 내부에 스파이가 있다는 첩보가 들어왔습니다. 대대적인 사상 검증이 필요할지도 모릅니다. 누군가 정보를 빼돌려 의회에 넘기고 있습니다. 잡히면 믹서기에 넣어야 합니다. 대규모 사찰 시작 tag_political_pressure 2 0 0 0 0 0 30 0 0 0 증거 부족으로 덮음 0 0 0 1 0 0 0 0 0 0
22 21,작전,순수 인간당이 변이의 기미가 보이는 요원들을 격리 수용할 대규모 시설 건립을 제안했습니다.,괴물과 싸우다 보면 괴물이 되는 법. 그들이 선을 넘기 전에 가둬야 합니다.,수용소 건립 승인,0,0,0,0,-40,0,0,0,1,0,제안 단호히 거절,1,0,0,1,0,0,0,0,0,0,,변이체 강제 수용,차별 정책 거부 21 작전 비정상적 위기 발생 국장님 결단이 필요합니다 순수 인간당이 변이의 기미가 보이는 요원들을 격리 수용할 대규모 시설 건립을 제안했습니다. 괴물과 싸우다 보면 괴물이 되는 법. 그들이 선을 넘기 전에 가둬야 합니다. 수용소 건립 승인 tag_ruthless 0 0 0 0 -40 0 0 0 1 0 제안 단호히 거절 1 0 0 1 0 0 0 0 0 0
23 22,극비,고대 공존당의 사제들이 심해의 유물을 인양했습니다. 만지는 자의 정신을 갉아먹지만 막대한 지식을 줍니다.,약간의 두통만 참으면 이계의 비밀을 모두 알 수 있습니다. 요원 하나쯤 미쳐도 괜찮습니다.,유물 집중 연구,0,1,0,0,0,0,50,0,0,0,유물 파괴 명령,1,0,0,1,0,0,0,0,0,0 22 극비 이계의 유물 인양 유물을 어떻게 처리하시겠습니까? 고대 공존당의 사제들이 심해의 유물을 인양했습니다. 만지는 자의 정신을 갉아먹지만 막대한 지식을 줍니다. 약간의 두통만 참으면 이계의 비밀을 모두 알 수 있습니다. 요원 하나쯤 미쳐도 괜찮습니다. 유물 집중 연구 tag_alien_contact 0 1 0 0 0 0 50 0 0 0 유물 파괴 명령 1 0 0 1 0 0 0 0 0 0
24 23,위기,얇아진 장막 너머로 거대한 눈동자가 며칠째 지구를 주시하고 있습니다. 대중의 공포가 확산되고 있습니다.,하늘을 보지 마십시오. 아니 차라리 눈을 감고 살게 하십시오.,인터넷 전면 차단,2,0,1,0,0,0,0,0,0,2,진정제 무상 배포,0,0,0,0,-50,0,0,0,0,0 23 위기 이계의 주시자 출현 대중을 어떻게 통제하시겠습니까? 얇아진 장막 너머로 거대한 눈동자가 며칠째 지구를 주시하고 있습니다. 대중의 공포가 확산되고 있습니다. 하늘을 보지 마십시오. 아니 차라리 눈을 감고 살게 하십시오. 인터넷 전면 차단 2 0 1 0 0 0 0 0 0 2 진정제 무상 배포 0 0 0 0 -50 0 0 0 0 0
25 24,일상,요원들이 괴수 진압 후 심각한 트라우마에 시달리고 있습니다. 심리 치료 지원이 시급합니다.,밤마다 촉수가 목을 조르는 악몽을 꾼답니다. 차라리 그 부분 기억을 지워버릴까요?,정신과 치료 지원,0,0,0,0,-20,10,0,0,0,0,트라우마 기억 소거,0,1,0,1,0,0,0,0,0,0 24 일상 요원 집단 트라우마 어떻게 지원하시겠습니까? 요원들이 괴수 진압 후 심각한 트라우마에 시달리고 있습니다. 심리 치료 지원이 시급합니다. 밤마다 촉수가 목을 조르는 악몽을 꾼답니다. 차라리 그 부분 기억을 지워버릴까요? 정신과 치료 지원 0 0 0 0 -20 10 0 0 0 0 트라우마 기억 소거 0 1 0 1 0 0 0 0 0 0
26 25,극비,의회 의원 중 한 명이 기구의 비리를 폭로하려 합니다. 침묵의 장막당이 사고사 처리를 제안합니다.,국장님 계단에서 구르거나 심장마비가 오는 건 아주 흔한 자연의 섭리입니다.,사고사 위장 지시,0,0,0,-1,0,0,-30,0,0,0,폭로전 정면 돌파,0,0,0,2,0,0,0,0,0,0,,내부 고발자 암살,비리 자진 시인 25 극비 내부 고발자 발생 어떻게 막으시겠습니까? 의회 의원 중 한 명이 기구의 비리를 폭로하려 합니다. 침묵의 장막당이 사고사 처리를 제안합니다. 국장님 계단에서 구르거나 심장마비가 오는 건 아주 흔한 자연의 섭리입니다. 사고사 위장 지시 tag_coverup 0 0 0 -1 0 0 -30 0 0 0 폭로전 정면 돌파 0 0 0 2 0 0 0 0 0 0
27 26,위기,차원 붕괴 폭탄의 타이머가 오작동을 일으켰습니다! 당장 끄지 않으면 7구역이 통째로 증발합니다.,최저가 입찰로 부품을 사서 폭탄을 만든 결과입니다. 누군가는 책임져야 합니다.,특수 해체팀 투입,0,0,0,0,0,-40,0,0,0,0,폭발 방관 및 대피,0,2,0,0,0,0,0,0,0,0,,목숨 건 해체 작전,구역 포기 결정 26 위기 차원 붕괴 폭탄 오작동 어떻게 대응하시겠습니까? 차원 붕괴 폭탄의 타이머가 오작동을 일으켰습니다! 당장 끄지 않으면 7구역이 통째로 증발합니다. 최저가 입찰로 부품을 사서 폭탄을 만든 결과입니다. 누군가는 책임져야 합니다. 특수 해체팀 투입 tag_sacrifice 0 0 0 0 0 -40 0 0 0 0 폭발 방관 및 대피 0 2 0 0 0 0 0 0 0 0
28 27,작전,군부와 결탁하여 민간 군사 기업(PMC)을 I.S.C.O의 통제 하에 두려는 작전입니다.,돈으로 충성심을 사는 건 가장 안전한 방법입니다. 물론 돈이 많을 때 이야기죠.,결탁 및 흡수 합병,0,0,0,0,-40,0,0,0,2,0,독자적 노선 유지,1,0,0,0,0,0,0,0,0,0 27 작전 PMC 흡수 합병 결탁하시겠습니까? 군부와 결탁하여 민간 군사 기업(PMC)을 I.S.C.O의 통제 하에 두려는 작전입니다. 돈으로 충성심을 사는 건 가장 안전한 방법입니다. 물론 돈이 많을 때 이야기죠. 결탁 및 흡수 합병 0 0 0 0 -40 0 0 0 2 0 독자적 노선 유지 1 0 0 0 0 0 0 0 0 0
29 28,작전,변칙 개체 관찰을 위해 1구역을 돔으로 덮고 거대한 실험장으로 만들자는 제안이 올라왔습니다.,시민들이 좀 불편해하겠지만 과학적 진보를 위해 그 정도는 감수해야죠.,돔 건설 강행,0,1,0,0,-50,0,0,0,0,0,윤리적 이유로 기각,0,0,0,1,0,0,0,0,0,0 28 작전 대규모 거주 구역 실험장화 제안을 승인하시겠습니까? 변칙 개체 관찰을 위해 1구역을 돔으로 덮고 거대한 실험장으로 만들자는 제안이 올라왔습니다. 시민들이 좀 불편해하겠지만 과학적 진보를 위해 그 정도는 감수해야죠. 돔 건설 강행 0 1 0 0 -50 0 0 0 0 0 윤리적 이유로 기각 0 0 0 1 0 0 0 0 0 0
30 29,극비,이계의 군주가 외교 사절을 보내왔습니다. 요원 10명을 노예로 주면 최고급 이계 무기를 주겠다고 합니다.,요원 10명의 생명 가치보다 이계 무기가 가져올 파급력이 월등히 높습니다.,거래 즉시 성사,0,0,0,0,0,-40,0,0,2,0,사절단 전원 사살,0,1,0,1,0,0,0,0,0,0,,요원을 노예로 상납,외교 사절 몰살 29 극비 이계 군주의 외교 제안을 수락하시겠습니까? 이계의 군주가 외교 사절을 보내왔습니다. 요원 10명을 노예로 주면 최고급 이계 무기를 주겠다고 합니다. 요원 10명의 생명 가치보다 이계 무기가 가져올 파급력이 월등히 높습니다. 거래 즉시 성사 tag_alien_contact 0 0 0 0 0 -40 0 0 2 0 사절단 전원 사살 0 1 0 1 0 0 0 0 0 0
31 30,일상,국장님 믹스 커피가 다 떨어졌습니다. 이게 무슨 세상의 종말보다 더 큰 위기인 줄 아십니까? 행정부서의 불만이 폭주합니다.,종말을 결재하기 전엔 커피 한 잔의 여유가 필요한 법입니다. 안 그렇습니까?,최고급 원두 지원,-1,0,0,0,-10,0,0,0,0,0,헛소리 금지령,1,0,0,0,0,0,0,0,0,0 30 일상 믹스 커피 재고 고갈 어떻게 해결하시겠습니까? 국장님 믹스 커피가 다 떨어졌습니다. 이게 무슨 세상의 종말보다 더 큰 위기인 줄 아십니까? 행정부서의 불만이 폭주합니다. 종말을 결재하기 전엔 커피 한 잔의 여유가 필요한 법입니다. 안 그렇습니까? 최고급 원두 지원 -1 0 0 0 -10 0 0 0 0 0 헛소리 금지령 1 0 0 0 0 0 0 0 0 0
32 31,위기,신화적 존재가 깨어나려 합니다. 하늘이 피처럼 붉게 물들었고 요원들은 미쳐가고 있습니다!,최종 결재의 시간입니다. 세상의 물리 법칙을 비틀 것입니까 아니면 피로 막을 것입니까?,현실 조작기 가동,0,2,0,0,0,0,-50,0,0,0,모든 예비병력 투입,0,0,0,1,0,-60,0,0,0,0,,현실 왜곡 감행,총력전 돌입 31 위기 신화적 존재 강림 어떻게 대적하시겠습니까? 신화적 존재가 깨어나려 합니다. 하늘이 피처럼 붉게 물들었고 요원들은 미쳐가고 있습니다! 최종 결재의 시간입니다. 세상의 물리 법칙을 비틀 것입니까 아니면 피로 막을 것입니까? 현실 조작기 가동 tag_madness 0 2 0 0 0 0 -50 0 0 0 모든 예비병력 투입 tag_sacrifice 0 0 0 1 0 -60 0 0 0 0
33 32,신화,격리된 실험체 하나가 벽을 긁으며 알아들을 수 없는 노래를 흥얼거립니다.,직원들 사이에서 그 노래가 귓가에 맴돈다는 소문이 돕니다.,진정제 투여 및 격리 강화,0,0,0,0,-15,0,0,0,0,0,그냥 무시하고 넘어간다,0,1,0,0,0,0,0,0,0,0,C 32 신화 실험체의 노래 어떻게 조치하시겠습니까? 격리된 실험체 하나가 벽을 긁으며 알아들을 수 없는 노래를 흥얼거립니다. 직원들 사이에서 그 노래가 귓가에 맴돈다는 소문이 돕니다. 진정제 투여 및 격리 강화 0 0 0 0 -15 0 0 0 0 0 그냥 무시하고 넘어간다 0 1 0 0 0 0 0 0 0 0 C
34 33,신화,지하 수조에서 건져올린 조각상이 자정마다 미세하게 위치를 바꿉니다.,CCTV 확인 결과 아무도 없었는데 조각상은 분명 돌아서 있었습니다.,석관에 봉인 후 콘크리트 매립,0,0,0,0,-30,-10,0,0,0,0,일단 창고에 방치,0,1,1,0,0,0,0,0,0,0,B 33 신화 움직이는 조각상 어떻게 처리하시겠습니까? 지하 수조에서 건져올린 조각상이 자정마다 미세하게 위치를 바꿉니다. CCTV 확인 결과 아무도 없었는데 조각상은 분명 돌아서 있었습니다. 석관에 봉인 후 콘크리트 매립 0 0 0 0 -30 -10 0 0 0 0 일단 창고에 방치 0 1 1 0 0 0 0 0 0 0 B
35 34,신화,심해 탐사팀이 인양한 알 하나가 부화 징후를 보입니다. 안에서 무언가 박동합니다.,생물학팀은 저것은 알이 아니라 심장이라고 보고했습니다.,즉각 냉동 처리 및 소각,0,0,0,0,-50,-20,0,0,0,0,부화 과정 관찰 지속,0,2,1,0,0,0,0,0,0,0,A,미지의 알 폐기,금단의 부화 관찰 34 신화 심해의 알 부화를 막으시겠습니까? 심해 탐사팀이 인양한 알 하나가 부화 징후를 보입니다. 안에서 무언가 박동합니다. 생물학팀은 저것은 알이 아니라 심장이라고 보고했습니다. 즉각 냉동 처리 및 소각 0 0 0 0 -50 -20 0 0 0 0 부화 과정 관찰 지속 tag_alien_contact 0 2 1 0 0 0 0 0 0 0 A
36 35,신화,장막 너머의 존재가 마침내 자신의 진짜 이름을 발음하기 시작했습니다. 듣는 요원들의 코피가 멈추지 않습니다.,언어병리학자는 이름의 세 음절만 듣고도 실어증에 걸렸습니다.,전 요원 청각 차단 및 격리 봉쇄,0,1,0,0,-70,-40,0,0,0,0,이름을 끝까지 듣고 기록한다,0,3,2,0,0,0,0,0,0,0,S,금단의 이름 봉인,금단의 이름 기록 35 신화 장막 너머의 이름 이름을 들으시겠습니까? 장막 너머의 존재가 마침내 자신의 진짜 이름을 발음하기 시작했습니다. 듣는 요원들의 코피가 멈추지 않습니다. 언어병리학자는 이름의 세 음절만 듣고도 실어증에 걸렸습니다. 전 요원 청각 차단 및 격리 봉쇄 tag_madness 0 1 0 0 -70 -40 0 0 0 0 이름을 끝까지 듣고 기록한다 tag_alien_contact 0 3 2 0 0 0 0 0 0 0 S
37 36,조직저항,현장 요원 셋이 초과근무수당 미지급에 항의하며 보고서 제출을 하루 미뤘습니다.,정시 퇴근이 인권이라는 팻말을 봤습니다.,밀린 수당 즉시 지급,0,0,0,0,-15,0,0,0,0,0,규정 운운하며 묵살,1,0,0,0,0,0,0,0,0,0,C 36 조직저항 초과근무 항의 어떻게 무마하시겠습니까? 현장 요원 셋이 초과근무수당 미지급에 항의하며 보고서 제출을 하루 미뤘습니다. 정시 퇴근이 인권이라는 팻말을 봤습니다. 밀린 수당 즉시 지급 0 0 0 0 -15 0 0 0 0 0 규정 운운하며 묵살 1 0 0 0 0 0 0 0 0 0 C
38 37,조직저항,중간관리자 다섯이 연명으로 국장의 승인 절차에 대한 항명 성명을 냈습니다.,서명자 명단에 낯익은 이름이 보입니다. 승진 대상자였는데요.,일대일 면담으로 무마,0,0,0,0,0,-25,0,0,0,0,주동자 색출 및 좌천,2,0,0,0,0,0,0,0,0,0,B 37 조직저항 항명 성명 어떻게 대응하시겠습니까? 중간관리자 다섯이 연명으로 국장의 승인 절차에 대한 항명 성명을 냈습니다. 서명자 명단에 낯익은 이름이 보입니다. 승진 대상자였는데요. 일대일 면담으로 무마 0 0 0 0 0 -25 0 0 0 0 주동자 색출 및 좌천 2 0 0 0 0 0 0 0 0 0 B
39 38,조직저항,격리동 경비 인력 절반이 동시에 병가를 제출했습니다. 명백한 태업입니다.,다들 같은 날 같은 감기에 걸렸다고 합니다. 참으로 공교롭죠.,대체 인력 긴급 투입,0,0,0,0,-30,-15,0,0,0,0,복귀할 때까지 방치,2,0,1,0,0,0,0,0,0,0,A,태업 진압,조직 와해 방관 38 조직저항 경비 태업 어떻게 수습하시겠습니까? 격리동 경비 인력 절반이 동시에 병가를 제출했습니다. 명백한 태업입니다. 다들 같은 날 같은 감기에 걸렸다고 합니다. 참으로 공교롭죠. 대체 인력 긴급 투입 0 0 0 0 -30 -15 0 0 0 0 복귀할 때까지 방치 tag_coup 2 0 1 0 0 0 0 0 0 0 A
40 39,조직저항,전 부서장급 열두 명이 동시 사표를 제출하며 내부 기밀을 언론에 흘리겠다 협박합니다.,이것은 항명이 아니라 쿠데타입니다.,전원 요구 수용 및 대규모 숙청,1,0,0,0,-60,-50,0,0,0,0,협박을 무시하고 강경 대응,3,1,0,0,0,0,0,0,0,0,S,대숙청 단행,쿠데타 조짐 39 조직저항 대규모 사표 어떻게 진압하시겠습니까? 전 부서장급 열두 명이 동시 사표를 제출하며 내부 기밀을 언론에 흘리겠다 협박합니다. 이것은 항명이 아니라 쿠데타입니다. 전원 요구 수용 및 대규모 숙청 tag_ruthless 1 0 0 0 -60 -50 0 0 0 0 협박을 무시하고 강경 대응 tag_coup 3 1 0 0 0 0 0 0 0 0 S
41 40,사회공황,동네 커뮤니티에 하늘에서 이상한 빛을 봤다는 글이 올라와 조회수가 치솟고 있습니다.,댓글 반응은 반반입니다. 반은 조작설 반은 목격담 추가.,여론 조작팀 투입해 진화,0,0,0,0,0,0,-15,0,0,0,며칠 지나면 묻힐 겁니다,0,0,1,0,0,0,0,0,0,0,C 40 사회공황 괴이한 목격담 어떻게 진화하시겠습니까? 동네 커뮤니티에 하늘에서 이상한 빛을 봤다는 글이 올라와 조회수가 치솟고 있습니다. 댓글 반응은 반반입니다. 반은 조작설 반은 목격담 추가. 여론 조작팀 투입해 진화 0 0 0 0 0 0 -15 0 0 0 며칠 지나면 묻힐 겁니다 0 0 1 0 0 0 0 0 0 0 C
42 41,사회공황,한 지역 방송국이 괴수 목격담을 특집 뉴스로 다뤘습니다. 시청률이 세 배 뛰었습니다.,미스터리 특종이라는 자막이 하루 종일 반복 재생됩니다.,방송국 인수 후 정정 보도,0,0,0,0,-30,0,0,0,0,0,허위 정보로 규정하고 방치,0,0,2,0,0,0,0,0,0,0,B 41 사회공황 특집 뉴스 보도 어떻게 대응하시겠습니까? 한 지역 방송국이 괴수 목격담을 특집 뉴스로 다뤘습니다. 시청률이 세 배 뛰었습니다. 미스터리 특종이라는 자막이 하루 종일 반복 재생됩니다. 방송국 인수 후 정정 보도 0 0 0 0 -30 0 0 0 0 0 허위 정보로 규정하고 방치 0 0 2 0 0 0 0 0 0 0 B
43 42,사회공황,대형 마트마다 생필품 사재기 행렬이 이어지고 있습니다. 폭동 조짐이 보입니다.,화장지 한 팩을 두고 몸싸움이 벌어졌다는 신고가 열 건째입니다.,비상 물자 방출 및 진정,0,0,0,0,-40,0,-15,0,0,0,질서 유지는 각자 알아서,1,0,2,0,0,0,0,0,0,0,A,비상 물자 방출,치안 붕괴 방관 42 사회공황 생필품 사재기 어떻게 진정시키시겠습니까? 대형 마트마다 생필품 사재기 행렬이 이어지고 있습니다. 폭동 조짐이 보입니다. 화장지 한 팩을 두고 몸싸움이 벌어졌다는 신고가 열 건째입니다. 비상 물자 방출 및 진정 0 0 0 0 -40 0 -15 0 0 0 질서 유지는 각자 알아서 tag_panic_mob 1 0 2 0 0 0 0 0 0 0 A
44 43,사회공황,종말론에 심취한 신도 수천 명이 본부 앞으로 행진하며 심판을 요구합니다.,그들의 팻말에는 이상하게도 정확한 좌표가 적혀 있었습니다.,전면 계엄 수준의 통제 발동,0,1,1,0,-50,-30,0,0,0,0,군중 앞에서 진실을 공개한다,0,1,3,0,0,0,0,0,0,0,S,계엄 선포,진실 공개 선언 43 사회공황 종말론 행진 어떻게 통제하시겠습니까? 종말론에 심취한 신도 수천 명이 본부 앞으로 행진하며 심판을 요구합니다. 그들의 팻말에는 이상하게도 정확한 좌표가 적혀 있었습니다. 전면 계엄 수준의 통제 발동 tag_ruthless 0 1 1 0 -50 -30 0 0 0 0 군중 앞에서 진실을 공개한다 tag_panic_mob 0 1 3 0 0 0 0 0 0 0 S
45 44,이사회압박,감사실에서 지난달 접대비 영수증 몇 장을 문제 삼기 시작했습니다.,법인카드로 산 초밥이 감사 보고서 제목이 됐습니다.,영수증 재발급 후 해명,0,0,0,0,-15,0,0,0,0,0,바쁘다는 핑계로 무시,0,0,0,1,0,0,0,0,0,0,C 44 이사회압박 접대비 감사 어떻게 해명하시겠습니까? 감사실에서 지난달 접대비 영수증 몇 장을 문제 삼기 시작했습니다. 법인카드로 산 초밥이 감사 보고서 제목이 됐습니다. 영수증 재발급 후 해명 0 0 0 0 -15 0 0 0 0 0 바쁘다는 핑계로 무시 0 0 0 1 0 0 0 0 0 0 C
46 45,이사회압박,이사회가 예산 집행 내역 전수 조사를 예고했습니다.,회계팀 막내가 벌써부터 사직서 초안을 쓰고 있습니다.,회계 자료 사전 정리,0,0,0,0,0,-25,0,0,0,0,조사에 순순히 응한다,0,0,0,2,0,0,0,0,0,0,B 45 이사회압박 예산 전수조사 어떻게 대비하시겠습니까? 이사회가 예산 집행 내역 전수 조사를 예고했습니다. 회계팀 막내가 벌써부터 사직서 초안을 쓰고 있습니다. 회계 자료 사전 정리 0 0 0 0 0 -25 0 0 0 0 조사에 순순히 응한다 0 0 0 2 0 0 0 0 0 0 B
47 46,이사회압박,긴급 임시 이사회가 소집되어 국장의 결재권 일부 정지를 논의합니다.,의제 제목이 국장의 독단 경영에 대하여입니다.,핵심 임원 개별 설득,0,0,0,0,-40,0,-15,0,0,0,정면으로 맞선다,1,0,0,2,0,0,0,0,0,0,A,막후 로비,이사회와 정면 충돌 46 이사회압박 결재권 정지 논의 어떻게 방어하시겠습니까? 긴급 임시 이사회가 소집되어 국장의 결재권 일부 정지를 논의합니다. 의제 제목이 국장의 독단 경영에 대하여입니다. 핵심 임원 개별 설득 tag_corruption 0 0 0 0 -40 0 -15 0 0 0 정면으로 맞선다 1 0 0 2 0 0 0 0 0 0 A
48 47,이사회압박,청문회 소환장이 도착했습니다. 결재권 자체를 박탈하겠다는 초강경 안건입니다.,출석하지 않으면 그 자체로 유죄로 간주하겠다는 문구가 적혀 있습니다.,총력을 다해 청문회 방어,0,0,0,1,-60,-30,-20,0,0,0,청문회를 보이콧한다,1,1,0,3,0,0,0,0,0,0,S,청문회 사수,청문회 보이콧 47 이사회압박 청문회 소환 어떻게 대응하시겠습니까? 청문회 소환장이 도착했습니다. 결재권 자체를 박탈하겠다는 초강경 안건입니다. 출석하지 않으면 그 자체로 유죄로 간주하겠다는 문구가 적혀 있습니다. 총력을 다해 청문회 방어 0 0 0 1 -60 -30 -20 0 0 0 청문회를 보이콧한다 tag_political_pressure 1 1 0 3 0 0 0 0 0 0 S
,,,,,,,,,,,,,,,,,,,,,,,,,

View File

@@ -69,6 +69,10 @@ export const ENDING_CATALOG = [
desc: '망설임 없는 결정 한 번이 모든 단계를 건너뛰었습니다. 경고도, 예고도 없이 — 그저 끝났습니다.',
reason: '단일 결정으로 파라미터가 급격히 5단계에 도달' },
{ id: 'term_expired', title: '임기 만료 (조용한 퇴장)',
desc: '붕괴도, 탄핵도 없었습니다. 그저 라운드가 거듭될수록 결재할 시간이 줄어들었고, 결국 정례회의 상정 건수가 바닥났습니다. 세상은 여전히 위태롭지만, 당신의 임기만은 조용히 끝났습니다.',
reason: '라운드가 거듭되며 정례회의까지의 결재 건수가 0이 됨' },
{ id: 'unidentified', title: '미확인 종말',
desc: '기록되지 않은 파멸입니다.',
reason: '알 수 없는 원인' },

View File

@@ -0,0 +1,62 @@
[
{
"id": "tag_ruthless",
"name": "피도 눈물도 없는 진압",
"icon": "🩸",
"description": "반발을 무력으로 짓밟았습니다. 공포는 훌륭한 통제 수단입니다."
},
{
"id": "tag_coverup",
"name": "진실의 장막",
"icon": "🤫",
"description": "대중이 알지 못하도록 변칙 사건을 완벽하게 은폐했습니다."
},
{
"id": "tag_alien_contact",
"name": "금지된 지식",
"icon": "👁️",
"description": "이계의 존재와 거래하거나 그들의 유물을 받아들였습니다. 장막 너머의 눈이 당신을 주시합니다."
},
{
"id": "tag_corruption",
"name": "검은 뒷돈",
"icon": "💼",
"description": "공적인 자산을 사적으로 유용하거나 부당한 거래를 승인했습니다."
},
{
"id": "tag_madness",
"name": "이성의 붕괴",
"icon": "🌀",
"description": "요원들을 비인도적인 실험과 광기에 노출시켰습니다. 인간성은 점점 희미해집니다."
},
{
"id": "tag_cult",
"name": "사이비의 그림자",
"icon": "🌑",
"description": "고대 신을 숭배하는 종교와 엮이기 시작했습니다. 그들의 교리가 스며듭니다."
},
{
"id": "tag_coup",
"name": "쿠데타의 그림자",
"icon": "🗡️",
"description": "권력자들이 등을 돌리기 시작했습니다. 칼끝이 서서히 당신을 향하고 있습니다."
},
{
"id": "tag_panic_mob",
"name": "군중의 광기",
"icon": "😱",
"description": "이성을 잃은 대중이 거리를 뒤덮었습니다. 더 이상 통제할 수 없습니다."
},
{
"id": "tag_political_pressure",
"name": "정치적 압박",
"icon": "⚖️",
"description": "이사회가 당신의 자리를 노리고 있습니다. 숫자 하나하나가 무기가 됩니다."
},
{
"id": "tag_sacrifice",
"name": "희생의 대가",
"icon": "⚰️",
"description": "누군가의 목숨과 맞바꾼 결정이었습니다. 그 무게는 사라지지 않습니다."
}
]

View File

@@ -0,0 +1,34 @@
[
{
"id": "poll_1",
"type": "오염",
"keyword": "장막 틈새의 오염",
"question": "알 수 없는 속삭임이 들립니다...",
"text": "사무실 벽에 기괴한 기하학적 무늬가 자라나고 있습니다. 쳐다보기만 해도 구역질이 납니다.",
"flavor": "\"저건 분명히 내 업무 일지보다 복잡한 구조야.\"",
"left": {
"text": "방치한다",
"stats": { "ent": 1, "pan": 1, "rsk": 1 }
},
"right": {
"text": "벽을 태운다",
"stats": { "bud": -20, "res": 1 }
}
},
{
"id": "poll_2",
"type": "오염",
"keyword": "부패하는 자원",
"question": "금고에서 악취가 납니다...",
"text": "예산으로 배정된 금괴들이 살덩어리로 변했습니다. 이대로는 결재에 사용할 수 없습니다.",
"flavor": "\"음, 적어도 인력 갈아넣는 것보단 싸게 먹히겠군.\"",
"left": {
"text": "폐기 처분",
"stats": { "bud": -50 }
},
"right": {
"text": "억지로 사용",
"stats": { "ent": 2, "rsk": 2 }
}
}
]

View File

@@ -6,22 +6,49 @@ const parsed = Papa.parse(csv, { header: true });
parsed.data.forEach(row => {
if (!row.id) return;
row.left_pan = 0;
row.right_pan = 0;
// Let's add panic to some rows for testing
if (row.id == 23 || row.id == 11) {
row.left_pan = 1;
row.right_pan = 0;
}
// Auto-generate some basic keyword and question based on text
if (row.text.includes('시위')) { row.keyword = '요원들의 시위 발생'; row.question = '시위를 어떻게 처리하시겠습니까?'; }
else if (row.text.includes('격리실')) { row.keyword = '격리실 오염 발생'; row.question = '오염을 어떻게 정화하시겠습니까?'; }
else if (row.text.includes('촉수')) { row.keyword = '촉수 괴물 출현'; row.question = '출동 승인하시겠습니까?'; }
else if (row.text.includes('DNA')) { row.keyword = '변이 DNA 이식 제안'; row.question = '실험을 승인하시겠습니까?'; }
else if (row.text.includes('폭격')) { row.keyword = '전술 폭격 건의'; row.question = '폭격을 승인하시겠습니까?'; }
else if (row.text.includes('암시장')) { row.keyword = '부산물 암시장 매각'; row.question = '매각을 허가하시겠습니까?'; }
else if (row.text.includes('대중 매체')) { row.keyword = '존재 양지화 프로젝트'; row.question = '프로젝트를 승인하시겠습니까?'; }
else if (row.text.includes('도서관')) { row.keyword = '대규모 도서관 건립'; row.question = '예산을 배정하시겠습니까?'; }
else if (row.text.includes('마녀사냥')) { row.keyword = '이단 색출 마녀사냥'; row.question = '어떻게 개입하시겠습니까?'; }
else if (row.text.includes('유튜버')) { row.keyword = '변칙 현상 영상 유출'; row.question = '어떻게 처리하시겠습니까?'; }
else if (row.text.includes('인신 공양')) { row.keyword = '사이비 종교 인신 공양'; row.question = '어떻게 대응하시겠습니까?'; }
else if (row.text.includes('감사')) { row.keyword = '의회의 긴급 감사'; row.question = '어떻게 대처하시겠습니까?'; }
else if (row.text.includes('초과 근무')) { row.keyword = '연구 부서 파업 조짐'; row.question = '요구를 수용하시겠습니까?'; }
else if (row.text.includes('무역로')) { row.keyword = '이계 무역로 개방'; row.question = '무역로를 개방하시겠습니까?'; }
else if (row.text.includes('비밀 금고')) { row.keyword = '전임 국장 비밀 금고'; row.question = '금고를 개방하시겠습니까?'; }
else if (row.text.includes('메가코프')) { row.keyword = '메가코프 협박 작전'; row.question = '작전을 실행하시겠습니까?'; }
else if (row.text.includes('시민')) { row.keyword = '민간인 구역 붕괴 위기'; row.question = '어떻게 명령하시겠습니까?'; }
else if (row.text.includes('파업')) { row.keyword = '행정부 파업 예고'; row.question = '어떻게 대응하시겠습니까?'; }
else if (row.text.includes('뇌를 결합')) { row.keyword = '불침번 요원 개발'; row.question = '배치하시겠습니까?'; }
else if (row.text.includes('스파이')) { row.keyword = '내부 스파이 첩보'; row.question = '어떻게 조사하시겠습니까?'; }
else if (row.text.includes('수용소')) { row.keyword = '변이 요원 수용소'; row.question = '건립을 승인하시겠습니까?'; }
else if (row.text.includes('유물')) { row.keyword = '이계의 유물 인양'; row.question = '유물을 어떻게 처리하시겠습니까?'; }
else if (row.text.includes('눈동자')) { row.keyword = '이계의 주시자 출현'; row.question = '대중을 어떻게 통제하시겠습니까?'; }
else if (row.text.includes('트라우마')) { row.keyword = '요원 집단 트라우마'; row.question = '어떻게 지원하시겠습니까?'; }
else if (row.text.includes('폭로')) { row.keyword = '내부 고발자 발생'; row.question = '어떻게 막으시겠습니까?'; }
else if (row.text.includes('타이머')) { row.keyword = '차원 붕괴 폭탄 오작동'; row.question = '어떻게 대응하시겠습니까?'; }
else if (row.text.includes('군사 기업')) { row.keyword = 'PMC 흡수 합병'; row.question = '결탁하시겠습니까?'; }
else if (row.text.includes('관찰')) { row.keyword = '대규모 거주 구역 실험장화'; row.question = '제안을 승인하시겠습니까?'; }
else if (row.text.includes('이계의 군주')) { row.keyword = '이계 군주의 외교'; row.question = '제안을 수락하시겠습니까?'; }
else if (row.text.includes('믹스 커피')) { row.keyword = '믹스 커피 재고 고갈'; row.question = '어떻게 해결하시겠습니까?'; }
else if (row.text.includes('신화적 존재')) { row.keyword = '신화적 존재 강림'; row.question = '어떻게 대적하시겠습니까?'; }
else { row.keyword = '비정상적 위기 발생'; row.question = '국장님, 결단이 필요합니다.'; }
});
const newCsv = Papa.unparse(parsed.data, {
columns: [
'id','type','text','flavor',
'id','type','keyword','question','text','flavor',
'left_text','left_res','left_ent','left_pan','left_rsk','left_bud','left_per','left_inf','left_wea','left_for','left_sur',
'right_text','right_res','right_ent','right_pan','right_rsk','right_bud','right_per','right_inf','right_wea','right_for','right_sur'
]
});
fs.writeFileSync('./src/data/cards.csv', newCsv);
console.log('CSV updated');
console.log('CSV updated with keyword and question');

37
client/update_csv_tags.js Normal file
View File

@@ -0,0 +1,37 @@
import fs from 'fs';
import Papa from 'papaparse';
const csv = fs.readFileSync('./src/data/cards.csv', 'utf-8');
const parsed = Papa.parse(csv, { header: true });
parsed.data.forEach(row => {
if (!row.id) return;
// Initialize empty tags
row.left_tag = '';
row.right_tag = '';
// Assign tags based on id or content
if (row.id == 1) { row.left_tag = 'tag_ruthless'; } // 무력 진압
if (row.id == 4) { row.left_tag = 'tag_madness'; } // DNA 이식 실험 승인
if (row.id == 6) { row.left_tag = 'tag_corruption'; } // 부산물 암시장 매각
if (row.id == 7) { row.left_tag = 'tag_coverup'; } // 대중 매체 장악 감시망
if (row.id == 10) { row.left_tag = 'tag_coverup'; } // 유튜버 제거
if (row.id == 11) { row.right_tag = 'tag_cult'; } // 종교 의식 방관
if (row.id == 12) { row.left_tag = 'tag_corruption'; } // 장부 은폐 공작
if (row.id == 14) { row.left_tag = 'tag_alien_contact'; } // 이계 무역로 개방
if (row.id == 17) { row.left_tag = 'tag_ruthless'; } // 시민 포기 및 폭격
if (row.id == 22) { row.left_tag = 'tag_alien_contact'; } // 유물 집중 연구
if (row.id == 29) { row.left_tag = 'tag_alien_contact'; } // 이계 외교 사절 거래
});
const newCsv = Papa.unparse(parsed.data, {
columns: [
'id','type','keyword','question','text','flavor',
'left_text','left_tag','left_res','left_ent','left_pan','left_rsk','left_bud','left_per','left_inf','left_wea','left_for','left_sur',
'right_text','right_tag','right_res','right_ent','right_pan','right_rsk','right_bud','right_per','right_inf','right_wea','right_for','right_sur'
]
});
fs.writeFileSync('./src/data/cards.csv', newCsv);
console.log('CSV updated with tags');

View File

@@ -174,3 +174,32 @@
- 태그 0~3개: 페이즈1 (사태 파악 및 은폐의 시작)
- 태그 4~7개: 페이즈2 (프로젝트 가동 및 이사회의 개입 본격화)
- 태그 8개 이상: 페이즈3 (통제 불능의 사태 및 종말의 카운트다운)
## 라운드 플로우차트 (Round Flowchart)
```mermaid
graph TD
A[라운드 시작] --> B(잔여 결재 건수 확인);
B --> C{결재 건수 남음?};
C -- Yes --> D[결재 단계];
D --> E(토큰/파라미터 증감);
E --> F{파산 상태인가?};
F -- Yes --> G[파산 카운터 증가];
F -- No --> H;
G --> H(잔여 결재 1감소);
H --> B;
C -- No --> I[정기 의회 단계 진입];
I --> J{파산 문책 경고 상태인가?};
J -- Yes (토큰 마이너스 상태) --> K[해임 / 게임 오버];
J -- No --> L{파산 카운터 확인};
L -- 1 이상 --> M[파산 문책 경고 및 패널티];
L -- 0 --> N[정상 상정 안건 처리];
M --> O;
N --> O(의원 표결 및 국장 개입);
O --> P{탄핵안 통과?};
P -- Yes --> Q[정치적 탄핵 / 게임 오버];
P -- No --> R[법안 결과 반영];
R --> S(다음 라운드 진입 및 필요 결재 건수 재설정);
S --> A;
```

View File

@@ -51,7 +51,15 @@
- **내부 감사**: [인력] 소모. 임원 기권 처리. 페널티: 내부 저항 상승.
## 4. 콘텐츠 진행 및 페이징 (Progression)
플레이어가 획득한 [내러티브 태그]의 스택에 따라 게임의 페이즈가 강제됩니다.
게임의 진행은 '시간의 흐름(결재 건수)'과 '스토리 진행(내러티브 태그)'이라는 두 가지 축을 기준으로됩니다.
#### 4.1. 시간의 흐름과 라운드 시스템 (Time & Rounds)
- **기초 단위**: 결재 1건 (플레이어의 카드 스와이프 1회).
- **라운드 (Round)**: '제 X차 그림자 의회 회의'로 구분되는 주기 단위. 정해진 결재 건수(XX건)가 모일 때마다 하나의 라운드가 종료되며 의회가 개최됩니다.
- **종료 조건**: 라운드가 계속 반복되며 의회의 압박을 방어하다가, 획득한 내러티브 태그가 8개 이상이 되어 종말 카운트다운 페이즈를 돌파하면 최종 엔딩이 트리거됩니다.
#### 4.2. 페이즈 전환과 덱 풀 변경 (Story Phases)
플레이어가 획득한 [내러티브 태그]의 스택에 따라 게임의 페이즈가 강제 전환됩니다. 페이즈가 전환될 때마다 덱에서 등장하는 딜레마 카드의 풀(Pool)이 극적으로 변경되며, 이는 반복되는 로그라이크 루프에서 게임이 지루해지는 것을 방지하는 핵심 장치입니다.
- **페이즈 1 (태그 0~3개)**: 사태 파악 단계. 일상/위기 딜레마 중심으로 토큰 수급과 덱 오염 빌드업.
- **페이즈 2 (태그 4~7개)**: 본격화 단계. 작전/극비 프로젝트가 등장하여 엔진 구축 및 자산 획득의 기회 제공.
- **페이즈 3 (태그 8개 이상)**: 종말 카운트다운. 극한의 오염 카드와 뉴스 속보가 쏟아지는 자원 고갈의 생존 구간.

View File

@@ -1,4 +1,4 @@
제공해주신 기획서를 바탕으로, 게임의 핵심 화면들을 텍스트로 구조화한 **텍스트 와이어프레임(Text Wireframe)** 문서를 작성했습니다. 모바일과 스팀 환경을 모두 고려하여, 중앙 집중형 레이아웃을 기준으로 UI 요소들의 배치를 정의했습니다.
모바일과 스팀 환경을 모두 고려하여, 중앙 집중형 레이아웃을 기준으로 UI 요소들의 배치를 정의했습니다.
# 📱 텍스트 와이어프레임 (Text Wireframe)
@@ -6,7 +6,7 @@
플레이어가 스와이프를 통해 딜레마를 해결하고 덱을 구축하는 핵심 플레이 화면입니다.
### [상단 고정 영역: 미니멀 상태창]
- **좌상단 (내러티브):** `[📖 4/8]` (현재 획득한 내러티브 태그 숫자만 작게 표기. 터치 시 연대기 팝업).
- **좌상단 (내러티브):** `[📖 4/8]` (현재 획득한 내러티브 태그 숫자만 작게 표기. 터치 시 연대기 팝업).우측 여백에 다가오는 의회의 압박감을 시각화하는 `[제 X차 정례회의 상정까지: 잔여 00건]` 텍스트를 나란히 배치합니다.
- **중앙~우상단 (핵심 붕괴 파라미터):**
- 바(Bar) 형태를 버리고, 파라미터별 아이콘과 5개의 점(Dot)으로 1~5단계를 심플하게 표기합니다.
- `[저항 ✊] ⚫⚫⚪⚪⚪` | `[엔트로피 🌀] ⚫⚫⚫⚫⚪` | `[대중공황 😱] ⚫⚫⚫⚪⚪`