移除行动点系统,倒计时本地走秒,字体优化
为什么:游戏的时间(40 分钟一天、只能自然流动)本身就是唯一的行动节奏 阀门,再叠一层"每天 10 点行动点"纯属重复惩罚——浇水还要体力既不拟真 也不好玩。删掉之后,稀缺的只剩时间本身,放置玩法的定位反而更纯粹。 改动范围(引擎到界面连根拔): - config 删 START_AP/AP_COST;models 删 ap 字段与 max_ap,存档 payload 里的旧 ap 键被 from_dict 自然忽略; - engine 删 _spend_ap 与全部 8 个调用点、每日恢复逻辑与日志; available_actions 不再按行动点筛,cost 只剩金币; - storage 建表去掉 ap 列,老库在 connect() 里 ALTER TABLE DROP COLUMN 无痛迁移(实测 saves/farm.db 已自动迁完); - bot 删 AP 分支(无 AP 后 available_actions 恒可用);client/TUI/CLI/ README/帮助文本同步清理。 网页端顺带两件事: - "下一天"倒计时改为客户端本地走秒——SSE 只在有变化时推送,以前倒计时 只在推送瞬间跳变,现在每秒都在动(记基点 + 1s 定时器补齐); - 字体优化:页面字体栈补齐 MiSans/HarmonyOS Sans/PingFang,正文字号 14→15,统计数字 tabular-nums;canvas 小字统一字体栈并加粗提对比度。 测试适配:删 2 个 AP 专属用例(无 AP 可测)、改名/收敛 5 个, 532 项全绿(skip 39),ruff/pyright 干净。真机验证:老库迁移、 统计条无行动点、倒计时逐秒递减(39:04→39:02)、动作不再报 no_ap。
This commit is contained in:
1 parent
92b1a628b1
commit
15d290a60a
18 files changed
+82
-179
No files matched your search
+17
-43
@@ -129,8 +129,6 @@ class FarmEngine:
|
||||
"level": state.level,
|
||||
"exp": state.exp,
|
||||
"exp_needed": state.exp_needed,
|
||||
"ap": state.ap,
|
||||
"max_ap": state.max_ap,
|
||||
"feed": state.feed,
|
||||
"plot_count": len(state.plots),
|
||||
"max_plots": state.max_plots,
|
||||
@@ -216,7 +214,6 @@ class FarmEngine:
|
||||
},
|
||||
"feed_price": config.FEED_UNIT_PRICE,
|
||||
"water_capacity": config.WATER_CAPACITY,
|
||||
"ap_cost": config.AP_COST,
|
||||
}
|
||||
|
||||
# ---- catalog 的三个子表。拆开是因为它们的"变化频率"各不相同:
|
||||
@@ -299,7 +296,7 @@ class FarmEngine:
|
||||
"""列出当前**合法**的动作,供 AI 直接决策使用。
|
||||
|
||||
每个动作包含:``action``(接口名)、``params``(参数模板)、
|
||||
``cost``(金币/行动点开销)、``reason``(为什么值得做)。
|
||||
``cost``(金币开销)、``reason``(为什么值得做)。
|
||||
"""
|
||||
with self._lock:
|
||||
state = self.state
|
||||
@@ -314,15 +311,12 @@ class FarmEngine:
|
||||
# ---- available_actions 的三个分区;拆开是为了各自能独立阅读/测试
|
||||
def _plot_actions(self, state: GameState) -> list[dict[str, Any]]:
|
||||
"""每块地当前能做的事:播种 / 收获 / 浇水。"""
|
||||
# 能种什么只取决于等级、金币和行动点,与**具体是哪块地**无关,
|
||||
# 能种什么只取决于等级和金币,与**具体是哪块地**无关,
|
||||
# 所以整趟只筛一次,不要放进地块循环里做 O(地块 × 作物) 次判断。
|
||||
plant_ap = config.AP_COST["plant"]
|
||||
plantable = [
|
||||
(key, info)
|
||||
for key, info in config.CROPS.items()
|
||||
if not self._is_locked(info)
|
||||
and state.gold >= info["seed_cost"]
|
||||
and state.ap >= plant_ap
|
||||
if not self._is_locked(info) and state.gold >= info["seed_cost"]
|
||||
]
|
||||
actions: list[dict[str, Any]] = []
|
||||
for plot in state.plots:
|
||||
@@ -333,7 +327,7 @@ class FarmEngine:
|
||||
{
|
||||
"action": "plant",
|
||||
"params": {"plot": position, "crop": key},
|
||||
"cost": {"gold": info["seed_cost"], "ap": plant_ap},
|
||||
"cost": {"gold": info["seed_cost"]},
|
||||
"reason": f"{position} 号地空闲,可种{info['name']}",
|
||||
}
|
||||
)
|
||||
@@ -342,11 +336,11 @@ class FarmEngine:
|
||||
{
|
||||
"action": "harvest",
|
||||
"params": {"plot": position},
|
||||
"cost": {"gold": 0, "ap": config.AP_COST["harvest"]},
|
||||
"cost": {"gold": 0},
|
||||
"reason": f"{position} 号地的{config.CROPS[plot.crop]['name']}已成熟",
|
||||
}
|
||||
)
|
||||
elif self._needs_water(plot) and state.ap >= config.AP_COST["water"]:
|
||||
elif self._needs_water(plot):
|
||||
reason = (
|
||||
f"{position} 号地缺水,作物已停止生长"
|
||||
if plot.moisture <= 0
|
||||
@@ -356,7 +350,7 @@ class FarmEngine:
|
||||
{
|
||||
"action": "water",
|
||||
"params": {"plot": position},
|
||||
"cost": {"gold": 0, "ap": config.AP_COST["water"]},
|
||||
"cost": {"gold": 0},
|
||||
"reason": reason,
|
||||
}
|
||||
)
|
||||
@@ -371,19 +365,19 @@ class FarmEngine:
|
||||
{
|
||||
"action": "collect",
|
||||
"params": {"animal_id": animal.aid},
|
||||
"cost": {"gold": 0, "ap": config.AP_COST["collect"]},
|
||||
"cost": {"gold": 0},
|
||||
"reason": f"{animal.name}有 {animal.pending} 份{animal.product}待收取",
|
||||
}
|
||||
)
|
||||
elif not animal.fed:
|
||||
need = animal.info["feed_per_day"]
|
||||
cost = 0 if state.feed >= need else need * config.FEED_EMERGENCY_PRICE
|
||||
if state.gold >= cost and state.ap >= config.AP_COST["feed"]:
|
||||
if state.gold >= cost:
|
||||
actions.append(
|
||||
{
|
||||
"action": "feed",
|
||||
"params": {"animal_id": animal.aid},
|
||||
"cost": {"gold": cost, "ap": config.AP_COST["feed"]},
|
||||
"cost": {"gold": cost},
|
||||
"reason": f"{animal.name}(#{animal.aid})今天还没喂食",
|
||||
}
|
||||
)
|
||||
@@ -398,7 +392,7 @@ class FarmEngine:
|
||||
{
|
||||
"action": "buy_plot",
|
||||
"params": {},
|
||||
"cost": {"gold": price, "ap": config.AP_COST["buy_plot"]},
|
||||
"cost": {"gold": price},
|
||||
"reason": f"可花 {price} 金币开垦第 {len(state.plots) + 1} 块地",
|
||||
}
|
||||
)
|
||||
@@ -408,7 +402,7 @@ class FarmEngine:
|
||||
{
|
||||
"action": "buy_feed",
|
||||
"params": {"quantity": config.FEED_BULK},
|
||||
"cost": {"gold": bulk_cost, "ap": config.AP_COST["buy_feed"]},
|
||||
"cost": {"gold": bulk_cost},
|
||||
"reason": "饲料库存偏低,建议补货",
|
||||
}
|
||||
)
|
||||
@@ -421,7 +415,7 @@ class FarmEngine:
|
||||
{
|
||||
"action": "buy_animal",
|
||||
"params": {"kind": key},
|
||||
"cost": {"gold": info["cost"], "ap": config.AP_COST["buy_animal"]},
|
||||
"cost": {"gold": info["cost"]},
|
||||
"reason": f"可购买{info['name']}({info['cost']} 金币)",
|
||||
}
|
||||
)
|
||||
@@ -457,7 +451,7 @@ class FarmEngine:
|
||||
{
|
||||
"action": "sell",
|
||||
"params": {"key": key, "quantity": count},
|
||||
"cost": {"gold": 0, "ap": config.AP_COST["sell"]},
|
||||
"cost": {"gold": 0},
|
||||
"reason": ";".join(reasons),
|
||||
}
|
||||
)
|
||||
@@ -474,7 +468,7 @@ class FarmEngine:
|
||||
{
|
||||
"action": "build",
|
||||
"params": {"key": key},
|
||||
"cost": {"gold": entry["next_cost"], "ap": config.AP_COST["build"]},
|
||||
"cost": {"gold": entry["next_cost"]},
|
||||
"reason": f"可造第 {entry['owned'] + 1} 座{entry['name']}:{entry['effect']}",
|
||||
}
|
||||
)
|
||||
@@ -531,17 +525,6 @@ class FarmEngine:
|
||||
return animal
|
||||
raise GameError("no_such_animal", f"没有编号为 {aid} 的动物")
|
||||
|
||||
def _spend_ap(self, action: str) -> None:
|
||||
cost = config.AP_COST.get(action, 0)
|
||||
if cost and self.state.ap < cost:
|
||||
raise GameError(
|
||||
"no_ap",
|
||||
f"行动点不足:{action} 需要 {cost} 点,当前只剩 {self.state.ap} 点。"
|
||||
f"等一天的时间自然走完就会恢复。",
|
||||
)
|
||||
if cost:
|
||||
self.state.ap -= cost
|
||||
|
||||
def _spend_gold(self, amount: int, what: str) -> None:
|
||||
amount = int(amount)
|
||||
if amount < 0:
|
||||
@@ -639,7 +622,6 @@ class FarmEngine:
|
||||
)
|
||||
|
||||
self._spend_gold(info["seed_cost"], f"购买{info['name']}种子")
|
||||
self._spend_ap("plant")
|
||||
|
||||
plot.crop = crop
|
||||
plot.growth = 0.0
|
||||
@@ -666,7 +648,6 @@ class FarmEngine:
|
||||
"already_watered",
|
||||
f"{plot.index + 1} 号地水分已满({plot.moisture}/{config.WATER_CAPACITY})",
|
||||
)
|
||||
self._spend_ap("water")
|
||||
plot.moisture = config.WATER_CAPACITY
|
||||
self.add_log(f"给 {plot.index + 1} 号地浇水(水分 {config.WATER_CAPACITY})", "action")
|
||||
self._notify()
|
||||
@@ -684,7 +665,6 @@ class FarmEngine:
|
||||
)
|
||||
crop_key = plot.crop
|
||||
info = config.CROPS[crop_key]
|
||||
self._spend_ap("harvest")
|
||||
|
||||
quantity = self._roll_yield()
|
||||
# 先入库再清地:谷仓满了会在这里抛 granary_full,作物原地不动,
|
||||
@@ -729,7 +709,6 @@ class FarmEngine:
|
||||
raise GameError("pasture_full", f"牧场已满(上限 {config.MAX_ANIMALS} 只)")
|
||||
|
||||
self._spend_gold(info["cost"], f"购买{info['name']}")
|
||||
self._spend_ap("buy_animal")
|
||||
|
||||
animal = Animal(kind)
|
||||
self.state.animals.append(animal)
|
||||
@@ -766,7 +745,6 @@ class FarmEngine:
|
||||
self.state.feed += shortfall
|
||||
self.add_log(f"饲料不足,临时高价补货 {shortfall} 份(-{cost} 金币)", "warn")
|
||||
|
||||
self._spend_ap("feed")
|
||||
self.state.feed -= quantity
|
||||
animal.fed = True
|
||||
animal.starve = 0
|
||||
@@ -788,7 +766,6 @@ class FarmEngine:
|
||||
f"{animal.name} #{animal.aid} 暂时没有可收取的{animal.product}"
|
||||
f"(还需 {animal.days_to_product} 天)",
|
||||
)
|
||||
self._spend_ap("collect")
|
||||
quantity = animal.pending
|
||||
key = f"animal:{animal.kind}"
|
||||
# 与收获同理:先确认装得下,再清空待收,免得产出凭空消失
|
||||
@@ -850,7 +827,6 @@ class FarmEngine:
|
||||
f"土地已达当前等级上限({self.state.max_plots} 块),升级后可继续开垦",
|
||||
)
|
||||
self._spend_gold(price, "开垦新土地")
|
||||
self._spend_ap("buy_plot")
|
||||
plot = Plot(len(self.state.plots))
|
||||
self.state.plots.append(plot)
|
||||
self.state.plots_bought += 1
|
||||
@@ -971,7 +947,6 @@ class FarmEngine:
|
||||
raise GameError("build_limit", f"{entry['name']}已建满({entry['max_level']} 座)")
|
||||
|
||||
self._spend_gold(entry["next_cost"], f"建造{entry['name']}")
|
||||
self._spend_ap("build")
|
||||
self.state.buildings[key] = entry["owned"] + 1
|
||||
built = self._building_entry(self.state, key)
|
||||
self.add_log(
|
||||
@@ -1070,7 +1045,7 @@ class FarmEngine:
|
||||
|
||||
顺序是一条流水线:浇水(水井/喷灌)保住作物 → 收割机把熟了的收进
|
||||
谷仓 → 磨坊把陈粮磨成饲料 → 喂食器喂牲口 → 售货机趁高价清库存。
|
||||
共同的设计底线:**只替代玩家本来要花行动点做的事,绝不自动花钱**
|
||||
共同的设计底线:**只替代玩家本来要花工夫做的事,绝不自动花钱**
|
||||
——喂食器缺饲料停摆、收割机遇满仓停摆,都宁可停也不硬来。
|
||||
"""
|
||||
return (
|
||||
@@ -1246,7 +1221,6 @@ class FarmEngine:
|
||||
state = self.state
|
||||
info = self._resolve_weather(state, weather)
|
||||
state.day += 1
|
||||
state.ap = state.max_ap
|
||||
events: list[str] = []
|
||||
|
||||
# ---- 建筑自动化。必须排在生长结算之前:水井救的正是"今天就断水"的
|
||||
@@ -1303,7 +1277,7 @@ class FarmEngine:
|
||||
self.add_log(text, "warn")
|
||||
today = f"第 {state.day} 天开始({reason}),今日{info['name']}"
|
||||
if events:
|
||||
self.add_log(f"{today},行动点已恢复", "info")
|
||||
self.add_log(f"{today},新的一天开始了", "info")
|
||||
else:
|
||||
self.add_log(f"{today},一切安好", "info")
|
||||
|
||||
|
||||
Reference in new issue
Block a user