种子与作物系统.md 19 KB

种子与作物系统设计文档

1. 系统概述

种子与作物系统是农场模块的核心玩法组件,负责管理种子类型、作物生长周期、灾害系统和收获机制。玩家通过种植不同类型的种子,经历生长过程,最终收获作物获得奖励。

2. 种子系统

2.1 种子类型

enum SEED_TYPE: int {
    case NORMAL = 1;     // 普通种子
    case MYSTERY = 2;    // 神秘种子
    case GIANT = 3;      // 巨化种子
}

2.2 普通种子列表

ID 名称 种子期(秒) 发芽期(秒) 生长期(秒) 最小产出 最大产出
1 萝卜种子 1800 3600 7200 1000 1500
2 辣椒种子 3600 7200 14400 1500 2000
3 苹果种子 7200 14400 28800 2000 2500
4 西瓜种子 10800 21600 43200 2500 3000
5 草莓种子 14400 28800 57600 3000 3500
6 南瓜种子 18000 36000 72000 3500 4000
7 核桃种子 21600 43200 86400 4000 4500
8 可可种子 25200 50400 100800 4500 5000
9 人参种子 28800 57600 115200 5000 5500
10 玫瑰种子 32400 64800 129600 5500 6000

2.3 神秘种子

神秘种子在种植时随机转化为一种普通种子,但有小概率转化为巨化种子。

概率 结果
90% 随机普通种子
10% 随机巨化种子

2.4 巨化种子

巨化种子是普通种子的强化版本,生长周期相同但产量更高。

ID 名称 对应普通种子 产量倍率
101 巨化草莓种子 草莓种子 1.5
102 巨化南瓜种子 南瓜种子 1.5
103 巨化核桃种子 核桃种子 1.5
104 巨化可可种子 可可种子 1.5
105 巨化人参种子 人参种子 1.5
106 巨化玫瑰种子 玫瑰种子 1.5

3. 作物生长周期

3.1 生长阶段

enum CROP_GROWTH_STAGE: int {
    case SEED = 1;       // 种子期
    case SPROUT = 2;     // 发芽期
    case GROWING = 3;    // 生长期
    case MATURE = 4;     // 成熟期
    case WITHERED = 5;   // 枯萎期
}

3.2 阶段特性

阶段 持续时间 可用化肥 灾害概率 状态转换
种子期 种子配置 自动进入发芽期
发芽期 种子配置 自动进入生长期
生长期 种子配置 自动进入成熟期
成熟期 24小时 超时进入枯萎期
枯萎期 永久 需手动铲除

3.3 化肥加速

每个生长阶段(种子期/发芽期/生长期)可使用一次化肥加速:

  • 普通化肥:减少当前阶段1小时生长时间
  • 高级化肥:减少当前阶段3小时生长时间

4. 灾害系统

4.1 灾害类型

enum DISASTER_TYPE: int {
    case DROUGHT = 1;    // 干旱
    case PEST = 2;       // 虫害
    case WEED = 3;       // 杂草
}

4.2 灾害特性

灾害类型 减产比例 处理道具 处理效果
干旱 5% 洒水壶 完全消除
虫害 5% 杀虫剂 完全消除
杂草 5% 除草剂 完全消除

4.3 灾害生成

  • 灾害生成在定时任务中处理
  • 每个生长阶段有不同的灾害生成概率
  • 土地类型影响灾害抵抗,降低灾害生成概率
  • 同一作物可能同时存在多种灾害
  • 每种灾害最多出现一次

4.4 神灵加持对灾害的影响

神灵加持系统可以防止特定类型的灾害生成:

  • 雨露之神:防止干旱灾害的生成
  • 屠草之神:防止杂草灾害的生成
  • 拭虫之神:防止虫害灾害的生成

灾害生成逻辑会检查用户是否有对应的神灵加持,如果有则不会生成该类型的灾害:

// 检查用户是否有神灵加持
$godBuffs = $this->godBuffRepository->getUserActiveBuffs($userId);

// 根据神灵加持调整可能生成的灾害类型
if ($this->hasGodBuff($godBuffs, GOD_BUFF_TYPE::RAIN)) {
    // 有雨露之神加持,不生成干旱灾害
    $disasterTypes = [DISASTER_TYPE::PEST, DISASTER_TYPE::WEED];
}
else if ($this->hasGodBuff($godBuffs, GOD_BUFF_TYPE::WEED)) {
    // 有屠草之神加持,不生成杂草灾害
    $disasterTypes = [DISASTER_TYPE::DROUGHT, DISASTER_TYPE::PEST];
}
else if ($this->hasGodBuff($godBuffs, GOD_BUFF_TYPE::PEST)) {
    // 有拭虫之神加持,不生成虫害灾害
    $disasterTypes = [DISASTER_TYPE::DROUGHT, DISASTER_TYPE::WEED];
}
else {
    // 没有神灵加持,所有灾害类型都可能生成
    $disasterTypes = [DISASTER_TYPE::DROUGHT, DISASTER_TYPE::PEST, DISASTER_TYPE::WEED];
}

4.5 灾害影响

灾害直接影响最终产量计算:

最终产量 = 基础产量 × (1 + 土地加成) × (1 + 房屋加成) × (1 - 灾害减产总和)

5. 收获系统

5.1 收获条件

作物必须处于成熟期才能收获。

5.2 产量计算

基础产量 = 随机(种子最小产量, 种子最大产量)
土地加成 = 根据土地类型确定的百分比
房屋加成 = 根据房屋等级确定的百分比
灾害减产 = 每个未处理的灾害减产5%

最终产量 = 基础产量 × (1 + 土地加成) × (1 + 房屋加成) × (1 - 灾害减产总和)

5.3 神灵加持对产量的影响

丰收之神加持可以确保收获时获得最高产量:

// 获取种子配置
$seedConfig = $this->seedRepository->find($crop->seed_id);

// 检查用户是否有丰收之神加持
$hasHarvestBuff = $this->godBuffRepository->hasActiveBuff($crop->user_id, GOD_BUFF_TYPE::HARVEST);

// 如果有丰收之神加持,直接使用最大产量
if ($hasHarvestBuff) {
    $baseOutput = $seedConfig->max_output;
} else {
    // 随机生成基础产量
    $baseOutput = rand($seedConfig->min_output, $seedConfig->max_output);
}

有丰收之神加持的用户在收获作物时,基础产量直接使用种子配置的最大产量值,而不是在最小产量和最大产量之间随机生成,这样可以确保获得最高的收益。

5.4 产量限制

  • 全局最低产量:1000
  • 有灾害时最高产量:2000
  • 全局最高产量:3000

如果计算结果低于最低产量,则按最低产量发放;如果超过最高产量,则按最高产量发放。

6. 数据结构

6.1 种子信息混合定义

种子信息采用混合定义方案,即在物品模块和农场模块中分别存储不同类型的种子属性:

6.1.1 混合定义原则

  1. 物品模块负责

    • 基本属性:名称、图标、描述、物品类型、堆叠上限等
    • 通用特殊属性:可能被其他模块使用的属性(如物品价值、UI分类等)
  2. 农场模块负责

    • 农场特有属性:生长时间、产量范围、灾害抵抗等
    • 种植相关的业务规则和计算逻辑

6.1.2 数据关联

通过在农场模块中添加关联字段,建立与物品模块的映射关系:

  • item_id:种子对应的物品ID
  • output_item_id:收获产出的物品ID

6.1.3 混合定义优势

  1. 职责分离:每个模块只负责自己领域的属性
  2. 数据一致性:避免数据重复和不一致
  3. 扩展性:各模块可以独立演化
  4. 性能优化:可以按需加载数据

6.2 种子表 (farm_seeds)

字段名 类型 说明
id bigint 主键ID
name varchar 种子名称
type tinyint 种子类型
item_id bigint 对应的物品ID
output_item_id bigint 产出的物品ID
seed_time int 种子期时间(秒)
sprout_time int 发芽期时间(秒)
growth_time int 生长期时间(秒)
min_output int 最小产出
max_output int 最大产出
disaster_resistance json 灾害抵抗
display_attributes json 显示属性
created_at timestamp 创建时间
updated_at timestamp 更新时间

6.3 作物表 (farm_crops)

字段名 类型 说明
id bigint 主键ID
land_id bigint 土地ID
seed_id bigint 种子ID
plant_time timestamp 种植时间
growth_stage tinyint 生长阶段
stage_end_time timestamp 阶段结束时间
disasters json 灾害情况
fertilized boolean 是否使用化肥
created_at timestamp 创建时间
updated_at timestamp 更新时间

6.4 收获记录表 (farm_harvest_logs)

字段名 类型 说明
id bigint 主键ID
user_id bigint 用户ID
land_id bigint 土地ID
crop_id bigint 作物ID
seed_id bigint 种子ID
output_amount int 产出数量
harvest_time timestamp 收获时间
created_at timestamp 创建时间

6.5 灾害JSON结构

{
  "disasters": [
    {"type": 1, "occurred_at": "2023-05-01 12:00:00"},
    {"type": 3, "occurred_at": "2023-05-01 14:30:00"}
  ]
}

6.6 索引设计

  • 主键索引:各表的id字段
  • 外键索引:land_idseed_iduser_id
  • 普通索引:growth_stage(加速查询特定生长阶段的作物)
  • 普通索引:stage_end_time(用于生长阶段更新检查)

7. 核心功能

7.1 种植作物

/**
 * 在指定土地上种植作物
 *
 * @param int $landId 土地ID
 * @param int $seedId 种子ID
 * @return FarmCrop 创建的作物对象
 * @throws LandNotEmptyException 土地非空异常
 * @throws SeedNotFoundException 种子不存在异常
 * @throws InsufficientSeedsException 种子不足异常
 */
public function plantCrop(int $landId, int $seedId): FarmCrop

7.2 使用化肥

/**
 * 对作物使用化肥
 *
 * @param int $cropId 作物ID
 * @param int $fertilizerId 化肥物品ID
 * @return bool 使用是否成功
 * @throws CropNotFertilizableException 作物不可施肥异常
 * @throws AlreadyFertilizedException 已经施肥异常
 * @throws InsufficientFertilizerException 化肥不足异常
 */
public function useFertilizer(int $cropId, int $fertilizerId): bool

7.3 处理灾害

/**
 * 处理作物灾害
 *
 * @param int $cropId 作物ID
 * @param int $disasterType 灾害类型
 * @param int $toolId 处理工具物品ID
 * @return bool 处理是否成功
 * @throws DisasterNotFoundException 灾害不存在异常
 * @throws InsufficientToolException 工具不足异常
 */
public function handleDisaster(int $cropId, int $disasterType, int $toolId): bool

7.4 收获作物

/**
 * 收获作物
 *
 * @param int $cropId 作物ID
 * @return int 收获数量
 * @throws CropNotMatureException 作物未成熟异常
 */
public function harvestCrop(int $cropId): int

7.5 铲除作物

/**
 * 铲除作物
 *
 * @param int $cropId 作物ID
 * @return bool 铲除是否成功
 */
public function removeCrop(int $cropId): bool

7.6 更新作物生长状态

/**
 * 更新作物生长状态
 * 此方法应由定时任务调用
 *
 * @return int 处理的作物数量
 */
public function updateCropGrowth(): int

7.7 生成灾害

/**
 * 为作物生成灾害
 * 此方法应由定时任务调用
 *
 * @return int 生成的灾害数量
 */
public function generateDisasters(): int

8. 业务逻辑

8.1 神秘种子处理

/**
 * 处理神秘种子的随机转化
 *
 * @param int $mysterySeedId 神秘种子ID
 * @return int 转化后的种子ID
 */
public function processMysterySeeds(int $mysterySeedId): int

8.2 产量计算

/**
 * 计算作物的最终产量
 *
 * @param FarmCrop $crop 作物对象
 * @param FarmLand $land 土地对象
 * @param int $houseLevel 房屋等级
 * @return int 最终产量
 */
public function calculateOutput(FarmCrop $crop, FarmLand $land, int $houseLevel): int

8.3 灾害生成概率计算

/**
 * 计算灾害生成概率
 *
 * @param FarmCrop $crop 作物对象
 * @param FarmLand $land 土地对象
 * @return float 灾害生成概率
 */
public function calculateDisasterProbability(FarmCrop $crop, FarmLand $land): float

8.4 生长阶段时间计算

/**
 * 计算各生长阶段的结束时间
 *
 * @param FarmSeed $seed 种子对象
 * @param DateTime $plantTime 种植时间
 * @return array 各阶段结束时间
 */
public function calculateGrowthStageEndTimes(FarmSeed $seed, DateTime $plantTime): array

9. 前端交互

9.1 作物展示

前端应展示作物的以下信息:

  • 作物类型(通过不同的视觉效果区分)
  • 当前生长阶段
  • 生长进度条
  • 预计成熟时间
  • 灾害情况(如果有)

9.2 作物操作

前端应提供以下作物操作:

  • 种植:选择种子种植在空闲土地上
  • 施肥:对生长中的作物使用化肥
  • 处理灾害:使用对应工具处理灾害
  • 收获:收获成熟的作物
  • 铲除:铲除枯萎的作物

10. 与其他系统的交互

10.1 与土地系统的交互

  • 土地状态影响作物的种植和生长
  • 土地类型影响作物的最终产量和灾害抵抗

10.2 与房屋系统的交互

  • 房屋等级影响作物的整体产出

10.3 与物品系统的交互

10.3.1 基本交互

  • 种植消耗物品系统中的种子
  • 收获添加物品到用户背包
  • 使用化肥和工具消耗物品

10.3.2 物品操作接口

农场模块需要调用物品模块的以下接口:

// 检查用户是否拥有足够的物品
$hasItem = $this->itemService->checkUserItem($userId, $itemId, $amount);

// 消耗用户物品
$result = $this->itemService->consumeUserItem($userId, $itemId, $amount, $reason);

// 添加物品到用户背包
$result = $this->itemService->addUserItem($userId, $itemId, $amount, $reason);

// 获取物品信息
$itemInfo = $this->itemService->getItemInfo($itemId);

10.3.3 种植作物示例

/**
 * 在指定土地上种植作物
 */
public function plantCrop(int $landId, int $seedId): array
{
    return DB::transaction(function () use ($landId, $seedId) {
        // 获取土地和种子信息
        $land = $this->landRepository->find($landId);
        $seedConfig = $this->seedRepository->find($seedId);
        $userId = $land->user_id;

        // 处理神秘种子
        if ($seedConfig->type == SEED_TYPE::MYSTERY) {
            $seedId = $this->seedService->processMysterySeeds($seedId);
            $seedConfig = $this->seedRepository->find($seedId);
        }

        // 检查用户是否拥有该种子物品
        $hasItem = $this->itemService->checkUserItem($userId, $seedConfig->item_id, 1);
        if (!$hasItem) {
            throw new SeedException("用户没有足够的种子");
        }

        // 消耗种子物品
        $this->itemService->consumeUserItem($userId, $seedConfig->item_id, 1, "种植作物");

        // 创建作物记录
        // ...

        return ['crop_id' => $crop->id, 'land_id' => $landId];
    });
}

10.3.4 收获作物示例

/**
 * 收获作物
 */
public function harvestCrop(int $cropId): array
{
    return DB::transaction(function () use ($cropId) {
        // 获取作物信息
        $crop = $this->cropRepository->find($cropId);
        $seedConfig = $this->seedRepository->find($crop->seed_id);
        $land = $this->landRepository->find($crop->land_id);
        $userId = $crop->user_id;

        // 验证作物状态
        // ...

        // 计算产出数量
        $outputAmount = $this->calculateOutput($crop, $land, $houseLevel);

        // 添加产出物品到用户背包
        $this->itemService->addUserItem(
            $userId,
            $seedConfig->output_item_id,
            $outputAmount,
            "收获作物"
        );

        // 记录收获日志
        // ...

        return [
            'output_item_id' => $seedConfig->output_item_id,
            'amount' => $outputAmount
        ];
    });
}

10.3.5 使用化肥示例

/**
 * 对作物使用化肥
 */
public function useFertilizer(int $cropId, int $fertilizerId): bool
{
    return DB::transaction(function () use ($cropId, $fertilizerId) {
        // 获取作物信息
        $crop = $this->cropRepository->find($cropId);
        $userId = $crop->user_id;

        // 验证作物状态
        // ...

        // 检查用户是否拥有该化肥物品
        $hasItem = $this->itemService->checkUserItem($userId, $fertilizerId, 1);
        if (!$hasItem) {
            throw new CropException("用户没有足够的化肥");
        }

        // 消耗化肥物品
        $this->itemService->consumeUserItem($userId, $fertilizerId, 1, "使用化肥");

        // 更新作物生长时间
        // ...

        return true;
    });
}

10.3.6 种子信息混合定义

种子信息采用混合定义方案,在物品模块和农场模块中分别存储不同类型的属性:

  1. 物品模块中存储

    • 种子的基本信息(名称、图标、描述等)
    • 种子的通用属性(物品类型、堆叠上限等)
    • 种子的UI展示属性(图标颜色、UI分类等)
  2. 农场模块中存储

    • 种子的生长时间(种子期、发芽期、生长期)
    • 种子的产量范围(最小产出、最大产出)
    • 种子的灾害抵抗属性
    • 种子与作物的关联关系
  3. 数据访问示例

    // 获取完整的种子信息
    public function getCompleteSeedInfo(int $seedId): array
    {
    // 获取农场模块中的种子信息
    $seedInfo = $this->seedRepository->find($seedId);
    
    // 获取物品模块中的种子物品信息
    $itemInfo = $this->itemService->getItemInfo($seedInfo->item_id);
    
    // 获取物品模块中的产出物品信息
    $outputItemInfo = $this->itemService->getItemInfo($seedInfo->output_item_id);
    
    // 合并信息返回
    return [
        'seed_id' => $seedInfo->id,
        'name' => $seedInfo->name,
        'growth_times' => [
            'seed' => $seedInfo->seed_time,
            'sprout' => $seedInfo->sprout_time,
            'growth' => $seedInfo->growth_time
        ],
        'output' => [
            'min' => $seedInfo->min_output,
            'max' => $seedInfo->max_output
        ],
        'item' => [
            'id' => $itemInfo['id'],
            'icon' => $itemInfo['icon'],
            'description' => $itemInfo['description']
        ],
        'output_item' => [
            'id' => $outputItemInfo['id'],
            'name' => $outputItemInfo['name']
        ]
    ];
    }
    

10.4 与宠物系统的交互

  • 宠物技能可以自动收菜、播种、处理灾害等

11. 扩展性考虑

11.1 季节系统

未来可以添加季节系统,不同季节适合种植不同作物:

  • 春季:草莓、玫瑰产量提升
  • 夏季:西瓜、辣椒产量提升
  • 秋季:南瓜、核桃产量提升
  • 冬季:人参、可可产量提升

11.2 新种子类型

系统设计预留了添加新种子类型的可能性,只需:

  1. 在SEED_TYPE枚举中添加新类型
  2. 在数据库中添加新种子配置
  3. 更新前端展示逻辑

11.3 特殊事件

未来可以添加特殊事件系统,如:

  • 丰收节:全局产量提升
  • 自然灾害:全局灾害概率提升
  • 种子节:特定种子产量提升