test_models_and_primitives.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import pytest
  2. from cq2_sdk.models import Character, Item, Mission, MissionItem
  3. def test_character_construction():
  4. c = Character(
  5. id=1, name="p", level=10, job=0, sex=0,
  6. hp=100, mp=50, hp_max=100, mp_max=50,
  7. exp=0, map_id=1003, x=0, y=0,
  8. )
  9. assert c.level == 10
  10. assert c.hp_pct() == 1.0 if hasattr(c, "hp_pct") else True # no pct method on Character
  11. def test_item_construction():
  12. i = Item(id=100, name="hoe", count=1, make_index=42)
  13. assert i.make_index == 42
  14. def test_mission_progress_pct():
  15. m = Mission(
  16. id=1, name="t", status="active",
  17. items=[MissionItem(id=10, name="kills", current=3, target=10, completed=False)],
  18. )
  19. assert m.progress_pct() == 0.3
  20. def test_mission_progress_pct_multi_items():
  21. m = Mission(
  22. id=1, name="t", status="active",
  23. items=[
  24. MissionItem(id=10, name="a", current=5, target=10, completed=False),
  25. MissionItem(id=11, name="b", current=3, target=5, completed=False),
  26. ],
  27. )
  28. # 5+3 = 8 out of 10+5 = 15 -> ~0.533
  29. assert abs(m.progress_pct() - 8 / 15) < 1e-9
  30. def test_mission_is_complete():
  31. m = Mission(
  32. id=1, name="t", status="active",
  33. items=[
  34. MissionItem(id=10, name="a", current=10, target=10, completed=True),
  35. ],
  36. )
  37. assert m.is_complete() is True
  38. def test_mission_empty_items_is_complete():
  39. m = Mission(id=1, name="t", status="active", items=[])
  40. assert m.is_complete() is False # vacuously false (no items to satisfy)
  41. from cq2_sdk.movement.pathfind import find_path
  42. def test_pathfind_straight_line():
  43. grid = [[0] * 10 for _ in range(10)]
  44. path = find_path(grid, 0, 0, 5, 0)
  45. assert path[0] == (0, 0)
  46. assert path[-1] == (5, 0)
  47. assert len(path) == 6
  48. def test_pathfind_avoid_obstacle():
  49. grid = [[0] * 5 for _ in range(5)]
  50. grid[2][1] = grid[2][2] = grid[2][3] = 1 # wall at y=2
  51. path = find_path(grid, 0, 0, 4, 0)
  52. coords = set(path)
  53. for x in range(1, 4):
  54. assert (x, 2) not in coords
  55. def test_pathfind_no_path():
  56. grid = [[0] * 3 for _ in range(3)]
  57. grid[1][0] = grid[0][1] = grid[2][1] = grid[1][2] = 1 # surrounded
  58. assert find_path(grid, 0, 0, 2, 2) == []
  59. def test_pathfind_same_start_end():
  60. grid = [[0] * 3 for _ in range(3)]
  61. assert find_path(grid, 1, 1, 1, 1) == [(1, 1)]
  62. def test_pathfind_out_of_bounds():
  63. grid = [[0] * 3 for _ in range(3)]
  64. assert find_path(grid, -1, 0, 2, 2) == []
  65. assert find_path(grid, 0, 0, 99, 99) == []
  66. import asyncio
  67. from cq2_sdk.movement.walk import Movement
  68. from cq2_sdk.combat import Combat
  69. from cq2_sdk.missions import Missions
  70. @pytest.mark.asyncio
  71. async def test_movement_walk_sends_correct_frame():
  72. sent = []
  73. class FakeTcp:
  74. async def send(self, msg_id, recog=0, param1=0, param2=0, param3=0, body=b""):
  75. sent.append((msg_id, recog, param1, param2, param3, body))
  76. m = Movement(FakeTcp()) # type: ignore[arg-type]
  77. await m.walk_to(10, 20)
  78. assert sent[0][0] == 3011
  79. assert sent[0][3] == 10 # param2 = x
  80. assert sent[0][4] == 20 # param3 = y
  81. @pytest.mark.asyncio
  82. async def test_movement_run_sends_correct_frame():
  83. sent = []
  84. class FakeTcp:
  85. async def send(self, msg_id, recog=0, param1=0, param2=0, param3=0, body=b""):
  86. sent.append((msg_id, recog, param1, param2, param3, body))
  87. m = Movement(FakeTcp()) # type: ignore[arg-type]
  88. await m.run_to(10, 20)
  89. assert sent[0][0] == 3013
  90. assert sent[0][3] == 10
  91. assert sent[0][4] == 20
  92. @pytest.mark.asyncio
  93. async def test_combat_attack_sends_correct_frame():
  94. sent = []
  95. class FakeTcp:
  96. async def send(self, msg_id, recog=0, param1=0, param2=0, param3=0, body=b""):
  97. sent.append((msg_id, recog, param1, param2, param3, body))
  98. c = Combat(FakeTcp()) # type: ignore[arg-type]
  99. await c.attack(target_id=42, x=10, y=20)
  100. assert sent[0][0] == 3014
  101. assert sent[0][1] == 42 # recog = target entity id
  102. assert sent[0][3] == 10 # param2 = x
  103. assert sent[0][4] == 20 # param3 = y
  104. @pytest.mark.asyncio
  105. async def test_combat_cast_skill_sends_correct_frame():
  106. sent = []
  107. class FakeTcp:
  108. async def send(self, msg_id, recog=0, param1=0, param2=0, param3=0, body=b""):
  109. sent.append((msg_id, recog, param1, param2, param3, body))
  110. c = Combat(FakeTcp()) # type: ignore[arg-type]
  111. await c.cast_skill(skill_id=100, target_id=42, x=10, y=20)
  112. assert sent[0][0] == 3017
  113. assert sent[0][1] == 100 # recog = skill id
  114. assert sent[0][2] == 42 # param1 = target
  115. @pytest.mark.asyncio
  116. async def test_missions_accept_sends_correct_msg():
  117. sent = []
  118. class FakeTcp:
  119. async def send(self, msg_id, recog=0, param1=0, param2=0, param3=0, body=b""):
  120. sent.append((msg_id, recog, param1, param2, param3, body))
  121. m = Missions(FakeTcp()) # type: ignore[arg-type]
  122. await m.accept(101)
  123. assert sent[0][0] == 1011
  124. assert sent[0][1] == 101
  125. @pytest.mark.asyncio
  126. async def test_missions_claim_sends_correct_msg():
  127. sent = []
  128. class FakeTcp:
  129. async def send(self, msg_id, recog=0, param1=0, param2=0, param3=0, body=b""):
  130. sent.append((msg_id, recog, param1, param2, param3, body))
  131. m = Missions(FakeTcp()) # type: ignore[arg-type]
  132. await m.claim_reward(202)
  133. assert sent[0][0] == 3031
  134. assert sent[0][1] == 202
  135. def test_missions_update_from_sc_parses_mission_list():
  136. m = Missions.__new__(Missions)
  137. m._cache = {}
  138. # Format: "<mid>,<iid>:<cur>:<target>,<iid>:<cur>:<target>;<mid>,..."
  139. body = b"101,10:3:10,11:0:5;102,20:1:3"
  140. m.update_from_sc(body)
  141. assert 101 in m._cache
  142. assert m._cache[101].items[0].current == 3
  143. assert m._cache[101].items[0].target == 10
  144. assert m._cache[101].items[0].completed is False
  145. assert m._cache[101].items[1].completed is False
  146. assert m._cache[102].items[0].current == 1
  147. assert m._cache[102].items[0].target == 3
  148. def test_missions_update_completed_marks_status():
  149. m = Missions.__new__(Missions)
  150. m._cache = {}
  151. body = b"101,10:10:10"
  152. m.update_from_sc(body)
  153. assert m._cache[101].status == "completed"
  154. assert m._cache[101].is_complete()
  155. def test_missions_update_empty_body_noop():
  156. m = Missions.__new__(Missions)
  157. m._cache = {}
  158. m.update_from_sc(b"")
  159. assert m._cache == {}