# test_npc_shop_parity.gd —— 验证方向 1:NPC 商店库存、金币购买与 1/5 折价出售 40250 1:1 对齐 extends SceneTree const NpcShopSystem = preload("res://npc_shop_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_npc_shop_parity (Direction 1: NPC Shop Buy/Sell 1:1) ===") test_npc_shop_stocks() test_sell_price_calculation() test_buy_flow_and_gold_deduction() test_buy_rejections() test_sell_flow_and_antiflag() print("\n=== SUMMARY: %d passed, %d failed ===" % [_passed, _failed]) if _failed > 0: printerr("TEST FAILED!") quit(1) else: print("ALL TESTS PASSED!") quit(0) func test_npc_shop_stocks() -> void: print("\n--- Test 1: NPC Shop Stock Configurations ---") _assert(NpcShopSystem.has_shop(9003), "General Store (9003) has shop registered") _assert(NpcShopSystem.has_shop(9001), "Weapon Shop (9001) has shop registered") _assert(NpcShopSystem.has_shop(9002), "Armor Shop (9002) has shop registered") _assert(not NpcShopSystem.has_shop(101), "Wild dog (101) does not have a shop") var general_items := NpcShopSystem.get_shop_items(9003) _assert(general_items.size() >= 10, "General Store has at least 10 basic stock items") _assert(general_items[0].vnum == 27001, "Slot 0 is Red Potion S (27001)") _assert(general_items[6].vnum == 22001, "Slot 6 is Town Scroll (22001)") _assert(general_items[9].vnum == 27400, "Slot 9 is Fishing Rod (27400)") func test_sell_price_calculation() -> void: print("\n--- Test 2: 40250 Official 1/5 Sell Price Calculation (shop.cpp:520) ---") # 1. Normal item with buy_price = 1000 -> sell_price = 1000 / 5 = 200 var normal_item := {"shop_buy_price": 1000, "anti_flags": 0} var sell_val := NpcShopSystem.calculate_sell_price(normal_item, 1) _assert(sell_val == 200, "1000 gold item sells for 200 gold (1/5 buy price)") # 2. Count = 5 -> sell_price = 200 * 5 = 1000 var sell_val_5 := NpcShopSystem.calculate_sell_price(normal_item, 5) _assert(sell_val_5 == 1000, "5 count sells for 1000 gold") # 3. Item with ITEM_ANTIFLAG_SELL cannot be sold var unsellable_item := {"shop_buy_price": 50000, "anti_flags": NpcShopSystem.ITEM_ANTIFLAG_SELL} var sell_unsellable := NpcShopSystem.calculate_sell_price(unsellable_item, 1) _assert(sell_unsellable == 0, "ITEM_ANTIFLAG_SELL item sells for 0 (forbidden)") func test_buy_flow_and_gold_deduction() -> void: print("\n--- Test 3: Buying Flow and Gold Deduction ---") var player_ctx := { "gold": 50000, "inventory": [] } # Buy 1 pack of Red Potion S from General Store (pos 0: 200 count, 1200 gold) var buy_res := NpcShopSystem.buy_item(0, 1, 9003, player_ctx) _assert(buy_res.ok and buy_res.code == "SUCCESS", "Buy succeeded") _assert(buy_res.cost == 1200, "Cost is 1200 gold") _assert(player_ctx.gold == 50000 - 1200, "Player gold deducted to 48800") _assert(player_ctx.inventory.size() == 1, "Item added to player inventory") _assert(player_ctx.inventory[0].vnum == 27001, "Added item is 27001") _assert(player_ctx.inventory[0].count == 200, "Added item has 200 count") func test_buy_rejections() -> void: print("\n--- Test 4: Buy Rejections (Insufficient Gold / Inventory Full) ---") # Insufficient Gold var poor_player := { "gold": 500, # Needs 1200 "inventory": [] } var res_poor := NpcShopSystem.buy_item(0, 1, 9003, poor_player) _assert(not res_poor.ok and res_poor.code == "NOT_ENOUGH_GOLD", "Rejected due to insufficient gold") _assert(poor_player.gold == 500, "Gold untouched") # Inventory Full var full_player := { "gold": 100000, "inventory": [] } for i in 90: full_player.inventory.append({"cell": i, "vnum": 10, "count": 1}) var res_full := NpcShopSystem.buy_item(0, 1, 9003, full_player) _assert(not res_full.ok and res_full.code == "INVENTORY_FULL", "Rejected due to inventory full") func test_sell_flow_and_antiflag() -> void: print("\n--- Test 5: Selling Flow & Anti-Flag Verification ---") var player_ctx := { "gold": 1000, "inventory": [ {"cell": 0, "vnum": 10, "count": 1}, # Sword +0 (buy_price: 1000 -> sells for 200) {"cell": 1, "vnum": 71085, "count": 2} # Scroll with anti-sell ] } var proto_mock := func(vnum: int) -> Dictionary: if vnum == 10: return {"shop_buy_price": 1000, "anti_flags": 0} elif vnum == 71085: return {"shop_buy_price": 50000, "anti_flags": NpcShopSystem.ITEM_ANTIFLAG_SELL} return {} # Sell unsellable item in cell 1 var res_unsellable := NpcShopSystem.sell_item(1, 1, player_ctx, proto_mock) _assert(not res_unsellable.ok and res_unsellable.code == "ITEM_ANTIFLAG_SELL", "ITEM_ANTIFLAG_SELL item sale rejected") # Sell valid item in cell 0 var res_sell := NpcShopSystem.sell_item(0, 1, player_ctx, proto_mock) _assert(res_sell.ok and res_sell.code == "SUCCESS", "Sale succeeded") _assert(res_sell.gold_earned == 200, "Earned 200 gold") _assert(player_ctx.gold == 1200, "Player gold increased to 1200") _assert(player_ctx.inventory.size() == 1, "Sold item removed from inventory") _assert(player_ctx.inventory[0].cell == 1, "Remaining item is cell 1")