README.md 10 KB

荷锄 — 种田游戏

基于 LÖVE2D (LuaJIT) 的竖屏触控农场游戏,支持 Android 打包与 Mod 系统。


快速上手

# 运行游戏(桌面)
& 'C:\Program Files\LOVE\lovec.exe' --console .

# 运行测试
busted .

存档位置:

  • Windows: %APPDATA%\LOVE\farm\
  • Linux: ~/.local/share/love/farm/
  • Android: /sdcard/Android/data/<包名>/files/

日志文件:%APPDATA%\LOVE\farm\game.log(运行时错误会写入此处)


分层架构

main.lua          ← LÖVE2D 回调入口,只做转发,不含逻辑
    │
engine/           基础设施层:引擎主循环、存档、事件总线、Mod加载
    │
love_adapter/     LÖVE2D 适配层:将 love API 包装为接口,隔离平台依赖
    │
game.lua          协调层:组装所有子系统,处理输入分发与绘制调度
    │
systems/          游戏逻辑层:农场、玩家、世界、背包等纯逻辑
    │
render/           渲染层:所有绘制代码,依赖 systems/ 提供的接口
    │
data/             纯数据层:Lua table 定义,无副作用,无依赖

依赖规则: 上层可依赖下层,同层通过 engine/eventbus 解耦,禁止下层依赖上层。


关键设计决策

接口隔离(engine/interface.lua)

所有跨层传递的对象都通过接口定义,用 classic.lua 的 Object:extend() 实现:

  • IWorldQuery — systems 层查询世界地图(WorldSystem 实现)
  • IFarmQuery — render 层查询农场状态(FarmSystem 实现)
  • IGameStateQuery — render 层查询玩家状态(game.lua 内 GameStateAdapter 实现)
  • IGraphics — 绘图接口(love_adapter/graphics.lua 实现,测试用 MockGraphics)
  • IFilesystem — 文件系统接口(love_adapter/filesystem.lua 实现)
  • IAudio / IWindow — 音频/窗口接口

这样 systems/ 和 render/ 完全不直接调用 love.*,可以在无 LÖVE 环境下单元测试。

对象实例化(classic.lua)

项目使用 classic 作为 OOP 基础。正确的实例化方式是调用类本身(触发 __call),而不是 :new():

-- 正确
local obj = MyClass(arg1, arg2)

-- 错误(:new() 直接调用方法,返回 nil)
local obj = MyClass:new(arg1, arg2)

new() 方法里直接给 self 赋字段,不需要 return self(__call 负责返回实例)。

适配层注入顺序(game.lua)

Game.new() 按固定顺序初始化,顺序不能乱:

  1. 创建 love_adapter 实例(LoveGraphics() 等)
  2. 将适配层注入 engine 模块(Save.setFS、ModLoader.setFS、Anim.setGraphics 等)
  3. 创建 systems 层对象(FarmSystem()、WorldSystem() 等)
  4. 创建 GameStateAdapter(包装 gameState 为 IGameStateQuery)
  5. 创建 render 层对象,注入接口
  6. 创建依赖接口的 systems 对象(PlayerSystem)
  7. 注入跨系统引用(TickSystem.setWorld、Save.setWorld 等)

Save 模块的调用方式

engine/save.lua 里的 _fs 是 IFilesystem 实例,所有调用必须用冒号语法:

_fs:exists(path)   -- 正确
_fs.exists(path)   -- 错误(self 会变成 path,导致 getInfo(nil))

目录结构

main.lua              LÖVE2D 回调入口
conf.lua              窗口配置
game.lua              游戏主控制器(协调层,~3000行)
classic.lua           轻量 OOP 库
assets/sphere.glsl        球面畸变 shader(世界层后处理)

engine/
  engine.lua          主循环、错误捕获、自动存档(每5分钟)
  eventbus.lua        发布/订阅事件总线
  registry.lua        统一数据访问(getCrop/getItem/getBlock/getTool)
  save.lua            多存档读写(save_1~3.lua),版本17
  camera.lua          摄像机(环形世界坐标、zoom、tileToScreen)
  mod_loader.lua      Mod 加载、API 工厂、钩子分发
  interface.lua       跨层接口定义 + MockGraphics(测试用)
  audio.lua           程序化音频合成
  sfx.lua             音效预合成与事件绑定
  anim.lua            动画帧计时
  pathfinding.lua     A* 寻路(环形地图)
  particles.lua       粒子效果

love_adapter/
  graphics.lua        IGraphics → love.graphics
  filesystem.lua      IFilesystem → love.filesystem
  audio.lua           IAudio → love.audio
  window.lua          IWindow → love.graphics.getWidth/Height

systems/
  state.lua           gameState 创建与操作(金币/经验/精力/天赋)
  farm.lua            农场格子逻辑(耕/种/浇/收)
  world_system.lua    100×100 环形地图(实现 IWorldQuery)
  player.lua          玩家移动 + 工具操作(整合 Character + A*)
  character.lua       角色像素坐标、动画状态机
  inventory.lua       背包(30格 + 快捷栏6格)
  block_system.lua    可放置方块(箱子/工作台/栅栏等)
  tick_system.lua     物流方块定时驱动(每0.5秒)
  time.lua            游戏时间、昼夜、节气
  weather.lua         天气系统(晴/阴/雨/暴雨)
  genetics.lua        作物基因系统(杂交、表现型、高产)
  talent_stats.lua    天赋+种族+职业+装备 → stats 表
  market.lua          动态市场价格
  drops.lua           掉落物(吸引+自动拾取)
  dungeon.lua         副本地图数据
  pet.lua             宠物(跟随、离线收益)
  achievement_system.lua  成就系统
  consumable_system.lua   消耗品使用逻辑
  equipment_system.lua    装备穿脱与套装效果
  shop_system.lua         商店购买逻辑
  workbench_system.lua    工作台合成
  forge_system.lua        扑克工厂锻造
  quiz_npc_system.lua     问答NPC
  lighting_system.lua     夜晚光照贴图

render/
  world_renderer.lua      地图底层/覆盖层精灵(y-sort)
  farm_renderer.lua       农场格子与作物生长动画
  character_renderer.lua  玩家行走/动作动画(strip 精灵表)
  ui_renderer.lua         HUD、商店、消息、飘字、Mod管理面板
  inventory_renderer.lua  背包/快捷栏/装备槽/拖拽
  block_renderer.lua      方块精灵
  crop_renderer.lua       作物图标工具
  pet_renderer.lua        宠物渲染与升级面板
  talent_renderer.lua     天赋选择/查看面板
  workbench_renderer.lua  工作台合成面板
  forge_renderer.lua      扑克工厂锻造面板
  dungeon_renderer.lua    副本地图渲染
  drops_renderer.lua      掉落物渲染
  effect_renderer.lua     粒子特效封装
  render_pipeline.lua     渲染调度框架

data/                     纯数据(return { ... },无副作用)
  config.lua              全局配置(窗口/玩家初始值)
  crops.lua               作物定义
  items.lua               消耗品/可放置物品
  blocks.lua              方块定义
  tools.lua               工具定义
  talents.lua             天赋定义
  equipment.lua           装备(扑克牌+骰子)
  equipment_sets.lua      套装效果
  races.lua               种族定义
  classes.lua             职业定义
  achievements.lua        成就定义
  recipes.lua             工作台配方
  forge.lua               锻造链
  levels.lua              升级经验表
  quiz.lua                问答题库
  solar_terms.lua         24节气
  seasons.lua             季节定义
  sfx.lua                 音效参数
  music.lua               背景音乐
  pets.lua                宠物定义
  redeem_codes.lua        兑换码

mods/
  seasonal_trader/        内置示例 Mod(季节商人)

spec/                     busted 单元测试

核心常量

-- engine/camera.lua
Camera.TILE_PX  = 32    -- 每格像素
Camera.MAP_COLS = 100   -- 地图宽(格)
Camera.MAP_ROWS = 100   -- 地图高(格)

-- engine/save.lua
version = 17            -- 存档版本,不兼容时提示删除旧存档

EventBus 主要事件

模块间通信通过 Bus.emit(event, data) / Bus.on(event, fn) 解耦。

事件 触发时机 data 字段
tile_harvested 收获作物 cropId, row, col, count, gene, dropSeed
tile_tilled 耕地 row, col
tile_watered 浇水 row, col
tree_chopped 砍树 row, col, wood
gravel_mined 采碎石堆 row, col, count
block_placed/mined 放置/挖掉方块 row, col, blockId
block_interact 交互方块 row, col, action, blockData
gain_exp 获得经验 amount
player_levelup 玩家升级 level
sold 出售作物 amount
day_changed 游戏日切换 —
weather_changed 天气/节气变化 solarTerm
achievement_unlocked 解锁成就 id
show_msg 显示底部消息 text
item_drop 背包满掉落 item, pixX, pixY

测试

busted .
文件 覆盖内容
spec/regression_core_spec.lua Genetics/Inventory/EventBus/Market/State/Save
spec/phase1~6_*_spec.lua 各层路径与接口验证
spec/layered_arch/ 属性测试(层依赖、接口契约)
spec/helpers/love_mock.lua love API stub,无需 LÖVE 运行时

Mod 系统

详见 MOD_GUIDE.md。

Mod 放在存档目录的 user_mods/<mod_name>/mod.lua,内置 Mod 在 mods/ 下。主菜单有 Mod 管理面板,可按存档槽独立启用/禁用。

-- mod.lua 最小结构
return {
    priority = 50,  -- 加载顺序,越小越先
    init = function(api)
        -- api.registerCrop/Item/Block/Tool/Talent/Race/Class/Achievement/Recipe
        -- api.overrideCrop/Item/Block/Tool/Talent
        -- api.wrapFunction(modulePath, fnName, wrapper)
        -- api.addConsumable / addToolAction / addDraw / addTick / addKey
        -- api.on(event, fn) / api.emit(event, data)
        -- api.replaceAsset(virtualPath, modRelativePath)
        -- 运行时(只在回调里用):getGame/getFarm/getPlayer/getGameState
        -- api.addMoney/addExp/addStamina/addItem/showMsg
    end,
}

存档版本历史

版本 变更
15 基础版本
16 新增 quizDoneDay
17 新增 achievements、enabledMods、多存档(save_1~3.lua)