# test_skill_book_reading_parity.gd —— 40250 大师技能书悟道与进阶 1:1 回归测试 extends SceneTree const SystemClass = preload("res://skill_book_reading_system.gd") var _passed: int = 0 var _failed: int = 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_skill_book_reading_parity (Batch 26 Direction 1) ===") _test_level_curve_and_book_requirements() _test_exp_and_cooldown_mechanics() _test_exorcism_and_hermit_scrolls() _test_failure_handling() _test_full_m1_to_g1_progression() _test_edge_cases_and_rejections() print("\n=== Result: %d Passed, %d Failed ===" % [_passed, _failed]) if _failed == 0: print("ALL TESTS PASSED!\n") else: printerr("SOME TESTS FAILED!\n") quit(0 if _failed == 0 else 1) func _test_level_curve_and_book_requirements() -> void: print("\n--- Test 1: Level Curve & 40250 Need Books Formula ---") var sys = SystemClass.new() sys.init_skill(1, 15) # Normal _assert(sys.get_skill_master_type(1) == SystemClass.SKILL_NORMAL, "Lv 15 is SKILL_NORMAL") sys.init_skill(1, 20) # M1 _assert(sys.get_skill_master_type(1) == SystemClass.SKILL_MASTER, "Lv 20 is SKILL_MASTER (M1)") _assert(sys.get_need_books_for_next_level(1) == 1, "M1 requires 1 book to M2") sys.init_skill(1, 21) # M2 _assert(sys.get_need_books_for_next_level(1) == 2, "M2 requires 2 books to M3") sys.init_skill(1, 24) # M5 _assert(sys.get_need_books_for_next_level(1) == 5, "M5 requires 5 books to M6") sys.init_skill(1, 29) # M10 _assert(sys.get_need_books_for_next_level(1) == 10, "M10 requires 10 books to G1") # Total books from M1 to G1: sum(1..10) = 55 var total_books: int = 0 for lvl in range(20, 30): sys.init_skill(1, lvl) total_books += sys.get_need_books_for_next_level(1) _assert(total_books == 55, "Total successful books needed for M1->G1 is exactly 55") func _test_exp_and_cooldown_mechanics() -> void: print("\n--- Test 2: EXP Consumption & 24h Cooldown ---") var sys = SystemClass.new(50000, 1000000) sys.init_skill(1, 20) # M1 # Insufficient EXP check sys.player_exp = 15000 var res1 = sys.learn_skill_by_book(1, 1, 1) # roll=1 (success) _assert(res1["error_code"] == "INSUFFICIENT_EXP", "Blocks reading when EXP < 20,000") _assert(sys.player_exp == 15000, "EXP not deducted when blocked") # Sufficient EXP check sys.player_exp = 50000 var res2 = sys.learn_skill_by_book(1, 1, 1) _assert(res2["error_code"] == "OK" and res2["success"] == true, "Reading succeeds with sufficient EXP") _assert(sys.player_exp == 30000, "20,000 EXP deducted on attempt (50000 -> 30000)") _assert(res2["level_up"] == true, "M1 leveled up to M2") # 24h Cooldown check (86400s) _assert(sys.skills[1]["next_read_time"] == 1000000 + 86400, "Next read time set to now + 86400") sys.current_time = 1000000 + 40000 # 40,000s passed, still < 86400 var res3 = sys.learn_skill_by_book(1, 1, 1) _assert(res3["error_code"] == "COOLDOWN_ACTIVE", "Reading blocked during 24h cooldown") func _test_exorcism_and_hermit_scrolls() -> void: print("\n--- Test 3: Exorcism Scroll (71001) & Hermit Advice (71094) ---") var sys = SystemClass.new(100000, 1000000) sys.init_skill(1, 21) # M2 (needs 2 books) sys.skills[1]["next_read_time"] = 1000000 + 50000 # In cooldown # Use Exorcism Scroll (71001) sys.use_exorcism_scroll() _assert(sys.has_affect(SystemClass.EXORCISM_SCROLL_VNUM) == true, "Exorcism affect active") # Reading while in cooldown with Exorcism var res = sys.learn_skill_by_book(1, 1, 1) _assert(res["error_code"] == "OK", "Exorcism scroll bypassed cooldown") _assert(res["consumed_exorcism"] == true, "Exorcism scroll consumed") _assert(sys.has_affect(SystemClass.EXORCISM_SCROLL_VNUM) == false, "Exorcism affect removed after read") # Use Hermit Advice (71094) sys.use_hermit_advice() _assert(sys.has_affect(SystemClass.HERMIT_ADVICE_VNUM) == true, "Hermit advice affect active") # Reading with roll=99 (which normally fails base 35%) sys.use_exorcism_scroll() var res2 = sys.learn_skill_by_book(1, 1, 99) _assert(res2["success"] == true, "Hermit advice forced 100% success rate on roll=99") _assert(res2["consumed_hermit"] == true, "Hermit advice consumed") _assert(sys.has_affect(SystemClass.HERMIT_ADVICE_VNUM) == false, "Hermit affect removed after read") func _test_failure_handling() -> void: print("\n--- Test 4: Reading Failure Penalty & Retention ---") var sys = SystemClass.new(100000, 1000000) sys.init_skill(1, 22, 1) # M3, read_count = 1 (needs 3 books) # Roll 90 without hermit advice -> Failure var res = sys.learn_skill_by_book(1, 1, 90) _assert(res["success"] == false, "Reading failed on roll 90") _assert(res["error_code"] == "READ_FAIL", "Error code is READ_FAIL") _assert(sys.skills[1]["level"] == 22, "Skill level remains unchanged at 22") _assert(sys.skills[1]["read_count"] == 1, "Skill read_count preserved at 1") _assert(sys.player_exp == 80000, "EXP still deducted on failure (20,000 EXP)") _assert(sys.skills[1]["next_read_time"] == 1000000 + 86400, "Cooldown applied even on failure") func _test_full_m1_to_g1_progression() -> void: print("\n--- Test 5: Full M1 to G1 Progression (55 Books) ---") var sys = SystemClass.new(5000000, 1000000) sys.init_skill(1, 20, 0) # Start at M1 (Lv 20) var books_succeeded: int = 0 var final_res: Dictionary = {} # Loop through all 55 books with 100% success using scrolls for step in range(55): sys.use_exorcism_scroll() sys.use_hermit_advice() final_res = sys.learn_skill_by_book(1, 1) _assert(final_res["success"] == true, "Book %d read succeeded" % (step + 1)) books_succeeded += 1 _assert(books_succeeded == 55, "Successfully read 55 books") _assert(sys.skills[1]["level"] == 30, "Skill level reached 30 (G1)") _assert(sys.get_skill_master_type(1) == SystemClass.SKILL_GRAND_MASTER, "Skill tier is SKILL_GRAND_MASTER") _assert(final_res["breakthrough_g"] == true, "Final book triggered breakthrough_g flag") func _test_edge_cases_and_rejections() -> void: print("\n--- Test 6: Edge Cases & Rejections ---") var sys = SystemClass.new(100000, 1000000) # 1. Unknown skill var r1 = sys.learn_skill_by_book(999, 999) _assert(r1["error_code"] == "NOT_LEARNABLE", "Reject unregistered skill") # 2. Skill mismatch on book socket sys.init_skill(1, 20) var r2 = sys.learn_skill_by_book(1, 2) # Socket says skill 2, but reading for skill 1 _assert(r2["error_code"] == "SKILL_MISMATCH", "Reject skill book with mismatched socket") # 3. Normal tier skill (< 20) sys.init_skill(2, 10) var r3 = sys.learn_skill_by_book(2, 2) _assert(r3["error_code"] == "NOT_MASTER_TIER", "Reject normal tier skill (< 20)") # 4. G1 tier skill (>= 30) sys.init_skill(3, 30) var r4 = sys.learn_skill_by_book(3, 3) _assert(r4["error_code"] == "ALREADY_G_OR_P", "Reject Grandmaster tier skill (>= 30)")