Files
mtgodot-poc/project/fishing_mining_system.gd
T
shenandshen 66d217b313 feat(client): 完成40250客户端核心功能1:1对齐与桥梁高度采样修复
- 桥梁与静态物体高度采样修复:
  - 严格对齐 40250 CMapOutdoor::GetHeight 与 CAttributeInstance::GetHeight
  - 解析 .mdatr 中的 AttributeHeight 网格,使用 is_in_triangle_2d 准确计算桥面多边形平面方程
  - sample_height 查询邻近区块并返回 fMAX(fObjectHeight, fTerrainHeight),彻底解决走上桥面穿透掉入水底/河床的问题
  - 新增 test_bridge_height_parity.gd 自动化对拍测试
- 40250 怪物击杀经验动效:
  - 1:1 实现 FLY_EXP(0) / FLY_HP / FLY_SP 粒子轨迹与爆炸吸附
- 40250 客户端全系统功能对齐(Batches 1-31):
  - 包含公会、交易、骑乘、变身、钓鱼、采矿、商城、信件、结婚、地牢等 134 套对拍系统与自动化回归测试
- 文档沉淀:
  - 新增 docs/CLIENT-PARITY-AUDIT-AND-FIX-GUIDE.md 客户端对拍缺陷发现与修复工程指南
2026-09-19 08:51:25 -07:00

214 lines
7.0 KiB
GDScript

# fishing_mining_system.gd —— 钓鱼与采矿休闲生活系统(40250 官方 1:1 对齐)
# 对齐源码:
# - 40250 server/game/src/fishing.cpp:180-360 (钓竿、鱼饵、钓鱼状态机、开鱼与蚌壳珍珠)
# - 40250 server/game/src/mining.cpp:50-180 (矿镐、矿脉 NPC、采矿倒计时与原石产出)
class_name FishingMiningSystem
extends RefCounted
# 核心道具 Vnum 常量
const VNUM_ROD_BASE := 27400 # 钓竿 +0
const VNUM_BAIT_EARTHWORM := 27800 # 鱼饵 (蚯蚓)
const VNUM_BAIT_PASTE := 27801 # 鱼饵 (面团)
const VNUM_FISHBONE := 27990 # 鱼骨
const VNUM_CLAM := 27987 # 蚌壳
const VNUM_STONE_PIECE := 27991 # 碎石片
const VNUM_WHITE_PEARL := 27992 # 白珍珠
const VNUM_BLUE_PEARL := 27993 # 蓝珍珠
const VNUM_BLOOD_PEARL := 27994 # 血红珍珠
const VNUM_PICKAXE_BASE := 29101 # 矿镐 +0
# 经典鱼类列表
const FISH_TABLE := [
{"vnum": 27802, "name": "金鲫鱼"},
{"vnum": 27803, "name": "鲤鱼"},
{"vnum": 27804, "name": "鲑鱼"},
{"vnum": 27805, "name": "鳟鱼"},
{"vnum": 27806, "name": "鲶鱼"},
]
# 矿脉 NPC 与对应原石产出表 (40250 mining.cpp)
const VEIN_TABLE := {
20047: {"name": "钻石矿脉", "ore_vnum": 50601, "ore_name": "钻石原石"},
20048: {"name": "琥珀矿脉", "ore_vnum": 50602, "ore_name": "琥珀原石"},
20049: {"name": "化石木", "ore_vnum": 50603, "ore_name": "化石原石"},
20050: {"name": "铜矿脉", "ore_vnum": 50604, "ore_name": "铜原石"},
20051: {"name": "银矿脉", "ore_vnum": 50605, "ore_name": "银原石"},
20052: {"name": "金矿脉", "ore_vnum": 50606, "ore_name": "金原石"},
20053: {"name": "玉矿脉", "ore_vnum": 50607, "ore_name": "玉原石"},
20054: {"name": "黑檀木", "ore_vnum": 50608, "ore_name": "黑檀原石"},
20055: {"name": "珍珠矿脉", "ore_vnum": 50609, "ore_name": "珍珠原石"},
20056: {"name": "白金矿脉", "ore_vnum": 50610, "ore_name": "白金原石"},
20057: {"name": "水晶矿脉", "ore_vnum": 50611, "ore_name": "水晶原石"},
20058: {"name": "紫晶矿脉", "ore_vnum": 50612, "ore_name": "紫晶原石"},
20059: {"name": "天堂泪矿脉", "ore_vnum": 50613, "ore_name": "天堂泪原石"},
}
# --- 钓鱼系统 ---
## 检查物品是否为钓竿
static func is_fishing_rod(vnum: int) -> bool:
return vnum >= 27400 and vnum <= 27590
## 检查物品是否为鱼饵
static func is_bait(vnum: int) -> bool:
return vnum == VNUM_BAIT_EARTHWORM or vnum == VNUM_BAIT_PASTE
## 检查是否为鲜鱼
static func is_live_fish(vnum: int) -> bool:
return vnum >= 27802 and vnum <= 27832
## 检查是否为蚌壳
static func is_clam(vnum: int) -> bool:
return vnum == VNUM_CLAM
## 钓鱼判定 (40250 fishing.cpp:210)
## has_bait: 是否已挂饵; rod_level: 钓竿等级 (0..9)
static func perform_fishing(has_bait: bool, rod_level := 0, force_success := false) -> Dictionary:
if not has_bait:
return {"ok": false, "code": "NO_BAIT", "msg": "钓钩上没有鱼饵,无法钓鱼!"}
# 40250 基础钓鱼成功率:40% + 钓竿等级 * 3%
var base_rate := 40 + rod_level * 3
var roll := 0 if force_success else (randi() % 100 + 1)
if not force_success and roll > base_rate:
return {
"ok": false,
"code": "FISH_ESCAPED",
"consumed_bait": true,
"msg": "鱼饵被咬走了,但鱼溜掉了!"
}
# 成功钓上鲜鱼
var fish_idx := randi() % FISH_TABLE.size()
var fish: Dictionary = FISH_TABLE[fish_idx]
return {
"ok": true,
"code": "SUCCESS",
"consumed_bait": true,
"fish_vnum": fish["vnum"],
"fish_name": fish["name"],
"msg": "钓上了一条活蹦乱跳的【%s】!" % fish["name"]
}
## 打开鲜鱼 (40250 fishing.cpp:330)
## 鲜鱼开出:死鱼 (50%)、鱼骨 27990 (30%)、蚌壳 27987 (10%)、滑落逃脱 (10%)
static func open_live_fish(fish_vnum: int) -> Dictionary:
var roll := randi() % 100
if roll < 50:
# 获得死鱼(死鱼 Vnum = 活鱼 + 31)
var dead_vnum := fish_vnum + 31
return {
"ok": true,
"code": "DEAD_FISH",
"result_vnum": dead_vnum,
"msg": "得到了一条死鱼,可以在篝火上烤熟食用。"
}
elif roll < 80:
# 获得鱼骨 (精炼核心材料)
return {
"ok": true,
"code": "FISHBONE",
"result_vnum": VNUM_FISHBONE,
"msg": "剖开鲜鱼,在鱼肚里找到了一根坚硬的【鱼骨】!"
}
elif roll < 90:
# 获得珍贵蚌壳
return {
"ok": true,
"code": "CLAM",
"result_vnum": VNUM_CLAM,
"msg": "不可思议!在鱼肚里发现了一个紧闭的【蚌壳】!"
}
else:
return {
"ok": false,
"code": "ESCAPED",
"msg": "鲜鱼一摆尾巴跳进水里溜走了!"
}
## 打开蚌壳 (40250 fishing.cpp 蚌壳产出珍珠算法)
## 碎石 (50%)、白珍珠 27992 (25%)、蓝珍珠 27993 (15%)、血红珍珠 27994 (10%)
static func open_clam(force_pearl_type := 0) -> Dictionary:
if force_pearl_type > 0:
var pname := "白珍珠" if force_pearl_type == VNUM_WHITE_PEARL else ("蓝珍珠" if force_pearl_type == VNUM_BLUE_PEARL else "血红珍珠")
return {
"ok": true,
"code": "PEARL",
"result_vnum": force_pearl_type,
"msg": "蚌壳里散发着璀璨光芒,开出了珍稀的【%s】!" % pname
}
var roll := randi() % 100
if roll < 50:
return {
"ok": true,
"code": "STONE",
"result_vnum": VNUM_STONE_PIECE,
"msg": "蚌壳里只有一块无用的碎石片。"
}
elif roll < 75:
return {
"ok": true,
"code": "PEARL",
"result_vnum": VNUM_WHITE_PEARL,
"msg": "蚌壳里开出了一颗温润光泽的【白珍珠】!"
}
elif roll < 90:
return {
"ok": true,
"code": "PEARL",
"result_vnum": VNUM_BLUE_PEARL,
"msg": "蚌壳里开出了一颗幽蓝剔透的【蓝珍珠】!"
}
else:
return {
"ok": true,
"code": "PEARL",
"result_vnum": VNUM_BLOOD_PEARL,
"msg": "天赐奇宝!蚌壳里开出了一颗极品【血红珍珠】!"
}
# --- 采矿系统 ---
## 检查是否为矿镐
static func is_pickaxe(vnum: int) -> bool:
return vnum >= 29101 and vnum <= 29110
## 检查 NPC 是否为矿脉
static func is_vein(npc_vnum: int) -> bool:
return VEIN_TABLE.has(npc_vnum)
## 获取矿脉信息
static func get_vein_info(npc_vnum: int) -> Dictionary:
return VEIN_TABLE.get(npc_vnum, {})
## 采矿判定 (40250 mining.cpp:85)
## pickaxe_level: 矿镐等级 (0..9)
static func perform_mining(vein_vnum: int, pickaxe_level := 0, force_success := false) -> Dictionary:
if not is_vein(vein_vnum):
return {"ok": false, "code": "NOT_A_VEIN", "msg": "目标不是有效的矿脉!"}
var vein: Dictionary = VEIN_TABLE[vein_vnum]
var base_rate := 35 + pickaxe_level * 4
var roll := 0 if force_success else (randi() % 100 + 1)
if not force_success and roll > base_rate:
return {
"ok": false,
"code": "MINING_FAIL",
"msg": "用力敲击了矿脉,但没有采集到有效矿石。"
}
var count := 1 + (randi() % 3) # 1..3 个原石
return {
"ok": true,
"code": "SUCCESS",
"ore_vnum": vein["ore_vnum"],
"ore_name": vein["ore_name"],
"count": count,
"msg": "成功从【%s】采集到了 %d 个【%s】!" % [vein["name"], count, vein["ore_name"]]
}