feat: 加天气 / 谷仓 / 建筑三套系统,收获改为入库 + 择时卖出
动手前先修掉了两处「死机制」,新增的三个系统各自激活一个: * 天气:小麦 2 天成熟、初始水分 2、每天只掉 1 点,等于永远不需要浇水。 现在水分消耗由天气决定(晴 1 / 多云 0 / 干旱 2,雨天反补 3),而 `state.forecast` 把明天的天气提前告诉你——「今晚浇不浇」成了能算的决策。 5 种天气、权重合计 100、同种不连出;暴风雨会毁未熟作物,温室免疫。 * 谷仓:市场波动原本纯装饰(收获当场按市价变现,没有择时空间)。现在收获与 收取只进 `state.inventory`,靠新增的 `sell` 换金币,价格波动才真正参与决策。 仓满时 `_store()` 抛 `granary_full` 且不改状态,作物原样留在地里。 * 建筑:新增 `build`,买的是「以后不用天天手动做」——水井自动浇干地、 喂食器自动喂食(只吃库存饲料,绝不自动花钱)、温室、谷仓扩容。 每天在 `advance_day` 里先于生长执行,同类越造越贵(×1.6)。 存档升到 `SAVE_VERSION=2`,配 `MIN_SAVE_VERSION=1` 向后兼容,旧档缺的新字段 一律走默认值,不作废玩家手里的档。`advance_day(weather=...)` 是留给测试的 确定性入口。 顺带修两个真 bug: * `daemon.py` 三处 `makedirs(os.path.dirname(path))`:`--pid-file pid.txt` 这种只给文件名的用法会让 dirname 变成空串,启动阶段直接崩。抽出 `ensure_parent_dir()` 兜底。 * `RemoteFarm.snapshot()` 是本地缓存,非实时客户端轮询会一直读到旧数据, 看起来像服务器卡死。补 docstring 指明要自己调 `refresh()`。 质量门禁:ruff format/check 全绿、pyright 0 错误、503 项测试 OK(skip 35)、 bench 无回归(brief 19.5µs / snapshot 48.5µs / POST full=1 400µs)。 另起真实服务器(--tick 3 + 预置存档)跑了 38 项端到端验收,全过。
This commit is contained in:
1 parent
a92c29ead2
commit
cf24cb43f1
21 files changed
+1349
-112
No files matched your search
@@ -515,9 +515,32 @@ def cmd_state(args) -> int:
|
||||
f"金币 {snap.get('gold')} 等级 Lv.{snap.get('level')} "
|
||||
f"({snap.get('exp')}/{snap.get('exp_needed')})"
|
||||
)
|
||||
day_weather = snap.get("weather_name")
|
||||
if day_weather:
|
||||
desc = snap.get("weather_desc")
|
||||
tomorrow = snap.get("forecast_name")
|
||||
print(f"天气 {day_weather}({desc})· 明日 {tomorrow}")
|
||||
print(f"行动点 {snap.get('ap')}/{snap.get('max_ap')} 饲料 {snap.get('feed')}")
|
||||
print(f"净资产 {snap.get('net_worth')}")
|
||||
print(f"土地 {snap.get('plot_count')}/{snap.get('max_plots')} 块")
|
||||
|
||||
used = snap.get("storage_used") or 0
|
||||
capacity = snap.get("granary_capacity") or 0
|
||||
tail = " ← 已满,先 sell 卖点存货" if (snap.get("granary_free") or 0) <= 0 else ""
|
||||
print(f"谷仓 {used}/{capacity} 格{tail}")
|
||||
market = snap.get("market") or {}
|
||||
for key, count in sorted((snap.get("inventory") or {}).items()):
|
||||
price = float(market.get(key, 0.0))
|
||||
base = config.base_price(key)
|
||||
ratio = (price / base - 1) * 100 if base else 0.0
|
||||
print(
|
||||
f" {config.goods_name(key):<6} x{count:<4} 现价 {price:>7.1f}"
|
||||
f"(基准 {base:>6.1f},{ratio:+.0f}%)"
|
||||
)
|
||||
built = {k: v for k, v in (snap.get("buildings") or {}).items() if v}
|
||||
if built:
|
||||
names = {k: v["name"] for k, v in config.BUILDINGS.items()}
|
||||
print("建筑 " + " ".join(f"{names[k]}×{v}" for k, v in built.items()))
|
||||
for plot in snap.get("plots", []):
|
||||
if plot["is_empty"]:
|
||||
continue
|
||||
@@ -558,10 +581,14 @@ def cmd_watch(args) -> int:
|
||||
last_seq = max(last_seq, int(entry.get("seq", 0)))
|
||||
print(f"[D{entry['day']:>3}] {entry['text']}")
|
||||
online = snap.get("online")
|
||||
day_no = snap.get("day")
|
||||
weather = snap.get("weather_name") or "—"
|
||||
used = snap.get("storage_used") or 0
|
||||
capacity = snap.get("granary_capacity") or 0
|
||||
head = (
|
||||
f"第 {snap.get('day')} 天 | 金币 {snap.get('gold')} | "
|
||||
f"第 {day_no} 天 {weather} | 金币 {snap.get('gold')} | "
|
||||
f"Lv.{snap.get('level')} | 行动点 {snap.get('ap')}/{snap.get('max_ap')} | "
|
||||
f"净资产 {snap.get('net_worth')}"
|
||||
f"仓储 {used}/{capacity} | 净资产 {snap.get('net_worth')}"
|
||||
)
|
||||
if online is not None:
|
||||
head += f" | 在线 {online}"
|
||||
@@ -635,6 +662,8 @@ QUICKSTART = """
|
||||
|
||||
uv run main.py actions # 我当前能做什么
|
||||
uv run main.py do harvest '{"plot":1}'
|
||||
uv run main.py do sell '{"key":"wheat"}' # 卖货才换得到金币
|
||||
uv run main.py do build '{"key":"well"}' # 造建筑换自动化
|
||||
uv run main.py state --json # 机器可读的完整状态
|
||||
uv run main.py watch # 实时看别人/机器人玩
|
||||
|
||||
|
||||
Reference in new issue
Block a user