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,146 @@
1
+ module card_duel::card_system {
2
+ use sui::random::{Self, Random};
3
+ use dubhe::dapp_service::{DappStorage, UserStorage};
4
+ use dubhe::dapp_system;
5
+ use card_duel::dapp_key::DappKey;
6
+ use card_duel::migrate;
7
+ use card_duel::error;
8
+ use card_duel::gold;
9
+ use card_duel::profile;
10
+ use card_duel::card;
11
+ use card_duel::deck;
12
+ use card_duel::game_config;
13
+
14
+ // ─── Card kinds (enum CardKind) ─────────────────────────────────────────
15
+ const KIND_STRIKE: u8 = 1;
16
+ const KIND_FIREBALL: u8 = 2;
17
+ const KIND_HEAL: u8 = 3;
18
+ const KIND_SHIELD: u8 = 4;
19
+
20
+ // ─── Rarities (enum Rarity) ─────────────────────────────────────────────
21
+ const RARITY_COMMON: u8 = 0;
22
+ const RARITY_RARE: u8 = 1;
23
+ const RARITY_EPIC: u8 = 2;
24
+
25
+ const DECK_SIZE: u64 = 5;
26
+
27
+ // ─── Card pack ──────────────────────────────────────────────────────────
28
+
29
+ /// Buy one card pack and mint a random card.
30
+ /// kind roll: Strike 40% / Fireball 25% / Heal 20% / Shield 15%
31
+ /// rarity roll: Common 70% / Rare 25% / Epic 5%
32
+ public entry fun open_pack(
33
+ dapp_storage: &DappStorage,
34
+ user_storage: &mut UserStorage,
35
+ rng: &Random,
36
+ ctx: &mut TxContext,
37
+ ) {
38
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
39
+ dapp_system::ensure_not_paused<DappKey>(dapp_storage);
40
+ error::not_registered(profile::has(user_storage));
41
+
42
+ let price = game_config::get_pack_price(dapp_storage);
43
+ error::insufficient_gold(gold::get(user_storage) >= price);
44
+ gold::sub(user_storage, price, ctx);
45
+
46
+ let mut gen = random::new_generator(rng, ctx);
47
+ let (kind, rarity) = roll_card(&mut gen);
48
+
49
+ // fresh_object_address gives a globally-unique card id with no counter.
50
+ let card_id = ctx.fresh_object_address();
51
+ card::mint(user_storage, card_id, kind, base_power(kind, rarity), rarity, ctx);
52
+ }
53
+
54
+ /// Select exactly 5 owned, distinct cards as the battle deck.
55
+ public entry fun set_deck(
56
+ dapp_storage: &DappStorage,
57
+ user_storage: &mut UserStorage,
58
+ card_ids: vector<address>,
59
+ ctx: &mut TxContext,
60
+ ) {
61
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
62
+ dapp_system::ensure_not_paused<DappKey>(dapp_storage);
63
+ error::not_registered(profile::has(user_storage));
64
+ error::deck_wrong_size(card_ids.length() == DECK_SIZE);
65
+
66
+ let mut i = 0;
67
+ while (i < DECK_SIZE) {
68
+ let id = *card_ids.borrow(i);
69
+ error::card_not_found(card::has(user_storage, id));
70
+ // Reject duplicates: id must not appear later in the list.
71
+ let mut j = i + 1;
72
+ while (j < DECK_SIZE) {
73
+ error::deck_duplicate_card(*card_ids.borrow(j) != id);
74
+ j = j + 1;
75
+ };
76
+ i = i + 1;
77
+ };
78
+
79
+ deck::set(user_storage, card_ids, ctx);
80
+ }
81
+
82
+ // ─── Package helpers ────────────────────────────────────────────────────
83
+
84
+ /// Mint the fixed starter set (3x Strike, Fireball, Shield — all Common)
85
+ /// and return the new card ids in deck order. Attack-heavy so a duel can
86
+ /// always be decided by knockout.
87
+ public(package) fun mint_starter_cards(
88
+ user_storage: &mut UserStorage,
89
+ ctx: &mut TxContext,
90
+ ): vector<address> {
91
+ let kinds = vector[KIND_STRIKE, KIND_STRIKE, KIND_STRIKE, KIND_FIREBALL, KIND_SHIELD];
92
+ let mut ids = vector::empty<address>();
93
+ let mut i = 0;
94
+ while (i < kinds.length()) {
95
+ let kind = *kinds.borrow(i);
96
+ let card_id = ctx.fresh_object_address();
97
+ card::mint(user_storage, card_id, kind, base_power(kind, RARITY_COMMON), RARITY_COMMON, ctx);
98
+ ids.push_back(card_id);
99
+ i = i + 1;
100
+ };
101
+ ids
102
+ }
103
+
104
+ /// Damage / heal / shield strength for a kind+rarity combination.
105
+ public(package) fun base_power(kind: u8, rarity: u8): u32 {
106
+ let base: u32 = if (kind == KIND_STRIKE) { 12 }
107
+ else if (kind == KIND_FIREBALL) { 18 }
108
+ else if (kind == KIND_HEAL) { 10 }
109
+ else { 8 };
110
+ if (rarity == RARITY_RARE) { base + base / 3 }
111
+ else if (rarity == RARITY_EPIC) { base + (base * 3) / 4 }
112
+ else { base }
113
+ }
114
+
115
+ public(package) fun kind_strike(): u8 { KIND_STRIKE }
116
+ public(package) fun kind_fireball(): u8 { KIND_FIREBALL }
117
+ public(package) fun kind_heal(): u8 { KIND_HEAL }
118
+ public(package) fun kind_shield(): u8 { KIND_SHIELD }
119
+ public(package) fun deck_size(): u64 { DECK_SIZE }
120
+
121
+ /// True if `kind` is an offensive card (targets the opponent).
122
+ public(package) fun is_attack_kind(kind: u8): bool {
123
+ kind == KIND_STRIKE || kind == KIND_FIREBALL
124
+ }
125
+
126
+ /// True if `kind` is a self-targeted card (heal or shield).
127
+ public(package) fun is_defense_kind(kind: u8): bool {
128
+ kind == KIND_HEAL || kind == KIND_SHIELD
129
+ }
130
+
131
+ fun roll_card(gen: &mut sui::random::RandomGenerator): (u8, u8) {
132
+ let roll_k = random::generate_u8_in_range(gen, 0, 99);
133
+ let roll_r = random::generate_u8_in_range(gen, 0, 99);
134
+
135
+ let kind = if (roll_k < 40) { KIND_STRIKE }
136
+ else if (roll_k < 65) { KIND_FIREBALL }
137
+ else if (roll_k < 85) { KIND_HEAL }
138
+ else { KIND_SHIELD };
139
+
140
+ let rarity = if (roll_r < 70) { RARITY_COMMON }
141
+ else if (roll_r < 95) { RARITY_RARE }
142
+ else { RARITY_EPIC };
143
+
144
+ (kind, rarity)
145
+ }
146
+ }
@@ -0,0 +1,515 @@
1
+ /// 1v1 duel flow built on ScenePermit (direct invitations) + SceneStorage
2
+ /// (stake escrow + turn state) + reactive writes (cross-user combat).
3
+ ///
4
+ /// Lifecycle:
5
+ /// create_duel challenger invites an opponent and escrows the stake
6
+ /// accept_duel opponent accepts the invitation, matches the stake, match starts
7
+ /// cancel_duel challenger reclaims the stake while the invite is pending
8
+ /// attack / defend turn-based card play; damage lands via reactive writes
9
+ /// surrender concede mid-match
10
+ /// claim_timeout_win declare victory when the opponent stalls past the timeout
11
+ /// finish_duel winner collects the pot (minus arena rake) and records the win
12
+ /// leave_duel loser exits the permit after the match is decided
13
+ /// cleanup_duel destroy the empty scene + permit (optional hygiene)
14
+ module card_duel::duel_system {
15
+ use sui::clock::Clock;
16
+ use dubhe::dapp_service::{Self, DappStorage, UserStorage, ScenePermit, SceneStorage, ObjectStorage};
17
+ use dubhe::dapp_system;
18
+ use card_duel::dapp_key;
19
+ use card_duel::dapp_key::DappKey;
20
+ use card_duel::migrate;
21
+ use card_duel::error;
22
+ use card_duel::gold;
23
+ use card_duel::profile;
24
+ use card_duel::battle_state;
25
+ use card_duel::deck;
26
+ use card_duel::card;
27
+ use card_duel::card_system;
28
+ use card_duel::game_config;
29
+ use card_duel::duel::{Self, Duel};
30
+ use card_duel::duel_permit::{Self, DuelPermit};
31
+ use card_duel::arena::{Self, Arena};
32
+
33
+ /// How long a duel invitation stays open.
34
+ const INVITE_WINDOW_MS: u64 = 24 * 60 * 60 * 1000;
35
+ /// Ladder points exchanged per match.
36
+ const RATING_DELTA: u32 = 25;
37
+
38
+ const STATE_WAITING: u8 = 0;
39
+ const STATE_ACTIVE: u8 = 1;
40
+ const STATE_FINISHED: u8 = 2;
41
+
42
+ // ─── Lifecycle ──────────────────────────────────────────────────────────
43
+
44
+ /// Challenge `opponent` for `stake` gold. Creates a direct-invitation
45
+ /// ScenePermit (only the opponent can accept) plus the duel SceneStorage,
46
+ /// escrows the challenger's stake and shares both objects.
47
+ public entry fun create_duel(
48
+ dapp_storage: &DappStorage,
49
+ user_storage: &mut UserStorage,
50
+ opponent: address,
51
+ stake: u64,
52
+ clock: &Clock,
53
+ ctx: &mut TxContext,
54
+ ) {
55
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
56
+ dapp_system::ensure_not_paused<DappKey>(dapp_storage);
57
+ // The on-scene player identity is always the canonical owner of
58
+ // user_storage — the transaction itself may be signed by the main
59
+ // wallet or by its active session key.
60
+ let player = dapp_service::canonical_owner(user_storage);
61
+ ensure_ready_for_match(user_storage);
62
+ error::cannot_play_self(opponent != player);
63
+ error::invalid_stake(stake > 0);
64
+ error::insufficient_gold(gold::get(user_storage) >= stake);
65
+
66
+ let now = sui::clock::timestamp_ms(clock);
67
+
68
+ // Direct invitation: only `opponent` may accept; capacity 2; the
69
+ // permit itself never expires (the invite window does).
70
+ let mut permit = duel_permit::new_duel_permit_with_invitations(
71
+ dapp_storage,
72
+ vector[opponent],
73
+ std::option::some(now + INVITE_WINDOW_MS),
74
+ std::option::none(),
75
+ std::option::some(2),
76
+ ctx,
77
+ );
78
+ // The challenger joins as the first confirmed participant.
79
+ duel_permit::join_duel_permit(&mut permit, user_storage, ctx);
80
+
81
+ let mut scene = duel::new_duel_with_permit(dapp_storage, &permit, ctx);
82
+ let scene_addr = sui::object::uid_to_address(dapp_service::scene_storage_id(&scene));
83
+
84
+ duel::set_challenger(&permit, &mut scene, user_storage, player, ctx);
85
+ duel::set_opponent(&permit, &mut scene, user_storage, opponent, ctx);
86
+ duel::set_stake(&permit, &mut scene, user_storage, stake, ctx);
87
+ duel::set_state(&permit, &mut scene, user_storage, STATE_WAITING, ctx);
88
+ duel::set_turn_addr(&permit, &mut scene, user_storage, @0x0, ctx);
89
+ duel::set_round(&permit, &mut scene, user_storage, 0, ctx);
90
+ duel::set_winner(&permit, &mut scene, user_storage, @0x0, ctx);
91
+ duel::set_used_cards_a(&permit, &mut scene, user_storage, vector::empty(), ctx);
92
+ duel::set_used_cards_b(&permit, &mut scene, user_storage, vector::empty(), ctx);
93
+ duel::set_last_action_ms(&permit, &mut scene, user_storage, now, ctx);
94
+
95
+ // Escrow the challenger's stake inside the scene bag.
96
+ gold::transfer_user_to_duel(&permit, user_storage, &mut scene, stake, ctx);
97
+
98
+ let max_hp = game_config::get_max_hp(dapp_storage);
99
+ battle_state::set(user_storage, scene_addr, max_hp, 0, ctx);
100
+
101
+ duel::share_duel(scene);
102
+ duel_permit::share_duel_permit(permit);
103
+ }
104
+
105
+ /// Accept a pending duel invitation: join the permit, match the stake and
106
+ /// start the match. The challenger moves first.
107
+ public entry fun accept_duel(
108
+ dapp_storage: &DappStorage,
109
+ user_storage: &mut UserStorage,
110
+ permit: &mut ScenePermit<DuelPermit>,
111
+ scene: &mut SceneStorage<Duel>,
112
+ clock: &Clock,
113
+ ctx: &mut TxContext,
114
+ ) {
115
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
116
+ dapp_system::ensure_not_paused<DappKey>(dapp_storage);
117
+ // The invited player is identified by the canonical owner of
118
+ // user_storage; the transaction may be session-signed.
119
+ let player = dapp_service::canonical_owner(user_storage);
120
+ ensure_ready_for_match(user_storage);
121
+ ensure_scene_bound(permit, scene);
122
+ error::match_not_waiting(duel::get_state(scene) == STATE_WAITING);
123
+ error::not_invited(duel::get_opponent(scene) == player);
124
+
125
+ // Moves the canonical owner from the invitee list to confirmed participants.
126
+ duel_permit::accept_duel_permit(permit, user_storage, ctx);
127
+
128
+ let stake = duel::get_stake(scene);
129
+ error::insufficient_gold(gold::get(user_storage) >= stake);
130
+ gold::transfer_user_to_duel(permit, user_storage, scene, stake, ctx);
131
+
132
+ let scene_addr = sui::object::uid_to_address(dapp_service::scene_storage_id(scene));
133
+ let max_hp = game_config::get_max_hp(dapp_storage);
134
+ battle_state::set(user_storage, scene_addr, max_hp, 0, ctx);
135
+
136
+ let now = sui::clock::timestamp_ms(clock);
137
+ let challenger = duel::get_challenger(scene);
138
+ duel::set_state(permit, scene, user_storage, STATE_ACTIVE, ctx);
139
+ duel::set_turn_addr(permit, scene, user_storage, challenger, ctx);
140
+ duel::set_last_action_ms(permit, scene, user_storage, now, ctx);
141
+ }
142
+
143
+ /// Cancel a duel that nobody accepted: the challenger reclaims the stake.
144
+ public entry fun cancel_duel(
145
+ dapp_storage: &DappStorage,
146
+ user_storage: &mut UserStorage,
147
+ permit: &mut ScenePermit<DuelPermit>,
148
+ scene: &mut SceneStorage<Duel>,
149
+ ctx: &mut TxContext,
150
+ ) {
151
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
152
+ ensure_scene_bound(permit, scene);
153
+ error::match_not_waiting(duel::get_state(scene) == STATE_WAITING);
154
+ error::not_challenger(duel::get_challenger(scene) == dapp_service::canonical_owner(user_storage));
155
+
156
+ let pot = duel::get_gold(scene);
157
+ duel::set_state(permit, scene, user_storage, STATE_FINISHED, ctx);
158
+ gold::transfer_duel_to_user(permit, scene, user_storage, pot, ctx);
159
+ battle_state::set(user_storage, @0x0, 0, 0, ctx);
160
+ duel_permit::leave_duel_permit(permit, user_storage, ctx);
161
+ }
162
+
163
+ // ─── Combat ─────────────────────────────────────────────────────────────
164
+
165
+ /// Play an attack card (Strike / Fireball) against the opponent. Damage is
166
+ /// written to the opponent's UserStorage through the ScenePermit — the
167
+ /// framework verifies both players are scene participants (reactive write).
168
+ /// A killing blow decides the match on the spot.
169
+ public entry fun attack(
170
+ dapp_storage: &DappStorage,
171
+ attacker_storage: &mut UserStorage,
172
+ target_storage: &mut UserStorage,
173
+ permit: &ScenePermit<DuelPermit>,
174
+ scene: &mut SceneStorage<Duel>,
175
+ card_id: address,
176
+ clock: &Clock,
177
+ ctx: &mut TxContext,
178
+ ) {
179
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
180
+ dapp_system::ensure_not_paused<DappKey>(dapp_storage);
181
+ ensure_scene_bound(permit, scene);
182
+
183
+ // Player identity is the attacker's canonical owner — the transaction
184
+ // may be signed by the main wallet or its active session key.
185
+ let player = dapp_service::canonical_owner(attacker_storage);
186
+ let scene_addr = sui::object::uid_to_address(dapp_service::scene_storage_id(scene));
187
+ error::match_not_active(duel::get_state(scene) == STATE_ACTIVE);
188
+ error::not_your_turn(duel::get_turn_addr(scene) == player);
189
+ error::wrong_match(battle_state::get_match_id(attacker_storage) == scene_addr);
190
+ error::wrong_match(battle_state::get_match_id(target_storage) == scene_addr);
191
+
192
+ // The opponent's storage must belong to the other duel player.
193
+ let expected_target = other_player(scene, player);
194
+ error::wrong_match(dapp_service::canonical_owner(target_storage) == expected_target);
195
+
196
+ let kind = take_card_for_turn(attacker_storage, permit, scene, player, card_id, ctx);
197
+ error::invalid_card_kind(card_system::is_attack_kind(kind));
198
+
199
+ let dmg = (card::get_power(attacker_storage, card_id) as u64);
200
+ let hp = battle_state::get_hp(target_storage);
201
+ let shield = battle_state::get_shield(target_storage);
202
+ let (new_hp, new_shield) = apply_damage(hp, shield, dmg);
203
+
204
+ if (new_hp == 0) {
205
+ // Killing blow: decide the match and settle the loser's records
206
+ // while they are still a scene participant.
207
+ duel::set_state(permit, scene, attacker_storage, STATE_FINISHED, ctx);
208
+ duel::set_winner(permit, scene, attacker_storage, player, ctx);
209
+ duel::set_turn_addr(permit, scene, attacker_storage, @0x0, ctx);
210
+ battle_state::set_reactive(permit, attacker_storage, target_storage, @0x0, 0, 0, ctx);
211
+ record_loss_reactive(permit, attacker_storage, target_storage, ctx);
212
+ } else {
213
+ battle_state::set_reactive(
214
+ permit, attacker_storage, target_storage, scene_addr, new_hp, new_shield, ctx,
215
+ );
216
+ duel::set_turn_addr(permit, scene, attacker_storage, expected_target, ctx);
217
+ };
218
+
219
+ let round = duel::get_round(scene);
220
+ duel::set_round(permit, scene, attacker_storage, round + 1, ctx);
221
+ duel::set_last_action_ms(permit, scene, attacker_storage, sui::clock::timestamp_ms(clock), ctx);
222
+ }
223
+
224
+ /// Play a self-targeted card (Heal / Shield) as the turn action.
225
+ public entry fun defend(
226
+ dapp_storage: &DappStorage,
227
+ user_storage: &mut UserStorage,
228
+ permit: &ScenePermit<DuelPermit>,
229
+ scene: &mut SceneStorage<Duel>,
230
+ card_id: address,
231
+ clock: &Clock,
232
+ ctx: &mut TxContext,
233
+ ) {
234
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
235
+ dapp_system::ensure_not_paused<DappKey>(dapp_storage);
236
+ ensure_scene_bound(permit, scene);
237
+
238
+ let player = dapp_service::canonical_owner(user_storage);
239
+ let scene_addr = sui::object::uid_to_address(dapp_service::scene_storage_id(scene));
240
+ error::match_not_active(duel::get_state(scene) == STATE_ACTIVE);
241
+ error::not_your_turn(duel::get_turn_addr(scene) == player);
242
+ error::wrong_match(battle_state::get_match_id(user_storage) == scene_addr);
243
+
244
+ let kind = take_card_for_turn(user_storage, permit, scene, player, card_id, ctx);
245
+ error::invalid_card_kind(card_system::is_defense_kind(kind));
246
+
247
+ let power = (card::get_power(user_storage, card_id) as u64);
248
+ if (kind == card_system::kind_heal()) {
249
+ let max_hp = game_config::get_max_hp(dapp_storage);
250
+ let hp = battle_state::get_hp(user_storage);
251
+ let healed = if (hp + power > max_hp) { max_hp } else { hp + power };
252
+ battle_state::set_hp(user_storage, healed, ctx);
253
+ } else {
254
+ let shield = battle_state::get_shield(user_storage);
255
+ battle_state::set_shield(user_storage, shield + power, ctx);
256
+ };
257
+
258
+ let next_turn = other_player(scene, player);
259
+ let round = duel::get_round(scene);
260
+ duel::set_turn_addr(permit, scene, user_storage, next_turn, ctx);
261
+ duel::set_round(permit, scene, user_storage, round + 1, ctx);
262
+ duel::set_last_action_ms(permit, scene, user_storage, sui::clock::timestamp_ms(clock), ctx);
263
+ }
264
+
265
+ /// Concede the match. The opponent becomes the winner and can collect the
266
+ /// pot with finish_duel.
267
+ public entry fun surrender(
268
+ dapp_storage: &DappStorage,
269
+ user_storage: &mut UserStorage,
270
+ permit: &mut ScenePermit<DuelPermit>,
271
+ scene: &mut SceneStorage<Duel>,
272
+ ctx: &mut TxContext,
273
+ ) {
274
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
275
+ ensure_scene_bound(permit, scene);
276
+
277
+ let player = dapp_service::canonical_owner(user_storage);
278
+ error::match_not_active(duel::get_state(scene) == STATE_ACTIVE);
279
+ error::not_in_match(is_duel_player(scene, player));
280
+
281
+ let winner = other_player(scene, player);
282
+ duel::set_state(permit, scene, user_storage, STATE_FINISHED, ctx);
283
+ duel::set_winner(permit, scene, user_storage, winner, ctx);
284
+ duel::set_turn_addr(permit, scene, user_storage, @0x0, ctx);
285
+
286
+ record_own_loss(user_storage, ctx);
287
+ battle_state::set(user_storage, @0x0, 0, 0, ctx);
288
+ duel_permit::leave_duel_permit(permit, user_storage, ctx);
289
+ }
290
+
291
+ /// Declare victory when the opponent has stalled past the turn timeout,
292
+ /// or immediately when they have exhausted every card in their deck
293
+ /// (each card can only be played once per duel).
294
+ public entry fun claim_timeout_win(
295
+ dapp_storage: &DappStorage,
296
+ user_storage: &mut UserStorage,
297
+ opponent_storage: &mut UserStorage,
298
+ permit: &ScenePermit<DuelPermit>,
299
+ scene: &mut SceneStorage<Duel>,
300
+ clock: &Clock,
301
+ ctx: &mut TxContext,
302
+ ) {
303
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
304
+ ensure_scene_bound(permit, scene);
305
+
306
+ let player = dapp_service::canonical_owner(user_storage);
307
+ error::match_not_active(duel::get_state(scene) == STATE_ACTIVE);
308
+ error::not_in_match(is_duel_player(scene, player));
309
+ // It must be the opponent's turn for them to be the one stalling.
310
+ let stalling = duel::get_turn_addr(scene);
311
+ error::not_your_turn(stalling != player);
312
+ error::wrong_match(dapp_service::canonical_owner(opponent_storage) == stalling);
313
+
314
+ let now = sui::clock::timestamp_ms(clock);
315
+ let timeout = game_config::get_turn_timeout_ms(dapp_storage);
316
+ let timed_out = now > duel::get_last_action_ms(scene) + timeout;
317
+ error::turn_not_timed_out(timed_out || is_exhausted(opponent_storage, scene, stalling));
318
+
319
+ duel::set_state(permit, scene, user_storage, STATE_FINISHED, ctx);
320
+ duel::set_winner(permit, scene, user_storage, player, ctx);
321
+ duel::set_turn_addr(permit, scene, user_storage, @0x0, ctx);
322
+ battle_state::set_reactive(permit, user_storage, opponent_storage, @0x0, 0, 0, ctx);
323
+ record_loss_reactive(permit, user_storage, opponent_storage, ctx);
324
+ duel::set_last_action_ms(permit, scene, user_storage, now, ctx);
325
+ }
326
+
327
+ // ─── Settlement ─────────────────────────────────────────────────────────
328
+
329
+ /// Winner collects the pot: stake x2 minus the arena rake, then exits the
330
+ /// permit. Demonstrates Scene → User and Scene → Object gold transfers.
331
+ public entry fun finish_duel(
332
+ dapp_storage: &DappStorage,
333
+ user_storage: &mut UserStorage,
334
+ permit: &mut ScenePermit<DuelPermit>,
335
+ scene: &mut SceneStorage<Duel>,
336
+ arena: &mut ObjectStorage<Arena>,
337
+ ctx: &mut TxContext,
338
+ ) {
339
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
340
+ ensure_scene_bound(permit, scene);
341
+
342
+ let player = dapp_service::canonical_owner(user_storage);
343
+ error::match_not_finished(duel::get_state(scene) == STATE_FINISHED);
344
+ error::not_match_winner(duel::get_winner(scene) == player);
345
+
346
+ let pot = duel::get_gold(scene);
347
+ let rake = pot * game_config::get_rake_bps(dapp_storage) / 10_000;
348
+ if (rake > 0) {
349
+ arena::transfer_duel_to_arena_gold(permit, scene, arena, user_storage, rake, ctx);
350
+ };
351
+ gold::transfer_duel_to_user(permit, scene, user_storage, pot - rake, ctx);
352
+
353
+ // Mark the win and clear the winner's match binding.
354
+ let (wins, losses, rating) = profile::get(user_storage);
355
+ profile::set(user_storage, wins + 1, losses, rating + RATING_DELTA, ctx);
356
+ // Prevent double-claiming the pot.
357
+ duel::set_winner(permit, scene, user_storage, @0x0, ctx);
358
+ battle_state::set(user_storage, @0x0, 0, 0, ctx);
359
+ duel_permit::leave_duel_permit(permit, user_storage, ctx);
360
+ }
361
+
362
+ /// Exit the permit after the match has been decided (loser-side cleanup).
363
+ public entry fun leave_duel(
364
+ permit: &mut ScenePermit<DuelPermit>,
365
+ scene: &SceneStorage<Duel>,
366
+ user_storage: &UserStorage,
367
+ ctx: &TxContext,
368
+ ) {
369
+ ensure_scene_bound(permit, scene);
370
+ // Leaving mid-match would break reactive combat writes.
371
+ error::match_not_finished(duel::get_state(scene) == STATE_FINISHED);
372
+ duel_permit::leave_duel_permit(permit, user_storage, ctx);
373
+ }
374
+
375
+ /// Destroy a fully settled duel (empty pot, no participants left).
376
+ /// Anyone can call — this is on-chain hygiene, not access-controlled.
377
+ public entry fun cleanup_duel(
378
+ scene: SceneStorage<Duel>,
379
+ permit: ScenePermit<DuelPermit>,
380
+ _ctx: &TxContext,
381
+ ) {
382
+ error::match_not_finished(duel::get_state(&scene) == STATE_FINISHED);
383
+ error::invalid_stake(duel::get_gold(&scene) == 0);
384
+
385
+ let mut scene = scene;
386
+ // The scene bag must be empty before destroy_typed_scene.
387
+ duel::remove_challenger_system_maintenance(&mut scene);
388
+ duel::remove_opponent_system_maintenance(&mut scene);
389
+ duel::remove_stake_system_maintenance(&mut scene);
390
+ duel::remove_state_system_maintenance(&mut scene);
391
+ duel::remove_turn_addr_system_maintenance(&mut scene);
392
+ duel::remove_round_system_maintenance(&mut scene);
393
+ duel::remove_winner_system_maintenance(&mut scene);
394
+ duel::remove_used_cards_a_system_maintenance(&mut scene);
395
+ duel::remove_used_cards_b_system_maintenance(&mut scene);
396
+ duel::remove_last_action_ms_system_maintenance(&mut scene);
397
+ if (dapp_system::has_scene_field<Duel, u64>(&scene, b"gold")) {
398
+ dapp_system::remove_scene_field_system_maintenance<DappKey, Duel, u64>(
399
+ dapp_key::new(), &mut scene, b"gold",
400
+ );
401
+ };
402
+ duel::destroy_duel(scene);
403
+
404
+ // Aborts unless every participant has left.
405
+ dapp_system::destroy_scene_permit<DappKey, DuelPermit>(dapp_key::new(), permit);
406
+ }
407
+
408
+ // ─── Helpers ────────────────────────────────────────────────────────────
409
+
410
+ fun ensure_ready_for_match(user_storage: &UserStorage) {
411
+ error::not_registered(profile::has(user_storage));
412
+ error::card_not_in_deck(deck::has(user_storage));
413
+ error::already_in_match(battle_state::get_match_id(user_storage) == @0x0);
414
+ }
415
+
416
+ fun ensure_scene_bound(permit: &ScenePermit<DuelPermit>, scene: &SceneStorage<Duel>) {
417
+ let bound = dapp_service::scene_storage_authorized_permit_id(scene);
418
+ error::wrong_match(std::option::is_some(bound));
419
+ error::wrong_match(
420
+ *std::option::borrow(bound)
421
+ == sui::object::uid_to_address(dapp_service::scene_permit_id(permit))
422
+ );
423
+ }
424
+
425
+ fun is_duel_player(scene: &SceneStorage<Duel>, addr: address): bool {
426
+ addr == duel::get_challenger(scene) || addr == duel::get_opponent(scene)
427
+ }
428
+
429
+ fun other_player(scene: &SceneStorage<Duel>, addr: address): address {
430
+ if (addr == duel::get_challenger(scene)) {
431
+ duel::get_opponent(scene)
432
+ } else {
433
+ duel::get_challenger(scene)
434
+ }
435
+ }
436
+
437
+ /// Validate the card for this turn (owned, in deck, unused) and mark it
438
+ /// used on the player's side. Returns the card kind.
439
+ fun take_card_for_turn(
440
+ player_storage: &UserStorage,
441
+ permit: &ScenePermit<DuelPermit>,
442
+ scene: &mut SceneStorage<Duel>,
443
+ player: address,
444
+ card_id: address,
445
+ ctx: &TxContext,
446
+ ): u8 {
447
+ error::card_not_found(card::has(player_storage, card_id));
448
+ let deck_ids = deck::get(player_storage);
449
+ error::card_not_in_deck(deck_ids.contains(&card_id));
450
+
451
+ let is_challenger = player == duel::get_challenger(scene);
452
+ let mut used = if (is_challenger) {
453
+ duel::get_used_cards_a(scene)
454
+ } else {
455
+ duel::get_used_cards_b(scene)
456
+ };
457
+ error::card_already_used(!used.contains(&card_id));
458
+ used.push_back(card_id);
459
+ if (is_challenger) {
460
+ duel::set_used_cards_a(permit, scene, player_storage, used, ctx);
461
+ } else {
462
+ duel::set_used_cards_b(permit, scene, player_storage, used, ctx);
463
+ };
464
+
465
+ card::get_kind(player_storage, card_id)
466
+ }
467
+
468
+ /// True if `player` has already used every card of their battle deck in
469
+ /// this duel — they cannot take any further turn action.
470
+ fun is_exhausted(
471
+ player_storage: &UserStorage,
472
+ scene: &SceneStorage<Duel>,
473
+ player: address,
474
+ ): bool {
475
+ let used = if (player == duel::get_challenger(scene)) {
476
+ duel::get_used_cards_a(scene)
477
+ } else {
478
+ duel::get_used_cards_b(scene)
479
+ };
480
+ let deck_ids = deck::get(player_storage);
481
+ let mut i = 0;
482
+ while (i < deck_ids.length()) {
483
+ if (!used.contains(deck_ids.borrow(i))) { return false };
484
+ i = i + 1;
485
+ };
486
+ true
487
+ }
488
+
489
+ fun apply_damage(hp: u64, shield: u64, dmg: u64): (u64, u64) {
490
+ if (shield >= dmg) {
491
+ (hp, shield - dmg)
492
+ } else {
493
+ let pierce = dmg - shield;
494
+ if (pierce >= hp) { (0, 0) } else { (hp - pierce, 0) }
495
+ }
496
+ }
497
+
498
+ fun record_own_loss(user_storage: &mut UserStorage, ctx: &mut TxContext) {
499
+ let (wins, losses, rating) = profile::get(user_storage);
500
+ let new_rating = if (rating > RATING_DELTA) { rating - RATING_DELTA } else { 0 };
501
+ profile::set(user_storage, wins, losses + 1, new_rating, ctx);
502
+ }
503
+
504
+ /// Record a loss on the defeated player's profile via reactive write.
505
+ fun record_loss_reactive(
506
+ permit: &ScenePermit<DuelPermit>,
507
+ from: &mut UserStorage,
508
+ target: &mut UserStorage,
509
+ ctx: &mut TxContext,
510
+ ) {
511
+ let (wins, losses, rating) = profile::get(target);
512
+ let new_rating = if (rating > RATING_DELTA) { rating - RATING_DELTA } else { 0 };
513
+ profile::set_reactive(permit, from, target, wins, losses + 1, new_rating, ctx);
514
+ }
515
+ }