# 荷锄 — 种田游戏 基于 LÖVE2D (LuaJIT) 的竖屏触控农场游戏,支持 Android 打包与 Mod 系统。 --- ## 快速上手 ```bash # 运行游戏(桌面) & '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](https://github.com/rxi/classic) 作为 OOP 基础。**正确的实例化方式是调用类本身**(触发 `__call`),而不是 `:new()`: ```lua -- 正确 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` 实例,所有调用必须用冒号语法: ```lua _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 单元测试 ``` --- ## 核心常量 ```lua -- 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` | --- ## 测试 ```bash 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.lua`,内置 Mod 在 `mods/` 下。主菜单有 Mod 管理面板,可按存档槽独立启用/禁用。 ```lua -- 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) |