피드백 수정 사항 적용 (모두 반영된 것은 아님)
▶주간 의회 명칭 변경: 주간 의회 --> 정기 의회
▶라운드 당 결재 건수는 다름 : 라운드가 증가할때마다 결재 건수가 줄어들도록 하여 아쉬움 발생 - 결재 건수가 0이 되면 게임 오버 (검토를 해보고 줄어드는게 나을지 늘어나는게 나을지 검토 해줘)
▶라운드 구성 개념의 디테일 부족: 기획서에 플로우차트(다이어그램)를 추가 ('결재 단계'와 '주간 의회 단계'의 흐름이 플레이어와 개발자 모두에게 명확하도록)
▶확인 결과 아래 데이터 없음. 데이터 추가 필요 (추가한 데이터가 시스템에 맞게 작동하도록 구현 해줘)
- 자산 데이터
- 오염 카드 데이터
- 내러티브 데이터
▶토큰 마이너스 상태 처리: 방향 B (패널티 및 파산 - 블랙코미디에 어울림), 마이너스를 허용하되 가혹한 대가를 치르게 하기
파산 문책 시스템 기획 및 구현 필요 (아래는 간략 시스템 작성 - 블랙 코미디가 구현되면 좋겠음)
- 마이너스 상태에서 결재 1건 처리 할 때 마다, 파산 카운터 증가
- 정기 의회에서 파산 문책 : 파산 카운터에 따라 문책의 강도가 달라 짐
- 파산 문책으로 경고를 받았는데도 개선(마이너스를 0 이상으로 복구)하지 못하면 다음 정기 의회에서 게임 오버
▶글이 너무 길어 읽기 힘든 문제:
- 게임 플레이 필수 글 (직관성 최우선): 카드 상단에 상황 키워드를 배치하고, 그 아래에 질문을 배치해 무엇을 요구 하는지 확실하게 알려 줌 (예: 상황 키워드 = 요원들의 시위 발생, 질문 = 시위를 어떻게 하시겠습니까?) 그리고 좌우 스와이프 시 등장하는 보상 표시에서 left_text, right_text의 텍스트 크기를 키우고 그 아래의 보상 오브젝트(예: [예산 -2] , [지지도 +10] )는 텍스트보다는 기호와 숫자를 활용하여 1초 만에 파악할 수 있도록 함.
- 서사에 필요한 글 (메인 텍스트): '게임 플레이 필수 글'에서 추가적인 스토리 정보를 원한다면 봐야 하는 글로 카드 데이터에서 text컬럼의 내용을 표시 합니다. 게임 플레이 필수 글보다는 폰트가 작아야 하고, 너무 길지 않게 핵심 서사 위주로 설명 합니다. (예를 들면, 상황 키워드가 요원들의 시위 발생인데, 왜 시위가 발생했는지에 대한 서사를 알려 주면 됩니다)
- 블랙 코미디/플레이버 텍스트 (선택적 정보): 공간을 분리합니다.(공간 구분이 확실해야 합니다.) 카드 하단에 글자를 작게 하고, 폰트를 다르게 하여 구분되도록 합니다.
This commit is contained in:
@@ -1,39 +1,64 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import './CouncilScreen.css';
|
||||
|
||||
export default function CouncilScreen({ params, setParams, tokens, setTokens, tags, setTags, onResolve }) {
|
||||
export default function CouncilScreen({ params, setParams, tokens, setTokens, tags, setTags, bankruptcyCount, setBankruptcyCount, bankruptcyWarning, onResolve }) {
|
||||
const [members, setMembers] = useState([]);
|
||||
const [agenda, setAgenda] = useState('');
|
||||
const [impeachment, setImpeachment] = useState(false);
|
||||
const [actionTaken, setActionTaken] = useState(false);
|
||||
const [result, setResult] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize 13 members (8 board, 5 global)
|
||||
// Their vote depends on risk parameter and randomness
|
||||
const newMembers = Array.from({ length: 13 }).map((_, i) => {
|
||||
const type = i < 8 ? 'board' : 'global';
|
||||
// High risk = more likely to vote against
|
||||
const rand = Math.random() * 100;
|
||||
let vote = '미정';
|
||||
if (rand < params.risk * 20) {
|
||||
vote = '반대';
|
||||
} else if (rand > 50) {
|
||||
vote = '찬성';
|
||||
}
|
||||
return { id: i, type, vote };
|
||||
});
|
||||
setMembers(newMembers);
|
||||
const [isBankruptcyWarning, setIsBankruptcyWarning] = useState(false);
|
||||
const [bankruptPenalty, setBankruptPenalty] = useState('');
|
||||
|
||||
const againstCount = newMembers.filter(m => m.vote === '반대').length;
|
||||
if (againstCount >= 9) {
|
||||
setAgenda('정치적 탄핵안 상정');
|
||||
setImpeachment(true);
|
||||
useEffect(() => {
|
||||
const isNegative = tokens.budget < 0 || tokens.personnel < 0 || tokens.info < 0;
|
||||
|
||||
if (isNegative) {
|
||||
if (bankruptcyWarning) {
|
||||
setAgenda('재무 건전성 악화 및 횡령 배임 혐의');
|
||||
setResult('당신은 파산했고 자금 횡령으로 체포되었습니다. 감옥의 식사는 다행히 무료입니다.');
|
||||
return;
|
||||
} else {
|
||||
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}회: 의회에서 강력한 경고가 나왔습니다. 다음 의회까지 복구하지 못하면 해임됩니다.`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const randAgenda = Math.random() > 0.5 ? '이득 규약 법안 상정' : '표적 규약 법안 상정';
|
||||
setAgenda(randAgenda);
|
||||
// Normal Agenda
|
||||
const newMembers = Array.from({ length: 13 }).map((_, i) => {
|
||||
const type = i < 8 ? 'board' : 'global';
|
||||
const rand = Math.random() * 100;
|
||||
let vote = '미정';
|
||||
if (rand < params.risk * 20) {
|
||||
vote = '반대';
|
||||
} else if (rand > 50) {
|
||||
vote = '찬성';
|
||||
}
|
||||
return { id: i, type, vote };
|
||||
});
|
||||
setMembers(newMembers);
|
||||
|
||||
const againstCount = newMembers.filter(m => m.vote === '반대').length;
|
||||
if (againstCount >= 9) {
|
||||
setAgenda('정치적 탄핵안 상정');
|
||||
setImpeachment(true);
|
||||
} else {
|
||||
const randAgenda = Math.random() > 0.5 ? '이득 규약 법안 상정' : '표적 규약 법안 상정';
|
||||
setAgenda(randAgenda);
|
||||
}
|
||||
}
|
||||
}, [params.risk]);
|
||||
}, [params.risk, tokens, bankruptcyWarning, bankruptcyCount]);
|
||||
|
||||
const handleAction = (type) => {
|
||||
if (actionTaken) return;
|
||||
@@ -77,10 +102,20 @@ export default function CouncilScreen({ params, setParams, tokens, setTokens, ta
|
||||
};
|
||||
|
||||
const finalize = () => {
|
||||
if (result && result.includes('파산')) {
|
||||
onResolve({ bankrupted: true });
|
||||
return;
|
||||
}
|
||||
if (result) {
|
||||
onResolve({ impeachment: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBankruptcyWarning) {
|
||||
// Acknowledged warning
|
||||
onResolve({ impeachment: false, bankruptcyWarning: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const againstCount = members.filter(m => m.vote === '반대').length;
|
||||
if (impeachment) {
|
||||
@@ -98,14 +133,15 @@ export default function CouncilScreen({ params, setParams, tokens, setTokens, ta
|
||||
setTags(prev => ({ ...prev, wealth: Math.max(0, prev.wealth - 1) }));
|
||||
}
|
||||
}
|
||||
onResolve({ impeachment: false });
|
||||
// If tokens are positive, clear bankruptcy warning
|
||||
onResolve({ impeachment: false, bankruptcyWarning: false });
|
||||
}
|
||||
};
|
||||
|
||||
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">
|
||||
@@ -115,22 +151,32 @@ export default function CouncilScreen({ params, setParams, tokens, setTokens, ta
|
||||
{impeachment && <div className="warning-blink">[🚨 정치적 탄핵안 상정!]</div>}
|
||||
</div>
|
||||
|
||||
<div className="members-grid">
|
||||
{members.map(m => (
|
||||
<div key={m.id} className={`member-icon ${m.type}`}>
|
||||
<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}`}>
|
||||
<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
|
||||
|
||||
Reference in New Issue
Block a user