핵심 컨셉 '엔진 빌딩 vs 정치적 견제' 적용

This commit is contained in:
Kyoung5seo
2026-04-16 23:19:50 +09:00
parent 4aa36a5273
commit 9ec2fc260a
8 changed files with 500 additions and 74 deletions

51
implementation_plan.md Normal file
View File

@@ -0,0 +1,51 @@
# 엔진 빌딩 및 정치 견제 시스템 개편안
국장님의 '빌드업의 재미(엔진 빌딩)'와 '정치적 견제적 딜레마'를 극대화하기 위해 기존의 단순 자원 증감 로직을 전향적으로 개편하여, 보드게임에서 사용되는 **규칙 개조(Rule-bending)****연쇄 효과(Combo Chain)** 시스템을 도입하려 합니다.
## User Review Required
> [!IMPORTANT]
> 본 개편안은 게임의 코어 루프(`processDecision`, `showPrediction` 등)에 막대한 영향을 미칩니다.
> 제시된 이벤트 트리거 구조와 새롭게 도입할 법안/자산 제원 양식이 기획 의도와 부합하는지 피드백 부탁드립니다.
---
## Proposed Changes
### 1. 결재 룰 개조 (Rule-Bending) 엔진 시스템
자산(Asset)과 시너지(Synergy)가 매 턴 자원을 주는 것을 넘어, 결재 스와이프(액션) 시 조건부로 발동하여 예측(Prediction)과 결과에 개입합니다.
- **이벤트 훅(Event Hook) 추가:**
- `on_predict`: 스와이프 도중 미리 계산 (예: 군사 1개 보유 시 10% 할인된 비용 노출)
- `on_approve` / `on_reject`: 결재 수락/거부 시 추가 자원 획득 로직 개입 (예: 엔트로피 50 이상일 때 거절 시 기밀/비자금 획득)
- `on_stat_change`: 특정 자원 변동 시 연쇄 반응 발동 (예: 비자금이 오를 때마다 예산이 오르는 그림자 펀드)
- `on_override`: 롱프레스 시 비용 대체 (기존 '신임도 -20' 대신 '비자금 10 소모' 등으로 치환)
- **구현 방식:**
- 스탯 변동을 `state.budget += ...` 에서 `updateStat('budget', value, source)` 형태로 래핑(Wrapping)하여 연쇄 작용(Trigger)을 감지합니다.
### 2. 의회의 표적 견제 (Targeted Sabotage)
의회 법안 모델에 국장의 태그 엔진을 직격하는 디버프 속성을 추가합니다.
- **새로운 법안 Type 추가 (`nullify_tag`):**
- 예: `target_tag: '군사'`, `type: 'nullify_tag'`, `upkeep_penalty: 20`
- 이 법안이 가결되면, 국장이 가진 모든 `[군사]` 태그 자산은 비활성화(회색 처리)되며 아무 효과도 내지 못합니다.
- 동시에 매 턴(일일 보고)마다 비활성화된 군사 자산 1개당 유지비 $20가 강제로 부과되어, 엔진이 짐짝으로 전락하게 만듭니다.
### 3. Fallback 데이터(DB) 확충 및 테스트용 예시 투입
국장님이 제시하신 예시를 내장 데이터(`loadFallbackData`)에 반영하여 즉시 테스트 환경을 구축합니다.
- **자산(Asset) 예시 투입:**
- `비밀 해커팀` (조건: 엔트로피 50 이상에서 거절 시 비자금 +2)
- `그림자 펀드` (조건: 비자금 획득 시 발동 -> 예산 환전 연쇄 작용)
- `용병 길드` (조건: 오버라이드 시 신임도 대신 비자금 -10 지불)
- **법안(Bill) 예시 투입:**
- `군축 조약` (타겟: 군사 / 효과: 군사 자산 무효화 및 유지비 페널티)
- `자산 압류 특별법` (타겟: 자본 / 효과: 자본 자산 무효화 및 비자금 전액 몰수)
## Verification Plan
### 수동 테스트 방안 (의도 검증)
1. 첫 턴부터 `용병 길드`, `그림자 펀드` 등 연쇄 콤보 자산을 직접 주입하여 스와이프를 테스트합니다.
2. 엔트로피를 인위적으로 50 이상 올린 뒤 스와이프를 거절(Left)해 비자금이 복사되는지 확인합니다.
3. 일부러 한 태그(예: 군사)를 모은 채로 의회 페이즈로 넘어가, 견제 법안(군축 조약)이 상정되고 가결 시 자산 목록에서 회색조 처리가 되며 페널티가 들어오는지 검증합니다.

11
src/data/agendas.csv Normal file
View File

@@ -0,0 +1,11 @@
id,title,desc
AG_01,대토벌의 시대,괴수 토벌 집중 주간. (작전부 안건 빈도 증가 / 전투 예산 소모 심화)
AG_02,긴축 재정,허리띠를 졸라맵니다. (매 턴 기본 예산 $10 추가 차감 / 자산 매각 이익 증가)
AG_03,투명한 사회,감사팀이 활개칩니다. (비자금 사용 및 불법 자산 매각 시 의심도 상승폭 2배)
AG_04,그림자 작전,어둠의 뒷거래가 활성화됩니다. (안건 승인 시 비자금 획득 확률 및 양 증가)
AG_05,이단 심문기,마녀사냥의 주간입니다. (오컬트/마법 관련 안건 승인 시 엔트로피 폭등)
AG_06,연구 장려 주간,과학 발전에 예산을 쏟습니다. (R&D 본부 비용 30% 감소 및 자산 획득률 증가)
AG_07,피의 숙청,"배신자를 색출합니다. (안건 거절 시 신임도 대폭 상승, 하지만 의심도도 동반 상승)"
AG_08,대규모 차원 균열,우주적 재난 주간. (모든 안건의 엔트로피 기본 증가량이 1.5배로 증폭됨)
AG_09,글로벌 후원금,스폰서들의 기분이 좋습니다. (시작 시 총 예산 +$150 일시불 지원)
AG_10,통제 사회,대중을 통제합니다. (정보 통제 관련 안건 비용 절반 감소)
1 id title desc
2 AG_01 대토벌의 시대 괴수 토벌 집중 주간. (작전부 안건 빈도 증가 / 전투 예산 소모 심화)
3 AG_02 긴축 재정 허리띠를 졸라맵니다. (매 턴 기본 예산 $10 추가 차감 / 자산 매각 이익 증가)
4 AG_03 투명한 사회 감사팀이 활개칩니다. (비자금 사용 및 불법 자산 매각 시 의심도 상승폭 2배)
5 AG_04 그림자 작전 어둠의 뒷거래가 활성화됩니다. (안건 승인 시 비자금 획득 확률 및 양 증가)
6 AG_05 이단 심문기 마녀사냥의 주간입니다. (오컬트/마법 관련 안건 승인 시 엔트로피 폭등)
7 AG_06 연구 장려 주간 과학 발전에 예산을 쏟습니다. (R&D 본부 비용 30% 감소 및 자산 획득률 증가)
8 AG_07 피의 숙청 배신자를 색출합니다. (안건 거절 시 신임도 대폭 상승, 하지만 의심도도 동반 상승)
9 AG_08 대규모 차원 균열 우주적 재난 주간. (모든 안건의 엔트로피 기본 증가량이 1.5배로 증폭됨)
10 AG_09 글로벌 후원금 스폰서들의 기분이 좋습니다. (시작 시 총 예산 +$150 일시불 지원)
11 AG_10 통제 사회 대중을 통제합니다. (정보 통제 관련 안건 비용 절반 감소)

21
src/data/assets.csv Normal file
View File

@@ -0,0 +1,21 @@
id,name,type,desc,passive_eff_key,passive_eff_val,risk_eff_key,risk_eff_val,sale_value,sale_risk_key,sale_risk_val,flavor
AST_001,유령 커피머신,Relic,"커피 맛은 천상, 밤마다 악몽.",budget,5,entropy,1,50,suspicion,5,카페인과 공포는 훌륭한 각성제.
AST_002,말하는 곰팡이,Bio,벽속의 비밀을 엿듣고 알려줍니다.,black_fund,5,suspicion,2,80,entropy,10,내부 고발자보다 낫습니다.
AST_003,트롤 고기,Bio,잘라내도 계속 자라나는 고기 덩어리.,budget,15,trust,-2,120,suspicion,20,메뉴가 함박스테이크로 고정됨.
AST_004,저주받은 금화,Relic,소유자의 수명을 대가로 금화 생성.,black_fund,10,trust,-5,200,entropy,15,시간으로 돈을 사는 겁니다.
AST_005,비유클리드 칩,Tech,현실에 존재하지 않는 연산을 수행.,budget,30,entropy,5,300,suspicion,30,1+1=3이 되는 기적.
AST_006,베헤모스의 알,Bio,S급 괴수의 알입니다. 따뜻합니다.,trust,2,entropy,10,500,entropy,50,좋은 징조는 아니죠.
AST_007,예언자의 눈알,Relic,10초 뒤의 미래를 보여줍니다.,black_fund,15,entropy,3,150,suspicion,10,내일의 하한가를 아는 고통.
AST_008,자동 서명 깃펜,Relic,서류에 자동으로 서명합니다.,trust,5,black_fund,-5,40,trust,-10,팔이 아플 일은 없겠군요.
AST_009,흡혈귀 혈액팩,Bio,강력한 재생 효과가 있는 흡혈귀 피.,-,-,suspicion,1,100,suspicion,15,빈티지 1980년산 O형.
AST_010,외계 통신기,Tech,우주 어딘가로 좌표를 송출합니다.,-,-,entropy,5,80,entropy,20,"응답하라, 오버."
AST_011,거짓말 탐지 넥타이,Relic,거짓말을 하면 목을 강하게 조릅니다.,trust,3,trust,-1,60,suspicion,5,숨 막히는 진실 공방.
AST_012,무한 동력 햄스터,Bio,지치지 않고 쳇바퀴를 도는 돌연변이.,budget,20,entropy,5,250,entropy,10,존재해선 안 될 생물입니다.
AST_013,부패한 장부 원본,Info,전임 국장의 횡령 내역 파일.,black_fund,20,suspicion,10,0,trust,-50,이걸 태우면 당신도 공범입니다.
AST_014,둠스데이 버튼,Tech,세계를 멸망시킬 버튼.,-,-,entropy,15,1000,entropy,100,절대 누르지 마시오.
AST_015,사랑의 묘약,Relic,상대를 맹목적으로 따르게 만듭니다.,trust,5,suspicion,5,150,suspicion,20,화학적 동의도 동의입니까?
AST_016,좀비 바이러스 백신,Bio,세계 유일의 치료제 샘플.,-,-,entropy,5,600,entropy,30,손 떨지 마세요.
AST_017,악마의 계약서,Relic,대가 란이 비어있는 백지 계약서.,black_fund,30,entropy,8,5,entropy,2,핏방울을 떨어뜨리세요.
AST_018,투명 망토,Tech,투명해지지만 소리는 남음.,suspicion,-2,-,-,90,suspicion,10,보이지 않는다고 없는 건 아니죠.
AST_019,기밀 하드디스크,Info,주요국 정상들의 치명적 스캔들.,budget,50,trust,-10,400,trust,-100,판도라의 폴더.
AST_020,식인 식물 화분,Bio,시체 처리용으로 훌륭합니다.,suspicion,-5,trust,-1,30,entropy,,
1 id name type desc passive_eff_key passive_eff_val risk_eff_key risk_eff_val sale_value sale_risk_key sale_risk_val flavor
2 AST_001 유령 커피머신 Relic 커피 맛은 천상, 밤마다 악몽. budget 5 entropy 1 50 suspicion 5 카페인과 공포는 훌륭한 각성제.
3 AST_002 말하는 곰팡이 Bio 벽속의 비밀을 엿듣고 알려줍니다. black_fund 5 suspicion 2 80 entropy 10 내부 고발자보다 낫습니다.
4 AST_003 트롤 고기 Bio 잘라내도 계속 자라나는 고기 덩어리. budget 15 trust -2 120 suspicion 20 메뉴가 함박스테이크로 고정됨.
5 AST_004 저주받은 금화 Relic 소유자의 수명을 대가로 금화 생성. black_fund 10 trust -5 200 entropy 15 시간으로 돈을 사는 겁니다.
6 AST_005 비유클리드 칩 Tech 현실에 존재하지 않는 연산을 수행. budget 30 entropy 5 300 suspicion 30 1+1=3이 되는 기적.
7 AST_006 베헤모스의 알 Bio S급 괴수의 알입니다. 따뜻합니다. trust 2 entropy 10 500 entropy 50 좋은 징조는 아니죠.
8 AST_007 예언자의 눈알 Relic 10초 뒤의 미래를 보여줍니다. black_fund 15 entropy 3 150 suspicion 10 내일의 하한가를 아는 고통.
9 AST_008 자동 서명 깃펜 Relic 서류에 자동으로 서명합니다. trust 5 black_fund -5 40 trust -10 팔이 아플 일은 없겠군요.
10 AST_009 흡혈귀 혈액팩 Bio 강력한 재생 효과가 있는 흡혈귀 피. - - suspicion 1 100 suspicion 15 빈티지 1980년산 O형.
11 AST_010 외계 통신기 Tech 우주 어딘가로 좌표를 송출합니다. - - entropy 5 80 entropy 20 응답하라, 오버.
12 AST_011 거짓말 탐지 넥타이 Relic 거짓말을 하면 목을 강하게 조릅니다. trust 3 trust -1 60 suspicion 5 숨 막히는 진실 공방.
13 AST_012 무한 동력 햄스터 Bio 지치지 않고 쳇바퀴를 도는 돌연변이. budget 20 entropy 5 250 entropy 10 존재해선 안 될 생물입니다.
14 AST_013 부패한 장부 원본 Info 전임 국장의 횡령 내역 파일. black_fund 20 suspicion 10 0 trust -50 이걸 태우면 당신도 공범입니다.
15 AST_014 둠스데이 버튼 Tech 세계를 멸망시킬 버튼. - - entropy 15 1000 entropy 100 절대 누르지 마시오.
16 AST_015 사랑의 묘약 Relic 상대를 맹목적으로 따르게 만듭니다. trust 5 suspicion 5 150 suspicion 20 화학적 동의도 동의입니까?
17 AST_016 좀비 바이러스 백신 Bio 세계 유일의 치료제 샘플. - - entropy 5 600 entropy 30 손 떨지 마세요.
18 AST_017 악마의 계약서 Relic 대가 란이 비어있는 백지 계약서. black_fund 30 entropy 8 5 entropy 2 핏방울을 떨어뜨리세요.
19 AST_018 투명 망토 Tech 투명해지지만 소리는 남음. suspicion -2 - - 90 suspicion 10 보이지 않는다고 없는 건 아니죠.
20 AST_019 기밀 하드디스크 Info 주요국 정상들의 치명적 스캔들. budget 50 trust -10 400 trust -100 판도라의 폴더.
21 AST_020 식인 식물 화분 Bio 시체 처리용으로 훌륭합니다. suspicion -5 trust -1 30 entropy

13
src/data/bills.csv Normal file
View File

@@ -0,0 +1,13 @@
id,name,desc,tag,type,flavor
B_01,생명 존중법,무력 사용 및 사살 안건의 승인을 금지합니다.,kill,lock_approve,괴수의 생명도 생명입니까?
B_02,무제한 발포령,작전부의 전투/사살 안건 거절을 금지합니다.,kill,lock_reject,탄약은 충분합니다. 쏘십시오.
B_03,마법 통제법,통제 불능의 오컬트/마법 안건 승인을 금지합니다.,magic,lock_approve,미신의 시대는 끝났습니다.
B_04,생명 윤리법,비윤리적인 생체 실험 및 변이 안건 승인을 금지합니다.,bio,lock_approve,우리는 아직 인간으로 남아있어야 합니다.
B_05,예산 삭감안,예산 낭비가 우려되는 대규모 안건 승인을 금지합니다.,budget,lock_approve,영수증 처리가 안 되는 일은 하지 마십시오.
B_06,강제 징집령,작전 본부의 모든 방어/전투 안건 거절을 금지합니다.,ops,lock_reject,도망칠 곳은 없습니다.
B_07,정보 공개법,대중을 기만하는 은폐/기억소거 안건 승인을 금지합니다.,secret,lock_approve,대중은 진실을 알 권리가 있습니다.
B_08,비밀 유지법,기구를 외부로 노출시키는 양지화 안건 승인을 금지합니다.,public,lock_approve,우리는 영원히 그림자 속에 남습니다.
B_09,특별 감사법,자금 유용 및 횡령 관련 안건 거절을 금지(강제 수사)합니다.,audit,lock_reject,숨길 것이 없다면 두려울 것도 없습니다.
B_10,연구 지원법,연구 부서의 모든 기술/연구 안건 거절을 금지합니다.,research,lock_reject,과학 발전만이 인류의 살 길입니다.
B_11,순혈주의 선언,이종족 및 비인간 관련 안건의 승인을 엄격히 금지합니다.,pure,lock_approve,뾰족한 귀를 가진 자들을 믿지 마십시오.
B_12,진화 강제법,신체 개조를 미루거나 거절하는 행위를 금지합니다.,bio,lock_reject,진화를 거부하는 것은 곧 도태입니다.
1 id name desc tag type flavor
2 B_01 생명 존중법 무력 사용 및 사살 안건의 승인을 금지합니다. kill lock_approve 괴수의 생명도 생명입니까?
3 B_02 무제한 발포령 작전부의 전투/사살 안건 거절을 금지합니다. kill lock_reject 탄약은 충분합니다. 쏘십시오.
4 B_03 마법 통제법 통제 불능의 오컬트/마법 안건 승인을 금지합니다. magic lock_approve 미신의 시대는 끝났습니다.
5 B_04 생명 윤리법 비윤리적인 생체 실험 및 변이 안건 승인을 금지합니다. bio lock_approve 우리는 아직 인간으로 남아있어야 합니다.
6 B_05 예산 삭감안 예산 낭비가 우려되는 대규모 안건 승인을 금지합니다. budget lock_approve 영수증 처리가 안 되는 일은 하지 마십시오.
7 B_06 강제 징집령 작전 본부의 모든 방어/전투 안건 거절을 금지합니다. ops lock_reject 도망칠 곳은 없습니다.
8 B_07 정보 공개법 대중을 기만하는 은폐/기억소거 안건 승인을 금지합니다. secret lock_approve 대중은 진실을 알 권리가 있습니다.
9 B_08 비밀 유지법 기구를 외부로 노출시키는 양지화 안건 승인을 금지합니다. public lock_approve 우리는 영원히 그림자 속에 남습니다.
10 B_09 특별 감사법 자금 유용 및 횡령 관련 안건 거절을 금지(강제 수사)합니다. audit lock_reject 숨길 것이 없다면 두려울 것도 없습니다.
11 B_10 연구 지원법 연구 부서의 모든 기술/연구 안건 거절을 금지합니다. research lock_reject 과학 발전만이 인류의 살 길입니다.
12 B_11 순혈주의 선언 이종족 및 비인간 관련 안건의 승인을 엄격히 금지합니다. pure lock_approve 뾰족한 귀를 가진 자들을 믿지 마십시오.
13 B_12 진화 강제법 신체 개조를 미루거나 거절하는 행위를 금지합니다. bio lock_reject 진화를 거부하는 것은 곧 도태입니다.

9
src/data/factions.csv Normal file
View File

@@ -0,0 +1,9 @@
id,name,color,share,rival,love_tag,hate_tag,desc
iron,철의 수호당,#ef4444,25,libra,kill,bio,괴수 즉결 처형 및 압도적 군사력 증강을 주장하는 강경파.
libra,지식 보존당,#3b82f6,20,iron,research,kill,괴수를 생포하고 연구하여 미지의 지식을 축적하려는 학자들.
pure,순수 인간당,#10b981,15,gene,pure,bio,"인간 중심주의를 표방하며, 이종족과 신체 개조를 극도로 혐오함."
gene,진화 미래당,#8b5cf6,10,pure,bio,pure,인류의 생물학적 한계를 초월하기 위해 괴수 DNA 이식 등을 시도함.
trade,차원 무역당,#f59e0b,10,covenant,budget,magic,이계와의 무역과 괴수 부산물 판매로 막대한 부를 축적하려는 자본가들.
covenant,고대 공존당,#06b6d4,5,trade,magic,budget,심연의 고대 신들을 숭배하며 그들과의 평화적(굴종적) 공존을 모색함.
veil,침묵의 장막당,#6b7280,10,reshape,secret,public,"대중이 진실을 알면 미쳐버릴 거라 믿으며, 철저한 은폐와 기억 소거를 집행함."
reshape,세계 재건당,#000000,5,veil,public,secret,비밀주의를 버리고 기구가 전면에 나서 세계 정부로 군림해야 한다고 믿음.
1 id name color share rival love_tag hate_tag desc
2 iron 철의 수호당 #ef4444 25 libra kill bio 괴수 즉결 처형 및 압도적 군사력 증강을 주장하는 강경파.
3 libra 지식 보존당 #3b82f6 20 iron research kill 괴수를 생포하고 연구하여 미지의 지식을 축적하려는 학자들.
4 pure 순수 인간당 #10b981 15 gene pure bio 인간 중심주의를 표방하며, 이종족과 신체 개조를 극도로 혐오함.
5 gene 진화 미래당 #8b5cf6 10 pure bio pure 인류의 생물학적 한계를 초월하기 위해 괴수 DNA 이식 등을 시도함.
6 trade 차원 무역당 #f59e0b 10 covenant budget magic 이계와의 무역과 괴수 부산물 판매로 막대한 부를 축적하려는 자본가들.
7 covenant 고대 공존당 #06b6d4 5 trade magic budget 심연의 고대 신들을 숭배하며 그들과의 평화적(굴종적) 공존을 모색함.
8 veil 침묵의 장막당 #6b7280 10 reshape secret public 대중이 진실을 알면 미쳐버릴 거라 믿으며, 철저한 은폐와 기억 소거를 집행함.
9 reshape 세계 재건당 #000000 5 veil public secret 비밀주의를 버리고 기구가 전면에 나서 세계 정부로 군림해야 한다고 믿음.

9
src/data/quests.csv Normal file
View File

@@ -0,0 +1,9 @@
id,title,target_type,target_dept,target_val,reward_txt,penalty_txt
Q_01,강경 진압,approve_count,작전 본부,3,예산 +$100,신임도 -10
Q_02,안정화 작업,maintain_entropy,-,25,신임도 +15,엔트로피 +15
Q_03,흑자 전환,black_fund,-,60,의심도 초기화,비자금 전액 몰수
Q_04,지식의 축적,approve_count,R&D 본부,2,새로운 자산 지급,예산 -$50
Q_05,정보 통제,approve_count,정보 본부,2,신임도 +10,의심도 +20
Q_06,철저한 관리,reject_count,-,3,예산 +$80,엔트로피 +10
Q_07,행정 쇄신,approve_count,행정 본부,2,의심도 -10,신임도 -5
Q_08,자본의 흐름,budget_up,-,600,비자금 +30,의심도 +15
1 id title target_type target_dept target_val reward_txt penalty_txt
2 Q_01 강경 진압 approve_count 작전 본부 3 예산 +$100 신임도 -10
3 Q_02 안정화 작업 maintain_entropy - 25 신임도 +15 엔트로피 +15
4 Q_03 흑자 전환 black_fund - 60 의심도 초기화 비자금 전액 몰수
5 Q_04 지식의 축적 approve_count R&D 본부 2 새로운 자산 지급 예산 -$50
6 Q_05 정보 통제 approve_count 정보 본부 2 신임도 +10 의심도 +20
7 Q_06 철저한 관리 reject_count - 3 예산 +$80 엔트로피 +10
8 Q_07 행정 쇄신 approve_count 행정 본부 2 의심도 -10 신임도 -5
9 Q_08 자본의 흐름 budget_up - 600 비자금 +30 의심도 +15

41
src/data/scenarios.csv Normal file
View File

@@ -0,0 +1,41 @@
id,act,dept,color,title,body,faction,rival,tags,conflict,cost,yes_e,no_e,flavor,reward_asset_id
101,1,행정 본부,#d97706,탕비실 믹스커피 횡령,야간조 요원들이 비품실의 믹스커피 50박스를 사적으로 빼돌렸습니다. 횡령으로 징계할까요?,지식 보존당,iron,audit,원칙 vs 융통성,0,-1,1,맥심 골드는 중대 사항입니다.,AST_001
102,1,작전 본부,#c92a2a,하수구 슬라임,하수구에서 악취를 유발하는 하급 슬라임이 발견되었습니다. 화학반을 투입해 소각하시겠습니까?,철의 수호당,libra,"kill,ops",강경진압 vs 방치,20,-3,3,소금 뿌리면 안 됩니까?,-
103,1,정보 본부,#1c4f82,인터넷 괴담 검열,SNS에 퍼진 기현상 목격담을 통제망을 가동해 강제 삭제하고 작성자를 추적할까요?,침묵의 장막당,reshape,secret,은폐 vs 자유,15,-2,4,좋아요가 만 개를 넘으면 실체가 됩니다.,-
104,1,R&D 본부,#6b21a8,말하는 곰팡이 배양,창고에서 인간의 언어를 모방하는 곰팡이가 발견되었습니다. 소각 규정을 무시하고 배양할까요?,진화 미래당,pure,"bio,research",호기심 vs 규정,30,4,-2,목소리가 꽤 좋답니다.,AST_002
105,1,행정 본부,#d97706,국회 로비 자금,기구의 예산을 늘리기 위해 의원들에게 '도서 구입비' 명목의 비자금을 찔러 넣어줄까요?,세계 재건당,veil,"budget,audit",타협 vs 청렴,60,0,0,돈 냄새는 귀신도 좋아합니다.,-
106,1,작전 본부,#c92a2a,이단 종교 집회,괴수를 섬기는 '별빛 교단'이 민간인을 모아 집회를 엽니다. 무장 타격대로 무력 해산시킬까요?,철의 수호당,covenant,"kill,ops",무력 vs 평화,40,-4,5,이단에게 자비는 없습니다.,-
107,1,정보 본부,#1c4f82,상수도 기억소거,전투를 목격한 시민이 너무 많습니다. 상수도에 약한 기억소거제를 타서 광역 은폐를 실시할까요?,침묵의 장막당,reshape,"secret,bio",통제 vs 윤리,30,-5,6,수돗물 맛이 조금 달콤해집니다.,-
108,1,R&D 본부,#6b21a8,이단 유물 발굴,재개발 구역에서 고대 마법 유적이 발견되었습니다. 군의 폭파 주장을 묵살하고 발굴할까요?,지식 보존당,iron,"magic,research",지식 vs 파괴,40,3,-3,흙 묻은 역사가 미래를 엽니다.,-
109,1,행정 본부,#d97706,괴수 가죽 밀거래,창고팀이 죽은 괴수 가죽을 블랙마켓에 팔려다 걸렸습니다. 징계 대신 가죽을 팔아 예산에 보탤까요?,차원 무역당,covenant,"budget,audit",자본 vs 규정,-80,5,-2,돈 냄새가 피 냄새를 덮습니다.,-
110,1,작전 본부,#c92a2a,뱀파이어 요원 채용,전향한 흡혈귀를 요원으로 쓰자는 건의입니다. 피를 주기적으로 제공해야 하지만 효율은 좋습니다.,고대 공존당,pure,"bio,ops",공존 vs 순혈,20,2,-2,피만 제때 주면 우리 편입니다.,AST_009
111,1,정보 본부,#1c4f82,라디오 전파 납치,심야 라디오 DJ가 우리 조직을 폭로하려 합니다. 납치 세뇌 후 선전 방송 스피커로 재활용할까요?,세계 재건당,veil,"public,secret",세뇌 vs 침묵,50,4,-3,통제의 꽃은 대중의 열광입니다.,-
112,1,R&D 본부,#6b21a8,부상 요원 기계화,팔을 잃은 요원이 사이보그 개조 수술을 자원했습니다. 순수 인간주의 원칙을 깨고 수술할까요?,진화 미래당,pure,"bio,research",진화 vs 순수,40,3,-2,살점보다 강철이 믿음직합니다.,-
113,1,행정 본부,#d97706,이종족 구호소 건립,차원 균열 난민들을 위해 도심 외곽에 이종족 난민 구호소를 세울까요? 시민 반발이 거셀 겁니다.,고대 공존당,trade,"pure,budget",공존 vs 배척,60,4,-4,우리는 모두 우주의 미아입니다.,-
114,1,작전 본부,#c92a2a,예언자 스카웃,10초 뒤를 보는 사기꾼이 고액 연봉을 요구하며 합류를 제안했습니다. 채용할까요?,차원 무역당,covenant,"magic,ops",실용 vs 원칙,70,5,-2,하한가를 피하는 값싼 비용.,AST_007
115,1,정보 본부,#1c4f82,기밀문서 유출자,내부 고발자가 비윤리적 실험을 폭로하려다 체포되었습니다. 그를 '조용히 처리'할까요?,침묵의 장막당,reshape,"secret,kill",은폐 vs 진실,30,-5,8,진실은 무덤 속에 있어야 합니다.,AST_013
201,2,작전 본부,#c92a2a,베헤모스의 알 회수,불법 경매장에 도시 붕괴급 괴수의 알이 나왔습니다. 파괴하지 않고 연구용으로 회수할까요?,진화 미래당,iron,"ops,bio",연구 vs 안전,100,8,-5,품속에 넣으니 따뜻하네요.,AST_006
202,2,R&D 본부,#6b21a8,비유클리드 칩 도입,연산력은 무한하지만 주변 공간을 뒤틀어버리는 외계 칩을 메인 서버에 장착할까요?,세계 재건당,pure,"research,magic",기술 vs 현실붕괴,60,6,0,1+1=3이 되는 기적.,AST_005
203,2,행정 본부,#d97706,저주받은 금화,소지자의 수명을 갉아먹지만 무한히 금화를 쏟아내는 유적 항아리를 자금원으로 쓸까요?,차원 무역당,covenant,"magic,budget",자본 vs 생명,-150,10,-5,돈 냄새가 핏물보다 진하군요.,AST_004
204,2,작전 본부,#c92a2a,전술핵 타격,12구역에 S급 괴수가 출현했습니다. 수만 명의 희생을 감수하고 소형 전술핵 타격을 승인할까요?,철의 수호당,libra,"kill,ops",학살 vs 방어실패,120,-15,20,도시는 다시 지으면 됩니다.,-
205,2,정보 본부,#1c4f82,해킹 그룹 참수,적대 해커들이 1급 기밀을 훔치고 있습니다. 역추적 암살 프로그램을 가동해 뇌를 태워버릴까요?,침묵의 장막당,reshape,"secret,kill",암살 vs 유출,50,-8,15,모니터 너머에 죽음 배달.,-
206,2,R&D 본부,#6b21a8,초인 부대 스파르탄,고통을 느끼지 못하는 생체 병기 부대를 창설하기 위해 인간의 존엄성을 포기하시겠습니까?,진화 미래당,pure,"bio,ops",진화 vs 윤리,90,10,-8,괴물을 잡기 위해 괴물이 됩니다.,-
207,2,행정 본부,#d97706,외계 물질 매각,창고의 위험한 외계 광물을 군수 기업이 거액에 사겠다고 합니다. 나중 파장은 무시하고 팔까요?,차원 무역당,veil,"budget,public",자본 vs 은폐,-200,12,-6,위험은 저들에게 전가합시다.,-
208,2,정보 본부,#1c4f82,숭배자 국회 진출,괴수를 섬기는 유력 인사가 국회에 입성하려 합니다. 선거를 조작하여 무조건 당선을 저지할까요?,철의 수호당,covenant,"secret,public",개입 vs 중립,60,-5,8,정치는 총칼 없는 전쟁입니다.,-
209,2,작전 본부,#c92a2a,생체 병기 폭주,피아를 식별하지 못하는 개조 요원이 폭주 중입니다. 목의 자폭 목걸이를 기폭시켜 처형할까요?,순수 인간당,gene,"kill,bio",처분 vs 관용,30,-6,10,불량품은 폐기해야 합니다.,-
210,2,R&D 본부,#6b21a8,타임머신 가동,작전 실패를 돌리기 위해 현실 우주 붕괴 리스크(70%)를 안고 회귀 장치를 가동하시겠습니까?,지식 보존당,iron,"research,magic",시간조작 vs 순응,150,18,-5,시간을 속이면 시간도 우리를 속입니다.,-
211,2,행정 본부,#d97706,차원문 상업 개방,이종족 상인들에게 통행료를 받고 차원문을 열어주는 관세법을 승인하시겠습니까?,차원 무역당,pure,"budget,magic",개방 vs 통제,-120,10,-4,돈은 차원을 넘어서도 통용됩니다.,-
212,2,정보 본부,#1c4f82,언론사 대표 암살,방송사 대표가 내일 아침 기구의 실체를 폭로하려 합니다. 오늘 밤 특수 요원을 보내 암살할까요?,침묵의 장막당,reshape,"kill,secret",암살 vs 폭로,70,-10,18,기사는 발행되지 않을 때 완벽합니다.,-
213,2,작전 본부,#c92a2a,지성체 괴수 휴전,언어를 구사하는 괴수 여왕이 학살을 멈추는 조건으로 영토 인정을 요구합니다. 휴전할까요?,고대 공존당,iron,ops,평화 vs 자존심,0,5,-12,적과 악수하려면 칼을 놔야 합니다.,-
214,2,R&D 본부,#6b21a8,복제 요원 양산,감정이 삭제된 복제 인간 요원들을 공장에서 찍어내듯 양산하는 비윤리적 계획을 승인할까요?,진화 미래당,pure,"bio,ops",복제 vs 윤리,110,12,-6,슬퍼할 가족이 없는 완벽한 소모품.,-
215,2,행정 본부,#d97706,최후의 벙커,대중을 버리고 수뇌부만 피신할 수 있는 초거대 지하 벙커 건설에 막대한 예산을 쏟아부을까요?,세계 재건당,trade,budget,생존 vs 방치,180,8,-4,지하엔 버려진 자들의 비명이 안 들립니다.,-
301,3,정보 본부,#1c4f82,비밀 결사 공개,세계가 패닉에 빠졌습니다. 음지에서 벗어나 우리가 전 세계를 직접 통치하겠다고 선포할까요?,세계 재건당,veil,"public,secret",양지화 vs 은폐,100,15,-5,구원자는 그림자에 숨지 않습니다.,-
302,3,작전 본부,#c92a2a,차원 붕괴 폭탄,적의 차원을 영구 붕괴시키는 블랙홀 폭탄입니다. 여파로 지구도 절반 파괴됩니다. 쏘시겠습니까?,철의 수호당,libra,"kill,ops",파멸 vs 지속,200,25,-10,불타는 세계 위에서 승리를 외칩니다.,AST_014
303,3,R&D 본부,#6b21a8,인류 강제 진화,전 인류의 DNA를 변이시키는 가스를 살포해 강제로 진화시킵니다. 부작용 치사율은 50%입니다.,진화 미래당,pure,bio,진화 vs 순수,250,30,-15,낡은 허물을 벗어던질 때입니다.,-
304,3,행정 본부,#d97706,글로벌 금융 해킹,자금이 완전히 고갈되었습니다. 대공황을 감수하고 적대국의 중앙 은행을 해킹해 예산을 증발시킬까요?,차원 무역당,audit,"budget,secret",자본 vs 경제붕괴,-300,20,-8,세상이 망해도 돈은 씁니다.,-
305,3,정보 본부,#1c4f82,지구 뇌파 리셋,폭동을 잠재우기 위해 위성에서 펄스를 쏴 전 인류를 기억 상실의 꼭두각시로 만들까요?,침묵의 장막당,reshape,"secret,public",백지화 vs 혼돈,180,22,-12,무지가 곧 평화요 망각이 구원입니다.,-
306,3,R&D 본부,#6b21a8,아카식 레코드 접속,우주의 모든 진리가 담긴 서버에 접속합니다. 연구원 90%가 뇌사할 확률을 안고 강행할까요?,지식 보존당,covenant,"research,magic",진리 vs 파멸,120,18,-10,우주의 비밀 앞에 인간은 나약합니다.,-
307,3,작전 본부,#c92a2a,군단 맹약,외계 군단이 인류 절반을 노예로 바치는 대가로 침공을 멈추겠답니다. 항복 문서에 서명할까요?,고대 공존당,iron,ops,굴복 vs 멸망,80,15,-18,무릎 꿇어서라도 숨을 이어가야 합니다.,-
308,3,행정 본부,#d97706,유전자 방주 발사,지구를 포기하고 순수 인류 엘리트들만 태운 방주를 우주로 발사해 탈출하시겠습니까?,순수 인간당,gene,"pure,budget",도피 vs 투쟁,200,25,-8,불순물들은 죽어가는 지구에 둡니다.,-
309,3,R&D 본부,#6b21a8,차원 융합 폭주,물리법칙을 무시하고 적 차원과 우리 차원을 융합시켜 아예 새로운 우주를 창조할까요?,세계 재건당,trade,"magic,research",재창조 vs 유지,220,35,-20,헌 우주를 부수고 새 우주를 엽시다.,-
310,3,작전 본부,#c92a2a,대재앙 방관,다른 대륙에 초거대 괴수가 깨어났습니다. 우리 전력을 보존하기 위해 그들의 멸망을 방관할까요?,침묵의 장막당,iron,"ops,kill",방관 vs 저지,0,20,-15,지는 싸움은 피하는 게 상책입니,-
1 id act dept color title body faction rival tags conflict cost yes_e no_e flavor reward_asset_id
2 101 1 행정 본부 #d97706 탕비실 믹스커피 횡령 야간조 요원들이 비품실의 믹스커피 50박스를 사적으로 빼돌렸습니다. 횡령으로 징계할까요? 지식 보존당 iron audit 원칙 vs 융통성 0 -1 1 맥심 골드는 중대 사항입니다. AST_001
3 102 1 작전 본부 #c92a2a 하수구 슬라임 하수구에서 악취를 유발하는 하급 슬라임이 발견되었습니다. 화학반을 투입해 소각하시겠습니까? 철의 수호당 libra kill,ops 강경진압 vs 방치 20 -3 3 소금 뿌리면 안 됩니까? -
4 103 1 정보 본부 #1c4f82 인터넷 괴담 검열 SNS에 퍼진 기현상 목격담을 통제망을 가동해 강제 삭제하고 작성자를 추적할까요? 침묵의 장막당 reshape secret 은폐 vs 자유 15 -2 4 좋아요가 만 개를 넘으면 실체가 됩니다. -
5 104 1 R&D 본부 #6b21a8 말하는 곰팡이 배양 창고에서 인간의 언어를 모방하는 곰팡이가 발견되었습니다. 소각 규정을 무시하고 배양할까요? 진화 미래당 pure bio,research 호기심 vs 규정 30 4 -2 목소리가 꽤 좋답니다. AST_002
6 105 1 행정 본부 #d97706 국회 로비 자금 기구의 예산을 늘리기 위해 의원들에게 '도서 구입비' 명목의 비자금을 찔러 넣어줄까요? 세계 재건당 veil budget,audit 타협 vs 청렴 60 0 0 돈 냄새는 귀신도 좋아합니다. -
7 106 1 작전 본부 #c92a2a 이단 종교 집회 괴수를 섬기는 '별빛 교단'이 민간인을 모아 집회를 엽니다. 무장 타격대로 무력 해산시킬까요? 철의 수호당 covenant kill,ops 무력 vs 평화 40 -4 5 이단에게 자비는 없습니다. -
8 107 1 정보 본부 #1c4f82 상수도 기억소거 전투를 목격한 시민이 너무 많습니다. 상수도에 약한 기억소거제를 타서 광역 은폐를 실시할까요? 침묵의 장막당 reshape secret,bio 통제 vs 윤리 30 -5 6 수돗물 맛이 조금 달콤해집니다. -
9 108 1 R&D 본부 #6b21a8 이단 유물 발굴 재개발 구역에서 고대 마법 유적이 발견되었습니다. 군의 폭파 주장을 묵살하고 발굴할까요? 지식 보존당 iron magic,research 지식 vs 파괴 40 3 -3 흙 묻은 역사가 미래를 엽니다. -
10 109 1 행정 본부 #d97706 괴수 가죽 밀거래 창고팀이 죽은 괴수 가죽을 블랙마켓에 팔려다 걸렸습니다. 징계 대신 가죽을 팔아 예산에 보탤까요? 차원 무역당 covenant budget,audit 자본 vs 규정 -80 5 -2 돈 냄새가 피 냄새를 덮습니다. -
11 110 1 작전 본부 #c92a2a 뱀파이어 요원 채용 전향한 흡혈귀를 요원으로 쓰자는 건의입니다. 피를 주기적으로 제공해야 하지만 효율은 좋습니다. 고대 공존당 pure bio,ops 공존 vs 순혈 20 2 -2 피만 제때 주면 우리 편입니다. AST_009
12 111 1 정보 본부 #1c4f82 라디오 전파 납치 심야 라디오 DJ가 우리 조직을 폭로하려 합니다. 납치 세뇌 후 선전 방송 스피커로 재활용할까요? 세계 재건당 veil public,secret 세뇌 vs 침묵 50 4 -3 통제의 꽃은 대중의 열광입니다. -
13 112 1 R&D 본부 #6b21a8 부상 요원 기계화 팔을 잃은 요원이 사이보그 개조 수술을 자원했습니다. 순수 인간주의 원칙을 깨고 수술할까요? 진화 미래당 pure bio,research 진화 vs 순수 40 3 -2 살점보다 강철이 믿음직합니다. -
14 113 1 행정 본부 #d97706 이종족 구호소 건립 차원 균열 난민들을 위해 도심 외곽에 이종족 난민 구호소를 세울까요? 시민 반발이 거셀 겁니다. 고대 공존당 trade pure,budget 공존 vs 배척 60 4 -4 우리는 모두 우주의 미아입니다. -
15 114 1 작전 본부 #c92a2a 예언자 스카웃 10초 뒤를 보는 사기꾼이 고액 연봉을 요구하며 합류를 제안했습니다. 채용할까요? 차원 무역당 covenant magic,ops 실용 vs 원칙 70 5 -2 하한가를 피하는 값싼 비용. AST_007
16 115 1 정보 본부 #1c4f82 기밀문서 유출자 내부 고발자가 비윤리적 실험을 폭로하려다 체포되었습니다. 그를 '조용히 처리'할까요? 침묵의 장막당 reshape secret,kill 은폐 vs 진실 30 -5 8 진실은 무덤 속에 있어야 합니다. AST_013
17 201 2 작전 본부 #c92a2a 베헤모스의 알 회수 불법 경매장에 도시 붕괴급 괴수의 알이 나왔습니다. 파괴하지 않고 연구용으로 회수할까요? 진화 미래당 iron ops,bio 연구 vs 안전 100 8 -5 품속에 넣으니 따뜻하네요. AST_006
18 202 2 R&D 본부 #6b21a8 비유클리드 칩 도입 연산력은 무한하지만 주변 공간을 뒤틀어버리는 외계 칩을 메인 서버에 장착할까요? 세계 재건당 pure research,magic 기술 vs 현실붕괴 60 6 0 1+1=3이 되는 기적. AST_005
19 203 2 행정 본부 #d97706 저주받은 금화 소지자의 수명을 갉아먹지만 무한히 금화를 쏟아내는 유적 항아리를 자금원으로 쓸까요? 차원 무역당 covenant magic,budget 자본 vs 생명 -150 10 -5 돈 냄새가 핏물보다 진하군요. AST_004
20 204 2 작전 본부 #c92a2a 전술핵 타격 12구역에 S급 괴수가 출현했습니다. 수만 명의 희생을 감수하고 소형 전술핵 타격을 승인할까요? 철의 수호당 libra kill,ops 학살 vs 방어실패 120 -15 20 도시는 다시 지으면 됩니다. -
21 205 2 정보 본부 #1c4f82 해킹 그룹 참수 적대 해커들이 1급 기밀을 훔치고 있습니다. 역추적 암살 프로그램을 가동해 뇌를 태워버릴까요? 침묵의 장막당 reshape secret,kill 암살 vs 유출 50 -8 15 모니터 너머에 죽음 배달. -
22 206 2 R&D 본부 #6b21a8 초인 부대 스파르탄 고통을 느끼지 못하는 생체 병기 부대를 창설하기 위해 인간의 존엄성을 포기하시겠습니까? 진화 미래당 pure bio,ops 진화 vs 윤리 90 10 -8 괴물을 잡기 위해 괴물이 됩니다. -
23 207 2 행정 본부 #d97706 외계 물질 매각 창고의 위험한 외계 광물을 군수 기업이 거액에 사겠다고 합니다. 나중 파장은 무시하고 팔까요? 차원 무역당 veil budget,public 자본 vs 은폐 -200 12 -6 위험은 저들에게 전가합시다. -
24 208 2 정보 본부 #1c4f82 숭배자 국회 진출 괴수를 섬기는 유력 인사가 국회에 입성하려 합니다. 선거를 조작하여 무조건 당선을 저지할까요? 철의 수호당 covenant secret,public 개입 vs 중립 60 -5 8 정치는 총칼 없는 전쟁입니다. -
25 209 2 작전 본부 #c92a2a 생체 병기 폭주 피아를 식별하지 못하는 개조 요원이 폭주 중입니다. 목의 자폭 목걸이를 기폭시켜 처형할까요? 순수 인간당 gene kill,bio 처분 vs 관용 30 -6 10 불량품은 폐기해야 합니다. -
26 210 2 R&D 본부 #6b21a8 타임머신 가동 작전 실패를 돌리기 위해 현실 우주 붕괴 리스크(70%)를 안고 회귀 장치를 가동하시겠습니까? 지식 보존당 iron research,magic 시간조작 vs 순응 150 18 -5 시간을 속이면 시간도 우리를 속입니다. -
27 211 2 행정 본부 #d97706 차원문 상업 개방 이종족 상인들에게 통행료를 받고 차원문을 열어주는 관세법을 승인하시겠습니까? 차원 무역당 pure budget,magic 개방 vs 통제 -120 10 -4 돈은 차원을 넘어서도 통용됩니다. -
28 212 2 정보 본부 #1c4f82 언론사 대표 암살 방송사 대표가 내일 아침 기구의 실체를 폭로하려 합니다. 오늘 밤 특수 요원을 보내 암살할까요? 침묵의 장막당 reshape kill,secret 암살 vs 폭로 70 -10 18 기사는 발행되지 않을 때 완벽합니다. -
29 213 2 작전 본부 #c92a2a 지성체 괴수 휴전 언어를 구사하는 괴수 여왕이 학살을 멈추는 조건으로 영토 인정을 요구합니다. 휴전할까요? 고대 공존당 iron ops 평화 vs 자존심 0 5 -12 적과 악수하려면 칼을 놔야 합니다. -
30 214 2 R&D 본부 #6b21a8 복제 요원 양산 감정이 삭제된 복제 인간 요원들을 공장에서 찍어내듯 양산하는 비윤리적 계획을 승인할까요? 진화 미래당 pure bio,ops 복제 vs 윤리 110 12 -6 슬퍼할 가족이 없는 완벽한 소모품. -
31 215 2 행정 본부 #d97706 최후의 벙커 대중을 버리고 수뇌부만 피신할 수 있는 초거대 지하 벙커 건설에 막대한 예산을 쏟아부을까요? 세계 재건당 trade budget 생존 vs 방치 180 8 -4 지하엔 버려진 자들의 비명이 안 들립니다. -
32 301 3 정보 본부 #1c4f82 비밀 결사 공개 세계가 패닉에 빠졌습니다. 음지에서 벗어나 우리가 전 세계를 직접 통치하겠다고 선포할까요? 세계 재건당 veil public,secret 양지화 vs 은폐 100 15 -5 구원자는 그림자에 숨지 않습니다. -
33 302 3 작전 본부 #c92a2a 차원 붕괴 폭탄 적의 차원을 영구 붕괴시키는 블랙홀 폭탄입니다. 여파로 지구도 절반 파괴됩니다. 쏘시겠습니까? 철의 수호당 libra kill,ops 파멸 vs 지속 200 25 -10 불타는 세계 위에서 승리를 외칩니다. AST_014
34 303 3 R&D 본부 #6b21a8 인류 강제 진화 전 인류의 DNA를 변이시키는 가스를 살포해 강제로 진화시킵니다. 부작용 치사율은 50%입니다. 진화 미래당 pure bio 진화 vs 순수 250 30 -15 낡은 허물을 벗어던질 때입니다. -
35 304 3 행정 본부 #d97706 글로벌 금융 해킹 자금이 완전히 고갈되었습니다. 대공황을 감수하고 적대국의 중앙 은행을 해킹해 예산을 증발시킬까요? 차원 무역당 audit budget,secret 자본 vs 경제붕괴 -300 20 -8 세상이 망해도 돈은 씁니다. -
36 305 3 정보 본부 #1c4f82 지구 뇌파 리셋 폭동을 잠재우기 위해 위성에서 펄스를 쏴 전 인류를 기억 상실의 꼭두각시로 만들까요? 침묵의 장막당 reshape secret,public 백지화 vs 혼돈 180 22 -12 무지가 곧 평화요 망각이 구원입니다. -
37 306 3 R&D 본부 #6b21a8 아카식 레코드 접속 우주의 모든 진리가 담긴 서버에 접속합니다. 연구원 90%가 뇌사할 확률을 안고 강행할까요? 지식 보존당 covenant research,magic 진리 vs 파멸 120 18 -10 우주의 비밀 앞에 인간은 나약합니다. -
38 307 3 작전 본부 #c92a2a 군단 맹약 외계 군단이 인류 절반을 노예로 바치는 대가로 침공을 멈추겠답니다. 항복 문서에 서명할까요? 고대 공존당 iron ops 굴복 vs 멸망 80 15 -18 무릎 꿇어서라도 숨을 이어가야 합니다. -
39 308 3 행정 본부 #d97706 유전자 방주 발사 지구를 포기하고 순수 인류 엘리트들만 태운 방주를 우주로 발사해 탈출하시겠습니까? 순수 인간당 gene pure,budget 도피 vs 투쟁 200 25 -8 불순물들은 죽어가는 지구에 둡니다. -
40 309 3 R&D 본부 #6b21a8 차원 융합 폭주 물리법칙을 무시하고 적 차원과 우리 차원을 융합시켜 아예 새로운 우주를 창조할까요? 세계 재건당 trade magic,research 재창조 vs 유지 220 35 -20 헌 우주를 부수고 새 우주를 엽시다. -
41 310 3 작전 본부 #c92a2a 대재앙 방관 다른 대륙에 초거대 괴수가 깨어났습니다. 우리 전력을 보존하기 위해 그들의 멸망을 방관할까요? 침묵의 장막당 iron ops,kill 방관 vs 저지 0 20 -15 지는 싸움은 피하는 게 상책입니 -

View File

@@ -213,6 +213,7 @@
<div class="safe-area-top">PROJECT SS</div> <div class="safe-area-top">PROJECT SS</div>
<div class="reset-btn" onclick="initDB()">↻ RESTART</div> <div class="reset-btn" onclick="initDB()">↻ RESTART</div>
<div class="reset-btn bg-purple-600 text-[10px]" style="top: 60px;" onclick="devAddComboAssets()">★ DEV 콤보 지급</div>
<div id="desk" class="screen desk-bg"> <div id="desk" class="screen desk-bg">
<div id="passive-toast" class="passive-toast"></div> <div id="passive-toast" class="passive-toast"></div>
@@ -240,6 +241,11 @@
<div class="stat-item"><div class="entropy-clock"><div class="clock-face"></div><div class="clock-hand" id="hand-entropy"></div></div><span class="stat-label">종말시계</span></div> <div class="stat-item"><div class="entropy-clock"><div class="clock-face"></div><div class="clock-hand" id="hand-entropy"></div></div><span class="stat-label">종말시계</span></div>
<div class="stat-item" style="width: 35%;" onclick="openModal('faction-modal')"><div class="share-bars" id="bar-share"></div><span class="stat-label">지분율 <i class="fa-solid fa-circle-info text-[8px]"></i></span></div> <div class="stat-item" style="width: 35%;" onclick="openModal('faction-modal')"><div class="share-bars" id="bar-share"></div><span class="stat-label">지분율 <i class="fa-solid fa-circle-info text-[8px]"></i></span></div>
</div> </div>
<div id="engine-hud" class="mt-2 flex justify-between items-center text-[10px] bg-gray-800 p-1 px-3 w-full border border-gray-600 shadow-inner rounded" style="margin-top: 8px;">
<div><i class="fa-solid fa-microchip text-yellow-500"></i> 주력: <span id="hud-main-tag" class="text-white font-bold ml-1">없음</span></div>
<div id="hud-synergy-badge" class="text-gray-500 transition-colors duration-500"><i class="fa-solid fa-bolt"></i> 연쇄 기믹 없음</div>
</div>
</div> </div>
<div class="desk-area"> <div class="desk-area">
@@ -298,6 +304,11 @@
<div id="council-phase-2" class="council-phase"> <div id="council-phase-2" class="council-phase">
<div class="flex-1 w-full"> <div class="flex-1 w-full">
<div id="sabotage-alert" class="hidden mb-2 bg-red-900 border border-red-500 p-2 rounded text-center shadow-[0_0_10px_rgba(239,68,68,0.5)] animate-pulse">
<div class="text-red-200 text-[10px] font-bold">⚠️ 표적 견제 발의</div>
<div class="text-white text-xs font-black mt-1">의회가 국장의 [<span id="sabotage-target-tag" class="text-yellow-400"></span>] 엔진을 맹비난합니다!</div>
</div>
<div class="vote-section border-red-500 border"><div class="vote-title text-red-500"><i class="fa-solid fa-scale-balanced"></i> 입법 전쟁</div> <div class="vote-section border-red-500 border"><div class="vote-title text-red-500"><i class="fa-solid fa-scale-balanced"></i> 입법 전쟁</div>
<div class="bill-card major"><div class="flex-1"><div class="flex justify-between"><span class="text-blue-400 text-[9px] font-bold">여당안 (A)</span><span class="text-[9px]" id="bill-a-faction"></span></div><div class="bill-name" id="bill-a-name"></div><div class="bill-desc text-[9px] italic text-gray-400 mt-1" id="bill-a-flavor"></div></div></div> <div class="bill-card major"><div class="flex-1"><div class="flex justify-between"><span class="text-blue-400 text-[9px] font-bold">여당안 (A)</span><span class="text-[9px]" id="bill-a-faction"></span></div><div class="bill-name" id="bill-a-name"></div><div class="bill-desc text-[9px] italic text-gray-400 mt-1" id="bill-a-flavor"></div></div></div>
<div class="text-center text-[10px] my-1 text-gray-500 font-bold">VS</div> <div class="text-center text-[10px] my-1 text-gray-500 font-bold">VS</div>
@@ -331,19 +342,16 @@
</div> </div>
<script> <script>
// ============================================================================
// 구글 시트 탭 별 독립된 링크 (유저가 제공한 링크 적용)
// ============================================================================
const SHEET_URLS = { const SHEET_URLS = {
factions: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=0&single=true&output=csv", factions: "./data/factions.csv",
agendas: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=529320998&single=true&output=csv", agendas: "./data/agendas.csv",
quests: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=57714287&single=true&output=csv", quests: "./data/quests.csv",
bills: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=1174273004&single=true&output=csv", bills: "./data/bills.csv",
assets: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=640542678&single=true&output=csv", assets: "./data/assets.csv",
scenarios: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=1732769140&single=true&output=csv" scenarios: "./data/scenarios.csv"
}; };
const EFFECT_NAMES = { budget: "예산", black_fund: "비자금", trust: "신임도", entropy: "엔트로피", suspicion: "의심도" }; const EFFECT_NAMES = { budget: "예산", black_fund: "비자금", trust: "신임도", entropy: "엔트로피", suspicion: "의심도", blackFund: "비자금" };
let DB_FACTIONS = []; let DB_AGENDAS = []; let DB_QUESTS = []; let DB_BILLS = []; let DB_ASSETS = []; let DB_SCENARIOS = []; let DB_FACTIONS = []; let DB_AGENDAS = []; let DB_QUESTS = []; let DB_BILLS = []; let DB_ASSETS = []; let DB_SCENARIOS = [];
@@ -362,8 +370,8 @@
function parseCSV(url) { function parseCSV(url) {
return new Promise((resolve) => { return new Promise((resolve) => {
if (!url || url.trim() === "") { resolve(null); return; } if (!url || url.trim() === "") { resolve(null); return; }
Papa.parse(url, {
download: true, const parseOptions = {
header: true, header: true,
skipEmptyLines: true, skipEmptyLines: true,
complete: function(results) { complete: function(results) {
@@ -378,20 +386,34 @@
console.error("CSV 파싱 에러:", err); console.error("CSV 파싱 에러:", err);
resolve(null); resolve(null);
} }
}); };
if (url.startsWith('./') && typeof require !== 'undefined') {
try {
const fs = require('fs');
const path = require('path');
const fileContent = fs.readFileSync(path.join(__dirname, url.replace('./', '')), 'utf8');
Papa.parse(fileContent, parseOptions);
return;
} catch(e) {
console.warn("fs 모듈 로드 실패, 웹 브라우저 fetch로 대체 시도:", e);
}
}
Papa.parse(url, { ...parseOptions, download: true });
}); });
} }
function loadFallbackData() { function loadFallbackData() {
if (DB_FACTIONS.length === 0) DB_FACTIONS = [ if (DB_FACTIONS.length === 0) DB_FACTIONS = [
{ id: 'iron', name: '철의 수호당', color: '#ef4444', share: 30, rival: 'libra', ideology: 0, desc: "괴수 즉결 처형 및 군사력 증강을 주장합니다." }, { id: 'iron', name: '철의 수호당', color: '#ef4444', share: 30, rival: 'libra', ideology: 0, hate_tag: '오컬트', desc: "괴수 즉결 처형 및 군사력 증강을 주장합니다." },
{ id: 'libra', name: '지식 보존당', color: '#3b82f6', share: 20, rival: 'iron', ideology: 1, desc: "괴수 생포 및 연구를 통한 이해를 중시합니다." }, { id: 'libra', name: '지식 보존당', color: '#3b82f6', share: 20, rival: 'iron', ideology: 1, hate_tag: '군사', desc: "괴수 생포 및 연구를 통한 이해를 중시합니다." },
{ id: 'pure', name: '순수 인간당', color: '#10b981', share: 15, rival: 'gene', ideology: 1, desc: "인간 중심주의, 이종족 및 변이를 배척합니다." }, { id: 'pure', name: '순수 인간당', color: '#10b981', share: 15, rival: 'gene', ideology: 1, hate_tag: '변이', desc: "인간 중심주의, 이종족 및 변이를 배척합니다." },
{ id: 'gene', name: '진화 미래당', color: '#8b5cf6', share: 10, rival: 'pure', ideology: 0, desc: "신체 개조 및 초인 양성을 통한 진화를 꿈꿉니다." }, { id: 'gene', name: '진화 미래당', color: '#8b5cf6', share: 10, rival: 'pure', ideology: 0, hate_tag: '순수', desc: "신체 개조 및 초인 양성을 통한 진화를 꿈꿉니다." },
{ id: 'trade', name: '차원 무역당', color: '#f59e0b', share: 10, rival: 'covenant', ideology: 0, desc: "괴수 부산물 판매 및 이계 무역을 지지합니다." }, { id: 'trade', name: '차원 무역당', color: '#f59e0b', share: 10, rival: 'covenant', ideology: 0, hate_tag: '숭배', desc: "괴수 부산물 판매 및 이계 무역을 지지합니다." },
{ id: 'covenant', name: '고대 공존당', color: '#06b6d4', share: 5, rival: 'trade', ideology: 1, desc: "괴수를 숭배하며 평화적 공존을 모색합니다." }, { id: 'covenant', name: '고대 공존당', color: '#06b6d4', share: 5, rival: 'trade', ideology: 1, hate_tag: '자본', desc: "괴수를 숭배하며 평화적 공존을 모색합니다." },
{ id: 'veil', name: '침묵의 장막당', color: '#6b7280', share: 5, rival: 'reshape', ideology: 1, desc: "대중에게 진실을 숨기고 비밀을 유지합니다." }, { id: 'veil', name: '침묵의 장막당', color: '#6b7280', share: 5, rival: 'reshape', ideology: 1, hate_tag: '양지화', desc: "대중에게 진실을 숨기고 비밀을 유지합니다." },
{ id: 'reshape', name: '세계 재건당', color: '#000000', share: 5, rival: 'veil', ideology: 0, desc: "조직의 양지화 및 세계 정부 수립을 목표로 합니다." } { id: 'reshape', name: '세계 재건당', color: '#000000', share: 5, rival: 'veil', ideology: 0, hate_tag: '은폐', desc: "조직의 양지화 및 세계 정부 수립을 목표로 합니다." }
]; ];
if (DB_AGENDAS.length === 0) DB_AGENDAS = [ if (DB_AGENDAS.length === 0) DB_AGENDAS = [
{ id: "AG_01", title: "대토벌의 시대", desc: "작전부 안건 2배 증가 / 전투 예산 소모 +20%" }, { id: "AG_01", title: "대토벌의 시대", desc: "작전부 안건 2배 증가 / 전투 예산 소모 +20%" },
@@ -400,11 +422,16 @@
{ id: "Q_01", title: "작전 승인 2회", target: { type: "approve_count", dept: "작전 본부", val: 2 }, reward_txt: "예산 +$100", penalty_txt: "신임도 -10" } { id: "Q_01", title: "작전 승인 2회", target: { type: "approve_count", dept: "작전 본부", val: 2 }, reward_txt: "예산 +$100", penalty_txt: "신임도 -10" }
]; ];
if (DB_BILLS.length === 0) DB_BILLS = [ if (DB_BILLS.length === 0) DB_BILLS = [
{ id: "B_01", name: "사살 금지법", desc: "사살 관련 안건 승인 금지", tag: "kill", type: "lock_approve", flavor: "생명은 소중합니다." }, { id: "B_01", name: "사살 금지법", desc: "사살 관련 안건 승인 금지", tag: "kill", target_tag: "군사", type: "lock_approve", flavor: "생명은 소중합니다." },
{ id: "B_03", name: "강제 징집령", desc: "작전부 안건 거절 불가", tag: "ops", type: "lock_reject", flavor: "국가가 부릅니다." } { id: "B_03", name: "강제 징집령", desc: "작전부 안건 거절 불가", tag: "ops", target_tag: "지식", type: "lock_reject", flavor: "국가가 부릅니다." },
{ id: "B_04", name: "군축 조약", desc: "모든 [군사] 자산 무효화 및 매 턴 $20 유지비", tag: "none", target_tag: "군사", type: "nullify_tag", nullify_tag: "군사", upkeep_penalty: 20, flavor: "방만 경영을 쇄신하겠습니다." },
{ id: "B_05", name: "자본 환수 특별법", desc: "모든 [자본] 자산 무효화 및 매 턴 $10 유지비", tag: "none", target_tag: "자본", type: "nullify_tag", nullify_tag: "자본", upkeep_penalty: 10, flavor: "자본의 타락을 막습니다." }
]; ];
if (DB_ASSETS.length === 0) DB_ASSETS = [ if (DB_ASSETS.length === 0) DB_ASSETS = [
{ id: "AST_001", name: "유령 커피머신", type: "Relic", desc: "커피 맛은 천상, 밤마다 악몽.", passive_effect: { budget: 5 }, holding_risk: { entropy: 1 }, sale_value: 50, sale_risk: { suspicion: 5 }, flavor: "카페인과 공포." } { id: "AST_001", name: "유령 커피머신", type: "Relic", desc: "커피 맛은 천상, 밤마다 악몽.", passive_effect: { budget: 5 }, holding_risk: { entropy: 1 }, sale_value: 50, sale_risk: { suspicion: 5 }, flavor: "카페인과 공포.", tags: ["군사"], combo_req: 2, combo_effect_key: "trust", combo_effect_val: 2 },
{ id: "AST_002", name: "비밀 해커팀", type: "Agent", desc: "통제 불능일수록 더 많은 비밀을.", passive_effect: null, holding_risk: null, sale_value: 100, flavor: "정보가 곧 돈이다.", tags: ["자본", "정보"], combo_req: 1, trigger: "on_reject", condition: "entropy >= 50", effect_type: "resource", effect_target: "blackFund", effect_val: 2, combo_effect_key: null },
{ id: "AST_003", name: "그림자 펀드", type: "Finance", desc: "검은 돈을 세탁하여 예산으로.", passive_effect: null, holding_risk: null, sale_value: 200, flavor: "돈은 꼬리표가 없습니다.", tags: ["자본"], combo_req: 2, trigger: "on_stat_increase", condition: "blackFund", effect_type: "resource", effect_target: "budget", effect_val: 5 },
{ id: "AST_004", name: "용병 길드", type: "Military", desc: "신임도 대신 비자금 10 소모로 강행 돌파.", passive_effect: null, holding_risk: null, sale_value: 80, flavor: "돈만 주면 법도 부수지.", tags: ["군사", "자본"], combo_req: 1, trigger: "on_override", condition: null, effect_type: "override", effect_target: "blackFund", effect_val: -10 }
]; ];
if (DB_SCENARIOS.length === 0) DB_SCENARIOS = [ if (DB_SCENARIOS.length === 0) DB_SCENARIOS = [
{ id: 101, act: 1, dept: "행정 본부", color: "#d97706", title: "탕비실 믹스커피 횡령", body: "야간조 요원들이 믹스커피를 횡령했습니다.", faction: "지식 보존당", rival: "iron", tags: ["audit"], conflict: "원칙 vs 융통성", cost: 0, yes_e: -1, no_e: +1, flavor: "맥심 골드는 중대 사항입니다.", reward_asset_id: "AST_001" }, { id: 101, act: 1, dept: "행정 본부", color: "#d97706", title: "탕비실 믹스커피 횡령", body: "야간조 요원들이 믹스커피를 횡령했습니다.", faction: "지식 보존당", rival: "iron", tags: ["audit"], conflict: "원칙 vs 융통성", cost: 0, yes_e: -1, no_e: +1, flavor: "맥심 골드는 중대 사항입니다.", reward_asset_id: "AST_001" },
@@ -430,7 +457,7 @@
if (factions && factions.length > 0 && factions[0].id) { if (factions && factions.length > 0 && factions[0].id) {
DB_FACTIONS = factions.map(f => ({ DB_FACTIONS = factions.map(f => ({
...f, share: Number(f.share) || 0, ideology: Number(f.ideology) || 0 ...f, share: Number(f.share) || 0, ideology: Number(f.ideology) || 0, hate_tag: f.hate_tag || ''
})); }));
} }
if (agendas && agendas.length > 0) DB_AGENDAS = agendas; if (agendas && agendas.length > 0) DB_AGENDAS = agendas;
@@ -439,7 +466,7 @@
...q, target: { type: q.target_type, dept: q.target_dept, val: Number(q.target_val) || 0 } ...q, target: { type: q.target_type, dept: q.target_dept, val: Number(q.target_val) || 0 }
})); }));
} }
if (bills && bills.length > 0) DB_BILLS = bills; if (bills && bills.length > 0) DB_BILLS = bills.map(b => ({ ...b, target_tag: b.target_tag || b.tag || '', nullify_tag: b.nullify_tag || null, upkeep_penalty: Number(b.upkeep_penalty) || 0 }));
if (assets && assets.length > 0) { if (assets && assets.length > 0) {
DB_ASSETS = assets.map(a => ({ DB_ASSETS = assets.map(a => ({
id: a.id, name: a.name, type: a.type, desc: a.desc, id: a.id, name: a.name, type: a.type, desc: a.desc,
@@ -447,7 +474,16 @@
holding_risk: a.risk_eff_key && a.risk_eff_key !== '-' ? { [a.risk_eff_key]: Number(a.risk_eff_val) } : null, holding_risk: a.risk_eff_key && a.risk_eff_key !== '-' ? { [a.risk_eff_key]: Number(a.risk_eff_val) } : null,
sale_value: Number(a.sale_value) || 0, sale_value: Number(a.sale_value) || 0,
sale_risk: a.sale_risk_key && a.sale_risk_key !== '-' ? { [a.sale_risk_key]: Number(a.sale_risk_val) } : null, sale_risk: a.sale_risk_key && a.sale_risk_key !== '-' ? { [a.sale_risk_key]: Number(a.sale_risk_val) } : null,
flavor: a.flavor flavor: a.flavor,
tags: a.tags && a.tags !== '-' ? String(a.tags).split(',').map(tag => tag.trim()) : [],
combo_req: Number(a.combo_req) || 0,
combo_effect_key: a.combo_effect_key && a.combo_effect_key !== '-' ? a.combo_effect_key : null,
combo_effect_val: Number(a.combo_effect_val) || 0,
trigger: a.trigger || null,
condition: a.condition || null,
effect_type: a.effect_type || null,
effect_target: a.effect_target || null,
effect_val: Number(a.effect_val) || 0
})); }));
} }
if (scenarios && scenarios.length > 0 && scenarios[0].id) { if (scenarios && scenarios.length > 0 && scenarios[0].id) {
@@ -519,22 +555,138 @@
updateHUD(); updateHUD();
} }
function applyAssetPassives() { function getMainTag() {
let msg = []; let counts = {};
state.assets.forEach(a => { state.assets.forEach(a => {
if(a.passive_effect) { if(a.tags) a.tags.forEach(t => counts[t] = (counts[t] || 0) + 1);
if(a.passive_effect.budget) { state.budget += a.passive_effect.budget; msg.push(`예산+${a.passive_effect.budget}`); } });
if(a.passive_effect.black_fund) { state.blackFund += a.passive_effect.black_fund; msg.push(`비자금+${a.passive_effect.black_fund}`); } let max = 0; let mainTag = null;
if(a.passive_effect.suspicion) { state.suspicion += a.passive_effect.suspicion; msg.push(`의심+${a.passive_effect.suspicion}`); } for(let t in counts) {
if(counts[t] > max) { max = counts[t]; mainTag = t; }
}
return { tag: mainTag, count: max, counts: counts };
}
function getNullifiedTags() {
let tags = [];
state.activeLaws.forEach(law => {
if (law.type === "nullify_tag" && law.nullify_tag) tags.push(law.nullify_tag);
});
return tags;
}
let eventQueue = [];
let isFlushing = false;
function enqueueEvent(triggerType, context) {
eventQueue.push({ triggerType, context });
if(!isFlushing) flushEvents();
}
function flushEvents() {
isFlushing = true;
let triggeredMsgs = [];
let counts = getMainTag().counts;
let nTags = getNullifiedTags();
while(eventQueue.length > 0) {
let ev = eventQueue.shift();
state.assets.forEach(a => {
if (a.tags && a.tags.some(t => nTags.includes(t))) return;
if (a.combo_req > 0 && (!a.tags || !a.tags.some(t => counts[t] >= a.combo_req))) return;
if (a.trigger === ev.triggerType) {
let conditionMet = true;
if (a.condition) {
if (ev.triggerType === 'on_reject' && a.condition === 'entropy >= 50' && state.entropy < 50) conditionMet = false;
else if (ev.triggerType === 'on_stat_increase' && a.condition !== ev.context) conditionMet = false;
}
if (conditionMet) {
if (a.effect_type === 'resource') {
if (a.effect_target === 'blackFund') { state.blackFund += a.effect_val; eventQueue.push({triggerType: 'on_stat_increase', context: 'blackFund'}); triggeredMsgs.push(`[${a.name}] 비자금+${a.effect_val}`); }
if (a.effect_target === 'budget') { state.budget += a.effect_val; eventQueue.push({triggerType: 'on_stat_increase', context: 'budget'}); triggeredMsgs.push(`[${a.name}] 예산+${a.effect_val}`); }
if (a.effect_target === 'trust') { state.trust += a.effect_val; eventQueue.push({triggerType: 'on_stat_increase', context: 'trust'}); triggeredMsgs.push(`[${a.name}] 신임도+${a.effect_val}`); }
}
} }
if(a.holding_risk) {
if(a.holding_risk.entropy) { state.entropy += a.holding_risk.entropy; msg.push(`엔트로피+${a.holding_risk.entropy}`); }
if(a.holding_risk.trust) { state.trust += a.holding_risk.trust; msg.push(`신임도${a.holding_risk.trust}`); }
} }
}); });
}
if (triggeredMsgs.length > 0) {
const toast = document.getElementById('passive-toast');
let uniques = [...new Set(triggeredMsgs)];
toast.innerText = "연쇄: " + uniques.join(', ');
toast.classList.remove('active'); void toast.offsetWidth; toast.classList.add('active');
}
isFlushing = false;
updateHUD();
}
function addStat(key, val) {
if(val === 0) return;
state[key] += val;
if(val > 0) {
enqueueEvent('on_stat_increase', key);
}
}
function applyAssetPassives() {
let msg = [];
let mainTagInfo = getMainTag();
let counts = mainTagInfo.counts;
let nullifiedTags = getNullifiedTags();
state.assets.forEach(a => {
let isNullified = a.tags && a.tags.some(t => nullifiedTags.includes(t));
if (isNullified) {
let penalty = 0;
state.activeLaws.forEach(law => {
if (law.type === "nullify_tag" && a.tags.includes(law.nullify_tag)) {
penalty += law.upkeep_penalty || 0;
}
});
if (penalty > 0) {
state.budget -= penalty;
msg.push(`[제재] ${a.name} 유지비-$${penalty}`);
}
return;
}
if(a.passive_effect) {
if(a.passive_effect.budget) { addStat('budget', a.passive_effect.budget); msg.push(`예산+${a.passive_effect.budget}`); }
if(a.passive_effect.black_fund) { addStat('blackFund', a.passive_effect.black_fund); msg.push(`비자금+${a.passive_effect.black_fund}`); }
if(a.passive_effect.suspicion) { addStat('suspicion', a.passive_effect.suspicion); msg.push(`의심+${a.passive_effect.suspicion}`); }
}
if(a.holding_risk) {
if(a.holding_risk.entropy) { addStat('entropy', a.holding_risk.entropy); msg.push(`엔트로피+${a.holding_risk.entropy}`); }
if(a.holding_risk.trust) { addStat('trust', a.holding_risk.trust); msg.push(`신임도${a.holding_risk.trust}`); }
}
// 시너지 검사 (오리지널 패시브 효과 전용)
if(!a.trigger && a.combo_req > 0 && a.tags && a.tags.some(t => counts[t] >= a.combo_req)) {
if(a.combo_effect_key === 'budget') { addStat('budget', a.combo_effect_val); msg.push(`[시너지]예산+${a.combo_effect_val}`); }
else if(a.combo_effect_key === 'black_fund') { addStat('blackFund', a.combo_effect_val); msg.push(`[시너지]비자금+${a.combo_effect_val}`); }
else if(a.combo_effect_key === 'trust') { addStat('trust', a.combo_effect_val); msg.push(`[시너지]신임도+${a.combo_effect_val}`); }
}
});
// 파벌 반응
if(mainTagInfo.tag && mainTagInfo.count > 0) {
DB_FACTIONS.forEach(f => {
if(f.hate_tag === mainTagInfo.tag && f.share > 0) {
f.share = Math.max(0, f.share - 1);
msg.push(`[적대] ${f.name} 지분 하락`);
}
});
}
if(msg.length > 0) { if(msg.length > 0) {
const toast = document.getElementById('passive-toast'); const toast = document.getElementById('passive-toast');
toast.innerText = "자산 효과: " + msg.join(', '); let uniqueMsg = [...new Set(msg)];
toast.innerText = "자산 효과: " + uniqueMsg.join(', ');
toast.classList.remove('active'); void toast.offsetWidth; toast.classList.add('active'); toast.classList.remove('active'); void toast.offsetWidth; toast.classList.add('active');
} }
} }
@@ -605,44 +757,56 @@
function updateHUD() { function updateHUD() {
document.getElementById('week-num').innerText = state.week; document.getElementById('week-num').innerText = state.week;
document.getElementById('day-num').innerText = state.day; document.getElementById('day-num').innerText = state.day;
if(state.currentAgenda) document.getElementById('agenda-title').innerText = state.currentAgenda.title;
if(state.currentQuest) {
const qTarget = state.currentQuest.target.val;
document.getElementById('quest-status').innerText = `${state.currentQuest.title} (${state.questProgress}/${qTarget})`;
}
document.getElementById('val-total').innerText = `$${state.budget}`; document.getElementById('val-total').innerText = `$${state.budget}`;
document.getElementById('val-black').innerText = `$${state.blackFund}`; document.getElementById('val-black').innerText = `$${state.blackFund}`;
document.getElementById('val-suspicion').innerText = `${state.suspicion}%`; document.getElementById('val-suspicion').innerText = `${state.suspicion}%`;
document.getElementById('bar-trust').style.width = `${state.trust}%`; document.getElementById('bar-trust').style.width = `${state.trust}%`;
document.getElementById('txt-trust').innerText = `${state.trust}%`; document.getElementById('txt-trust').innerText = `${state.trust}%`;
const deg = 270 + (state.entropy / 100) * 90;
document.getElementById('hand-entropy').style.transform = `rotate(${deg}deg)`;
const barContainer = document.getElementById('bar-share'); let cRot = (state.entropy / 100) * 360;
if(barContainer) { document.getElementById('hand-entropy').style.transform = `translateX(-50%) rotate(${cRot}deg)`;
barContainer.innerHTML = '';
const listContainer = document.getElementById('faction-list-detail');
if(listContainer) listContainer.innerHTML = '';
DB_FACTIONS.sort((a,b) => b.share - a.share).forEach(f => { if (state.currentAgenda) {
const div = document.createElement('div'); document.getElementById('agenda-title').innerText = state.currentAgenda.title;
div.className = 'share-segment'; div.style.width = `${f.share}%`; div.style.backgroundColor = f.color; const dept = state.currentAgenda.dept;
barContainer.appendChild(div); document.getElementById('dept-name-display').innerText = `${dept} 예산`;
document.getElementById('val-dept').innerText = `$${state.deptBudgets[dept] || 0}`;
if(listContainer) {
listContainer.innerHTML += `
<div class="bg-gray-800 p-2 rounded mb-2 text-xs">
<div class="flex justify-between font-bold mb-1"><span style="color:${f.color}">${f.name}</span><span>${f.share}%</span></div>
<div class="text-gray-400 text-[10px]">이념: ${f.ideology === 0 ? '급진' : '보수'} | 라이벌: ${DB_FACTIONS.find(r=>r.id===f.rival)?.name || '없음'}</div>
<div class="text-gray-500 mt-1 italic">"${f.desc}"</div>
</div>`;
} }
if (state.currentQuest) {
document.getElementById('quest-status').innerText = `${state.questProgress}/${state.currentQuest.target.val}`;
}
const sb = document.getElementById('bar-share'); sb.innerHTML = "";
DB_FACTIONS.forEach(f => {
if(f.share > 0) sb.innerHTML += `<div class="share-segment" style="width:${f.share}%; background:${f.color};"></div>`;
}); });
}
const top = DB_FACTIONS[0];
const topEl = document.getElementById('top-faction');
if(topEl && top) { topEl.innerText = top.name; topEl.style.color = top.color; }
document.getElementById('asset-count').innerText = state.assets.length; document.getElementById('asset-count').innerText = state.assets.length;
// 엔진 빌딩 (Engine HUD) 실시간 업데이트
if(typeof getMainTag === 'function') {
let mainTagInfo = getMainTag();
let targetTag = mainTagInfo.tag;
document.getElementById('hud-main-tag').innerText = targetTag ? `${targetTag} (${mainTagInfo.count}개)` : '없음';
let hasActiveSynergy = false;
let nTags = typeof getNullifiedTags === 'function' ? getNullifiedTags() : [];
state.assets.forEach(a => {
if (a.tags && a.tags.some(t => nTags.includes(t))) return;
if (a.combo_req > 0 && a.tags && a.tags.some(t => mainTagInfo.counts[t] >= a.combo_req)) hasActiveSynergy = true;
});
let synBadge = document.getElementById('hud-synergy-badge');
if(hasActiveSynergy) {
synBadge.className = "text-yellow-400 font-bold animate-pulse";
synBadge.innerHTML = `<i class="fa-solid fa-bolt"></i> 시너지 가동 중!`;
} else {
synBadge.className = "text-gray-500";
synBadge.innerHTML = `<i class="fa-solid fa-bolt"></i> 연쇄 없음`;
}
}
} }
const card = document.getElementById('current-card'); const card = document.getElementById('current-card');
@@ -652,12 +816,32 @@
document.addEventListener('mousemove', moveDrag); document.addEventListener('touchmove', moveDrag, {passive:false}); document.addEventListener('mousemove', moveDrag); document.addEventListener('touchmove', moveDrag, {passive:false});
document.addEventListener('mouseup', endDrag); document.addEventListener('touchend', endDrag); document.addEventListener('mouseup', endDrag); document.addEventListener('touchend', endDrag);
function getOverrideModifiers() {
let costType = 'trust';
let costVal = -20;
let counts = getMainTag().counts;
let nTags = getNullifiedTags();
state.assets.forEach(a => {
if (a.tags && a.tags.some(t => nTags.includes(t))) return;
if (a.trigger === 'on_override' && (!a.combo_req || (a.tags && a.tags.some(t => counts[t] >= a.combo_req)))) {
costType = a.effect_target;
costVal = a.effect_val;
}
});
return { type: costType, val: costVal };
}
function startDrag(e) { function startDrag(e) {
isDragging = true; startX = (e.type==='touchstart')?e.touches[0].clientX:e.clientX; isDragging = true; startX = (e.type==='touchstart')?e.touches[0].clientX:e.clientX;
if(state.lockState !== 'none') { if(state.lockState !== 'none') {
card.style.transform = "scale(0.98)"; card.style.transform = "scale(0.98)";
pressTimer = setTimeout(() => { pressTimer = setTimeout(() => {
if(Math.abs(currentX) < 10) { if(Math.abs(currentX) < 10) {
let ov = getOverrideModifiers();
if(ov.type === 'blackFund' && state.blackFund + ov.val < 0) {
alert("비자금이 부족하여 돌파할 수 없습니다.");
return;
}
state.lockState = 'none'; state.lockState = 'none';
document.getElementById('lock-overlay').classList.remove('active'); document.getElementById('lock-overlay').classList.remove('active');
card.style.transform = "scale(1.05)"; card.style.transform = "scale(1.05)";
@@ -737,7 +921,14 @@
desc.innerText = "결재 승인"; desc.innerText = "결재 승인";
document.getElementById('p-budget').innerHTML = `<span class="p-down">-$${data.cost || 0}</span>`; document.getElementById('p-budget').innerHTML = `<span class="p-down">-$${data.cost || 0}</span>`;
document.getElementById('p-entropy').innerHTML = eHtml; document.getElementById('p-entropy').innerHTML = eHtml;
document.getElementById('p-trust').innerHTML = state.lockState === 'approve' ? `<span class="p-down">-20 (강행)</span>` : `-`;
let trHtml = `-`;
if (state.lockState === 'approve') {
let ov = getOverrideModifiers();
trHtml = ov.type === 'blackFund' ? `<span class="p-down">비자금 ${ov.val} (강행)</span>` : `<span class="p-down">${ov.val} (강행)</span>`;
}
document.getElementById('p-trust').innerHTML = trHtml;
document.getElementById('p-faction-gain').innerHTML = `<span style="color:${fac.color}">${fac.name} ▲</span>`; document.getElementById('p-faction-gain').innerHTML = `<span style="color:${fac.color}">${fac.name} ▲</span>`;
document.getElementById('p-faction-loss').innerHTML = `<span style="color:${rivalColor}">${rivalName} ▼</span>`; document.getElementById('p-faction-loss').innerHTML = `<span style="color:${rivalColor}">${rivalName} ▼</span>`;
if (data.reward_asset_id && data.reward_asset_id !== '-') { if (data.reward_asset_id && data.reward_asset_id !== '-') {
@@ -749,7 +940,14 @@
desc.innerText = "결재 반려"; desc.innerText = "결재 반려";
document.getElementById('p-budget').innerHTML = `-`; document.getElementById('p-budget').innerHTML = `-`;
document.getElementById('p-entropy').innerHTML = eHtml; document.getElementById('p-entropy').innerHTML = eHtml;
document.getElementById('p-trust').innerHTML = state.lockState === 'reject' ? `<span class="p-down">-20 (거부권)</span>` : `<span class="p-up">+2</span>`;
let trHtml = `<span class="p-up">+2</span>`;
if (state.lockState === 'reject') {
let ov = getOverrideModifiers();
trHtml = ov.type === 'blackFund' ? `<span class="p-down">비자금 ${ov.val} (거부권)</span>` : `<span class="p-down">${ov.val} (거부권)</span>`;
}
document.getElementById('p-trust').innerHTML = trHtml;
document.getElementById('p-faction-gain').innerHTML = ""; document.getElementById('p-faction-gain').innerHTML = "";
document.getElementById('p-faction-loss').innerHTML = `<span style="color:${rivalColor}">${rivalName} ▲ (반사익)</span>`; document.getElementById('p-faction-loss').innerHTML = `<span style="color:${rivalColor}">${rivalName} ▲ (반사익)</span>`;
document.getElementById('p-asset-gain').innerHTML = ""; document.getElementById('p-asset-gain').innerHTML = "";
@@ -762,12 +960,17 @@
const rival = fac ? DB_FACTIONS.find(f => f.id === fac.rival) : null; const rival = fac ? DB_FACTIONS.find(f => f.id === fac.rival) : null;
let animateDir = type === 'approve' ? 500 : -500; let animateDir = type === 'approve' ? 500 : -500;
if ((type === 'approve' && data.lock_type === 'approve') || (type === 'reject' && data.lock_type === 'reject')) { state.trust -= 20; } let isOverride = ((type === 'approve' && data.lock_type === 'approve') || (type === 'reject' && data.lock_type === 'reject'));
if (isOverride) {
let ov = getOverrideModifiers();
if (ov.type === 'blackFund') addStat('blackFund', ov.val);
else addStat('trust', ov.val);
}
if (type === 'approve') { if (type === 'approve') {
state.budget -= (data.cost || 0); addStat('budget', -(data.cost || 0));
if(state.deptBudgets[data.dept]) state.deptBudgets[data.dept] -= (data.cost || 0); if(state.deptBudgets[data.dept]) state.deptBudgets[data.dept] -= (data.cost || 0);
state.entropy += (data.yes_e || 0); addStat('entropy', (data.yes_e || 0));
if(fac) fac.share += 2; if(fac) fac.share += 2;
if(rival) rival.share -= 1; if(rival) rival.share -= 1;
@@ -779,10 +982,14 @@
const tube = document.getElementById('dividend-tube'); const tube = document.getElementById('dividend-tube');
const b = document.createElement('div'); b.className = 'dividend-block'; b.style.backgroundColor = data.color || '#fff'; const b = document.createElement('div'); b.className = 'dividend-block'; b.style.backgroundColor = data.color || '#fff';
tube.appendChild(b); tube.appendChild(b);
enqueueEvent('on_approve', null);
} else { } else {
state.entropy += (data.no_e || 0); addStat('entropy', (data.no_e || 0));
state.trust += 2; addStat('trust', 2);
if(rival) rival.share += 1; if(rival) rival.share += 1;
enqueueEvent('on_reject', null);
} }
card.style.transition = "transform 0.3s ease-in"; card.style.transition = "transform 0.3s ease-in";
@@ -816,12 +1023,29 @@
document.getElementById('council-phase-1').classList.remove('active'); document.getElementById('council-phase-1').classList.remove('active');
document.getElementById('council-phase-2').classList.add('active'); document.getElementById('council-phase-2').classList.add('active');
let mainTagInfo = getMainTag();
let targetTag = mainTagInfo.tag;
let targetBills = targetTag ? DB_BILLS.filter(b => b.target_tag === targetTag) : [];
let b1 = DB_BILLS[0]; let b2 = DB_BILLS[1] || DB_BILLS[0]; let b1 = DB_BILLS[0]; let b2 = DB_BILLS[1] || DB_BILLS[0];
if (DB_BILLS.length > 1) {
// Sabotage UI 연동 로직
const sabotageAlert = document.getElementById('sabotage-alert');
if (targetBills.length > 0) {
b1 = targetBills[0];
let others = DB_BILLS.filter(b => b.id !== b1.id);
b2 = others.length > 0 ? others[Math.floor(Math.random()*others.length)] : b1;
sabotageAlert.classList.remove('hidden');
document.getElementById('sabotage-target-tag').innerText = targetTag;
} else if (DB_BILLS.length > 1) {
let i = Math.floor(Math.random()*DB_BILLS.length); let i = Math.floor(Math.random()*DB_BILLS.length);
let j = Math.floor(Math.random()*DB_BILLS.length); let j = Math.floor(Math.random()*DB_BILLS.length);
while(i===j) j = Math.floor(Math.random()*DB_BILLS.length); while(i===j) j = Math.floor(Math.random()*DB_BILLS.length);
b1 = DB_BILLS[i]; b2 = DB_BILLS[j]; b1 = DB_BILLS[i]; b2 = DB_BILLS[j];
sabotageAlert.classList.add('hidden');
} }
state.council.billA = b1; state.council.billB = b2; state.council.billA = b1; state.council.billB = b2;
@@ -889,10 +1113,35 @@
function addAsset(a) { state.assets.push(a); } function addAsset(a) { state.assets.push(a); }
function renderAssets() { function renderAssets() {
const list = document.getElementById('asset-list'); list.innerHTML = ""; const list = document.getElementById('asset-list'); list.innerHTML = "";
let counts = getMainTag().counts;
let nTags = getNullifiedTags();
state.assets.forEach((a, idx) => { state.assets.forEach((a, idx) => {
let isNullified = a.tags && a.tags.some(t => nTags.includes(t));
let nullifyBadge = isNullified ? `<span class="bg-red-800 text-white px-1 rounded ml-1 text-[8px] font-bold shadow-md">무효화됨</span>` : "";
let pass = ""; let pass = "";
if(a.passive_effect) { const k = Object.keys(a.passive_effect)[0]; pass = `[매턴 ${EFFECT_NAMES[k] || k} ${a.passive_effect[k]}]`; } if(!isNullified && a.passive_effect) { const k = Object.keys(a.passive_effect)[0]; pass = `[매턴 ${EFFECT_NAMES[k] || k} ${a.passive_effect[k]}]`; }
list.innerHTML += `<div class="list-item"><div class="flex-1"><span class="font-bold block">${a.name}</span><span class="text-[9px] text-gray-400">${a.desc} <span class="text-green-400">${pass}</span></span></div><button class="bg-green-700 px-2 py-1 rounded text-xs text-white ml-2 flex-shrink-0" onclick="sellAsset(${idx})">판매 (+$${a.sale_value})</button></div>`;
let syn = "";
if(!isNullified && a.combo_req > 0 && a.tags) {
let active = a.tags.some(t => counts[t] >= a.combo_req);
let synColor = active ? 'text-yellow-400' : 'text-gray-500';
let effectDesc = "";
if(a.trigger) {
let condStr = a.condition ? `조건: ${a.condition}` : '';
effectDesc = `[연쇄] ${a.trigger} ${condStr} -> ${EFFECT_NAMES[a.effect_target] || a.effect_target} ${a.effect_val}`;
} else {
effectDesc = `${EFFECT_NAMES[a.combo_effect_key] || a.combo_effect_key} +${a.combo_effect_val}`;
}
syn = `<br><span class="${synColor}">[시너지: ${a.tags.join(',')} ${a.combo_req}개] ${effectDesc}</span>`;
}
let tagsHtml = (a.tags && a.tags.length > 0) ? `<span class="bg-gray-700 text-gray-300 px-1 rounded ml-1 text-[8px]">${a.tags.join(',')}</span>` : "";
let opacity = isNullified ? 'opacity-50' : '';
list.innerHTML += `<div class="list-item ${opacity}"><div class="flex-1"><span class="font-bold flex items-center">${a.name} ${tagsHtml} ${nullifyBadge}</span><span class="text-[9px] text-gray-400">${a.desc} <span class="text-green-400">${pass}</span>${syn}</span></div><button class="bg-green-700 px-2 py-1 rounded text-xs text-white ml-2 flex-shrink-0" onclick="sellAsset(${idx})">판매 (+$${a.sale_value})</button></div>`;
}); });
if(state.assets.length===0) list.innerHTML = "<div class='text-center text-xs text-gray-500'>없음</div>"; if(state.assets.length===0) list.innerHTML = "<div class='text-center text-xs text-gray-500'>없음</div>";
} }
@@ -918,6 +1167,28 @@
window.closeModal = (id) => document.getElementById(id).classList.remove('active'); window.closeModal = (id) => document.getElementById(id).classList.remove('active');
window.sellAsset = sellAsset; window.sellAsset = sellAsset;
function endGame() {
alert("게임 오버! 파멸했습니다.");
initDB();
}
function devAddComboAssets() {
let a1 = DB_ASSETS.find(a => a.name.includes("해커팀") || a.id === "AST_002");
let a2 = DB_ASSETS.find(a => a.name.includes("그림자 펀드") || a.id === "AST_003");
let a3 = DB_ASSETS.find(a => a.name.includes("용병 길드") || a.id === "AST_004");
let a4 = DB_ASSETS.find(a => a.name.includes("유령 커피머신") || a.id === "AST_001");
if(a1) state.assets.push({...a1});
if(a2) state.assets.push({...a2});
if(a3) state.assets.push({...a3});
if(a4) state.assets.push({...a4});
state.entropy = 60; // 강제 트리거 발동을 위해 60으로 세팅
updateHUD();
alert("DEV 모드: 핵심 연쇄 자산 4장 강제 주입 완료!\n오컬트/비밀해커/그림자펀드/용병길드의 '자본/군사' 엔진 성능을 바로 테스트하세요. (엔트로피 60으로 상승)");
}
// 앱 시작 지점 // 앱 시작 지점
initDB(); initDB();
</script> </script>