"""游戏平衡参数与静态定义。 本模块只描述"世界是什么样的"(作物表、动物表、经济参数), 不包含任何状态或逻辑,方便单独调参。 """ from __future__ import annotations from typing import Any # -------------------------------------------------------------------------- # 基础经济参数 # -------------------------------------------------------------------------- START_GOLD = 120 # 初始金币 START_LEVEL = 1 # 初始等级 START_AP = 10 # 每天行动点上限 START_FEED = 5 # 初始饲料库存 BASE_PLOTS = 6 # 初始土地数量 MAX_PLOTS = 16 # 土地数量硬上限 PLOT_BASE_PRICE = 200 # 第一块新地的价格 PLOT_PRICE_GROWTH = 1.75 # 每多买一块,价格乘以该系数 FEED_UNIT_PRICE = 6 # 主动购买饲料的单价 FEED_EMERGENCY_PRICE = 9 # 自动补货(缺饲料时喂食)的惩罚单价 FEED_BULK = 10 # 一次购买饲料的默认批量 WATER_CAPACITY = 3 # 一次浇水可支撑的天数 PLANT_INITIAL_MOISTURE = 2 # 播种后地块自带的水分 PRICE_VOLATILITY = 0.18 # 每天价格波动幅度(±18%) LUCKY_CHANCE = 0.12 # 丰收暴击概率(产出翻倍) MAX_LOG_ENTRIES = 300 # 内存中保留的事件条数上限 MAX_FEED_PURCHASE = 200 # 单次购买饲料的数量上限 # 每级提供的土地上限(基础 6 + 2/级,封顶 MAX_PLOTS) PLOTS_PER_LEVEL = 2 # 行动点消耗表 AP_COST = { "plant": 1, "water": 1, "harvest": 0, "feed": 1, "collect": 0, "buy_animal": 0, "buy_plot": 0, "buy_feed": 0, "build": 0, "sell": 0, } # 经验曲线:升到 level+1 所需经验 def exp_to_next(level: int) -> int: """返回从 level 升到 level+1 所需的经验值。""" return int(80 * (max(1, level) ** 1.6)) # 升级奖励金币 def level_up_bonus(level: int) -> int: """升到 level 时发放的金币奖励。""" return 80 + 40 * level # -------------------------------------------------------------------------- # 作物表 # -------------------------------------------------------------------------- # key : 内部标识(API 与存档使用) # name : 中文名 # seed_cost : 种子价格 # grow_days : 成熟所需生长天数(需要水分支撑) # base_price : 收获物的基础售价 # exp : 每次收获获得的经验 # level_req : 解锁所需等级 CROPS: dict[str, dict[str, Any]] = { "wheat": { "name": "小麦", "seed_cost": 5, "grow_days": 2, "base_price": 14, "exp": 3, "level_req": 1, }, "carrot": { "name": "胡萝卜", "seed_cost": 8, "grow_days": 3, "base_price": 26, "exp": 5, "level_req": 1, }, "tomato": { "name": "番茄", "seed_cost": 16, "grow_days": 4, "base_price": 52, "exp": 9, "level_req": 2, }, "corn": { "name": "玉米", "seed_cost": 28, "grow_days": 6, "base_price": 95, "exp": 14, "level_req": 3, }, "pumpkin": { "name": "南瓜", "seed_cost": 45, "grow_days": 8, "base_price": 180, "exp": 22, "level_req": 4, }, "strawberry": { "name": "草莓", "seed_cost": 70, "grow_days": 10, "base_price": 320, "exp": 35, "level_req": 5, }, } # 生长阶段阈值(按生长进度 0.0~1.0 划分) GROWTH_STAGES = [ (0.00, "已播种"), (0.30, "幼苗"), (0.65, "生长中"), (1.00, "已成熟"), ] # -------------------------------------------------------------------------- # 动物表 # -------------------------------------------------------------------------- # key : 内部标识 # name : 中文名 # cost : 购买价格 # product : 产出物标识(中文名) # base_price : 产出物基础售价 # cycle : 产出周期(天) # feed_per_day: 每天消耗饲料数 # exp : 每次收集产出获得的经验 # level_req : 解锁所需等级 ANIMALS: dict[str, dict[str, Any]] = { "chicken": { "name": "鸡", "cost": 90, "product": "鸡蛋", "base_price": 15, "cycle": 1, "feed_per_day": 1, "exp": 4, "level_req": 1, }, "sheep": { "name": "羊", "cost": 220, "product": "羊毛", "base_price": 55, "cycle": 2, "feed_per_day": 1, "exp": 9, "level_req": 2, }, "cow": { "name": "牛", "cost": 480, "product": "牛奶", "base_price": 72, "cycle": 1, "feed_per_day": 2, "exp": 15, "level_req": 3, }, } MAX_ANIMALS = 8 # 牧场总容量 # -------------------------------------------------------------------------- # 时间 # -------------------------------------------------------------------------- # 一天有多长(真实秒数)。慢是刻意的:这是“放置农场”—— # 一天 40 分钟,作物 2~10 天成熟(小麦 80 分钟、草莓约 6.7 小时)。 # 没有任何“手动推进一天”的接口,时间只能自然流动。 DEFAULT_TICK_SECONDS = 2400.0 MIN_TICK_SECONDS = 3.0 # 调试用:把一天缩到几秒 MAX_TICK_SECONDS = 86400.0 # 上限给到一整天,想多慢都不会被夹住 # -------------------------------------------------------------------------- # 天气 # -------------------------------------------------------------------------- # 每天随机一种天气,直接改写"水分"这条曲线: # * evaporation 决定作物每天掉多少水分(0~2),浇水于是从"常量消耗"变成看天吃饭; # * water_gain 是自动补水,雨天/暴风雨相当于免费替你浇了一遍; # * damage 只作用于**还没成熟**的地块——已经熟了、正等你收的东西不该被天灾吃掉。 # 权重合计 100;同一种天气不连着出现(重掷,见 WEATHER_REROLLS),免得一连旱一周。 # # key : 内部标识(存档与 API 使用) # name : 中文名 # weight : 出现权重 # evaporation : 当天水分蒸发量 # water_gain : 当天自动补水(封顶 WATER_CAPACITY) # damage : 每块未成熟地块被毁的独立概率 # desc : 给玩家与 AI 看的一句话 WEATHER: dict[str, dict[str, Any]] = { "sunny": { "name": "晴", "weight": 30, "evaporation": 1, "water_gain": 0, "damage": 0.0, "desc": "阳光充足,水分正常蒸发", }, "cloudy": { "name": "多云", "weight": 27, "evaporation": 0, "water_gain": 0, "damage": 0.0, "desc": "日头不毒,作物几乎不失水", }, "rain": { "name": "雨", "weight": 24, "evaporation": 0, "water_gain": WATER_CAPACITY, "damage": 0.0, "desc": "雨水充足,所有作物自动补满水分", }, "drought": { "name": "干旱", "weight": 12, "evaporation": 2, "water_gain": 0, "damage": 0.0, "desc": "水分蒸发加倍,一天不浇就可能停长", }, "storm": { "name": "暴风雨", "weight": 7, "evaporation": 0, "water_gain": WATER_CAPACITY, "damage": 0.12, "desc": "暴雨伴狂风,未成熟的作物可能被毁", }, } DEFAULT_WEATHER = "sunny" # 新牧场开局天气 WEATHER_REROLLS = 3 # 同一天气最多重掷几次 def weather_info(key: str | None) -> dict[str, Any]: """取某种天气的完整定义;未知 key 退回默认天气。""" return WEATHER.get(key or "", WEATHER[DEFAULT_WEATHER]) # -------------------------------------------------------------------------- # 谷仓(仓储 + 自由买卖) # -------------------------------------------------------------------------- # 收获不再当场按市价卖掉,而是先进谷仓;卖不卖、什么时候卖由玩家决定。 # 这样"市场每天波动"才第一次变成真决策:囤到高位再出,或者急着用钱低价走。 GRANARY_BASE = 40 # 初始仓容 GRANARY_PER_LEVEL = 10 # 每升 1 级 +10 GRANARY_PER_UPGRADE = 25 # 每座谷仓建筑 +25 MAX_GRANARY = 400 # 仓容硬上限 # -------------------------------------------------------------------------- # 建筑 # -------------------------------------------------------------------------- # 给金币一个"买地 / 买牲畜"之外的长期出口,而且买的是**自动化**: # 花一笔钱,换掉一件每天都要手动做的事。 # # key : 内部标识 # name : 中文名 # cost : 第一座的价格(第 owned+1 座 = cost * BUILD_PRICE_GROWTH ** owned) # level_req : 解锁等级 # max_level : 最多能造几座 # effect : 一句话说明(原样出现在 catalog 与界面上) BUILDINGS: dict[str, dict[str, Any]] = { "granary": { "name": "谷仓", "cost": 300, "level_req": 1, "max_level": 5, "effect": f"仓储上限 +{GRANARY_PER_UPGRADE}", }, "well": { "name": "水井", "cost": 400, "level_req": 2, "max_level": 3, "effect": "每天自动给 1 块干透的地浇满", }, "feeder": { "name": "自动喂食器", "cost": 650, "level_req": 3, "max_level": 3, "effect": "每天自动喂 1 只牲畜(只吃库存饲料,不自动花钱)", }, "greenhouse": { "name": "温室", "cost": 1500, "level_req": 5, "max_level": 1, "effect": "作物不再被暴风雨损毁,水分蒸发减半", }, } BUILD_PRICE_GROWTH = 1.6 # 同类建筑第 n 座的价格系数 def build_price(key: str, owned: int) -> int: """第 owned+1 座建筑的价格(owned 是这类建筑已建的数量)。""" return int(BUILDINGS[key]["cost"] * (BUILD_PRICE_GROWTH**owned)) # -------------------------------------------------------------------------- # 货物(可买卖的东西) # -------------------------------------------------------------------------- # 谷仓、市场、sell 动作共用同一套 key:作物直接用作物 key(wheat), # 畜产品用 animal:<牲畜 key>(animal:chicken)。命名收敛在一处, # 客户端就不必自己拼前缀,也不会出现「两个地方各拼一种」的漂移。 def goods_info(key: str) -> dict[str, Any] | None: """货物 key 对应的静态定义;未知 key 返回 None。""" if key.startswith("animal:"): return ANIMALS.get(key.split(":", 1)[1]) return CROPS.get(key) def goods_name(key: str) -> str: """货物 key -> 中文名。畜产品给的是产出名(鸡蛋)而不是牲畜名(鸡)。""" if key.startswith("animal:"): info = ANIMALS.get(key.split(":", 1)[1]) return str(info["product"]) if info else key info = CROPS.get(key) return str(info["name"]) if info else key def base_price(key: str) -> float: """货物 key 的基准价,也就是市场每天围绕它波动的那个中心。""" info = goods_info(key) return float(info["base_price"]) if info else 0.0 # -------------------------------------------------------------------------- # 进度目标 # -------------------------------------------------------------------------- GOALS = [ (200, "起步:攒够 200 金币"), (600, "小农:攒够 600 金币"), (1500, "农场主:攒够 1500 金币"), (4000, "庄园主:攒够 4000 金币"), (10000, "农业大亨:攒够 10000 金币"), ] def goal_index(net_worth: float) -> int: """返回已达成的最低目标索引(-1 表示一个都没达成)。""" reached = -1 for i, (amount, _) in enumerate(GOALS): if net_worth >= amount: reached = i return reached