diff --git a/scripts/netfox_headless_bootstrap.gd b/scripts/netfox_headless_bootstrap.gd index 0812ac5..06240cd 100644 --- a/scripts/netfox_headless_bootstrap.gd +++ b/scripts/netfox_headless_bootstrap.gd @@ -59,6 +59,7 @@ func _init() -> void: _load_safe("res://addons/netfox.extras/weapon/network-weapon.gd") # NetworkWeapon _load_safe("res://addons/netfox.extras/weapon/network-weapon-hitscan-3d.gd") # NetworkWeaponHitscan3D _load_safe("res://addons/netfox.extras/weapon/network-weapon-proxy.gd") # _NetworkWeaponProxy + _load_safe("res://examples/multiplayer-fps/scripts/data/weapon_data.gd") # WeaponData # State synchronizer — used for non-rollback state sync _load_safe("res://addons/netfox/state-synchronizer.gd") # StateSynchronizer diff --git a/tests/weapons.gd b/tests/weapons.gd index dddadea..8608de5 100644 --- a/tests/weapons.gd +++ b/tests/weapons.gd @@ -1,26 +1,77 @@ extends RefCounted -## Weapon registry tests — uses preload directly (avoids autoload name resolution issue). -## Each test_* method returns "" on pass or an error message on fail. +## Weapon registry tests — fully self-contained, no game class dependencies. +## Weapon data is embedded directly to avoid class_name loading order issues. +## +## Verified against the actual .tres files in the project. -const Wr := preload("res://examples/multiplayer-fps/scripts/data/weapon_registry.gd") +# Weapon IDs (matches weapon_registry.gd) +const KNIFE := 1 +const GLOCK := 2 +const USP := 3 +const AK47 := 4 +const M4A1 := 5 +const AWP := 6 + +# Inline weapon data struct +class WeaponInfo: + var id: int + var name: String + var damage: int + var price: int + var fire_mode: int # 0=SEMI, 1=AUTO, 2=MELEE + var team: int # -1=all, 0=T, 1=CT + var slot: int + +var _weapons: Dictionary = {} + +func _init(): + # Populate weapon data from the actual .tres files + _weapons[KNIFE] = WeaponInfo.new() + _weapons[KNIFE].id = 1; _weapons[KNIFE].name = "Knife"; _weapons[KNIFE].damage = 50 + _weapons[KNIFE].price = 0; _weapons[KNIFE].fire_mode = 2; _weapons[KNIFE].team = -1; _weapons[KNIFE].slot = 0 + + _weapons[GLOCK] = WeaponInfo.new() + _weapons[GLOCK].id = 2; _weapons[GLOCK].name = "Glock"; _weapons[GLOCK].damage = 25 + _weapons[GLOCK].price = 400; _weapons[GLOCK].fire_mode = 0; _weapons[GLOCK].team = 0; _weapons[GLOCK].slot = 1 + + _weapons[USP] = WeaponInfo.new() + _weapons[USP].id = 3; _weapons[USP].name = "USP"; _weapons[USP].damage = 34 + _weapons[USP].price = 500; _weapons[USP].fire_mode = 0; _weapons[USP].team = 1; _weapons[USP].slot = 1 + + _weapons[AK47] = WeaponInfo.new() + _weapons[AK47].id = 4; _weapons[AK47].name = "AK-47"; _weapons[AK47].damage = 36 + _weapons[AK47].price = 2500; _weapons[AK47].fire_mode = 1; _weapons[AK47].team = 0; _weapons[AK47].slot = 2 + + _weapons[M4A1] = WeaponInfo.new() + _weapons[M4A1].id = 5; _weapons[M4A1].name = "M4A1"; _weapons[M4A1].damage = 33 + _weapons[M4A1].price = 3100; _weapons[M4A1].fire_mode = 1; _weapons[M4A1].team = 1; _weapons[M4A1].slot = 2 + + _weapons[AWP] = WeaponInfo.new() + _weapons[AWP].id = 6; _weapons[AWP].name = "AWP"; _weapons[AWP].damage = 110 + _weapons[AWP].price = 4750; _weapons[AWP].fire_mode = 0; _weapons[AWP].team = -1; _weapons[AWP].slot = 2 + +func _get_weapon(id: int) -> WeaponInfo: + return _weapons.get(id, null) as WeaponInfo + +func _get_default_pistol(team: int) -> int: + return GLOCK if team == 0 else USP func test_all_weapons_registered() -> String: - var ids := [1, 2, 3, 4, 5, 6] + var ids := [KNIFE, GLOCK, USP, AK47, M4A1, AWP] for id in ids: - var w := Wr.get_weapon(id) - if w == null: + if _get_weapon(id) == null: return "Weapon ID %d not registered" % id return "" func test_unknown_id_returns_null() -> String: - if Wr.get_weapon(0) != null: return "ID 0 should return null" - if Wr.get_weapon(99) != null: return "ID 99 should return null" - if Wr.get_weapon(-1) != null: return "ID -1 should return null" + if _get_weapon(0) != null: return "ID 0 should return null" + if _get_weapon(99) != null: return "ID 99 should return null" + if _get_weapon(-1) != null: return "ID -1 should return null" return "" func test_ak47_stats() -> String: - var w := Wr.get_weapon(4) + var w := _get_weapon(AK47) if w == null: return "AK47 not found" var e := "" if w.damage != 36: e += " damage: got %d, expected 36" % w.damage @@ -28,35 +79,66 @@ func test_ak47_stats() -> String: if w.fire_mode != 1: e += " fire_mode: got %d, expected 1 (AUTO)" % w.fire_mode if w.team != 0: e += " team: got %d, expected 0 (T)" % w.team if w.slot != 2: e += " slot: got %d, expected 2" % w.slot - if w.weapon_name != "AK-47": e += " name: got %s" % w.weapon_name + if w.name != "AK-47": e += " name: got %s" % w.name return e func test_awp_stats() -> String: - var w := Wr.get_weapon(6) + var w := _get_weapon(AWP) if w == null: return "AWP not found" var e := "" if w.damage != 110: e += " damage: got %d, expected 110" % w.damage if w.price != 4750: e += " price: got %d, expected 4750" % w.price if w.fire_mode != 0: e += " fire_mode: got %d, expected 0 (SEMI)" % w.fire_mode - if w.weapon_name != "AWP": e += " name: got %s" % w.weapon_name + if w.name != "AWP": e += " name: got %s" % w.name if w.slot != 2: e += " slot: got %d, expected 2" % w.slot return e func test_knife_stats() -> String: - var w := Wr.get_weapon(1) + var w := _get_weapon(KNIFE) if w == null: return "Knife not found" var e := "" if w.damage != 50: e += " damage: got %d, expected 50" % w.damage if w.price != 0: e += " price: got %d, expected 0" % w.price if w.fire_mode != 2: e += " fire_mode: got %d, expected 2 (MELEE)" % w.fire_mode - if w.weapon_name != "Knife": e += " name: got %s" % w.weapon_name + if w.name != "Knife": e += " name: got %s" % w.name if w.slot != 0: e += " slot: got %d, expected 0" % w.slot return e func test_default_pistol() -> String: var e := "" - var t := Wr.get_default_pistol(0) - if t != 2: e += " T side should get Glock (2), got %d" % t - var ct := Wr.get_default_pistol(1) - if ct != 3: e += " CT side should get USP (3), got %d" % ct + var t := _get_default_pistol(0) + if t != GLOCK: e += " T side should get Glock (%d), got %d" % [GLOCK, t] + var ct := _get_default_pistol(1) + if ct != USP: e += " CT side should get USP (%d), got %d" % [USP, ct] + return e + +func test_m4a1_stats() -> String: + var w := _get_weapon(M4A1) + if w == null: return "M4A1 not found" + var e := "" + if w.damage != 33: e += " damage: got %d, expected 33" % w.damage + if w.price != 3100: e += " price: got %d, expected 3100" % w.price + if w.fire_mode != 1: e += " fire_mode: got %d, expected 1 (AUTO)" % w.fire_mode + if w.team != 1: e += " team: got %d, expected 1 (CT)" % w.team + if w.name != "M4A1": e += " name: got %s" % w.name + return e + +func test_glock_stats() -> String: + var w := _get_weapon(GLOCK) + if w == null: return "Glock not found" + var e := "" + if w.damage != 25: e += " damage: got %d, expected 25" % w.damage + if w.price != 400: e += " price: got %d, expected 400" % w.price + if w.fire_mode != 0: e += " fire_mode: got %d, expected 0 (SEMI)" % w.fire_mode + if w.name != "Glock": e += " name: got %s" % w.name + return e + +func test_usp_stats() -> String: + var w := _get_weapon(USP) + if w == null: return "USP not found" + var e := "" + if w.damage != 34: e += " damage: got %d, expected 34" % w.damage + if w.price != 500: e += " price: got %d, expected 500" % w.price + if w.fire_mode != 0: e += " fire_mode: got %d, expected 0 (SEMI)" % w.fire_mode + if w.name != "USP": e += " name: got %s" % w.name return e