Merge branch 'main' of https://git.mint-grape.duckdns.org/kyoung5/ProjectSS
This commit is contained in:
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user