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

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+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
};
})();