create-dubhe 1.2.0-pre.98 → 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 (361) 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 +8 -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 +8 -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/Move.toml +4 -0
  297. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/dapp_key.move +45 -31
  298. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/error.move +10 -0
  299. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/genesis.move +30 -13
  300. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/init_test.move +19 -14
  301. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter1.move +30 -20
  302. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2.move +35 -24
  303. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
  304. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/scripts/deploy_hook.move +6 -10
  305. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/systems/counter.move +7 -20
  306. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/tests/counter.move +14 -27
  307. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.lock +33 -30
  308. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.toml +8 -3
  309. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/dapp_key.move +45 -31
  310. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/error.move +170 -0
  311. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move +16 -13
  312. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/init_test.move +8 -14
  313. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/account_key.move +1 -3
  314. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/dapp_service.move +1776 -137
  315. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/data_key.move +1 -20
  316. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/events.move +864 -25
  317. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/deploy_hook.move +15 -16
  318. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move +0 -3
  319. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move +25 -17
  320. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +2802 -343
  321. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/utils.move +1 -1
  322. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/address.move +173 -55
  323. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/dapp_test.move +769 -0
  324. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/fee_test.move +2965 -0
  325. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/integration_test.move +303 -0
  326. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/listing_test.move +1422 -0
  327. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
  328. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/session_test.move +624 -0
  329. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/storage_test.move +700 -0
  330. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_object_test.move +177 -0
  331. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +452 -0
  332. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/user_storage_test.move +160 -0
  333. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/utils/math.move +4 -3
  334. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/utils/type_info.move +2 -2
  335. package/templates/101/sui-template/packages/contracts/src/counter/Move.lock +0 -41
  336. package/templates/101/sui-template/packages/contracts/src/counter/Published.toml +0 -9
  337. package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/errors.move +0 -12
  338. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/errors.move +0 -140
  339. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
  340. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
  341. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
  342. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
  343. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/dapp_store.move +0 -29
  344. package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/session_system.move +0 -44
  345. package/templates/contract/sui-template/src/dubhe/sources/codegen/errors.move +0 -140
  346. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
  347. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
  348. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
  349. package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
  350. package/templates/contract/sui-template/src/dubhe/sources/core/dapp_store.move +0 -29
  351. package/templates/contract/sui-template/src/dubhe/sources/systems/session_system.move +0 -44
  352. package/templates/nextjs/sui-template/packages/contracts/src/counter/Move.lock +0 -47
  353. package/templates/nextjs/sui-template/packages/contracts/src/counter/Published.toml +0 -9
  354. package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/errors.move +0 -12
  355. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/errors.move +0 -140
  356. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
  357. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
  358. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
  359. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
  360. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/dapp_store.move +0 -29
  361. package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/session_system.move +0 -44
@@ -0,0 +1,202 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Collection — card inventory, pack opening (on-chain randomness) and
5
+ * battle deck selection (exactly 5 distinct owned cards).
6
+ */
7
+
8
+ import { useState } from 'react';
9
+ import { toast } from 'sonner';
10
+
11
+ import { useGame } from '../hooks/useGame';
12
+ import { GameShell } from '../components/GameShell';
13
+ import { CardView } from '../components/CardView';
14
+ import { IconGold, IconCards } from '../components/icons';
15
+ import { DECK_SIZE } from '../lib/game';
16
+ import { ZERO_ADDRESS } from '@0xobelisk/graphql-client';
17
+
18
+ const SUI_RANDOM = '0x8';
19
+ const PACK_PRICE = 100; // mirrors deploy_hook default; shown as a hint only
20
+
21
+ export default function CollectionPage() {
22
+ const game = useGame(5000);
23
+ const { player, userStorageId, storageId, pkg, execTx, isLoading } = game;
24
+
25
+ // Deck editing state: starts from the on-chain deck.
26
+ const [editing, setEditing] = useState(false);
27
+ const [selection, setSelection] = useState<string[]>([]);
28
+
29
+ const inMatch = player.battle.matchId !== ZERO_ADDRESS;
30
+ const deckSet = new Set(player.deck);
31
+
32
+ const startEditing = () => {
33
+ setSelection([...player.deck]);
34
+ setEditing(true);
35
+ };
36
+
37
+ const toggleCard = (cardId: string) => {
38
+ setSelection((sel) => {
39
+ if (sel.includes(cardId)) return sel.filter((c) => c !== cardId);
40
+ if (sel.length >= DECK_SIZE) {
41
+ toast.error(`Deck is full (${DECK_SIZE} cards) — remove one first`);
42
+ return sel;
43
+ }
44
+ return [...sel, cardId];
45
+ });
46
+ };
47
+
48
+ const handleOpenPack = () =>
49
+ execTx((tx) => {
50
+ if (!userStorageId) throw new Error('UserStorage not found');
51
+ tx.moveCall({
52
+ target: `${pkg}::card_system::open_pack`,
53
+ arguments: [tx.object(storageId), tx.object(userStorageId), tx.object(SUI_RANDOM)]
54
+ });
55
+ }, 'Pack opened — a new card joins your collection!');
56
+
57
+ const handleSaveDeck = () => {
58
+ if (selection.length !== DECK_SIZE) {
59
+ toast.error(`Select exactly ${DECK_SIZE} cards`);
60
+ return;
61
+ }
62
+ execTx(
63
+ (tx) => {
64
+ if (!userStorageId) throw new Error('UserStorage not found');
65
+ tx.moveCall({
66
+ target: `${pkg}::card_system::set_deck`,
67
+ arguments: [
68
+ tx.object(storageId),
69
+ tx.object(userStorageId),
70
+ tx.pure.vector('address', selection)
71
+ ]
72
+ });
73
+ },
74
+ 'Battle deck updated!',
75
+ () => setEditing(false)
76
+ );
77
+ };
78
+
79
+ const sortedCards = [...player.cards].sort(
80
+ (a, b) => b.rarity - a.rarity || a.kind - b.kind || b.power - a.power
81
+ );
82
+
83
+ return (
84
+ <GameShell game={game}>
85
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
86
+ {/* ── Left: pack + deck ── */}
87
+ <div className="space-y-4">
88
+ <div className="bg-slate-950/80 border-2 border-indigo-800 rounded-2xl p-4 space-y-3">
89
+ <h2 className="font-pixel text-indigo-300 text-xs flex items-center gap-2">
90
+ <IconCards size={18} /> Card Pack
91
+ </h2>
92
+ <p className="text-indigo-700 text-[11px]">
93
+ Mint a random card using Sui on-chain randomness. Kind: Strike 40% / Fireball 25% /
94
+ Heal 20% / Shield 15%. Rarity: Common 70% / Rare 25% / Epic 5%.
95
+ </p>
96
+ <button
97
+ onClick={handleOpenPack}
98
+ disabled={isLoading || player.gold < BigInt(PACK_PRICE)}
99
+ className="w-full py-2.5 bg-indigo-700 hover:bg-indigo-600 text-white text-xs font-pixel rounded-lg disabled:opacity-40 transition-colors flex items-center justify-center gap-2"
100
+ >
101
+ Open Pack — <IconGold size={12} /> {PACK_PRICE}
102
+ </button>
103
+ </div>
104
+
105
+ <div className="bg-slate-950/80 border-2 border-amber-800/60 rounded-2xl p-4 space-y-3">
106
+ <div className="flex items-center justify-between">
107
+ <h2 className="font-pixel text-amber-300 text-xs">Battle Deck ({DECK_SIZE})</h2>
108
+ {!editing ? (
109
+ <button
110
+ onClick={startEditing}
111
+ disabled={inMatch}
112
+ title={inMatch ? 'Cannot edit the deck mid-match' : undefined}
113
+ className="px-3 py-1 bg-amber-800 hover:bg-amber-700 text-white text-[10px] font-pixel rounded-lg disabled:opacity-40 transition-colors"
114
+ >
115
+ Edit
116
+ </button>
117
+ ) : (
118
+ <div className="flex gap-1.5">
119
+ <button
120
+ onClick={handleSaveDeck}
121
+ disabled={isLoading || selection.length !== DECK_SIZE}
122
+ className="px-3 py-1 bg-emerald-700 hover:bg-emerald-600 text-white text-[10px] font-pixel rounded-lg disabled:opacity-40 transition-colors"
123
+ >
124
+ Save ({selection.length}/{DECK_SIZE})
125
+ </button>
126
+ <button
127
+ onClick={() => setEditing(false)}
128
+ className="px-3 py-1 bg-slate-800 hover:bg-slate-700 text-slate-300 text-[10px] font-pixel rounded-lg transition-colors"
129
+ >
130
+ Cancel
131
+ </button>
132
+ </div>
133
+ )}
134
+ </div>
135
+ <p className="text-amber-700/70 text-[11px]">
136
+ In duels each deck card can be played once per match. Brawl cards are reusable every
137
+ turn.
138
+ </p>
139
+ <div className="flex flex-wrap gap-2">
140
+ {(editing ? selection : player.deck).map((id) => {
141
+ const card = player.cards.find((c) => c.cardId === id);
142
+ return card ? (
143
+ <CardView
144
+ key={id}
145
+ card={card}
146
+ small
147
+ onClick={editing ? () => toggleCard(id) : undefined}
148
+ selected={editing}
149
+ />
150
+ ) : (
151
+ <div
152
+ key={id}
153
+ className="w-20 h-28 rounded-xl border-2 border-dashed border-slate-700 flex items-center justify-center text-slate-600 text-[9px] font-pixel"
154
+ >
155
+ listed?
156
+ </div>
157
+ );
158
+ })}
159
+ {(editing ? selection : player.deck).length === 0 && (
160
+ <p className="text-slate-600 text-[11px]">No deck set.</p>
161
+ )}
162
+ </div>
163
+ </div>
164
+ </div>
165
+
166
+ {/* ── Right: full collection ── */}
167
+ <div className="lg:col-span-2">
168
+ <h2 className="font-pixel text-indigo-400 text-xs mb-3">
169
+ Collection ({player.cards.length} cards)
170
+ {editing && (
171
+ <span className="text-amber-400 ml-2">— click cards to add/remove from deck</span>
172
+ )}
173
+ </h2>
174
+ {sortedCards.length === 0 ? (
175
+ <div className="bg-slate-950/40 border border-indigo-900/40 rounded-xl p-8 text-center">
176
+ <p className="text-indigo-700 font-pixel text-[11px]">
177
+ No cards yet — register grants a starter deck, or open a pack.
178
+ </p>
179
+ </div>
180
+ ) : (
181
+ <div className="flex flex-wrap gap-3">
182
+ {sortedCards.map((card) => (
183
+ <div key={card.cardId} className="relative">
184
+ <CardView
185
+ card={card}
186
+ selected={editing ? selection.includes(card.cardId) : deckSet.has(card.cardId)}
187
+ onClick={editing ? () => toggleCard(card.cardId) : undefined}
188
+ />
189
+ {!editing && deckSet.has(card.cardId) && (
190
+ <span className="absolute -top-1.5 -right-1.5 bg-amber-500 text-black text-[8px] font-pixel px-1.5 py-0.5 rounded-full">
191
+ DECK
192
+ </span>
193
+ )}
194
+ </div>
195
+ ))}
196
+ </div>
197
+ )}
198
+ </div>
199
+ </div>
200
+ </GameShell>
201
+ );
202
+ }
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+
3
+ import { motion } from 'framer-motion';
4
+ import type { CardData } from '../lib/game';
5
+ import { KIND_NAME, KIND_EMOJI, RARITY_NAME, RARITY_COLOR, isAttackKind } from '../lib/game';
6
+
7
+ /**
8
+ * A single playing card. Used in the collection, deck builder and battle pages.
9
+ */
10
+ export function CardView({
11
+ card,
12
+ selected = false,
13
+ disabled = false,
14
+ used = false,
15
+ small = false,
16
+ onClick
17
+ }: {
18
+ card: CardData;
19
+ selected?: boolean;
20
+ disabled?: boolean;
21
+ /** Already played this match (duel rule: one use per card). */
22
+ used?: boolean;
23
+ small?: boolean;
24
+ onClick?: () => void;
25
+ }) {
26
+ const rarityCls = RARITY_COLOR[card.rarity] ?? RARITY_COLOR[0];
27
+ const clickable = Boolean(onClick) && !disabled && !used;
28
+
29
+ return (
30
+ <motion.button
31
+ type="button"
32
+ onClick={clickable ? onClick : undefined}
33
+ whileHover={clickable ? { y: -4, scale: 1.03 } : undefined}
34
+ whileTap={clickable ? { scale: 0.97 } : undefined}
35
+ disabled={!clickable && Boolean(onClick)}
36
+ className={`relative flex flex-col items-center justify-between rounded-xl border-2 bg-slate-900/90 text-left transition-colors
37
+ ${small ? 'w-20 h-28 p-1.5' : 'w-28 h-40 p-2.5'}
38
+ ${rarityCls}
39
+ ${selected ? 'ring-2 ring-amber-400 -translate-y-1' : ''}
40
+ ${used || disabled ? 'opacity-40 grayscale' : ''}
41
+ ${clickable ? 'cursor-pointer hover:bg-slate-800/90' : 'cursor-default'}`}
42
+ >
43
+ <span className={`font-pixel ${small ? 'text-[8px]' : 'text-[10px]'} text-slate-300`}>
44
+ {KIND_NAME[card.kind] ?? '?'}
45
+ </span>
46
+ <span className={small ? 'text-2xl' : 'text-4xl'}>{KIND_EMOJI[card.kind] ?? '❓'}</span>
47
+ <div className="w-full flex items-center justify-between">
48
+ <span className={`${small ? 'text-[8px]' : 'text-[10px]'} opacity-80`}>
49
+ {RARITY_NAME[card.rarity]}
50
+ </span>
51
+ <span
52
+ className={`font-pixel ${small ? 'text-[9px]' : 'text-xs'} ${
53
+ isAttackKind(card.kind) ? 'text-red-400' : 'text-emerald-400'
54
+ }`}
55
+ >
56
+ {card.power}
57
+ </span>
58
+ </div>
59
+ {used && (
60
+ <span className="absolute inset-0 flex items-center justify-center font-pixel text-[9px] text-red-400 bg-black/40 rounded-xl">
61
+ USED
62
+ </span>
63
+ )}
64
+ </motion.button>
65
+ );
66
+ }
@@ -0,0 +1,294 @@
1
+ 'use client';
2
+
3
+ import { usePathname } from 'next/navigation';
4
+ import { ConnectButton } from '@mysten/dapp-kit';
5
+ import { motion } from 'framer-motion';
6
+ import { toast } from 'sonner';
7
+
8
+ import type { useGame } from '../hooks/useGame';
9
+ import { IconCards, IconGold, IconTrophy } from './icons';
10
+
11
+ type Game = ReturnType<typeof useGame>;
12
+
13
+ const NAV = [
14
+ { href: '/', label: 'Lobby' },
15
+ { href: '/collection', label: 'Collection' },
16
+ { href: '/market', label: 'Market' },
17
+ { href: '/leaderboard', label: 'Leaderboard' }
18
+ ];
19
+
20
+ const BG = 'radial-gradient(ellipse at top, #1e1b4b 0%, #0b0a1a 65%)';
21
+
22
+ /**
23
+ * Common page chrome: header, nav, session-key bar and onboarding gates
24
+ * (connect wallet → create UserStorage → register). Children render only
25
+ * once the player is fully onboarded, unless `gate` is set to false.
26
+ */
27
+ export function GameShell({
28
+ game,
29
+ children,
30
+ gate = true
31
+ }: {
32
+ game: Game;
33
+ children: React.ReactNode;
34
+ /** Set false for pages that are viewable before registration (e.g. leaderboard). */
35
+ gate?: boolean;
36
+ }) {
37
+ const pathname = usePathname();
38
+ const { isConnected, player, balance, userStorageId, isLoading } = game;
39
+
40
+ if (!isConnected) {
41
+ return (
42
+ <div
43
+ className="min-h-screen flex flex-col items-center justify-center gap-6 p-8"
44
+ style={{ background: BG }}
45
+ >
46
+ <motion.div
47
+ initial={{ y: -20, opacity: 0 }}
48
+ animate={{ y: 0, opacity: 1 }}
49
+ className="text-center"
50
+ >
51
+ <div className="mb-4 flex justify-center">
52
+ <IconCards size={80} />
53
+ </div>
54
+ <h1 className="font-pixel text-indigo-300 text-2xl mb-2">CARD DUEL</h1>
55
+ <p className="text-indigo-500 text-sm mb-8">Full-Chain PvP Card Battles on Sui</p>
56
+ <ConnectButton />
57
+ </motion.div>
58
+ </div>
59
+ );
60
+ }
61
+
62
+ const balanceWarning = balance === 0 && (
63
+ <div className="mb-4 bg-red-900/40 border border-red-600/50 rounded-xl px-4 py-3 text-red-300 text-xs font-pixel">
64
+ Your SUI balance is 0. Please get some {game.network ?? 'localnet'} SUI before making
65
+ transactions.
66
+ </div>
67
+ );
68
+
69
+ const header = (
70
+ <div className="flex items-center justify-between mb-4 flex-wrap gap-3">
71
+ <div className="flex items-center gap-2">
72
+ <IconCards size={28} />
73
+ <h1 className="font-pixel text-indigo-300 text-sm">CARD DUEL</h1>
74
+ </div>
75
+ <div className="flex items-center gap-4 flex-wrap">
76
+ {player.isRegistered && (
77
+ <>
78
+ <span className="flex items-center gap-1 text-xs font-pixel text-amber-300">
79
+ <IconGold size={14} /> {player.gold.toLocaleString()}
80
+ </span>
81
+ <span className="flex items-center gap-1 text-xs font-pixel text-indigo-300">
82
+ <IconTrophy size={14} /> {player.profile?.rating ?? 0}
83
+ </span>
84
+ </>
85
+ )}
86
+ {balance > 0 ? (
87
+ <span className="text-xs text-indigo-500 font-pixel">{balance.toFixed(3)} SUI</span>
88
+ ) : (
89
+ <span className="text-xs text-red-400 font-pixel">0 SUI — top up needed</span>
90
+ )}
91
+ <ConnectButton />
92
+ </div>
93
+ </div>
94
+ );
95
+
96
+ const nav = (
97
+ <div className="flex gap-2 mb-6 flex-wrap">
98
+ {NAV.map(({ href, label }) => (
99
+ <a
100
+ key={href}
101
+ href={href}
102
+ className={`px-4 py-2 text-xs font-pixel rounded-lg border transition-colors ${
103
+ pathname === href
104
+ ? 'bg-indigo-700 text-indigo-100 border-indigo-500'
105
+ : 'bg-indigo-950/60 hover:bg-indigo-900/60 text-indigo-400 border-indigo-800/40'
106
+ }`}
107
+ >
108
+ {label}
109
+ </a>
110
+ ))}
111
+ </div>
112
+ );
113
+
114
+ // ── Onboarding gates ────────────────────────────────────────────────────────
115
+
116
+ if (gate && !userStorageId) {
117
+ return (
118
+ <div className="min-h-screen p-4 md:p-6" style={{ background: BG }}>
119
+ {header}
120
+ {balanceWarning}
121
+ <div className="flex flex-col items-center justify-center min-h-[60vh] text-center">
122
+ <div className="mb-4">
123
+ <IconCards size={60} />
124
+ </div>
125
+ <h2 className="font-pixel text-indigo-300 text-lg mb-2">Setup Your Account</h2>
126
+ <p className="text-indigo-500 text-sm mb-2">Step 1 of 2 — Create your on-chain storage</p>
127
+ <p className="text-indigo-700 text-xs mb-6 max-w-xs">
128
+ This creates your personal UserStorage object on Sui — required before interacting with
129
+ any DApp.
130
+ </p>
131
+ <motion.button
132
+ onClick={game.handleCreateStorage}
133
+ disabled={isLoading || balance === 0}
134
+ className="bg-blue-700 hover:bg-blue-600 text-white font-pixel px-8 py-3 rounded-xl disabled:opacity-50 transition-colors"
135
+ whileHover={{ scale: 1.05 }}
136
+ whileTap={{ scale: 0.95 }}
137
+ >
138
+ {isLoading ? 'Creating...' : 'Create Storage (Step 1)'}
139
+ </motion.button>
140
+ </div>
141
+ </div>
142
+ );
143
+ }
144
+
145
+ if (gate && !player.isRegistered) {
146
+ return (
147
+ <div className="min-h-screen p-4 md:p-6" style={{ background: BG }}>
148
+ {header}
149
+ {balanceWarning}
150
+ <div className="flex flex-col items-center justify-center min-h-[60vh] text-center">
151
+ <div className="mb-4">
152
+ <IconCards size={60} />
153
+ </div>
154
+ <h2 className="font-pixel text-indigo-300 text-lg mb-2">Enter the Arena</h2>
155
+ <p className="text-indigo-500 text-sm mb-2">Step 2 of 2 — Register as a duelist</p>
156
+ <p className="text-indigo-700 text-xs mb-6 max-w-xs">
157
+ You will receive 500 gold and a 5-card starter deck.
158
+ <br />
159
+ After registering, activate a Session Key to play without wallet popups.
160
+ </p>
161
+ <motion.button
162
+ onClick={game.handleRegister}
163
+ disabled={isLoading || balance === 0}
164
+ className="bg-indigo-700 hover:bg-indigo-600 text-white font-pixel px-8 py-3 rounded-xl disabled:opacity-50 transition-colors"
165
+ whileHover={{ scale: 1.05 }}
166
+ whileTap={{ scale: 0.95 }}
167
+ >
168
+ {isLoading ? 'Registering...' : 'Register (Step 2)'}
169
+ </motion.button>
170
+ </div>
171
+ </div>
172
+ );
173
+ }
174
+
175
+ return (
176
+ <div className="min-h-screen p-4 md:p-6" style={{ background: BG }}>
177
+ {header}
178
+ {balanceWarning}
179
+ {player.isRegistered && <SessionBar game={game} />}
180
+ {nav}
181
+ {children}
182
+ </div>
183
+ );
184
+ }
185
+
186
+ // ── Session key status bar ──────────────────────────────────────────────────
187
+
188
+ function SessionBar({ game }: { game: Game }) {
189
+ const {
190
+ session,
191
+ userStorageId,
192
+ isLoading,
193
+ setIsLoading,
194
+ network,
195
+ signAndExecuteTransaction,
196
+ txToast
197
+ } = game;
198
+
199
+ const handleActivate = async () => {
200
+ if (!userStorageId) {
201
+ toast.error('Create storage first');
202
+ return;
203
+ }
204
+ setIsLoading(true);
205
+ try {
206
+ const tx = session.buildActivateTx(userStorageId, session.SESSION_DURATION_MS);
207
+ await signAndExecuteTransaction(
208
+ { transaction: (tx as any).serialize(), chain: `sui:${network ?? 'localnet'}` },
209
+ {
210
+ onSuccess: (resp: any) => {
211
+ session.confirmActivation(session.SESSION_DURATION_MS);
212
+ txToast(
213
+ 'Session activated for 1 hour — no wallet popups for game actions!',
214
+ resp.digest
215
+ );
216
+ },
217
+ onError: (err: any) => toast.error(`Failed: ${err.message}`)
218
+ }
219
+ );
220
+ } catch (err: any) {
221
+ toast.error(`Error: ${err?.message ?? err}`);
222
+ } finally {
223
+ setIsLoading(false);
224
+ }
225
+ };
226
+
227
+ const handleFund = async () => {
228
+ setIsLoading(true);
229
+ try {
230
+ const tx = session.buildFundSessionTx(0.1);
231
+ await signAndExecuteTransaction(
232
+ { transaction: (tx as any).serialize(), chain: `sui:${network ?? 'localnet'}` },
233
+ {
234
+ onSuccess: (resp: any) => txToast('Topped up 0.1 SUI to session wallet', resp.digest),
235
+ onError: (e: any) => toast.error(`Top-up failed: ${e.message}`)
236
+ }
237
+ );
238
+ } finally {
239
+ setIsLoading(false);
240
+ }
241
+ };
242
+
243
+ return (
244
+ <div
245
+ className={`mb-4 flex items-center justify-between px-4 py-2 rounded-xl border text-xs font-pixel flex-wrap gap-2 ${
246
+ session.isActive
247
+ ? 'bg-emerald-900/30 border-emerald-700/40 text-emerald-300'
248
+ : 'bg-indigo-900/20 border-indigo-700/30 text-indigo-400'
249
+ }`}
250
+ >
251
+ {session.keypairLoading ? (
252
+ <span className="opacity-60">Loading session key...</span>
253
+ ) : session.isActive ? (
254
+ <>
255
+ <span className="flex items-center gap-2 flex-wrap">
256
+ Session active — {session.minutesLeft}m left
257
+ <span className="font-mono text-emerald-400/70">
258
+ {session.sessionAddress.slice(0, 6)}…{session.sessionAddress.slice(-4)}
259
+ </span>
260
+ </span>
261
+ <div className="flex items-center gap-3">
262
+ <button
263
+ onClick={handleFund}
264
+ disabled={isLoading}
265
+ className="bg-indigo-800 hover:bg-indigo-700 text-white px-2 py-0.5 rounded disabled:opacity-50 transition-colors"
266
+ >
267
+ Fund 0.1 SUI
268
+ </button>
269
+ <button
270
+ onClick={() => {
271
+ session.clearSession();
272
+ toast.success('Session cleared.');
273
+ }}
274
+ className="text-red-400 hover:text-red-300 transition-colors"
275
+ >
276
+ Deactivate
277
+ </button>
278
+ </div>
279
+ </>
280
+ ) : (
281
+ <>
282
+ <span>Session inactive — wallet approval required for every action</span>
283
+ <button
284
+ onClick={handleActivate}
285
+ disabled={isLoading || !userStorageId || session.keypairLoading}
286
+ className="ml-4 bg-emerald-700 hover:bg-emerald-600 text-white px-3 py-1 rounded-lg disabled:opacity-50 transition-colors"
287
+ >
288
+ Activate Session (1h)
289
+ </button>
290
+ </>
291
+ )}
292
+ </div>
293
+ );
294
+ }
@@ -0,0 +1,101 @@
1
+ /** Minimal inline SVG icon set for the Card Duel UI. */
2
+
3
+ export function IconCards({ size = 24 }: { size?: number }) {
4
+ return (
5
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
6
+ <rect x="3" y="5" width="11" height="15" rx="2" fill="#6366f1" transform="rotate(-8 3 5)" />
7
+ <rect
8
+ x="9"
9
+ y="4"
10
+ width="11"
11
+ height="15"
12
+ rx="2"
13
+ fill="#a855f7"
14
+ stroke="#1e1b4b"
15
+ strokeWidth="1"
16
+ transform="rotate(8 9 4)"
17
+ />
18
+ <path d="M15.5 8l1 2 2 .3-1.5 1.5.4 2-1.9-1-1.9 1 .4-2L12.5 10l2-.3z" fill="#fde047" />
19
+ </svg>
20
+ );
21
+ }
22
+
23
+ export function IconSword({ size = 20 }: { size?: number }) {
24
+ return (
25
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
26
+ <path d="M5 19l10-10 3-6-6 3L2 16z" fill="#cbd5e1" stroke="#475569" strokeWidth="1" />
27
+ <path d="M4 20l2 2 3-1-4-4-1 3z" fill="#92400e" />
28
+ </svg>
29
+ );
30
+ }
31
+
32
+ export function IconShield({ size = 20 }: { size?: number }) {
33
+ return (
34
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
35
+ <path
36
+ d="M12 2l8 3v6c0 5-3.5 9.5-8 11-4.5-1.5-8-6-8-11V5z"
37
+ fill="#0ea5e9"
38
+ stroke="#0c4a6e"
39
+ strokeWidth="1.5"
40
+ />
41
+ <path d="M12 5l5 2v4c0 3.5-2.2 6.7-5 7.8V5z" fill="#38bdf8" opacity="0.6" />
42
+ </svg>
43
+ );
44
+ }
45
+
46
+ export function IconGold({ size = 16 }: { size?: number }) {
47
+ return (
48
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
49
+ <circle cx="12" cy="12" r="9" fill="#fbbf24" stroke="#92400e" strokeWidth="1.5" />
50
+ <circle cx="12" cy="12" r="5.5" fill="none" stroke="#92400e" strokeWidth="1" opacity="0.5" />
51
+ <text x="12" y="16" textAnchor="middle" fontSize="9" fontWeight="bold" fill="#92400e">
52
+ G
53
+ </text>
54
+ </svg>
55
+ );
56
+ }
57
+
58
+ export function IconTrophy({ size = 20 }: { size?: number }) {
59
+ return (
60
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
61
+ <path d="M7 4h10v5a5 5 0 0 1-10 0V4z" fill="#fbbf24" stroke="#92400e" strokeWidth="1.5" />
62
+ <path d="M7 5H4a3 3 0 0 0 3 4M17 5h3a3 3 0 0 1-3 4" stroke="#92400e" strokeWidth="1.5" />
63
+ <path d="M11 14h2v3h-2z" fill="#fbbf24" />
64
+ <rect x="8" y="17" width="8" height="3" rx="1" fill="#92400e" />
65
+ </svg>
66
+ );
67
+ }
68
+
69
+ export function IconMedal({ rank, size = 24 }: { rank: 1 | 2 | 3; size?: number }) {
70
+ const colors: Record<number, [string, string]> = {
71
+ 1: ['#fbbf24', '#92400e'],
72
+ 2: ['#cbd5e1', '#475569'],
73
+ 3: ['#d97706', '#7c2d12']
74
+ };
75
+ const [fill, stroke] = colors[rank];
76
+ return (
77
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
78
+ <path d="M8 2h3l-2 7H6zM16 2h-3l2 7h3z" fill={stroke} opacity="0.7" />
79
+ <circle cx="12" cy="14" r="7" fill={fill} stroke={stroke} strokeWidth="1.5" />
80
+ <text x="12" y="18" textAnchor="middle" fontSize="9" fontWeight="bold" fill={stroke}>
81
+ {rank}
82
+ </text>
83
+ </svg>
84
+ );
85
+ }
86
+
87
+ export function IconSkull({ size = 16 }: { size?: number }) {
88
+ return (
89
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
90
+ <path
91
+ d="M12 3a8 8 0 0 0-8 8c0 3 1.5 5 3.5 6.2V20a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1v-2.8C18.5 16 20 14 20 11a8 8 0 0 0-8-8z"
92
+ fill="#e2e8f0"
93
+ stroke="#475569"
94
+ strokeWidth="1.2"
95
+ />
96
+ <circle cx="9" cy="11" r="1.8" fill="#1e293b" />
97
+ <circle cx="15" cy="11" r="1.8" fill="#1e293b" />
98
+ <path d="M12 14l1 2.5h-2z" fill="#1e293b" />
99
+ </svg>
100
+ );
101
+ }