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