feat: initial commit for TheFarmer project

This commit is contained in:
Karriis
2026-02-18 13:52:06 +08:00
commit 8ceb5fa9db
420 changed files with 61918 additions and 0 deletions

View File

@@ -0,0 +1,474 @@
/**
* 基于 tools/seed-shop-merged-export.json 计算经验收益率
*
* 规则:
* 1) 每次收获经验 = exp新版已去除铲地+1经验
* 2) 种植速度:
* - 不施肥2 秒种 18 块地 => 9 块/秒
* - 普通肥2 秒种 12 块地 => 6 块/秒
* 3) 普通肥:直接减少一个生长阶段(按 Plant.json 的 grow_phases 取首个非0阶段时长
*
* 用法:
* node tools/calc-exp-yield.js
* node tools/calc-exp-yield.js --lands 18 --level 27
* node tools/calc-exp-yield.js --input tools/seed-shop-merged-export.json
*
* 运行时调用:
* const { getPlantingRecommendation } = require('../tools/calc-exp-yield');
* const rec = getPlantingRecommendation(27, 18);
*/
const fs = require('fs');
const path = require('path');
const DEFAULT_INPUT = path.join(__dirname, 'seed-shop-merged-export.json');
const PLANT_CONFIG_PATH = path.join(__dirname, '..', 'gameConfig', 'Plant.json');
const DEFAULT_OUT_JSON = path.join(__dirname, 'exp-yield-result.json');
const DEFAULT_OUT_CSV = path.join(__dirname, 'exp-yield-result.csv');
const DEFAULT_OUT_TXT = path.join(__dirname, 'exp-yield-summary.txt');
const NO_FERT_PLANTS_PER_2_SEC = 18;
const NORMAL_FERT_PLANTS_PER_2_SEC = 12;
const NO_FERT_PLANT_SPEED_PER_SEC = NO_FERT_PLANTS_PER_2_SEC / 2; // 9 块/秒
const NORMAL_FERT_PLANT_SPEED_PER_SEC = NORMAL_FERT_PLANTS_PER_2_SEC / 2; // 6 块/秒
function toNum(v, fallback = 0) {
const n = Number(v);
return Number.isFinite(n) ? n : fallback;
}
function parseArgs(argv) {
const opts = {
input: DEFAULT_INPUT,
outJson: DEFAULT_OUT_JSON,
outCsv: DEFAULT_OUT_CSV,
outTxt: DEFAULT_OUT_TXT,
lands: 18,
level: null,
top: 20,
};
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (a === '--input' && argv[i + 1]) opts.input = argv[++i];
else if (a === '--out-json' && argv[i + 1]) opts.outJson = argv[++i];
else if (a === '--out-csv' && argv[i + 1]) opts.outCsv = argv[++i];
else if (a === '--out-txt' && argv[i + 1]) opts.outTxt = argv[++i];
else if (a === '--lands' && argv[i + 1]) opts.lands = Math.max(1, Math.floor(toNum(argv[++i], 18)));
else if (a === '--level' && argv[i + 1]) opts.level = Math.max(1, Math.floor(toNum(argv[++i], 1)));
else if (a === '--top' && argv[i + 1]) opts.top = Math.max(1, Math.floor(toNum(argv[++i], 20)));
else if (a === '--help' || a === '-h') {
printHelp();
process.exit(0);
}
}
return opts;
}
function printHelp() {
console.log('Usage: node tools/calc-exp-yield.js [options]');
console.log('');
console.log('Options:');
console.log(' --input <path> 输入 JSON 文件路径');
console.log(' --lands <n> 地块数(默认 18');
console.log(' --level <n> 指定账号等级,输出该等级可用最优作物');
console.log(' --top <n> 摘要 Top 数量(默认 20');
console.log(' --out-json <path> 输出 JSON 路径');
console.log(' --out-csv <path> 输出 CSV 路径');
console.log(' --out-txt <path> 输出 TXT 路径');
}
function readSeeds(inputPath) {
const text = fs.readFileSync(inputPath, 'utf8');
const data = JSON.parse(text);
if (Array.isArray(data)) return data;
if (data && Array.isArray(data.rows)) return data.rows;
if (data && Array.isArray(data.seeds)) return data.seeds;
throw new Error('无法识别输入数据格式,需要数组或 rows/seeds 字段');
}
function parseGrowPhases(growPhases) {
if (!growPhases || typeof growPhases !== 'string') return [];
return growPhases
.split(';')
.map(x => x.trim())
.filter(Boolean)
.map(seg => {
const parts = seg.split(':');
return parts.length >= 2 ? toNum(parts[1], 0) : 0;
})
.filter(sec => sec > 0);
}
function loadSeedPhaseReduceMap() {
const text = fs.readFileSync(PLANT_CONFIG_PATH, 'utf8');
const rows = JSON.parse(text);
if (!Array.isArray(rows)) {
throw new Error(`Plant 配置格式异常: ${PLANT_CONFIG_PATH}`);
}
const map = new Map();
for (const p of rows) {
const seedId = toNum(p.seed_id, 0);
if (seedId <= 0 || map.has(seedId)) continue;
const phases = parseGrowPhases(p.grow_phases);
if (phases.length === 0) continue;
map.set(seedId, phases[0]); // 普通肥减少一个阶段:以首个阶段时长为准
}
return map;
}
function calcEffectiveGrowTime(growSec, seedId, seedPhaseReduceMap) {
const reduce = toNum(seedPhaseReduceMap.get(seedId), 0);
if (reduce <= 0) return growSec;
return Math.max(1, growSec - reduce);
}
function formatSec(sec) {
const s = Math.max(0, Math.round(sec));
if (s < 60) return `${s}s`;
const m = Math.floor(s / 60);
const r = s % 60;
if (m < 60) return r > 0 ? `${m}m${r}s` : `${m}m`;
const h = Math.floor(m / 60);
const mm = m % 60;
return r > 0 ? `${h}h${mm}m${r}s` : `${h}h${mm}m`;
}
function csvCell(v) {
const s = v == null ? '' : String(v);
if (s.includes(',') || s.includes('"') || s.includes('\n')) {
return `"${s.replace(/"/g, '""')}"`;
}
return s;
}
function buildRows(rawSeeds, lands, seedPhaseReduceMap) {
const plantSecondsNoFert = lands / NO_FERT_PLANT_SPEED_PER_SEC;
const plantSecondsNormalFert = lands / NORMAL_FERT_PLANT_SPEED_PER_SEC;
const rows = [];
let skipped = 0;
let missingPhaseReduceCount = 0;
for (const s of rawSeeds) {
const seedId = toNum(s.seedId || s.seed_id);
const name = s.name || `seed_${seedId}`;
const requiredLevel = toNum(s.requiredLevel || s.required_level || 1, 1);
const price = toNum(s.price, 0);
const expHarvest = toNum(s.exp, 0);
const growTimeSec = toNum(s.growTimeSec || s.growTime || s.grow_time || 0, 0);
if (seedId <= 0 || growTimeSec <= 0) {
skipped++;
continue;
}
const expPerCycle = expHarvest;
const reduceSec = toNum(seedPhaseReduceMap.get(seedId), 0);
if (reduceSec <= 0) missingPhaseReduceCount++;
const growTimeNormalFert = calcEffectiveGrowTime(growTimeSec, seedId, seedPhaseReduceMap);
// 整个农场一轮 = 生长时间 + 本轮全部地块种植耗时
const cycleSecNoFert = growTimeSec + plantSecondsNoFert;
const cycleSecNormalFert = growTimeNormalFert + plantSecondsNormalFert;
const farmExpPerHourNoFert = (lands * expPerCycle / cycleSecNoFert) * 3600;
const farmExpPerHourNormalFert = (lands * expPerCycle / cycleSecNormalFert) * 3600;
const gainPercent = farmExpPerHourNoFert > 0
? ((farmExpPerHourNormalFert - farmExpPerHourNoFert) / farmExpPerHourNoFert) * 100
: 0;
const expPerGoldSeed = price > 0 ? expPerCycle / price : 0;
rows.push({
seedId,
goodsId: toNum(s.goodsId || s.goods_id),
plantId: toNum(s.plantId || s.plant_id),
name,
requiredLevel,
unlocked: !!s.unlocked,
price,
expHarvest,
expPerCycle,
growTimeSec,
growTimeStr: s.growTimeStr || formatSec(growTimeSec),
normalFertReduceSec: reduceSec,
growTimeNormalFert,
growTimeNormalFertStr: formatSec(growTimeNormalFert),
cycleSecNoFert,
cycleSecNormalFert,
farmExpPerHourNoFert,
farmExpPerHourNormalFert,
farmExpPerDayNoFert: farmExpPerHourNoFert * 24,
farmExpPerDayNormalFert: farmExpPerHourNormalFert * 24,
gainPercent,
expPerGoldSeed,
fruitId: toNum(s?.fruit?.id || s.fruitId),
fruitCount: toNum(s?.fruit?.count || s.fruitCount),
});
}
return { rows, skipped, plantSecondsNoFert, plantSecondsNormalFert, missingPhaseReduceCount };
}
function pickTop(rows, key, topN) {
return [...rows]
.sort((a, b) => b[key] - a[key])
.slice(0, topN);
}
function buildBestByLevel(rows) {
const maxLevel = rows.reduce((m, r) => Math.max(m, r.requiredLevel), 1);
const result = [];
for (let lv = 1; lv <= maxLevel; lv++) {
// 按用户指定等级做理论可种分析,不受商店 unlocked 状态影响
const available = rows.filter(r => r.requiredLevel <= lv);
if (available.length === 0) continue;
const bestNo = pickTop(available, 'farmExpPerHourNoFert', 1)[0];
const bestFert = pickTop(available, 'farmExpPerHourNormalFert', 1)[0];
result.push({
level: lv,
bestNoFert: {
seedId: bestNo.seedId,
name: bestNo.name,
expPerHour: Number(bestNo.farmExpPerHourNoFert.toFixed(2)),
},
bestNormalFert: {
seedId: bestFert.seedId,
name: bestFert.name,
expPerHour: Number(bestFert.farmExpPerHourNormalFert.toFixed(2)),
},
});
}
return result;
}
function writeJson(outPath, payload) {
fs.writeFileSync(outPath, JSON.stringify(payload, null, 2), 'utf8');
}
function writeCsv(outPath, rows) {
const headers = [
'seedId',
'name',
'requiredLevel',
'price',
'expHarvest',
'expPerCycle',
'growTimeSec',
'growTimeNormalFert',
'cycleSecNoFert',
'cycleSecNormalFert',
'farmExpPerHourNoFert',
'farmExpPerHourNormalFert',
'farmExpPerDayNoFert',
'farmExpPerDayNormalFert',
'gainPercent',
'expPerGoldSeed',
];
const lines = [headers.join(',')];
for (const r of rows) {
lines.push(headers.map(h => csvCell(r[h])).join(','));
}
fs.writeFileSync(outPath, `${lines.join('\n')}\n`, 'utf8');
}
function writeSummaryTxt(outPath, opts, meta, topNo, topFert, levelInfo) {
const lines = [];
lines.push('经验收益率分析结果');
lines.push('');
lines.push(`数据源: ${meta.input}`);
lines.push(`导出时间: ${new Date().toISOString()}`);
lines.push(`地块数: ${opts.lands}`);
lines.push(`种植速度(不施肥): ${NO_FERT_PLANTS_PER_2_SEC}块/${2}s (${NO_FERT_PLANT_SPEED_PER_SEC}块/s)`);
lines.push(`种植速度(普通肥): ${NORMAL_FERT_PLANTS_PER_2_SEC}块/${2}s (${NORMAL_FERT_PLANT_SPEED_PER_SEC}块/s)`);
lines.push(`整场种植耗时(不施肥): ${formatSec(meta.plantSecondsNoFert)}`);
lines.push(`整场种植耗时(普通肥): ${formatSec(meta.plantSecondsNormalFert)}`);
lines.push(`普通肥规则: 直接减少一个生长阶段(按 Plant.json 的首个阶段时长)`);
lines.push(`缺少阶段配置的种子数: ${meta.missingPhaseReduceCount}`);
lines.push('');
lines.push(`Top ${topNo.length}(不施肥,按每小时经验)`);
lines.push('排名 | 名称 | Lv需 | 生长 | 单轮经验 | 每小时经验');
topNo.forEach((r, i) => {
lines.push(
`${String(i + 1).padStart(2)} | ${r.name} | ${r.requiredLevel} | ${r.growTimeStr} | ${r.expPerCycle} | ${r.farmExpPerHourNoFert.toFixed(2)}`
);
});
lines.push('');
lines.push(`Top ${topFert.length}(普通肥,按每小时经验)`);
lines.push('排名 | 名称 | Lv需 | 肥后生长 | 单轮经验 | 每小时经验 | 提升');
topFert.forEach((r, i) => {
lines.push(
`${String(i + 1).padStart(2)} | ${r.name} | ${r.requiredLevel} | ${r.growTimeNormalFertStr} | ${r.expPerCycle} | ${r.farmExpPerHourNormalFert.toFixed(2)} | ${r.gainPercent.toFixed(2)}%`
);
});
lines.push('');
if (levelInfo) {
lines.push(`当前等级 Lv${levelInfo.level} 推荐`);
lines.push(`不施肥: ${levelInfo.bestNoFert.name}(seed=${levelInfo.bestNoFert.seedId}) -> ${levelInfo.bestNoFert.expPerHour.toFixed(2)} exp/h`);
lines.push(`普通肥: ${levelInfo.bestNormalFert.name}(seed=${levelInfo.bestNormalFert.seedId}) -> ${levelInfo.bestNormalFert.expPerHour.toFixed(2)} exp/h`);
lines.push('');
}
fs.writeFileSync(outPath, `${lines.join('\n')}\n`, 'utf8');
}
function analyzeExpYield(opts = {}) {
const lands = Math.max(1, Math.floor(toNum(opts.lands, 18)));
const level = opts.level == null ? null : Math.max(1, Math.floor(toNum(opts.level, 1)));
const top = Math.max(1, Math.floor(toNum(opts.top, 20)));
const input = opts.input || DEFAULT_INPUT;
const inputAbs = path.resolve(input);
const rawSeeds = readSeeds(inputAbs);
const seedPhaseReduceMap = loadSeedPhaseReduceMap();
const { rows, skipped, plantSecondsNoFert, plantSecondsNormalFert, missingPhaseReduceCount } = buildRows(rawSeeds, lands, seedPhaseReduceMap);
if (rows.length === 0) {
throw new Error('没有可计算的种子数据(请检查输入文件)');
}
const topNo = pickTop(rows, 'farmExpPerHourNoFert', top);
const topFert = pickTop(rows, 'farmExpPerHourNormalFert', top);
const bestByLevel = buildBestByLevel(rows);
let currentLevel = null;
if (level != null) {
currentLevel = bestByLevel.find(x => x.level === level) || null;
}
return {
generatedAt: new Date().toISOString(),
input: inputAbs,
config: {
lands,
plantSpeedPerSecNoFert: NO_FERT_PLANT_SPEED_PER_SEC,
plantSpeedPerSecNormalFert: NORMAL_FERT_PLANT_SPEED_PER_SEC,
plantSecondsNoFert,
plantSecondsNormalFert,
fertilizer: {
mode: 'minus_one_phase',
},
rule: {
expPerCycle: 'expHarvest',
},
},
stats: {
rawCount: rawSeeds.length,
calculatedCount: rows.length,
skippedCount: skipped,
missingPhaseReduceCount,
},
topNoFert: topNo.map(r => ({
seedId: r.seedId,
name: r.name,
requiredLevel: r.requiredLevel,
expPerHour: Number(r.farmExpPerHourNoFert.toFixed(4)),
})),
topNormalFert: topFert.map(r => ({
seedId: r.seedId,
name: r.name,
requiredLevel: r.requiredLevel,
expPerHour: Number(r.farmExpPerHourNormalFert.toFixed(4)),
gainPercent: Number(r.gainPercent.toFixed(4)),
})),
bestByLevel,
currentLevel,
rows,
};
}
function getPlantingRecommendation(level, lands, opts = {}) {
const safeLevel = Math.max(1, Math.floor(toNum(level, 1)));
const payload = analyzeExpYield({
input: opts.input || DEFAULT_INPUT,
lands: lands == null ? 18 : lands,
top: opts.top || 20,
level: safeLevel,
});
const availableRows = payload.rows.filter(r => r.requiredLevel <= safeLevel);
const bestNoFertRow = pickTop(availableRows, 'farmExpPerHourNoFert', 1)[0] || null;
const bestNormalFertRow = pickTop(availableRows, 'farmExpPerHourNormalFert', 1)[0] || null;
return {
level: safeLevel,
lands: payload.config.lands,
input: payload.input,
bestNoFert: bestNoFertRow ? {
seedId: bestNoFertRow.seedId,
name: bestNoFertRow.name,
requiredLevel: bestNoFertRow.requiredLevel,
expPerHour: Number(bestNoFertRow.farmExpPerHourNoFert.toFixed(4)),
} : null,
bestNormalFert: bestNormalFertRow ? {
seedId: bestNormalFertRow.seedId,
name: bestNormalFertRow.name,
requiredLevel: bestNormalFertRow.requiredLevel,
expPerHour: Number(bestNormalFertRow.farmExpPerHourNormalFert.toFixed(4)),
} : null,
candidatesNoFert: pickTop(availableRows, 'farmExpPerHourNoFert', opts.top || 20).map(r => ({
seedId: r.seedId,
name: r.name,
requiredLevel: r.requiredLevel,
expPerHour: Number(r.farmExpPerHourNoFert.toFixed(4)),
})),
candidatesNormalFert: pickTop(availableRows, 'farmExpPerHourNormalFert', opts.top || 20).map(r => ({
seedId: r.seedId,
name: r.name,
requiredLevel: r.requiredLevel,
expPerHour: Number(r.farmExpPerHourNormalFert.toFixed(4)),
gainPercent: Number(r.gainPercent.toFixed(4)),
})),
};
}
function main() {
const opts = parseArgs(process.argv.slice(2));
const payload = analyzeExpYield(opts);
const rows = payload.rows;
const topNo = pickTop(rows, 'farmExpPerHourNoFert', opts.top);
const topFert = pickTop(rows, 'farmExpPerHourNormalFert', opts.top);
const currentLevel = payload.currentLevel;
writeJson(path.resolve(opts.outJson), payload);
writeCsv(path.resolve(opts.outCsv), rows);
writeSummaryTxt(
path.resolve(opts.outTxt),
opts,
{
input: payload.input,
plantSecondsNoFert: payload.config.plantSecondsNoFert,
plantSecondsNormalFert: payload.config.plantSecondsNormalFert,
missingPhaseReduceCount: payload.stats.missingPhaseReduceCount,
},
topNo,
topFert,
currentLevel
);
console.log(`[收益率] 计算完成,共 ${rows.length} 条(跳过 ${payload.stats.skippedCount} 条)`);
console.log(`[收益率] JSON: ${path.resolve(opts.outJson)}`);
console.log(`[收益率] CSV : ${path.resolve(opts.outCsv)}`);
console.log(`[收益率] TXT : ${path.resolve(opts.outTxt)}`);
if (currentLevel) {
console.log(`[收益率] Lv${opts.level} 最优(不施肥): ${currentLevel.bestNoFert.name} ${currentLevel.bestNoFert.expPerHour} exp/h`);
console.log(`[收益率] Lv${opts.level} 最优(普通肥): ${currentLevel.bestNormalFert.name} ${currentLevel.bestNormalFert.expPerHour} exp/h`);
}
}
module.exports = {
analyzeExpYield,
getPlantingRecommendation,
DEFAULT_INPUT,
};
if (require.main === module) {
try {
main();
} catch (e) {
console.error(`[收益率] 失败: ${e.message}`);
process.exit(1);
}
}

127
server/tools/crop_list.md Normal file
View File

@@ -0,0 +1,127 @@
# 农作物列表 (共 123 种)
| ID | 名称 | 等级 | 季数 | 产量 | 生长(小时) | 阶段详情 |
|---|---|---|---|---|---|---|
| 1020001 | 草莓 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;开花:5760;成熟:0; |
| 1020002 | 白萝卜 | 1 | 1 | 5 | 0.0h | 种子:30;发芽:30;成熟:0; |
| 1020003 | 胡萝卜 | 1 | 1 | 10 | 0.0h | 种子:30;发芽:30;小叶子:30;大叶子:30;成熟:0; |
| 1020004 | 玉米 | 1 | 1 | 40 | 1.3h | 种子:960;发芽:960;小叶子:960;大叶子:960;开花:960;成熟:0; |
| 1020005 | 土豆 | 1 | 1 | 60 | 2.0h | 种子:1440;发芽:1440;小叶子:1440;大叶子:1440;初熟:1440;成熟:0; |
| 1020006 | 茄子 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;开花:5760;成熟:0; |
| 1020007 | 番茄 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;开花:5760;成熟:0; |
| 1020008 | 豌豆 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;初熟:5760;成熟:0; |
| 1020009 | 辣椒 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;初熟:5760;成熟:0; |
| 1020010 | 南瓜 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;初熟:5760;成熟:0; |
| 1020011 | 苹果 | 1 | 1 | 200 | 12.0h | 种子:8640;发芽:8640;小叶子:8640;大叶子:8640;开花:8640;成熟:0; |
| 1020013 | 葡萄 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;开花:5760;成熟:0; |
| 1020014 | 西瓜 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;开花:5760;成熟:0; |
| 1020015 | 香蕉 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;小叶子:17280;大叶子:17280;开花:17280;成熟:0; |
| 1020016 | 菠萝蜜 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;初熟:3600;成熟:0; |
| 1020018 | 桃子 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;开花:5760;小叶子:5760;大叶子:5760;成熟:0; |
| 1020019 | 橙子 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;小叶子:17280;大叶子:17280;开花:17280;成熟:0; |
| 1020022 | 鳄梨 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;成熟:0; |
| 1020023 | 石榴 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;开花:5760;成熟:0; |
| 1020026 | 柚子 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;小叶子:17280;大叶子:17280;开花:17280;成熟:0; |
| 1020027 | 菠萝 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;开花:7200;成熟:0; |
| 1020029 | 椰子 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;初熟:3600;成熟:0; |
| 1020031 | 葫芦 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;初熟:21600;成熟:0; |
| 1020033 | 火龙果 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;初熟:21600;成熟:0; |
| 1020034 | 樱桃 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;开花:7200;成熟:0; |
| 1020035 | 荔枝 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;开花:21600;成熟:0; |
| 1020036 | 箬竹 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;初熟:10800;成熟:0; |
| 1020037 | 莲藕 | 1 | 1 | 200 | 12.0h | 种子:8640;发芽:8640;小叶子:8640;大叶子:8640;开花:8640;成熟:0; |
| 1020038 | 木瓜 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;开花:7200;成熟:0; |
| 1020039 | 杨桃 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;开花:3600;成熟:0; |
| 1020041 | 红玫瑰 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;小叶子:17280;大叶子:17280;初熟:17280;成熟:0; |
| 1020042 | 柠檬 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;初熟:21600;成熟:0; |
| 1020043 | 无花果 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;开花:21600;成熟:0; |
| 1020044 | 丝瓜 | 1 | 1 | 200 | 12.0h | 种子:8640;长枝:8640;开花:8640;小叶子:8640;大叶子:8640;结果:0; |
| 1020045 | 猕猴桃 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;开花:3600;成熟:0; |
| 1020047 | 甘蔗 | 1 | 1 | 200 | 12.0h | 种子:8640;发芽:8640;幼苗:8640;分叶:8640;伸长:8640;成熟:0; |
| 1020048 | 杨梅 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;开花:7200;成熟:0; |
| 1020049 | 花生 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;初熟:7200;成熟:0; |
| 1020050 | 蘑菇 | 1 | 2 | 200 | 4.0h | 种子:3600;发芽:3600;大叶子:3600;初熟:3600;成熟:0; |
| 1020051 | 红枣 | 1 | 1 | 200 | 12.0h | 种子:8640;发芽:8640;小叶子:8640;大叶子:8640;开花:8640;成熟:0; |
| 1020052 | 金针菇 | 1 | 2 | 200 | 12.0h | 种子:7200;发菌:7200;出菇:7200;幼菇:10800;初熟:10800;成熟:0; |
| 1020053 | 桂圆 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;成熟:0; |
| 1020054 | 梨 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;初熟:7200;成熟:0; |
| 1020055 | 枇杷 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;初熟:3600;成熟:0; |
| 1020056 | 哈密瓜 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;开花:7200;成熟:0; |
| 1020057 | 芒果 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;初熟:3600;成熟:0; |
| 1020058 | 榴莲 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;成熟:0; |
| 1020059 | 大白菜 | 1 | 1 | 20 | 0.1h | 种子:60;发芽:60;幼苗:60;成株:60;卷心:60;成熟:0; |
| 1020060 | 水稻 | 1 | 1 | 30 | 0.7h | 种子:480;幼苗:480;秧苗:480;幼穗:480;开花:480;成熟:0; |
| 1020061 | 小麦 | 1 | 1 | 40 | 1.0h | 种子:720;发芽:720;小叶子:720;大叶子:720;幼穗:720;成熟:0; |
| 1020062 | 四叶草 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;小叶子:17280;大叶子:17280;初熟:17280;成熟:0; |
| 1020063 | 苦瓜 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;成熟:0; |
| 1020064 | 大葱 | 1 | 1 | 30 | 0.3h | 种子:300;发芽:300;小叶子:300;大叶子:300;成熟:0; |
| 1020065 | 大蒜 | 1 | 1 | 20 | 0.2h | 种子:120;发芽:120;幼苗:120;伸长:120;初熟:120;成熟:0; |
| 1020066 | 鲜姜 | 1 | 1 | 60 | 1.7h | 种子:1500;发芽:1500;小叶子:1500;大叶子:1500;成熟:0; |
| 1020067 | 香瓜 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;开花:3600;成熟:0; |
| 1020068 | 冬瓜 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;开花:3600;成熟:0; |
| 1020070 | 黄豆 | 1 | 1 | 200 | 12.0h | 种子:8640;发芽:8640;小叶子:8640;大叶子:8640;初熟:8640;成熟:0; |
| 1020071 | 小白菜 | 1 | 1 | 80 | 2.5h | 种子:2250;发芽:2250;小叶子:2250;大叶子:2250;成熟:0; |
| 1020072 | 榛子 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;开花:17280;小叶子:17280;大叶子:17280;成熟:0; |
| 1020073 | 菠菜 | 1 | 1 | 200 | 4.0h | 种子:2880;发芽:2880;小叶子:2880;大叶子:2880;初熟:2880;成熟:0; |
| 1020074 | 金桔 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;初熟:21600;成熟:0; |
| 1020075 | 桑葚 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;成熟:0; |
| 1020076 | 山竹 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;开花:21600;成熟:0; |
| 1020077 | 蓝莓 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;初熟:7200;成熟:0; |
| 1020078 | 杏子 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;初熟:10800;成熟:0; |
| 1020079 | 番石榴 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;开花:21600;成熟:0; |
| 1020080 | 月柿 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;开花:21600;成熟:0; |
| 1020083 | 红毛丹 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;初熟:3600;成熟:0; |
| 1020084 | 芭蕉 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;结果:0; |
| 1020085 | 番荔枝 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;开花:3600;结果:0; |
| 1020086 | 橄榄 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;成熟:0; |
| 1020087 | 百香果 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;开花:3600;结果:0; |
| 1020088 | 灯笼果 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;结果:0; |
| 1020089 | 芦荟 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;开花:3600;成熟:0; |
| 1020090 | 薄荷 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;成熟:0; |
| 1020091 | 山楂 | 1 | 1 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:14400;开花:14400;初熟:14400;成熟:0; |
| 1020095 | 栗子 | 1 | 1 | 200 | 12.0h | 种子:8640;发芽:8640;小叶子:8640;大叶子:8640;开花:8640;成熟:0; |
| 1020096 | 生菜 | 1 | 1 | 80 | 3.0h | 种子:2160;发芽:2160;小叶子:2160;大叶子:2160;初熟:2160;成熟:0; |
| 1020097 | 黄瓜 | 1 | 1 | 200 | 12.0h | 种子:8640;发芽:8640;小叶子:8640;大叶子:8640;开花:8640;成熟:0; |
| 1020098 | 花菜 | 1 | 1 | 200 | 12.0h | 种子:8640;发芽:8640;幼苗:8640;卷心:8640;初熟:8640;成熟:0; |
| 1020099 | 油菜 | 1 | 1 | 200 | 4.0h | 种子:3600;发芽:3600;小叶子:3600;大叶子:3600;成熟:0; |
| 1020100 | 竹笋 | 1 | 1 | 200 | 4.0h | 种子:2880;发芽:2880;幼苗:2880;伸长:2880;初熟:2880;成熟:0; |
| 1020103 | 天香百合 | 1 | 1 | 200 | 4.0h | 种子:2880;发芽:2880;小叶子:2880;大叶子:2880;初熟:2880;成熟:0; |
| 1020104 | 非洲菊 | 1 | 1 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:2400;花蕾:2400;盛开:2400;成熟:0; |
| 1020105 | 小雏菊 | 1 | 1 | 200 | 4.0h | 种子:2880;发芽:2880;小叶子:2880;大叶子:2880;初熟:2880;成熟:0; |
| 1020110 | 满天星 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;花蕾:5760;盛开:0; |
| 1020116 | 曼陀罗华 | 1 | 2 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:3600;花蕾:3600;盛开:0; |
| 1020120 | 蒲公英 | 1 | 1 | 200 | 24.0h | 种子:17280;小叶子:17280;大叶子:17280;花蕾:17280;盛开:17280;成熟:0; |
| 1020126 | 曼珠沙华 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;初熟:7200;成熟:0; |
| 1020128 | 茉莉花 | 1 | 1 | 200 | 4.0h | 种子:2880;发芽:2880;小叶子:2880;大叶子:2880;花蕾:2880;盛开:0; |
| 1020135 | 火绒草 | 1 | 1 | 200 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;花蕾:5760;盛开:0; |
| 1020141 | 花香根鸢尾 | 1 | 1 | 200 | 12.0h | 种子:8640;发芽:8640;小叶子:8640;大叶子:8640;花蕾:8640;盛开:0; |
| 1020142 | 虞美人 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;小叶子:17280;大叶子:17280;花蕾:17280;盛开:0; |
| 1020143 | 含羞草 | 1 | 1 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:7200;花蕾:7200;盛开:7200;成熟:0; |
| 1020145 | 向日葵 | 1 | 1 | 200 | 4.0h | 种子:2400;发芽:2400;小叶子:2400;大叶子:2400;开花:2400;初熟:2400;成熟:0; |
| 1020147 | 牵牛花 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;小叶子:17280;大叶子:17280;花蕾:17280;盛开:0; |
| 1020161 | 秋菊(黄色) | 1 | 1 | 200 | 4.0h | 种子:2880;发芽:2880;小叶子:2880;大叶子:2880;花蕾:2880;盛开:0; |
| 1020162 | 秋菊(红色) | 1 | 1 | 200 | 4.0h | 种子:2880;发芽:2880;小叶子:2880;大叶子:2880;花蕾:2880;盛开:0; |
| 1020201 | 天山雪莲 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;幼株:21600;成熟:0; |
| 1020202 | 金边灵芝 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;芝蕾:4800;幼芝:7200;初熟:7200;成熟:0; |
| 1020204 | 人参 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;初熟:7200;成熟:0; |
| 1020218 | 瓶子树 | 1 | 2 | 200 | 4.0h | 种子:2400;长枝:2400;小叶子:2400;大叶子:3600;初熟:3600;成树:0; |
| 1020220 | 猪笼草 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;初熟:10800;成熟:0; |
| 1020221 | 天堂鸟 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;花蕾:21600;盛开:0; |
| 1020222 | 豹皮花 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;花蕾:7200;盛开:0; |
| 1020225 | 宝华玉兰 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;花蕾:7200;盛开:0; |
| 1020226 | 依米花 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;花蕾:21600;盛开:0; |
| 1020227 | 大王花 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;幼蕾:4800;含苞:7200;初放:7200;盛开:0; |
| 1020228 | 人参果 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;开花:21600;成熟:0; |
| 1020229 | 何首乌 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;初熟:21600;成熟:0; |
| 1020235 | 金花茶 | 1 | 2 | 200 | 8.0h | 种子:4800;发芽:4800;小叶子:4800;大叶子:7200;花蕾:7200;盛开:0; |
| 1020242 | 似血杜鹃 | 1 | 2 | 200 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;花蕾:21600;盛开:0; |
| 1020259 | 银莲花 | 1 | 1 | 200 | 4.0h | 种子:2880;发芽:2880;小叶:2880;大叶:2880;花蕾:2880;开花:0; |
| 1020305 | 韭菜 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;小叶子:17280;大叶子:17280;初熟:17280;成熟:0; |
| 1020306 | 芹菜 | 1 | 1 | 200 | 24.0h | 种子:17280;发芽:17280;小叶子:17280;大叶子:17280;初熟:17280;成熟:0; |
| 1020308 | 核桃 | 1 | 1 | 200 | 12.0h | 种子:10800;发芽:10800;小叶子:10800;大叶子:10800;成熟:0; |
| 1020396 | 迎春花 | 1 | 1 | 200 | 4.0h | 种子:2880;幼芽:2880;小叶:2880;大叶:2880;花蕾:2880;开花:0; |
| 1020413 | 李子 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;开花:10800;成熟:0; |
| 1020442 | 睡莲 | 1 | 2 | 200 | 12.0h | 种子:7200;发芽:7200;小叶子:7200;大叶子:10800;幼株:10800;成熟:0; |
| 1021542 | 新春红包 | 1 | 1 | 20 | 8.0h | 种子:5760;发芽:5760;小叶子:5760;大叶子:5760;初熟:5760;成熟:0; |
| 2020002 | 白萝卜 | 1 | 1 | 5 | 0.0h | 种子:1;发芽:1;成熟:0; |
| 2029998 | 哈哈南瓜 | 1 | 1 | 50 | 24.0h | 种子:14400;发芽:14400;小叶子:14400;大叶子:21600;初熟:21600;成熟:0; |

View File

@@ -0,0 +1,37 @@
const fs = require('fs');
const path = require('path');
const plantPath = path.join(__dirname, '../gameConfig/Plant.json');
try {
const data = fs.readFileSync(plantPath, 'utf8');
const plants = JSON.parse(data);
let content = `# 农作物列表 (共 ${plants.length} 种)\n\n`;
content += `| ID | 名称 | 等级 | 季数 | 产量 | 生长(小时) | 阶段详情 |\n`;
content += `|---|---|---|---|---|---|---|\n`;
plants.sort((a, b) => a.land_level_need - b.land_level_need || a.id - b.id);
plants.forEach(p => {
let totalTime = 0;
if (p.grow_phases) {
const parts = p.grow_phases.split(';');
parts.forEach(part => {
if (part) {
const [stage, time] = part.split(':');
if (time) totalTime += parseInt(time);
}
});
}
const hours = (totalTime / 3600).toFixed(1);
content += `| ${p.id} | ${p.name} | ${p.land_level_need} | ${p.seasons} | ${p.fruit ? p.fruit.count : '?'} | ${hours}h | ${p.grow_phases} |\n`;
});
fs.writeFileSync(path.join(__dirname, 'crop_list.md'), content);
console.log('列表已生成: server/tools/crop_list.md');
} catch (err) {
console.error('读取失败:', err);
}

File diff suppressed because it is too large Load Diff