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.
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/package.json +16 -16
- package/templates/101/sui-template/docker-compose.testnet.yml +73 -0
- package/templates/101/sui-template/packages/client/src/app/page.tsx +545 -41
- package/templates/101/sui-template/packages/client/src/app/providers.tsx +12 -4
- package/templates/101/sui-template/packages/contracts/Pub.localnet.toml +4 -4
- package/templates/101/sui-template/packages/contracts/counter.lock.json +20 -0
- package/templates/101/sui-template/packages/contracts/deployment.ts +8 -3
- package/templates/101/sui-template/packages/contracts/dubhe.config.json +59 -13
- package/templates/101/sui-template/packages/contracts/dubhe.config.ts +1 -4
- package/templates/101/sui-template/packages/contracts/metadata.json +1015 -68
- package/templates/101/sui-template/packages/contracts/package.json +7 -4
- package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/dapp_key.move +45 -31
- package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/error.move +8 -0
- package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/genesis.move +30 -13
- package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/init_test.move +19 -14
- package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2.move +198 -0
- package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2withkey.move +90 -0
- package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/resources/value.move +30 -20
- package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
- package/templates/101/sui-template/packages/contracts/src/counter/sources/scripts/deploy_hook.move +6 -11
- package/templates/101/sui-template/packages/contracts/src/counter/sources/systems/counter.move +23 -11
- package/templates/101/sui-template/packages/contracts/src/counter/sources/tests/counter.move +15 -14
- package/templates/101/sui-template/packages/contracts/src/dubhe/Move.lock +33 -30
- package/templates/101/sui-template/packages/contracts/src/dubhe/Move.toml +8 -3
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/dapp_key.move +45 -31
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/error.move +170 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move +16 -13
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/init_test.move +8 -14
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/account_key.move +1 -3
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/dapp_service.move +1776 -137
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/data_key.move +1 -20
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/events.move +864 -25
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/scripts/deploy_hook.move +15 -16
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move +0 -3
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move +25 -17
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +2802 -343
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/utils.move +1 -1
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/address.move +173 -55
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/dapp_test.move +769 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/fee_test.move +2965 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/integration_test.move +303 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/listing_test.move +1422 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/session_test.move +624 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/storage_test.move +700 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/typed_object_test.move +177 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +452 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/user_storage_test.move +160 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/utils/math.move +4 -3
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/utils/type_info.move +2 -2
- package/templates/contract/sui-template/dubhe.config.ts +0 -1
- package/templates/contract/sui-template/package.json +3 -3
- package/templates/contract/sui-template/src/dubhe/Move.lock +33 -30
- package/templates/contract/sui-template/src/dubhe/Move.toml +8 -3
- package/templates/contract/sui-template/src/dubhe/sources/codegen/dapp_key.move +45 -31
- package/templates/contract/sui-template/src/dubhe/sources/codegen/error.move +170 -0
- package/templates/contract/sui-template/src/dubhe/sources/codegen/genesis.move +16 -13
- package/templates/contract/sui-template/src/dubhe/sources/codegen/init_test.move +8 -14
- package/templates/contract/sui-template/src/dubhe/sources/core/account_key.move +1 -3
- package/templates/contract/sui-template/src/dubhe/sources/core/dapp_service.move +1776 -137
- package/templates/contract/sui-template/src/dubhe/sources/core/data_key.move +1 -20
- package/templates/contract/sui-template/src/dubhe/sources/core/events.move +864 -25
- package/templates/contract/sui-template/src/dubhe/sources/scripts/deploy_hook.move +15 -16
- package/templates/contract/sui-template/src/dubhe/sources/scripts/migrate.move +0 -3
- package/templates/contract/sui-template/src/dubhe/sources/systems/address_system.move +25 -17
- package/templates/contract/sui-template/src/dubhe/sources/systems/dapp_system.move +2802 -343
- package/templates/contract/sui-template/src/dubhe/sources/systems/utils.move +1 -1
- package/templates/contract/sui-template/src/dubhe/sources/tests/address.move +173 -55
- package/templates/contract/sui-template/src/dubhe/sources/tests/dapp_test.move +769 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/fee_test.move +2965 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/integration_test.move +303 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/listing_test.move +1422 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/scene_session_test.move +269 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/session_test.move +624 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/storage_test.move +700 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/typed_object_test.move +177 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/typed_scene_test.move +452 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/user_storage_test.move +160 -0
- package/templates/contract/sui-template/src/dubhe/sources/utils/math.move +4 -3
- package/templates/contract/sui-template/src/dubhe/sources/utils/type_info.move +2 -2
- package/templates/contract/sui-template/src/template/Move.toml +12 -0
- package/templates/contract/sui-template/src/template/sources/codegen/dapp_key.move +47 -0
- package/templates/contract/sui-template/src/template/sources/codegen/genesis.move +34 -0
- package/templates/contract/sui-template/src/template/sources/codegen/init_test.move +23 -0
- package/templates/contract/sui-template/src/template/sources/codegen/user_storage_init.move +27 -0
- package/templates/contract/sui-template/src/template/sources/scripts/deploy_hook.move +13 -0
- package/templates/contract/sui-template/src/template/sources/scripts/migrate.move +7 -0
- package/templates/nextjs/sui-card-duel/.gitignore_ +51 -0
- package/templates/nextjs/sui-card-duel/README.md +213 -0
- package/templates/nextjs/sui-card-duel/docker-compose.yml +34 -0
- package/templates/nextjs/sui-card-duel/mprocs.yaml +17 -0
- package/templates/nextjs/sui-card-duel/package.json +43 -0
- package/templates/nextjs/sui-card-duel/packages/client/.gitignore_ +48 -0
- package/templates/nextjs/sui-card-duel/packages/client/.prettierignore +1 -0
- package/templates/nextjs/sui-card-duel/packages/client/.prettierrc.json +8 -0
- package/templates/nextjs/sui-card-duel/packages/client/eslint.config.mjs +22 -0
- package/templates/nextjs/sui-card-duel/packages/client/next.config.mjs +8 -0
- package/templates/nextjs/sui-card-duel/packages/client/package.json +47 -0
- package/templates/nextjs/sui-card-duel/packages/client/postcss.config.mjs +8 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/ArenaPanel.tsx +307 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/page.tsx +622 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/brawl/[id]/page.tsx +559 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/collection/page.tsx +202 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/components/CardView.tsx +66 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/components/GameShell.tsx +294 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/components/icons.tsx +101 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/duel/[id]/page.tsx +477 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/favicon.ico +0 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/globals.css +85 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/hooks/useGame.ts +313 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/layout.tsx +24 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/leaderboard/page.tsx +123 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/game.ts +77 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/scenes.ts +226 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/market/page.tsx +418 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/page.tsx +427 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/providers.tsx +69 -0
- package/templates/nextjs/sui-card-duel/packages/client/tailwind.config.ts +21 -0
- package/templates/nextjs/sui-card-duel/packages/client/tsconfig.json +30 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/.gitignore_ +51 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/.prettierignore +1 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/.prettierrc.json +8 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/Dockerfile +5 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/Pub.localnet.toml +13 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/card_duel.lock.json +66 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/docker-compose.yml +34 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/dubhe.config.ts +150 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/logs/combined.log +31383 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/package.json +49 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/config.ts +17 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-brawl.ts +270 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-duel.ts +272 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-lobby.ts +177 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-market.ts +219 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-session.ts +342 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/query-dapp-revenue.ts +95 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/seed.ts +92 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/withdraw-dapp-revenue.ts +56 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/Move.toml +15 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/dapp_key.move +46 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/card_kind.move +47 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/match_state.move +37 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/rarity.move +37 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/error.move +126 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/genesis.move +33 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/init_test.move +23 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/objects/arena.move +137 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/brawl_permit.move +125 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/duel_permit.move +125 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/battle_state.move +255 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/card.move +282 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/deck.move +64 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/game_config.move +267 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/gold.move +205 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/profile.move +255 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/brawl.move +313 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/duel.move +313 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/user_storage_init.move +26 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/deploy_hook.move +32 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/migrate.move +7 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/arena_system.move +57 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/brawl_system.move +559 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/card_system.move +146 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/duel_system.move +515 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/market_system.move +115 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/player_system.move +37 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/brawl_tests.move +385 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/duel_tests.move +656 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/test_helpers.move +31 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/sui-indexer-schema.graphql +5973 -0
- package/templates/nextjs/sui-card-duel/pnpm-workspace.yaml +2 -0
- package/templates/nextjs/sui-farm/.gitignore_ +51 -0
- package/templates/nextjs/sui-farm/README.md +15 -0
- package/templates/nextjs/sui-farm/UPGRADE_NOTES.md +97 -0
- package/templates/nextjs/sui-farm/docker-compose.yml +34 -0
- package/templates/nextjs/sui-farm/mprocs.yaml +17 -0
- package/templates/nextjs/sui-farm/package.json +43 -0
- package/templates/nextjs/sui-farm/packages/client/.gitignore_ +48 -0
- package/templates/nextjs/sui-farm/packages/client/.prettierignore +1 -0
- package/templates/nextjs/sui-farm/packages/client/.prettierrc.json +8 -0
- package/templates/nextjs/sui-farm/packages/client/eslint.config.mjs +22 -0
- package/templates/nextjs/sui-farm/packages/client/next.config.mjs +8 -0
- package/templates/nextjs/sui-farm/packages/client/package.json +47 -0
- package/templates/nextjs/sui-farm/packages/client/postcss.config.mjs +8 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/admin/page.tsx +618 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/FarmLand.tsx +526 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/FarmPlotCard.tsx +215 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/PetAvatar.tsx +868 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/PetPanel.tsx +1360 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/PlantModal.tsx +147 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/ProxyCard.tsx +684 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/RanchLand.tsx +822 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/ResourceHUD.tsx +125 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/ShopPanel.tsx +232 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/components/icons/GameIcons.tsx +574 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/favicon.ico +0 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/globals.css +423 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/hooks/useWorldPermitId.ts +66 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/layout.tsx +23 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/leaderboard/page.tsx +185 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/lib/crops.ts +97 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/market/page.tsx +763 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/page.tsx +1173 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/providers.tsx +69 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/state/index.tsx +5 -0
- package/templates/nextjs/sui-farm/packages/client/tailwind.config.ts +25 -0
- package/templates/nextjs/sui-farm/packages/client/tsconfig.json +30 -0
- package/templates/nextjs/sui-farm/packages/contracts/.gitignore_ +51 -0
- package/templates/nextjs/sui-farm/packages/contracts/.prettierignore +1 -0
- package/templates/nextjs/sui-farm/packages/contracts/.prettierrc.json +8 -0
- package/templates/nextjs/sui-farm/packages/contracts/Dockerfile +5 -0
- package/templates/nextjs/sui-farm/packages/contracts/Pub.localnet.toml +13 -0
- package/templates/nextjs/sui-farm/packages/contracts/counter.lock.json +14 -0
- package/templates/nextjs/sui-farm/packages/contracts/docker-compose.yml +34 -0
- package/templates/nextjs/sui-farm/packages/contracts/dubhe.config.ts +178 -0
- package/templates/nextjs/sui-farm/packages/contracts/harvest.lock.json +120 -0
- package/templates/nextjs/sui-farm/packages/contracts/package.json +48 -0
- package/templates/nextjs/sui-farm/packages/contracts/scripts/config.ts +17 -0
- package/templates/nextjs/sui-farm/packages/contracts/scripts/query-dapp-revenue.ts +95 -0
- package/templates/nextjs/sui-farm/packages/contracts/scripts/read-permit.ts +72 -0
- package/templates/nextjs/sui-farm/packages/contracts/scripts/seed.ts +85 -0
- package/templates/nextjs/sui-farm/packages/contracts/scripts/withdraw-dapp-revenue.ts +56 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/Move.toml +14 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/dapp_key.move +31 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/error.move +10 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/genesis.move +33 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/init_test.move +23 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/resources/counter1.move +64 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/resources/counter2.move +70 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/scripts/deploy_hook.move +8 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/scripts/migrate.move +8 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/systems/counter.move +13 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/counter/sources/tests/counter.move +28 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/Move.toml +14 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/dapp_key.move +46 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/crop_type.move +47 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/egg_type.move +42 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/pet_rarity.move +37 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/enums/pet_species.move +52 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/error.move +110 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/genesis.move +33 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/init_test.move +23 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/permits/world.move +125 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/carrot.move +136 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/carrot_seed.move +80 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/common_egg.move +136 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/corn.move +136 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/corn_seed.move +80 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/crow_charges.move +162 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/crow_damage.move +207 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/farm_plot.move +261 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/gold.move +80 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet.move +467 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_config.move +372 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_hatch.move +162 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_slot_index.move +83 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pet_slots.move +64 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/profile.move +162 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pumpkin.move +136 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/pumpkin_seed.move +80 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/rare_egg.move +136 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/scarecrow.move +64 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/season_config.move +197 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/season_stats.move +64 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/seasonal_egg.move +136 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/shop_config.move +267 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/trophy.move +245 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/wheat.move +136 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/wheat_seed.move +80 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/resources/world_permit_id.move +64 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/user_storage_init.move +26 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/scripts/deploy_hook.move +45 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/scripts/migrate.move +7 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/crow_system.move +96 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/farm_system.move +182 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/market_system.move +172 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/pet_system.move +495 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/season_system.move +70 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/shop_system.move +112 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/world_system.move +34 -0
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/tests/farm_tests.move +1192 -0
- package/templates/nextjs/sui-farm/pnpm-workspace.yaml +2 -0
- package/templates/nextjs/sui-template/packages/client/src/app/components/ProxyCard.tsx +683 -0
- package/templates/nextjs/sui-template/packages/client/src/app/page.tsx +510 -5
- package/templates/nextjs/sui-template/packages/client/src/app/providers.tsx +13 -5
- package/templates/nextjs/sui-template/packages/contracts/Pub.localnet.toml +4 -4
- package/templates/nextjs/sui-template/packages/contracts/counter.lock.json +14 -0
- package/templates/nextjs/sui-template/packages/contracts/deployment.ts +8 -3
- package/templates/nextjs/sui-template/packages/contracts/dubhe.config.json +9 -9
- package/templates/nextjs/sui-template/packages/contracts/dubhe.config.ts +0 -4
- package/templates/nextjs/sui-template/packages/contracts/metadata.json +180 -124
- package/templates/nextjs/sui-template/packages/contracts/package.json +8 -4
- package/templates/nextjs/sui-template/packages/contracts/src/counter/Move.toml +4 -0
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/dapp_key.move +45 -31
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/error.move +10 -0
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/genesis.move +30 -13
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/init_test.move +19 -14
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter1.move +30 -20
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/resources/counter2.move +35 -24
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/user_storage_init.move +26 -0
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/scripts/deploy_hook.move +6 -10
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/systems/counter.move +7 -20
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/tests/counter.move +14 -27
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.lock +33 -30
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.toml +8 -3
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/dapp_key.move +45 -31
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/error.move +170 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move +16 -13
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/init_test.move +8 -14
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/account_key.move +1 -3
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/dapp_service.move +1776 -137
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/data_key.move +1 -20
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/events.move +864 -25
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/deploy_hook.move +15 -16
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move +0 -3
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move +25 -17
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +2802 -343
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/utils.move +1 -1
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/address.move +173 -55
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/dapp_test.move +769 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/fee_test.move +2965 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/integration_test.move +303 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/listing_test.move +1422 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/session_test.move +624 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/storage_test.move +700 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_object_test.move +177 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +452 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/user_storage_test.move +160 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/utils/math.move +4 -3
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/utils/type_info.move +2 -2
- package/templates/101/sui-template/packages/contracts/src/counter/Move.lock +0 -41
- package/templates/101/sui-template/packages/contracts/src/counter/Published.toml +0 -9
- package/templates/101/sui-template/packages/contracts/src/counter/sources/codegen/errors.move +0 -12
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/errors.move +0 -140
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/core/dapp_store.move +0 -29
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/session_system.move +0 -44
- package/templates/contract/sui-template/src/dubhe/sources/codegen/errors.move +0 -140
- package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
- package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
- package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
- package/templates/contract/sui-template/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
- package/templates/contract/sui-template/src/dubhe/sources/core/dapp_store.move +0 -29
- package/templates/contract/sui-template/src/dubhe/sources/systems/session_system.move +0 -44
- package/templates/nextjs/sui-template/packages/contracts/src/counter/Move.lock +0 -47
- package/templates/nextjs/sui-template/packages/contracts/src/counter/Published.toml +0 -9
- package/templates/nextjs/sui-template/packages/contracts/src/counter/sources/codegen/errors.move +0 -12
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/errors.move +0 -140
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_config.move +0 -186
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_fee_state.move +0 -306
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dapp_metadata.move +0 -396
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/resources/dubhe_config.move +0 -246
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/core/dapp_store.move +0 -29
- 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
|
+
}
|