Files
mtgodot-poc/project/test_horse_level_growth_parity.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

180 lines
7.0 KiB
GDScript

# test_horse_level_growth_parity.gd —— 40250 官方马匹等级进化与养殖喂食系统 1:1 回归测试套件
extends SceneTree
const HorseLevelGrowthSystem = preload("res://horse_level_growth_system.gd")
var _passed := 0
var _failed := 0
func _assert(cond: bool, msg: String) -> void:
if cond:
_passed += 1
print(" [PASS] %s" % msg)
else:
_failed += 1
printerr(" [FAIL] %s" % msg)
func _init() -> void:
print("\n=== Running test_horse_level_growth_parity (Direction 2: Horse Level Growth 1:1) ===")
_test_horse_tiers_and_leveling()
_test_feeding_food_tier_matching()
_test_horse_death_and_herb_revive()
_test_mount_dismount_and_stamina()
_test_serialization()
_finish()
func _test_horse_tiers_and_leveling() -> void:
print("\n--- Test 1: Horse Tiers & Level Progression ---")
var sys := HorseLevelGrowthSystem.new()
sys.initialize_horse(1)
_assert(sys.horse_level == 1, "Initialized at level 1")
_assert(sys.get_tier() == HorseLevelGrowthSystem.TIER_BEGINNER, "Tier is Beginner")
_assert(sys.get_tier_name() == "初级小马", "Tier name is 初级小马")
# Upgrade to Lv 11 with char level 25 (Combat horse requires 35)
sys.horse_level = 10
var res_low = sys.upgrade_horse_level(25)
_assert(res_low["ok"] == false, "Upgrade rejected: char level 25 < 35")
_assert(res_low["reason"] == "CHAR_LEVEL_TOO_LOW", "Reason is CHAR_LEVEL_TOO_LOW")
# Upgrade with char level 35
var res_combat = sys.upgrade_horse_level(35)
_assert(res_combat["ok"] == true, "Upgraded to Lv 11")
_assert(sys.horse_level == 11, "Horse level is 11")
_assert(sys.get_tier() == HorseLevelGrowthSystem.TIER_COMBAT, "Tier is Combat")
_assert(sys.get_tier_name() == "战斗装甲马", "Tier name is 战斗装甲马")
# Upgrade to Lv 21 with char level 45 (Military horse requires 50)
sys.horse_level = 20
var res_low2 = sys.upgrade_horse_level(45)
_assert(res_low2["ok"] == false, "Upgrade rejected: char level 45 < 50")
# Upgrade with char level 50
var res_mil = sys.upgrade_horse_level(50)
_assert(res_mil["ok"] == true, "Upgraded to Lv 21")
_assert(sys.horse_level == 21, "Horse level is 21")
_assert(sys.get_tier() == HorseLevelGrowthSystem.TIER_MILITARY, "Tier is Military")
_assert(sys.get_tier_name() == "军阶战马", "Tier name is 军阶战马")
func _test_feeding_food_tier_matching() -> void:
print("\n--- Test 2: Feeding Food Tier Matching (Hay, Carrot, Ginseng) ---")
var sys := HorseLevelGrowthSystem.new()
sys.initialize_horse(1) # Beginner horse
sys.current_health = 1 # Lower health so we can feed
var inv: Array = [
{"vnum": HorseLevelGrowthSystem.VNUM_HAY, "name": "干草", "count": 2},
{"vnum": HorseLevelGrowthSystem.VNUM_CARROT, "name": "胡萝卜", "count": 2},
{"vnum": HorseLevelGrowthSystem.VNUM_RED_GINSENG, "name": "红参", "count": 2}
]
# 1. Beginner horse fed Carrot -> mismatch
var feed_mismatch = sys.feed_horse(inv, 1)
_assert(feed_mismatch["ok"] == false, "Feeding Carrot to Beginner horse rejected")
_assert(feed_mismatch["reason"] == "FOOD_TIER_MISMATCH", "Reason is FOOD_TIER_MISMATCH")
# 2. Beginner horse fed Hay -> success
var feed_succ = sys.feed_horse(inv, 0)
_assert(feed_succ["ok"] == true, "Feeding Hay to Beginner horse succeeded")
_assert(sys.current_health == 3, "Health increased to 3")
_assert(inv[0]["count"] == 1, "Hay count decremented to 1")
# 3. Combat horse fed Carrot
sys.initialize_horse(15)
sys.current_health = 10
var feed_combat = sys.feed_horse(inv, 1)
_assert(feed_combat["ok"] == true, "Feeding Carrot to Combat horse succeeded")
# 4. Military horse fed Red Ginseng
sys.initialize_horse(25)
sys.current_health = 20
var feed_mil = sys.feed_horse(inv, 2)
_assert(feed_mil["ok"] == true, "Feeding Red Ginseng to Military horse succeeded")
func _test_horse_death_and_herb_revive() -> void:
print("\n--- Test 3: Horse Death & Ape Herb Revive ---")
var sys := HorseLevelGrowthSystem.new()
sys.initialize_horse(1) # Beginner horse
sys.current_health = 0 # Dead/Fainted
var inv: Array = [
{"vnum": HorseLevelGrowthSystem.VNUM_HERB_NORMAL, "name": "中级猴草", "count": 1},
{"vnum": HorseLevelGrowthSystem.VNUM_HERB_EASY, "name": "初级猴草", "count": 1}
]
# Cannot mount dead horse
var player := {"attack_power": 100, "defense": 100}
var mount_dead = sys.mount_horse(player)
_assert(mount_dead["ok"] == false, "Cannot mount dead horse")
_assert(mount_dead["reason"] == "HORSE_IS_DEAD", "Reason is HORSE_IS_DEAD")
# Revive with wrong herb (Normal herb on Beginner horse)
var revive_wrong = sys.revive_horse(inv, 0)
_assert(revive_wrong["ok"] == false, "Wrong herb tier rejected")
_assert(revive_wrong["reason"] == "HERB_TIER_MISMATCH", "Reason is HERB_TIER_MISMATCH")
# Revive with correct Easy Ape herb
var revive_sig := {"fired": false, "hp": 0}
sys.horse_revived.connect(func(h: int):
revive_sig["fired"] = true
revive_sig["hp"] = h
)
var revive_ok = sys.revive_horse(inv, 1)
_assert(revive_ok["ok"] == true, "Horse successfully revived with Easy Ape herb")
_assert(sys.current_health > 0, "Current health > 0")
_assert(inv[1] == null, "Herb consumed")
_assert(revive_sig["fired"] == true, "horse_revived signal emitted")
func _test_mount_dismount_and_stamina() -> void:
print("\n--- Test 4: Mount, Dismount, & Stamina Depletion ---")
var sys := HorseLevelGrowthSystem.new()
sys.initialize_horse(21) # Military horse Lv 21: atk 84, def 50
var player := {"attack_power": 500, "defense": 200}
# Mount
var mount_res = sys.mount_horse(player)
_assert(mount_res["ok"] == true, "Mounted military horse")
_assert(sys.is_mounted == true, "is_mounted is true")
_assert(player["attack_power"] == 584, "Attack power increased 500 -> 584 (+84)")
_assert(player["defense"] == 250, "Defense increased 200 -> 250 (+50)")
# Consume stamina until exhausted
var exhaust_sig := {"fired": false}
sys.stamina_exhausted.connect(func():
exhaust_sig["fired"] = true
)
sys.consume_stamina(999, player)
_assert(exhaust_sig["fired"] == true, "stamina_exhausted signal emitted")
_assert(sys.is_mounted == false, "Auto-dismounted on exhaustion")
_assert(player["attack_power"] == 500, "Attack power reversed back to 500")
_assert(player["defense"] == 200, "Defense reversed back to 200")
func _test_serialization() -> void:
print("\n--- Test 5: Serialization & Restoration ---")
var sys1 := HorseLevelGrowthSystem.new()
sys1.initialize_horse(25)
sys1.current_health = 30
sys1.current_stamina = 80
sys1.is_mounted = true
var data = sys1.serialize()
var sys2 := HorseLevelGrowthSystem.new()
sys2.deserialize(data)
_assert(sys2.horse_level == 25, "Restored horse level 25")
_assert(sys2.get_tier_name() == "军阶战马", "Restored tier military")
_assert(sys2.current_health == 30, "Restored health 30")
_assert(sys2.current_stamina == 80, "Restored stamina 80")
_assert(sys2.is_mounted == true, "Restored mounted state")
func _finish() -> void:
print("\n=======================================================")
print("Horse Level Growth Parity Tests: %d Passed, %d Failed" % [_passed, _failed])
print("=======================================================\n")
if _failed > 0:
quit(1)
else:
quit(0)