feat(admin-configurator): add web configurator, visual level editor, audit logs and session management

This commit is contained in:
Antigravity committed 2026-09-22 09:25:01 +00:00
1 parent e820d1354d
commit d8be8f4366
13 files changed
+11204 -292

No files matched your search

+209
View File
@@ -0,0 +1,209 @@
/**
* PvZ Dynamic Game Configuration & Balance System (PvZConfig)
* Fetches active balance multipliers from /api/config with fallback defaults and offline localStorage cache.
*/
window.PvZConfig = (function () {
const DEFAULT_CONFIG = {
preset: 'Classic 2009',
economy: {
sunMultiplier: 1.0,
startingSun: 50,
coinDropRate: 1.0,
sunDropRate: 1.0,
plantCostMultiplier: 1.0,
upgradeCostMultiplier: 1.0,
plantPrices: {
sunflower: 0,
peashooter: 0,
wallnut: 0,
potatomine: 150,
snowpea: 350,
repeater: 500,
chomper: 450,
cherrybomb: 400,
jalapeno: 450,
sunshroom: 250,
fumeshroom: 300,
lilypad: 200,
gravebuster: 250,
flowerpot: 200,
cabbagepult: 400,
melonpult: 750
},
plantSunCosts: {
sunflower: 50,
peashooter: 100,
wallnut: 50,
potatomine: 25,
snowpea: 175,
repeater: 200,
chomper: 150,
cherrybomb: 150,
jalapeno: 125,
sunshroom: 25,
fumeshroom: 75,
lilypad: 25,
gravebuster: 75,
flowerpot: 25,
cabbagepult: 100,
melonpult: 300
}
},
zombies: {
hpMultiplier: 1.0,
speedMultiplier: 1.0,
damageMultiplier: 1.0,
waveDensityMultiplier: 1.0,
types: {
normal: { hp: 200, speed: 22, damage: 30 },
conehead: { hp: 300, speed: 22, damage: 30 },
buckethead: { hp: 600, speed: 22, damage: 30 },
newspaper: { hp: 300, speed: 18, damage: 30 },
screendoor: { hp: 650, speed: 20, damage: 30 },
football: { hp: 600, speed: 42, damage: 35 },
dancing: { hp: 350, speed: 25, damage: 30 },
gargantuar: { hp: 3000, speed: 14, damage: 300 },
zomboss: { hp: 10000, speed: 0, damage: 500 },
polevaulter: { hp: 180, speed: 44, damage: 30 },
duckytube: { hp: 120, speed: 24, damage: 30 },
flag: { hp: 200, speed: 28, damage: 30 }
}
},
plants: {
damageMultiplier: 1.0,
hpMultiplier: 1.0,
cooldownMultiplier: 1.0,
sunProduceIntervalMultiplier: 1.0
},
zenGarden: {
dropRateMultiplier: 1.0,
growthRateMultiplier: 1.0,
fertilizerMultiplier: 1.0,
waterIntervalMinutes: 30
},
events: {
doubleGold: false,
fastCooldowns: false,
infiniteSun: false,
turboZombies: false,
activeEventName: 'none'
}
};
let activeConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
// Try loading cached config from localStorage
try {
const cached = localStorage.getItem('pvz_game_config');
if (cached) {
const parsed = JSON.parse(cached);
if (parsed && typeof parsed === 'object') {
activeConfig = Object.assign({}, DEFAULT_CONFIG, parsed);
}
}
} catch (e) {
// ignore
}
async function fetchConfig() {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 2500);
const res = await fetch('/api/config', { signal: controller.signal });
clearTimeout(timeoutId);
if (res.ok) {
const data = await res.json();
if (data && typeof data === 'object') {
activeConfig = Object.assign({}, DEFAULT_CONFIG, data);
try {
localStorage.setItem('pvz_game_config', JSON.stringify(activeConfig));
} catch (e) {}
}
}
} catch (err) {
console.warn('[PvZConfig] Using cached/default balance config:', err.message);
}
return activeConfig;
}
// Auto-fetch on script load
if (typeof window !== 'undefined') {
fetchConfig();
}
return {
fetchConfig,
getConfig: () => activeConfig,
getPreset: () => activeConfig.preset || 'Classic 2009',
// Economy
getEconomy: () => activeConfig.economy || DEFAULT_CONFIG.economy,
getStartingSun: () => {
const eco = activeConfig.economy || {};
const base = typeof eco.startingSun === 'number' ? eco.startingSun : 50;
return activeConfig.events?.infiniteSun ? Math.max(base, 250) : base;
},
getSunMultiplier: () => {
const eco = activeConfig.economy || {};
const mult = typeof eco.sunMultiplier === 'number' ? eco.sunMultiplier : 1.0;
return activeConfig.events?.infiniteSun ? mult * 1.5 : mult;
},
getCoinDropRate: () => {
const eco = activeConfig.economy || {};
const mult = typeof eco.coinDropRate === 'number' ? eco.coinDropRate : 1.0;
return activeConfig.events?.doubleGold ? mult * 2.0 : mult;
},
getSunDropRate: () => activeConfig.economy?.sunDropRate ?? 1.0,
getPlantShopPrice: (plantId) => {
const eco = activeConfig.economy || {};
const prices = eco.plantPrices || {};
const base = prices[plantId] ?? (plantId === 'sunflower' || plantId === 'peashooter' || plantId === 'wallnut' ? 0 : 250);
const mult = eco.plantCostMultiplier ?? 1.0;
return Math.round(base * mult);
},
getPlantSunCost: (plantId, defaultCost = 50) => {
const eco = activeConfig.economy || {};
const sunCosts = eco.plantSunCosts || {};
return sunCosts[plantId] !== undefined ? sunCosts[plantId] : defaultCost;
},
// Zombies & Combat
getZombieGlobal: () => activeConfig.zombies || DEFAULT_CONFIG.zombies,
getZombieHpMult: () => activeConfig.zombies?.hpMultiplier ?? 1.0,
getZombieSpeedMult: () => {
const mult = activeConfig.zombies?.speedMultiplier ?? 1.0;
return activeConfig.events?.turboZombies ? mult * 1.3 : mult;
},
getZombieDamageMult: () => activeConfig.zombies?.damageMultiplier ?? 1.0,
getWaveDensityMult: () => activeConfig.zombies?.waveDensityMultiplier ?? 1.0,
getZombieTypeConfig: (type) => {
const types = activeConfig.zombies?.types || {};
return types[type] || DEFAULT_CONFIG.zombies.types[type] || { hp: 200, speed: 22, damage: 30 };
},
// Plants
getPlantGlobal: () => activeConfig.plants || DEFAULT_CONFIG.plants,
getPlantDamageMult: () => activeConfig.plants?.damageMultiplier ?? 1.0,
getPlantHpMult: () => activeConfig.plants?.hpMultiplier ?? 1.0,
getPlantCooldownMult: () => {
const mult = activeConfig.plants?.cooldownMultiplier ?? 1.0;
return activeConfig.events?.fastCooldowns ? mult * 0.4 : mult;
},
getSunIntervalMult: () => activeConfig.plants?.sunProduceIntervalMultiplier ?? 1.0,
// Zen Garden
getZenGarden: () => activeConfig.zenGarden || DEFAULT_CONFIG.zenGarden,
getZenDropRateMult: () => {
const mult = activeConfig.zenGarden?.dropRateMultiplier ?? 1.0;
return activeConfig.events?.doubleGold ? mult * 2.0 : mult;
},
getZenGrowthRateMult: () => activeConfig.zenGarden?.growthRateMultiplier ?? 1.0,
getZenFertilizerMult: () => activeConfig.zenGarden?.fertilizerMultiplier ?? 1.0,
getZenWaterIntervalMinutes: () => activeConfig.zenGarden?.waterIntervalMinutes ?? 30,
// Events
getEvents: () => activeConfig.events || DEFAULT_CONFIG.events
};
})();