create-dubhe 1.2.0-pre.99 β†’ 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +16 -16
  4. package/templates/101/sui-template/docker-compose.testnet.yml +73 -0
  5. package/templates/101/sui-template/packages/client/src/app/page.tsx +545 -41
  6. package/templates/101/sui-template/packages/client/src/app/providers.tsx +12 -4
  7. package/templates/101/sui-template/packages/contracts/Pub.localnet.toml +4 -4
  8. package/templates/101/sui-template/packages/contracts/counter.lock.json +20 -0
  9. package/templates/101/sui-template/packages/contracts/deployment.ts +8 -3
  10. package/templates/101/sui-template/packages/contracts/dubhe.config.json +59 -13
  11. package/templates/101/sui-template/packages/contracts/dubhe.config.ts +1 -4
  12. package/templates/101/sui-template/packages/contracts/metadata.json +1015 -68
  13. package/templates/101/sui-template/packages/contracts/package.json +7 -4
  14. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/dapp_key.move +45 -31
  15. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/error.move +8 -0
  16. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/genesis.move +30 -13
  17. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/init_test.move +19 -14
  18. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2.move +198 -0
  19. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2withkey.move +90 -0
  20. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/resources/value.move +30 -20
  21. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
  22. package/templates/101/sui-template/packages/contracts/src/counter/sources/scripts/deploy_hook.move +6 -11
  23. package/templates/101/sui-template/packages/contracts/src/counter/sources/systems/counter.move +23 -11
  24. package/templates/101/sui-template/packages/contracts/src/counter/sources/tests/counter.move +15 -14
  25. package/templates/101/sui-template/packages/contracts/src/dubhe/Move.lock +33 -30
  26. package/templates/101/sui-template/packages/contracts/src/dubhe/Move.toml +4 -3
  27. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/dapp_key.move +45 -31
  28. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/error.move +170 -0
  29. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move +16 -13
  30. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/init_test.move +8 -14
  31. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/account_key.move +1 -3
  32. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/dapp_service.move +1776 -137
  33. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/data_key.move +1 -20
  34. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/events.move +864 -25
  35. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/scripts/deploy_hook.move +15 -16
  36. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move +0 -3
  37. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move +25 -17
  38. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +2802 -343
  39. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/utils.move +1 -1
  40. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/address.move +173 -55
  41. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/dapp_test.move +769 -0
  42. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/fee_test.move +2965 -0
  43. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/integration_test.move +303 -0
  44. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/listing_test.move +1422 -0
  45. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
  46. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/session_test.move +624 -0
  47. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/storage_test.move +700 -0
  48. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/typed_object_test.move +177 -0
  49. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +452 -0
  50. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/user_storage_test.move +160 -0
  51. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/utils/math.move +4 -3
  52. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/utils/type_info.move +2 -2
  53. package/templates/contract/sui-template/dubhe.config.ts +0 -1
  54. package/templates/contract/sui-template/package.json +3 -3
  55. package/templates/contract/sui-template/src/dubhe/Move.lock +33 -30
  56. package/templates/contract/sui-template/src/dubhe/Move.toml +4 -3
  57. package/templates/contract/sui-template/src/dubhe/sources/codegen/dapp_key.move +45 -31
  58. package/templates/contract/sui-template/src/dubhe/sources/codegen/error.move +170 -0
  59. package/templates/contract/sui-template/src/dubhe/sources/codegen/genesis.move +16 -13
  60. package/templates/contract/sui-template/src/dubhe/sources/codegen/init_test.move +8 -14
  61. package/templates/contract/sui-template/src/dubhe/sources/core/account_key.move +1 -3
  62. package/templates/contract/sui-template/src/dubhe/sources/core/dapp_service.move +1776 -137
  63. package/templates/contract/sui-template/src/dubhe/sources/core/data_key.move +1 -20
  64. package/templates/contract/sui-template/src/dubhe/sources/core/events.move +864 -25
  65. package/templates/contract/sui-template/src/dubhe/sources/scripts/deploy_hook.move +15 -16
  66. package/templates/contract/sui-template/src/dubhe/sources/scripts/migrate.move +0 -3
  67. package/templates/contract/sui-template/src/dubhe/sources/systems/address_system.move +25 -17
  68. package/templates/contract/sui-template/src/dubhe/sources/systems/dapp_system.move +2802 -343
  69. package/templates/contract/sui-template/src/dubhe/sources/systems/utils.move +1 -1
  70. package/templates/contract/sui-template/src/dubhe/sources/tests/address.move +173 -55
  71. package/templates/contract/sui-template/src/dubhe/sources/tests/dapp_test.move +769 -0
  72. package/templates/contract/sui-template/src/dubhe/sources/tests/fee_test.move +2965 -0
  73. package/templates/contract/sui-template/src/dubhe/sources/tests/integration_test.move +303 -0
  74. package/templates/contract/sui-template/src/dubhe/sources/tests/listing_test.move +1422 -0
  75. package/templates/contract/sui-template/src/dubhe/sources/tests/scene_session_test.move +269 -0
  76. package/templates/contract/sui-template/src/dubhe/sources/tests/session_test.move +624 -0
  77. package/templates/contract/sui-template/src/dubhe/sources/tests/storage_test.move +700 -0
  78. package/templates/contract/sui-template/src/dubhe/sources/tests/typed_object_test.move +177 -0
  79. package/templates/contract/sui-template/src/dubhe/sources/tests/typed_scene_test.move +452 -0
  80. package/templates/contract/sui-template/src/dubhe/sources/tests/user_storage_test.move +160 -0
  81. package/templates/contract/sui-template/src/dubhe/sources/utils/math.move +4 -3
  82. package/templates/contract/sui-template/src/dubhe/sources/utils/type_info.move +2 -2
  83. package/templates/contract/sui-template/src/template/Move.toml +12 -0
  84. package/templates/contract/sui-template/src/template/sources/codegen/dapp_key.move +47 -0
  85. package/templates/contract/sui-template/src/template/sources/codegen/genesis.move +34 -0
  86. package/templates/contract/sui-template/src/template/sources/codegen/init_test.move +23 -0
  87. package/templates/contract/sui-template/src/template/sources/codegen/user_storage_init.move +27 -0
  88. package/templates/contract/sui-template/src/template/sources/scripts/deploy_hook.move +13 -0
  89. package/templates/contract/sui-template/src/template/sources/scripts/migrate.move +7 -0
  90. package/templates/nextjs/sui-card-duel/.gitignore_ +51 -0
  91. package/templates/nextjs/sui-card-duel/README.md +213 -0
  92. package/templates/nextjs/sui-card-duel/docker-compose.yml +34 -0
  93. package/templates/nextjs/sui-card-duel/mprocs.yaml +17 -0
  94. package/templates/nextjs/sui-card-duel/package.json +43 -0
  95. package/templates/nextjs/sui-card-duel/packages/client/.gitignore_ +48 -0
  96. package/templates/nextjs/sui-card-duel/packages/client/.prettierignore +1 -0
  97. package/templates/nextjs/sui-card-duel/packages/client/.prettierrc.json +8 -0
  98. package/templates/nextjs/sui-card-duel/packages/client/eslint.config.mjs +22 -0
  99. package/templates/nextjs/sui-card-duel/packages/client/next.config.mjs +8 -0
  100. package/templates/nextjs/sui-card-duel/packages/client/package.json +47 -0
  101. package/templates/nextjs/sui-card-duel/packages/client/postcss.config.mjs +8 -0
  102. package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/ArenaPanel.tsx +307 -0
  103. package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/page.tsx +622 -0
  104. package/templates/nextjs/sui-card-duel/packages/client/src/app/brawl/[id]/page.tsx +559 -0
  105. package/templates/nextjs/sui-card-duel/packages/client/src/app/collection/page.tsx +202 -0
  106. package/templates/nextjs/sui-card-duel/packages/client/src/app/components/CardView.tsx +66 -0
  107. package/templates/nextjs/sui-card-duel/packages/client/src/app/components/GameShell.tsx +294 -0
  108. package/templates/nextjs/sui-card-duel/packages/client/src/app/components/icons.tsx +101 -0
  109. package/templates/nextjs/sui-card-duel/packages/client/src/app/duel/[id]/page.tsx +477 -0
  110. package/templates/nextjs/sui-card-duel/packages/client/src/app/favicon.ico +0 -0
  111. package/templates/nextjs/sui-card-duel/packages/client/src/app/globals.css +85 -0
  112. package/templates/nextjs/sui-card-duel/packages/client/src/app/hooks/useGame.ts +313 -0
  113. package/templates/nextjs/sui-card-duel/packages/client/src/app/layout.tsx +24 -0
  114. package/templates/nextjs/sui-card-duel/packages/client/src/app/leaderboard/page.tsx +123 -0
  115. package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/game.ts +77 -0
  116. package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/scenes.ts +226 -0
  117. package/templates/nextjs/sui-card-duel/packages/client/src/app/market/page.tsx +418 -0
  118. package/templates/nextjs/sui-card-duel/packages/client/src/app/page.tsx +427 -0
  119. package/templates/nextjs/sui-card-duel/packages/client/src/app/providers.tsx +69 -0
  120. package/templates/nextjs/sui-card-duel/packages/client/tailwind.config.ts +21 -0
  121. package/templates/nextjs/sui-card-duel/packages/client/tsconfig.json +30 -0
  122. package/templates/nextjs/sui-card-duel/packages/contracts/.gitignore_ +51 -0
  123. package/templates/nextjs/sui-card-duel/packages/contracts/.prettierignore +1 -0
  124. package/templates/nextjs/sui-card-duel/packages/contracts/.prettierrc.json +8 -0
  125. package/templates/nextjs/sui-card-duel/packages/contracts/Dockerfile +5 -0
  126. package/templates/nextjs/sui-card-duel/packages/contracts/Pub.localnet.toml +13 -0
  127. package/templates/nextjs/sui-card-duel/packages/contracts/card_duel.lock.json +66 -0
  128. package/templates/nextjs/sui-card-duel/packages/contracts/docker-compose.yml +34 -0
  129. package/templates/nextjs/sui-card-duel/packages/contracts/dubhe.config.ts +150 -0
  130. package/templates/nextjs/sui-card-duel/packages/contracts/logs/combined.log +31383 -0
  131. package/templates/nextjs/sui-card-duel/packages/contracts/package.json +49 -0
  132. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/config.ts +17 -0
  133. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-brawl.ts +270 -0
  134. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-duel.ts +272 -0
  135. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-lobby.ts +177 -0
  136. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-market.ts +219 -0
  137. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-session.ts +342 -0
  138. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/query-dapp-revenue.ts +95 -0
  139. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/seed.ts +92 -0
  140. package/templates/nextjs/sui-card-duel/packages/contracts/scripts/withdraw-dapp-revenue.ts +56 -0
  141. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/Move.toml +15 -0
  142. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/dapp_key.move +46 -0
  143. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/card_kind.move +47 -0
  144. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/match_state.move +37 -0
  145. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/rarity.move +37 -0
  146. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/error.move +126 -0
  147. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/genesis.move +33 -0
  148. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/init_test.move +23 -0
  149. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/objects/arena.move +137 -0
  150. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/brawl_permit.move +125 -0
  151. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/duel_permit.move +125 -0
  152. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/battle_state.move +255 -0
  153. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/card.move +282 -0
  154. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/deck.move +64 -0
  155. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/game_config.move +267 -0
  156. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/gold.move +205 -0
  157. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/profile.move +255 -0
  158. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/brawl.move +313 -0
  159. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/duel.move +313 -0
  160. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/user_storage_init.move +26 -0
  161. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/deploy_hook.move +32 -0
  162. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/migrate.move +7 -0
  163. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/arena_system.move +57 -0
  164. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/brawl_system.move +559 -0
  165. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/card_system.move +146 -0
  166. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/duel_system.move +515 -0
  167. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/market_system.move +115 -0
  168. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/player_system.move +37 -0
  169. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/brawl_tests.move +385 -0
  170. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/duel_tests.move +656 -0
  171. package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/test_helpers.move +31 -0
  172. package/templates/nextjs/sui-card-duel/packages/contracts/sui-indexer-schema.graphql +5973 -0
  173. package/templates/nextjs/sui-card-duel/pnpm-workspace.yaml +2 -0
  174. package/templates/nextjs/sui-farm/.gitignore_ +51 -0
  175. package/templates/nextjs/sui-farm/README.md +15 -0
  176. package/templates/nextjs/sui-farm/UPGRADE_NOTES.md +97 -0
  177. package/templates/nextjs/sui-farm/docker-compose.yml +34 -0
  178. package/templates/nextjs/sui-farm/mprocs.yaml +17 -0
  179. package/templates/nextjs/sui-farm/package.json +43 -0
  180. package/templates/nextjs/sui-farm/packages/client/.gitignore_ +48 -0
  181. package/templates/nextjs/sui-farm/packages/client/.prettierignore +1 -0
  182. package/templates/nextjs/sui-farm/packages/client/.prettierrc.json +8 -0
  183. package/templates/nextjs/sui-farm/packages/client/eslint.config.mjs +22 -0
  184. package/templates/nextjs/sui-farm/packages/client/next.config.mjs +8 -0
  185. package/templates/nextjs/sui-farm/packages/client/package.json +47 -0
  186. package/templates/nextjs/sui-farm/packages/client/postcss.config.mjs +8 -0
  187. package/templates/nextjs/sui-farm/packages/client/src/app/admin/page.tsx +618 -0
  188. package/templates/nextjs/sui-farm/packages/client/src/app/components/FarmLand.tsx +526 -0
  189. package/templates/nextjs/sui-farm/packages/client/src/app/components/FarmPlotCard.tsx +215 -0
  190. package/templates/nextjs/sui-farm/packages/client/src/app/components/PetAvatar.tsx +868 -0
  191. package/templates/nextjs/sui-farm/packages/client/src/app/components/PetPanel.tsx +1360 -0
  192. package/templates/nextjs/sui-farm/packages/client/src/app/components/PlantModal.tsx +147 -0
  193. package/templates/nextjs/sui-farm/packages/client/src/app/components/ProxyCard.tsx +684 -0
  194. package/templates/nextjs/sui-farm/packages/client/src/app/components/RanchLand.tsx +822 -0
  195. package/templates/nextjs/sui-farm/packages/client/src/app/components/ResourceHUD.tsx +125 -0
  196. package/templates/nextjs/sui-farm/packages/client/src/app/components/ShopPanel.tsx +232 -0
  197. package/templates/nextjs/sui-farm/packages/client/src/app/components/icons/GameIcons.tsx +574 -0
  198. package/templates/nextjs/sui-farm/packages/client/src/app/favicon.ico +0 -0
  199. package/templates/nextjs/sui-farm/packages/client/src/app/globals.css +423 -0
  200. package/templates/nextjs/sui-farm/packages/client/src/app/hooks/useWorldPermitId.ts +66 -0
  201. package/templates/nextjs/sui-farm/packages/client/src/app/layout.tsx +23 -0
  202. package/templates/nextjs/sui-farm/packages/client/src/app/leaderboard/page.tsx +185 -0
  203. package/templates/nextjs/sui-farm/packages/client/src/app/lib/crops.ts +97 -0
  204. package/templates/nextjs/sui-farm/packages/client/src/app/market/page.tsx +763 -0
  205. package/templates/nextjs/sui-farm/packages/client/src/app/page.tsx +1173 -0
  206. package/templates/nextjs/sui-farm/packages/client/src/app/providers.tsx +69 -0
  207. package/templates/nextjs/sui-farm/packages/client/src/app/state/index.tsx +5 -0
  208. package/templates/nextjs/sui-farm/packages/client/tailwind.config.ts +25 -0
  209. package/templates/nextjs/sui-farm/packages/client/tsconfig.json +30 -0
  210. package/templates/nextjs/sui-farm/packages/contracts/.gitignore_ +51 -0
  211. package/templates/nextjs/sui-farm/packages/contracts/.prettierignore +1 -0
  212. package/templates/nextjs/sui-farm/packages/contracts/.prettierrc.json +8 -0
  213. package/templates/nextjs/sui-farm/packages/contracts/Dockerfile +5 -0
  214. package/templates/nextjs/sui-farm/packages/contracts/Pub.localnet.toml +13 -0
  215. package/templates/nextjs/sui-farm/packages/contracts/counter.lock.json +14 -0
  216. package/templates/nextjs/sui-farm/packages/contracts/docker-compose.yml +34 -0
  217. package/templates/nextjs/sui-farm/packages/contracts/dubhe.config.ts +178 -0
  218. package/templates/nextjs/sui-farm/packages/contracts/harvest.lock.json +120 -0
  219. package/templates/nextjs/sui-farm/packages/contracts/package.json +48 -0
  220. package/templates/nextjs/sui-farm/packages/contracts/scripts/config.ts +17 -0
  221. package/templates/nextjs/sui-farm/packages/contracts/scripts/query-dapp-revenue.ts +95 -0
  222. package/templates/nextjs/sui-farm/packages/contracts/scripts/read-permit.ts +72 -0
  223. package/templates/nextjs/sui-farm/packages/contracts/scripts/seed.ts +85 -0
  224. package/templates/nextjs/sui-farm/packages/contracts/scripts/withdraw-dapp-revenue.ts +56 -0
  225. package/templates/nextjs/sui-farm/packages/contracts/src/counter/Move.toml +14 -0
  226. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/dapp_key.move +31 -0
  227. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/error.move +10 -0
  228. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/genesis.move +33 -0
  229. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/init_test.move +23 -0
  230. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/resources/counter1.move +64 -0
  231. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/resources/counter2.move +70 -0
  232. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
  233. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/scripts/deploy_hook.move +8 -0
  234. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/scripts/migrate.move +8 -0
  235. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/systems/counter.move +13 -0
  236. package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/tests/counter.move +28 -0
  237. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/Move.toml +14 -0
  238. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/dapp_key.move +46 -0
  239. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/crop_type.move +47 -0
  240. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/egg_type.move +42 -0
  241. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/pet_rarity.move +37 -0
  242. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/pet_species.move +52 -0
  243. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/error.move +110 -0
  244. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/genesis.move +33 -0
  245. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/init_test.move +23 -0
  246. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/permits/world.move +125 -0
  247. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/carrot.move +136 -0
  248. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/carrot_seed.move +80 -0
  249. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/common_egg.move +136 -0
  250. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/corn.move +136 -0
  251. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/corn_seed.move +80 -0
  252. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/crow_charges.move +162 -0
  253. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/crow_damage.move +207 -0
  254. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/farm_plot.move +261 -0
  255. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/gold.move +80 -0
  256. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet.move +467 -0
  257. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_config.move +372 -0
  258. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_hatch.move +162 -0
  259. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_slot_index.move +83 -0
  260. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_slots.move +64 -0
  261. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/profile.move +162 -0
  262. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pumpkin.move +136 -0
  263. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pumpkin_seed.move +80 -0
  264. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/rare_egg.move +136 -0
  265. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/scarecrow.move +64 -0
  266. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/season_config.move +197 -0
  267. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/season_stats.move +64 -0
  268. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/seasonal_egg.move +136 -0
  269. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/shop_config.move +267 -0
  270. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/trophy.move +245 -0
  271. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/wheat.move +136 -0
  272. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/wheat_seed.move +80 -0
  273. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/world_permit_id.move +64 -0
  274. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/user_storage_init.move +26 -0
  275. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/scripts/deploy_hook.move +45 -0
  276. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/scripts/migrate.move +7 -0
  277. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/crow_system.move +96 -0
  278. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/farm_system.move +182 -0
  279. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/market_system.move +172 -0
  280. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/pet_system.move +495 -0
  281. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/season_system.move +70 -0
  282. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/shop_system.move +112 -0
  283. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/world_system.move +34 -0
  284. package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/tests/farm_tests.move +1192 -0
  285. package/templates/nextjs/sui-farm/pnpm-workspace.yaml +2 -0
  286. package/templates/nextjs/sui-template/packages/client/src/app/components/ProxyCard.tsx +683 -0
  287. package/templates/nextjs/sui-template/packages/client/src/app/page.tsx +510 -5
  288. package/templates/nextjs/sui-template/packages/client/src/app/providers.tsx +13 -5
  289. package/templates/nextjs/sui-template/packages/contracts/Pub.localnet.toml +4 -4
  290. package/templates/nextjs/sui-template/packages/contracts/counter.lock.json +14 -0
  291. package/templates/nextjs/sui-template/packages/contracts/deployment.ts +8 -3
  292. package/templates/nextjs/sui-template/packages/contracts/dubhe.config.json +9 -9
  293. package/templates/nextjs/sui-template/packages/contracts/dubhe.config.ts +0 -4
  294. package/templates/nextjs/sui-template/packages/contracts/metadata.json +180 -124
  295. package/templates/nextjs/sui-template/packages/contracts/package.json +8 -4
  296. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/dapp_key.move +45 -31
  297. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/error.move +10 -0
  298. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/genesis.move +30 -13
  299. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/init_test.move +19 -14
  300. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter1.move +30 -20
  301. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2.move +35 -24
  302. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
  303. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/scripts/deploy_hook.move +6 -10
  304. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/systems/counter.move +7 -20
  305. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/tests/counter.move +14 -27
  306. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.lock +33 -30
  307. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.toml +4 -3
  308. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/dapp_key.move +45 -31
  309. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/error.move +170 -0
  310. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move +16 -13
  311. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/init_test.move +8 -14
  312. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/account_key.move +1 -3
  313. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/dapp_service.move +1776 -137
  314. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/data_key.move +1 -20
  315. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/events.move +864 -25
  316. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/deploy_hook.move +15 -16
  317. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move +0 -3
  318. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move +25 -17
  319. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +2802 -343
  320. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/utils.move +1 -1
  321. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/address.move +173 -55
  322. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/dapp_test.move +769 -0
  323. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/fee_test.move +2965 -0
  324. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/integration_test.move +303 -0
  325. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/listing_test.move +1422 -0
  326. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
  327. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/session_test.move +624 -0
  328. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/storage_test.move +700 -0
  329. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_object_test.move +177 -0
  330. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +452 -0
  331. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/user_storage_test.move +160 -0
  332. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/utils/math.move +4 -3
  333. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/utils/type_info.move +2 -2
  334. package/templates/101/sui-template/packages/contracts/src/counter/Move.lock +0 -41
  335. package/templates/101/sui-template/packages/contracts/src/counter/Published.toml +0 -9
  336. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/errors.move +0 -12
  337. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/errors.move +0 -140
  338. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
  339. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
  340. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
  341. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
  342. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/dapp_store.move +0 -29
  343. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/session_system.move +0 -44
  344. package/templates/contract/sui-template/src/dubhe/sources/codegen/errors.move +0 -140
  345. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
  346. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
  347. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
  348. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
  349. package/templates/contract/sui-template/src/dubhe/sources/core/dapp_store.move +0 -29
  350. package/templates/contract/sui-template/src/dubhe/sources/systems/session_system.move +0 -44
  351. package/templates/nextjs/sui-template/packages/contracts/src/counter/Move.lock +0 -47
  352. package/templates/nextjs/sui-template/packages/contracts/src/counter/Published.toml +0 -9
  353. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/errors.move +0 -12
  354. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/errors.move +0 -140
  355. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
  356. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
  357. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
  358. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
  359. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/dapp_store.move +0 -29
  360. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/session_system.move +0 -44
@@ -0,0 +1,559 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Brawl page β€” multiplayer free-for-all inside a SceneStorage<Brawl>.
5
+ *
6
+ * Demonstrates:
7
+ * - open-invitation ScenePermit (join/leave while the room is open)
8
+ * - turn rotation across N players with eliminations
9
+ * - attacking ANY alive player via reactive writes (pick a target)
10
+ * - last-player-standing pot settlement
11
+ */
12
+
13
+ import { useState, useEffect, useCallback, useRef, use } from 'react';
14
+ import { motion } from 'framer-motion';
15
+ import { toast } from 'sonner';
16
+
17
+ import { useGame } from '../../hooks/useGame';
18
+ import { GameShell } from '../../components/GameShell';
19
+ import { CardView } from '../../components/CardView';
20
+ import { IconGold, IconSkull } from '../../components/icons';
21
+ import { fetchBrawlById, fetchArenaObjectId, type BrawlScene } from '../../lib/scenes';
22
+ import {
23
+ STATE_WAITING,
24
+ STATE_ACTIVE,
25
+ STATE_FINISHED,
26
+ isAttackKind,
27
+ shortAddr
28
+ } from '../../lib/game';
29
+ import { ZERO_ADDRESS } from '@0xobelisk/graphql-client';
30
+
31
+ const CLOCK = '0x6';
32
+ const MAX_HP = 30n;
33
+
34
+ interface PlayerVitals {
35
+ hp: bigint;
36
+ shield: bigint;
37
+ matchId: string;
38
+ }
39
+
40
+ export default function BrawlPage({ params }: { params: Promise<{ id: string }> }) {
41
+ const { id: sceneId } = use(params);
42
+ const game = useGame(5000);
43
+ // In-match actions are session-friendly: contracts resolve the player
44
+ // identity from UserStorage's canonical owner, so execTx silently signs
45
+ // with the session key when one is active.
46
+ const {
47
+ account,
48
+ contract,
49
+ ecsWorld,
50
+ graphqlClient,
51
+ player,
52
+ userStorageId,
53
+ storageId,
54
+ pkg,
55
+ execTx,
56
+ isLoading
57
+ } = game;
58
+
59
+ const [brawl, setBrawl] = useState<BrawlScene | null>(null);
60
+ const [vitals, setVitals] = useState<Record<string, PlayerVitals>>({});
61
+ const [arenaId, setArenaId] = useState<string | null>(null);
62
+ const [target, setTarget] = useState<string | null>(null);
63
+ const [notFound, setNotFound] = useState(false);
64
+ const storageIdCache = useRef<Record<string, string>>({});
65
+
66
+ const me = account?.address ?? '';
67
+
68
+ // ── Polling (2.5 s) ─────────────────────────────────────────────────────────
69
+
70
+ const fetchMatch = useCallback(async () => {
71
+ if (!graphqlClient) return;
72
+ try {
73
+ const b = await fetchBrawlById(graphqlClient, sceneId);
74
+ if (!b) {
75
+ setNotFound(true);
76
+ return;
77
+ }
78
+ setBrawl(b);
79
+ } catch (err) {
80
+ console.error('[brawl] fetch error:', err);
81
+ }
82
+ }, [graphqlClient, sceneId]);
83
+
84
+ useEffect(() => {
85
+ fetchMatch();
86
+ const interval = setInterval(fetchMatch, 2500);
87
+ return () => clearInterval(interval);
88
+ }, [fetchMatch]);
89
+
90
+ // Vitals of every player (battle_state per address)
91
+ const playersKey = brawl?.players.join(',') ?? '';
92
+ useEffect(() => {
93
+ if (!ecsWorld || !brawl) return;
94
+ let cancelled = false;
95
+ const poll = async () => {
96
+ const entries = await Promise.all(
97
+ brawl.players.map(async (addr) => {
98
+ const b = await ecsWorld
99
+ .getComponent<{ matchId: string; hp: string; shield: string }>(addr, 'battleState')
100
+ .catch(() => null);
101
+ return [
102
+ addr,
103
+ b
104
+ ? { hp: BigInt(b.hp ?? 0), shield: BigInt(b.shield ?? 0), matchId: String(b.matchId) }
105
+ : { hp: 0n, shield: 0n, matchId: ZERO_ADDRESS }
106
+ ] as const;
107
+ })
108
+ );
109
+ if (!cancelled) setVitals(Object.fromEntries(entries));
110
+ };
111
+ poll();
112
+ const interval = setInterval(poll, 2500);
113
+ return () => {
114
+ cancelled = true;
115
+ clearInterval(interval);
116
+ };
117
+ // eslint-disable-next-line react-hooks/exhaustive-deps
118
+ }, [ecsWorld, sceneId, playersKey]);
119
+
120
+ useEffect(() => {
121
+ if (!graphqlClient) return;
122
+ fetchArenaObjectId(graphqlClient)
123
+ .then(setArenaId)
124
+ .catch(() => {});
125
+ }, [graphqlClient]);
126
+
127
+ const resolveStorageId = useCallback(
128
+ async (addr: string): Promise<string> => {
129
+ if (storageIdCache.current[addr]) return storageIdCache.current[addr];
130
+ if (!contract) throw new Error('Contract not ready');
131
+ const id = await contract.getUserStorageId(addr);
132
+ storageIdCache.current[addr] = id;
133
+ return id;
134
+ },
135
+ [contract]
136
+ );
137
+
138
+ // ── Actions ─────────────────────────────────────────────────────────────────
139
+
140
+ const simpleCall = (fn: string, msg: string, extra: 'clock' | 'none' = 'none') => {
141
+ if (!brawl) return;
142
+ execTx((tx) => {
143
+ if (!userStorageId) throw new Error('UserStorage not found');
144
+ const args = [
145
+ tx.object(storageId),
146
+ tx.object(userStorageId),
147
+ tx.object(brawl.permitId),
148
+ tx.object(brawl.sceneId)
149
+ ];
150
+ if (extra === 'clock') args.push(tx.object(CLOCK));
151
+ tx.moveCall({ target: `${pkg}::brawl_system::${fn}`, arguments: args });
152
+ }, msg);
153
+ };
154
+
155
+ const handleStart = () => {
156
+ if (!brawl) return;
157
+ execTx((tx) => {
158
+ if (!userStorageId) throw new Error('UserStorage not found');
159
+ tx.moveCall({
160
+ target: `${pkg}::brawl_system::start_brawl`,
161
+ arguments: [
162
+ tx.object(storageId),
163
+ tx.object(userStorageId),
164
+ tx.object(brawl.permitId),
165
+ tx.object(brawl.sceneId),
166
+ tx.object(CLOCK)
167
+ ]
168
+ });
169
+ }, 'Brawl started β€” fight!');
170
+ };
171
+
172
+ const playCard = async (cardId: string, kind: number) => {
173
+ if (!brawl) return;
174
+ if (isAttackKind(kind)) {
175
+ if (!target) {
176
+ toast.error('Pick a target first (click an opponent panel)');
177
+ return;
178
+ }
179
+ let targetStorageId: string;
180
+ try {
181
+ targetStorageId = await resolveStorageId(target);
182
+ } catch {
183
+ toast.error('Could not resolve target storage');
184
+ return;
185
+ }
186
+ execTx((tx) => {
187
+ if (!userStorageId) throw new Error('UserStorage not found');
188
+ tx.moveCall({
189
+ target: `${pkg}::brawl_system::brawl_attack`,
190
+ arguments: [
191
+ tx.object(storageId),
192
+ tx.object(userStorageId),
193
+ tx.object(targetStorageId),
194
+ tx.object(brawl.permitId),
195
+ tx.object(brawl.sceneId),
196
+ tx.pure.address(cardId),
197
+ tx.object(CLOCK)
198
+ ]
199
+ });
200
+ }, `Attacked ${shortAddr(target)}!`);
201
+ } else {
202
+ execTx((tx) => {
203
+ if (!userStorageId) throw new Error('UserStorage not found');
204
+ tx.moveCall({
205
+ target: `${pkg}::brawl_system::brawl_defend`,
206
+ arguments: [
207
+ tx.object(storageId),
208
+ tx.object(userStorageId),
209
+ tx.object(brawl.permitId),
210
+ tx.object(brawl.sceneId),
211
+ tx.pure.address(cardId),
212
+ tx.object(CLOCK)
213
+ ]
214
+ });
215
+ }, 'Defense played!');
216
+ }
217
+ };
218
+
219
+ const handleTimeoutKick = async () => {
220
+ if (!brawl) return;
221
+ const stalling = brawl.alive[brawl.turnIndex];
222
+ let targetStorageId: string;
223
+ try {
224
+ targetStorageId = await resolveStorageId(stalling);
225
+ } catch {
226
+ toast.error('Could not resolve stalling player storage');
227
+ return;
228
+ }
229
+ execTx((tx) => {
230
+ if (!userStorageId) throw new Error('UserStorage not found');
231
+ tx.moveCall({
232
+ target: `${pkg}::brawl_system::brawl_timeout_kick`,
233
+ arguments: [
234
+ tx.object(storageId),
235
+ tx.object(userStorageId),
236
+ tx.object(targetStorageId),
237
+ tx.object(brawl.permitId),
238
+ tx.object(brawl.sceneId),
239
+ tx.object(CLOCK)
240
+ ]
241
+ });
242
+ }, `Kicked ${shortAddr(stalling)} for stalling!`);
243
+ };
244
+
245
+ const handleFinish = () => {
246
+ if (!brawl) return;
247
+ if (!arenaId) {
248
+ toast.error('Arena object not resolved yet');
249
+ return;
250
+ }
251
+ execTx((tx) => {
252
+ if (!userStorageId) throw new Error('UserStorage not found');
253
+ tx.moveCall({
254
+ target: `${pkg}::brawl_system::finish_brawl`,
255
+ arguments: [
256
+ tx.object(storageId),
257
+ tx.object(userStorageId),
258
+ tx.object(brawl.permitId),
259
+ tx.object(brawl.sceneId),
260
+ tx.object(arenaId)
261
+ ]
262
+ });
263
+ }, 'Pot collected β€” last one standing!');
264
+ };
265
+
266
+ const handleLeaveFinished = () => {
267
+ if (!brawl) return;
268
+ execTx((tx) => {
269
+ if (!userStorageId) throw new Error('UserStorage not found');
270
+ tx.moveCall({
271
+ target: `${pkg}::brawl_system::leave_finished_brawl`,
272
+ arguments: [tx.object(brawl.permitId), tx.object(brawl.sceneId), tx.object(userStorageId)]
273
+ });
274
+ }, 'Left the brawl.');
275
+ };
276
+
277
+ // ── Render ──────────────────────────────────────────────────────────────────
278
+
279
+ if (notFound) {
280
+ return (
281
+ <GameShell game={game}>
282
+ <CenterNote text="Brawl not found (it may have been cleaned up)." backHref="/" />
283
+ </GameShell>
284
+ );
285
+ }
286
+ if (!brawl) {
287
+ return (
288
+ <GameShell game={game}>
289
+ <CenterNote text="Loading brawl…" />
290
+ </GameShell>
291
+ );
292
+ }
293
+
294
+ const isHost = brawl.host === me;
295
+ const joined = brawl.players.includes(me);
296
+ const amAlive = brawl.alive.includes(me);
297
+ const currentTurnAddr = brawl.alive[brawl.turnIndex] ?? ZERO_ADDRESS;
298
+ const myTurn = brawl.state === STATE_ACTIVE && currentTurnAddr === me;
299
+ const iWon = brawl.state === STATE_FINISHED && brawl.winner === me;
300
+ const potClaimed = brawl.state === STATE_FINISHED && brawl.winner === ZERO_ADDRESS;
301
+
302
+ return (
303
+ <GameShell game={game}>
304
+ <div className="max-w-5xl mx-auto space-y-6">
305
+ {/* Status banner */}
306
+ <div className="flex items-center justify-between bg-slate-950/80 border-2 border-purple-800 rounded-2xl px-5 py-3 flex-wrap gap-2">
307
+ <div className="flex items-center gap-2">
308
+ <IconSkull size={22} />
309
+ <div>
310
+ <p className="font-pixel text-purple-200 text-xs">
311
+ Brawl Β· {brawl.players.length}/{brawl.maxPlayers} players Β·{' '}
312
+ {brawl.state === STATE_WAITING
313
+ ? 'Room open'
314
+ : brawl.state === STATE_ACTIVE
315
+ ? myTurn
316
+ ? 'YOUR TURN'
317
+ : `${shortAddr(currentTurnAddr)}'s turn`
318
+ : 'Finished'}
319
+ </p>
320
+ <p className="text-purple-600 text-[11px] mt-0.5 flex items-center gap-1">
321
+ Pot <IconGold size={11} /> {brawl.gold.toString()} Β· entry{' '}
322
+ {brawl.entryFee.toString()} Β· round {brawl.round}
323
+ </p>
324
+ </div>
325
+ </div>
326
+
327
+ <div className="flex gap-2 flex-wrap">
328
+ {brawl.state === STATE_WAITING && isHost && (
329
+ <>
330
+ <button
331
+ onClick={handleStart}
332
+ disabled={isLoading || brawl.players.length < 2}
333
+ className="px-4 py-1.5 bg-purple-700 hover:bg-purple-600 text-white text-[10px] font-pixel rounded-lg disabled:opacity-40 transition-colors"
334
+ >
335
+ Start ({brawl.players.length} in)
336
+ </button>
337
+ <button
338
+ onClick={() => simpleCall('cancel_brawl', 'Room cancelled, fee refunded.')}
339
+ disabled={isLoading || brawl.players.length > 1}
340
+ title={brawl.players.length > 1 ? 'Players must leave first' : undefined}
341
+ className="px-3 py-1.5 bg-slate-800 hover:bg-slate-700 text-slate-300 text-[10px] font-pixel rounded-lg disabled:opacity-40 transition-colors"
342
+ >
343
+ Cancel Room
344
+ </button>
345
+ </>
346
+ )}
347
+ {brawl.state === STATE_WAITING && joined && !isHost && (
348
+ <button
349
+ onClick={() => simpleCall('leave_brawl', 'Left the room, fee refunded.')}
350
+ disabled={isLoading}
351
+ className="px-3 py-1.5 bg-slate-800 hover:bg-slate-700 text-slate-300 text-[10px] font-pixel rounded-lg disabled:opacity-40 transition-colors"
352
+ >
353
+ Leave Room
354
+ </button>
355
+ )}
356
+ {brawl.state === STATE_WAITING && !joined && (
357
+ <button
358
+ onClick={() => simpleCall('join_brawl', 'Joined the brawl!')}
359
+ disabled={
360
+ isLoading ||
361
+ brawl.players.length >= brawl.maxPlayers ||
362
+ player.gold < brawl.entryFee
363
+ }
364
+ className="px-4 py-1.5 bg-purple-700 hover:bg-purple-600 text-white text-[10px] font-pixel rounded-lg disabled:opacity-40 transition-colors"
365
+ >
366
+ Join (entry {brawl.entryFee.toString()})
367
+ </button>
368
+ )}
369
+ {brawl.state === STATE_ACTIVE && amAlive && (
370
+ <>
371
+ {!myTurn && (
372
+ <button
373
+ onClick={handleTimeoutKick}
374
+ disabled={isLoading}
375
+ title="Removes the current-turn player once they stall past the timeout"
376
+ className="px-3 py-1.5 bg-amber-800 hover:bg-amber-700 text-white text-[10px] font-pixel rounded-lg disabled:opacity-40 transition-colors"
377
+ >
378
+ Kick Staller
379
+ </button>
380
+ )}
381
+ <button
382
+ onClick={() => simpleCall('brawl_surrender', 'You dropped out.', 'clock')}
383
+ disabled={isLoading}
384
+ className="px-3 py-1.5 bg-slate-800 hover:bg-slate-700 text-slate-300 text-[10px] font-pixel rounded-lg disabled:opacity-40 transition-colors"
385
+ >
386
+ Surrender
387
+ </button>
388
+ </>
389
+ )}
390
+ </div>
391
+ </div>
392
+
393
+ {/* Player grid */}
394
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
395
+ {brawl.players.map((addr) => {
396
+ const v = vitals[addr];
397
+ const alive = brawl.alive.includes(addr) || brawl.state === STATE_WAITING;
398
+ const isTurn = brawl.state === STATE_ACTIVE && currentTurnAddr === addr;
399
+ const isMe = addr === me;
400
+ const targetable =
401
+ brawl.state === STATE_ACTIVE && myTurn && !isMe && brawl.alive.includes(addr);
402
+ const inThisMatch = v?.matchId === brawl.sceneId;
403
+ const pct = v && inThisMatch ? Math.min(100, Number((v.hp * 100n) / MAX_HP)) : 0;
404
+ return (
405
+ <button
406
+ key={addr}
407
+ type="button"
408
+ onClick={targetable ? () => setTarget(addr) : undefined}
409
+ className={`text-left rounded-xl border-2 p-3 transition-colors ${
410
+ target === addr && targetable
411
+ ? 'border-red-500 bg-red-950/40'
412
+ : isTurn
413
+ ? 'border-amber-500 bg-amber-950/30'
414
+ : 'border-purple-900/60 bg-slate-950/60'
415
+ } ${!alive ? 'opacity-40 grayscale' : ''} ${
416
+ targetable ? 'cursor-crosshair hover:border-red-400' : 'cursor-default'
417
+ }`}
418
+ >
419
+ <div className="flex items-center justify-between mb-1.5">
420
+ <span className="font-pixel text-[10px] text-purple-200">
421
+ {isMe ? 'You' : shortAddr(addr)}
422
+ {addr === brawl.host && <span className="text-purple-500"> β™”</span>}
423
+ </span>
424
+ {!alive && brawl.state !== STATE_WAITING && <IconSkull size={14} />}
425
+ </div>
426
+ {brawl.state !== STATE_WAITING && v && inThisMatch ? (
427
+ <>
428
+ <div className="h-2 bg-slate-900 rounded-full overflow-hidden border border-slate-700">
429
+ <div
430
+ style={{ width: `${pct}%` }}
431
+ className={`h-full ${
432
+ pct > 50 ? 'bg-emerald-500' : pct > 20 ? 'bg-amber-500' : 'bg-red-500'
433
+ }`}
434
+ />
435
+ </div>
436
+ <div className="flex justify-between mt-1 text-[10px]">
437
+ <span className="text-emerald-400 font-pixel">{v.hp.toString()} HP</span>
438
+ {v.shield > 0n && (
439
+ <span className="text-sky-400 font-pixel">πŸ›‘ {v.shield.toString()}</span>
440
+ )}
441
+ </div>
442
+ </>
443
+ ) : (
444
+ <p className="text-slate-600 text-[10px]">
445
+ {brawl.state === STATE_WAITING ? 'ready' : 'out'}
446
+ </p>
447
+ )}
448
+ {targetable && (
449
+ <p className="text-red-400 text-[9px] font-pixel mt-1">
450
+ {target === addr ? 'β—Ž TARGET' : 'click to target'}
451
+ </p>
452
+ )}
453
+ </button>
454
+ );
455
+ })}
456
+ </div>
457
+
458
+ {/* Finished: settlement */}
459
+ {brawl.state === STATE_FINISHED && (
460
+ <motion.div
461
+ initial={{ opacity: 0, scale: 0.95 }}
462
+ animate={{ opacity: 1, scale: 1 }}
463
+ className="bg-slate-950/80 border-2 border-amber-700 rounded-2xl p-6 text-center space-y-3"
464
+ >
465
+ <p className="font-pixel text-amber-300 text-sm">
466
+ {potClaimed
467
+ ? 'Brawl settled.'
468
+ : iWon
469
+ ? 'πŸ† LAST ONE STANDING!'
470
+ : brawl.winner !== ZERO_ADDRESS
471
+ ? `${shortAddr(brawl.winner)} wins the pot`
472
+ : 'Brawl over'}
473
+ </p>
474
+ {iWon && (
475
+ <button
476
+ onClick={handleFinish}
477
+ disabled={isLoading || !arenaId}
478
+ className="px-6 py-2.5 bg-amber-700 hover:bg-amber-600 text-white text-xs font-pixel rounded-xl disabled:opacity-40 transition-colors"
479
+ >
480
+ Collect Pot ({brawl.gold.toString()} gold, 3% arena rake)
481
+ </button>
482
+ )}
483
+ {!iWon && joined && (
484
+ <button
485
+ onClick={handleLeaveFinished}
486
+ disabled={isLoading}
487
+ className="px-6 py-2.5 bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs font-pixel rounded-xl disabled:opacity-40 transition-colors"
488
+ >
489
+ Leave Brawl
490
+ </button>
491
+ )}
492
+ <p>
493
+ <a href="/" className="text-indigo-500 hover:text-indigo-300 text-[11px] underline">
494
+ ← Back to lobby
495
+ </a>
496
+ </p>
497
+ </motion.div>
498
+ )}
499
+
500
+ {/* Hand */}
501
+ {brawl.state === STATE_ACTIVE && amAlive && (
502
+ <div className="bg-slate-950/60 border border-purple-900/60 rounded-2xl p-4">
503
+ <h2 className="font-pixel text-purple-400 text-xs mb-3">
504
+ Your Hand{' '}
505
+ {myTurn
506
+ ? target
507
+ ? `β€” attacking ${shortAddr(target)}`
508
+ : 'β€” pick a target, then a card'
509
+ : '(waiting for your turn)'}
510
+ </h2>
511
+ <div className="flex flex-wrap gap-3">
512
+ {player.deck.map((id) => {
513
+ const card = player.cards.find((c) => c.cardId === id);
514
+ if (!card) return null;
515
+ return (
516
+ <CardView
517
+ key={id}
518
+ card={card}
519
+ disabled={!myTurn || isLoading || (isAttackKind(card.kind) && !target)}
520
+ onClick={() => playCard(id, card.kind)}
521
+ />
522
+ );
523
+ })}
524
+ </div>
525
+ <p className="text-purple-800 text-[10px] mt-3">
526
+ Brawl cards are reusable every turn. Attack any alive player β€” eliminations rotate the
527
+ turn order.
528
+ </p>
529
+ </div>
530
+ )}
531
+
532
+ {brawl.state === STATE_WAITING && (
533
+ <CenterNote
534
+ text={
535
+ isHost
536
+ ? 'Share this page URL β€” anyone can join your room from the lobby.'
537
+ : joined
538
+ ? 'Waiting for the host to start…'
539
+ : 'Room is open β€” join from the banner above.'
540
+ }
541
+ />
542
+ )}
543
+ </div>
544
+ </GameShell>
545
+ );
546
+ }
547
+
548
+ function CenterNote({ text, backHref }: { text: string; backHref?: string }) {
549
+ return (
550
+ <div className="flex flex-col items-center justify-center py-16 gap-4">
551
+ <p className="text-indigo-500 font-pixel text-xs">{text}</p>
552
+ {backHref && (
553
+ <a href={backHref} className="text-indigo-600 hover:text-indigo-400 text-[11px] underline">
554
+ ← Back to lobby
555
+ </a>
556
+ )}
557
+ </div>
558
+ );
559
+ }