|
|
|
|
@@ -213,6 +213,7 @@
|
|
|
|
|
|
|
|
|
|
<div class="safe-area-top">PROJECT SS</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="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" 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 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 class="desk-area">
|
|
|
|
|
@@ -298,6 +304,11 @@
|
|
|
|
|
|
|
|
|
|
<div id="council-phase-2" class="council-phase">
|
|
|
|
|
<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="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>
|
|
|
|
|
@@ -331,19 +342,16 @@
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// 구글 시트 탭 별 독립된 링크 (유저가 제공한 링크 적용)
|
|
|
|
|
// ============================================================================
|
|
|
|
|
const SHEET_URLS = {
|
|
|
|
|
factions: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=0&single=true&output=csv",
|
|
|
|
|
agendas: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=529320998&single=true&output=csv",
|
|
|
|
|
quests: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=57714287&single=true&output=csv",
|
|
|
|
|
bills: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=1174273004&single=true&output=csv",
|
|
|
|
|
assets: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=640542678&single=true&output=csv",
|
|
|
|
|
scenarios: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSSzLgVoqnhadH-1oUvITvjZI8UBmBCkoDma4zumjopFXC2hHcvglgyfVpBLt5lhXnk9z20ZDvZdm5k/pub?gid=1732769140&single=true&output=csv"
|
|
|
|
|
factions: "./data/factions.csv",
|
|
|
|
|
agendas: "./data/agendas.csv",
|
|
|
|
|
quests: "./data/quests.csv",
|
|
|
|
|
bills: "./data/bills.csv",
|
|
|
|
|
assets: "./data/assets.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 = [];
|
|
|
|
|
|
|
|
|
|
@@ -362,8 +370,8 @@
|
|
|
|
|
function parseCSV(url) {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
if (!url || url.trim() === "") { resolve(null); return; }
|
|
|
|
|
Papa.parse(url, {
|
|
|
|
|
download: true,
|
|
|
|
|
|
|
|
|
|
const parseOptions = {
|
|
|
|
|
header: true,
|
|
|
|
|
skipEmptyLines: true,
|
|
|
|
|
complete: function(results) {
|
|
|
|
|
@@ -378,20 +386,34 @@
|
|
|
|
|
console.error("CSV 파싱 에러:", err);
|
|
|
|
|
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() {
|
|
|
|
|
if (DB_FACTIONS.length === 0) DB_FACTIONS = [
|
|
|
|
|
{ id: 'iron', name: '철의 수호당', color: '#ef4444', share: 30, rival: 'libra', ideology: 0, desc: "괴수 즉결 처형 및 군사력 증강을 주장합니다." },
|
|
|
|
|
{ id: 'libra', name: '지식 보존당', color: '#3b82f6', share: 20, rival: 'iron', ideology: 1, desc: "괴수 생포 및 연구를 통한 이해를 중시합니다." },
|
|
|
|
|
{ id: 'pure', name: '순수 인간당', color: '#10b981', share: 15, rival: 'gene', ideology: 1, desc: "인간 중심주의, 이종족 및 변이를 배척합니다." },
|
|
|
|
|
{ id: 'gene', name: '진화 미래당', color: '#8b5cf6', share: 10, rival: 'pure', ideology: 0, desc: "신체 개조 및 초인 양성을 통한 진화를 꿈꿉니다." },
|
|
|
|
|
{ id: 'trade', name: '차원 무역당', color: '#f59e0b', share: 10, rival: 'covenant', ideology: 0, desc: "괴수 부산물 판매 및 이계 무역을 지지합니다." },
|
|
|
|
|
{ id: 'covenant', name: '고대 공존당', color: '#06b6d4', share: 5, rival: 'trade', ideology: 1, desc: "괴수를 숭배하며 평화적 공존을 모색합니다." },
|
|
|
|
|
{ id: 'veil', name: '침묵의 장막당', color: '#6b7280', share: 5, rival: 'reshape', ideology: 1, desc: "대중에게 진실을 숨기고 비밀을 유지합니다." },
|
|
|
|
|
{ id: 'reshape', name: '세계 재건당', color: '#000000', share: 5, rival: 'veil', 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, hate_tag: '군사', 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, hate_tag: '순수', 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, hate_tag: '자본', 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, hate_tag: '은폐', desc: "조직의 양지화 및 세계 정부 수립을 목표로 합니다." }
|
|
|
|
|
];
|
|
|
|
|
if (DB_AGENDAS.length === 0) DB_AGENDAS = [
|
|
|
|
|
{ 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" }
|
|
|
|
|
];
|
|
|
|
|
if (DB_BILLS.length === 0) DB_BILLS = [
|
|
|
|
|
{ id: "B_01", name: "사살 금지법", desc: "사살 관련 안건 승인 금지", tag: "kill", type: "lock_approve", flavor: "생명은 소중합니다." },
|
|
|
|
|
{ id: "B_03", name: "강제 징집령", desc: "작전부 안건 거절 불가", tag: "ops", type: "lock_reject", flavor: "국가가 부릅니다." }
|
|
|
|
|
{ id: "B_01", name: "사살 금지법", desc: "사살 관련 안건 승인 금지", tag: "kill", target_tag: "군사", type: "lock_approve", 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 = [
|
|
|
|
|
{ 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 = [
|
|
|
|
|
{ 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) {
|
|
|
|
|
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;
|
|
|
|
|
@@ -439,7 +466,7 @@
|
|
|
|
|
...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) {
|
|
|
|
|
DB_ASSETS = assets.map(a => ({
|
|
|
|
|
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,
|
|
|
|
|
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,
|
|
|
|
|
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) {
|
|
|
|
|
@@ -519,22 +555,138 @@
|
|
|
|
|
updateHUD();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getMainTag() {
|
|
|
|
|
let counts = {};
|
|
|
|
|
state.assets.forEach(a => {
|
|
|
|
|
if(a.tags) a.tags.forEach(t => counts[t] = (counts[t] || 0) + 1);
|
|
|
|
|
});
|
|
|
|
|
let max = 0; let mainTag = null;
|
|
|
|
|
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 (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) { 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}`); }
|
|
|
|
|
if(a.passive_effect.suspicion) { state.suspicion += a.passive_effect.suspicion; msg.push(`의심+${a.passive_effect.suspicion}`); }
|
|
|
|
|
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) { 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(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) {
|
|
|
|
|
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');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -605,44 +757,56 @@
|
|
|
|
|
function updateHUD() {
|
|
|
|
|
document.getElementById('week-num').innerText = state.week;
|
|
|
|
|
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-black').innerText = `$${state.blackFund}`;
|
|
|
|
|
document.getElementById('val-suspicion').innerText = `${state.suspicion}%`;
|
|
|
|
|
|
|
|
|
|
document.getElementById('bar-trust').style.width = `${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');
|
|
|
|
|
if(barContainer) {
|
|
|
|
|
barContainer.innerHTML = '';
|
|
|
|
|
const listContainer = document.getElementById('faction-list-detail');
|
|
|
|
|
if(listContainer) listContainer.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
DB_FACTIONS.sort((a,b) => b.share - a.share).forEach(f => {
|
|
|
|
|
const div = document.createElement('div');
|
|
|
|
|
div.className = 'share-segment'; div.style.width = `${f.share}%`; div.style.backgroundColor = f.color;
|
|
|
|
|
barContainer.appendChild(div);
|
|
|
|
|
|
|
|
|
|
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>`;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let cRot = (state.entropy / 100) * 360;
|
|
|
|
|
document.getElementById('hand-entropy').style.transform = `translateX(-50%) rotate(${cRot}deg)`;
|
|
|
|
|
|
|
|
|
|
if (state.currentAgenda) {
|
|
|
|
|
document.getElementById('agenda-title').innerText = state.currentAgenda.title;
|
|
|
|
|
const dept = state.currentAgenda.dept;
|
|
|
|
|
document.getElementById('dept-name-display').innerText = `${dept} 예산`;
|
|
|
|
|
document.getElementById('val-dept').innerText = `$${state.deptBudgets[dept] || 0}`;
|
|
|
|
|
}
|
|
|
|
|
const top = DB_FACTIONS[0];
|
|
|
|
|
const topEl = document.getElementById('top-faction');
|
|
|
|
|
if(topEl && top) { topEl.innerText = top.name; topEl.style.color = top.color; }
|
|
|
|
|
|
|
|
|
|
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>`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
@@ -652,12 +816,32 @@
|
|
|
|
|
document.addEventListener('mousemove', moveDrag); document.addEventListener('touchmove', moveDrag, {passive:false});
|
|
|
|
|
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) {
|
|
|
|
|
isDragging = true; startX = (e.type==='touchstart')?e.touches[0].clientX:e.clientX;
|
|
|
|
|
if(state.lockState !== 'none') {
|
|
|
|
|
card.style.transform = "scale(0.98)";
|
|
|
|
|
pressTimer = setTimeout(() => {
|
|
|
|
|
if(Math.abs(currentX) < 10) {
|
|
|
|
|
let ov = getOverrideModifiers();
|
|
|
|
|
if(ov.type === 'blackFund' && state.blackFund + ov.val < 0) {
|
|
|
|
|
alert("비자금이 부족하여 돌파할 수 없습니다.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
state.lockState = 'none';
|
|
|
|
|
document.getElementById('lock-overlay').classList.remove('active');
|
|
|
|
|
card.style.transform = "scale(1.05)";
|
|
|
|
|
@@ -737,7 +921,14 @@
|
|
|
|
|
desc.innerText = "결재 승인";
|
|
|
|
|
document.getElementById('p-budget').innerHTML = `<span class="p-down">-$${data.cost || 0}</span>`;
|
|
|
|
|
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-loss').innerHTML = `<span style="color:${rivalColor}">${rivalName} ▼</span>`;
|
|
|
|
|
if (data.reward_asset_id && data.reward_asset_id !== '-') {
|
|
|
|
|
@@ -749,7 +940,14 @@
|
|
|
|
|
desc.innerText = "결재 반려";
|
|
|
|
|
document.getElementById('p-budget').innerHTML = `-`;
|
|
|
|
|
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-loss').innerHTML = `<span style="color:${rivalColor}">${rivalName} ▲ (반사익)</span>`;
|
|
|
|
|
document.getElementById('p-asset-gain').innerHTML = "";
|
|
|
|
|
@@ -762,12 +960,17 @@
|
|
|
|
|
const rival = fac ? DB_FACTIONS.find(f => f.id === fac.rival) : null;
|
|
|
|
|
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') {
|
|
|
|
|
state.budget -= (data.cost || 0);
|
|
|
|
|
addStat('budget', -(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(rival) rival.share -= 1;
|
|
|
|
|
|
|
|
|
|
@@ -779,10 +982,14 @@
|
|
|
|
|
const tube = document.getElementById('dividend-tube');
|
|
|
|
|
const b = document.createElement('div'); b.className = 'dividend-block'; b.style.backgroundColor = data.color || '#fff';
|
|
|
|
|
tube.appendChild(b);
|
|
|
|
|
|
|
|
|
|
enqueueEvent('on_approve', null);
|
|
|
|
|
} else {
|
|
|
|
|
state.entropy += (data.no_e || 0);
|
|
|
|
|
state.trust += 2;
|
|
|
|
|
addStat('entropy', (data.no_e || 0));
|
|
|
|
|
addStat('trust', 2);
|
|
|
|
|
if(rival) rival.share += 1;
|
|
|
|
|
|
|
|
|
|
enqueueEvent('on_reject', null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
card.style.transition = "transform 0.3s ease-in";
|
|
|
|
|
@@ -816,12 +1023,29 @@
|
|
|
|
|
document.getElementById('council-phase-1').classList.remove('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];
|
|
|
|
|
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 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];
|
|
|
|
|
|
|
|
|
|
sabotageAlert.classList.add('hidden');
|
|
|
|
|
}
|
|
|
|
|
state.council.billA = b1; state.council.billB = b2;
|
|
|
|
|
|
|
|
|
|
@@ -889,10 +1113,35 @@
|
|
|
|
|
function addAsset(a) { state.assets.push(a); }
|
|
|
|
|
function renderAssets() {
|
|
|
|
|
const list = document.getElementById('asset-list'); list.innerHTML = "";
|
|
|
|
|
let counts = getMainTag().counts;
|
|
|
|
|
let nTags = getNullifiedTags();
|
|
|
|
|
|
|
|
|
|
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 = "";
|
|
|
|
|
if(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>`;
|
|
|
|
|
if(!isNullified && a.passive_effect) { const k = Object.keys(a.passive_effect)[0]; pass = `[매턴 ${EFFECT_NAMES[k] || k} ${a.passive_effect[k]}]`; }
|
|
|
|
|
|
|
|
|
|
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>";
|
|
|
|
|
}
|
|
|
|
|
@@ -918,6 +1167,28 @@
|
|
|
|
|
window.closeModal = (id) => document.getElementById(id).classList.remove('active');
|
|
|
|
|
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();
|
|
|
|
|
</script>
|
|
|
|
|
|