From a4ad55fa243833c1e4f48e839ba4dd2d5f71bb5a Mon Sep 17 00:00:00 2001 From: ponderingdemocritus Date: Sun, 4 Aug 2024 14:52:12 +1000 Subject: [PATCH] prettier --- .github/workflows/ci.yaml | 44 +- .prettierignore | 16 + .prettierrc | 8 + clients/react-vite/.eslintrc.cjs | 29 +- clients/react-vite/.storybook/main.ts | 30 +- clients/react-vite/.storybook/preview.ts | 14 +- clients/react-vite/README.md | 32 +- clients/react-vite/components.json | 32 +- clients/react-vite/index.html | 29 +- clients/react-vite/package.json | 126 +- clients/react-vite/postcss.config.js | 10 +- .../react-vite/public/images/bg-skulls.png | Bin 252436 -> 34716 bytes clients/react-vite/src/App.tsx | 12 +- .../src/components/game/BeastCard.tsx | 121 +- .../src/components/game/Confirming.tsx | 50 +- .../src/components/game/ItemBar.tsx | 72 +- .../src/components/game/ItemBarList.tsx | 14 +- .../src/components/game/LevelUp.tsx | 227 +- .../src/components/game/LootMarket.tsx | 470 +- .../src/components/game/PlayerCard.tsx | 36 +- .../src/components/game/PlayerPage.tsx | 72 +- .../src/components/game/PlayerProfile.tsx | 149 +- .../src/components/game/PlayerTabs.tsx | 190 +- .../src/components/game/RoundedContainer.tsx | 26 +- .../src/components/pages/LandingPage.tsx | 238 +- .../components/providers/QueryProvider.tsx | 18 +- .../components/providers/StarknetProvider.tsx | 22 +- .../src/components/ui/animatedNumber.tsx | 14 +- .../react-vite/src/components/ui/button.tsx | 80 +- .../src/components/ui/pagination.tsx | 182 +- .../react-vite/src/components/ui/slider.tsx | 30 +- clients/react-vite/src/components/ui/tabs.tsx | 60 +- clients/react-vite/src/hooks/index.ts | 56 +- .../react-vite/src/hooks/useSurvivorState.ts | 4 +- clients/react-vite/src/index.css | 108 +- clients/react-vite/src/lib/utils.ts | 6 +- clients/react-vite/src/main.tsx | 10 +- .../src/stories/BeastCard.stories.ts | 38 +- .../react-vite/src/stories/Button.stories.ts | 58 +- clients/react-vite/src/stories/Configure.mdx | 37 +- .../src/stories/ConfirmationCard.stories.ts | 24 +- .../react-vite/src/stories/Header.stories.ts | 40 +- clients/react-vite/src/stories/Header.tsx | 104 +- .../react-vite/src/stories/ItemBar.stories.ts | 36 +- .../src/stories/ItemBarList.stories.ts | 150 +- .../react-vite/src/stories/LevelUp.stories.ts | 42 +- .../src/stories/LootMarket.stories.ts | 42 +- .../react-vite/src/stories/Page.stories.ts | 36 +- clients/react-vite/src/stories/Page.tsx | 149 +- .../src/stories/PlayerCard.stories.ts | 42 +- .../src/stories/PlayerPage.stories.ts | 42 +- .../src/stories/PlayerProfile.stories.ts | 42 +- .../src/stories/PlayerTabs.stories.ts | 126 +- clients/react-vite/src/stories/header.css | 36 +- clients/react-vite/src/stories/page.css | 80 +- clients/react-vite/tailwind.config.js | 160 +- clients/react-vite/tsconfig.app.json | 56 +- clients/react-vite/tsconfig.json | 28 +- clients/react-vite/tsconfig.node.json | 22 +- clients/react-vite/vite-env.d.ts | 10 +- clients/react-vite/vite.config.ts | 34 +- lerna.json | 6 + package.json | 25 +- packages/core/coverage/base.css | 354 +- packages/core/coverage/block-navigation.js | 30 +- packages/core/coverage/coverage-final.json | 21370 +++++++++++++++- packages/core/coverage/index.html | 485 +- packages/core/coverage/prettify.css | 102 +- packages/core/coverage/prettify.js | 1048 +- packages/core/coverage/sorter.js | 70 +- .../core/coverage/src/constants/index.html | 276 +- .../core/coverage/src/constants/index.ts.html | 145 +- packages/core/coverage/src/index.html | 272 +- packages/core/coverage/src/index.ts.html | 141 +- .../core/coverage/src/objects/beasts.ts.html | 145 +- packages/core/coverage/src/objects/index.html | 475 +- .../core/coverage/src/objects/index.ts.html | 145 +- .../core/coverage/src/objects/loot.ts.html | 145 +- .../coverage/src/objects/obstacles.ts.html | 145 +- .../coverage/src/objects/prediction.ts.html | 145 +- .../coverage/src/objects/survivor.ts.html | 145 +- .../coverage/src/provider/execute.ts.html | 145 +- .../core/coverage/src/provider/index.html | 317 +- .../core/coverage/src/provider/index.ts.html | 145 +- .../core/coverage/src/state/format.ts.html | 145 +- packages/core/coverage/src/state/index.html | 356 +- .../core/coverage/src/state/index.ts.html | 145 +- packages/core/coverage/src/state/mock.ts.html | 145 +- .../core/coverage/src/type/events.ts.html | 145 +- packages/core/coverage/src/type/index.html | 317 +- packages/core/coverage/src/type/index.ts.html | 145 +- packages/core/package.json | 62 +- packages/core/src/constants/index.ts | 2 +- packages/core/src/objects/beasts.test.ts | 178 +- packages/core/src/objects/beasts.ts | 459 +- packages/core/src/objects/loot.test.ts | 166 +- packages/core/src/objects/loot.ts | 979 +- packages/core/src/objects/obstacles.ts | 16 +- packages/core/src/objects/prediction.ts | 393 +- packages/core/src/objects/survivor.test.ts | 342 +- packages/core/src/objects/survivor.ts | 1212 +- packages/core/src/provider/execute.ts | 432 +- packages/core/src/provider/index.ts | 110 +- packages/core/src/query/graphql.ts | 498 +- packages/core/src/state/format.ts | 1388 +- packages/core/src/state/index.ts | 38 +- packages/core/src/state/mock.ts | 664 +- packages/core/src/state/state.test.ts | 1100 +- packages/core/src/type/events.ts | 404 +- packages/core/src/type/index.ts | 1359 +- packages/core/tsconfig.json | 36 +- packages/core/tsup.config.ts | 14 +- packages/react/package.json | 74 +- packages/react/src/providers/index.tsx | 8 +- packages/react/src/queries/index.ts | 208 +- packages/react/tsconfig.json | 56 +- packages/react/tsup.config.ts | 14 +- pnpm-workspace.yaml | 4 +- readme.md | 24 +- 119 files changed, 32789 insertions(+), 9196 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 lerna.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ee46f8e..229b5b3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,33 +1,33 @@ name: CI on: - push: - branches: [main] - pull_request: - branches: [main] + push: + branches: [main] + pull_request: + branches: [main] jobs: - build-and-test: - runs-on: ubuntu-latest + build-and-test: + runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 + steps: + - uses: actions/checkout@v3 - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: "18" + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "18" - - name: Install pnpm - uses: pnpm/action-setup@v2 - with: - version: 8 + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 - - name: Install dependencies - run: pnpm install + - name: Install dependencies + run: pnpm install - - name: Build - run: pnpm run build + - name: Build + run: pnpm run build - - name: Test - run: pnpm test + - name: Test + run: pnpm test diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..63dcce3 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,16 @@ +**/*/target +**/*/dist +packages/torii-client/wasm +packages/torii-client/pkg +packages/torii-wasm/pkg/ +packages/utils-wasm/pkg/ + +examples/dojo-starter +packages/create-dojo + +# ignore lock files +**/*-lock.yaml +package-lock.json +dev-dist + +**/CHANGELOG.md diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..36ff165 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "trailingComma": "es5", + "tabWidth": 4, + "semi": true, + "singleQuote": false, + "bracketSpacing": true, + "printWidth": 80 +} diff --git a/clients/react-vite/.eslintrc.cjs b/clients/react-vite/.eslintrc.cjs index 29cb6d5..1f68439 100644 --- a/clients/react-vite/.eslintrc.cjs +++ b/clients/react-vite/.eslintrc.cjs @@ -1,14 +1,19 @@ module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended'], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + "plugin:storybook/recommended", ], - }, -} + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, +}; diff --git a/clients/react-vite/.storybook/main.ts b/clients/react-vite/.storybook/main.ts index 3197ba6..2cf2173 100644 --- a/clients/react-vite/.storybook/main.ts +++ b/clients/react-vite/.storybook/main.ts @@ -1,20 +1,20 @@ import type { StorybookConfig } from "@storybook/react-vite"; const config: StorybookConfig = { - stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], - addons: [ - "@storybook/addon-onboarding", - "@storybook/addon-links", - "@storybook/addon-essentials", - "@chromatic-com/storybook", - "@storybook/addon-interactions", - ], - core: { - builder: "@storybook/builder-vite", // 👈 The builder enabled here. - }, - framework: { - name: "@storybook/react-vite", - options: {}, - }, + stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + addons: [ + "@storybook/addon-onboarding", + "@storybook/addon-links", + "@storybook/addon-essentials", + "@chromatic-com/storybook", + "@storybook/addon-interactions", + ], + core: { + builder: "@storybook/builder-vite", // 👈 The builder enabled here. + }, + framework: { + name: "@storybook/react-vite", + options: {}, + }, }; export default config; diff --git a/clients/react-vite/.storybook/preview.ts b/clients/react-vite/.storybook/preview.ts index 37914b1..19d2768 100644 --- a/clients/react-vite/.storybook/preview.ts +++ b/clients/react-vite/.storybook/preview.ts @@ -1,14 +1,14 @@ import type { Preview } from "@storybook/react"; const preview: Preview = { - parameters: { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i, - }, + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, }, - }, }; export default preview; diff --git a/clients/react-vite/README.md b/clients/react-vite/README.md index e1cdc89..297660d 100644 --- a/clients/react-vite/README.md +++ b/clients/react-vite/README.md @@ -4,27 +4,31 @@ This template provides a minimal setup to get React working in Vite with HMR and Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: -- Configure the top-level `parserOptions` property like this: +- Configure the top-level `parserOptions` property like this: ```js export default { - // other rules... - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - project: ['./tsconfig.json', './tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: __dirname, - }, -} + // other rules... + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + project: [ + "./tsconfig.json", + "./tsconfig.node.json", + "./tsconfig.app.json", + ], + tsconfigRootDir: __dirname, + }, +}; ``` -- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` -- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/clients/react-vite/components.json b/clients/react-vite/components.json index 1c6facd..cecb2b8 100644 --- a/clients/react-vite/components.json +++ b/clients/react-vite/components.json @@ -1,17 +1,17 @@ { - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": false, - "tsx": true, - "tailwind": { - "config": "tailwind.config.js", - "css": "src/index.css", - "baseColor": "slate", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils" - } -} \ No newline at end of file + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} diff --git a/clients/react-vite/index.html b/clients/react-vite/index.html index 611e2ac..0adb5ab 100644 --- a/clients/react-vite/index.html +++ b/clients/react-vite/index.html @@ -1,17 +1,20 @@ - - - - - - - + + + + + + + - Vite + React + TS - - -
- - + Vite + React + TS + + +
+ + diff --git a/clients/react-vite/package.json b/clients/react-vite/package.json index 8626a81..d7d9ae6 100644 --- a/clients/react-vite/package.json +++ b/clients/react-vite/package.json @@ -1,65 +1,65 @@ { - "name": "survivor", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview", - "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" - }, - "dependencies": { - "@cartridge/connector": "^0.3.36", - "@lootsurvivor/core": "workspace:^", - "@lootsurvivor/react": "workspace:^", - "@radix-ui/react-slider": "^1.2.0", - "@radix-ui/react-slot": "^1.1.0", - "@radix-ui/react-tabs": "^1.1.0", - "@starknet-react/chains": "^0.1.7", - "@starknet-react/core": "^2.8.3", - "@tanstack/react-query": "^5.51.15", - "class-variance-authority": "^0.7.0", - "clsx": "^2.1.1", - "framer-motion": "^11.3.19", - "get-starknet-core": "^3.3.2", - "graphql-request": "^7.1.0", - "lucide-react": "^0.416.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "starknet": "^6.11.0", - "tailwind-merge": "^2.4.0", - "tailwindcss-animate": "^1.0.7" - }, - "devDependencies": { - "@chromatic-com/storybook": "^1.6.1", - "@storybook/addon-essentials": "^8.2.6", - "@storybook/addon-interactions": "^8.2.6", - "@storybook/addon-links": "^8.2.6", - "@storybook/addon-onboarding": "^8.2.6", - "@storybook/blocks": "^8.2.6", - "@storybook/builder-vite": "^8.2.6", - "@storybook/react": "^8.2.6", - "@storybook/react-vite": "^8.2.6", - "@storybook/test": "^8.2.6", - "@types/node": "^20.14.12", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^7.15.0", - "@typescript-eslint/parser": "^7.15.0", - "@vitejs/plugin-react": "^4.3.1", - "autoprefixer": "^10.4.19", - "eslint": "^8.57.0", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.7", - "eslint-plugin-storybook": "^0.8.0", - "postcss": "^8.4.40", - "storybook": "^8.2.6", - "tailwindcss": "^3.4.7", - "typescript": "^5.2.2", - "vite": "^5.3.4", - "vite-plugin-svgr": "^4.2.0" - } + "name": "survivor", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" + }, + "dependencies": { + "@cartridge/connector": "^0.3.36", + "@lootsurvivor/core": "workspace:^", + "@lootsurvivor/react": "workspace:^", + "@radix-ui/react-slider": "^1.2.0", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.0", + "@starknet-react/chains": "^0.1.7", + "@starknet-react/core": "^2.8.3", + "@tanstack/react-query": "^5.51.15", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "framer-motion": "^11.3.19", + "get-starknet-core": "^3.3.2", + "graphql-request": "^7.1.0", + "lucide-react": "^0.416.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "starknet": "^6.11.0", + "tailwind-merge": "^2.4.0", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@chromatic-com/storybook": "^1.6.1", + "@storybook/addon-essentials": "^8.2.6", + "@storybook/addon-interactions": "^8.2.6", + "@storybook/addon-links": "^8.2.6", + "@storybook/addon-onboarding": "^8.2.6", + "@storybook/blocks": "^8.2.6", + "@storybook/builder-vite": "^8.2.6", + "@storybook/react": "^8.2.6", + "@storybook/react-vite": "^8.2.6", + "@storybook/test": "^8.2.6", + "@types/node": "^20.14.12", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@typescript-eslint/eslint-plugin": "^7.15.0", + "@typescript-eslint/parser": "^7.15.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.7", + "eslint-plugin-storybook": "^0.8.0", + "postcss": "^8.4.40", + "storybook": "^8.2.6", + "tailwindcss": "^3.4.7", + "typescript": "^5.2.2", + "vite": "^5.3.4", + "vite-plugin-svgr": "^4.2.0" + } } diff --git a/clients/react-vite/postcss.config.js b/clients/react-vite/postcss.config.js index 2e7af2b..49c0612 100644 --- a/clients/react-vite/postcss.config.js +++ b/clients/react-vite/postcss.config.js @@ -1,6 +1,6 @@ export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/clients/react-vite/public/images/bg-skulls.png b/clients/react-vite/public/images/bg-skulls.png index 997df8a0cf5f20f3adf8113ab1fc5f49e2f6c951..396a7844839c342398aad591cec26bd619934153 100644 GIT binary patch literal 34716 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 B*^&SN literal 252436 zcmaI82Urv9)&?5%C|j{?1wlc;21qw5AT20JZvsN-Xb_MtT_6Rw1py(_k*d-}ngQvM zSO}3Cq=g=(LqZTj2qXmVpnISHcYimX03O%cO~kEiSEJug8M-r&_R8@ z>*gTP-VY$qza0M93%s&49@z%`*>_LRCIAHD;obS+0;Ojj2VUd~FxS-tm35t*2TuOw zcFp)22!uiNY~J1j+C5aHfBo9c`&?9xG5Vx#`doMz_WHR?iIQUZT~s-9DNMa#rt;L+ zr2V@nj)B5g>p$2WI5&#F^%4(%KtH(GO@aM_Q_|%TVdJ5Em+Mjh|H+Pt|`q)X(#UJU>#5wKh2pQACu|q>^!TcwyhCi?y5Gw45PXFxQc8IYNoij zodE9d*U^0>=08qOx1We;r;PQ>P-0Qz>RlCCxo3hys@6Adi-g}i8|*FFjvSH~*>8Eg~80TZqt$038ZDU;B7fUg5 z0Yb-~WJZ1%I5bwi!qruH+!#E%{1~=O@4B!Z!YA5L zo#cY6H&@$Y5-X{#_eK7Z+%VnP-%lyHba<0c4Vi+)g zu(k%a@y4dTSV5_Lf6R~zrl|S_Ax4o!r@bMtdE8+-UUh%p`&Ew&nzFw6sa^kU<)uaK zZeY}!+&03+-G?W*xQQ*@cAO)IzZ}QG z*XsY{ffmh&j(wc6ubL8cesc7ml|fn8_Fw<^N#K5g5C1;tmMr1g`*KuQ2u~!RbRBwl z8}6m*>p?5yM{gsayO^5j04g@7dc5?KSlKMbiS)E}Y32UYkn%f2vCEnsmem>J4m2k< zVnyU+RqcR4CG{cWzqJcAJ-{EG6VAhMW-YrA6|rdpP9*}W0nLUrHs`a;+k$&9o6!z7 zKO6kNM}KC|nSbA&%45|JprjFHDJ0V0R$kow$Lgf68=hzh`}_#ZQ4t9JC5m$6)mil$ zOJDpb7$;kMfL++;GKQj;VC}+x z-%`O-zNi1(9%zn_2HJgNtjlbfp4c1XoUqqa0~u%qD4C+s@!s;YA~|=4Y+A!>&!^b- zSIEW)zhR!wY4VZL{zub3@adOH{bo&hSO4|T;#UjraUJ;Q1eEnj@YY;5W3!&dO~Ehy ze6qSHq*!-z*Ecdg%Y|{|@yT&sm)bu? zctWw$k86KvP2~jVF)X9<1M#M=Y14Svx6ay+>~lZG=74hiwTBLknOk4|f8QVX@p`xW zDK4rD!6OeV4;%?R0Z~4PPL98PKP)4+vKBBZ>K7HczlC_WEjRa!`nOXr4`|Opz7iVC zA&*+jli0k{JAMZQirB!Pl$nVb&HWO;@7D!^*Sb#qrm;M?{&@#9m(*UfcaTudr-2Ge zb73C71lng@95+UF8yb_wFXhk#JUt#aPlbezu6KH*H1!K{?8Q>j%QrQhbJV5%FO z4n;yK__rzEHq>YZ@gpawZxwpYspZz1)Sd48_a zy$=&$@_|owYFjY7bI(5ug9;*lQ_bJ6Nfx}7`ZX(ArlHAvbF0<##07zBy7&U8YJzFX zjH#;iS*xVdlGoEV<VZqQa^TFl_829C{*Ch z%~&Ca;MlW?mXIL0${o0|F- zy@98{_N}S9Jha^h2L9=#SS{$M5?oEi=i%dwb$5K|`+rIP`EQYTOSYewiOeggE$Dfq zuIw(?`KU3jG5a~`Iq4I8VFA0^ADLEvh75OrlK0(*pXASQ-S8ynDt*B}7bZpf=H6(scv9tcQ zMK}DXKFW{lh@V*vuGsF>F6*|wJH&!1(FYfk&#bkY;)&QDfSj0vq>M#!I^Q;+JYm7) zwcOZNzsK?ggp&@!=pCUj{x_&jQnkhKDfb*ZJE`9J zm!Im*yl0CKgM;uP9fTJ1n4#2_amD_6`|yEuhaG8np8@23?*!tV<7VTfllg~A1QQ|s z&izVFmFBYIi1!)l3|rmA`3j5XKtb)$O;?=S&f~d$>)^Xk{|opS0s9{dab=E1KX$}E zp*rL2T!g0@*1en_mJ{cxhNguebAc^1p1H%xEYnbJ#HyCwOWLeMQm9*Xww4GL}qX$ zR84M)xVmV8548QfnBR)IZezs4;Dqh~3dz-*-n$!iy^C0}*BZLczsL3Cx4&-2wJX-q z*p0?X`1fr}48KLs%d_);rOGnEq9X&{I|ySm>?k)d^c?$~R_R?7)HJnR53c+1J|D|h zb*b@6^*gnVT1Ug+rX2ACGJ8Y2j0d)Qf(vx_kdvNHhISV*Vb_tJP5(TyJ_+9%u9k|k z8H+zvv+Q6&4hw(Xle__2DQyZ0Qm12*9jUL=)QG%6#~A5BhTmnJ|40mTVy!P2*mQ(Y zSh0g*@I%k$R%XWC?pNEFzNSCW=3|W={6{=cc-Zl=ISGhh$c@+RnjYvy_OdI=UzD=-e3edVnF^X>JR_IH zwH(5xsSmL4I{Q9P&CJ(yD~r?qhah@k+S$#d^y0?u0SmaUMu10rUR$))Pq6ITc=@agtPA&9aH-`AtX zkiyJOm}7g5FpsBVyX9K158#^8xG!?y^7n3|5Mo{pv4d&O$3rrm?%q##ct)pD0cb9% zavA7(j(E*>wDV9u7~1I50nrJ0LPJ)cfXc|2`s9)r)=Py^^UQn5c*$lQOax@67RGBNlWT^a5$1@Ugn`_0xBq9Pwa?LqWw|=?bG^5)fv35kx^mSQ?XbETT1pz(?|H& zTWLwixS(m7p3N8VErNM+gIr{Zm}ICkF{-cLKc|v%nItgQDQaw(Rm4hz(9?lGo56Z^|sY8sxr3Z zbYaa`&guEWf!HCAoj&gV*V<1a6vTV5aBzQhX750B+qIlC7T8M7D> z5Xjp~F>;P_m`nEmC#&-(pHU7eFkZ=D6Nn~j`5t6=r%otq<;Y)FRLd}$&m*lzMGc_| zD@|4-kCPA!{-(zam*K|aI>L65av6NyTFJ+|wNKKoHs}jh?Zo=1fS~u|seL0ZmLX7; zRfkVn{fC5eVp+~D4+}SzCf7gb%CILwgWa3LU6S@&=#7+GXd4WFD4kwOJ84>AD(&fw z2%1CL!~gc!>G0gZ>WXbOkdDY6Dq9}qYkFO73xWa`nC%7N*)%I zt2PJoWV1pufYY>jPuMi zYr8d^p_g#ISNdI{^WpZ2z)sln``EFY6C@o%q%jMYZQpx?bfY3D=zDq1a|aLqBYh`@ z+<4@f7Fw{2`gx@Nb^-9yuudt8P4j@MgsAA7$BQ)v)!?Zs^Dei+P~|$Jn* zGwcZdmjeHpRvvm+9q(JdV|I4zK;#r=cd0uSm16N4ndhjWj!M@h;&?C5et#Jn8{hc^OTN~Gur3t}rft+zCvmY8q3^Zz(zJg3vRgtp#I<`#@J^Y-TAMo0iKB znUN{|b$4A%a1!fiw#}MXW}MCdt!JJ`!KRmD&EUqWe_{V2(O<*(cpj&HYLHEv|6L@pTG$zO4PLq7oOsf56 z^pKA+XVmVCimCWOryIoXejWPQ(ZtrXyooualZgyZt58MZjAJ_{=HkM0{FyZ9Wp-It z)r9%vp?5Ze_JXoq9$%Eij3s0h#mS1Jj#4mYe+{=5tmo;x?;+L~rKN#>;mcvF!r=)7 zk)pv;B=|yHl)741MbmN(-!#*1VdQ~|;!+uNW#5bC8mT+m3%)&1n$45h!d3TwOg4Ij zpeLO#){=PK+rhcqTd%-~jt3+CE_2cqwK?5ef;W`W9%Pk^Zgm8&9u1RTgmJ6ds zY+PInJpy|3i8RpBkQ7jZRW6hxj={?AaSzJqoei@)_#v;iOf>+E@s=)74Lc3iM;(E4 zl|T;|Q5OS^L`VmxN2KveEEw$KJMh7LINKa0E}RkKJJr7;n{2CXdg>9dsRlVR^`3vi zOAjNtJ7d-2!mB`nmj;S7F(HH$H zXQv+g0sdmG@ek0=@1CAB4sCUI4I2L_*KpV^FLp)Qr{068+M;6ny!=C5($8j>d}Uy2#EqFyZEIgzGYJ6M0Q zv_b=>u^S}#T7W|95ws+1gwg}yG<*Hctd@Ly>#W-9sKcfAu!^x|&2WDGRK&RGQbf=N^hM~<=Odo@;c+W<+ZqJrNk`^y*kOFZSS%NRgINb4Hpto}? z@S6*DBi&m!QfGi|SnuActzBgf7WMd)xeZaIZ6x5~&s|WEj*3{vl(FG2f{f|x?K5e9 z2!(cJwMzZ@HA2&bUzXs#;XvDu-F*?p>$eaWA8$7KMYp0A#Vz(B)?}y~5GxAlMPGyf zp31s#xNU9d@*0$i1d^Dml;^)?B6}|UTY`c6CI6g_%Ikc7S)@gP7z3Xoh1&#c$<7z_ zUVhY!RBNr^{ygEQFMr!VhtLv?mgvoDY)E%$l7Wb$^^NqpSU~E4%5|n+UE&>ztdKuA zL=e|DFp{yXBRj|j2=Vf~P{SPI0 zF^-cy`ncwHRtW%f_x+3pG#emph7lA@)R0?+czf;>3y(JI&Jtm_)N)TX&GhC4_+<28 znlEiPsI7KJyIzeM?@v=?Wb$@LC{wtc{3$)_YtiIs%?IxAa#@p>KwbHB)eTpak0)On zj!|R8gI@`?33+@I;7!!=57|sRpttJQg9&}fj5-jEyMbA3+#G*1Q0W z*?P($8DU-f?t*@+Yyk#ndT#9Vhh^`Fx#?iG-2wlQ)?1kMj5mXK?gm0lV}UBhmeE1C zk9sCcyy4^no-F{bUf;;>kJ*SD6Nk}d^y}Q*VJ`KyG@yiTERq3R9WFp<>l$hAl+spp*VbdTn8ru0E1 zX*FH2#R=m`z{VJ%suc*~zS1O!q2K7E3O2TEr5}B`bh%1C_=N)akQ%IH*C|aKjhF2K zqD8Yl7C_bX;H`r{O@pp4o}$rcyD3-1tWRINXN4VYulOt|aB>wAU)7G?DtbC5)aS&b z*|Xk=v)U=9_&vDZLwd3LBhVBi=#2D8_o61n;4bwd(D}Lr2~yrO;W$nh4_Mtjb;-y6T_(L3KC)z~!Rf4m#lDpS z+AyYcA7(A#mP>SLF1Xq^dBlb=ah0K6-8by3G7~l$x4|;l7MCx?QQh(t zSBOJ7ADMbIDPkbcekl+YJtvMf<1 z{_QgcY!2g??1z8f+=WuDgy{l&=_-Sm}`C>$G4wNu6wq}m0 zjyN!befrc=cBCRr&gsNGNZW+7x+kiYlJ>wO+4?Rb&26XeDfhnYQHz`wfNkB1{bnKV&8jOu`(F_SR)y1^q> zd>%)AM!4|PazCuI4E&diy#=hHsXE(jU$7k}6?K>pbE<$@HId56Cmb?9Phl@8%=n<4 z!!}Dv(TO+)UXu_8UbQZ7tA*LZW+8CctTrgnuA@;ebruq+VbJ$N%LqJshY->H-xbX2 z6WK09-%7%$UwG-@ks&hdx$o11H$9$orx7=lraOX=Ub zFJ^SeM|CE*CXw%;s??tLhT>ec6-(#TSgLa(zvfyw6=+}HoJRg1i9i|Sy#5@?{h@PO_LjEOGH&K6VN=7bNKpOTx-L0M#_ zigCVQxK(;ef3fDPG?IM7<@^YxstJx+5}ses)L&5UT`ImP@)i8c`a3YU3HE@WMpdS` zpp@k-8sH6gG?wVX*-3=0+s|FLFT(3G#~q4r;%U?XlC|&Boz?P=os7Ag?Fr>Hm2g)V zU?(X;P`xnSL?2UD#bL{^YbQiAh>{}|(mgzUNsHjKR z%==8L_1yhtz^%Pxn|H-@e9p8Gn(?vdP#yx4)lUC;Tlw>*wUb~_+Cg`a(e7|>zl%F@ zHU>b4C+2(qCGY=Q{)y7-Fx8)q{kfS~@Q|1MG0Gvm-P_vNfwn5eS!XW}U9p~)Ik-Bx zq7rsbK(KeGQ@9Ru2*{PwzY9}xHA^wf-b}23T?4ob*^TXJ7F!H^ zHY}TE)^?q?DYJv-+L)v#J|6O2#?U=Oyf@jekS{HC*|NhXYK!Uo;r2i{cx3k2?Py3uqMzRCbOC z-Xg0TW|&_{N{UZPUY;JG8g}gU_q~;0ZdWYI4n$pyP1jFl0^KpPo)m6ma`JkQDz@^) zt4AfgYgJN8&>^d+{I!p)J1QOF+|w9wJp=JX*phQH%tGXnWbV}Ku1ILRGh{6o%y`x6 zkIrXC$!RRdCu5UVAA>-rxUl0t+lxCf`+w6D_@2_Qo9`JlD>`>h>_g0D-X^oR`?Ly? ze?%>2-5CPnc>=l9$t@r={gk4(`*E76>otJDp^Pi`B5BvGaQqON1G@g}TRt~rT~l+) zy%3ngH*GVszTbI!$f=~GYBOy;StWQIqT!mIc@zCJCWT|lde~DL@Eo<1OT0GR-)cN} zy0PljK<(+nKtA8o3T%pr`uev*FR62jn>ojpQ(3-Ij^KE zS&qD`yHdw*vp+{&o>jwQthx<%k$Y2YeH;jPHmSCS+GqxK5f&H9xj(jSFNNiZEqzQX zEis)G#5uHJL?Sfj>@CMa<3rN|JYclyg_E#oWeHV&)yO}t$hf76QVeM)XL>;S&%DDN zr7v6MFQ?j1d&=6Egz&>%jvuWQWhR%(7ciG;u=^#?0P9u^JF*}J?z9t4N$+Hmb^epZ zL#Q?bfdXIHKV2Z6l8m&!FI4@K*yN4~u8fO+&SS-k==zXmf2yBj$2(%4C_`F-{Mf7Y zou?Il&B4TcCQ{5rIFeAFk+GD$kX1*l$^eVs|5`}Dd?h#b!}h&jsieatjm26 z-RwNteYs;zxWJ95?tN43R|a<9_#>-$*bGhR_z7&h0X3Ghe_C`Z&s$SZp80n5H>!_Q zc!mT(xmrDTs&agzcE^#j`8F^{t+fo9UdP)6_GVDnXW8p=^==8`Ciw0@R$v8SF?Hy_Z-hd**cwH`pjzas`mCY(D8-VK;{rIoK^p_Jui&lmFic*{jz&%G%~Ie zfOBBifR8U?>hJ6ss0iv1AYZjQ01|DV0hUe#3#R`1c6R zl;&_GG=@A~*I}y|EK>lP^H04TRbx4_-kLNZ1W;+x!2jEW*TE(K6zhM0xg=wLpkufG z*L%A83D5cV$@dto>J*6|$QnO=PCV2eci&NMiVQJuwvT?U1rWMs&F3=x_-4jjUqGY#n@7EEWbm1Z!iEZw zZ=BNtTW2f^k~*;3f!RxA$_-pTh}Nt^d%>4U7-OR6;}$CK!>O}QcIVNrzDa+6Zk_DU z#KVK~J&a7e0Q#B2V9C+mM#k+Tq{%j)V&OVul|ubDEe|m0h<3FcBG=7AiEh-_fbFqM zC#K@K>pA#TXH&FXTwONZiN3C}2l-;U2{}mK7~mJmx{?%|f1Ww0xc}aZS-Nt>!m1LBr1tP~UGc}va3~$u@CsWM z_GUObaq}eO;aS)4KsA*@%X!@19Ma0i*=Uv;%jX@%EpgVBlNq)Pmi?ka0g%SZ0I&Qj z48cPKd}2Ivt7mGN)qe_SUBYTo+AY6}oz%}=L}D%Hh#nJoN-9Y@=An#WAbI4HaW@ z$c9_!s@H1efjNyad6<7{uU(TDZN4HoCTyv^AFZ$4q!*cD+C>&F{B(K`ta3|!JJa;# zGr)B-RoWIFjy7#{xh}6iETlCkDb!sl>;103kdqdZ%UXXcZ7GyIywhVC@lJ}H5B{># z&IQ^2m3eIkseT@Fzw@>99tTu0%mreVuBy1vOD!lnpj#Xkl*l_LlN?Ews5qd4z;ENPgmDYaXi`xNS8M zU0;`ddp~wGpgdn9s8n=$Y=nq9j~rl}Zb3TAW34zuyHJM_YdQ4eHDBirJC-@wT0YPo zZ2U~w^=vR?zn(0k@r#-#;DKdQU|p?o`RQ7h!NYH8PmORL|C_Ova_N2hcDBxD0Ff<;^3{g%)6@eu zvPC5T7BqHsayP@)5~g*rTBUB`VL6beYmU4E^cxfr1E0PUdD3!DXb#!epDz5c82sFP}4N=|LxiUgMhA^^FC^Z&s$K_Oz#dSr=`g|T)$glz)G@N z&QJJbrwosFl0>GQtY;pcbjC<}Q`6w%4-b_x6%S&9vhKV zyQ5}aQ~E+6jM~lrOxiV<22VN_D{kJ+DetaBm+fEF?a%sE1~KnEoql}wi6QzoF5z!< zav8U|Cj^NN)S*e|U_tJ>#lBHZTe0cPaavT>*iYq~a1LKs{yua*>)8>(MB;1bhx`S- z?AgiJ9q6vNzqkVQ$vmA7DD9{ES@GLJ1C3JI<5A%`>#(2E6DAKGQ0%n1H2|!{g& zl}D~jU#jUuD)O3(ig5RFZ!{Y63>m3)GMQUu@Vde?jE{Y62`j{f)imQcd`&+R0o39y zr2@Ta&;Jwb6<7)SU$7ZSeVbBw22dvd+cV#VWeUlu5BwzNcdKI=8B=FkM41(~>5dtF z00p*&+h%Q6YdihWip}(;$aR0;z}q^_WhaDkY3YZIE77No2i}LdT`Km{K#`qNOVrI= zQ2G3}qMD=?57@zXODmJKua)cmXcrW}?48&;DN0Vq?g3)}-UFWemYx1aMb&$7#dL>m zTGkd))?21$0?&G=aarNPP?t$g&%c~lNzVaQeH6FRTwvvghfj+%m?A6FU3?ij8tq%j z9DA3BG=NGypQM5urGGrpIN1a@xJNhzyar&*&>hgRWF%m5?wfDIkBW46&8K-fpL;^d`}h0b>krr zdEQ~sw@2w;*?_vpS(^9Z^9ENL-VOv!DOMW#yW!BBq0n7lAbiCW*Jk@L$7@zDj{af2 zHfmy6WTD7{#1v5e@>LTM1U$Lw6tBwO9PyT8l}CUW3L_ODI(H}la#&s7saZkZ|HR|9 z+=Z^w1!(~l&b^rp`1}w58-sVPaJ^JmGtsHpBczqwwBjeye#S>Yam4s+>FAF7x74&j zluJcMEYR&c2rRI^H65_9xVMl2!}_DgBmgv~qI373Fz5^h100Fo9Ohf#jZw{wG**4WPdKnVn#FY}!=eA2{Ud?*>5#kkp6s_|1 zv2kVQpY@}gma=2VDHM*(m&wf_)02o9XJW7~Q-Ynx;*2bDiv7qd-i&M!VKnQHPbATb z98-}H{D3-na?|z72C_34iykUwO0r@9gFv&TlDI@6Xxs-2J-O2X=6_GCphCA#^^{jtkn?9^4?J;4ZC!q`e_C*okQ-N`jaE;{lzua;r-+()i}r8IbUGP0=G=bN0WU z(65Z9p5SPADtN3UP2q#)K7hfmcqb##V(P>^)QPCCq!WsIl#J zok8-GvZ7?z;&dA0qb5l#=2)fAz~#DuHA8$5Jl|HxCEdChV7>SpEJk)jd@U-pt8Y5{%s_#E^w(zgz2w_MYpKG; zcBTu>AkZ7M1%}gxBw0AY!bh-W&f#J6mQaFk8W?lJ8 z%sHQEX`Zml9H(2P@!8L&m91V4lLzWfV0*?#D0IMW-ND64NTEH9?=L^R*9D#HB}sII z0A4P`y-PG7iFNN9>lv`$4=`$@?GPazyPRYIYLj-{Jyq>CVAkQ5f;fMMoF<`6w$7rs zxf#}OT$89)gFZT%;@t)?U<{*$oPmn3yyA?sooIMNq;6&F_YyRvkKK;!;+xK9rIDuP zmO>3(hzDb<@`3Qc-jOvLF1EeREu>9lB+HE4yAz4}iL$X>@eK>3Y&`uPGHSKP=gsWa z)qKa%_M8yYET&JHfQI4l=g-TbHUsiKsE{fp+rlqMwLTpzhw^smN1@wBKvoK2tKAf! z*PKk;ns~tKP+=_RHi|Ca8%-TGpABk?9Ygo9uMce6T9Ao-F5U!W71$u)EV9FijvAFG zn=pM5ob!uK#WriCNn%wxZf3Dc=F5)5bJMle4mB-6Gn}{R4<8|f8$=m6MAf=Y+>yjK z)3{`fgB5i(NuoTyduhC)%3VQ2b7Aj?M4RHChpk_JxrCCxaBP0fKOSN*jE?l!%%?;L zekM01RHZ}80Ncs9;t2#gXM;;-GX&gW2jbSi>^fSeg^f($cu_GN;G2(n+s(Kkz%#jW z>c6~Vtvxk}d+Jj!MS2?LDHk!MNfK|WyjWKq;g~z$;Fjolc>^Zd>eeqXveONz-hi#y z{^>&T*cb@>5zn)`OhK5MR`T_Zob@n8so&w!+9d9{!@r8#Lb!}7N#FI1ml7$(w4kY( zei_mdePc7SM3Fpv-kTt33^X_Lf9pKibVVc$^eb4_;jIcE7YPBZLx0thItS>SZ)8g| ze8eatRKBQPU0bGlbmVmub58nm(un^&;NgpqG-ngSvW^&I`|?bF9?l*{l5G zC{f$lx@7#y;PMOh(t>TpxCKg5CRky6W7vNqJmGx;WfE0Z!H7pb9XII_IdrqlfHdo5 zpZ2VkO)2h+m*8So_#)zt8}iVY>&*r=)&m5OzfNk+qzRf}P+}bYCNhb?VmO6;ToY_`cpYCI--V(oDq8DOWMVL|=iO9NaC`0lP3! zS!*fZL|bHUd(K6(yzR!63oERnm77CnG6D<$!oW_BXB{L9eXbgwRc@iUJNML7m>O!q+nu<# zMERl^dk%K{v(ud+p-HSBbv8AqJlnCGP?5u?bIv%tLcp7_6XUuNTzCS|(}Fa` z(fOoc|Iq@yx>eMs&BkN}|8UM;8)xi&#}aNaN=<3e((t%7GE}zlVuh$;_%xcS&1?fQ#9gy5$`9pdHh8yhmdLlj`|^QlfSu<+FNz8hqB3^-G+nHp zyIn3JM$usq61iPD#eZUhxW~g~DVM_$3S^eV_V;of0rA4cL=CjuU;EhW^>vW?Kdky6 z7UCZtY&sO_n-y!lJ)1F&ZpVMnpt-~@m+}a`m8QV)`yE7O_6%qMp?T`>lTk-Dgzn7) z6Eg+=LW^IA0%4NbraqC2#eGLu)iY_<3$qP+Ujj@5avrs$pO3L-F@OM32700;C7ic| zl7FSF&;bep^(wSi!(VnqVw6zx%==;*+oefx(|fJU_id=%^b?GY&FER>hP!^ZSLg-{ zRS$B@~eAL~`|x_U;1@0ar%mH`%mal!kq?ob%4 zhD8fJ@(Nt^C5PsA4T%r~Y(Cl7?H3oegm`cJ0tA>)GJX#KkdPo|Sx4~i&jl#3#-y`Z zJuOK&M+6Pcpw#1_m+G!TWvTQIvU;aS?<^~JbGt^-aZAW1<8DADoV}R*Rb!GadQk!a z!FfrN2UeWFliyyX$|o1@iRq?>nh<&8+iCz-d5)|5+6PWR(Mqt zR}J0+TR1NHX9iA8rK&pbYM{D=nv+lS&>vo zL>;6MF4W*dp23AHXmmNrB1w{~tFwHjCeb-d8ls4^_9glbX47mYQ8(niejTNOJ)O}P zmNRNbP8(pi%V{CF2VHTs?X5^$TAszJEiy0{8#1Z*bzB}tY)3BTsm;8`8IL6gI=ybi zd|q3bE8928eE8aA1Ln~C1PrrlenynH6NG??bZfd-dO(f$u#>k_%AVVRXL+Hzl6e3Q zCUXh#8gk%Z%wWZpXth@@YHmVp`ppn0V8B?k0SC*jl%1XmYZ8y!5MS62I(6^_JT1!e z#W`HTlf&gx0xkR|F@w=n)%{;T;lX4|!MlOKVhr1Pj8+Vk^ zuz!A$D6#EZj}H1eTf!bKn>0k3e3x>{FgmX9H5PsNrj54^QRxpEg4lxA1)$;ez8-Sd z6_t#)uUGj10dGF&jbU>r(np4=F$mPnszCqO}^hTSa6%`v8 zB^%;&f>m2hPhPw{A9k}Q`zc=#K8_2t@OAcNYU!r0Bh;IH*yEL-zV{o8PJHu|4doy1 z`Y4^@t>?^HRX4U`YQqghVbnb{R^y-L#eMgR&yD@jW0xsYlOt+efZ6Bpe5-r~`)Q#T z-ESu->{~5Sh^KF`p03rxjGIL38Vp29l%{v88LP9Kp<9kQ%2+&=LZ7RD#ny++25B=a z^1T@JxfKOW5JfIUDAXpsMhqidO!JOKRkNGTNs28lm0#Qc{Jv)E$4&bYSV65xZKFg} zaMC}@t338&?WKi$tJ$Zy1~%+>NhIErs|^oj|TnARmsqh zt|A$$kVAgy)8miaJI7kOh&x8R5L2q_+Hidl#nyGk%^eFJ5Ytr__|TJJvb>m_+P=29 znTJvE0l}PmxcV>vS=5~i1_MTz1+`jtzYWNUY{}a-pQeTlKPd`4in%8jFN+iNr7Iyb z#m)Axb_dirFkGX0%+eWK5$iHrlRA4T_Kp0^Q9ByY&6*tUf>!tDQOtH`QzaLog?EGc zcq>dgN*NQoJ|V};P@KeQU=UxU_yl|F-R7=A#`ITQ%zD=l#Gi}m`i*r9oDQdzCB*2~ zkFMg)=A~Q1Y);>i73sw#^BB7bd=L?iZTUk0LpYXh4`P=usJDhY*|f^#X%v=l2Osz2 zGQt%;4?AZ&#~GCC~}5q&XB27?Ldk7@);v7DUbV zfJogzp0)C-BV}oosLtNxFIJ5_eyO}qyf?ieHY+D9vd*qgzz*FJ95syk|4qk65ZeV) z|5_~0Z09aKakOW;xP1Uhdj;07eC6xew}Ekn73Iil%V;a-N7uc7l_@tYuCw5uJ_A|y zPOZ;exUl4sLQt3mf%Il~X7jBiAx8D$`fd{jx#C5;QVEMc`W;g^UR6&K8NM27MFOf2 zq5Op6iB5)0cu;1xZqF2A+4)GrHauxY`%9RyXi2iKggr$;zYQ4OJVf(VtnjM3lagL>=FV45oGdB`l!E9#uuh3v(O8Pzdfbh;T5BgI zo7IDjgN5D>Bs^v2P=T9VBCh+&uwS zI#Udz`c{48^Q90ft}eyZTm6}y5JTf_Fwt_&bD{P*_;J^%)ezIluID!&6=WMMNHy`3 zxZiFy!0P30DwhQr*{Ap!2(>)*K5{A86in>7W0RLn{v)dy1qxxY8;1sWqMDc}1v3*2I8`yJS9_#c&%va3R06v=99WS> zauxyIFxh+5Q*8I2PO9Or7Dg$G?Q;73@2}IiT4=O%=t7;t=;)L4&>tcqE@?mo`>TeQ52wa?CANt`z)u2S zkKz0O@bw*TO{d$|&Ui*=Y^aE+2n>oKMG&M&3o1%4(m_fzks=U~76cL)$AW+mrFTS{ zbVCUykl-i~sXL=iO!PwbqV~#x)}`v2xrM z-a)nyt|#sEBd7VdsgpM+s-0AiTj6#{>@NtFf2U*Cc-t)0r_&w>DOpR6x0P9w8d0<# z=C$Jv!oW=Ef`#w8=uJ{9y?}*;~rx4wjYlN|dZ5Cz<8DvO8G%zEFqWnZMM zuqLjsO{OAaUf#Z(Tdi8yD$G&M4`)V3L0FL+EUNi4|F1M;AcnKmP zy4P;``-7E5HL5=|hCBa(V--3a)A4}(huBi#2}(S!UW)ho#R>Rmyu$BN)9GC%R%m`AC}*Zk zH4es&(6~l$@6lb&tp$N^;0c!|3JyGrMsvzN51bQt(`_JrDzUu~zOugPkCHF&QH&X8 zO9Z=sQpgW&(#%@0u>qRLLGrjcPAM*rD0OuJ?AiQY^8*IXLWglzeelI^k603C~RDV5Vc&hlVY^Z{vFZDnHlk{c(s&c=DMqoak=dj43iTVky6cHa6FP2{M^^T z+3>}FuShYdP2&C|^=dI`r^?rI#XpGnlsm?fqOTx(rq0av`Da&?&q@?5Dm=$T zYK)H$%WrFiX6u%!*7;VNxjuO2A9SmKZ|GRRH+*j6cZN`Um_qQhbzOW$rCeoN5>v-E zguUUVjO?irs^*;PefI{EhQ<*solhjFEo|MfbLu?dOuj~bXp8!Kd8S2L*X49y@L!M_ zx9qnxlWA;+A%mxwUsw=dj9R67?KilJ2w{Hd3V_7w+81 zyOR^c~?_(PU?~_+!MEpMM&0JjUgAqwz8V+DBLKI{L}nvz1fWw zgZdX`D3aN2Y=1A^Q2sXM)p}-=3p$kfwzS}MP^rp9U?o_37u*atD?H29FT`#P&wDZ@ zB}$q|>)2<*(_0UXtZ4)@^zb2<;amv?lz-lzOX^L z=bn;_UwT4H`q`ftvG2P7gZaoY&8?;hVO@rralPJ$0sXnOf+BoAq{`P~VW^?%_}po? zvBo8v>?XGHh7Bflc*~yzwQX;&uQX5L+fSyALrYqP!phI^B2BWjg0#dni!83@QqXia zdsFH!e>pT$lGyU(hkc`ph%9oclet+EVhpX}$3nAi?{v`VAweyGDON0#!e1TSrRDYY z`l8!cxo~@V_sm!rX}5+{$_5t(GfT3bC-goIAb5(1Q;{GN89R|c=nk9n(&(cMB zteiNd!+8DF^-_nhAYtrK^abd5R4`juI@elCw~P0d))yCx29 zNJH5`@x^bOpHvOL{4GwC7dptRjouH{A5$|X4_b^d+d$$*6$uO9sZM3c_XgBO8<@02 zeCEjs@wlDd*%z8I;*4AM-Qh}PRA(Bi+UsNECzUl58XY^Ly!J8-ibIq9#7qMhaTi$e zl_vttnJO*SbZ>`$!!ff-b7MbA)wgES4o}aIf3$&0w`drh&9`n_I8fFyMZ1H+#FG71 zWBj!1&*8$gLpwNeu7KyMq_L;I|BfO#kK|VGRql)5-vDVI6smD44QMK{l4NE--x`Q3 zvD7-sG4`cQ`ie19#7*3rz#r4t7psFEu@+6F%F%qo?Ut=C>CU_wvBmrPR-3c> zAc&s&06DL)7V)$)0lpQ4avlkGyEL&ZGO3CC+~M`Ip*g7iV?EhWH)$>}`d<=8IRk;KD@?T_^@GcQjw%Je8Yh?Wxr7=Pd6Ss?923f0tP*E! zEq`w7z>0)wWL5^2`ltJL=ut!!N?K=vDfGQ4mTsB???*d*#9kih{CTR;5FlhyFl?YW zB$$2!ka+MTd1)Hgp~Kw`Ni2tdriqJY{X*1HM8O(u{k|Xp(!O3UQM&|x~Cye8J8th}5ev32urB^8wwRRa|S3lYnIJa@w z$-WZ_(6fIw)>v#(fkc1RIo?N=w-P^yK!tDQG*kXZWW1yPNLo-c9&A!(=9R-=!*k!T zi%?3LuG`lK`|uUrqVB{Cj)7A+uWh$6MZGE)n?lGC+ptN1pQXmaPc1EB6xED!=^x90 zmzv1bQXo*yoxf^^Jbtb*A5VWS)lENNOrihXTvs|QynEdi`jQZ6?uH#A!2Tm1EuDe{ z7^yltM27AQr+d)vEFQm;@kXu^Pc!_#jVHeE=m}C|&8N@JSud8ciy#q&>l%P|tKGSA zMpLyv)c7y|j6+(5ugoVUy-&Qe(r#&$doe$En;+SR0uM^=HOj<*-K84+{SF1LEV|v3A3Tw3o z%Z`LU$Z`YNEP2Ph>eo1QbSO}70JkSqFIi1C#a!Z@mIpbfwnQ`gtNo;! zZFgYiy!KjLaL%QY!jW$;=V`;J+#BSAvgP~@g)jDxHbcpg-fOz!4H;{@SE`-5I(%z# zK@VX&et%RtN#)_hUd~S=IX_!MIs_&uE89GojvS2cBztp+K(r%u4Y=}EvZO=N4>lXu z`4JJFks*IwhIf_jpKw6Tv)`|+rrKBg+#8KQutI1kW=wl62X@j|MWqw$0^CsYtE@F% zn8AD%&y>;7Nmq7tYXf|xmK(t^Bb6q7rU}@dfW~FO}5YwHP+SKKy*)E z745uH3EG)ZJ#x-8jqgGJ$6M`Uor@7Tl?+q*&qJ%I+_n0gZlmotF=x{+o}zvR)7wbi zqs<3nLHz>^NB;iX0YA-q%)=6$FXD@&B!hK&nW-G))be%}WejUjN{r!VktVOt`v_B) z5@W-{pb(wPh@a%^dY197djm7PUCubqu4}hEP(Zpi3$fxvQ5jjnC4bM=ibu89zFLR7 zN`B6=?$pkc11csd0Nu8NC5DKWu9iSxJ!s@uXGj6LNSEmUW0e7169G-%cc0n8^WJyn zf`rAJtTs=JBvO@SjDfpDl)Zs2W7PuEXL@kjIK=KrcDH45n5kJRLJ*f)vw*HU-dABy zNY}4Ga9Cl>!M6$3J?JxxtIX#Zlk9!W3BUL)vyMl_H!>g%mCZefxbnQhzOPRo94_}nPHC{9_JvIS)?U)dt%Xkd||T&<(*9* zJhdT@v1zJ`CiRMN!?!1(auJa_etsLG+v8s*H#frqZi&%I32r6_nI=Y-mrpk>ew~|L zJDgX-+{5)>nOt9Ar-ZjmZfC~H^9C+om@|qv0N5POuE}uq-_gZ>T3Q@#M1+`}hQ%Do zN@(NsI@Ml(yL6<3!MMGdGo^#Fw!#dSlOB8zbfWv1jm$ArNAB^68zUK&MOTstE00R$ zBJJ4c`|Z7W$N1;NQRBx+K$U1SH-Wo4+h`cG?7YxuIIod7q-0=N{`ctCXt*=Txk?UX zCB{GLmiSS9)TpD(+T?-cv})q{!4y1Ap-eLuJbDM~a`=x&e|t7qWa{>%y{$N*J|Q(p zDg3Oo`8DDh^$;Im10WhzZ!g6{+Q%8SD97E2yt8s?E)8QdM6)|bJ?J(7f}+0U5+Xb9 zkkgq8DcgU0gXj^J7S%-m%J>L%yjT|FAle zfxCqrc~eXtQ2e71@K+~UFM=-?V+RWXv*v>BMm^%NreXq2TUbjmA*@>x*?Ht>**_n6 z@Acuq9AP6B`c#JWYS;hvoks+|Cx?UK`Trp9-}xa7>Qqm(yBO^CUK9Ib=Rq4DbhA<; zfGVC{{n@~(dQ|HTDci(zxaf7q?X%n46>+KC`h&$ImbqXeTq34)B`dCt${f~nff6qV zPenv5MDnfeoZ9QMC4)!B2R3elRpdSK4pjKXTYg4{?H@zbfIJA~og;nvfJgLh~Yh zC7SCMZAmHiELIb@RK6}QOvnQA-kelbu4#DjKz$9G)96q>Vv8dwuV&YvS4D4|aIv*T zu1?*bS26;MUyw|UC)zxgsHcxWTf_GdOIRy7I!6Ty|zoypI%d+ z_kB&aikO72NJhq8?c&&eqPx1vM-00C9&+mdId^&<>}LdW6KmSmYSp$5NO3Up@t^)$ zo>jUdF0JXVrCXI$djm_~ zY|R$Y+j*0LXy5Om4IB-p2}^m}0UMVFp79o*?2o6-hqE1hOSI0cv8WNHBZxrtv_FMrfo$P)_u-fq zp)Yqb6j%#jqnDk9=@1y*BlGa35ZdDxn(ppV~CteEL|o@|1Y5l8>E|QoU;GW1uq#O+yLfVRM9>T;B)-RS_cl@W2wf{;$j% zWl0S)6#_pgHH@OkzMh@OMNHc4#jP^kMV>5zcBC0+S;-7?aXe(0ANZ1eE`IFZh)75$ zh~jjm)@RZXuC8hIN!-woVpt+Q{zYxn$U{}7^TM7%=pKsjsYG(HPlu`Dp;$5#X}`x^ z3isut<`8|Mhv`$@6}$g-d^LMHyGF0=#`9vYKh}#s#zH5?p<&@#wiA7O_HNZkCK6Yw z{Oa12m2x*O)gF&X1>2v(x(GN>MF$h+LVsmNEH;t0TYGZnqxoC@U z&E>*BNs*{mGppUYH8NG`vgbjLSz(D=rq4KojM`<ae@iJlEp}OI56(*dr4k zlP3*1H3iE3yt_sNM>J;i;49iZS+z34Yok-TEHS3m`&C|zwfcOTkH@@TiwMTMj7C<(Nj$y;NQY=5mxQwZU|8x2hxcwXV4G)$3l%a;RBZ3EqeR7wAN#s8bA ziyO*UMd@D;_Dxu4aRvqel*(PzLK3VqGBZ3Ya>fZtaPZ<;(9g%FHz2MVit_yTEfsqW z^G5PyX$M8dyKkg63=9GTbr9*9%qjKMpuGuHXDg9Y;)r9yhf&yLjNE6Kfm>%+Zb?xH z&Y#xrH>PwDuR3VzwpmBoPhZ~-f>xBBIGI>S^eqcEgmCYzMiRG@H<`s=Xi;n2wLQl) zJtATp4(zC6hl5TIEvY|+9KU<8G*@FVFG0l-)9-(e6Wzn>7*|vv_?%-7f=!ZPjOzO$ zBkEcF6&C3h^n!@sug6*6u$G63z7>EXvJPn{nooWZ$)kC)9b3j zmtB<5zHc$1V~3vPhyeG=|KXGH-%P@fJwu!=QCfVHbT}d6LqY8K7GIA3oaBLQyR)D( zQ);@&*xMNHR{Qi1pxU{0G~4*=q8D~h-*h4KPKwUy;*uAmUz%*^E?HdEiu{%S(C_RB4QQ(TtH|1t!Jg2w+uBWb?CZTKw&+A;R{gPO8z+9P5ZLAe{AZQ} z9W>=R*_zY1%kMNQsnT5U&3t1;waa2U$RpvR8tBfbMU#qXQo`ayOf}(XgKH`Rc5t6} zB>Hd~%+jhqT_t#;M3Xf$(%}tD4ozu_(k;=fS}QCU!WE4ibg`E-`xZh`jGsFegJF?5 z@iFwR%rZ87Wjr^o!xFNT={>C!>Q|IS8WSj^))+tZTuVlk_)|L2r&HN9lK62fm3PBO zSFx#Bw(G5LxXX@3r>=y|zOD_JS@YfOs%mzJB>J1Jtu9-s3Qk!;Y;Wrx#{#co!cyiB zc1yHqF8P^g;N#|NWFfQraRx<>xHETMC5yn;1aM}3VA|?{zu-XH=-C5|X5cM@X9Z)k zFIgz9eT`}9PPYhLJX#`Yx! zRx(#1u{!FW4AJsQ1dOV5^7l_y?htjk?-o%!7#92(s+jE9oP3PXgGMn2;pwTL<0=I< z>aM|8-o+#0_VScxl`yp2wxkLN2Xk>JOqw&GJ4d>@EDE>$38j1v@%$65{iIyDcy7O( z70yCyV|sss*pvRfF2}bs#97-aIS!W&--Hc*ms1bx3 z8(dRf-6MRCqUc_dM!Gl`!@obPpo{-*qb?=f6x11VXQhR8L6|l~POr6;!UbrKDMghn z(f*?V9@nh&{Ie|H?Eh2#MpZz3xbu1<1vw3IwjqaCu%yr1Aj7K zeC{@?ZD|)PIXtn-BbB#RvLpVH0rE?g%ZP!yBB}u4JoaI|`yh5&Bw(knd7M-8@~OfE z`^je){grJO#-S5|Q7;wUClGZNgioY2J|xKJ!k2LO6*Em@T*1rx*?WW0FOuREiR6WE zX?(Gc@rK5w>S~1S4B@QreKp6$4H`-ld4GOfTDV1p=Ui2vs65u?s^COyT3wb@>leL& zrl_AeSG0{hV>cNu0Y*LD8A`oo1SU3N1z|kV@AGH&&!2-k8G#0~Lth6r>B;`X0*bSb zh7j$yKKQYsaH>f8q@^@?l#HjP$2%w8GMJXHRCR-*3z_S z1IF2q4&xUMIXt&m`zR$M8O0xyo2GHWIgvp`-Jv#V-MFnPYEwcJ-=-q;ZoA!(H@xr} ze!bm}lizknQE*1wwL*;b5@^u}ZXI6Rlb?jJZRZfF)gxQ1@mVO4{-h>3tJ^MahdS-< zToAmoRMitU^_uYr1Hf_G+?t3iPBi9gsT^{6<1tPT*;%Xiq0+!@>P)j%rXxZiXVBtE zDdRI;_ZX%FUS;o0yC{UexWcv=z@^Wex-G$M`d(*AN+Zs^9#^acyE7zrEUeH_-2t$R zkkvEPc+I`-%e*t$`Dcr$QMDMZAFF9Q@IBDq?ubcc{&(sL5CYN+HUH3(J@>dE_u||c z6gHD+sR=A9(wAGD$N%=GxU%9NvD_GD+KZaC*#}%H>yE%+69;M*zFszaGIemf!k7&7 zbXk*4R&MKnKF3gRZUx*RvZUWU7%_{v?YMSi9{Vq{4|q-$s7Pn=;))XBM_|7&)adm( zF6Lg-$b0IcJ2=QqnLy;z8=5La$_}~;%CO|(&r7V&s;H(7zQ?ZA)lXxV<(<2EY? z)zF=HW@LJ03r`>cEsHqrK_N4asJ^w}XnU8>1rp0p=;TZNVJa%C-ap)60KT%76W8GY0h8^tk|WjUrhj%~k@fHMfw*$sK;UV;Ua55S8E+@F zt~@L(k_#g&Jn?Rt1ydrY#2hN~qbicBlY@uG9TX?J*Rm?pvh*rTH@Kj&k@tR@eYx9> z4nM>`8O+ZZcp!;%l|-V%KB|jPp8}4>dJ=bC3yg=KwgS>~=t|>4&q2|ikNN?= z4W!9}Wy*@iR=Q3YRa& zP&6e)^*B&sE-3cLwxl$~yFC5P5;|-*Y|a|kD_)cBvPgoftGLI;b(Kktyh-Df9Yiq? zyPGVfx~R4ltvN2>uR1N=7WETsh;Q3ppJ0=ws{s?Wi%T3jTLqH8r z{Ox*Q$o*tq-L#4@@vwW%)H|isi0C;F2AmqA)C$=<#C}uCzRtXPz9EuPYLFLf*t8c4 z2rW(zy`P^{Uaq1pYC>XT+gb#yJ$6S)DZbn@(wW6MD`-DSV)nic*TTOqgikD5RC?GQ zr`@C<(`dc6R#gkFWO2>aL;&#&JIOjh;-04teSLjm9~c;H+tpOSN5-V@^f9SnfZ*_7 z5d&egOBeRL=BN{seJOWl)L?2;{({QC$3x90RWi7)?>@@t=I`?I$lEO{!0R}Y<-a7U zz=0br+?IeKZ;#FBzH~o((LMF6WtW2wo-yS1ZZ{zM*y8qA2uM)w{a1}_cWv1iwlk}> zRASmYt&g1VH2;J@%Nc#~I_}zK7ZPKCe#7&rZKY}7dR1D-A~hy1-7kN*gtn??VG{%4 zri`gF;`b$1z;7dj@7E0D>snGL@?ZEd5xGK<)^^}Z&kzeAVNQjx>b9|TpG<{VRm$4W zU7;fiAw&yT=1tLhYaZ7sMb(HZ6pBVuQ>(=0_$%-~AZK`zh83ch`1QipI!V@KaZgzFBQ~jOG3&Qj233? zm6a;FoO)bg+_xphh@hWX{aN@DMnLg0gt^hgyzIe9^Nq?l4lvMpXYmi};zE~}9vl6~ z@V5isv^FMGL29o8Pf`agA}QK-Cyt+#a$&BLb^o)*y_W8rslfOCFb2 zqtaKIiIGhj8>zkJgS%?hqQ`wRP{HSF#R{cLHi2ZLWsdM|veba!!wkS6qMR4t+%ZED^#;!LME0V>; zEs93hnYU69@qIx2(XXL)iD z{LgMXiLI5$Rixkqhp#HX3mMR%h>r~AnC9B{g zp&IU{;b)lJ_OeVHPg8Rvv&uG=n{J-%gU>YVn1t84eRUKRvuUYuVaG*vsOnZxh0a;C^10r$21c($*_ zV1=XI(%Lp2x*(|GyY!}-$}^e&+;FgPqPETdj(Ij%S20c;ytn;iDGMRKyOJ7EXL&8P zis65Rb_h5`GUjOqrH(;axvHXc;*7Y(io@EuX>e7?D{=W46m17zT2KjcHKg> z=o1+5o4^^Ha1|*rt1;C%eNGHEd_v5$@|WPWw-YbU{PG&zNzN*kMBJX;3TTPAe7aCb zWi`UTB?u2?niU!KPeZ!W3htuJ3O}O07=@NlhLIpM@#!ri=aJ$0y!yVWOkptd<)g37 z+*l2DeAhi}3&b^ti%TwmHDRYZ67%`Gh#Mdk%-GI9vS`QCq-G%V-D9`dz+P^ZEnJ*w z0M)!wNxk&LHkcvgd#s-}Eb2Wp#?c)=g=lX+lm)&*S?1_|F68{=^5<6YV zowwY9&`BO(Y9JAgie%c80xT@;-P@@8`1KKq&iJypUaTX|@>h@4uQx>JWD!Yx=U{li zVviD7qLHb>W0ZPG%Vd$)u-bAK3H;oKF}<`lO4w_Z#+T6e^4MTOo@C_a#qZ%y!oF=Z z*Tgy7H1ysSzv^n=gmZqZ#Jek(DIYGHIfJ@s!QN&P&N#C_oh#r2_L8qbPV54SDmbH4 znR{FbiYMdrAbB9C41oPGoP?7ox=^inAU!Oib_&zd!}fH;ZlbeP4||sp=;yhOiV6X4 ziMi@3Dap+$_MO_}y1{;eA`M=|6z4DdqSMKWvUX5>RIMHiKSPQgh&}y?%-MZW1mv=D zR)CMZiC|_5u5%2KXAn+;2%8E2PI7lT@4tWayt6fx|1L@Ie+~Jg>rWIzzZiK!E$hij zG&{@<`;d*OHZW+Zf3!!4bg!tMrUN|3v=vB3M+CKQPD{EK=~{bsD)90%urC1EjqLxv zm8-^22LzxeHq#JOFxv5#jb0wc83Xqv7@XB^zbG@`F3%l#qfA2nT4Ot?{~M9wKCCbC z+=c8+w=#NooAen+x`>Y}*dDfuEQ4!iJNR^tQ>3I2&q%RX{a6lliUGd&Tc$)1PA@~` zov*jAd|jSe3g*tg>`3f;TWvOgNl>wwDLKxfL|*zb${GrR3@JuzI&O_um91@Wv`^)3 zy|M}*;u4Z?b**v3fjL#K7^WB2=D%|JOQWP3YwhjV^{q)t)D6OoojyZ-H%aP(Oug%9 z@NLlsOnj_bkFZwc1>2_tF<=d_u=O|sBAU2%2S#U0ycIt)UC@Lm%?%7M(Vo!)kh`8j z@A2{3C&ZlimRd_&SRU^Nd391fB^DWRPLW>b6{BZ0En3oVKv$tG z8nwUAjw*1If%SlM5D@L4o8T2aH6(w3j)#W-=oIDB^^&^(v z`%#y3J`>c>?@?8@WRq@n%=GbL7iAM|LaH9OSS3)`gLJ(`0eA`gedWVVDt_X`!?P>- zsBIMA8Yw}vK>h8n;z*u(si^=sIQ!2qnm4&UL|4ajpu$ga2}EsM4xYP|xI_Y^3XpU@ z^Ex0iXtspPalJtjzG5(_hujD;FYVtufviS8!oL$HP+&9(;ju%%*^3myOGeIv{Ao+w z$yO-0hL@?^7wU>yfc|d8oHXRNYIH>;q$?bx>k{`ue3SO3j56#&VOTgGT!p3vobxWL74~DzE zCe6MDX4-ZFBn+LJ{C-I~gLzjpzv{Hih8l~q@zfX^(p9s`+Gbmla>+8kpaEgw$?1@U zT&TSZ(adw<^~T{|OfPf0`>o-2J?^rm+H)SoK<0frjKl(WXY5b928~}t0q#cOVM6N@ zUqKgO0#)dhui({m05t2^*mllD34E)f>_(|ZiQGg9!bZ1cc!!BNJ~AmbN(!p^m^eq4JeTh%Xf+rP%9mrwS|QOQ z$h6+lc7*H5-u=(_^72J!Vd)OV$m}^C5RQV}T#cH$$BK??(T~ye(;bpSm+wVqgXO>& z-l+^C@1ay6=otKB40;}?;q55>TOYPEUZf%a^wFBWSO)x}8m{rC;-J%g>XV)!?ZK$% z6_;OQ!Ohn$>Y*pi0G-@{7RFNacCq@?!{9#%8I7hp-BA)p-Pv5b>pjzkB+Njw3#4X4{H!+@uFOm<)lB7Y+uKCZ3^ zG0O`SH_H|IyAwF%{m+xOAQ62z9haRA;B%QPg%_VyjYNE(3Lj1AG zdF<}s(N9Xnn7?{}PaD{z>0rgE*WfG%@l|?QK$t&pp%D;l{rf`?<>!B+1w86g`6r#G zup)@(7SG-J+JSY@uqKr;eF@VT!Wo&vo$9lL!cw^wR+~O0zh3s}VB%C-B$@L41&9`xMQ!={-S`2B3>s>-wDl(X{UO3y$f#bkMSzZi12{d{2c*-yheO?W`$vPoTND9qyeD=?-@7hdq>3^XZy~&~Hj%bBs-df1A_5q!r?MzGYIpiX)ZSAO5 zi1)zWxpVh%AkJ6TK56dqo`=PB$5z3s#k$a{pI5QBE=j6MRn_GW9$&w&ogeIXSnbVA zqf$WNonVJq+rn(ySg6v$qy*IJ%=Ih9A}m2{Cl?1xAQ45?Jy4#tXcX4PHEl+ZY|FvuoG?b&QH>_;7tO`HVPz zw5W?Y2J|>+%UTRz5LVm1#jl4qYS1^Q$Nh* zz*sjAmeAlz7o##f-oZGN8NL=%qu-N_{Uyn_OigoYyu98#42vWwKd+FJ;1wF<$uCwm zCQ4KS{62-^pL+W>z9 ztV3PJja;p(0+;VMJu|^G782cpZ;WV;RlVdl6b}BQkDSj(Ri>exqXy5Sy9d$z*>_&9 z_#pg~PIbQ9Zgha2x9toDWSeWAT6f7P_#rB26s+p>gzGGy0%&*WhMSZ0FFvX0OQ-pl zU_42JSljnE_Gj6zT(&_AQakqML&4tu;H$mIw?x<{&y9V#?_A}EuFS14mzMI5NN1m6 zawZ_c+*wwVZ$1irW&$tv?1-}BQRONdRX_p(+mg0oAVo@@$wKi8E}#IW7x+wShi|~x zNqXeXfWbY=zpG@Y)SMDM->F#gY39URtIJ6~XlgDwQ5 zww=CoVyt~siN{^|sa&zBrS&{8<={N{l|LB_E20 zMjC3xG~TYSy|^Jg6`8lB-WoF>`{^>Y%iylS07a+F!qv{cplYg2FP+YHR?=wI!#sS! zs=hz4T?8kHfnHyhd2=@HV_mkKdulisL`+X^v>%@?IB5JizG#6q#GbGhN6}hjwFakt zX2htfT|)S6t0+iyFaW>f$CSh5G{y9=@QSSu{t^77Gxl+}oNbI78%&Vqpk+PWphlmf zNh5ktcFEZ0UO zmqAB8aIWH$Qgp$=mr(1-BCxf(?%X~UH~qPO?m4A(LOw~$u#=vk%)K_2{Z;Pc5aC<6 zIhACJ9)CNLh+;J9DyR=n_?7WjRy|h_qG*|1+cPXnB)9-^a>EH%Ppw|)2Il+W_BFqU zb3MkjGEkVOjpFHt!F`$c$d4h7C_Fs}H4ws>MbC_oGh-9o+n-*lkMMjDbZZ0i+UY;HGnbrE4=`hTlVQ*p^O5+r~ zLl~_EF1kugLz0DBn4({CeW=33efy9watUKjw;|4?@ zk-Igx{n>g8y;*(0vtxb6m(l&pV0PdipCt9G-gzP;9-4|u zZ>%?t3pC9Z>U88T@s_6w8@tVdol*+g5{BMVH-EV(&RZWlS^U#rVz=i^R0ZG?SouSOp!OC zWwH7RF+I*nBdLnSO&c941pBN(Xa{~WoE=8BH(*jjC^2LvrHnER;c%U;qB4NrBxEg{ zAq|Ul-lZ3;1i*26mO@O+^Entj3uQ%-=w|&LJdV*k{{0a5O{KAR2(35#cVK(+_un6v zr}kdq;g81&IC_r9568Y>(7fR=5+dX9ikP+hfykGoZn=a}>lQ0*Xs7=J%N_m0J+XrE z)DqhHPJKb<;Sa;y`7YSvN~CC_7IsgpqFzlMD7VD)&AwE1u#T! zKS3vP6s4p-dKfBW*ihafb2l#PocCCow^v$l;fIhzOqll)AX+$}!|Z98Q7Fc^Q@ zR8`m`vQ0f{1s4zK6sK`EhpfqErzihH@1le{k`3u`{Z`#;h4HFNgJoK{#sh)o!GMk5 zEvv@h+p^giX%n6Si-Q}cE9X5f*3t|nZGJ^�%KUruPhmo)W0r^O5*4`Bov!wD-cO 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

%!BLFPt$QDrT3sI< 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 yW9R_b#h56aRnSXf0|0 diff --git a/clients/react-vite/src/App.tsx b/clients/react-vite/src/App.tsx index 28d696f..46d955e 100644 --- a/clients/react-vite/src/App.tsx +++ b/clients/react-vite/src/App.tsx @@ -5,13 +5,13 @@ import { useSurvivorState } from "./hooks/useSurvivorState"; import { useConnect } from "@starknet-react/core"; function App() { - const { survivor } = useSurvivorState(); + const { survivor } = useSurvivorState(); - return ( - <> - - - ); + return ( + <> + + + ); } export default App; diff --git a/clients/react-vite/src/components/game/BeastCard.tsx b/clients/react-vite/src/components/game/BeastCard.tsx index c94c87b..5491ffc 100644 --- a/clients/react-vite/src/components/game/BeastCard.tsx +++ b/clients/react-vite/src/components/game/BeastCard.tsx @@ -3,68 +3,73 @@ import { RoundedContainer } from "./RoundedContainer"; import { motion } from "framer-motion"; export interface Beast { - name: string; - level: number; - health: number; - maxHealth: number; - image: string; + name: string; + level: number; + health: number; + maxHealth: number; + image: string; } export const BeastCard = ({ beast }: { beast: Beast }) => { - return ( - -

{beast.name}
- -
-
Tier 1
-
-
- + return ( + +
+ {beast.name}
-
-
- {beast.health}/{beast.maxHealth} HP -
-
-
+ +
+
Tier 1
+
+
+ +
+
+
+ {beast.health}/{beast.maxHealth} HP +
+
+
-
lvl. {beast.level}
-
- - +
lvl. {beast.level}
+
+ + + + + + {/* Add your content here */} +
+ Giant: argghhh, who do you think you are!!! I will crush + your skull and eat your flesh! Time to die human. +
+
+ +
+ + +
+
+ + +
+
- - - {/* Add your content here */} -
- Giant: argghhh, who do you think you are!!! I will crush your skull - and eat your flesh! Time to die human. -
-
- -
- - -
-
- - -
-
- - ); + ); }; diff --git a/clients/react-vite/src/components/game/Confirming.tsx b/clients/react-vite/src/components/game/Confirming.tsx index 0eb6b1c..628ecd9 100644 --- a/clients/react-vite/src/components/game/Confirming.tsx +++ b/clients/react-vite/src/components/game/Confirming.tsx @@ -1,38 +1,38 @@ import { RoundedContainer } from "./RoundedContainer"; const items = [ - { - title: "+2 Strength", - }, - { - title: "Equip Sword", - }, + { + title: "+2 Strength", + }, + { + title: "Equip Sword", + }, ]; export const Confirmation = () => { - return ( - -
Upgrading...
+ return ( + +
Upgrading...
- - {items.map((item) => ( - -
{item.title}
-
- ))} -
-
- ); + + {items.map((item) => ( + +
{item.title}
+
+ ))} +
+
+ ); }; export const ConfirmationCard = ({ - children, + children, }: { - children: React.ReactNode; + children: React.ReactNode; }) => { - return ( -
- {children} -
- ); + return ( +
+ {children} +
+ ); }; diff --git a/clients/react-vite/src/components/game/ItemBar.tsx b/clients/react-vite/src/components/game/ItemBar.tsx index 5e21089..96ed0e7 100644 --- a/clients/react-vite/src/components/game/ItemBar.tsx +++ b/clients/react-vite/src/components/game/ItemBar.tsx @@ -3,49 +3,49 @@ import { motion } from "framer-motion"; import Ring from "@/components/icons/ring.svg"; export interface Item { - id: number; - name: string; - greatness: number; - type: "Weapon" | "Waist" | "Head" | "Feet" | "Hands" | "Chest"; + id: number; + name: string; + greatness: number; + type: "Weapon" | "Waist" | "Head" | "Feet" | "Hands" | "Chest"; } export interface ItemBarProps { - item: Item; + item: Item; } const ItemBar = ({ item }: ItemBarProps) => { - // TODO: Inject actual greatness logic to next level - const getBackgroundWidth = (greatness: number) => { - return `${Math.min(100, Math.max(0, greatness))}%`; - }; - - return ( -
- -
-
- -
{item.name}
+ // TODO: Inject actual greatness logic to next level + const getBackgroundWidth = (greatness: number) => { + return `${Math.min(100, Math.max(0, greatness))}%`; + }; + + return ( +
+ +
+
+ +
{item.name}
+
+ +
Greatness: {item.greatness}
+
+ +
+ +
+ + +
- -
Greatness: {item.greatness}
-
- -
- -
- - -
-
- ); + ); }; export default ItemBar; diff --git a/clients/react-vite/src/components/game/ItemBarList.tsx b/clients/react-vite/src/components/game/ItemBarList.tsx index 59e21d3..c9ff680 100644 --- a/clients/react-vite/src/components/game/ItemBarList.tsx +++ b/clients/react-vite/src/components/game/ItemBarList.tsx @@ -1,11 +1,11 @@ import ItemBar, { Item } from "./ItemBar"; export const ItemBarList = ({ items }: { items: Item[] }) => { - return ( -
- {items.map((item) => ( - - ))} -
- ); + return ( +
+ {items.map((item) => ( + + ))} +
+ ); }; diff --git a/clients/react-vite/src/components/game/LevelUp.tsx b/clients/react-vite/src/components/game/LevelUp.tsx index 990cc1c..3b06ea5 100644 --- a/clients/react-vite/src/components/game/LevelUp.tsx +++ b/clients/react-vite/src/components/game/LevelUp.tsx @@ -3,133 +3,138 @@ import { Button } from "../ui/button"; import { RoundedContainer } from "./RoundedContainer"; import { Slider } from "@/components/ui/slider"; const stats = [ - { - stat: "Strength", - description: "Increases your physical damage", - currentStatPoints: 2, - onLevelUp: () => {}, - }, + { + stat: "Strength", + description: "Increases your physical damage", + currentStatPoints: 2, + onLevelUp: () => {}, + }, - { - stat: "Intelligence", - description: "Increases your spell damage", - currentStatPoints: 2, - onLevelUp: () => {}, - }, - { - stat: "Vitality", - description: "Increases your health points", - currentStatPoints: 2, - onLevelUp: () => {}, - }, - { - stat: "Dexterity", - description: "Increases your critical chance", - currentStatPoints: 2, - onLevelUp: () => {}, - }, - { - stat: "Wisdom", - description: "Wisdom increases chance of avoiding a Beast ambush", - currentStatPoints: 2, - onLevelUp: () => {}, - }, - { - stat: "Charisma", - description: "Charisma provides discounts on the marketplace and potions", - currentStatPoints: 2, - onLevelUp: () => {}, - }, + { + stat: "Intelligence", + description: "Increases your spell damage", + currentStatPoints: 2, + onLevelUp: () => {}, + }, + { + stat: "Vitality", + description: "Increases your health points", + currentStatPoints: 2, + onLevelUp: () => {}, + }, + { + stat: "Dexterity", + description: "Increases your critical chance", + currentStatPoints: 2, + onLevelUp: () => {}, + }, + { + stat: "Wisdom", + description: "Wisdom increases chance of avoiding a Beast ambush", + currentStatPoints: 2, + onLevelUp: () => {}, + }, + { + stat: "Charisma", + description: + "Charisma provides discounts on the marketplace and potions", + currentStatPoints: 2, + onLevelUp: () => {}, + }, ]; export const LevelUp = () => { - const [availableStatPoints, setAvailableStatPoints] = useState(2); - const [statPoints, setStatPoints] = useState(stats.map(() => 0)); + const [availableStatPoints, setAvailableStatPoints] = useState(2); + const [statPoints, setStatPoints] = useState(stats.map(() => 0)); - const handleStatChange = (index: number, value: number) => { - const oldValue = statPoints[index]; - const newStatPoints = [...statPoints]; - newStatPoints[index] = value; - setStatPoints(newStatPoints); - setAvailableStatPoints((prev) => prev - (value - oldValue)); - }; - return ( - -
Level Up
+ const handleStatChange = (index: number, value: number) => { + const oldValue = statPoints[index]; + const newStatPoints = [...statPoints]; + newStatPoints[index] = value; + setStatPoints(newStatPoints); + setAvailableStatPoints((prev) => prev - (value - oldValue)); + }; + return ( + +
Level Up
- -
-
{availableStatPoints} Stat Points
-
+ +
+
{availableStatPoints} Stat Points
+
- - -
- {stats.map((stat, index) => ( - handleStatChange(index, value)} - currentPlayerStat={stat.currentStatPoints} - /> - ))} -
- - - + + +
+ {stats.map((stat, index) => ( + + handleStatChange(index, value) + } + currentPlayerStat={stat.currentStatPoints} + /> + ))} +
+ + + + +
+
-
-
-
- ); + ); }; interface LevelUpBarProps { - currentStatPoints: number; - stat: string; - description: string; - onLevelUp: () => void; + currentStatPoints: number; + stat: string; + description: string; + onLevelUp: () => void; } export const LevelUpBar = ({ - level, - availableStatPoints, - currentValue, - onStatChange, - currentPlayerStat, + level, + availableStatPoints, + currentValue, + onStatChange, + currentPlayerStat, }: { - level: LevelUpBarProps; - availableStatPoints: number; - currentValue: number; - onStatChange: (value: number) => void; - currentPlayerStat: number; // Add this line + level: LevelUpBarProps; + availableStatPoints: number; + currentValue: number; + onStatChange: (value: number) => void; + currentPlayerStat: number; // Add this line }) => { - return ( -
-
- {level.stat} - onStatChange(value)} - /> -
- +{currentValue} - [{currentPlayerStat}] + return ( +
+
+ {level.stat} + onStatChange(value)} + /> +
+ +{currentValue} + + [{currentPlayerStat}] + +
+
+
{level.description}
-
-
{level.description}
-
- ); + ); }; diff --git a/clients/react-vite/src/components/game/LootMarket.tsx b/clients/react-vite/src/components/game/LootMarket.tsx index 6708b90..40a452b 100644 --- a/clients/react-vite/src/components/game/LootMarket.tsx +++ b/clients/react-vite/src/components/game/LootMarket.tsx @@ -3,260 +3,260 @@ import { Button } from "../ui/button"; import { RoundedContainer } from "./RoundedContainer"; const items = [ - { - id: 1, - name: "Sword", - tier: 1, - slot: "Weapon", - type: "Sword", - cost: 100, - }, - { - id: 2, - name: "Axe", - tier: 1, - slot: "Weapon", - type: "Axe", - cost: 100, - }, - { - id: 3, - name: "Shield", - tier: 1, - slot: "Offhand", - type: "Shield", - cost: 100, - }, - { - id: 4, - name: "Helmet", - tier: 1, - slot: "Head", - type: "Helmet", - cost: 100, - }, - { - id: 5, - name: "Chestplate", - tier: 1, - slot: "Chest", - type: "Chestplate", - cost: 100, - }, - { - id: 6, - name: "Boots", - tier: 1, - slot: "Feet", - type: "Boots", - cost: 100, - }, - { - id: 7, - name: "Gloves", - tier: 1, - slot: "Hands", - type: "Gloves", - cost: 100, - }, - { - id: 4, - name: "Helmet", - tier: 1, - slot: "Head", - type: "Helmet", - cost: 100, - }, - { - id: 5, - name: "Chestplate", - tier: 1, - slot: "Chest", - type: "Chestplate", - cost: 100, - }, - { - id: 6, - name: "Boots", - tier: 1, - slot: "Feet", - type: "Boots", - cost: 100, - }, - { - id: 7, - name: "Gloves", - tier: 1, - slot: "Hands", - type: "Gloves", - cost: 100, - }, - { - id: 5, - name: "Chestplate", - tier: 1, - slot: "Chest", - type: "Chestplate", - cost: 100, - }, - { - id: 6, - name: "Boots", - tier: 1, - slot: "Feet", - type: "Boots", - cost: 100, - }, - { - id: 7, - name: "Gloves", - tier: 1, - slot: "Hands", - type: "Gloves", - cost: 100, - }, - { - id: 5, - name: "Chestplate", - tier: 1, - slot: "Chest", - type: "Chestplate", - cost: 100, - }, - { - id: 6, - name: "Boots", - tier: 1, - slot: "Feet", - type: "Boots", - cost: 100, - }, - { - id: 7, - name: "Gloves", - tier: 1, - slot: "Hands", - type: "Gloves", - cost: 100, - }, - { - id: 5, - name: "Chestplate", - tier: 1, - slot: "Chest", - type: "Chestplate", - cost: 100, - }, - { - id: 6, - name: "Boots", - tier: 1, - slot: "Feet", - type: "Boots", - cost: 100, - }, - { - id: 7, - name: "Gloves", - tier: 1, - slot: "Hands", - type: "Gloves", - cost: 100, - }, + { + id: 1, + name: "Sword", + tier: 1, + slot: "Weapon", + type: "Sword", + cost: 100, + }, + { + id: 2, + name: "Axe", + tier: 1, + slot: "Weapon", + type: "Axe", + cost: 100, + }, + { + id: 3, + name: "Shield", + tier: 1, + slot: "Offhand", + type: "Shield", + cost: 100, + }, + { + id: 4, + name: "Helmet", + tier: 1, + slot: "Head", + type: "Helmet", + cost: 100, + }, + { + id: 5, + name: "Chestplate", + tier: 1, + slot: "Chest", + type: "Chestplate", + cost: 100, + }, + { + id: 6, + name: "Boots", + tier: 1, + slot: "Feet", + type: "Boots", + cost: 100, + }, + { + id: 7, + name: "Gloves", + tier: 1, + slot: "Hands", + type: "Gloves", + cost: 100, + }, + { + id: 4, + name: "Helmet", + tier: 1, + slot: "Head", + type: "Helmet", + cost: 100, + }, + { + id: 5, + name: "Chestplate", + tier: 1, + slot: "Chest", + type: "Chestplate", + cost: 100, + }, + { + id: 6, + name: "Boots", + tier: 1, + slot: "Feet", + type: "Boots", + cost: 100, + }, + { + id: 7, + name: "Gloves", + tier: 1, + slot: "Hands", + type: "Gloves", + cost: 100, + }, + { + id: 5, + name: "Chestplate", + tier: 1, + slot: "Chest", + type: "Chestplate", + cost: 100, + }, + { + id: 6, + name: "Boots", + tier: 1, + slot: "Feet", + type: "Boots", + cost: 100, + }, + { + id: 7, + name: "Gloves", + tier: 1, + slot: "Hands", + type: "Gloves", + cost: 100, + }, + { + id: 5, + name: "Chestplate", + tier: 1, + slot: "Chest", + type: "Chestplate", + cost: 100, + }, + { + id: 6, + name: "Boots", + tier: 1, + slot: "Feet", + type: "Boots", + cost: 100, + }, + { + id: 7, + name: "Gloves", + tier: 1, + slot: "Hands", + type: "Gloves", + cost: 100, + }, + { + id: 5, + name: "Chestplate", + tier: 1, + slot: "Chest", + type: "Chestplate", + cost: 100, + }, + { + id: 6, + name: "Boots", + tier: 1, + slot: "Feet", + type: "Boots", + cost: 100, + }, + { + id: 7, + name: "Gloves", + tier: 1, + slot: "Hands", + type: "Gloves", + cost: 100, + }, ]; export const LootMarket = () => { - return ( - -
Loot Market
+ return ( + +
Loot Market
- -
-
Purchase cost: 100
-
Potions
-
+ +
+
Purchase cost: 100
+
Potions
+
- -
-
item
-
Tier
-
Slot
-
Type
-
Cost
-
Actions
-
+ +
+
item
+
Tier
+
Slot
+
Type
+
Cost
+
Actions
+
- -
- {items.map((item) => ( - - ))} -
- - - + +
+ {items.map((item) => ( + + ))} +
+ + + + +
+
-
-
-
- ); + ); }; interface Item { - id: number; - name: string; - tier: number; - slot: string; - type: string; - cost: number; + id: number; + name: string; + tier: number; + slot: string; + type: string; + cost: number; } export const LootMarketItem = ({ item }: { item: Item }) => { - return ( -
-
{item.name}
-
{item.tier}
-
{item.slot}
-
{item.type}
-
{item.cost}
-
- -
-
- ); + return ( +
+
{item.name}
+
{item.tier}
+
{item.slot}
+
{item.type}
+
{item.cost}
+
+ +
+
+ ); }; export const ConfirmButton = ({ primaryText }: { primaryText: string }) => { - const [showOptions, setShowOptions] = useState(false); + const [showOptions, setShowOptions] = useState(false); - const handlePrimaryClick = () => { - setShowOptions(true); - }; + const handlePrimaryClick = () => { + setShowOptions(true); + }; - const handleOptionClick = () => { - setShowOptions(false); - }; + const handleOptionClick = () => { + setShowOptions(false); + }; - return ( -
- {!showOptions ? ( - - ) : ( -
- - + return ( +
+ {!showOptions ? ( + + ) : ( +
+ + +
+ )}
- )} -
- ); + ); }; diff --git a/clients/react-vite/src/components/game/PlayerCard.tsx b/clients/react-vite/src/components/game/PlayerCard.tsx index 60a249b..212e1bd 100644 --- a/clients/react-vite/src/components/game/PlayerCard.tsx +++ b/clients/react-vite/src/components/game/PlayerCard.tsx @@ -3,23 +3,23 @@ import { PlayerTabs } from "./PlayerTabs"; import { RoundedContainer } from "./RoundedContainer"; export const PlayerCard = () => { - return ( - -
- -
+ return ( + +
+ +
- -
- ); + +
+ ); }; diff --git a/clients/react-vite/src/components/game/PlayerPage.tsx b/clients/react-vite/src/components/game/PlayerPage.tsx index 88a0f4f..fc177dc 100644 --- a/clients/react-vite/src/components/game/PlayerPage.tsx +++ b/clients/react-vite/src/components/game/PlayerPage.tsx @@ -4,42 +4,42 @@ import { RoundedContainer } from "./RoundedContainer"; import Ring from "@/components/icons/ring.svg"; export const PlayerPage = () => { - const menuItems = [ - { - label: "Profile", - value: "profile", - icon: , - }, - { - label: "Inventory", - value: "inventory", - icon: , - }, - { - label: "Settings", - value: "settings", - icon: , - }, - ]; - return ( - -
- {menuItems.map((item) => ( - - ))} -
+ const menuItems = [ + { + label: "Profile", + value: "profile", + icon: , + }, + { + label: "Inventory", + value: "inventory", + icon: , + }, + { + label: "Settings", + value: "settings", + icon: , + }, + ]; + return ( + +
+ {menuItems.map((item) => ( + + ))} +
- + -
- {menuItems.map((item) => ( - - ))} -
-
- ); +
+ {menuItems.map((item) => ( + + ))} +
+
+ ); }; diff --git a/clients/react-vite/src/components/game/PlayerProfile.tsx b/clients/react-vite/src/components/game/PlayerProfile.tsx index 2138bb7..c08ebf1 100644 --- a/clients/react-vite/src/components/game/PlayerProfile.tsx +++ b/clients/react-vite/src/components/game/PlayerProfile.tsx @@ -1,97 +1,100 @@ import { motion } from "framer-motion"; export interface PlayerProfileProps { - name: string; - id: number; - avatar: string; - gold: number; - health: number; - maxHealth: number; - level: number; + name: string; + id: number; + avatar: string; + gold: number; + health: number; + maxHealth: number; + level: number; } export const PlayerProfile = ({ player }: { player: PlayerProfileProps }) => { - return ( -
- -
-
#{player.id}
-
{player.name}
-
- - + return ( +
+ +
+
#{player.id}
+
{player.name}
+
+ + - + +
+
-
-
- ); + ); }; export const GoldBar = ({ gold }: { gold: number }) => { - return ( -
-
Gold: {gold}
-
- ); + return ( +
+
Gold: {gold}
+
+ ); }; export const XpBar = ({ - level, - xp, - maxXp, + level, + xp, + maxXp, }: { - level: number; - xp: number; - maxXp: number; + level: number; + xp: number; + maxXp: number; }) => { - return ( -
-
- -
-
-
- Level: {level} - XP: {xp}/{maxXp} + return ( +
+
+ +
+
+
+ Level: {level} - XP: {xp}/{maxXp} +
+
-
-
- ); + ); }; export const HealthBar = ({ - health, - maxHealth, + health, + maxHealth, }: { - health: number; - maxHealth: number; + health: number; + maxHealth: number; }) => { - return ( -
-
- -
- {health}/{maxHealth} HP + return ( +
+
+ +
+ {health}/{maxHealth} HP +
+
-
-
- ); + ); }; diff --git a/clients/react-vite/src/components/game/PlayerTabs.tsx b/clients/react-vite/src/components/game/PlayerTabs.tsx index 4f75ddd..12a7618 100644 --- a/clients/react-vite/src/components/game/PlayerTabs.tsx +++ b/clients/react-vite/src/components/game/PlayerTabs.tsx @@ -5,104 +5,108 @@ import { ItemBarList } from "./ItemBarList"; import { Item } from "./ItemBar"; const items: Item[] = [ - { - id: 1, - name: "“bane bender” katana of Power", - greatness: 10, - type: "Weapon", - }, - { - id: 2, - name: "“bane bender” katana of Power", - greatness: 20, - type: "Weapon", - }, - { - id: 3, - name: "“bane bender” katana of Power", - greatness: 10, - type: "Weapon", - }, - { - id: 4, - name: "“bane bender” katana of Power", - greatness: 30, - type: "Weapon", - }, - { - id: 5, - name: "“bane bender” katana of Power", - greatness: 40, - type: "Weapon", - }, - { - id: 6, - name: "“bane bender” katana of Power", - greatness: 2, - type: "Weapon", - }, - { - id: 7, - name: "“bane bender” katana of Power", - greatness: 69, - type: "Weapon", - }, - { - id: 8, - name: "“bane bender” katana of Power", - greatness: 10, - type: "Weapon", - }, -]; - -export const PlayerTabs = () => { - const tabs = useMemo(() => { - return [ - { + { id: 1, - name: "Equipped", - content: , - }, - { + name: "“bane bender” katana of Power", + greatness: 10, + type: "Weapon", + }, + { id: 2, - name: "Bag", - content: "Change your password here.", - }, - { + name: "“bane bender” katana of Power", + greatness: 20, + type: "Weapon", + }, + { id: 3, - name: "History", - content: "Change your password here.", - }, - { + name: "“bane bender” katana of Power", + greatness: 10, + type: "Weapon", + }, + { id: 4, - name: "Actions", - content: "Change your password here.", - }, - { + name: "“bane bender” katana of Power", + greatness: 30, + type: "Weapon", + }, + { id: 5, - name: "Future", - content: "Change your password here.", - }, - ]; - }, []); + name: "“bane bender” katana of Power", + greatness: 40, + type: "Weapon", + }, + { + id: 6, + name: "“bane bender” katana of Power", + greatness: 2, + type: "Weapon", + }, + { + id: 7, + name: "“bane bender” katana of Power", + greatness: 69, + type: "Weapon", + }, + { + id: 8, + name: "“bane bender” katana of Power", + greatness: 10, + type: "Weapon", + }, +]; + +export const PlayerTabs = () => { + const tabs = useMemo(() => { + return [ + { + id: 1, + name: "Equipped", + content: , + }, + { + id: 2, + name: "Bag", + content: "Change your password here.", + }, + { + id: 3, + name: "History", + content: "Change your password here.", + }, + { + id: 4, + name: "Actions", + content: "Change your password here.", + }, + { + id: 5, + name: "Future", + content: "Change your password here.", + }, + ]; + }, []); - return ( - - - - {tabs.map((tab) => ( - - {tab.name} - - ))} - + return ( + + + + {tabs.map((tab) => ( + + {tab.name} + + ))} + - {tabs.map((tab) => ( - - {tab.content} - - ))} - - - ); + {tabs.map((tab) => ( + + {tab.content} + + ))} + + + ); }; diff --git a/clients/react-vite/src/components/game/RoundedContainer.tsx b/clients/react-vite/src/components/game/RoundedContainer.tsx index cba7742..894a2f8 100644 --- a/clients/react-vite/src/components/game/RoundedContainer.tsx +++ b/clients/react-vite/src/components/game/RoundedContainer.tsx @@ -1,17 +1,17 @@ export const RoundedContainer = ({ - children, - className, - inner, + children, + className, + inner, }: { - children: React.ReactNode; - className?: string; - inner?: boolean; + children: React.ReactNode; + className?: string; + inner?: boolean; }) => { - return ( -
- {children} -
- ); + return ( +
+ {children} +
+ ); }; diff --git a/clients/react-vite/src/components/pages/LandingPage.tsx b/clients/react-vite/src/components/pages/LandingPage.tsx index 0b123b0..85ed66c 100644 --- a/clients/react-vite/src/components/pages/LandingPage.tsx +++ b/clients/react-vite/src/components/pages/LandingPage.tsx @@ -1,11 +1,11 @@ import { useAdventurersByXPWithScores } from "@/hooks"; import { - Pagination, - PaginationContent, - PaginationItem, - PaginationLink, - PaginationNext, - PaginationPrevious, + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, } from "@/components/ui/pagination"; import { useConnect } from "@starknet-react/core"; @@ -13,111 +13,137 @@ import { useState } from "react"; import { AnimatedNumber } from "../ui/animatedNumber"; export const LandingPage = () => { - const { connect, connectors } = useConnect(); + const { connect, connectors } = useConnect(); - const [page, setPage] = useState(1); + const [page, setPage] = useState(1); - const handlePreviousPage = () => { - setPage((prev) => Math.max(prev - 1, 1)); - }; + const handlePreviousPage = () => { + setPage((prev) => Math.max(prev - 1, 1)); + }; - const handleNextPage = () => { - setPage((prev) => Math.min(prev + 1, totalPages)); - }; + const handleNextPage = () => { + setPage((prev) => Math.min(prev + 1, totalPages)); + }; - const { adventurersWithScores, isLoading, isError } = - useAdventurersByXPWithScores({ page }); + const { adventurersWithScores, isLoading, isError } = + useAdventurersByXPWithScores({ page }); - const itemsPerPage = 10; - const totalPages = 5; - const startingRank = (page - 1) * itemsPerPage + 1; + const itemsPerPage = 10; + const totalPages = 5; + const startingRank = (page - 1) * itemsPerPage + 1; - return ( -
-
-
Leaderboard
-
-
- - - - - - - - - - - {isLoading &&
Loading...
} - {isError &&
Error
} - {adventurersWithScores?.slice(0, 1).map((adventurer, index) => ( - - - - - - - ))} - {adventurersWithScores - ?.slice(1, 12) - .map((adventurer, index) => ( - - - - - - - ))} - -
- Rank - - Player - - Score - - Lords Payout -
- {startingRank} - - {adventurer.name} - - {adventurer.xp} - - -
- {startingRank + index + 1} - - {adventurer.name} - - {adventurer.xp} - - -
-
- - - - - - {[...Array(totalPages)].map((_, i) => ( - - setPage(i + 1)} - isActive={page === i + 1} - > - {i + 1} - - - ))} - - - - - + return ( +
+
+
Leaderboard
+
+
+ + + + + + + + + + + + + + + {adventurersWithScores + ?.slice(0, 1) + .map((adventurer, index) => ( + + + + + + + ))} + {adventurersWithScores + ?.slice(1, 12) + .map((adventurer, index) => ( + + + + + + + ))} + +
+ Rank + + Player + + Score + + Lords Payout +
+ {isLoading && ( + + Loading... + + )} + {isError && Error} +
+ {startingRank} + + {adventurer.name} + + {adventurer.xp} + + +
+ {startingRank + index + 1} + + {adventurer.name} + + {adventurer.xp} + + +
+
+ + + + + + {[...Array(totalPages)].map((_, i) => ( + + setPage(i + 1)} + isActive={page === i + 1} + > + {i + 1} + + + ))} + + + + + +
+
{" "}
-
{" "} -
- ); + ); }; diff --git a/clients/react-vite/src/components/providers/QueryProvider.tsx b/clients/react-vite/src/components/providers/QueryProvider.tsx index 87c4d08..5f950b8 100644 --- a/clients/react-vite/src/components/providers/QueryProvider.tsx +++ b/clients/react-vite/src/components/providers/QueryProvider.tsx @@ -1,16 +1,18 @@ import React from "react"; import { - useQuery, - useMutation, - useQueryClient, - QueryClient, - QueryClientProvider, + useQuery, + useMutation, + useQueryClient, + QueryClient, + QueryClientProvider, } from "@tanstack/react-query"; const queryClient = new QueryClient(); export function QueryProvider({ children }: { children: React.ReactNode }) { - return ( - {children} - ); + return ( + + {children} + + ); } diff --git a/clients/react-vite/src/components/providers/StarknetProvider.tsx b/clients/react-vite/src/components/providers/StarknetProvider.tsx index 809734b..4b6afec 100644 --- a/clients/react-vite/src/components/providers/StarknetProvider.tsx +++ b/clients/react-vite/src/components/providers/StarknetProvider.tsx @@ -4,16 +4,16 @@ import { sepolia, mainnet } from "@starknet-react/chains"; import { StarknetConfig, publicProvider, voyager } from "@starknet-react/core"; export function StarknetProvider({ children }: { children: React.ReactNode }) { - const cartridge = new CartridgeConnector([]); + const cartridge = new CartridgeConnector([]); - return ( - - {children} - - ); + return ( + + {children} + + ); } diff --git a/clients/react-vite/src/components/ui/animatedNumber.tsx b/clients/react-vite/src/components/ui/animatedNumber.tsx index cdd9ec5..c70e726 100644 --- a/clients/react-vite/src/components/ui/animatedNumber.tsx +++ b/clients/react-vite/src/components/ui/animatedNumber.tsx @@ -1,13 +1,13 @@ import { motion, useMotionValue, useTransform, animate } from "framer-motion"; import { useEffect } from "react"; export const AnimatedNumber = ({ value }: { value: number }) => { - const count = useMotionValue(0); - const rounded = useTransform(count, (latest) => latest.toFixed(2)); + const count = useMotionValue(0); + const rounded = useTransform(count, (latest) => latest.toFixed(2)); - useEffect(() => { - const controls = animate(count, value, { duration: 1 }); - return controls.stop; - }, [count, value]); + useEffect(() => { + const controls = animate(count, value, { duration: 1 }); + return controls.stop; + }, [count, value]); - return {rounded}; + return {rounded}; }; diff --git a/clients/react-vite/src/components/ui/button.tsx b/clients/react-vite/src/components/ui/button.tsx index 5f25ecc..4b73f04 100644 --- a/clients/react-vite/src/components/ui/button.tsx +++ b/clients/react-vite/src/components/ui/button.tsx @@ -5,52 +5,52 @@ import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors uppercase focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", - { - variants: { - variant: { - default: - "bg-primary text-primary-foreground hover:bg-primary/60 bg-primary/40 text-primary border-primary border", - destructive: - "bg-destructive/40 text-destructive-foreground border-destructive border-red-800 border hover:bg-destructive/90", - outline: - "border border-input bg-transparent border-primary text-primary hover:bg-primary/40 ", - secondary: - "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-primary hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: "h-10 px-4 py-2", - sm: "h-9 rounded-md px-3", - lg: "h-11 rounded-md px-8", - icon: "h-10 w-10", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors uppercase focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground hover:bg-primary/60 bg-primary/40 text-primary border-primary border", + destructive: + "bg-destructive/40 text-destructive-foreground border-destructive border-red-800 border hover:bg-destructive/90", + outline: + "border border-input bg-transparent border-primary text-primary hover:bg-primary/40 ", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-primary hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } ); export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; } const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; - return ( - - ); - } + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + } ); Button.displayName = "Button"; diff --git a/clients/react-vite/src/components/ui/pagination.tsx b/clients/react-vite/src/components/ui/pagination.tsx index ea40d19..2703458 100644 --- a/clients/react-vite/src/components/ui/pagination.tsx +++ b/clients/react-vite/src/components/ui/pagination.tsx @@ -1,117 +1,117 @@ -import * as React from "react" -import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" +import * as React from "react"; +import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"; -import { cn } from "@/lib/utils" -import { ButtonProps, buttonVariants } from "@/components/ui/button" +import { cn } from "@/lib/utils"; +import { ButtonProps, buttonVariants } from "@/components/ui/button"; const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( -
+ + - \ No newline at end of file diff --git a/packages/core/coverage/src/objects/prediction.ts.html b/packages/core/coverage/src/objects/prediction.ts.html index e3a22a1..0c30fcf 100644 --- a/packages/core/coverage/src/objects/prediction.ts.html +++ b/packages/core/coverage/src/objects/prediction.ts.html @@ -1,68 +1,69 @@ - + + Code coverage report for src/objects/prediction.ts + + + + + + + - - Code coverage report for src/objects/prediction.ts - - - - - - - - - -
-
-

All files / src/objects prediction.ts

-
- -
- 0% - Statements - 0/178 -
- - -
- 0% - Branches - 0/1 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/178 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

+            
+
1 2 3 @@ -722,13 +723,20 @@

All files / src/objects< }  

-
-
- +
+ +
+ + - \ No newline at end of file diff --git a/packages/core/coverage/src/objects/survivor.ts.html b/packages/core/coverage/src/objects/survivor.ts.html index 8a424a1..903215d 100644 --- a/packages/core/coverage/src/objects/survivor.ts.html +++ b/packages/core/coverage/src/objects/survivor.ts.html @@ -1,68 +1,69 @@ - + + Code coverage report for src/objects/survivor.ts + + + + + + + - - Code coverage report for src/objects/survivor.ts - - - - - - - - - -
-
-

All files / src/objects survivor.ts

-
- -
- 44.25% - Statements - 235/531 -
- - -
- 54.34% - Branches - 25/46 -
- - -
- 33.33% - Functions - 17/51 -
- - -
- 44.25% - Lines - 235/531 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

+            
+
1 2 3 @@ -1922,13 +1923,20 @@

All files / src/objects< }  

-
-
- +
+ +
+ + - \ No newline at end of file diff --git a/packages/core/coverage/src/provider/execute.ts.html b/packages/core/coverage/src/provider/execute.ts.html index 53beb69..ad2507f 100644 --- a/packages/core/coverage/src/provider/execute.ts.html +++ b/packages/core/coverage/src/provider/execute.ts.html @@ -1,68 +1,69 @@ - + + Code coverage report for src/provider/execute.ts + + + + + + + - - Code coverage report for src/provider/execute.ts - - - - - - - - - -
-
-

All files / src/provider execute.ts

-
- -
- 0% - Statements - 0/199 -
- - -
- 0% - Branches - 0/1 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/199 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

+            
+
1 2 3 @@ -773,13 +774,20 @@

All files / src/provider }  

-
-
- +
+ +
+ + - \ No newline at end of file diff --git a/packages/core/coverage/src/provider/index.html b/packages/core/coverage/src/provider/index.html index 0a127c8..a1cb770 100644 --- a/packages/core/coverage/src/provider/index.html +++ b/packages/core/coverage/src/provider/index.html @@ -1,123 +1,211 @@ - + + Code coverage report for src/provider + + + + + + + - - Code coverage report for src/provider - - - - - - - - - -
-
-

All files src/provider

-
- -
- 0% - Statements - 0/245 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/2 -
- - -
- 0% - Lines - 0/245 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - + +
+
+

All files src/provider

+
+
+ 0% + Statements + 0/245 +
+ +
+ 0% + Branches + 0/2 +
+ +
+ 0% + Functions + 0/2 +
-
- - - - - - - - - - - +
+ 0% + Lines + 0/245 +
+ +

+ Press n or j to go to the next uncovered + block, b, p or k for the previous + block. +

+ + +
+
+
FileStatementsBranchesFunctionsLines
execute.ts -
-
0%0/1990%0/10%0/10%0/199
index.ts -
-
0%0/460%0/10%0/10%0/46
+ + + + + + + + + + + + + + + + + + + + + + + + + + + - -
+ File + + Statements + + Branches + + Functions + + Lines +
+ execute.ts + +
+
+
+
+
0%0/1990%0/10%0/10%0/199
-
-
-
- +
+ +
+ + - \ No newline at end of file diff --git a/packages/core/coverage/src/provider/index.ts.html b/packages/core/coverage/src/provider/index.ts.html index 408f567..f05b3b6 100644 --- a/packages/core/coverage/src/provider/index.ts.html +++ b/packages/core/coverage/src/provider/index.ts.html @@ -1,68 +1,69 @@ - + + Code coverage report for src/provider/index.ts + + + + + + + - - Code coverage report for src/provider/index.ts - - - - - - - - - -
-
-

All files / src/provider index.ts

-
- -
- 0% - Statements - 0/46 -
- - -
- 0% - Branches - 0/1 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/46 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

+            
+
1 2 3 @@ -248,13 +249,20 @@

All files / src/provider }  

-
-
- +
+ +
+ + - \ No newline at end of file diff --git a/packages/core/coverage/src/state/format.ts.html b/packages/core/coverage/src/state/format.ts.html index e4b5b65..5d9f574 100644 --- a/packages/core/coverage/src/state/format.ts.html +++ b/packages/core/coverage/src/state/format.ts.html @@ -1,68 +1,69 @@ - + + Code coverage report for src/state/format.ts + + + + + + + - - Code coverage report for src/state/format.ts - - - - - - - - - -
-
-

All files / src/state format.ts

-
- -
- 16.59% - Statements - 124/747 -
- - -
- 100% - Branches - 4/4 -
- - -
- 10.52% - Functions - 4/38 -
- - -
- 16.59% - Lines - 124/747 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

+            
+
1 2 3 @@ -2597,13 +2598,20 @@

All files / src/state}  

-
-
- +
+ +
+ + - \ No newline at end of file diff --git a/packages/core/coverage/src/state/index.html b/packages/core/coverage/src/state/index.html index 5fa9fab..8f82c6f 100644 --- a/packages/core/coverage/src/state/index.html +++ b/packages/core/coverage/src/state/index.html @@ -1,138 +1,237 @@ - + + Code coverage report for src/state + + + + + + + - - Code coverage report for src/state - - - - - - - - - -
-
-

All files src/state

-
- -
- 48.64% - Statements - 594/1221 -
- - -
- 100% - Branches - 23/23 -
- - -
- 20% - Functions - 9/45 -
- - -
- 48.64% - Lines - 594/1221 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - + +
+
+

All files src/state

+
+
+ 48.64% + Statements + 594/1221 +
+ +
+ 100% + Branches + 23/23 +
+ +
+ 20% + Functions + 9/45 +
-
- - - - - - - - - - - +
+ 48.64% + Lines + 594/1221 +
+ +

+ Press n or j to go to the next uncovered + block, b, p or k for the previous + block. +

+ + +
+
+
FileStatementsBranchesFunctionsLines
format.ts -
-
16.59%124/747100%4/410.52%4/3816.59%124/747
index.ts -
-
81.81%18/22100%3/350%2/481.81%18/22
+ + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - -
+ File + + Statements + + Branches + + Functions + + Lines +
+ format.ts + +
+
+
+
+
16.59%124/747100%4/410.52%4/3816.59%124/747
mock.ts -
-
100%452/452100%16/16100%3/3100%452/452
+ index.ts + +
+
+
+
+
81.81%18/22100%3/350%2/481.81%18/22
-
-
-
- +
+ +
+ + - \ No newline at end of file diff --git a/packages/core/coverage/src/state/index.ts.html b/packages/core/coverage/src/state/index.ts.html index d96c22b..c32e172 100644 --- a/packages/core/coverage/src/state/index.ts.html +++ b/packages/core/coverage/src/state/index.ts.html @@ -1,68 +1,69 @@ - + + Code coverage report for src/state/index.ts + + + + + + + - - Code coverage report for src/state/index.ts - - - - - - - - - -
-
-

All files / src/state index.ts

-
- -
- 81.81% - Statements - 18/22 -
- - -
- 100% - Branches - 3/3 -
- - -
- 50% - Functions - 2/4 -
- - -
- 81.81% - Lines - 18/22 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

+            
+
1 2 3 @@ -173,13 +174,20 @@

All files / src/state

-
-
- +
+ + + + - \ No newline at end of file diff --git a/packages/core/coverage/src/state/mock.ts.html b/packages/core/coverage/src/state/mock.ts.html index 708f948..2ac5a2f 100644 --- a/packages/core/coverage/src/state/mock.ts.html +++ b/packages/core/coverage/src/state/mock.ts.html @@ -1,68 +1,69 @@ - + + Code coverage report for src/state/mock.ts + + + + + + + - - Code coverage report for src/state/mock.ts - - - - - - - - - -
-
-

All files / src/state mock.ts

-
- -
- 100% - Statements - 452/452 -
- - -
- 100% - Branches - 16/16 -
- - -
- 100% - Functions - 3/3 -
- - -
- 100% - Lines - 452/452 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

+            
+
1 2 3 @@ -1523,13 +1524,20 @@

All files / src/state

-
-
- +
+ + + + - \ No newline at end of file diff --git a/packages/core/coverage/src/type/events.ts.html b/packages/core/coverage/src/type/events.ts.html index 7fa51d6..a896464 100644 --- a/packages/core/coverage/src/type/events.ts.html +++ b/packages/core/coverage/src/type/events.ts.html @@ -1,68 +1,69 @@ - + + Code coverage report for src/type/events.ts + + + + + + + - - Code coverage report for src/type/events.ts - - - - - - - - - -
-
-

All files / src/type events.ts

-
- -
- 100% - Statements - 24/24 -
- - -
- 100% - Branches - 3/3 -
- - -
- 100% - Functions - 0/0 -
- - -
- 100% - Lines - 24/24 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

+            
+
1 2 3 @@ -1088,13 +1089,20 @@

All files / src/type };  

-
-
- +
+ + + + - \ No newline at end of file diff --git a/packages/core/coverage/src/type/index.html b/packages/core/coverage/src/type/index.html index 6c25aa6..085c7ac 100644 --- a/packages/core/coverage/src/type/index.html +++ b/packages/core/coverage/src/type/index.html @@ -1,123 +1,211 @@ - + + Code coverage report for src/type + + + + + + + - - Code coverage report for src/type - - - - - - - - - -
-
-

All files src/type

-
- -
- 100% - Statements - 557/557 -
- - -
- 100% - Branches - 19/19 -
- - -
- 100% - Functions - 0/0 -
- - -
- 100% - Lines - 557/557 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - + +
+
+

All files src/type

+
+
+ 100% + Statements + 557/557 +
+ +
+ 100% + Branches + 19/19 +
+ +
+ 100% + Functions + 0/0 +
-
- - - - - - - - - - - +
+ 100% + Lines + 557/557 +
+ +

+ Press n or j to go to the next uncovered + block, b, p or k for the previous + block. +

+ + +
+
+
FileStatementsBranchesFunctionsLines
events.ts -
-
100%24/24100%3/3100%0/0100%24/24
index.ts -
-
100%533/533100%16/16100%0/0100%533/533
+ + + + + + + + + + + + + + + + + + + + + + + + + + + - -
+ File + + Statements + + Branches + + Functions + + Lines +
+ events.ts + +
+
+
+
+
100%24/24100%3/3100%0/0100%24/24
-
-
-
- +
+ + + + - \ No newline at end of file diff --git a/packages/core/coverage/src/type/index.ts.html b/packages/core/coverage/src/type/index.ts.html index f5cf297..4c5d5ee 100644 --- a/packages/core/coverage/src/type/index.ts.html +++ b/packages/core/coverage/src/type/index.ts.html @@ -1,68 +1,69 @@ - + + Code coverage report for src/type/index.ts + + + + + + + - - Code coverage report for src/type/index.ts - - - - - - - - - -
-
-

All files / src/type index.ts

-
- -
- 100% - Statements - 533/533 -
- - -
- 100% - Branches - 16/16 -
- - -
- 100% - Functions - 0/0 -
- - -
- 100% - Lines - 533/533 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

+            
+
1 2 3 @@ -2429,13 +2430,20 @@

All files / src/type };  

-
-
- +
+ + + + - \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json index 5ae2c8f..c75028d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,34 +1,34 @@ { - "name": "@lootsurvivor/core", - "version": "0.0.1", - "description": "", - "main": "./dist/index.js", - "source": "src/index.ts", - "scripts": { - "build": "tsup --format cjs,esm --dts", - "test": "vitest run --coverage" - }, - "types": "./dist/index.d.ts", - "exports": { - ".": { - "require": "./dist/index.js", - "import": "./dist/index.mjs", - "types": "./dist/index.d.ts" + "name": "@lootsurvivor/core", + "version": "0.0.1", + "description": "", + "main": "./dist/index.js", + "source": "src/index.ts", + "scripts": { + "build": "tsup --format cjs,esm --dts", + "test": "vitest run --coverage" + }, + "types": "./dist/index.d.ts", + "exports": { + ".": { + "require": "./dist/index.js", + "import": "./dist/index.mjs", + "types": "./dist/index.d.ts" + } + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "graphql": "^16.9.0", + "graphql-request": "^7.1.0", + "starknet": "^6.11.0", + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.2", + "zustand": "^4.5.4" + }, + "devDependencies": { + "@vitest/coverage-v8": "^2.0.2" } - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "graphql": "^16.9.0", - "graphql-request": "^7.1.0", - "starknet": "^6.11.0", - "tsup": "^8.1.0", - "typescript": "^5.5.3", - "vitest": "^2.0.2", - "zustand": "^4.5.4" - }, - "devDependencies": { - "@vitest/coverage-v8": "^2.0.2" - } } diff --git a/packages/core/src/constants/index.ts b/packages/core/src/constants/index.ts index 2aae874..f182ba8 100644 --- a/packages/core/src/constants/index.ts +++ b/packages/core/src/constants/index.ts @@ -1,5 +1,5 @@ export const S3_BUCKET = - "https://loot-survivor.s3.ap-northeast-1.amazonaws.com/"; + "https://loot-survivor.s3.ap-northeast-1.amazonaws.com/"; export const ITEM_CHARISMA_DISCOUNT = 1; export const ITEM_BASE_PRICE = 4; diff --git a/packages/core/src/objects/beasts.test.ts b/packages/core/src/objects/beasts.test.ts index 1b69feb..c9dfee4 100644 --- a/packages/core/src/objects/beasts.test.ts +++ b/packages/core/src/objects/beasts.test.ts @@ -4,111 +4,117 @@ import { Beasts, BeastType, AttackType, ArmorType } from "../type"; import { S3_BUCKET } from "../constants"; describe("BeastManager", () => { - let beastManager: BeastManager; + let beastManager: BeastManager; - beforeEach(() => { - beastManager = new BeastManager(); - }); - - describe("getBeastName", () => { - it("should return the correct beast name for a given beast number", () => { - expect(beastManager.getBeastName(Beasts.Bear)).toBe("Bear"); - expect(beastManager.getBeastName(Beasts.Wolf)).toBe("Wolf"); + beforeEach(() => { + beastManager = new BeastManager(); }); - }); - describe("getBeastNumber", () => { - it("should return the correct beast number for a given beast name", () => { - expect(beastManager.getBeastNumber("Bear")).toBe(Beasts.Bear); - expect(beastManager.getBeastNumber("Wolf")).toBe(Beasts.Wolf); + describe("getBeastName", () => { + it("should return the correct beast name for a given beast number", () => { + expect(beastManager.getBeastName(Beasts.Bear)).toBe("Bear"); + expect(beastManager.getBeastName(Beasts.Wolf)).toBe("Wolf"); + }); }); - it("should return undefined for an invalid beast name", () => { - expect(beastManager.getBeastNumber("InvalidBeast")).toBeUndefined(); + describe("getBeastNumber", () => { + it("should return the correct beast number for a given beast name", () => { + expect(beastManager.getBeastNumber("Bear")).toBe(Beasts.Bear); + expect(beastManager.getBeastNumber("Wolf")).toBe(Beasts.Wolf); + }); + + it("should return undefined for an invalid beast name", () => { + expect(beastManager.getBeastNumber("InvalidBeast")).toBeUndefined(); + }); }); - }); - describe("getBeastTier", () => { - it("should return the correct type and tier for a given beast", () => { - const bearTier = beastManager.getBeastTier(Beasts.Bear); - expect(bearTier).toEqual({ type: BeastType.Hunter, tier: 5 }); + describe("getBeastTier", () => { + it("should return the correct type and tier for a given beast", () => { + const bearTier = beastManager.getBeastTier(Beasts.Bear); + expect(bearTier).toEqual({ type: BeastType.Hunter, tier: 5 }); + }); }); - }); - describe("getAttackType", () => { - it("should return the correct attack type for a given beast type", () => { - expect(beastManager.getAttackType(BeastType.Magical)).toBe( - AttackType.Magic - ); - expect(beastManager.getAttackType(BeastType.Hunter)).toBe( - AttackType.Blade - ); - expect(beastManager.getAttackType(BeastType.Brute)).toBe( - AttackType.Bludgeon - ); + describe("getAttackType", () => { + it("should return the correct attack type for a given beast type", () => { + expect(beastManager.getAttackType(BeastType.Magical)).toBe( + AttackType.Magic + ); + expect(beastManager.getAttackType(BeastType.Hunter)).toBe( + AttackType.Blade + ); + expect(beastManager.getAttackType(BeastType.Brute)).toBe( + AttackType.Bludgeon + ); + }); }); - }); - describe("getArmorType", () => { - it("should return the correct armor type for a given beast type", () => { - expect(beastManager.getArmorType(BeastType.Magical)).toBe( - ArmorType.Cloth - ); - expect(beastManager.getArmorType(BeastType.Hunter)).toBe(ArmorType.Hide); - expect(beastManager.getArmorType(BeastType.Brute)).toBe(ArmorType.Metal); + describe("getArmorType", () => { + it("should return the correct armor type for a given beast type", () => { + expect(beastManager.getArmorType(BeastType.Magical)).toBe( + ArmorType.Cloth + ); + expect(beastManager.getArmorType(BeastType.Hunter)).toBe( + ArmorType.Hide + ); + expect(beastManager.getArmorType(BeastType.Brute)).toBe( + ArmorType.Metal + ); + }); }); - }); - describe("getBeastType", () => { - it("should return the correct beast type for a given beast", () => { - expect(beastManager.getBeastType(Beasts.Bear)).toBe(BeastType.Hunter); + describe("getBeastType", () => { + it("should return the correct beast type for a given beast", () => { + expect(beastManager.getBeastType(Beasts.Bear)).toBe( + BeastType.Hunter + ); + }); }); - }); - describe("maxBeastId", () => { - it("should return the highest beast id", () => { - const maxId = beastManager.maxBeastId(); - expect(maxId).toBeGreaterThan(0); - expect(typeof maxId).toBe("number"); + describe("maxBeastId", () => { + it("should return the highest beast id", () => { + const maxId = beastManager.maxBeastId(); + expect(maxId).toBeGreaterThan(0); + expect(typeof maxId).toBe("number"); + }); }); - }); - describe("getBeastImage", () => { - it("should return the correct image URL for a given beast", () => { - const imageUrl = beastManager.getBeastImage(Beasts.Bear); - expect(imageUrl).toContain(S3_BUCKET); - expect(imageUrl).toContain("/monsters/bear.png"); + describe("getBeastImage", () => { + it("should return the correct image URL for a given beast", () => { + const imageUrl = beastManager.getBeastImage(Beasts.Bear); + expect(imageUrl).toContain(S3_BUCKET); + expect(imageUrl).toContain("/monsters/bear.png"); + }); }); - }); - describe("elementalAdjustedDamage", () => { - it("should increase damage for effective weapon-armor pairs", () => { - const baseDamage = 100; - const adjustedDamage = beastManager.elementalAdjustedDamage( - baseDamage, - "Magic", - "Metal" - ); - expect(adjustedDamage).toBe(150); - }); + describe("elementalAdjustedDamage", () => { + it("should increase damage for effective weapon-armor pairs", () => { + const baseDamage = 100; + const adjustedDamage = beastManager.elementalAdjustedDamage( + baseDamage, + "Magic", + "Metal" + ); + expect(adjustedDamage).toBe(150); + }); - it("should decrease damage for ineffective weapon-armor pairs", () => { - const baseDamage = 100; - const adjustedDamage = beastManager.elementalAdjustedDamage( - baseDamage, - "Magic", - "Hide" - ); - expect(adjustedDamage).toBe(50); - }); + it("should decrease damage for ineffective weapon-armor pairs", () => { + const baseDamage = 100; + const adjustedDamage = beastManager.elementalAdjustedDamage( + baseDamage, + "Magic", + "Hide" + ); + expect(adjustedDamage).toBe(50); + }); - it("should not change damage for neutral weapon-armor pairs", () => { - const baseDamage = 100; - const adjustedDamage = beastManager.elementalAdjustedDamage( - baseDamage, - "Magic", - "Cloth" - ); - expect(adjustedDamage).toBe(100); + it("should not change damage for neutral weapon-armor pairs", () => { + const baseDamage = 100; + const adjustedDamage = beastManager.elementalAdjustedDamage( + baseDamage, + "Magic", + "Cloth" + ); + expect(adjustedDamage).toBe(100); + }); }); - }); }); diff --git a/packages/core/src/objects/beasts.ts b/packages/core/src/objects/beasts.ts index 404f6f3..5dea16f 100644 --- a/packages/core/src/objects/beasts.ts +++ b/packages/core/src/objects/beasts.ts @@ -2,245 +2,246 @@ import { S3_BUCKET } from "../constants"; import { ArmorType, AttackType, BeastType, Beasts } from "../type"; export class BeastManager { - private static readonly BEAST_ATTACK_TYPES: Record = { - [BeastType.Magical]: AttackType.Magic, - [BeastType.Hunter]: AttackType.Blade, - [BeastType.Brute]: AttackType.Bludgeon, - }; - - private static readonly BEAST_ARMOR_TYPES: Record = { - [BeastType.Magical]: ArmorType.Cloth, - [BeastType.Hunter]: ArmorType.Hide, - [BeastType.Brute]: ArmorType.Metal, - }; - - // pass in alternateImagePath to use a different image path - private alternateImagePath: string = ""; - - constructor(alternateImagePath?: string) { - this.alternateImagePath = alternateImagePath || ""; - } - - getBeastName(beast: Beasts): string { - return Beasts[beast]; - } - - getBeastNumber(beastName: string): Beasts | undefined { - return Beasts[beastName as keyof typeof Beasts]; - } - - getBeastTier(beast: Beasts): { type: BeastType; tier: number } { - return BEAST_TIERS[beast]; - } - - getAttackType(beastType: BeastType): AttackType { - return BeastManager.BEAST_ATTACK_TYPES[beastType]; - } - - getArmorType(beastType: BeastType): ArmorType { - return BeastManager.BEAST_ARMOR_TYPES[beastType]; - } - - getBeastType(beast: Beasts): BeastType { - return BEAST_TIERS[beast].type; - } - - maxBeastId(): number { - return Math.max( - ...Object.values(Beasts).filter( - (value): value is number => typeof value === "number" - ) - ); - } - - getBeastImage(beast: Beasts): string { - if (this.alternateImagePath) { - return this.alternateImagePath + BEAST_IMAGES[beast]; - } - return S3_BUCKET + BEAST_IMAGES[beast]; - } - - elementalAdjustedDamage( - base_attack: number, - weapon_type: string, - armor_type: string - ): number { - const ELEMENTAL_EFFECT = Math.floor(base_attack / 2); - const EFFECTIVE_PAIRS: Record = { - Magic: "Metal", - Blade: "Cloth", - Bludgeon: "Hide", - }; - const INEFFECTIVE_PAIRS: Record = { - Magic: "Hide", - Blade: "Metal", - Bludgeon: "Cloth", + private static readonly BEAST_ATTACK_TYPES: Record = + { + [BeastType.Magical]: AttackType.Magic, + [BeastType.Hunter]: AttackType.Blade, + [BeastType.Brute]: AttackType.Bludgeon, + }; + + private static readonly BEAST_ARMOR_TYPES: Record = { + [BeastType.Magical]: ArmorType.Cloth, + [BeastType.Hunter]: ArmorType.Hide, + [BeastType.Brute]: ArmorType.Metal, }; - if (EFFECTIVE_PAIRS[weapon_type] === armor_type) { - return base_attack + ELEMENTAL_EFFECT; + // pass in alternateImagePath to use a different image path + private alternateImagePath: string = ""; + + constructor(alternateImagePath?: string) { + this.alternateImagePath = alternateImagePath || ""; + } + + getBeastName(beast: Beasts): string { + return Beasts[beast]; + } + + getBeastNumber(beastName: string): Beasts | undefined { + return Beasts[beastName as keyof typeof Beasts]; + } + + getBeastTier(beast: Beasts): { type: BeastType; tier: number } { + return BEAST_TIERS[beast]; } - if (INEFFECTIVE_PAIRS[weapon_type] === armor_type) { - return base_attack - ELEMENTAL_EFFECT; + getAttackType(beastType: BeastType): AttackType { + return BeastManager.BEAST_ATTACK_TYPES[beastType]; } - return base_attack; - } + getArmorType(beastType: BeastType): ArmorType { + return BeastManager.BEAST_ARMOR_TYPES[beastType]; + } + + getBeastType(beast: Beasts): BeastType { + return BEAST_TIERS[beast].type; + } + + maxBeastId(): number { + return Math.max( + ...Object.values(Beasts).filter( + (value): value is number => typeof value === "number" + ) + ); + } + + getBeastImage(beast: Beasts): string { + if (this.alternateImagePath) { + return this.alternateImagePath + BEAST_IMAGES[beast]; + } + return S3_BUCKET + BEAST_IMAGES[beast]; + } + + elementalAdjustedDamage( + base_attack: number, + weapon_type: string, + armor_type: string + ): number { + const ELEMENTAL_EFFECT = Math.floor(base_attack / 2); + const EFFECTIVE_PAIRS: Record = { + Magic: "Metal", + Blade: "Cloth", + Bludgeon: "Hide", + }; + const INEFFECTIVE_PAIRS: Record = { + Magic: "Hide", + Blade: "Metal", + Bludgeon: "Cloth", + }; + + if (EFFECTIVE_PAIRS[weapon_type] === armor_type) { + return base_attack + ELEMENTAL_EFFECT; + } + + if (INEFFECTIVE_PAIRS[weapon_type] === armor_type) { + return base_attack - ELEMENTAL_EFFECT; + } + + return base_attack; + } } export const BEAST_TIERS: Record = { - [Beasts.Warlock]: { type: BeastType.Magical, tier: 1 }, - [Beasts.Typhon]: { type: BeastType.Magical, tier: 1 }, - [Beasts.Jiangshi]: { type: BeastType.Magical, tier: 1 }, - [Beasts.Anansi]: { type: BeastType.Magical, tier: 1 }, - [Beasts.Basilisk]: { type: BeastType.Magical, tier: 1 }, - [Beasts.Gorgon]: { type: BeastType.Magical, tier: 2 }, - [Beasts.Kitsune]: { type: BeastType.Magical, tier: 2 }, - [Beasts.Lich]: { type: BeastType.Magical, tier: 2 }, - [Beasts.Chimera]: { type: BeastType.Magical, tier: 2 }, - [Beasts.Wendigo]: { type: BeastType.Magical, tier: 2 }, - [Beasts.Rakshasa]: { type: BeastType.Magical, tier: 3 }, - [Beasts.Werewolf]: { type: BeastType.Magical, tier: 3 }, - [Beasts.Banshee]: { type: BeastType.Magical, tier: 3 }, - [Beasts.Draugr]: { type: BeastType.Magical, tier: 3 }, - [Beasts.Vampire]: { type: BeastType.Magical, tier: 3 }, - [Beasts.Goblin]: { type: BeastType.Magical, tier: 4 }, - [Beasts.Ghoul]: { type: BeastType.Magical, tier: 4 }, - [Beasts.Wraith]: { type: BeastType.Magical, tier: 4 }, - [Beasts.Sprite]: { type: BeastType.Magical, tier: 4 }, - [Beasts.Kappa]: { type: BeastType.Magical, tier: 4 }, - [Beasts.Fairy]: { type: BeastType.Magical, tier: 5 }, - [Beasts.Leprechaun]: { type: BeastType.Magical, tier: 5 }, - [Beasts.Kelpie]: { type: BeastType.Magical, tier: 5 }, - [Beasts.Pixie]: { type: BeastType.Magical, tier: 5 }, - [Beasts.Gnome]: { type: BeastType.Magical, tier: 5 }, - [Beasts.Griffin]: { type: BeastType.Hunter, tier: 1 }, - [Beasts.Manticore]: { type: BeastType.Hunter, tier: 1 }, - [Beasts.Phoenix]: { type: BeastType.Hunter, tier: 1 }, - [Beasts.Dragon]: { type: BeastType.Hunter, tier: 1 }, - [Beasts.Minotaur]: { type: BeastType.Hunter, tier: 1 }, - [Beasts.Qilin]: { type: BeastType.Hunter, tier: 2 }, - [Beasts.Ammit]: { type: BeastType.Hunter, tier: 2 }, - [Beasts.Nue]: { type: BeastType.Hunter, tier: 2 }, - [Beasts.Skinwalker]: { type: BeastType.Hunter, tier: 2 }, - [Beasts.Chupacabra]: { type: BeastType.Hunter, tier: 2 }, - [Beasts.Weretiger]: { type: BeastType.Hunter, tier: 3 }, - [Beasts.Wyvern]: { type: BeastType.Hunter, tier: 3 }, - [Beasts.Roc]: { type: BeastType.Hunter, tier: 3 }, - [Beasts.Harpy]: { type: BeastType.Hunter, tier: 3 }, - [Beasts.Pegasus]: { type: BeastType.Hunter, tier: 3 }, - [Beasts.Hippogriff]: { type: BeastType.Hunter, tier: 4 }, - [Beasts.Fenrir]: { type: BeastType.Hunter, tier: 4 }, - [Beasts.Jaguar]: { type: BeastType.Hunter, tier: 4 }, - [Beasts.Satori]: { type: BeastType.Hunter, tier: 4 }, - [Beasts.DireWolf]: { type: BeastType.Hunter, tier: 4 }, - [Beasts.Bear]: { type: BeastType.Hunter, tier: 5 }, - [Beasts.Wolf]: { type: BeastType.Hunter, tier: 5 }, - [Beasts.Mantis]: { type: BeastType.Hunter, tier: 5 }, - [Beasts.Spider]: { type: BeastType.Hunter, tier: 5 }, - [Beasts.Rat]: { type: BeastType.Hunter, tier: 5 }, - [Beasts.Kraken]: { type: BeastType.Brute, tier: 1 }, - [Beasts.Colossus]: { type: BeastType.Brute, tier: 1 }, - [Beasts.Balrog]: { type: BeastType.Brute, tier: 1 }, - [Beasts.Leviathan]: { type: BeastType.Brute, tier: 1 }, - [Beasts.Tarrasque]: { type: BeastType.Brute, tier: 1 }, - [Beasts.Titan]: { type: BeastType.Brute, tier: 2 }, - [Beasts.Nephilim]: { type: BeastType.Brute, tier: 2 }, - [Beasts.Behemoth]: { type: BeastType.Brute, tier: 2 }, - [Beasts.Hydra]: { type: BeastType.Brute, tier: 2 }, - [Beasts.Juggernaut]: { type: BeastType.Brute, tier: 2 }, - [Beasts.Oni]: { type: BeastType.Brute, tier: 3 }, - [Beasts.Jotunn]: { type: BeastType.Brute, tier: 3 }, - [Beasts.Ettin]: { type: BeastType.Brute, tier: 3 }, - [Beasts.Cyclops]: { type: BeastType.Brute, tier: 3 }, - [Beasts.Giant]: { type: BeastType.Brute, tier: 3 }, - [Beasts.NemeanLion]: { type: BeastType.Brute, tier: 4 }, - [Beasts.Berserker]: { type: BeastType.Brute, tier: 4 }, - [Beasts.Yeti]: { type: BeastType.Brute, tier: 4 }, - [Beasts.Golem]: { type: BeastType.Brute, tier: 4 }, - [Beasts.Ent]: { type: BeastType.Brute, tier: 4 }, - [Beasts.Troll]: { type: BeastType.Brute, tier: 5 }, - [Beasts.Bigfoot]: { type: BeastType.Brute, tier: 5 }, - [Beasts.Ogre]: { type: BeastType.Brute, tier: 5 }, - [Beasts.Orc]: { type: BeastType.Brute, tier: 5 }, - [Beasts.Skeleton]: { type: BeastType.Brute, tier: 5 }, + [Beasts.Warlock]: { type: BeastType.Magical, tier: 1 }, + [Beasts.Typhon]: { type: BeastType.Magical, tier: 1 }, + [Beasts.Jiangshi]: { type: BeastType.Magical, tier: 1 }, + [Beasts.Anansi]: { type: BeastType.Magical, tier: 1 }, + [Beasts.Basilisk]: { type: BeastType.Magical, tier: 1 }, + [Beasts.Gorgon]: { type: BeastType.Magical, tier: 2 }, + [Beasts.Kitsune]: { type: BeastType.Magical, tier: 2 }, + [Beasts.Lich]: { type: BeastType.Magical, tier: 2 }, + [Beasts.Chimera]: { type: BeastType.Magical, tier: 2 }, + [Beasts.Wendigo]: { type: BeastType.Magical, tier: 2 }, + [Beasts.Rakshasa]: { type: BeastType.Magical, tier: 3 }, + [Beasts.Werewolf]: { type: BeastType.Magical, tier: 3 }, + [Beasts.Banshee]: { type: BeastType.Magical, tier: 3 }, + [Beasts.Draugr]: { type: BeastType.Magical, tier: 3 }, + [Beasts.Vampire]: { type: BeastType.Magical, tier: 3 }, + [Beasts.Goblin]: { type: BeastType.Magical, tier: 4 }, + [Beasts.Ghoul]: { type: BeastType.Magical, tier: 4 }, + [Beasts.Wraith]: { type: BeastType.Magical, tier: 4 }, + [Beasts.Sprite]: { type: BeastType.Magical, tier: 4 }, + [Beasts.Kappa]: { type: BeastType.Magical, tier: 4 }, + [Beasts.Fairy]: { type: BeastType.Magical, tier: 5 }, + [Beasts.Leprechaun]: { type: BeastType.Magical, tier: 5 }, + [Beasts.Kelpie]: { type: BeastType.Magical, tier: 5 }, + [Beasts.Pixie]: { type: BeastType.Magical, tier: 5 }, + [Beasts.Gnome]: { type: BeastType.Magical, tier: 5 }, + [Beasts.Griffin]: { type: BeastType.Hunter, tier: 1 }, + [Beasts.Manticore]: { type: BeastType.Hunter, tier: 1 }, + [Beasts.Phoenix]: { type: BeastType.Hunter, tier: 1 }, + [Beasts.Dragon]: { type: BeastType.Hunter, tier: 1 }, + [Beasts.Minotaur]: { type: BeastType.Hunter, tier: 1 }, + [Beasts.Qilin]: { type: BeastType.Hunter, tier: 2 }, + [Beasts.Ammit]: { type: BeastType.Hunter, tier: 2 }, + [Beasts.Nue]: { type: BeastType.Hunter, tier: 2 }, + [Beasts.Skinwalker]: { type: BeastType.Hunter, tier: 2 }, + [Beasts.Chupacabra]: { type: BeastType.Hunter, tier: 2 }, + [Beasts.Weretiger]: { type: BeastType.Hunter, tier: 3 }, + [Beasts.Wyvern]: { type: BeastType.Hunter, tier: 3 }, + [Beasts.Roc]: { type: BeastType.Hunter, tier: 3 }, + [Beasts.Harpy]: { type: BeastType.Hunter, tier: 3 }, + [Beasts.Pegasus]: { type: BeastType.Hunter, tier: 3 }, + [Beasts.Hippogriff]: { type: BeastType.Hunter, tier: 4 }, + [Beasts.Fenrir]: { type: BeastType.Hunter, tier: 4 }, + [Beasts.Jaguar]: { type: BeastType.Hunter, tier: 4 }, + [Beasts.Satori]: { type: BeastType.Hunter, tier: 4 }, + [Beasts.DireWolf]: { type: BeastType.Hunter, tier: 4 }, + [Beasts.Bear]: { type: BeastType.Hunter, tier: 5 }, + [Beasts.Wolf]: { type: BeastType.Hunter, tier: 5 }, + [Beasts.Mantis]: { type: BeastType.Hunter, tier: 5 }, + [Beasts.Spider]: { type: BeastType.Hunter, tier: 5 }, + [Beasts.Rat]: { type: BeastType.Hunter, tier: 5 }, + [Beasts.Kraken]: { type: BeastType.Brute, tier: 1 }, + [Beasts.Colossus]: { type: BeastType.Brute, tier: 1 }, + [Beasts.Balrog]: { type: BeastType.Brute, tier: 1 }, + [Beasts.Leviathan]: { type: BeastType.Brute, tier: 1 }, + [Beasts.Tarrasque]: { type: BeastType.Brute, tier: 1 }, + [Beasts.Titan]: { type: BeastType.Brute, tier: 2 }, + [Beasts.Nephilim]: { type: BeastType.Brute, tier: 2 }, + [Beasts.Behemoth]: { type: BeastType.Brute, tier: 2 }, + [Beasts.Hydra]: { type: BeastType.Brute, tier: 2 }, + [Beasts.Juggernaut]: { type: BeastType.Brute, tier: 2 }, + [Beasts.Oni]: { type: BeastType.Brute, tier: 3 }, + [Beasts.Jotunn]: { type: BeastType.Brute, tier: 3 }, + [Beasts.Ettin]: { type: BeastType.Brute, tier: 3 }, + [Beasts.Cyclops]: { type: BeastType.Brute, tier: 3 }, + [Beasts.Giant]: { type: BeastType.Brute, tier: 3 }, + [Beasts.NemeanLion]: { type: BeastType.Brute, tier: 4 }, + [Beasts.Berserker]: { type: BeastType.Brute, tier: 4 }, + [Beasts.Yeti]: { type: BeastType.Brute, tier: 4 }, + [Beasts.Golem]: { type: BeastType.Brute, tier: 4 }, + [Beasts.Ent]: { type: BeastType.Brute, tier: 4 }, + [Beasts.Troll]: { type: BeastType.Brute, tier: 5 }, + [Beasts.Bigfoot]: { type: BeastType.Brute, tier: 5 }, + [Beasts.Ogre]: { type: BeastType.Brute, tier: 5 }, + [Beasts.Orc]: { type: BeastType.Brute, tier: 5 }, + [Beasts.Skeleton]: { type: BeastType.Brute, tier: 5 }, }; export const BEAST_IMAGES: Record = { - [Beasts.Warlock]: "/monsters/warlock.png", - [Beasts.Typhon]: "/monsters/typhon.png", - [Beasts.Jiangshi]: "/monsters/jiangshi.png", - [Beasts.Anansi]: "/monsters/anansi.png", - [Beasts.Basilisk]: "/monsters/basilisk.png", - [Beasts.Gorgon]: "/monsters/gorgon.png", - [Beasts.Kitsune]: "/monsters/kitsune.png", - [Beasts.Lich]: "/monsters/lich.png", - [Beasts.Chimera]: "/monsters/chimera.png", - [Beasts.Wendigo]: "/monsters/wendigo.png", - [Beasts.Rakshasa]: "/monsters/rakshasa.png", - [Beasts.Werewolf]: "/monsters/werewolf.png", - [Beasts.Banshee]: "/monsters/banshee.png", - [Beasts.Draugr]: "/monsters/draugr.png", - [Beasts.Vampire]: "/monsters/vampire.png", - [Beasts.Goblin]: "/monsters/goblin.png", - [Beasts.Ghoul]: "/monsters/ghoul.png", - [Beasts.Wraith]: "/monsters/wraith.png", - [Beasts.Sprite]: "/monsters/sprite.png", - [Beasts.Kappa]: "/monsters/kappa.png", - [Beasts.Fairy]: "/monsters/fairy.png", - [Beasts.Leprechaun]: "/monsters/leprechaun.png", - [Beasts.Kelpie]: "/monsters/kelpie.png", - [Beasts.Pixie]: "/monsters/pixie.png", - [Beasts.Gnome]: "/monsters/gnome.png", - [Beasts.Griffin]: "/monsters/griffin.png", - [Beasts.Manticore]: "/monsters/manticore.png", - [Beasts.Phoenix]: "/monsters/phoenix.png", - [Beasts.Dragon]: "/monsters/dragon.png", - [Beasts.Minotaur]: "/monsters/minotaur.png", - [Beasts.Qilin]: "/monsters/qilin.png", - [Beasts.Ammit]: "/monsters/ammit.png", - [Beasts.Nue]: "/monsters/nue.png", - [Beasts.Skinwalker]: "/monsters/skinwalker.png", - [Beasts.Chupacabra]: "/monsters/chupacabra.png", - [Beasts.Weretiger]: "/monsters/weretiger.png", - [Beasts.Wyvern]: "/monsters/wyvern.png", - [Beasts.Roc]: "/monsters/roc.png", - [Beasts.Harpy]: "/monsters/harpy.png", - [Beasts.Pegasus]: "/monsters/pegasus.png", - [Beasts.Hippogriff]: "/monsters/hippogriff.png", - [Beasts.Fenrir]: "/monsters/fenrir.png", - [Beasts.Jaguar]: "/monsters/jaguar.png", - [Beasts.Satori]: "/monsters/satori.png", - [Beasts.DireWolf]: "/monsters/direwolf.png", - [Beasts.Bear]: "/monsters/bear.png", - [Beasts.Wolf]: "/monsters/wolf.png", - [Beasts.Mantis]: "/monsters/mantis.png", - [Beasts.Spider]: "/monsters/spider.png", - [Beasts.Rat]: "/monsters/rat.png", - [Beasts.Kraken]: "/monsters/kraken.png", - [Beasts.Colossus]: "/monsters/colossus.png", - [Beasts.Balrog]: "/monsters/balrog.png", - [Beasts.Leviathan]: "/monsters/leviathan.png", - [Beasts.Tarrasque]: "/monsters/tarrasque.png", - [Beasts.Titan]: "/monsters/titan.png", - [Beasts.Nephilim]: "/monsters/nephilim.png", - [Beasts.Behemoth]: "/monsters/behemoth.png", - [Beasts.Hydra]: "/monsters/hydra.png", - [Beasts.Juggernaut]: "/monsters/juggernaut.png", - [Beasts.Oni]: "/monsters/oni.png", - [Beasts.Jotunn]: "/monsters/jotunn.png", - [Beasts.Ettin]: "/monsters/ettin.png", - [Beasts.Cyclops]: "/monsters/cyclops.png", - [Beasts.Giant]: "/monsters/giant.png", - [Beasts.NemeanLion]: "/monsters/nemeanlion.png", - [Beasts.Berserker]: "/monsters/berserker.png", - [Beasts.Yeti]: "/monsters/yeti.png", - [Beasts.Golem]: "/monsters/golem.png", - [Beasts.Ent]: "/monsters/ent.png", - [Beasts.Troll]: "/monsters/troll.png", - [Beasts.Bigfoot]: "/monsters/bigfoot.png", - [Beasts.Ogre]: "/monsters/ogre.png", - [Beasts.Orc]: "/monsters/orc.png", - [Beasts.Skeleton]: "/monsters/skeleton.png", + [Beasts.Warlock]: "/monsters/warlock.png", + [Beasts.Typhon]: "/monsters/typhon.png", + [Beasts.Jiangshi]: "/monsters/jiangshi.png", + [Beasts.Anansi]: "/monsters/anansi.png", + [Beasts.Basilisk]: "/monsters/basilisk.png", + [Beasts.Gorgon]: "/monsters/gorgon.png", + [Beasts.Kitsune]: "/monsters/kitsune.png", + [Beasts.Lich]: "/monsters/lich.png", + [Beasts.Chimera]: "/monsters/chimera.png", + [Beasts.Wendigo]: "/monsters/wendigo.png", + [Beasts.Rakshasa]: "/monsters/rakshasa.png", + [Beasts.Werewolf]: "/monsters/werewolf.png", + [Beasts.Banshee]: "/monsters/banshee.png", + [Beasts.Draugr]: "/monsters/draugr.png", + [Beasts.Vampire]: "/monsters/vampire.png", + [Beasts.Goblin]: "/monsters/goblin.png", + [Beasts.Ghoul]: "/monsters/ghoul.png", + [Beasts.Wraith]: "/monsters/wraith.png", + [Beasts.Sprite]: "/monsters/sprite.png", + [Beasts.Kappa]: "/monsters/kappa.png", + [Beasts.Fairy]: "/monsters/fairy.png", + [Beasts.Leprechaun]: "/monsters/leprechaun.png", + [Beasts.Kelpie]: "/monsters/kelpie.png", + [Beasts.Pixie]: "/monsters/pixie.png", + [Beasts.Gnome]: "/monsters/gnome.png", + [Beasts.Griffin]: "/monsters/griffin.png", + [Beasts.Manticore]: "/monsters/manticore.png", + [Beasts.Phoenix]: "/monsters/phoenix.png", + [Beasts.Dragon]: "/monsters/dragon.png", + [Beasts.Minotaur]: "/monsters/minotaur.png", + [Beasts.Qilin]: "/monsters/qilin.png", + [Beasts.Ammit]: "/monsters/ammit.png", + [Beasts.Nue]: "/monsters/nue.png", + [Beasts.Skinwalker]: "/monsters/skinwalker.png", + [Beasts.Chupacabra]: "/monsters/chupacabra.png", + [Beasts.Weretiger]: "/monsters/weretiger.png", + [Beasts.Wyvern]: "/monsters/wyvern.png", + [Beasts.Roc]: "/monsters/roc.png", + [Beasts.Harpy]: "/monsters/harpy.png", + [Beasts.Pegasus]: "/monsters/pegasus.png", + [Beasts.Hippogriff]: "/monsters/hippogriff.png", + [Beasts.Fenrir]: "/monsters/fenrir.png", + [Beasts.Jaguar]: "/monsters/jaguar.png", + [Beasts.Satori]: "/monsters/satori.png", + [Beasts.DireWolf]: "/monsters/direwolf.png", + [Beasts.Bear]: "/monsters/bear.png", + [Beasts.Wolf]: "/monsters/wolf.png", + [Beasts.Mantis]: "/monsters/mantis.png", + [Beasts.Spider]: "/monsters/spider.png", + [Beasts.Rat]: "/monsters/rat.png", + [Beasts.Kraken]: "/monsters/kraken.png", + [Beasts.Colossus]: "/monsters/colossus.png", + [Beasts.Balrog]: "/monsters/balrog.png", + [Beasts.Leviathan]: "/monsters/leviathan.png", + [Beasts.Tarrasque]: "/monsters/tarrasque.png", + [Beasts.Titan]: "/monsters/titan.png", + [Beasts.Nephilim]: "/monsters/nephilim.png", + [Beasts.Behemoth]: "/monsters/behemoth.png", + [Beasts.Hydra]: "/monsters/hydra.png", + [Beasts.Juggernaut]: "/monsters/juggernaut.png", + [Beasts.Oni]: "/monsters/oni.png", + [Beasts.Jotunn]: "/monsters/jotunn.png", + [Beasts.Ettin]: "/monsters/ettin.png", + [Beasts.Cyclops]: "/monsters/cyclops.png", + [Beasts.Giant]: "/monsters/giant.png", + [Beasts.NemeanLion]: "/monsters/nemeanlion.png", + [Beasts.Berserker]: "/monsters/berserker.png", + [Beasts.Yeti]: "/monsters/yeti.png", + [Beasts.Golem]: "/monsters/golem.png", + [Beasts.Ent]: "/monsters/ent.png", + [Beasts.Troll]: "/monsters/troll.png", + [Beasts.Bigfoot]: "/monsters/bigfoot.png", + [Beasts.Ogre]: "/monsters/ogre.png", + [Beasts.Orc]: "/monsters/orc.png", + [Beasts.Skeleton]: "/monsters/skeleton.png", }; diff --git a/packages/core/src/objects/loot.test.ts b/packages/core/src/objects/loot.test.ts index 64a5566..54a5350 100644 --- a/packages/core/src/objects/loot.test.ts +++ b/packages/core/src/objects/loot.test.ts @@ -1,91 +1,91 @@ import { describe, it, expect, vi } from "vitest"; import { LootManager } from "./loot"; import { - Loot, - ItemNamePrefix, - ItemNameSuffix, - ItemSuffix, - ItemType, - ItemSlot, - StatBoost, - StatType, + Loot, + ItemNamePrefix, + ItemNameSuffix, + ItemSuffix, + ItemType, + ItemSlot, + StatBoost, + StatType, } from "../type"; describe("LootManager", () => { - const lootManager = new LootManager(Loot.Pendant, 1000, BigInt(1234567)); - - it("should return the correct item name", () => { - expect(lootManager.getItemName()).toBe("Pendant"); - }); - - it("should return the correct item number", () => { - expect(lootManager.getItemNumber("Pendant")).toBe(Loot.Pendant); - }); - - it("should return the correct item type", () => { - expect(lootManager.getItemType()).toBe(ItemType.Necklace); - }); - - it("should return the correct item slot", () => { - expect(lootManager.getItemSlot()).toBe(ItemSlot.Neck); - }); - - it("should return the correct item name prefix", () => { - expect(lootManager.getItemNamePrefix(ItemNamePrefix.Dire)).toBe("Dire"); - }); - - it("should return the correct item name prefix number", () => { - expect(lootManager.getItemNamePrefixNumber("Agony")).toBe( - ItemNamePrefix.Agony - ); - }); - - it("should return the correct item name suffix", () => { - expect(lootManager.getItemNameSuffix(ItemNameSuffix.Bane)).toBe("Bane"); - }); - - it("should return the correct item name suffix number", () => { - expect(lootManager.getItemNameSuffixNumber("Bane")).toBe( - ItemNameSuffix.Bane - ); - }); - - it("should return the correct item suffix number", () => { - expect(lootManager.getItemSuffixNumber("Of Anger")).toBe( - ItemSuffix.OfAnger - ); - }); - - it("should return the correct item suffix boost string", () => { - const expectedBoostString = "Strength +2 Dexterity +1"; - expect(lootManager.getItemSuffixBoostString(ItemSuffix.OfAnger)).toBe( - expectedBoostString - ); - }); - - it("should return the correct item suffix boost string", () => { - const expectedBoostString = "Strength +2 Dexterity +1"; - expect(lootManager.getItemSuffixBoostString(ItemSuffix.OfAnger)).toBe( - expectedBoostString - ); - }); - - it("should return the correct item slot number", () => { - expect(lootManager.getItemSlotNumber(ItemSlot.Neck)).toBe(7); - }); - - it("should return the correct item slot from number", () => { - expect(lootManager.getItemSlotFromNumber(1)).toBe(ItemSlot.Weapon); - }); - describe("getFullItemName", () => { - it("should return a full item name with all components", () => { - // Mock the necessary methods - - const result = lootManager.getFullItemName(); - - expect(result).toBe( - "Whisper Lights Pendant Of Anger (Strength +2 Dexterity +1)" - ); + const lootManager = new LootManager(Loot.Pendant, 1000, BigInt(1234567)); + + it("should return the correct item name", () => { + expect(lootManager.getItemName()).toBe("Pendant"); + }); + + it("should return the correct item number", () => { + expect(lootManager.getItemNumber("Pendant")).toBe(Loot.Pendant); + }); + + it("should return the correct item type", () => { + expect(lootManager.getItemType()).toBe(ItemType.Necklace); + }); + + it("should return the correct item slot", () => { + expect(lootManager.getItemSlot()).toBe(ItemSlot.Neck); + }); + + it("should return the correct item name prefix", () => { + expect(lootManager.getItemNamePrefix(ItemNamePrefix.Dire)).toBe("Dire"); + }); + + it("should return the correct item name prefix number", () => { + expect(lootManager.getItemNamePrefixNumber("Agony")).toBe( + ItemNamePrefix.Agony + ); + }); + + it("should return the correct item name suffix", () => { + expect(lootManager.getItemNameSuffix(ItemNameSuffix.Bane)).toBe("Bane"); + }); + + it("should return the correct item name suffix number", () => { + expect(lootManager.getItemNameSuffixNumber("Bane")).toBe( + ItemNameSuffix.Bane + ); + }); + + it("should return the correct item suffix number", () => { + expect(lootManager.getItemSuffixNumber("Of Anger")).toBe( + ItemSuffix.OfAnger + ); + }); + + it("should return the correct item suffix boost string", () => { + const expectedBoostString = "Strength +2 Dexterity +1"; + expect(lootManager.getItemSuffixBoostString(ItemSuffix.OfAnger)).toBe( + expectedBoostString + ); + }); + + it("should return the correct item suffix boost string", () => { + const expectedBoostString = "Strength +2 Dexterity +1"; + expect(lootManager.getItemSuffixBoostString(ItemSuffix.OfAnger)).toBe( + expectedBoostString + ); + }); + + it("should return the correct item slot number", () => { + expect(lootManager.getItemSlotNumber(ItemSlot.Neck)).toBe(7); + }); + + it("should return the correct item slot from number", () => { + expect(lootManager.getItemSlotFromNumber(1)).toBe(ItemSlot.Weapon); + }); + describe("getFullItemName", () => { + it("should return a full item name with all components", () => { + // Mock the necessary methods + + const result = lootManager.getFullItemName(); + + expect(result).toBe( + "Whisper Lights Pendant Of Anger (Strength +2 Dexterity +1)" + ); + }); }); - }); }); diff --git a/packages/core/src/objects/loot.ts b/packages/core/src/objects/loot.ts index 1733a90..b5b0581 100644 --- a/packages/core/src/objects/loot.ts +++ b/packages/core/src/objects/loot.ts @@ -1,509 +1,516 @@ import { - ITEM_SLOT_TO_NUMBER, - ITEM_SUFFIX_BOOST, - ItemNamePrefix, - ItemNameSuffix, - ItemSlot, - ItemSuffix, - ItemType, - Loot, - NUMBER_TO_ITEM_SLOT, - StatBoost, - StatType, + ITEM_SLOT_TO_NUMBER, + ITEM_SUFFIX_BOOST, + ItemNamePrefix, + ItemNameSuffix, + ItemSlot, + ItemSuffix, + ItemType, + Loot, + NUMBER_TO_ITEM_SLOT, + StatBoost, + StatType, } from "../type"; export class LootManager { - private static TWO_POW_64 = BigInt("0x10000000000000000"); - private static NUM_ITEMS = 101; - - private static SUFFIX_UNLOCK_GREATNESS = 15; - private static PREFIXES_UNLOCK_GREATNESS = 19; - - private item: Loot; - private xp: number; - private seed: bigint; - - constructor(item: Loot, xp: number, seed: bigint) { - this.item = item; - this.xp = xp; - this.seed = seed; - } - - getFullItemName(): string { - const itemName = this.getItemName(); - const specials = this.getSpecials( - this.item, - this.calculateGreatness(this.xp), - this.seed - ); - - let name = itemName; - - if (specials.special1 !== 0) { - const suffix = this.getItemSuffixName(specials.special1); - const boostString = this.getItemSuffixBoostString(specials.special1); - name += ` ${suffix} (${boostString})`; + private static TWO_POW_64 = BigInt("0x10000000000000000"); + private static NUM_ITEMS = 101; + + private static SUFFIX_UNLOCK_GREATNESS = 15; + private static PREFIXES_UNLOCK_GREATNESS = 19; + + private item: Loot; + private xp: number; + private seed: bigint; + + constructor(item: Loot, xp: number, seed: bigint) { + this.item = item; + this.xp = xp; + this.seed = seed; } - if (specials.special2 !== 0) { - const prefix1 = this.getItemNamePrefix(specials.special2); - name = `${prefix1} ${name}`; + getFullItemName(): string { + const itemName = this.getItemName(); + const specials = this.getSpecials( + this.item, + this.calculateGreatness(this.xp), + this.seed + ); + + let name = itemName; + + if (specials.special1 !== 0) { + const suffix = this.getItemSuffixName(specials.special1); + const boostString = this.getItemSuffixBoostString( + specials.special1 + ); + name += ` ${suffix} (${boostString})`; + } + + if (specials.special2 !== 0) { + const prefix1 = this.getItemNamePrefix(specials.special2); + name = `${prefix1} ${name}`; + } + + if (specials.special3 !== 0) { + const prefix2 = this.getItemNameSuffix(specials.special3); + name = `${prefix2} ${name}`; + } + + return name.trim(); } - if (specials.special3 !== 0) { - const prefix2 = this.getItemNameSuffix(specials.special3); - name = `${prefix2} ${name}`; + private getSpecials( + item: Loot, + greatness: number, + seed: bigint + ): { special1: number; special2: number; special3: number } { + if (greatness < LootManager.SUFFIX_UNLOCK_GREATNESS) { + return { special1: 0, special2: 0, special3: 0 }; + } else if (greatness < LootManager.PREFIXES_UNLOCK_GREATNESS) { + return { + special1: this.getItemSuffix(item, seed), + special2: 0, + special3: 0, + }; + } else { + return { + special1: this.getItemSuffix(item, seed), + special2: this.getPrefix1(item, seed), + special3: this.getPrefix2(item, seed), + }; + } } - return name.trim(); - } - - private getSpecials( - item: Loot, - greatness: number, - seed: bigint - ): { special1: number; special2: number; special3: number } { - if (greatness < LootManager.SUFFIX_UNLOCK_GREATNESS) { - return { special1: 0, special2: 0, special3: 0 }; - } else if (greatness < LootManager.PREFIXES_UNLOCK_GREATNESS) { - return { - special1: this.getItemSuffix(item, seed), - special2: 0, - special3: 0, - }; - } else { - return { - special1: this.getItemSuffix(item, seed), - special2: this.getPrefix1(item, seed), - special3: this.getPrefix2(item, seed), - }; + getItemName(): string { + return Loot[this.item]; } - } - - getItemName(): string { - return Loot[this.item]; - } - - getItemNumber(itemName: string): Loot | undefined { - return Loot[itemName as keyof typeof Loot]; - } - - getItemType(): ItemType { - return ITEM_TYPES[this.item]; - } - - getItemSlot(): ItemSlot { - return ITEM_SLOTS[this.item]; - } - - getItemNamePrefix(prefix: ItemNamePrefix): string { - return ItemNamePrefix[prefix]; - } - - getItemNamePrefixNumber(prefixName: string): ItemNamePrefix | undefined { - return ItemNamePrefix[prefixName as keyof typeof ItemNamePrefix]; - } - - getItemNameSuffix(suffix: ItemNameSuffix): string { - return ItemNameSuffix[suffix]; - } - - getItemNameSuffixNumber(suffixName: string): ItemNameSuffix | undefined { - return ItemNameSuffix[suffixName as keyof typeof ItemNameSuffix]; - } - - getItemSuffix(itemId: number, seed: bigint): ItemSuffix { - const namingSeed = this.generateNamingSeed(itemId, seed); - const suffixIndex = Number( - (namingSeed % BigInt(Object.keys(ItemSuffix).length / 2)) + BigInt(1) - ); - return suffixIndex as ItemSuffix; - } - - private generateNamingSeed(itemId: number, seed: bigint): bigint { - const nameSeedU64 = seed % LootManager.TWO_POW_64; - - let itemEntropy: bigint; - const sum = nameSeedU64 + BigInt(itemId); - if (sum < LootManager.TWO_POW_64) { - itemEntropy = sum; - } else { - itemEntropy = nameSeedU64 - BigInt(itemId); + + getItemNumber(itemName: string): Loot | undefined { + return Loot[itemName as keyof typeof Loot]; + } + + getItemType(): ItemType { + return ITEM_TYPES[this.item]; } - const rnd = itemEntropy % BigInt(LootManager.NUM_ITEMS); - return ( - rnd * BigInt(this.getSlotLength(this.getSlot(itemId))) + BigInt(itemId) - ); - } - - getItemSuffixName(suffix: ItemSuffix): string { - return ItemSuffix[suffix].replace(/([A-Z])/g, " $1").trim(); - } - - private getSlot(itemId: number): ItemSlot { - return ITEM_SLOTS[itemId as Loot]; - } - - private getSlotLength(slot: ItemSlot): number { - return Object.values(ITEM_SLOTS).filter((s) => s === slot).length; - } - - getItemSuffixNumber(suffixName: string): ItemSuffix | undefined { - const formattedName = suffixName.replace(/\s+/g, ""); - return ItemSuffix[formattedName as keyof typeof ItemSuffix]; - } - - calculateGreatness(xp: number) { - return Math.max(Math.floor(Math.sqrt(xp)), 1); - } - - getItemSuffixBoost(suffix: ItemSuffix): StatBoost { - return ITEM_SUFFIX_BOOST[suffix]; - } - - getItemSuffixBoostString(suffix: ItemSuffix): string { - const boost = ITEM_SUFFIX_BOOST[suffix]; - return Object.entries(boost) - .map( - ([stat, value]) => `${StatType[stat as unknown as StatType]} +${value}` - ) - .join(" "); - } - - getItemSlotNumber(slot: ItemSlot): number { - return ITEM_SLOT_TO_NUMBER[slot]; - } - - getItemSlotFromNumber(number: number): ItemSlot | undefined { - return NUMBER_TO_ITEM_SLOT[number]; - } - - getPrefix1(itemId: number, seed: bigint): ItemNamePrefix { - const namingSeed = this.generateNamingSeed(itemId, seed); - const prefixIndex = Number( - (namingSeed % BigInt(Object.keys(ItemNamePrefix).length / 2)) + BigInt(1) - ); - return prefixIndex as ItemNamePrefix; - } - - getPrefix2(itemId: number, seed: bigint): ItemNameSuffix { - const namingSeed = this.generateNamingSeed(itemId, seed); - const suffixIndex = Number( - (namingSeed % BigInt(Object.keys(ItemNameSuffix).length / 2)) + BigInt(1) - ); - return suffixIndex as ItemNameSuffix; - } + getItemSlot(): ItemSlot { + return ITEM_SLOTS[this.item]; + } + + getItemNamePrefix(prefix: ItemNamePrefix): string { + return ItemNamePrefix[prefix]; + } + + getItemNamePrefixNumber(prefixName: string): ItemNamePrefix | undefined { + return ItemNamePrefix[prefixName as keyof typeof ItemNamePrefix]; + } + + getItemNameSuffix(suffix: ItemNameSuffix): string { + return ItemNameSuffix[suffix]; + } + + getItemNameSuffixNumber(suffixName: string): ItemNameSuffix | undefined { + return ItemNameSuffix[suffixName as keyof typeof ItemNameSuffix]; + } + + getItemSuffix(itemId: number, seed: bigint): ItemSuffix { + const namingSeed = this.generateNamingSeed(itemId, seed); + const suffixIndex = Number( + (namingSeed % BigInt(Object.keys(ItemSuffix).length / 2)) + + BigInt(1) + ); + return suffixIndex as ItemSuffix; + } + + private generateNamingSeed(itemId: number, seed: bigint): bigint { + const nameSeedU64 = seed % LootManager.TWO_POW_64; + + let itemEntropy: bigint; + const sum = nameSeedU64 + BigInt(itemId); + if (sum < LootManager.TWO_POW_64) { + itemEntropy = sum; + } else { + itemEntropy = nameSeedU64 - BigInt(itemId); + } + + const rnd = itemEntropy % BigInt(LootManager.NUM_ITEMS); + return ( + rnd * BigInt(this.getSlotLength(this.getSlot(itemId))) + + BigInt(itemId) + ); + } + + getItemSuffixName(suffix: ItemSuffix): string { + return ItemSuffix[suffix].replace(/([A-Z])/g, " $1").trim(); + } + + private getSlot(itemId: number): ItemSlot { + return ITEM_SLOTS[itemId as Loot]; + } + + private getSlotLength(slot: ItemSlot): number { + return Object.values(ITEM_SLOTS).filter((s) => s === slot).length; + } + + getItemSuffixNumber(suffixName: string): ItemSuffix | undefined { + const formattedName = suffixName.replace(/\s+/g, ""); + return ItemSuffix[formattedName as keyof typeof ItemSuffix]; + } + + calculateGreatness(xp: number) { + return Math.max(Math.floor(Math.sqrt(xp)), 1); + } + + getItemSuffixBoost(suffix: ItemSuffix): StatBoost { + return ITEM_SUFFIX_BOOST[suffix]; + } + + getItemSuffixBoostString(suffix: ItemSuffix): string { + const boost = ITEM_SUFFIX_BOOST[suffix]; + return Object.entries(boost) + .map( + ([stat, value]) => + `${StatType[stat as unknown as StatType]} +${value}` + ) + .join(" "); + } + + getItemSlotNumber(slot: ItemSlot): number { + return ITEM_SLOT_TO_NUMBER[slot]; + } + + getItemSlotFromNumber(number: number): ItemSlot | undefined { + return NUMBER_TO_ITEM_SLOT[number]; + } + + getPrefix1(itemId: number, seed: bigint): ItemNamePrefix { + const namingSeed = this.generateNamingSeed(itemId, seed); + const prefixIndex = Number( + (namingSeed % BigInt(Object.keys(ItemNamePrefix).length / 2)) + + BigInt(1) + ); + return prefixIndex as ItemNamePrefix; + } + + getPrefix2(itemId: number, seed: bigint): ItemNameSuffix { + const namingSeed = this.generateNamingSeed(itemId, seed); + const suffixIndex = Number( + (namingSeed % BigInt(Object.keys(ItemNameSuffix).length / 2)) + + BigInt(1) + ); + return suffixIndex as ItemNameSuffix; + } } export const ITEM_TYPES: Record = { - [Loot.Pendant]: ItemType.Necklace, - [Loot.Necklace]: ItemType.Necklace, - [Loot.Amulet]: ItemType.Necklace, - [Loot.SilverRing]: ItemType.Ring, - [Loot.BronzeRing]: ItemType.Ring, - [Loot.PlatinumRing]: ItemType.Ring, - [Loot.TitaniumRing]: ItemType.Ring, - [Loot.GoldRing]: ItemType.Ring, - [Loot.GhostWand]: ItemType.Magic, - [Loot.GraveWand]: ItemType.Magic, - [Loot.BoneWand]: ItemType.Magic, - [Loot.Wand]: ItemType.Magic, - [Loot.Grimoire]: ItemType.Magic, - [Loot.Chronicle]: ItemType.Magic, - [Loot.Tome]: ItemType.Magic, - [Loot.Book]: ItemType.Magic, - [Loot.DivineRobe]: ItemType.Cloth, - [Loot.SilkRobe]: ItemType.Cloth, - [Loot.LinenRobe]: ItemType.Cloth, - [Loot.Robe]: ItemType.Cloth, - [Loot.Shirt]: ItemType.Cloth, - [Loot.Crown]: ItemType.Cloth, - [Loot.DivineHood]: ItemType.Cloth, - [Loot.SilkHood]: ItemType.Cloth, - [Loot.LinenHood]: ItemType.Cloth, - [Loot.Hood]: ItemType.Cloth, - [Loot.BrightsilkSash]: ItemType.Cloth, - [Loot.SilkSash]: ItemType.Cloth, - [Loot.WoolSash]: ItemType.Cloth, - [Loot.LinenSash]: ItemType.Cloth, - [Loot.Sash]: ItemType.Cloth, - [Loot.DivineSlippers]: ItemType.Cloth, - [Loot.SilkSlippers]: ItemType.Cloth, - [Loot.WoolShoes]: ItemType.Cloth, - [Loot.LinenShoes]: ItemType.Cloth, - [Loot.Shoes]: ItemType.Cloth, - [Loot.DivineGloves]: ItemType.Cloth, - [Loot.SilkGloves]: ItemType.Cloth, - [Loot.WoolGloves]: ItemType.Cloth, - [Loot.LinenGloves]: ItemType.Cloth, - [Loot.Gloves]: ItemType.Cloth, - [Loot.Katana]: ItemType.Blade, - [Loot.Falchion]: ItemType.Blade, - [Loot.Scimitar]: ItemType.Blade, - [Loot.LongSword]: ItemType.Blade, - [Loot.ShortSword]: ItemType.Blade, - [Loot.DemonHusk]: ItemType.Hide, - [Loot.DragonskinArmor]: ItemType.Hide, - [Loot.StuddedLeatherArmor]: ItemType.Hide, - [Loot.HardLeatherArmor]: ItemType.Hide, - [Loot.LeatherArmor]: ItemType.Hide, - [Loot.DemonCrown]: ItemType.Hide, - [Loot.DragonsCrown]: ItemType.Hide, - [Loot.WarCap]: ItemType.Hide, - [Loot.LeatherCap]: ItemType.Hide, - [Loot.Cap]: ItemType.Hide, - [Loot.DemonhideBelt]: ItemType.Hide, - [Loot.DragonskinBelt]: ItemType.Hide, - [Loot.StuddedLeatherBelt]: ItemType.Hide, - [Loot.HardLeatherBelt]: ItemType.Hide, - [Loot.LeatherBelt]: ItemType.Hide, - [Loot.DemonhideBoots]: ItemType.Hide, - [Loot.DragonskinBoots]: ItemType.Hide, - [Loot.StuddedLeatherBoots]: ItemType.Hide, - [Loot.HardLeatherBoots]: ItemType.Hide, - [Loot.LeatherBoots]: ItemType.Hide, - [Loot.DemonsHands]: ItemType.Hide, - [Loot.DragonskinGloves]: ItemType.Hide, - [Loot.StuddedLeatherGloves]: ItemType.Hide, - [Loot.HardLeatherGloves]: ItemType.Hide, - [Loot.LeatherGloves]: ItemType.Hide, - [Loot.Warhammer]: ItemType.Bludgeon, - [Loot.Quarterstaff]: ItemType.Bludgeon, - [Loot.Maul]: ItemType.Bludgeon, - [Loot.Mace]: ItemType.Bludgeon, - [Loot.Club]: ItemType.Bludgeon, - [Loot.HolyChestplate]: ItemType.Metal, - [Loot.OrnateChestplate]: ItemType.Metal, - [Loot.PlateMail]: ItemType.Metal, - [Loot.ChainMail]: ItemType.Metal, - [Loot.RingMail]: ItemType.Metal, - [Loot.AncientHelm]: ItemType.Metal, - [Loot.OrnateHelm]: ItemType.Metal, - [Loot.GreatHelm]: ItemType.Metal, - [Loot.FullHelm]: ItemType.Metal, - [Loot.Helm]: ItemType.Metal, - [Loot.OrnateBelt]: ItemType.Metal, - [Loot.WarBelt]: ItemType.Metal, - [Loot.PlatedBelt]: ItemType.Metal, - [Loot.MeshBelt]: ItemType.Metal, - [Loot.HeavyBelt]: ItemType.Metal, - [Loot.HolyGreaves]: ItemType.Metal, - [Loot.OrnateGreaves]: ItemType.Metal, - [Loot.Greaves]: ItemType.Metal, - [Loot.ChainBoots]: ItemType.Metal, - [Loot.HeavyBoots]: ItemType.Metal, - [Loot.HolyGauntlets]: ItemType.Metal, - [Loot.OrnateGauntlets]: ItemType.Metal, - [Loot.Gauntlets]: ItemType.Metal, - [Loot.ChainGloves]: ItemType.Metal, - [Loot.HeavyGloves]: ItemType.Metal, + [Loot.Pendant]: ItemType.Necklace, + [Loot.Necklace]: ItemType.Necklace, + [Loot.Amulet]: ItemType.Necklace, + [Loot.SilverRing]: ItemType.Ring, + [Loot.BronzeRing]: ItemType.Ring, + [Loot.PlatinumRing]: ItemType.Ring, + [Loot.TitaniumRing]: ItemType.Ring, + [Loot.GoldRing]: ItemType.Ring, + [Loot.GhostWand]: ItemType.Magic, + [Loot.GraveWand]: ItemType.Magic, + [Loot.BoneWand]: ItemType.Magic, + [Loot.Wand]: ItemType.Magic, + [Loot.Grimoire]: ItemType.Magic, + [Loot.Chronicle]: ItemType.Magic, + [Loot.Tome]: ItemType.Magic, + [Loot.Book]: ItemType.Magic, + [Loot.DivineRobe]: ItemType.Cloth, + [Loot.SilkRobe]: ItemType.Cloth, + [Loot.LinenRobe]: ItemType.Cloth, + [Loot.Robe]: ItemType.Cloth, + [Loot.Shirt]: ItemType.Cloth, + [Loot.Crown]: ItemType.Cloth, + [Loot.DivineHood]: ItemType.Cloth, + [Loot.SilkHood]: ItemType.Cloth, + [Loot.LinenHood]: ItemType.Cloth, + [Loot.Hood]: ItemType.Cloth, + [Loot.BrightsilkSash]: ItemType.Cloth, + [Loot.SilkSash]: ItemType.Cloth, + [Loot.WoolSash]: ItemType.Cloth, + [Loot.LinenSash]: ItemType.Cloth, + [Loot.Sash]: ItemType.Cloth, + [Loot.DivineSlippers]: ItemType.Cloth, + [Loot.SilkSlippers]: ItemType.Cloth, + [Loot.WoolShoes]: ItemType.Cloth, + [Loot.LinenShoes]: ItemType.Cloth, + [Loot.Shoes]: ItemType.Cloth, + [Loot.DivineGloves]: ItemType.Cloth, + [Loot.SilkGloves]: ItemType.Cloth, + [Loot.WoolGloves]: ItemType.Cloth, + [Loot.LinenGloves]: ItemType.Cloth, + [Loot.Gloves]: ItemType.Cloth, + [Loot.Katana]: ItemType.Blade, + [Loot.Falchion]: ItemType.Blade, + [Loot.Scimitar]: ItemType.Blade, + [Loot.LongSword]: ItemType.Blade, + [Loot.ShortSword]: ItemType.Blade, + [Loot.DemonHusk]: ItemType.Hide, + [Loot.DragonskinArmor]: ItemType.Hide, + [Loot.StuddedLeatherArmor]: ItemType.Hide, + [Loot.HardLeatherArmor]: ItemType.Hide, + [Loot.LeatherArmor]: ItemType.Hide, + [Loot.DemonCrown]: ItemType.Hide, + [Loot.DragonsCrown]: ItemType.Hide, + [Loot.WarCap]: ItemType.Hide, + [Loot.LeatherCap]: ItemType.Hide, + [Loot.Cap]: ItemType.Hide, + [Loot.DemonhideBelt]: ItemType.Hide, + [Loot.DragonskinBelt]: ItemType.Hide, + [Loot.StuddedLeatherBelt]: ItemType.Hide, + [Loot.HardLeatherBelt]: ItemType.Hide, + [Loot.LeatherBelt]: ItemType.Hide, + [Loot.DemonhideBoots]: ItemType.Hide, + [Loot.DragonskinBoots]: ItemType.Hide, + [Loot.StuddedLeatherBoots]: ItemType.Hide, + [Loot.HardLeatherBoots]: ItemType.Hide, + [Loot.LeatherBoots]: ItemType.Hide, + [Loot.DemonsHands]: ItemType.Hide, + [Loot.DragonskinGloves]: ItemType.Hide, + [Loot.StuddedLeatherGloves]: ItemType.Hide, + [Loot.HardLeatherGloves]: ItemType.Hide, + [Loot.LeatherGloves]: ItemType.Hide, + [Loot.Warhammer]: ItemType.Bludgeon, + [Loot.Quarterstaff]: ItemType.Bludgeon, + [Loot.Maul]: ItemType.Bludgeon, + [Loot.Mace]: ItemType.Bludgeon, + [Loot.Club]: ItemType.Bludgeon, + [Loot.HolyChestplate]: ItemType.Metal, + [Loot.OrnateChestplate]: ItemType.Metal, + [Loot.PlateMail]: ItemType.Metal, + [Loot.ChainMail]: ItemType.Metal, + [Loot.RingMail]: ItemType.Metal, + [Loot.AncientHelm]: ItemType.Metal, + [Loot.OrnateHelm]: ItemType.Metal, + [Loot.GreatHelm]: ItemType.Metal, + [Loot.FullHelm]: ItemType.Metal, + [Loot.Helm]: ItemType.Metal, + [Loot.OrnateBelt]: ItemType.Metal, + [Loot.WarBelt]: ItemType.Metal, + [Loot.PlatedBelt]: ItemType.Metal, + [Loot.MeshBelt]: ItemType.Metal, + [Loot.HeavyBelt]: ItemType.Metal, + [Loot.HolyGreaves]: ItemType.Metal, + [Loot.OrnateGreaves]: ItemType.Metal, + [Loot.Greaves]: ItemType.Metal, + [Loot.ChainBoots]: ItemType.Metal, + [Loot.HeavyBoots]: ItemType.Metal, + [Loot.HolyGauntlets]: ItemType.Metal, + [Loot.OrnateGauntlets]: ItemType.Metal, + [Loot.Gauntlets]: ItemType.Metal, + [Loot.ChainGloves]: ItemType.Metal, + [Loot.HeavyGloves]: ItemType.Metal, }; export const ITEM_TIERS: Record = { - [Loot.Pendant]: 1, - [Loot.Necklace]: 1, - [Loot.Amulet]: 1, - [Loot.SilverRing]: 2, - [Loot.BronzeRing]: 3, - [Loot.PlatinumRing]: 1, - [Loot.TitaniumRing]: 1, - [Loot.GoldRing]: 1, - [Loot.GhostWand]: 1, - [Loot.GraveWand]: 2, - [Loot.BoneWand]: 3, - [Loot.Wand]: 5, - [Loot.Grimoire]: 1, - [Loot.Chronicle]: 2, - [Loot.Tome]: 3, - [Loot.Book]: 5, - [Loot.DivineRobe]: 1, - [Loot.SilkRobe]: 2, - [Loot.LinenRobe]: 3, - [Loot.Robe]: 4, - [Loot.Shirt]: 5, - [Loot.Crown]: 1, - [Loot.DivineHood]: 2, - [Loot.SilkHood]: 3, - [Loot.LinenHood]: 4, - [Loot.Hood]: 5, - [Loot.BrightsilkSash]: 1, - [Loot.SilkSash]: 2, - [Loot.WoolSash]: 3, - [Loot.LinenSash]: 4, - [Loot.Sash]: 5, - [Loot.DivineSlippers]: 1, - [Loot.SilkSlippers]: 2, - [Loot.WoolShoes]: 3, - [Loot.LinenShoes]: 4, - [Loot.Shoes]: 5, - [Loot.DivineGloves]: 1, - [Loot.SilkGloves]: 2, - [Loot.WoolGloves]: 3, - [Loot.LinenGloves]: 4, - [Loot.Gloves]: 5, - [Loot.Katana]: 1, - [Loot.Falchion]: 2, - [Loot.Scimitar]: 3, - [Loot.LongSword]: 4, - [Loot.ShortSword]: 5, - [Loot.DemonHusk]: 1, - [Loot.DragonskinArmor]: 2, - [Loot.StuddedLeatherArmor]: 3, - [Loot.HardLeatherArmor]: 4, - [Loot.LeatherArmor]: 5, - [Loot.DemonCrown]: 1, - [Loot.DragonsCrown]: 2, - [Loot.WarCap]: 3, - [Loot.LeatherCap]: 4, - [Loot.Cap]: 5, - [Loot.DemonhideBelt]: 1, - [Loot.DragonskinBelt]: 2, - [Loot.StuddedLeatherBelt]: 3, - [Loot.HardLeatherBelt]: 4, - [Loot.LeatherBelt]: 5, - [Loot.DemonhideBoots]: 1, - [Loot.DragonskinBoots]: 2, - [Loot.StuddedLeatherBoots]: 3, - [Loot.HardLeatherBoots]: 4, - [Loot.LeatherBoots]: 5, - [Loot.DemonsHands]: 1, - [Loot.DragonskinGloves]: 2, - [Loot.StuddedLeatherGloves]: 3, - [Loot.HardLeatherGloves]: 4, - [Loot.LeatherGloves]: 5, - [Loot.Warhammer]: 1, - [Loot.Quarterstaff]: 2, - [Loot.Maul]: 3, - [Loot.Mace]: 4, - [Loot.Club]: 5, - [Loot.HolyChestplate]: 1, - [Loot.OrnateChestplate]: 2, - [Loot.PlateMail]: 3, - [Loot.ChainMail]: 4, - [Loot.RingMail]: 5, - [Loot.AncientHelm]: 1, - [Loot.OrnateHelm]: 2, - [Loot.GreatHelm]: 3, - [Loot.FullHelm]: 4, - [Loot.Helm]: 5, - [Loot.OrnateBelt]: 1, - [Loot.WarBelt]: 2, - [Loot.PlatedBelt]: 3, - [Loot.MeshBelt]: 4, - [Loot.HeavyBelt]: 5, - [Loot.HolyGreaves]: 1, - [Loot.OrnateGreaves]: 2, - [Loot.Greaves]: 3, - [Loot.ChainBoots]: 4, - [Loot.HeavyBoots]: 5, - [Loot.HolyGauntlets]: 1, - [Loot.OrnateGauntlets]: 2, - [Loot.Gauntlets]: 3, - [Loot.ChainGloves]: 4, - [Loot.HeavyGloves]: 5, + [Loot.Pendant]: 1, + [Loot.Necklace]: 1, + [Loot.Amulet]: 1, + [Loot.SilverRing]: 2, + [Loot.BronzeRing]: 3, + [Loot.PlatinumRing]: 1, + [Loot.TitaniumRing]: 1, + [Loot.GoldRing]: 1, + [Loot.GhostWand]: 1, + [Loot.GraveWand]: 2, + [Loot.BoneWand]: 3, + [Loot.Wand]: 5, + [Loot.Grimoire]: 1, + [Loot.Chronicle]: 2, + [Loot.Tome]: 3, + [Loot.Book]: 5, + [Loot.DivineRobe]: 1, + [Loot.SilkRobe]: 2, + [Loot.LinenRobe]: 3, + [Loot.Robe]: 4, + [Loot.Shirt]: 5, + [Loot.Crown]: 1, + [Loot.DivineHood]: 2, + [Loot.SilkHood]: 3, + [Loot.LinenHood]: 4, + [Loot.Hood]: 5, + [Loot.BrightsilkSash]: 1, + [Loot.SilkSash]: 2, + [Loot.WoolSash]: 3, + [Loot.LinenSash]: 4, + [Loot.Sash]: 5, + [Loot.DivineSlippers]: 1, + [Loot.SilkSlippers]: 2, + [Loot.WoolShoes]: 3, + [Loot.LinenShoes]: 4, + [Loot.Shoes]: 5, + [Loot.DivineGloves]: 1, + [Loot.SilkGloves]: 2, + [Loot.WoolGloves]: 3, + [Loot.LinenGloves]: 4, + [Loot.Gloves]: 5, + [Loot.Katana]: 1, + [Loot.Falchion]: 2, + [Loot.Scimitar]: 3, + [Loot.LongSword]: 4, + [Loot.ShortSword]: 5, + [Loot.DemonHusk]: 1, + [Loot.DragonskinArmor]: 2, + [Loot.StuddedLeatherArmor]: 3, + [Loot.HardLeatherArmor]: 4, + [Loot.LeatherArmor]: 5, + [Loot.DemonCrown]: 1, + [Loot.DragonsCrown]: 2, + [Loot.WarCap]: 3, + [Loot.LeatherCap]: 4, + [Loot.Cap]: 5, + [Loot.DemonhideBelt]: 1, + [Loot.DragonskinBelt]: 2, + [Loot.StuddedLeatherBelt]: 3, + [Loot.HardLeatherBelt]: 4, + [Loot.LeatherBelt]: 5, + [Loot.DemonhideBoots]: 1, + [Loot.DragonskinBoots]: 2, + [Loot.StuddedLeatherBoots]: 3, + [Loot.HardLeatherBoots]: 4, + [Loot.LeatherBoots]: 5, + [Loot.DemonsHands]: 1, + [Loot.DragonskinGloves]: 2, + [Loot.StuddedLeatherGloves]: 3, + [Loot.HardLeatherGloves]: 4, + [Loot.LeatherGloves]: 5, + [Loot.Warhammer]: 1, + [Loot.Quarterstaff]: 2, + [Loot.Maul]: 3, + [Loot.Mace]: 4, + [Loot.Club]: 5, + [Loot.HolyChestplate]: 1, + [Loot.OrnateChestplate]: 2, + [Loot.PlateMail]: 3, + [Loot.ChainMail]: 4, + [Loot.RingMail]: 5, + [Loot.AncientHelm]: 1, + [Loot.OrnateHelm]: 2, + [Loot.GreatHelm]: 3, + [Loot.FullHelm]: 4, + [Loot.Helm]: 5, + [Loot.OrnateBelt]: 1, + [Loot.WarBelt]: 2, + [Loot.PlatedBelt]: 3, + [Loot.MeshBelt]: 4, + [Loot.HeavyBelt]: 5, + [Loot.HolyGreaves]: 1, + [Loot.OrnateGreaves]: 2, + [Loot.Greaves]: 3, + [Loot.ChainBoots]: 4, + [Loot.HeavyBoots]: 5, + [Loot.HolyGauntlets]: 1, + [Loot.OrnateGauntlets]: 2, + [Loot.Gauntlets]: 3, + [Loot.ChainGloves]: 4, + [Loot.HeavyGloves]: 5, }; export const ITEM_SLOTS: Record = { - [Loot.Pendant]: ItemSlot.Neck, - [Loot.Necklace]: ItemSlot.Neck, - [Loot.Amulet]: ItemSlot.Neck, - [Loot.SilverRing]: ItemSlot.Ring, - [Loot.BronzeRing]: ItemSlot.Ring, - [Loot.PlatinumRing]: ItemSlot.Ring, - [Loot.TitaniumRing]: ItemSlot.Ring, - [Loot.GoldRing]: ItemSlot.Ring, - [Loot.GhostWand]: ItemSlot.Weapon, - [Loot.GraveWand]: ItemSlot.Weapon, - [Loot.BoneWand]: ItemSlot.Weapon, - [Loot.Wand]: ItemSlot.Weapon, - [Loot.Grimoire]: ItemSlot.Weapon, - [Loot.Chronicle]: ItemSlot.Weapon, - [Loot.Tome]: ItemSlot.Weapon, - [Loot.Book]: ItemSlot.Weapon, - [Loot.DivineRobe]: ItemSlot.Chest, - [Loot.SilkRobe]: ItemSlot.Chest, - [Loot.LinenRobe]: ItemSlot.Chest, - [Loot.Robe]: ItemSlot.Chest, - [Loot.Shirt]: ItemSlot.Chest, - [Loot.Crown]: ItemSlot.Head, - [Loot.DivineHood]: ItemSlot.Head, - [Loot.SilkHood]: ItemSlot.Head, - [Loot.LinenHood]: ItemSlot.Head, - [Loot.Hood]: ItemSlot.Head, - [Loot.BrightsilkSash]: ItemSlot.Waist, - [Loot.SilkSash]: ItemSlot.Waist, - [Loot.WoolSash]: ItemSlot.Waist, - [Loot.LinenSash]: ItemSlot.Waist, - [Loot.Sash]: ItemSlot.Waist, - [Loot.DivineSlippers]: ItemSlot.Foot, - [Loot.SilkSlippers]: ItemSlot.Foot, - [Loot.WoolShoes]: ItemSlot.Foot, - [Loot.LinenShoes]: ItemSlot.Foot, - [Loot.Shoes]: ItemSlot.Foot, - [Loot.DivineGloves]: ItemSlot.Hand, - [Loot.SilkGloves]: ItemSlot.Hand, - [Loot.WoolGloves]: ItemSlot.Hand, - [Loot.LinenGloves]: ItemSlot.Hand, - [Loot.Gloves]: ItemSlot.Hand, - [Loot.Katana]: ItemSlot.Weapon, - [Loot.Falchion]: ItemSlot.Weapon, - [Loot.Scimitar]: ItemSlot.Weapon, - [Loot.LongSword]: ItemSlot.Weapon, - [Loot.ShortSword]: ItemSlot.Weapon, - [Loot.DemonHusk]: ItemSlot.Chest, - [Loot.DragonskinArmor]: ItemSlot.Chest, - [Loot.StuddedLeatherArmor]: ItemSlot.Chest, - [Loot.HardLeatherArmor]: ItemSlot.Chest, - [Loot.LeatherArmor]: ItemSlot.Chest, - [Loot.DemonCrown]: ItemSlot.Head, - [Loot.DragonsCrown]: ItemSlot.Head, - [Loot.WarCap]: ItemSlot.Head, - [Loot.LeatherCap]: ItemSlot.Head, - [Loot.Cap]: ItemSlot.Head, - [Loot.DemonhideBelt]: ItemSlot.Waist, - [Loot.DragonskinBelt]: ItemSlot.Waist, - [Loot.StuddedLeatherBelt]: ItemSlot.Waist, - [Loot.HardLeatherBelt]: ItemSlot.Waist, - [Loot.LeatherBelt]: ItemSlot.Waist, - [Loot.DemonhideBoots]: ItemSlot.Foot, - [Loot.DragonskinBoots]: ItemSlot.Foot, - [Loot.StuddedLeatherBoots]: ItemSlot.Foot, - [Loot.HardLeatherBoots]: ItemSlot.Foot, - [Loot.LeatherBoots]: ItemSlot.Foot, - [Loot.DemonsHands]: ItemSlot.Hand, - [Loot.DragonskinGloves]: ItemSlot.Hand, - [Loot.StuddedLeatherGloves]: ItemSlot.Hand, - [Loot.HardLeatherGloves]: ItemSlot.Hand, - [Loot.LeatherGloves]: ItemSlot.Hand, - [Loot.Warhammer]: ItemSlot.Weapon, - [Loot.Quarterstaff]: ItemSlot.Weapon, - [Loot.Maul]: ItemSlot.Weapon, - [Loot.Mace]: ItemSlot.Weapon, - [Loot.Club]: ItemSlot.Weapon, - [Loot.HolyChestplate]: ItemSlot.Chest, - [Loot.OrnateChestplate]: ItemSlot.Chest, - [Loot.PlateMail]: ItemSlot.Chest, - [Loot.ChainMail]: ItemSlot.Chest, - [Loot.RingMail]: ItemSlot.Chest, - [Loot.AncientHelm]: ItemSlot.Head, - [Loot.OrnateHelm]: ItemSlot.Head, - [Loot.GreatHelm]: ItemSlot.Head, - [Loot.FullHelm]: ItemSlot.Head, - [Loot.Helm]: ItemSlot.Head, - [Loot.OrnateBelt]: ItemSlot.Waist, - [Loot.WarBelt]: ItemSlot.Waist, - [Loot.PlatedBelt]: ItemSlot.Waist, - [Loot.MeshBelt]: ItemSlot.Waist, - [Loot.HeavyBelt]: ItemSlot.Waist, - [Loot.HolyGreaves]: ItemSlot.Foot, - [Loot.OrnateGreaves]: ItemSlot.Foot, - [Loot.Greaves]: ItemSlot.Foot, - [Loot.ChainBoots]: ItemSlot.Foot, - [Loot.HeavyBoots]: ItemSlot.Foot, - [Loot.HolyGauntlets]: ItemSlot.Hand, - [Loot.OrnateGauntlets]: ItemSlot.Hand, - [Loot.Gauntlets]: ItemSlot.Hand, - [Loot.ChainGloves]: ItemSlot.Hand, - [Loot.HeavyGloves]: ItemSlot.Hand, + [Loot.Pendant]: ItemSlot.Neck, + [Loot.Necklace]: ItemSlot.Neck, + [Loot.Amulet]: ItemSlot.Neck, + [Loot.SilverRing]: ItemSlot.Ring, + [Loot.BronzeRing]: ItemSlot.Ring, + [Loot.PlatinumRing]: ItemSlot.Ring, + [Loot.TitaniumRing]: ItemSlot.Ring, + [Loot.GoldRing]: ItemSlot.Ring, + [Loot.GhostWand]: ItemSlot.Weapon, + [Loot.GraveWand]: ItemSlot.Weapon, + [Loot.BoneWand]: ItemSlot.Weapon, + [Loot.Wand]: ItemSlot.Weapon, + [Loot.Grimoire]: ItemSlot.Weapon, + [Loot.Chronicle]: ItemSlot.Weapon, + [Loot.Tome]: ItemSlot.Weapon, + [Loot.Book]: ItemSlot.Weapon, + [Loot.DivineRobe]: ItemSlot.Chest, + [Loot.SilkRobe]: ItemSlot.Chest, + [Loot.LinenRobe]: ItemSlot.Chest, + [Loot.Robe]: ItemSlot.Chest, + [Loot.Shirt]: ItemSlot.Chest, + [Loot.Crown]: ItemSlot.Head, + [Loot.DivineHood]: ItemSlot.Head, + [Loot.SilkHood]: ItemSlot.Head, + [Loot.LinenHood]: ItemSlot.Head, + [Loot.Hood]: ItemSlot.Head, + [Loot.BrightsilkSash]: ItemSlot.Waist, + [Loot.SilkSash]: ItemSlot.Waist, + [Loot.WoolSash]: ItemSlot.Waist, + [Loot.LinenSash]: ItemSlot.Waist, + [Loot.Sash]: ItemSlot.Waist, + [Loot.DivineSlippers]: ItemSlot.Foot, + [Loot.SilkSlippers]: ItemSlot.Foot, + [Loot.WoolShoes]: ItemSlot.Foot, + [Loot.LinenShoes]: ItemSlot.Foot, + [Loot.Shoes]: ItemSlot.Foot, + [Loot.DivineGloves]: ItemSlot.Hand, + [Loot.SilkGloves]: ItemSlot.Hand, + [Loot.WoolGloves]: ItemSlot.Hand, + [Loot.LinenGloves]: ItemSlot.Hand, + [Loot.Gloves]: ItemSlot.Hand, + [Loot.Katana]: ItemSlot.Weapon, + [Loot.Falchion]: ItemSlot.Weapon, + [Loot.Scimitar]: ItemSlot.Weapon, + [Loot.LongSword]: ItemSlot.Weapon, + [Loot.ShortSword]: ItemSlot.Weapon, + [Loot.DemonHusk]: ItemSlot.Chest, + [Loot.DragonskinArmor]: ItemSlot.Chest, + [Loot.StuddedLeatherArmor]: ItemSlot.Chest, + [Loot.HardLeatherArmor]: ItemSlot.Chest, + [Loot.LeatherArmor]: ItemSlot.Chest, + [Loot.DemonCrown]: ItemSlot.Head, + [Loot.DragonsCrown]: ItemSlot.Head, + [Loot.WarCap]: ItemSlot.Head, + [Loot.LeatherCap]: ItemSlot.Head, + [Loot.Cap]: ItemSlot.Head, + [Loot.DemonhideBelt]: ItemSlot.Waist, + [Loot.DragonskinBelt]: ItemSlot.Waist, + [Loot.StuddedLeatherBelt]: ItemSlot.Waist, + [Loot.HardLeatherBelt]: ItemSlot.Waist, + [Loot.LeatherBelt]: ItemSlot.Waist, + [Loot.DemonhideBoots]: ItemSlot.Foot, + [Loot.DragonskinBoots]: ItemSlot.Foot, + [Loot.StuddedLeatherBoots]: ItemSlot.Foot, + [Loot.HardLeatherBoots]: ItemSlot.Foot, + [Loot.LeatherBoots]: ItemSlot.Foot, + [Loot.DemonsHands]: ItemSlot.Hand, + [Loot.DragonskinGloves]: ItemSlot.Hand, + [Loot.StuddedLeatherGloves]: ItemSlot.Hand, + [Loot.HardLeatherGloves]: ItemSlot.Hand, + [Loot.LeatherGloves]: ItemSlot.Hand, + [Loot.Warhammer]: ItemSlot.Weapon, + [Loot.Quarterstaff]: ItemSlot.Weapon, + [Loot.Maul]: ItemSlot.Weapon, + [Loot.Mace]: ItemSlot.Weapon, + [Loot.Club]: ItemSlot.Weapon, + [Loot.HolyChestplate]: ItemSlot.Chest, + [Loot.OrnateChestplate]: ItemSlot.Chest, + [Loot.PlateMail]: ItemSlot.Chest, + [Loot.ChainMail]: ItemSlot.Chest, + [Loot.RingMail]: ItemSlot.Chest, + [Loot.AncientHelm]: ItemSlot.Head, + [Loot.OrnateHelm]: ItemSlot.Head, + [Loot.GreatHelm]: ItemSlot.Head, + [Loot.FullHelm]: ItemSlot.Head, + [Loot.Helm]: ItemSlot.Head, + [Loot.OrnateBelt]: ItemSlot.Waist, + [Loot.WarBelt]: ItemSlot.Waist, + [Loot.PlatedBelt]: ItemSlot.Waist, + [Loot.MeshBelt]: ItemSlot.Waist, + [Loot.HeavyBelt]: ItemSlot.Waist, + [Loot.HolyGreaves]: ItemSlot.Foot, + [Loot.OrnateGreaves]: ItemSlot.Foot, + [Loot.Greaves]: ItemSlot.Foot, + [Loot.ChainBoots]: ItemSlot.Foot, + [Loot.HeavyBoots]: ItemSlot.Foot, + [Loot.HolyGauntlets]: ItemSlot.Hand, + [Loot.OrnateGauntlets]: ItemSlot.Hand, + [Loot.Gauntlets]: ItemSlot.Hand, + [Loot.ChainGloves]: ItemSlot.Hand, + [Loot.HeavyGloves]: ItemSlot.Hand, }; diff --git a/packages/core/src/objects/obstacles.ts b/packages/core/src/objects/obstacles.ts index 9021953..b612a9f 100644 --- a/packages/core/src/objects/obstacles.ts +++ b/packages/core/src/objects/obstacles.ts @@ -1,14 +1,14 @@ import { Obstacles } from "../type"; export class ObstacleManager { - constructor() {} + constructor() {} - getObstacleName(obstacle: Obstacles): string { - return Obstacles[obstacle].replace(/([A-Z])/g, " $1").trim(); - } + getObstacleName(obstacle: Obstacles): string { + return Obstacles[obstacle].replace(/([A-Z])/g, " $1").trim(); + } - getObstacleNumber(obstacleName: string): Obstacles | undefined { - const formattedName = obstacleName.replace(/\s+/g, ""); - return Obstacles[formattedName as keyof typeof Obstacles]; - } + getObstacleNumber(obstacleName: string): Obstacles | undefined { + const formattedName = obstacleName.replace(/\s+/g, ""); + return Obstacles[formattedName as keyof typeof Obstacles]; + } } diff --git a/packages/core/src/objects/prediction.ts b/packages/core/src/objects/prediction.ts index 7b589d5..db1a227 100644 --- a/packages/core/src/objects/prediction.ts +++ b/packages/core/src/objects/prediction.ts @@ -5,214 +5,217 @@ import { Survivor } from "./survivor"; // import { LootManager } from "./loot"; export class PredictionManager { - private beasts: BeastManager = new BeastManager(); - private survivor: Survivor = new Survivor(); - // private lootManager: LootManager = new LootManager(); - - constructor() {} - - getRandomness( - xp: number, - adventurerEntropy: bigint - ): { rnd1: bigint; rnd2: bigint } { - const params = [BigInt(xp), adventurerEntropy]; - const poseidon = hash.computePoseidonHashOnElements(params); - - return { - rnd1: BigInt(poseidon) % UINT_128_MAX, - rnd2: BigInt(poseidon) / UINT_128_MAX, - }; - } - - beastEncounters(xpList: number[], adventurerEntropy: bigint): any[] { - return xpList.map((xp) => { - const level = BigInt(Math.floor(Math.sqrt(xp))); - const { rnd2 } = this.getRandomness(xp, adventurerEntropy); - - return { - ...this.beastEncounter(adventurerEntropy, level, xp, rnd2), - adventurerLevel: Math.floor(Math.sqrt(xp)), - xp: xp, - }; - }); - } - - getBeastHealth(level: bigint, seed: bigint): bigint { - const baseHealth = BigInt(1) + (seed % (level * BigInt(20))); - const levelBonus = this.getLevelBonus(level); - const totalHealth = baseHealth + levelBonus; - - return totalHealth > 511 ? BigInt(511) : totalHealth; - } - - private getLevelBonus(level: bigint): bigint { - if (level >= 50) return BigInt(500); - if (level >= 40) return BigInt(400); - if (level >= 30) return BigInt(200); - if (level >= 20) return BigInt(100); - return BigInt(10); - } - - getObstacleLevel(level: bigint, entropy: bigint): bigint { - let obstacleLevel = BigInt(1) + (entropy % (level * BigInt(3))); - - if (level >= 50) { - obstacleLevel += BigInt(80); - } else if (level >= 40) { - obstacleLevel += BigInt(40); - } else if (level >= 30) { - obstacleLevel += BigInt(20); - } else if (level >= 20) { - obstacleLevel += BigInt(10); + private beasts: BeastManager = new BeastManager(); + private survivor: Survivor = new Survivor(); + // private lootManager: LootManager = new LootManager(); + + constructor() {} + + getRandomness( + xp: number, + adventurerEntropy: bigint + ): { rnd1: bigint; rnd2: bigint } { + const params = [BigInt(xp), adventurerEntropy]; + const poseidon = hash.computePoseidonHashOnElements(params); + + return { + rnd1: BigInt(poseidon) % UINT_128_MAX, + rnd2: BigInt(poseidon) / UINT_128_MAX, + }; } - return obstacleLevel; - } + beastEncounters(xpList: number[], adventurerEntropy: bigint): any[] { + return xpList.map((xp) => { + const level = BigInt(Math.floor(Math.sqrt(xp))); + const { rnd2 } = this.getRandomness(xp, adventurerEntropy); + + return { + ...this.beastEncounter(adventurerEntropy, level, xp, rnd2), + adventurerLevel: Math.floor(Math.sqrt(xp)), + xp: xp, + }; + }); + } - getAttackLocation(entropy: bigint): string { - const locations = ["Chest", "Head", "Waist", "Foot", "Hand"]; - const index = Number(entropy % BigInt(locations.length)); - return locations[index]; - } + getBeastHealth(level: bigint, seed: bigint): bigint { + const baseHealth = BigInt(1) + (seed % (level * BigInt(20))); + const levelBonus = this.getLevelBonus(level); + const totalHealth = baseHealth + levelBonus; - getXpReward(level: bigint, tier: bigint): bigint { - let xp = ((BigInt(6) - tier) * level) / BigInt(2); + return totalHealth > 511 ? BigInt(511) : totalHealth; + } - if (xp < 4) { - return BigInt(4); + private getLevelBonus(level: bigint): bigint { + if (level >= 50) return BigInt(500); + if (level >= 40) return BigInt(400); + if (level >= 30) return BigInt(200); + if (level >= 20) return BigInt(100); + return BigInt(10); } - return xp; - } + getObstacleLevel(level: bigint, entropy: bigint): bigint { + let obstacleLevel = BigInt(1) + (entropy % (level * BigInt(3))); - abilityBasedAvoidThreat(level: bigint, entropy: bigint): bigint { - return entropy % level; - } + if (level >= 50) { + obstacleLevel += BigInt(80); + } else if (level >= 40) { + obstacleLevel += BigInt(40); + } else if (level >= 30) { + obstacleLevel += BigInt(20); + } else if (level >= 20) { + obstacleLevel += BigInt(10); + } - criticalMultiplier(luck: number, entropy: bigint): number { - if (luck > Number(entropy % BigInt(100))) { - return Number(entropy % BigInt(5)) + 1; + return obstacleLevel; } - return 0; - } - - criticalHitBonus( - base_damage: number, - luck: number, - ring: Item | undefined, - entropy: bigint - ): number { - let total = 0; - - if (luck > Number(entropy % BigInt(100))) { - let damage_boost_base = Math.floor(base_damage / 5); - let damage_multiplier = Number(entropy % BigInt(5)) + 1; - total = damage_boost_base * damage_multiplier; - - if (ring?.item === "Titanium Ring" && total > 0) { - total += Math.floor( - (total * 3 * Math.floor(Math.sqrt(ring.xp!))) / 100 - ); - } + getAttackLocation(entropy: bigint): string { + const locations = ["Chest", "Head", "Waist", "Foot", "Hand"]; + const index = Number(entropy % BigInt(locations.length)); + return locations[index]; + } + + getXpReward(level: bigint, tier: bigint): bigint { + let xp = ((BigInt(6) - tier) * level) / BigInt(2); + + if (xp < 4) { + return BigInt(4); + } + + return xp; + } + + abilityBasedAvoidThreat(level: bigint, entropy: bigint): bigint { + return entropy % level; + } + + criticalMultiplier(luck: number, entropy: bigint): number { + if (luck > Number(entropy % BigInt(100))) { + return Number(entropy % BigInt(5)) + 1; + } + + return 0; } - return total; - } - - calculateEncounterDamage( - type: string | undefined, - tier: number, - level: number, - adventurerArmor: Item | undefined, - entropy: bigint, - minimumDmg: number - ) { - if (!type) return minimumDmg; - - let base_attack = level * (6 - tier!); - - let base_armor = 0; - let elemental_damage = 0; - - if (adventurerArmor) { - base_armor = - this.survivor.calculateLevel() * (6 - adventurerArmor?.tier!); - elemental_damage = this.beasts.elementalAdjustedDamage( - base_attack, - type, - adventurerArmor?.type! - ); - } else { - elemental_damage = base_attack * 1.5; + criticalHitBonus( + base_damage: number, + luck: number, + ring: Item | undefined, + entropy: bigint + ): number { + let total = 0; + + if (luck > Number(entropy % BigInt(100))) { + let damage_boost_base = Math.floor(base_damage / 5); + let damage_multiplier = Number(entropy % BigInt(5)) + 1; + total = damage_boost_base * damage_multiplier; + + if (ring?.item === "Titanium Ring" && total > 0) { + total += Math.floor( + (total * 3 * Math.floor(Math.sqrt(ring.xp!))) / 100 + ); + } + } + + return total; } - let crit_bonus = this.criticalHitBonus( - elemental_damage, - 10, - undefined, - entropy - ); - - let total_attack = elemental_damage + crit_bonus; - let total_damage = Math.floor(total_attack - base_armor); - - return Math.max(minimumDmg, total_damage); - } - - beastEncounter( - adventurerEntropy: bigint, - level: bigint, - xp: number, - rnd2: bigint, - items?: Item[] - ): BeastEncounter { - const seed = this.getRandomness(xp, adventurerEntropy).rnd1; - - const beast_id = (Number(seed) % this.beasts.maxBeastId()) + 1; - - const beast_health = this.getBeastHealth(level, seed); - - const beast_tier = this.beasts.getBeastTier(Number(beast_id)); - - const beast_type = this.beasts.getAttackType( - this.beasts.getBeastType(Number(beast_id)) - ); - - const beast_level = this.getObstacleLevel(level, seed); - - const ambush_location = this.getAttackLocation(rnd2); - const roll = this.abilityBasedAvoidThreat(level, seed); - const xp_reward = this.getXpReward(beast_level, BigInt(beast_tier.tier)); - // const specialName = this.lootManager.getSpecialName(seed); - const criticalMultiplier = this.criticalMultiplier(10, rnd2); - - const adventurerArmor = items?.find( - (item) => item.slot === ambush_location - ); - - const damage = this.calculateEncounterDamage( - beast_type, - Number(beast_tier), - Number(beast_level), - adventurerArmor, - rnd2, - 2 - ); - - return { - encounter: "Beast", - id: BigInt(beast_id), - type: beast_type, - tier: Number(beast_tier), - level: Number(beast_level), - health: Number(beast_health), - location: ambush_location, - dodgeRoll: Number(roll) + 1, - nextXp: xp + Number(xp_reward), - specialName: "", - criticalMultiplier, - damage, - }; - } + calculateEncounterDamage( + type: string | undefined, + tier: number, + level: number, + adventurerArmor: Item | undefined, + entropy: bigint, + minimumDmg: number + ) { + if (!type) return minimumDmg; + + let base_attack = level * (6 - tier!); + + let base_armor = 0; + let elemental_damage = 0; + + if (adventurerArmor) { + base_armor = + this.survivor.calculateLevel() * (6 - adventurerArmor?.tier!); + elemental_damage = this.beasts.elementalAdjustedDamage( + base_attack, + type, + adventurerArmor?.type! + ); + } else { + elemental_damage = base_attack * 1.5; + } + + let crit_bonus = this.criticalHitBonus( + elemental_damage, + 10, + undefined, + entropy + ); + + let total_attack = elemental_damage + crit_bonus; + let total_damage = Math.floor(total_attack - base_armor); + + return Math.max(minimumDmg, total_damage); + } + + beastEncounter( + adventurerEntropy: bigint, + level: bigint, + xp: number, + rnd2: bigint, + items?: Item[] + ): BeastEncounter { + const seed = this.getRandomness(xp, adventurerEntropy).rnd1; + + const beast_id = (Number(seed) % this.beasts.maxBeastId()) + 1; + + const beast_health = this.getBeastHealth(level, seed); + + const beast_tier = this.beasts.getBeastTier(Number(beast_id)); + + const beast_type = this.beasts.getAttackType( + this.beasts.getBeastType(Number(beast_id)) + ); + + const beast_level = this.getObstacleLevel(level, seed); + + const ambush_location = this.getAttackLocation(rnd2); + const roll = this.abilityBasedAvoidThreat(level, seed); + const xp_reward = this.getXpReward( + beast_level, + BigInt(beast_tier.tier) + ); + // const specialName = this.lootManager.getSpecialName(seed); + const criticalMultiplier = this.criticalMultiplier(10, rnd2); + + const adventurerArmor = items?.find( + (item) => item.slot === ambush_location + ); + + const damage = this.calculateEncounterDamage( + beast_type, + Number(beast_tier), + Number(beast_level), + adventurerArmor, + rnd2, + 2 + ); + + return { + encounter: "Beast", + id: BigInt(beast_id), + type: beast_type, + tier: Number(beast_tier), + level: Number(beast_level), + health: Number(beast_health), + location: ambush_location, + dodgeRoll: Number(roll) + 1, + nextXp: xp + Number(xp_reward), + specialName: "", + criticalMultiplier, + damage, + }; + } } diff --git a/packages/core/src/objects/survivor.test.ts b/packages/core/src/objects/survivor.test.ts index 6631f40..a4aec47 100644 --- a/packages/core/src/objects/survivor.test.ts +++ b/packages/core/src/objects/survivor.test.ts @@ -4,206 +4,208 @@ import { SELECTORS } from "../type"; import { beforeEach, describe, expect, it } from "vitest"; import { - mockStartGameEvent, - mockAdventurerUpgradedEvent, - mockDiscoveredHealthEvent, - mockDiscoveredGoldEvent, - mockDiscoveredXPEvent, - mockEquipmentChangedEvent, + mockStartGameEvent, + mockAdventurerUpgradedEvent, + mockDiscoveredHealthEvent, + mockDiscoveredGoldEvent, + mockDiscoveredXPEvent, + mockEquipmentChangedEvent, } from "../state/mock"; describe("Survivor", () => { - let survivor: Survivor; - - beforeEach(() => { - survivor = new Survivor(); - }); - - describe("updateFromEvent", () => { - it("should handle StartGame event", () => { - survivor.updateFromEvent({ - name: SELECTORS.StartGame, - event: mockStartGameEvent, - }); - - expect(survivor.id).toBe(1); - expect(survivor.name).toBe(mockStartGameEvent.adventurerMeta.name); - expect(survivor.owner).toBe(mockStartGameEvent.adventurerState.owner); - expect(survivor.health).toBe(100); - expect(survivor.maxHealth).toBe(100); - expect(survivor.xp).toBe(0); - expect(survivor.level).toBe(1); - expect(survivor.gold).toBe(0); - expect(survivor.stats).toEqual( - mockStartGameEvent.adventurerMeta.startingStats - ); - expect(survivor.startEntropy).toBe( - mockStartGameEvent.adventurerMeta.startEntropy - ); - expect(survivor.startingStats).toEqual( - mockStartGameEvent.adventurerMeta.startingStats - ); - expect(survivor.interfaceCamel).toBe( - mockStartGameEvent.adventurerMeta.interfaceCamel - ); - expect(survivor.revealBlock).toBe(mockStartGameEvent.revealBlock); + let survivor: Survivor; + + beforeEach(() => { + survivor = new Survivor(); }); - }); - it("should handle AdventurerUpgraded event", () => { - survivor.updateFromEvent({ - name: SELECTORS.AdventurerUpgraded, - event: mockAdventurerUpgradedEvent, + describe("updateFromEvent", () => { + it("should handle StartGame event", () => { + survivor.updateFromEvent({ + name: SELECTORS.StartGame, + event: mockStartGameEvent, + }); + + expect(survivor.id).toBe(1); + expect(survivor.name).toBe(mockStartGameEvent.adventurerMeta.name); + expect(survivor.owner).toBe( + mockStartGameEvent.adventurerState.owner + ); + expect(survivor.health).toBe(100); + expect(survivor.maxHealth).toBe(100); + expect(survivor.xp).toBe(0); + expect(survivor.level).toBe(1); + expect(survivor.gold).toBe(0); + expect(survivor.stats).toEqual( + mockStartGameEvent.adventurerMeta.startingStats + ); + expect(survivor.startEntropy).toBe( + mockStartGameEvent.adventurerMeta.startEntropy + ); + expect(survivor.startingStats).toEqual( + mockStartGameEvent.adventurerMeta.startingStats + ); + expect(survivor.interfaceCamel).toBe( + mockStartGameEvent.adventurerMeta.interfaceCamel + ); + expect(survivor.revealBlock).toBe(mockStartGameEvent.revealBlock); + }); }); - expect(survivor.id).toBe( - mockAdventurerUpgradedEvent.adventurerStateWithBag.adventurerState - .adventurerId - ); - expect(survivor.stats).toEqual( - mockAdventurerUpgradedEvent.adventurerStateWithBag.adventurerState - .adventurer.stats - ); - expect(survivor.bag).toEqual( - mockAdventurerUpgradedEvent.adventurerStateWithBag.bag - ); - }); - - it("should handle DiscoveredHealth event", () => { - survivor.updateFromEvent({ - name: SELECTORS.DiscoveredHealth, - event: mockDiscoveredHealthEvent, + it("should handle AdventurerUpgraded event", () => { + survivor.updateFromEvent({ + name: SELECTORS.AdventurerUpgraded, + event: mockAdventurerUpgradedEvent, + }); + + expect(survivor.id).toBe( + mockAdventurerUpgradedEvent.adventurerStateWithBag.adventurerState + .adventurerId + ); + expect(survivor.stats).toEqual( + mockAdventurerUpgradedEvent.adventurerStateWithBag.adventurerState + .adventurer.stats + ); + expect(survivor.bag).toEqual( + mockAdventurerUpgradedEvent.adventurerStateWithBag.bag + ); }); - expect(survivor.health).toBe( - mockDiscoveredHealthEvent.adventurerState.adventurer.health - ); - }); + it("should handle DiscoveredHealth event", () => { + survivor.updateFromEvent({ + name: SELECTORS.DiscoveredHealth, + event: mockDiscoveredHealthEvent, + }); - it("should handle DiscoveredGold event", () => { - survivor.updateFromEvent({ - name: SELECTORS.DiscoveredGold, - event: mockDiscoveredGoldEvent, + expect(survivor.health).toBe( + mockDiscoveredHealthEvent.adventurerState.adventurer.health + ); }); - expect(survivor.gold).toBe( - mockDiscoveredGoldEvent.adventurerState.adventurer.gold - ); - }); + it("should handle DiscoveredGold event", () => { + survivor.updateFromEvent({ + name: SELECTORS.DiscoveredGold, + event: mockDiscoveredGoldEvent, + }); - it("should handle DiscoveredXP event", () => { - survivor.updateFromEvent({ - name: SELECTORS.DiscoveredXP, - event: mockDiscoveredXPEvent, + expect(survivor.gold).toBe( + mockDiscoveredGoldEvent.adventurerState.adventurer.gold + ); }); - expect(survivor.xp).toBe( - mockDiscoveredXPEvent.adventurerState.adventurer.xp - ); - }); + it("should handle DiscoveredXP event", () => { + survivor.updateFromEvent({ + name: SELECTORS.DiscoveredXP, + event: mockDiscoveredXPEvent, + }); - it("should handle EquipmentChanged event", () => { - survivor.updateFromEvent({ - name: SELECTORS.EquipmentChanged, - event: mockEquipmentChangedEvent, + expect(survivor.xp).toBe( + mockDiscoveredXPEvent.adventurerState.adventurer.xp + ); }); - expect(survivor.items).toEqual({ - weapon: { slot: "weapon", xp: 0 }, - chest: { slot: "chest", xp: 0 }, - head: { slot: "head", xp: 0 }, - waist: { slot: "waist", xp: 0 }, - foot: { slot: "foot", xp: 0 }, - hand: { slot: "hand", xp: 0 }, - neck: { slot: "neck", xp: 0 }, - ring: { slot: "ring", xp: 0 }, - }); - expect(survivor.bag).toEqual( - mockEquipmentChangedEvent.adventurerStateWithBag.bag - ); - }); - - it("should set optimistic state", () => { - const newState = { - health: 80, - gold: 100, - xp: 50, - }; - - survivor.setOptimisticState(newState); - - expect(survivor.health).toBe(80); - expect(survivor.gold).toBe(100); - expect(survivor.xp).toBe(50); - expect(survivor.level).toBe(Math.floor(Math.sqrt(50))); // Check if level is updated - }); - - it("should revert optimistic state", () => { - const initialHealth = survivor.health; - const initialGold = survivor.gold; - const initialXp = survivor.xp; - - survivor.setOptimisticState({ - health: 80, - gold: 100, - xp: 50, + it("should handle EquipmentChanged event", () => { + survivor.updateFromEvent({ + name: SELECTORS.EquipmentChanged, + event: mockEquipmentChangedEvent, + }); + + expect(survivor.items).toEqual({ + weapon: { slot: "weapon", xp: 0 }, + chest: { slot: "chest", xp: 0 }, + head: { slot: "head", xp: 0 }, + waist: { slot: "waist", xp: 0 }, + foot: { slot: "foot", xp: 0 }, + hand: { slot: "hand", xp: 0 }, + neck: { slot: "neck", xp: 0 }, + ring: { slot: "ring", xp: 0 }, + }); + expect(survivor.bag).toEqual( + mockEquipmentChangedEvent.adventurerStateWithBag.bag + ); }); - survivor.revertOptimisticState(); + it("should set optimistic state", () => { + const newState = { + health: 80, + gold: 100, + xp: 50, + }; - expect(survivor.health).toBe(initialHealth); - expect(survivor.gold).toBe(initialGold); - expect(survivor.xp).toBe(initialXp); - }); + survivor.setOptimisticState(newState); - it("should confirm optimistic state", () => { - const newState = { - health: 80, - gold: 100, - xp: 50, - }; + expect(survivor.health).toBe(80); + expect(survivor.gold).toBe(100); + expect(survivor.xp).toBe(50); + expect(survivor.level).toBe(Math.floor(Math.sqrt(50))); // Check if level is updated + }); - survivor.setOptimisticState(newState); - survivor.confirmOptimisticState(); + it("should revert optimistic state", () => { + const initialHealth = survivor.health; + const initialGold = survivor.gold; + const initialXp = survivor.xp; - // Set a new optimistic state - survivor.setOptimisticState({ - health: 70, - gold: 150, - xp: 75, - }); + survivor.setOptimisticState({ + health: 80, + gold: 100, + xp: 50, + }); - // Revert the new state - survivor.revertOptimisticState(); + survivor.revertOptimisticState(); - // Check if it reverts to the confirmed state, not the initial state - expect(survivor.health).toBe(80); - expect(survivor.gold).toBe(100); - expect(survivor.xp).toBe(50); - }); + expect(survivor.health).toBe(initialHealth); + expect(survivor.gold).toBe(initialGold); + expect(survivor.xp).toBe(initialXp); + }); - it("should update derived properties when setting optimistic state", () => { - const newState = { - stats: { - ...survivor.stats, - vitality: survivor.stats.vitality + 10, - }, - xp: 400, - }; + it("should confirm optimistic state", () => { + const newState = { + health: 80, + gold: 100, + xp: 50, + }; + + survivor.setOptimisticState(newState); + survivor.confirmOptimisticState(); + + // Set a new optimistic state + survivor.setOptimisticState({ + health: 70, + gold: 150, + xp: 75, + }); + + // Revert the new state + survivor.revertOptimisticState(); + + // Check if it reverts to the confirmed state, not the initial state + expect(survivor.health).toBe(80); + expect(survivor.gold).toBe(100); + expect(survivor.xp).toBe(50); + }); + + it("should update derived properties when setting optimistic state", () => { + const newState = { + stats: { + ...survivor.stats, + vitality: survivor.stats.vitality + 10, + }, + xp: 400, + }; - survivor.setOptimisticState(newState); + survivor.setOptimisticState(newState); - expect(survivor.maxHealth).toBe(survivor.stats.vitality * 10); - expect(survivor.level).toBe(Math.floor(Math.sqrt(400))); - }); + expect(survivor.maxHealth).toBe(survivor.stats.vitality * 10); + expect(survivor.level).toBe(Math.floor(Math.sqrt(400))); + }); - it("should not revert state if no optimistic update was made", () => { - const initialHealth = survivor.health; - const initialGold = survivor.gold; + it("should not revert state if no optimistic update was made", () => { + const initialHealth = survivor.health; + const initialGold = survivor.gold; - survivor.revertOptimisticState(); + survivor.revertOptimisticState(); - expect(survivor.health).toBe(initialHealth); - expect(survivor.gold).toBe(initialGold); - }); + expect(survivor.health).toBe(initialHealth); + expect(survivor.gold).toBe(initialGold); + }); }); diff --git a/packages/core/src/objects/survivor.ts b/packages/core/src/objects/survivor.ts index 9848e6f..73d6880 100644 --- a/packages/core/src/objects/survivor.ts +++ b/packages/core/src/objects/survivor.ts @@ -1,21 +1,21 @@ import { - ITEM_BASE_PRICE, - ITEM_CHARISMA_DISCOUNT, - ITEM_MINIMUM_PRICE, - POTION_BASE_PRICE, + ITEM_BASE_PRICE, + ITEM_CHARISMA_DISCOUNT, + ITEM_MINIMUM_PRICE, + POTION_BASE_PRICE, } from "../constants"; import { useSurvivorStore } from "../state"; import { - Stats, - Item, - ItemSlot, - StatType, - Bag, - Beast, - Battle, - Discovery, - SELECTORS, - // Loot, + Stats, + Item, + ItemSlot, + StatType, + Bag, + Beast, + Battle, + Discovery, + SELECTORS, + // Loot, } from "../type"; import * as EventTypes from "../type/events"; @@ -23,597 +23,611 @@ import { BeastManager } from "./beasts"; import { LootManager } from "./loot"; export class Survivor { - private beasts: BeastManager = new BeastManager(); - private previousState: Partial | null = null; - - id: number | null = null; - name: number | null = null; - owner: string | null = null; - statUpgradesAvailable: number = 0; - stats: Stats = { - strength: 0, - dexterity: 0, - vitality: 0, - intelligence: 0, - wisdom: 0, - charisma: 0, - luck: 0, - }; - health: number = 0; - maxHealth: number = 0; - xp: number = 0; - level: number = 1; - gold: number = 0; - items: Record = { - [ItemSlot.Weapon]: { slot: ItemSlot.Weapon, xp: 0 }, - [ItemSlot.Chest]: { slot: ItemSlot.Chest, xp: 0 }, - [ItemSlot.Head]: { slot: ItemSlot.Head, xp: 0 }, - [ItemSlot.Waist]: { slot: ItemSlot.Waist, xp: 0 }, - [ItemSlot.Foot]: { slot: ItemSlot.Foot, xp: 0 }, - [ItemSlot.Hand]: { slot: ItemSlot.Hand, xp: 0 }, - [ItemSlot.Neck]: { slot: ItemSlot.Neck, xp: 0 }, - [ItemSlot.Ring]: { slot: ItemSlot.Ring, xp: 0 }, - }; - bag: Bag | null = null; - - beast: Beast | null = null; - - startEntropy: string | null = null; - adventurerEntropy: string | null = null; - startingStats: Stats | null = null; - interfaceCamel: boolean = false; - - battle: Battle | null = null; - - discovery: Discovery | null = null; - - revealBlock: number | null = null; - - constructor() {} - - getItemsWithBoosts(): Record { - return { - [ItemSlot.Weapon]: new LootManager( - parseInt(this.items[ItemSlot.Weapon]?.item!) ?? null, - this.items[ItemSlot.Weapon]?.xp ?? 0, - BigInt(this.adventurerEntropy ?? "0") - ), - [ItemSlot.Chest]: new LootManager( - parseInt(this.items[ItemSlot.Chest]?.item!) ?? null, - this.items[ItemSlot.Chest]?.xp ?? 0, - BigInt(this.adventurerEntropy ?? "0") - ), - [ItemSlot.Head]: new LootManager( - parseInt(this.items[ItemSlot.Head]?.item!) ?? null, - this.items[ItemSlot.Head]?.xp ?? 0, - BigInt(this.adventurerEntropy ?? "0") - ), - [ItemSlot.Waist]: new LootManager( - parseInt(this.items[ItemSlot.Waist]?.item!) ?? null, - this.items[ItemSlot.Waist]?.xp ?? 0, - BigInt(this.adventurerEntropy ?? "0") - ), - [ItemSlot.Foot]: new LootManager( - parseInt(this.items[ItemSlot.Foot]?.item!) ?? null, - this.items[ItemSlot.Foot]?.xp ?? 0, - BigInt(this.adventurerEntropy ?? "0") - ), - [ItemSlot.Hand]: new LootManager( - parseInt(this.items[ItemSlot.Hand]?.item!) ?? null, - this.items[ItemSlot.Hand]?.xp ?? 0, - BigInt(this.adventurerEntropy ?? "0") - ), - [ItemSlot.Neck]: new LootManager( - parseInt(this.items[ItemSlot.Neck]?.item!) ?? null, - this.items[ItemSlot.Neck]?.xp ?? 0, - BigInt(this.adventurerEntropy ?? "0") - ), - [ItemSlot.Ring]: new LootManager( - parseInt(this.items[ItemSlot.Ring]?.item!) ?? null, - this.items[ItemSlot.Ring]?.xp ?? 0, - BigInt(this.adventurerEntropy ?? "0") - ), + private beasts: BeastManager = new BeastManager(); + private previousState: Partial | null = null; + + id: number | null = null; + name: number | null = null; + owner: string | null = null; + statUpgradesAvailable: number = 0; + stats: Stats = { + strength: 0, + dexterity: 0, + vitality: 0, + intelligence: 0, + wisdom: 0, + charisma: 0, + luck: 0, }; - } - - setOptimisticState(newState: Partial): void { - // Store the current state - this.previousState = { - health: this.health, - maxHealth: this.maxHealth, - xp: this.xp, - level: this.level, - gold: this.gold, - stats: { ...this.stats }, - items: { ...this.items }, - bag: this.bag, - beast: this.beast, - battle: this.battle, + health: number = 0; + maxHealth: number = 0; + xp: number = 0; + level: number = 1; + gold: number = 0; + items: Record = { + [ItemSlot.Weapon]: { slot: ItemSlot.Weapon, xp: 0 }, + [ItemSlot.Chest]: { slot: ItemSlot.Chest, xp: 0 }, + [ItemSlot.Head]: { slot: ItemSlot.Head, xp: 0 }, + [ItemSlot.Waist]: { slot: ItemSlot.Waist, xp: 0 }, + [ItemSlot.Foot]: { slot: ItemSlot.Foot, xp: 0 }, + [ItemSlot.Hand]: { slot: ItemSlot.Hand, xp: 0 }, + [ItemSlot.Neck]: { slot: ItemSlot.Neck, xp: 0 }, + [ItemSlot.Ring]: { slot: ItemSlot.Ring, xp: 0 }, }; + bag: Bag | null = null; + + beast: Beast | null = null; + + startEntropy: string | null = null; + adventurerEntropy: string | null = null; + startingStats: Stats | null = null; + interfaceCamel: boolean = false; + + battle: Battle | null = null; + + discovery: Discovery | null = null; + + revealBlock: number | null = null; + + constructor() {} + + getItemsWithBoosts(): Record { + return { + [ItemSlot.Weapon]: new LootManager( + parseInt(this.items[ItemSlot.Weapon]?.item!) ?? null, + this.items[ItemSlot.Weapon]?.xp ?? 0, + BigInt(this.adventurerEntropy ?? "0") + ), + [ItemSlot.Chest]: new LootManager( + parseInt(this.items[ItemSlot.Chest]?.item!) ?? null, + this.items[ItemSlot.Chest]?.xp ?? 0, + BigInt(this.adventurerEntropy ?? "0") + ), + [ItemSlot.Head]: new LootManager( + parseInt(this.items[ItemSlot.Head]?.item!) ?? null, + this.items[ItemSlot.Head]?.xp ?? 0, + BigInt(this.adventurerEntropy ?? "0") + ), + [ItemSlot.Waist]: new LootManager( + parseInt(this.items[ItemSlot.Waist]?.item!) ?? null, + this.items[ItemSlot.Waist]?.xp ?? 0, + BigInt(this.adventurerEntropy ?? "0") + ), + [ItemSlot.Foot]: new LootManager( + parseInt(this.items[ItemSlot.Foot]?.item!) ?? null, + this.items[ItemSlot.Foot]?.xp ?? 0, + BigInt(this.adventurerEntropy ?? "0") + ), + [ItemSlot.Hand]: new LootManager( + parseInt(this.items[ItemSlot.Hand]?.item!) ?? null, + this.items[ItemSlot.Hand]?.xp ?? 0, + BigInt(this.adventurerEntropy ?? "0") + ), + [ItemSlot.Neck]: new LootManager( + parseInt(this.items[ItemSlot.Neck]?.item!) ?? null, + this.items[ItemSlot.Neck]?.xp ?? 0, + BigInt(this.adventurerEntropy ?? "0") + ), + [ItemSlot.Ring]: new LootManager( + parseInt(this.items[ItemSlot.Ring]?.item!) ?? null, + this.items[ItemSlot.Ring]?.xp ?? 0, + BigInt(this.adventurerEntropy ?? "0") + ), + }; + } - // Apply the new state - Object.assign(this, newState); - - // Update derived properties - this.maxHealth = this.calculateMaxHealth(); - this.level = this.calculateLevel(); - - // Update the store - this.updateStore(); - } - - revertOptimisticState(): void { - if (this.previousState) { - Object.assign(this, this.previousState); - this.previousState = null; - this.updateStore(); - } - } - - confirmOptimisticState(): void { - // Clear the previous state - this.previousState = null; - } - - updateFromEvents(events: { name: string; event: any }[]): void { - for (const event of events) { - this.updateFromEvent(event); - } - } - - getItemPrice(tier: number) { - const price = - (6 - tier) * ITEM_BASE_PRICE - - ITEM_CHARISMA_DISCOUNT * this.stats.charisma; - if (price < ITEM_MINIMUM_PRICE) { - return ITEM_MINIMUM_PRICE; - } else { - return price; - } - } - - getPotionPrice() { - return Math.max( - this.calculateLevel() - POTION_BASE_PRICE * this.stats.charisma, - 1 - ); - } - - checkAvailableSlots(): boolean { - const equippedItemsCount = Object.values(this.items).filter( - (item) => item.xp !== undefined - ).length; - const bagItemsCount = this.bag - ? Object.keys(this.bag).filter((key) => key !== "mutated").length - : 0; - const totalItemsCount = equippedItemsCount + bagItemsCount; - - const maxItemSlots = 20; // Assuming the maximum is still 20 - - return totalItemsCount < maxItemSlots; - } - - updateFromEvent(event: { name: string; event: any }): void { - switch (event.name) { - case SELECTORS.StartGame: - this.handleStartGameEvent(event.event as EventTypes.StartGameEvent); - break; - case SELECTORS.AdventurerUpgraded: - this.handleAdventurerUpgradedEvent( - event.event as EventTypes.AdventurerUpgradedEvent - ); - break; - case SELECTORS.DiscoveredHealth: - this.handleDiscoveredHealthEvent( - event.event as EventTypes.DiscoveredHealthEvent - ); - break; - case SELECTORS.DiscoveredGold: - this.handleDiscoveredGoldEvent( - event.event as EventTypes.DiscoveredGoldEvent - ); - break; - case SELECTORS.DiscoveredXP: - this.handleDiscoveredXPEvent( - event.event as EventTypes.DiscoveredXPEvent - ); - break; - case SELECTORS.DiscoveredLoot: - this.handleDiscoveredLootEvent( - event.event as EventTypes.DiscoveredLootEvent - ); - break; - case SELECTORS.EquipmentChanged: - this.handleEquipmentChangedEvent( - event.event as EventTypes.EquipmentChangedEvent - ); - break; - case SELECTORS.DodgedObstacle: - this.handleDodgedObstacleEvent( - event.event as EventTypes.DodgedObstacleEvent - ); - break; - case SELECTORS.HitByObstacle: - this.handleHitByObstacleEvent( - event.event as EventTypes.HitByObstacleEvent - ); - break; - case SELECTORS.DiscoveredBeast: - this.handleDiscoveredBeastEvent( - event.event as EventTypes.DiscoveredBeastEvent - ); - break; - case SELECTORS.AmbushedByBeast: - this.handleAmbushedByBeastEvent( - event.event as EventTypes.AmbushedByBeastEvent - ); - break; - case SELECTORS.AttackedBeast: - this.handleAttackedBeastEvent( - event.event as EventTypes.AttackedBeastEvent - ); - break; - case SELECTORS.AttackedByBeast: - this.handleAttackedByBeastEvent( - event.event as EventTypes.AttackedByBeastEvent - ); - break; - case SELECTORS.SlayedBeast: - this.handleSlayedBeastEvent(event.event as EventTypes.SlayedBeastEvent); - break; - case SELECTORS.FleeFailed: - this.handleFleeFailedEvent(event.event as EventTypes.FleeFailedEvent); - break; - case SELECTORS.FleeSucceeded: - this.handleFleeSucceededEvent( - event.event as EventTypes.FleeSucceededEvent - ); - break; - case SELECTORS.PurchasedItems: - this.handlePurchasedItemsEvent( - event.event as EventTypes.PurchasedItemsEvent - ); - break; - case SELECTORS.PurchasedPotions: - this.handlePurchasedPotionsEvent( - event.event as EventTypes.PurchasedPotionsEvent - ); - break; - case SELECTORS.EquippedItems: - this.handleEquippedItemsEvent( - event.event as EventTypes.EquippedItemsEvent - ); - break; - case SELECTORS.DroppedItems: - this.handleDroppedItemsEvent( - event.event as EventTypes.DroppedItemsEvent - ); - break; - case SELECTORS.GreatnessIncreased: - this.handleGreatnessIncreasedEvent( - event.event as EventTypes.GreatnessIncreasedEvent - ); - break; - case SELECTORS.ItemsLeveledUp: - this.handleItemsLeveledUpEvent( - event.event as EventTypes.ItemsLeveledUpEvent + setOptimisticState(newState: Partial): void { + // Store the current state + this.previousState = { + health: this.health, + maxHealth: this.maxHealth, + xp: this.xp, + level: this.level, + gold: this.gold, + stats: { ...this.stats }, + items: { ...this.items }, + bag: this.bag, + beast: this.beast, + battle: this.battle, + }; + + // Apply the new state + Object.assign(this, newState); + + // Update derived properties + this.maxHealth = this.calculateMaxHealth(); + this.level = this.calculateLevel(); + + // Update the store + this.updateStore(); + } + + revertOptimisticState(): void { + if (this.previousState) { + Object.assign(this, this.previousState); + this.previousState = null; + this.updateStore(); + } + } + + confirmOptimisticState(): void { + // Clear the previous state + this.previousState = null; + } + + updateFromEvents(events: { name: string; event: any }[]): void { + for (const event of events) { + this.updateFromEvent(event); + } + } + + getItemPrice(tier: number) { + const price = + (6 - tier) * ITEM_BASE_PRICE - + ITEM_CHARISMA_DISCOUNT * this.stats.charisma; + if (price < ITEM_MINIMUM_PRICE) { + return ITEM_MINIMUM_PRICE; + } else { + return price; + } + } + + getPotionPrice() { + return Math.max( + this.calculateLevel() - POTION_BASE_PRICE * this.stats.charisma, + 1 ); - break; - case SELECTORS.NewHighScore: - this.handleNewHighScoreEvent( - event.event as EventTypes.NewHighScoreEvent + } + + checkAvailableSlots(): boolean { + const equippedItemsCount = Object.values(this.items).filter( + (item) => item.xp !== undefined + ).length; + const bagItemsCount = this.bag + ? Object.keys(this.bag).filter((key) => key !== "mutated").length + : 0; + const totalItemsCount = equippedItemsCount + bagItemsCount; + + const maxItemSlots = 20; // Assuming the maximum is still 20 + + return totalItemsCount < maxItemSlots; + } + + updateFromEvent(event: { name: string; event: any }): void { + switch (event.name) { + case SELECTORS.StartGame: + this.handleStartGameEvent( + event.event as EventTypes.StartGameEvent + ); + break; + case SELECTORS.AdventurerUpgraded: + this.handleAdventurerUpgradedEvent( + event.event as EventTypes.AdventurerUpgradedEvent + ); + break; + case SELECTORS.DiscoveredHealth: + this.handleDiscoveredHealthEvent( + event.event as EventTypes.DiscoveredHealthEvent + ); + break; + case SELECTORS.DiscoveredGold: + this.handleDiscoveredGoldEvent( + event.event as EventTypes.DiscoveredGoldEvent + ); + break; + case SELECTORS.DiscoveredXP: + this.handleDiscoveredXPEvent( + event.event as EventTypes.DiscoveredXPEvent + ); + break; + case SELECTORS.DiscoveredLoot: + this.handleDiscoveredLootEvent( + event.event as EventTypes.DiscoveredLootEvent + ); + break; + case SELECTORS.EquipmentChanged: + this.handleEquipmentChangedEvent( + event.event as EventTypes.EquipmentChangedEvent + ); + break; + case SELECTORS.DodgedObstacle: + this.handleDodgedObstacleEvent( + event.event as EventTypes.DodgedObstacleEvent + ); + break; + case SELECTORS.HitByObstacle: + this.handleHitByObstacleEvent( + event.event as EventTypes.HitByObstacleEvent + ); + break; + case SELECTORS.DiscoveredBeast: + this.handleDiscoveredBeastEvent( + event.event as EventTypes.DiscoveredBeastEvent + ); + break; + case SELECTORS.AmbushedByBeast: + this.handleAmbushedByBeastEvent( + event.event as EventTypes.AmbushedByBeastEvent + ); + break; + case SELECTORS.AttackedBeast: + this.handleAttackedBeastEvent( + event.event as EventTypes.AttackedBeastEvent + ); + break; + case SELECTORS.AttackedByBeast: + this.handleAttackedByBeastEvent( + event.event as EventTypes.AttackedByBeastEvent + ); + break; + case SELECTORS.SlayedBeast: + this.handleSlayedBeastEvent( + event.event as EventTypes.SlayedBeastEvent + ); + break; + case SELECTORS.FleeFailed: + this.handleFleeFailedEvent( + event.event as EventTypes.FleeFailedEvent + ); + break; + case SELECTORS.FleeSucceeded: + this.handleFleeSucceededEvent( + event.event as EventTypes.FleeSucceededEvent + ); + break; + case SELECTORS.PurchasedItems: + this.handlePurchasedItemsEvent( + event.event as EventTypes.PurchasedItemsEvent + ); + break; + case SELECTORS.PurchasedPotions: + this.handlePurchasedPotionsEvent( + event.event as EventTypes.PurchasedPotionsEvent + ); + break; + case SELECTORS.EquippedItems: + this.handleEquippedItemsEvent( + event.event as EventTypes.EquippedItemsEvent + ); + break; + case SELECTORS.DroppedItems: + this.handleDroppedItemsEvent( + event.event as EventTypes.DroppedItemsEvent + ); + break; + case SELECTORS.GreatnessIncreased: + this.handleGreatnessIncreasedEvent( + event.event as EventTypes.GreatnessIncreasedEvent + ); + break; + case SELECTORS.ItemsLeveledUp: + this.handleItemsLeveledUpEvent( + event.event as EventTypes.ItemsLeveledUpEvent + ); + break; + case SELECTORS.NewHighScore: + this.handleNewHighScoreEvent( + event.event as EventTypes.NewHighScoreEvent + ); + break; + case SELECTORS.AdventurerDied: + this.handleAdventurerDiedEvent( + event.event as EventTypes.AdventurerDiedEvent + ); + break; + case SELECTORS.AdventurerLeveledUp: + this.handleAdventurerLeveledUpEvent( + event.event as EventTypes.AdventurerLeveledUpEvent + ); + break; + case SELECTORS.UpgradesAvailable: + this.handleUpgradesAvailableEvent( + event.event as EventTypes.UpgradesAvailableEvent + ); + break; + default: + console.warn(`Unhandled event type: ${event.name}`); + } + this.updateStore(); + } + + private updateStore(): void { + useSurvivorStore.getState().updateSurvivor(this); + } + + private handleStartGameEvent(event: EventTypes.StartGameEvent): void { + const { adventurerState, adventurerMeta, revealBlock } = event; + this.updateFromAdventurerState(adventurerState); + this.startEntropy = adventurerMeta.startEntropy; + this.startingStats = adventurerMeta.startingStats; + this.interfaceCamel = adventurerMeta.interfaceCamel; + this.name = adventurerMeta.name; + this.revealBlock = revealBlock; + } + + private handleAdventurerUpgradedEvent( + event: EventTypes.AdventurerUpgradedEvent + ): void { + const { adventurerStateWithBag } = event; + this.updateFromAdventurerState(adventurerStateWithBag.adventurerState); + this.bag = adventurerStateWithBag.bag; + } + + private handleDiscoveredHealthEvent( + event: EventTypes.DiscoveredHealthEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleDiscoveredGoldEvent( + event: EventTypes.DiscoveredGoldEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleDiscoveredXPEvent(event: EventTypes.DiscoveredXPEvent): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleDiscoveredLootEvent( + event: EventTypes.DiscoveredLootEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleEquipmentChangedEvent( + event: EventTypes.EquipmentChangedEvent + ): void { + this.updateFromAdventurerState( + event.adventurerStateWithBag.adventurerState ); - break; - case SELECTORS.AdventurerDied: - this.handleAdventurerDiedEvent( - event.event as EventTypes.AdventurerDiedEvent + this.bag = event.adventurerStateWithBag.bag; + } + + private handleDodgedObstacleEvent( + event: EventTypes.DodgedObstacleEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleHitByObstacleEvent( + event: EventTypes.HitByObstacleEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleDiscoveredBeastEvent( + event: EventTypes.DiscoveredBeastEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + + this.beast = this.createBeastObject(event); + this.battle = this.createBattleObject(event, "adventurer"); + } + + private handleAmbushedByBeastEvent( + event: EventTypes.AmbushedByBeastEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + this.takeDamage(event.damage); + + this.beast = this.createBeastObject(event); + this.battle = this.createBattleObject(event, "beast"); + } + + private handleAttackedBeastEvent( + event: EventTypes.AttackedBeastEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + + this.beast = this.createBeastObject(event); + this.battle = this.createBattleObject(event, "adventurer"); + } + + private handleAttackedByBeastEvent( + event: EventTypes.AttackedByBeastEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + + this.beast = this.createBeastObject(event); + this.battle = this.createBattleObject(event, "beast"); + } + + private handleSlayedBeastEvent(event: EventTypes.SlayedBeastEvent): void { + this.updateFromAdventurerState(event.adventurerState); + this.beast = null; + this.battle = null; + } + + private handleFleeFailedEvent(event: EventTypes.FleeFailedEvent): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleFleeSucceededEvent( + event: EventTypes.FleeSucceededEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + this.beast = null; + this.battle = null; + } + + private handlePurchasedItemsEvent( + event: EventTypes.PurchasedItemsEvent + ): void { + this.updateFromAdventurerState( + event.adventurerStateWithBag.adventurerState ); - break; - case SELECTORS.AdventurerLeveledUp: - this.handleAdventurerLeveledUpEvent( - event.event as EventTypes.AdventurerLeveledUpEvent + this.bag = event.adventurerStateWithBag.bag; + } + + private handlePurchasedPotionsEvent( + event: EventTypes.PurchasedPotionsEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleEquippedItemsEvent( + event: EventTypes.EquippedItemsEvent + ): void { + this.updateFromAdventurerState( + event.adventurerStateWithBag.adventurerState ); - break; - case SELECTORS.UpgradesAvailable: - this.handleUpgradesAvailableEvent( - event.event as EventTypes.UpgradesAvailableEvent + this.bag = event.adventurerStateWithBag.bag; + } + + private handleDroppedItemsEvent(event: EventTypes.DroppedItemsEvent): void { + this.updateFromAdventurerState( + event.adventurerStateWithBag.adventurerState ); - break; - default: - console.warn(`Unhandled event type: ${event.name}`); - } - this.updateStore(); - } - - private updateStore(): void { - useSurvivorStore.getState().updateSurvivor(this); - } - - private handleStartGameEvent(event: EventTypes.StartGameEvent): void { - const { adventurerState, adventurerMeta, revealBlock } = event; - this.updateFromAdventurerState(adventurerState); - this.startEntropy = adventurerMeta.startEntropy; - this.startingStats = adventurerMeta.startingStats; - this.interfaceCamel = adventurerMeta.interfaceCamel; - this.name = adventurerMeta.name; - this.revealBlock = revealBlock; - } - - private handleAdventurerUpgradedEvent( - event: EventTypes.AdventurerUpgradedEvent - ): void { - const { adventurerStateWithBag } = event; - this.updateFromAdventurerState(adventurerStateWithBag.adventurerState); - this.bag = adventurerStateWithBag.bag; - } - - private handleDiscoveredHealthEvent( - event: EventTypes.DiscoveredHealthEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleDiscoveredGoldEvent( - event: EventTypes.DiscoveredGoldEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleDiscoveredXPEvent(event: EventTypes.DiscoveredXPEvent): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleDiscoveredLootEvent( - event: EventTypes.DiscoveredLootEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleEquipmentChangedEvent( - event: EventTypes.EquipmentChangedEvent - ): void { - this.updateFromAdventurerState( - event.adventurerStateWithBag.adventurerState - ); - this.bag = event.adventurerStateWithBag.bag; - } - - private handleDodgedObstacleEvent( - event: EventTypes.DodgedObstacleEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleHitByObstacleEvent(event: EventTypes.HitByObstacleEvent): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleDiscoveredBeastEvent( - event: EventTypes.DiscoveredBeastEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - - this.beast = this.createBeastObject(event); - this.battle = this.createBattleObject(event, "adventurer"); - } - - private handleAmbushedByBeastEvent( - event: EventTypes.AmbushedByBeastEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - this.takeDamage(event.damage); - - this.beast = this.createBeastObject(event); - this.battle = this.createBattleObject(event, "beast"); - } - - private handleAttackedBeastEvent(event: EventTypes.AttackedBeastEvent): void { - this.updateFromAdventurerState(event.adventurerState); - - this.beast = this.createBeastObject(event); - this.battle = this.createBattleObject(event, "adventurer"); - } - - private handleAttackedByBeastEvent( - event: EventTypes.AttackedByBeastEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - - this.beast = this.createBeastObject(event); - this.battle = this.createBattleObject(event, "beast"); - } - - private handleSlayedBeastEvent(event: EventTypes.SlayedBeastEvent): void { - this.updateFromAdventurerState(event.adventurerState); - this.beast = null; - this.battle = null; - } - - private handleFleeFailedEvent(event: EventTypes.FleeFailedEvent): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleFleeSucceededEvent(event: EventTypes.FleeSucceededEvent): void { - this.updateFromAdventurerState(event.adventurerState); - this.beast = null; - this.battle = null; - } - - private handlePurchasedItemsEvent( - event: EventTypes.PurchasedItemsEvent - ): void { - this.updateFromAdventurerState( - event.adventurerStateWithBag.adventurerState - ); - this.bag = event.adventurerStateWithBag.bag; - } - - private handlePurchasedPotionsEvent( - event: EventTypes.PurchasedPotionsEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleEquippedItemsEvent(event: EventTypes.EquippedItemsEvent): void { - this.updateFromAdventurerState( - event.adventurerStateWithBag.adventurerState - ); - this.bag = event.adventurerStateWithBag.bag; - } - - private handleDroppedItemsEvent(event: EventTypes.DroppedItemsEvent): void { - this.updateFromAdventurerState( - event.adventurerStateWithBag.adventurerState - ); - this.bag = event.adventurerStateWithBag.bag; - } - - private handleGreatnessIncreasedEvent( - event: EventTypes.GreatnessIncreasedEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleItemsLeveledUpEvent( - event: EventTypes.ItemsLeveledUpEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleNewHighScoreEvent(event: EventTypes.NewHighScoreEvent): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleAdventurerDiedEvent( - event: EventTypes.AdventurerDiedEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private handleAdventurerLeveledUpEvent( - event: EventTypes.AdventurerLeveledUpEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - this.level = event.newLevel; - } - - private handleUpgradesAvailableEvent( - event: EventTypes.UpgradesAvailableEvent - ): void { - this.updateFromAdventurerState(event.adventurerState); - } - - private updateFromAdventurerState(state: EventTypes.AdventurerState): void { - this.id = state.adventurerId; - this.owner = state.owner; - this.health = state.adventurer.health; - this.xp = state.adventurer.xp; - this.gold = state.adventurer.gold; - this.stats = state.adventurer.stats; - this.items = this.convertEquipmentToItems(state.adventurer.equipment); - this.maxHealth = this.calculateMaxHealth(); - this.level = this.calculateLevel(); - this.adventurerEntropy = state.adventurerEntropy; - this.statUpgradesAvailable = state.adventurer.statUpgradesAvailable; - } - - private createBeastObject(event: any): Beast { - return { - beast: this.beasts.getBeastName(event.id), - createdTime: new Date().getTime().toString(), - seed: event.seed.toString(), - level: event.beastSpecs.level, - slainOnTime: null, - special1: event.beastSpecs.specials.special1, - special2: event.beastSpecs.specials.special2, - special3: event.beastSpecs.specials.special3, - adventurerId: this.id as number, - lastUpdatedTime: new Date().getTime().toString(), - health: event.adventurerState.adventurer.beastHealth, - timestamp: new Date().getTime().toString(), - }; - } - - private createBattleObject( - event: any, - attacker: "adventurer" | "beast" - ): Battle { - return { - attacker, - adventurerId: this.id as number, - beast: this.beasts.getBeastName(event.id), - beastHealth: event.adventurerState.adventurer.beastHealth, - beastLevel: event.beastSpecs.level, - blockTime: new Date().toISOString(), - criticalHit: event.criticalHit, - damageDealt: attacker === "adventurer" ? event.damage : 0, - damageLocation: event.location.toString(), - damageTaken: attacker === "beast" ? event.damage : 0, - discoveryTime: new Date().toISOString(), - fled: false, - goldEarned: 0, - seed: event.seed.toString(), - special1: event.beastSpecs.specials.special1, - special2: event.beastSpecs.specials.special2, - special3: event.beastSpecs.specials.special3, - timestamp: new Date().getTime().toString(), - txHash: "", - xpEarnedAdventurer: 0, - xpEarnedItems: 0, - }; - } - - private convertEquipmentToItems( - equipment: Record - ): Record { - const items: Record = {} as Record; - for (const [slot, equip] of Object.entries(equipment)) { - items[slot as ItemSlot] = { - slot: slot as ItemSlot, - xp: equip.xp, - }; - } - return items; - } - - calculateMaxHealth(): number { - return this.stats.vitality * 10; - } - - calculateLevel(): number { - return Math.max(Math.floor(Math.sqrt(this.xp)), 1); - } - - getStat(stat: StatType): number { - return this.stats[StatType[stat].toLowerCase() as keyof Stats]; - } - - updateStat(stat: StatType, value: number): void { - const statKey = StatType[stat].toLowerCase() as keyof Stats; - this.stats[statKey] = value; - if (stat === StatType.Vitality) { - this.maxHealth = this.calculateMaxHealth(); - } - } - - unequipItem(slot: ItemSlot): void { - this.items[slot] = { slot }; - } - - addXp(amount: number): void { - this.xp += amount; - this.level = this.calculateLevel(); - } - - addGold(amount: number): void { - this.gold += amount; - } - - takeDamage(amount: number): void { - this.health = Math.max(0, this.health - amount); - } - - heal(amount: number): void { - this.health = Math.min(this.maxHealth, this.health + amount); - } + this.bag = event.adventurerStateWithBag.bag; + } + + private handleGreatnessIncreasedEvent( + event: EventTypes.GreatnessIncreasedEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleItemsLeveledUpEvent( + event: EventTypes.ItemsLeveledUpEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleNewHighScoreEvent(event: EventTypes.NewHighScoreEvent): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleAdventurerDiedEvent( + event: EventTypes.AdventurerDiedEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private handleAdventurerLeveledUpEvent( + event: EventTypes.AdventurerLeveledUpEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + this.level = event.newLevel; + } + + private handleUpgradesAvailableEvent( + event: EventTypes.UpgradesAvailableEvent + ): void { + this.updateFromAdventurerState(event.adventurerState); + } + + private updateFromAdventurerState(state: EventTypes.AdventurerState): void { + this.id = state.adventurerId; + this.owner = state.owner; + this.health = state.adventurer.health; + this.xp = state.adventurer.xp; + this.gold = state.adventurer.gold; + this.stats = state.adventurer.stats; + this.items = this.convertEquipmentToItems(state.adventurer.equipment); + this.maxHealth = this.calculateMaxHealth(); + this.level = this.calculateLevel(); + this.adventurerEntropy = state.adventurerEntropy; + this.statUpgradesAvailable = state.adventurer.statUpgradesAvailable; + } + + private createBeastObject(event: any): Beast { + return { + beast: this.beasts.getBeastName(event.id), + createdTime: new Date().getTime().toString(), + seed: event.seed.toString(), + level: event.beastSpecs.level, + slainOnTime: null, + special1: event.beastSpecs.specials.special1, + special2: event.beastSpecs.specials.special2, + special3: event.beastSpecs.specials.special3, + adventurerId: this.id as number, + lastUpdatedTime: new Date().getTime().toString(), + health: event.adventurerState.adventurer.beastHealth, + timestamp: new Date().getTime().toString(), + }; + } + + private createBattleObject( + event: any, + attacker: "adventurer" | "beast" + ): Battle { + return { + attacker, + adventurerId: this.id as number, + beast: this.beasts.getBeastName(event.id), + beastHealth: event.adventurerState.adventurer.beastHealth, + beastLevel: event.beastSpecs.level, + blockTime: new Date().toISOString(), + criticalHit: event.criticalHit, + damageDealt: attacker === "adventurer" ? event.damage : 0, + damageLocation: event.location.toString(), + damageTaken: attacker === "beast" ? event.damage : 0, + discoveryTime: new Date().toISOString(), + fled: false, + goldEarned: 0, + seed: event.seed.toString(), + special1: event.beastSpecs.specials.special1, + special2: event.beastSpecs.specials.special2, + special3: event.beastSpecs.specials.special3, + timestamp: new Date().getTime().toString(), + txHash: "", + xpEarnedAdventurer: 0, + xpEarnedItems: 0, + }; + } + + private convertEquipmentToItems( + equipment: Record + ): Record { + const items: Record = {} as Record; + for (const [slot, equip] of Object.entries(equipment)) { + items[slot as ItemSlot] = { + slot: slot as ItemSlot, + xp: equip.xp, + }; + } + return items; + } + + calculateMaxHealth(): number { + return this.stats.vitality * 10; + } + + calculateLevel(): number { + return Math.max(Math.floor(Math.sqrt(this.xp)), 1); + } + + getStat(stat: StatType): number { + return this.stats[StatType[stat].toLowerCase() as keyof Stats]; + } + + updateStat(stat: StatType, value: number): void { + const statKey = StatType[stat].toLowerCase() as keyof Stats; + this.stats[statKey] = value; + if (stat === StatType.Vitality) { + this.maxHealth = this.calculateMaxHealth(); + } + } + + unequipItem(slot: ItemSlot): void { + this.items[slot] = { slot }; + } + + addXp(amount: number): void { + this.xp += amount; + this.level = this.calculateLevel(); + } + + addGold(amount: number): void { + this.gold += amount; + } + + takeDamage(amount: number): void { + this.health = Math.max(0, this.health - amount); + } + + heal(amount: number): void { + this.health = Math.min(this.maxHealth, this.health + amount); + } } diff --git a/packages/core/src/provider/execute.ts b/packages/core/src/provider/execute.ts index dc3af99..27a3639 100644 --- a/packages/core/src/provider/execute.ts +++ b/packages/core/src/provider/execute.ts @@ -10,227 +10,243 @@ // * 5. Format txs and pipe back into the store import { - Account, - InvokeTransactionReceiptResponse, - RpcProvider, + Account, + InvokeTransactionReceiptResponse, + RpcProvider, } from "starknet"; import { ItemPurchase, MulticallEntry, Stats } from "../type"; import { parseEvents } from "../state/format"; import { useSurvivorStore } from "../state"; export class ExecuteProvider { - private lootSurvivorAddress!: string; - - private defaultAccount?: Account; - - private provider: RpcProvider; - - constructor( - lootSurvivorAddress: string, - provider: RpcProvider, - defaultAccount?: Account - ) { - this.lootSurvivorAddress = lootSurvivorAddress; - - this.defaultAccount = defaultAccount; - - this.provider = provider; - } - - private getAccount(account?: Account): Account { - if (account) return account; - if (this.defaultAccount) return this.defaultAccount; - throw new Error("No account provided"); - } - - private async processAndUpdateState( - receipt: InvokeTransactionReceiptResponse - ) { - const events = parseEvents(receipt as InvokeTransactionReceiptResponse); - - useSurvivorStore.getState().survivor?.updateFromEvents(events); - } - - async newGame( - clientRewardAddress: string, - weapon: number, - name: string, - goldenTokenId: bigint, - vrfFeeLimit: bigint, - account?: Account - ) { - try { - const { transaction_hash } = await this.getAccount(account).execute({ - contractAddress: this.lootSurvivorAddress, - entrypoint: "new_game", - calldata: [ - clientRewardAddress, - weapon, - name, - goldenTokenId, - vrfFeeLimit, - ], - }); - - this.processAndUpdateState( - (await this.provider.waitForTransaction( - transaction_hash - )) as InvokeTransactionReceiptResponse - ); - } catch (e) { - console.error(e); + private lootSurvivorAddress!: string; + + private defaultAccount?: Account; + + private provider: RpcProvider; + + constructor( + lootSurvivorAddress: string, + provider: RpcProvider, + defaultAccount?: Account + ) { + this.lootSurvivorAddress = lootSurvivorAddress; + + this.defaultAccount = defaultAccount; + + this.provider = provider; + } + + private getAccount(account?: Account): Account { + if (account) return account; + if (this.defaultAccount) return this.defaultAccount; + throw new Error("No account provided"); + } + + private async processAndUpdateState( + receipt: InvokeTransactionReceiptResponse + ) { + const events = parseEvents(receipt as InvokeTransactionReceiptResponse); + + useSurvivorStore.getState().survivor?.updateFromEvents(events); } - } - - async explore(adventurerId: string, tillBeast: boolean, account?: Account) { - try { - const { transaction_hash } = await this.getAccount(account).execute({ - contractAddress: this.lootSurvivorAddress, - entrypoint: "explore", - calldata: [adventurerId, tillBeast ? 1 : 0], - }); - this.processAndUpdateState( - (await this.provider.waitForTransaction( - transaction_hash - )) as InvokeTransactionReceiptResponse - ); - } catch (e) { - console.error(e); + + async newGame( + clientRewardAddress: string, + weapon: number, + name: string, + goldenTokenId: bigint, + vrfFeeLimit: bigint, + account?: Account + ) { + try { + const { transaction_hash } = await this.getAccount(account).execute( + { + contractAddress: this.lootSurvivorAddress, + entrypoint: "new_game", + calldata: [ + clientRewardAddress, + weapon, + name, + goldenTokenId, + vrfFeeLimit, + ], + } + ); + + this.processAndUpdateState( + (await this.provider.waitForTransaction( + transaction_hash + )) as InvokeTransactionReceiptResponse + ); + } catch (e) { + console.error(e); + } } - } - - async attack(adventurerId: string, toTheDeath: boolean, account?: Account) { - try { - const { transaction_hash } = await this.getAccount(account).execute({ - contractAddress: this.lootSurvivorAddress, - entrypoint: "attack", - calldata: [adventurerId, toTheDeath ? 1 : 0], - }); - - this.processAndUpdateState( - (await this.provider.waitForTransaction( - transaction_hash - )) as InvokeTransactionReceiptResponse - ); - } catch (e) { - console.error(e); + + async explore(adventurerId: string, tillBeast: boolean, account?: Account) { + try { + const { transaction_hash } = await this.getAccount(account).execute( + { + contractAddress: this.lootSurvivorAddress, + entrypoint: "explore", + calldata: [adventurerId, tillBeast ? 1 : 0], + } + ); + this.processAndUpdateState( + (await this.provider.waitForTransaction( + transaction_hash + )) as InvokeTransactionReceiptResponse + ); + } catch (e) { + console.error(e); + } } - } - - async flee(adventurerId: string, toTheDeath: boolean, account?: Account) { - try { - const { transaction_hash } = await this.getAccount(account).execute({ - contractAddress: this.lootSurvivorAddress, - entrypoint: "flee", - calldata: [adventurerId, toTheDeath ? 1 : 0], - }); - this.processAndUpdateState( - (await this.provider.waitForTransaction( - transaction_hash - )) as InvokeTransactionReceiptResponse - ); - } catch (e) { - console.error(e); + + async attack(adventurerId: string, toTheDeath: boolean, account?: Account) { + try { + const { transaction_hash } = await this.getAccount(account).execute( + { + contractAddress: this.lootSurvivorAddress, + entrypoint: "attack", + calldata: [adventurerId, toTheDeath ? 1 : 0], + } + ); + + this.processAndUpdateState( + (await this.provider.waitForTransaction( + transaction_hash + )) as InvokeTransactionReceiptResponse + ); + } catch (e) { + console.error(e); + } + } + + async flee(adventurerId: string, toTheDeath: boolean, account?: Account) { + try { + const { transaction_hash } = await this.getAccount(account).execute( + { + contractAddress: this.lootSurvivorAddress, + entrypoint: "flee", + calldata: [adventurerId, toTheDeath ? 1 : 0], + } + ); + this.processAndUpdateState( + (await this.provider.waitForTransaction( + transaction_hash + )) as InvokeTransactionReceiptResponse + ); + } catch (e) { + console.error(e); + } } - } - - async equip(adventurerId: string, items: number[], account?: Account) { - try { - const { transaction_hash } = await this.getAccount(account).execute({ - contractAddress: this.lootSurvivorAddress, - entrypoint: "equip", - calldata: [adventurerId, ...items], - }); - this.processAndUpdateState( - (await this.provider.waitForTransaction( - transaction_hash - )) as InvokeTransactionReceiptResponse - ); - } catch (e) { - console.error(e); + + async equip(adventurerId: string, items: number[], account?: Account) { + try { + const { transaction_hash } = await this.getAccount(account).execute( + { + contractAddress: this.lootSurvivorAddress, + entrypoint: "equip", + calldata: [adventurerId, ...items], + } + ); + this.processAndUpdateState( + (await this.provider.waitForTransaction( + transaction_hash + )) as InvokeTransactionReceiptResponse + ); + } catch (e) { + console.error(e); + } } - } - - async drop(adventurerId: string, items: number[], account?: Account) { - try { - const { transaction_hash } = await this.getAccount(account).execute({ - contractAddress: this.lootSurvivorAddress, - entrypoint: "drop", - calldata: [adventurerId, ...items], - }); - this.processAndUpdateState( - (await this.provider.waitForTransaction( - transaction_hash - )) as InvokeTransactionReceiptResponse - ); - } catch (e) { - console.error(e); + + async drop(adventurerId: string, items: number[], account?: Account) { + try { + const { transaction_hash } = await this.getAccount(account).execute( + { + contractAddress: this.lootSurvivorAddress, + entrypoint: "drop", + calldata: [adventurerId, ...items], + } + ); + this.processAndUpdateState( + (await this.provider.waitForTransaction( + transaction_hash + )) as InvokeTransactionReceiptResponse + ); + } catch (e) { + console.error(e); + } } - } - - async upgrade( - adventurerId: string, - potions: number, - statUpgrades: Stats, - items: ItemPurchase[], - account?: Account - ) { - try { - const { transaction_hash } = await this.getAccount(account).execute({ - contractAddress: this.lootSurvivorAddress, - entrypoint: "upgrade", - calldata: [ - adventurerId, - potions, - ...Object.values(statUpgrades), - ...items.flatMap((item) => item), - ], - }); - this.processAndUpdateState( - (await this.provider.waitForTransaction( - transaction_hash - )) as InvokeTransactionReceiptResponse - ); - } catch (e) { - console.error(e); + + async upgrade( + adventurerId: string, + potions: number, + statUpgrades: Stats, + items: ItemPurchase[], + account?: Account + ) { + try { + const { transaction_hash } = await this.getAccount(account).execute( + { + contractAddress: this.lootSurvivorAddress, + entrypoint: "upgrade", + calldata: [ + adventurerId, + potions, + ...Object.values(statUpgrades), + ...items.flatMap((item) => item), + ], + } + ); + this.processAndUpdateState( + (await this.provider.waitForTransaction( + transaction_hash + )) as InvokeTransactionReceiptResponse + ); + } catch (e) { + console.error(e); + } } - } - - async updateCostToPlay(account?: Account) { - try { - const { transaction_hash } = await this.getAccount(account).execute({ - contractAddress: this.lootSurvivorAddress, - entrypoint: "update_cost_to_play", - calldata: [], - }); - this.processAndUpdateState( - (await this.provider.waitForTransaction( - transaction_hash - )) as InvokeTransactionReceiptResponse - ); - } catch (e) { - console.error(e); + + async updateCostToPlay(account?: Account) { + try { + const { transaction_hash } = await this.getAccount(account).execute( + { + contractAddress: this.lootSurvivorAddress, + entrypoint: "update_cost_to_play", + calldata: [], + } + ); + this.processAndUpdateState( + (await this.provider.waitForTransaction( + transaction_hash + )) as InvokeTransactionReceiptResponse + ); + } catch (e) { + console.error(e); + } } - } - - async multiCall(calls: MulticallEntry[], account?: Account) { - try { - const { transaction_hash } = await this.getAccount(account).execute( - calls.map((call) => ({ - contractAddress: this.lootSurvivorAddress, - entrypoint: call.entrypoint, - calldata: call.calldata, - })) - ); - - this.processAndUpdateState( - (await this.provider.waitForTransaction( - transaction_hash - )) as InvokeTransactionReceiptResponse - ); - } catch (e) { - console.error("Multicall error:", e); - throw e; + + async multiCall(calls: MulticallEntry[], account?: Account) { + try { + const { transaction_hash } = await this.getAccount(account).execute( + calls.map((call) => ({ + contractAddress: this.lootSurvivorAddress, + entrypoint: call.entrypoint, + calldata: call.calldata, + })) + ); + + this.processAndUpdateState( + (await this.provider.waitForTransaction( + transaction_hash + )) as InvokeTransactionReceiptResponse + ); + } catch (e) { + console.error("Multicall error:", e); + throw e; + } } - } } diff --git a/packages/core/src/provider/index.ts b/packages/core/src/provider/index.ts index 610357d..333331a 100644 --- a/packages/core/src/provider/index.ts +++ b/packages/core/src/provider/index.ts @@ -3,59 +3,59 @@ import { RpcProvider, Account } from "starknet"; import { ExecuteProvider } from "./execute"; export class LootSurvivor { - // modules that can be read - beasts!: BeastManager; - loot!: LootManager; - prediction!: PredictionManager; - - // modules that can be executed - executeProvider!: ExecuteProvider; - - // addresses - private lootSurvivorAddress!: string; - private beastsAddress!: string; - private goldenTokenAddress!: string; - - // accounts and provider - private provider!: RpcProvider; - private account!: Account | undefined; - - constructor( - nodeUrl: string, - lootSurvivorAddress: string, - beastsAddress: string, - goldenTokenAddress: string, - account?: Account | undefined - ) { - this.lootSurvivorAddress = lootSurvivorAddress; - this.beastsAddress = beastsAddress; - this.goldenTokenAddress = goldenTokenAddress; - - this.provider = new RpcProvider({ nodeUrl }); - this.account = account; - - this.beasts = new BeastManager(); - this.prediction = new PredictionManager(); - this.executeProvider = new ExecuteProvider( - this.lootSurvivorAddress, - this.provider, - this.account - ); - } - - getBeastsAddress(): string { - return this.beastsAddress; - } - - getLootSurvivorAddress(): string { - return this.lootSurvivorAddress; - } - - getGoldenTokenAddress(): string { - return this.goldenTokenAddress; - } - - getProvider(): RpcProvider { - return this.provider; - } + // modules that can be read + beasts!: BeastManager; + loot!: LootManager; + prediction!: PredictionManager; + + // modules that can be executed + executeProvider!: ExecuteProvider; + + // addresses + private lootSurvivorAddress!: string; + private beastsAddress!: string; + private goldenTokenAddress!: string; + + // accounts and provider + private provider!: RpcProvider; + private account!: Account | undefined; + + constructor( + nodeUrl: string, + lootSurvivorAddress: string, + beastsAddress: string, + goldenTokenAddress: string, + account?: Account | undefined + ) { + this.lootSurvivorAddress = lootSurvivorAddress; + this.beastsAddress = beastsAddress; + this.goldenTokenAddress = goldenTokenAddress; + + this.provider = new RpcProvider({ nodeUrl }); + this.account = account; + + this.beasts = new BeastManager(); + this.prediction = new PredictionManager(); + this.executeProvider = new ExecuteProvider( + this.lootSurvivorAddress, + this.provider, + this.account + ); + } + + getBeastsAddress(): string { + return this.beastsAddress; + } + + getLootSurvivorAddress(): string { + return this.lootSurvivorAddress; + } + + getGoldenTokenAddress(): string { + return this.goldenTokenAddress; + } + + getProvider(): RpcProvider { + return this.provider; + } } diff --git a/packages/core/src/query/graphql.ts b/packages/core/src/query/graphql.ts index 8402bf7..64951cb 100644 --- a/packages/core/src/query/graphql.ts +++ b/packages/core/src/query/graphql.ts @@ -170,375 +170,375 @@ const GOLDEN_TOKEN_FRAGMENT = ` `; const getAdventurer = gql` - ${ADVENTURERS_FRAGMENT} - query getAdventurer($owner: HexValue) { - adventurers(where: { owner: { eq: $owner } }) { - ...AdventurerFields + ${ADVENTURERS_FRAGMENT} + query getAdventurer($owner: HexValue) { + adventurers(where: { owner: { eq: $owner } }) { + ...AdventurerFields + } } - } `; const getDiscoveries = gql` - ${DISCOVERIES_FRAGMENT} - query getDiscoveries($id: FeltValue) { - discoveries( - where: { adventurerId: { eq: $id } } - limit: 1000000 - orderBy: { timestamp: { desc: true } } - ) { - ...DiscoveryFields + ${DISCOVERIES_FRAGMENT} + query getDiscoveries($id: FeltValue) { + discoveries( + where: { adventurerId: { eq: $id } } + limit: 1000000 + orderBy: { timestamp: { desc: true } } + ) { + ...DiscoveryFields + } } - } `; const getLatestDiscoveries = gql` - ${DISCOVERIES_FRAGMENT} - query getLatestDiscoveries($id: FeltValue) { - discoveries( - where: { adventurerId: { eq: $id } } - limit: 10 - orderBy: { timestamp: { desc: true } } - ) { - ...DiscoveryFields + ${DISCOVERIES_FRAGMENT} + query getLatestDiscoveries($id: FeltValue) { + discoveries( + where: { adventurerId: { eq: $id } } + limit: 10 + orderBy: { timestamp: { desc: true } } + ) { + ...DiscoveryFields + } } - } `; const getLastDiscovery = gql` - ${DISCOVERIES_FRAGMENT} - query get_last_discovery($adventurerId: FeltValue) { - discoveries( - where: { adventurerId: { eq: $adventurerId } } - limit: 1 - orderBy: { timestamp: { desc: true } } - ) { - ...DiscoveryFields + ${DISCOVERIES_FRAGMENT} + query get_last_discovery($adventurerId: FeltValue) { + discoveries( + where: { adventurerId: { eq: $adventurerId } } + limit: 1 + orderBy: { timestamp: { desc: true } } + ) { + ...DiscoveryFields + } } - } `; const getLastBeastDiscovery = gql` - ${DISCOVERIES_FRAGMENT} - query get_last_beast_query($id: FeltValue) { - discoveries( - where: { adventurerId: { eq: $id }, entityLevel: { gt: 0 } } - limit: 1 - orderBy: { timestamp: { desc: true } } - ) { - ...DiscoveryFields + ${DISCOVERIES_FRAGMENT} + query get_last_beast_query($id: FeltValue) { + discoveries( + where: { adventurerId: { eq: $id }, entityLevel: { gt: 0 } } + limit: 1 + orderBy: { timestamp: { desc: true } } + ) { + ...DiscoveryFields + } } - } `; const getDiscoveryByTxHash = gql` - ${DISCOVERIES_FRAGMENT} - query get_discovery($txHash: HexValue) { - discoveries(where: { txHash: { eq: $txHash } }) { - ...DiscoveryFields + ${DISCOVERIES_FRAGMENT} + query get_discovery($txHash: HexValue) { + discoveries(where: { txHash: { eq: $txHash } }) { + ...DiscoveryFields + } } - } `; const getItems = gql` - ${ITEMS_FRAGMENT} - query get_items { - items { - ...ItemFields + ${ITEMS_FRAGMENT} + query get_items { + items { + ...ItemFields + } } - } `; const getAdventurersByOwner = gql` - ${ADVENTURERS_FRAGMENT} - query get_adventurers_by_owner($owner: HexValue) { - adventurers(where: { owner: { eq: $owner } }, limit: 10000000) { - ...AdventurerFields + ${ADVENTURERS_FRAGMENT} + query get_adventurers_by_owner($owner: HexValue) { + adventurers(where: { owner: { eq: $owner } }, limit: 10000000) { + ...AdventurerFields + } } - } `; const getAdventurerById = gql` - ${ADVENTURERS_FRAGMENT} - query get_adventurer_by_id($id: FeltValue) { - adventurers(where: { id: { eq: $id } }) { - ...AdventurerFields + ${ADVENTURERS_FRAGMENT} + query get_adventurer_by_id($id: FeltValue) { + adventurers(where: { id: { eq: $id } }) { + ...AdventurerFields + } } - } `; const getAdventurersInList = gql` - ${ADVENTURERS_FRAGMENT} - query get_adventurer_by_id($ids: [FeltValue!]) { - adventurers(where: { id: { In: $ids } }, limit: 10000000) { - ...AdventurerFields + ${ADVENTURERS_FRAGMENT} + query get_adventurer_by_id($ids: [FeltValue!]) { + adventurers(where: { id: { In: $ids } }, limit: 10000000) { + ...AdventurerFields + } } - } `; const getAdventurersInListByXp = gql` - ${ADVENTURERS_FRAGMENT} - query get_adventurer_by_id_order_xp($ids: [FeltValue!]) { - adventurers( - where: { id: { In: $ids } } - limit: 10000000 - orderBy: { xp: { desc: true } } - ) { - ...AdventurerFields + ${ADVENTURERS_FRAGMENT} + query get_adventurer_by_id_order_xp($ids: [FeltValue!]) { + adventurers( + where: { id: { In: $ids } } + limit: 10000000 + orderBy: { xp: { desc: true } } + ) { + ...AdventurerFields + } } - } `; const getAdventurerByGold = gql` - ${ADVENTURERS_FRAGMENT} - query get_adventurer_by_gold { - adventurers(orderBy: { gold: { desc: true } }, limit: 10000000) { - ...AdventurerFields + ${ADVENTURERS_FRAGMENT} + query get_adventurer_by_gold { + adventurers(orderBy: { gold: { desc: true } }, limit: 10000000) { + ...AdventurerFields + } } - } `; const getAdventurerByXP = gql` - ${ADVENTURERS_FRAGMENT} - query get_adventurer_by_xp { - adventurers(orderBy: { xp: { desc: true } }, limit: 10000000) { - ...AdventurerFields + ${ADVENTURERS_FRAGMENT} + query get_adventurer_by_xp { + adventurers(orderBy: { xp: { desc: true } }, limit: 10000000) { + ...AdventurerFields + } } - } `; const getAdventurersByXPPaginated = gql` - ${ADVENTURERS_FRAGMENT} - query get_adventurer_by_xp_paginated($skip: Int) { - adventurers(limit: 10, skip: $skip, orderBy: { xp: { desc: true } }) { - ...AdventurerFields + ${ADVENTURERS_FRAGMENT} + query get_adventurer_by_xp_paginated($skip: Int) { + adventurers(limit: 10, skip: $skip, orderBy: { xp: { desc: true } }) { + ...AdventurerFields + } } - } `; const getBeast = gql` - ${BEASTS_FRAGMENT} - query get_beast_by_id( - $beast: BeastValue - $adventurerId: FeltValue - $seed: HexValue - ) { - beasts( - where: { - beast: { eq: $beast } - adventurerId: { eq: $adventurerId } - seed: { eq: $seed } - } + ${BEASTS_FRAGMENT} + query get_beast_by_id( + $beast: BeastValue + $adventurerId: FeltValue + $seed: HexValue ) { - ...BeastFields + beasts( + where: { + beast: { eq: $beast } + adventurerId: { eq: $adventurerId } + seed: { eq: $seed } + } + ) { + ...BeastFields + } } - } `; const getKilledBeasts = gql` - ${BEASTS_FRAGMENT} - query get_killed_beasts { - beasts(where: { health: { eq: 0 } }, limit: 10000000) { - ...BeastFields + ${BEASTS_FRAGMENT} + query get_killed_beasts { + beasts(where: { health: { eq: 0 } }, limit: 10000000) { + ...BeastFields + } } - } `; const getBeastsByAdventurer = gql` - ${DISCOVERIES_FRAGMENT} - query get_beast_by_id($id: FeltValue) { - discoveries(where: { adventurerId: { eq: $id } }) { - ...DiscoveryFields + ${DISCOVERIES_FRAGMENT} + query get_beast_by_id($id: FeltValue) { + discoveries(where: { adventurerId: { eq: $id } }) { + ...DiscoveryFields + } } - } `; const getLatestBattlesByAdventurer = gql` - ${BATTLES_FRAGMENT} - query get_latest_battles($adventurerId: FeltValue) { - battles( - limit: 10 - orderBy: { timestamp: { desc: true } } - where: { adventurerId: { eq: $adventurerId } } - ) { - ...BattleFields + ${BATTLES_FRAGMENT} + query get_latest_battles($adventurerId: FeltValue) { + battles( + limit: 10 + orderBy: { timestamp: { desc: true } } + where: { adventurerId: { eq: $adventurerId } } + ) { + ...BattleFields + } } - } `; const getBattlesByAdventurer = gql` - ${BATTLES_FRAGMENT} - query get_battles($adventurerId: FeltValue) { - battles( - limit: 1000000 - orderBy: { timestamp: { desc: true } } - where: { adventurerId: { eq: $adventurerId } } - ) { - ...BattleFields + ${BATTLES_FRAGMENT} + query get_battles($adventurerId: FeltValue) { + battles( + limit: 1000000 + orderBy: { timestamp: { desc: true } } + where: { adventurerId: { eq: $adventurerId } } + ) { + ...BattleFields + } } - } `; const getBattlesByBeast = gql` - ${BATTLES_FRAGMENT} - query get_battles_by_beast( - $adventurerId: FeltValue - $beast: BeastValue - $seed: HexValue - ) { - battles( - where: { - adventurerId: { eq: $adventurerId } - beast: { eq: $beast } - seed: { eq: $seed } - } - orderBy: { timestamp: { desc: true } } + ${BATTLES_FRAGMENT} + query get_battles_by_beast( + $adventurerId: FeltValue + $beast: BeastValue + $seed: HexValue ) { - ...BattleFields + battles( + where: { + adventurerId: { eq: $adventurerId } + beast: { eq: $beast } + seed: { eq: $seed } + } + orderBy: { timestamp: { desc: true } } + ) { + ...BattleFields + } } - } `; const getLastBattleByAdventurer = gql` - ${BATTLES_FRAGMENT} - query get_latest_battle_by_adventurer($adventurerId: FeltValue) { - battles( - limit: 1 - where: { adventurerId: { eq: $adventurerId } } - orderBy: { timestamp: { desc: true } } - ) { - ...BattleFields + ${BATTLES_FRAGMENT} + query get_latest_battle_by_adventurer($adventurerId: FeltValue) { + battles( + limit: 1 + where: { adventurerId: { eq: $adventurerId } } + orderBy: { timestamp: { desc: true } } + ) { + ...BattleFields + } } - } `; const getBattleByTxHash = gql` - ${BATTLES_FRAGMENT} - query get_latest_battle_by_tx($txHash: HexValue) { - battles( - where: { txHash: { eq: $txHash } } - orderBy: { timestamp: { asc: true } } - ) { - ...BattleFields + ${BATTLES_FRAGMENT} + query get_latest_battle_by_tx($txHash: HexValue) { + battles( + where: { txHash: { eq: $txHash } } + orderBy: { timestamp: { asc: true } } + ) { + ...BattleFields + } } - } `; const getItemsByTokenId = gql` - ${ITEMS_FRAGMENT} - query get_items($item: ItemValue) { - items(where: { item: { eq: $item } }) { - ...ItemFields + ${ITEMS_FRAGMENT} + query get_items($item: ItemValue) { + items(where: { item: { eq: $item } }) { + ...ItemFields + } } - } `; const getLatestMarketItems = gql` - ${ITEMS_FRAGMENT} - query get_latest_market_items($id: FeltValue) { - items( - where: { adventurerId: { eq: $id }, isAvailable: { eq: true } } - limit: 100000 - ) { - ...ItemFields + ${ITEMS_FRAGMENT} + query get_latest_market_items($id: FeltValue) { + items( + where: { adventurerId: { eq: $id }, isAvailable: { eq: true } } + limit: 100000 + ) { + ...ItemFields + } } - } `; const getItemsByAdventurer = gql` - ${ITEMS_FRAGMENT} - query get_items_by_adventurer($id: FeltValue) { - items( - where: { adventurerId: { eq: $id }, owner: { eq: true } } - limit: 10000000 - ) { - ...ItemFields + ${ITEMS_FRAGMENT} + query get_items_by_adventurer($id: FeltValue) { + items( + where: { adventurerId: { eq: $id }, owner: { eq: true } } + limit: 10000000 + ) { + ...ItemFields + } } - } `; const getItemsByOwner = gql` - ${ITEMS_FRAGMENT} - query get_items_by_owner($owner: HexValue) { - items(where: { owner: { eq: $owner } }, limit: 10000000) { - ...ItemFields + ${ITEMS_FRAGMENT} + query get_items_by_owner($owner: HexValue) { + items(where: { owner: { eq: $owner } }, limit: 10000000) { + ...ItemFields + } } - } `; const getTopScores = gql` - ${SCORES_FRAGMENT} - query get_top_scores { - scores(limit: 10000000) { - ...ScoreFields + ${SCORES_FRAGMENT} + query get_top_scores { + scores(limit: 10000000) { + ...ScoreFields + } } - } `; const getScoresAndAdventurers = gql` - ${SCORES_FRAGMENT} - ${ADVENTURERS_FRAGMENT} - query getScoresAndAdventurers { - scores(limit: 10000000, orderBy: { totalPayout: { desc: true } }) { - ...ScoreFields + ${SCORES_FRAGMENT} + ${ADVENTURERS_FRAGMENT} + query getScoresAndAdventurers { + scores(limit: 10000000, orderBy: { totalPayout: { desc: true } }) { + ...ScoreFields + } + adventurers(limit: 10000000, orderBy: { xp: { desc: true } }) { + ...AdventurerFields + } } - adventurers(limit: 10000000, orderBy: { xp: { desc: true } }) { - ...AdventurerFields - } - } `; const getScoresInList = gql` - ${SCORES_FRAGMENT} - query get_top_scores($ids: [FeltValue!]) { - scores(where: { adventurerId: { In: $ids } }, limit: 10000000) { - ...ScoreFields + ${SCORES_FRAGMENT} + query get_top_scores($ids: [FeltValue!]) { + scores(where: { adventurerId: { In: $ids } }, limit: 10000000) { + ...ScoreFields + } } - } `; const getGoldenTokensByOwner = gql` - ${GOLDEN_TOKEN_FRAGMENT} - query getGoldenTokensByOwner($contractAddress: String!, $owner: String!) { - getERC721Tokens( - contract_address: $contractAddress - cursor: 0 - limit: 10000 - owner: $owner - ) { - ...GoldenTokenFields + ${GOLDEN_TOKEN_FRAGMENT} + query getGoldenTokensByOwner($contractAddress: String!, $owner: String!) { + getERC721Tokens( + contract_address: $contractAddress + cursor: 0 + limit: 10000 + owner: $owner + ) { + ...GoldenTokenFields + } } - } `; export { - getAdventurer, - getDiscoveries, - getLatestDiscoveries, - getLastDiscovery, - getLastBeastDiscovery, - getDiscoveryByTxHash, - getAdventurersByOwner, - getAdventurerById, - getAdventurersInList, - getAdventurersInListByXp, - getAdventurerByGold, - getBeastsByAdventurer, - getBeast, - getKilledBeasts, - getLatestBattlesByAdventurer, - getBattlesByBeast, - getLastBattleByAdventurer, - getBattlesByAdventurer, - getBattleByTxHash, - getItems, - getItemsByTokenId, - getLatestMarketItems, - getItemsByOwner, - getItemsByAdventurer, - getAdventurerByXP, - getAdventurersByXPPaginated, - getTopScores, - getScoresInList, - getGoldenTokensByOwner, - getScoresAndAdventurers, + getAdventurer, + getDiscoveries, + getLatestDiscoveries, + getLastDiscovery, + getLastBeastDiscovery, + getDiscoveryByTxHash, + getAdventurersByOwner, + getAdventurerById, + getAdventurersInList, + getAdventurersInListByXp, + getAdventurerByGold, + getBeastsByAdventurer, + getBeast, + getKilledBeasts, + getLatestBattlesByAdventurer, + getBattlesByBeast, + getLastBattleByAdventurer, + getBattlesByAdventurer, + getBattleByTxHash, + getItems, + getItemsByTokenId, + getLatestMarketItems, + getItemsByOwner, + getItemsByAdventurer, + getAdventurerByXP, + getAdventurersByXPPaginated, + getTopScores, + getScoresInList, + getGoldenTokensByOwner, + getScoresAndAdventurers, }; diff --git a/packages/core/src/state/format.ts b/packages/core/src/state/format.ts index e10c28a..80bd1fe 100644 --- a/packages/core/src/state/format.ts +++ b/packages/core/src/state/format.ts @@ -3,842 +3,844 @@ import { Beast, HASHED_SELECTORS, SELECTORS } from "../type"; import * as EventTypes from "../type/events"; export const formatBeastData = ( - event: EventTypes.DiscoveredBeastEvent + event: EventTypes.DiscoveredBeastEvent ): Beast => { - const currentTime = new Date().toISOString(); - return { - adventurerId: event.adventurerState.adventurerId, - beast: `Beast_${event.id}`, // Placeholder name based on ID - createdTime: currentTime, - health: 100, // Placeholder value, should be calculated based on the seed - lastUpdatedTime: currentTime, - level: event.beastSpecs.level, - seed: event.seed.toString(), - slainOnTime: null, - special1: event.beastSpecs.specials.special1, - special2: event.beastSpecs.specials.special2, - special3: event.beastSpecs.specials.special3, - timestamp: currentTime, - }; + const currentTime = new Date().toISOString(); + return { + adventurerId: event.adventurerState.adventurerId, + beast: `Beast_${event.id}`, // Placeholder name based on ID + createdTime: currentTime, + health: 100, // Placeholder value, should be calculated based on the seed + lastUpdatedTime: currentTime, + level: event.beastSpecs.level, + seed: event.seed.toString(), + slainOnTime: null, + special1: event.beastSpecs.specials.special1, + special2: event.beastSpecs.specials.special2, + special3: event.beastSpecs.specials.special3, + timestamp: currentTime, + }; }; export function parseAdventurerState(data: string[]) { - return { - owner: data[0], - adventurerId: parseInt(data[1]), - adventurerEntropy: data[2], - adventurer: { - health: parseInt(data[3]), - xp: parseInt(data[4]), - gold: parseInt(data[5]), - beastHealth: parseInt(data[6]), - statUpgradesAvailable: parseInt(data[7]), - stats: { - strength: parseInt(data[8]), - dexterity: parseInt(data[9]), - vitality: parseInt(data[10]), - intelligence: parseInt(data[11]), - wisdom: parseInt(data[12]), - charisma: parseInt(data[13]), - luck: parseInt(data[14]), - }, - equipment: { - weapon: { - id: parseInt(data[15]), - xp: parseInt(data[16]), - }, - chest: { - id: parseInt(data[17]), - xp: parseInt(data[18]), - }, - head: { - id: parseInt(data[19]), - xp: parseInt(data[20]), - }, - waist: { - id: parseInt(data[21]), - xp: parseInt(data[22]), - }, - foot: { - id: parseInt(data[23]), - xp: parseInt(data[24]), - }, - hand: { - id: parseInt(data[25]), - xp: parseInt(data[26]), - }, - neck: { - id: parseInt(data[27]), - xp: parseInt(data[28]), - }, - ring: { - id: parseInt(data[29]), - xp: parseInt(data[30]), - }, - }, - mutated: convertToBoolean(parseInt(data[31])), - }, - }; + return { + owner: data[0], + adventurerId: parseInt(data[1]), + adventurerEntropy: data[2], + adventurer: { + health: parseInt(data[3]), + xp: parseInt(data[4]), + gold: parseInt(data[5]), + beastHealth: parseInt(data[6]), + statUpgradesAvailable: parseInt(data[7]), + stats: { + strength: parseInt(data[8]), + dexterity: parseInt(data[9]), + vitality: parseInt(data[10]), + intelligence: parseInt(data[11]), + wisdom: parseInt(data[12]), + charisma: parseInt(data[13]), + luck: parseInt(data[14]), + }, + equipment: { + weapon: { + id: parseInt(data[15]), + xp: parseInt(data[16]), + }, + chest: { + id: parseInt(data[17]), + xp: parseInt(data[18]), + }, + head: { + id: parseInt(data[19]), + xp: parseInt(data[20]), + }, + waist: { + id: parseInt(data[21]), + xp: parseInt(data[22]), + }, + foot: { + id: parseInt(data[23]), + xp: parseInt(data[24]), + }, + hand: { + id: parseInt(data[25]), + xp: parseInt(data[26]), + }, + neck: { + id: parseInt(data[27]), + xp: parseInt(data[28]), + }, + ring: { + id: parseInt(data[29]), + xp: parseInt(data[30]), + }, + }, + mutated: convertToBoolean(parseInt(data[31])), + }, + }; } export function convertToBoolean(value: number): boolean { - return value === 1; + return value === 1; } export const parseAdventurerMeta = (data: string[]) => { - return { - startEntropy: data[1], - startingStats: { - strength: parseInt(data[2]), - dexterity: parseInt(data[3]), - vitality: parseInt(data[4]), - intelligence: parseInt(data[5]), - wisdom: parseInt(data[6]), - charisma: parseInt(data[7]), - luck: parseInt(data[8]), - }, - interfaceCamel: convertToBoolean(parseInt(data[9])), - name: parseInt(data[10]), - }; + return { + startEntropy: data[1], + startingStats: { + strength: parseInt(data[2]), + dexterity: parseInt(data[3]), + vitality: parseInt(data[4]), + intelligence: parseInt(data[5]), + wisdom: parseInt(data[6]), + charisma: parseInt(data[7]), + luck: parseInt(data[8]), + }, + interfaceCamel: convertToBoolean(parseInt(data[9])), + name: parseInt(data[10]), + }; }; export function parseBag(data: string[]) { - return { - item1: { - id: parseInt(data[0]), - xp: parseInt(data[1]), - }, - item2: { - id: parseInt(data[2]), - xp: parseInt(data[3]), - }, - item3: { - id: parseInt(data[4]), - xp: parseInt(data[5]), - }, - item4: { - id: parseInt(data[6]), - xp: parseInt(data[7]), - }, - item5: { - id: parseInt(data[8]), - xp: parseInt(data[9]), - }, - item6: { - id: parseInt(data[10]), - xp: parseInt(data[11]), - }, - item7: { - id: parseInt(data[12]), - xp: parseInt(data[13]), - }, - item8: { - id: parseInt(data[14]), - xp: parseInt(data[15]), - }, - item9: { - id: parseInt(data[16]), - xp: parseInt(data[17]), - }, - item10: { - id: parseInt(data[18]), - xp: parseInt(data[19]), - }, - item11: { - id: parseInt(data[20]), - xp: parseInt(data[21]), - }, - item12: { - id: parseInt(data[22]), - xp: parseInt(data[23]), - }, - item13: { - id: parseInt(data[24]), - xp: parseInt(data[25]), - }, - item14: { - id: parseInt(data[26]), - xp: parseInt(data[27]), - }, - item15: { - id: parseInt(data[28]), - xp: parseInt(data[29]), - }, - mutated: convertToBoolean(parseInt(data[30])), - }; + return { + item1: { + id: parseInt(data[0]), + xp: parseInt(data[1]), + }, + item2: { + id: parseInt(data[2]), + xp: parseInt(data[3]), + }, + item3: { + id: parseInt(data[4]), + xp: parseInt(data[5]), + }, + item4: { + id: parseInt(data[6]), + xp: parseInt(data[7]), + }, + item5: { + id: parseInt(data[8]), + xp: parseInt(data[9]), + }, + item6: { + id: parseInt(data[10]), + xp: parseInt(data[11]), + }, + item7: { + id: parseInt(data[12]), + xp: parseInt(data[13]), + }, + item8: { + id: parseInt(data[14]), + xp: parseInt(data[15]), + }, + item9: { + id: parseInt(data[16]), + xp: parseInt(data[17]), + }, + item10: { + id: parseInt(data[18]), + xp: parseInt(data[19]), + }, + item11: { + id: parseInt(data[20]), + xp: parseInt(data[21]), + }, + item12: { + id: parseInt(data[22]), + xp: parseInt(data[23]), + }, + item13: { + id: parseInt(data[24]), + xp: parseInt(data[25]), + }, + item14: { + id: parseInt(data[26]), + xp: parseInt(data[27]), + }, + item15: { + id: parseInt(data[28]), + xp: parseInt(data[29]), + }, + mutated: convertToBoolean(parseInt(data[30])), + }; } export function parseItems(data: string[]) { - const purchases = []; - const chunkedArray = chunkArray(data, 5); - for (let i = 0; i < chunkedArray.length; i++) { - purchases.push({ - item: { - id: parseInt(chunkedArray[i][0]), - tier: parseInt(chunkedArray[i][1]), - itemType: parseInt(chunkedArray[i][2]), - slot: parseInt(chunkedArray[i][3]), - }, - price: parseInt(chunkedArray[i][4]), - }); - } - return purchases; + const purchases = []; + const chunkedArray = chunkArray(data, 5); + for (let i = 0; i < chunkedArray.length; i++) { + purchases.push({ + item: { + id: parseInt(chunkedArray[i][0]), + tier: parseInt(chunkedArray[i][1]), + itemType: parseInt(chunkedArray[i][2]), + slot: parseInt(chunkedArray[i][3]), + }, + price: parseInt(chunkedArray[i][4]), + }); + } + return purchases; } export function parseItemLevels(data: string[]) { - const itemLevels = []; - const chunkedArray = chunkArray(data, 8); - for (let i = 0; i < chunkedArray.length; i++) { - itemLevels.push({ - itemId: parseInt(chunkedArray[i][0]), - previousLevel: parseInt(chunkedArray[i][1]), - newLevel: parseInt(chunkedArray[i][2]), - suffixUnlocked: convertToBoolean(parseInt(chunkedArray[i][3])), - prefixesUnlocked: convertToBoolean(parseInt(chunkedArray[i][4])), - specials: { - special1: parseInt(chunkedArray[i][5]), - special2: parseInt(chunkedArray[i][6]), - special3: parseInt(chunkedArray[i][7]), - }, - }); - } - return itemLevels; + const itemLevels = []; + const chunkedArray = chunkArray(data, 8); + for (let i = 0; i < chunkedArray.length; i++) { + itemLevels.push({ + itemId: parseInt(chunkedArray[i][0]), + previousLevel: parseInt(chunkedArray[i][1]), + newLevel: parseInt(chunkedArray[i][2]), + suffixUnlocked: convertToBoolean(parseInt(chunkedArray[i][3])), + prefixesUnlocked: convertToBoolean(parseInt(chunkedArray[i][4])), + specials: { + special1: parseInt(chunkedArray[i][5]), + special2: parseInt(chunkedArray[i][6]), + special3: parseInt(chunkedArray[i][7]), + }, + }); + } + return itemLevels; } export function parseEquippedItems(data: string[]) { - const equippedLength = parseInt(data[0]); - const equippedItems = []; - const unequippedItems = []; - for (let i = 1; i <= equippedLength; i++) { - equippedItems.push(parseInt(data[i])); - } - const unequippedLength = parseInt(data[equippedLength + 1]); - for (let i = 2; i <= unequippedLength + 1; i++) { - unequippedItems.push(parseInt(data[i + equippedLength])); - } - return { equippedItems, unequippedItems }; + const equippedLength = parseInt(data[0]); + const equippedItems = []; + const unequippedItems = []; + for (let i = 1; i <= equippedLength; i++) { + equippedItems.push(parseInt(data[i])); + } + const unequippedLength = parseInt(data[equippedLength + 1]); + for (let i = 2; i <= unequippedLength + 1; i++) { + unequippedItems.push(parseInt(data[i + equippedLength])); + } + return { equippedItems, unequippedItems }; } export function parseEquipmentChanged(data: string[]) { - const equippedLength = parseInt(data[0]); - const equippedItems = []; - const baggedItems = []; - const droppedItems = []; - for (let i = 1; i <= equippedLength; i++) { - equippedItems.push(parseInt(data[i])); - } - const baggedLength = parseInt(data[equippedLength + 1]); - for (let i = 2; i <= baggedLength + 1; i++) { - baggedItems.push(parseInt(data[i + equippedLength])); - } - const droppedLength = parseInt(data[baggedLength + 1]); - for (let i = 2; i <= droppedLength + 1; i++) { - droppedItems.push(parseInt(data[i + baggedLength])); - } - return { equippedItems, baggedItems, droppedItems }; + const equippedLength = parseInt(data[0]); + const equippedItems = []; + const baggedItems = []; + const droppedItems = []; + for (let i = 1; i <= equippedLength; i++) { + equippedItems.push(parseInt(data[i])); + } + const baggedLength = parseInt(data[equippedLength + 1]); + for (let i = 2; i <= baggedLength + 1; i++) { + baggedItems.push(parseInt(data[i + equippedLength])); + } + const droppedLength = parseInt(data[baggedLength + 1]); + for (let i = 2; i <= droppedLength + 1; i++) { + droppedItems.push(parseInt(data[i + baggedLength])); + } + return { equippedItems, baggedItems, droppedItems }; } export function chunkArray(array: T[], chunkSize: number): T[][] { - const chunks: T[][] = []; - for (let i = 0; i < array?.length; i += chunkSize) { - const nextChunk = array?.slice(i, i + chunkSize); - chunks.push(nextChunk); - if (nextChunk.length < chunkSize) break; // Stop if we've hit the end of the array - } - return chunks; + const chunks: T[][] = []; + for (let i = 0; i < array?.length; i += chunkSize) { + const nextChunk = array?.slice(i, i + chunkSize); + chunks.push(nextChunk); + if (nextChunk.length < chunkSize) break; // Stop if we've hit the end of the array + } + return chunks; } export function parseStartGameEvent(data: string[]): { - name: string; - event: EventTypes.StartGameEvent; + name: string; + event: EventTypes.StartGameEvent; } { - return { - name: SELECTORS.StartGame, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - adventurerMeta: parseAdventurerMeta(data.slice(31, 42)), - revealBlock: parseInt(data[42]), - }, - }; + return { + name: SELECTORS.StartGame, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + adventurerMeta: parseAdventurerMeta(data.slice(31, 42)), + revealBlock: parseInt(data[42]), + }, + }; } export function parseAdventurerUpgradedEvent(data: string[]): { - name: string; - event: EventTypes.AdventurerUpgradedEvent; + name: string; + event: EventTypes.AdventurerUpgradedEvent; } { - return { - name: SELECTORS.AdventurerUpgraded, - event: { - adventurerStateWithBag: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - bag: parseBag(data.slice(32, 63)), - }, - strengthIncrease: parseInt(data[64]), - dexterityIncrease: parseInt(data[65]), - vitalityIncrease: parseInt(data[66]), - intelligenceIncrease: parseInt(data[67]), - wisdomIncrease: parseInt(data[68]), - charismaIncrease: parseInt(data[69]), - }, - }; + return { + name: SELECTORS.AdventurerUpgraded, + event: { + adventurerStateWithBag: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + bag: parseBag(data.slice(32, 63)), + }, + strengthIncrease: parseInt(data[64]), + dexterityIncrease: parseInt(data[65]), + vitalityIncrease: parseInt(data[66]), + intelligenceIncrease: parseInt(data[67]), + wisdomIncrease: parseInt(data[68]), + charismaIncrease: parseInt(data[69]), + }, + }; } export function parseDiscoveredHealthEvent(data: string[]): { - name: string; - event: EventTypes.DiscoveredHealthEvent; + name: string; + event: EventTypes.DiscoveredHealthEvent; } { - return { - name: SELECTORS.DiscoveredHealth, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - healthAmount: parseInt(data[32]), - }, - }; + return { + name: SELECTORS.DiscoveredHealth, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + healthAmount: parseInt(data[32]), + }, + }; } export function parseDiscoveredGoldEvent(data: string[]): { - name: string; - event: EventTypes.DiscoveredGoldEvent; + name: string; + event: EventTypes.DiscoveredGoldEvent; } { - return { - name: SELECTORS.DiscoveredGold, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - goldAmount: parseInt(data[32]), - }, - }; + return { + name: SELECTORS.DiscoveredGold, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + goldAmount: parseInt(data[32]), + }, + }; } export function parseDiscoveredXPEvent(data: string[]): { - name: string; - event: EventTypes.DiscoveredXPEvent; + name: string; + event: EventTypes.DiscoveredXPEvent; } { - return { - name: SELECTORS.DiscoveredXP, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - xpAmount: parseInt(data[32]), - }, - }; + return { + name: SELECTORS.DiscoveredXP, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + xpAmount: parseInt(data[32]), + }, + }; } export function parseDiscoveredLootEvent(data: string[]): { - name: string; - event: EventTypes.DiscoveredLootEvent; + name: string; + event: EventTypes.DiscoveredLootEvent; } { - return { - name: SELECTORS.DiscoveredLoot, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - itemId: parseInt(data[32]), - }, - }; + return { + name: SELECTORS.DiscoveredLoot, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + itemId: parseInt(data[32]), + }, + }; } export function parseEquipmentChangedEvent(data: string[]): { - name: string; - event: EventTypes.EquipmentChangedEvent; + name: string; + event: EventTypes.EquipmentChangedEvent; } { - const { equippedItems, baggedItems, droppedItems } = parseEquipmentChanged( - data.slice(63) - ); - return { - name: SELECTORS.EquipmentChanged, - event: { - adventurerStateWithBag: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - bag: parseBag(data.slice(32, 63)), - }, - equippedItems, - baggedItems, - droppedItems, - }, - }; + const { equippedItems, baggedItems, droppedItems } = parseEquipmentChanged( + data.slice(63) + ); + return { + name: SELECTORS.EquipmentChanged, + event: { + adventurerStateWithBag: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + bag: parseBag(data.slice(32, 63)), + }, + equippedItems, + baggedItems, + droppedItems, + }, + }; } export function parseDodgedObstacleEvent(data: string[]): { - name: string; - event: EventTypes.DodgedObstacleEvent; + name: string; + event: EventTypes.DodgedObstacleEvent; } { - return { - name: SELECTORS.DodgedObstacle, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - id: parseInt(data[32]), - level: parseInt(data[33]), - damageTaken: parseInt(data[34]), - damageLocation: parseInt(data[35]), - xpEarnedAdventurer: parseInt(data[36]), - xpEarnedItems: parseInt(data[37]), - }, - }; + return { + name: SELECTORS.DodgedObstacle, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + id: parseInt(data[32]), + level: parseInt(data[33]), + damageTaken: parseInt(data[34]), + damageLocation: parseInt(data[35]), + xpEarnedAdventurer: parseInt(data[36]), + xpEarnedItems: parseInt(data[37]), + }, + }; } export function parseHitByObstacleEvent(data: string[]): { - name: string; - event: EventTypes.HitByObstacleEvent; + name: string; + event: EventTypes.HitByObstacleEvent; } { - return { - name: SELECTORS.HitByObstacle, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - id: parseInt(data[32]), - level: parseInt(data[33]), - damageTaken: parseInt(data[34]), - damageLocation: parseInt(data[35]), - xpEarnedAdventurer: parseInt(data[36]), - xpEarnedItems: parseInt(data[37]), - }, - }; + return { + name: SELECTORS.HitByObstacle, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + id: parseInt(data[32]), + level: parseInt(data[33]), + damageTaken: parseInt(data[34]), + damageLocation: parseInt(data[35]), + xpEarnedAdventurer: parseInt(data[36]), + xpEarnedItems: parseInt(data[37]), + }, + }; } export function parseDiscoveredBeastEvent(data: string[]): { - name: string; - event: EventTypes.DiscoveredBeastEvent; + name: string; + event: EventTypes.DiscoveredBeastEvent; } { - return { - name: SELECTORS.DiscoveredBeast, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - seed: parseInt(data[32]), - id: parseInt(data[33]), - beastSpecs: { - tier: parseInt(data[34]), - itemType: parseInt(data[35]), - level: parseInt(data[36]), - specials: { - special1: parseInt(data[37]), - special2: parseInt(data[38]), - special3: parseInt(data[39]), - }, - }, - }, - }; + return { + name: SELECTORS.DiscoveredBeast, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + seed: parseInt(data[32]), + id: parseInt(data[33]), + beastSpecs: { + tier: parseInt(data[34]), + itemType: parseInt(data[35]), + level: parseInt(data[36]), + specials: { + special1: parseInt(data[37]), + special2: parseInt(data[38]), + special3: parseInt(data[39]), + }, + }, + }, + }; } export function parseAmbushedByBeastEvent(data: string[]): { - name: string; - event: EventTypes.AmbushedByBeastEvent; + name: string; + event: EventTypes.AmbushedByBeastEvent; } { - return { - name: SELECTORS.AmbushedByBeast, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - seed: parseInt(data[32]), - id: parseInt(data[33]), - beastSpecs: { - tier: parseInt(data[34]), - itemType: parseInt(data[35]), - level: parseInt(data[36]), - specials: { - special1: parseInt(data[37]), - special2: parseInt(data[38]), - special3: parseInt(data[39]), - }, - }, - damage: parseInt(data[40]), - criticalHit: convertToBoolean(parseInt(data[41])), - location: parseInt(data[42]), - }, - }; + return { + name: SELECTORS.AmbushedByBeast, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + seed: parseInt(data[32]), + id: parseInt(data[33]), + beastSpecs: { + tier: parseInt(data[34]), + itemType: parseInt(data[35]), + level: parseInt(data[36]), + specials: { + special1: parseInt(data[37]), + special2: parseInt(data[38]), + special3: parseInt(data[39]), + }, + }, + damage: parseInt(data[40]), + criticalHit: convertToBoolean(parseInt(data[41])), + location: parseInt(data[42]), + }, + }; } export function parseAttackedBeastEvent(data: string[]): { - name: string; - event: EventTypes.AttackedBeastEvent; + name: string; + event: EventTypes.AttackedBeastEvent; } { - return { - name: SELECTORS.AttackedBeast, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - seed: parseInt(data[32]), - id: parseInt(data[33]), - beastSpecs: { - tier: parseInt(data[34]), - itemType: parseInt(data[35]), - level: parseInt(data[36]), - specials: { - special1: parseInt(data[37]), - special2: parseInt(data[38]), - special3: parseInt(data[39]), - }, - }, - damage: parseInt(data[40]), - criticalHit: convertToBoolean(parseInt(data[41])), - location: parseInt(data[42]), - }, - }; + return { + name: SELECTORS.AttackedBeast, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + seed: parseInt(data[32]), + id: parseInt(data[33]), + beastSpecs: { + tier: parseInt(data[34]), + itemType: parseInt(data[35]), + level: parseInt(data[36]), + specials: { + special1: parseInt(data[37]), + special2: parseInt(data[38]), + special3: parseInt(data[39]), + }, + }, + damage: parseInt(data[40]), + criticalHit: convertToBoolean(parseInt(data[41])), + location: parseInt(data[42]), + }, + }; } export function parseAttackedByBeastEvent(data: string[]): { - name: string; - event: EventTypes.AttackedByBeastEvent; + name: string; + event: EventTypes.AttackedByBeastEvent; } { - return { - name: SELECTORS.AttackedByBeast, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - seed: parseInt(data[32]), - id: parseInt(data[33]), - beastSpecs: { - tier: parseInt(data[34]), - itemType: parseInt(data[35]), - level: parseInt(data[36]), - specials: { - special1: parseInt(data[37]), - special2: parseInt(data[38]), - special3: parseInt(data[39]), - }, - }, - damage: parseInt(data[40]), - criticalHit: convertToBoolean(parseInt(data[41])), - location: parseInt(data[42]), - }, - }; + return { + name: SELECTORS.AttackedByBeast, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + seed: parseInt(data[32]), + id: parseInt(data[33]), + beastSpecs: { + tier: parseInt(data[34]), + itemType: parseInt(data[35]), + level: parseInt(data[36]), + specials: { + special1: parseInt(data[37]), + special2: parseInt(data[38]), + special3: parseInt(data[39]), + }, + }, + damage: parseInt(data[40]), + criticalHit: convertToBoolean(parseInt(data[41])), + location: parseInt(data[42]), + }, + }; } export function parseSlayedBeastEvent(data: string[]): { - name: string; - event: EventTypes.SlayedBeastEvent; + name: string; + event: EventTypes.SlayedBeastEvent; } { - return { - name: SELECTORS.SlayedBeast, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - seed: parseInt(data[32]), - id: parseInt(data[33]), - beastSpecs: { - tier: parseInt(data[34]), - itemType: parseInt(data[35]), - level: parseInt(data[36]), - specials: { - special1: parseInt(data[37]), - special2: parseInt(data[38]), - special3: parseInt(data[39]), - }, - }, - damageDealt: parseInt(data[40]), - criticalHit: convertToBoolean(parseInt(data[41])), - xpEarnedAdventurer: parseInt(data[42]), - xpEarnedItems: parseInt(data[43]), - goldEarned: parseInt(data[44]), - }, - }; + return { + name: SELECTORS.SlayedBeast, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + seed: parseInt(data[32]), + id: parseInt(data[33]), + beastSpecs: { + tier: parseInt(data[34]), + itemType: parseInt(data[35]), + level: parseInt(data[36]), + specials: { + special1: parseInt(data[37]), + special2: parseInt(data[38]), + special3: parseInt(data[39]), + }, + }, + damageDealt: parseInt(data[40]), + criticalHit: convertToBoolean(parseInt(data[41])), + xpEarnedAdventurer: parseInt(data[42]), + xpEarnedItems: parseInt(data[43]), + goldEarned: parseInt(data[44]), + }, + }; } export function parseFleeFailedEvent(data: string[]): { - name: string; - event: EventTypes.FleeFailedEvent; + name: string; + event: EventTypes.FleeFailedEvent; } { - return { - name: SELECTORS.FleeFailed, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - seed: parseInt(data[32]), - id: parseInt(data[33]), - beastSpecs: { - tier: parseInt(data[34]), - itemType: parseInt(data[35]), - level: parseInt(data[36]), - specials: { - special1: parseInt(data[37]), - special2: parseInt(data[38]), - special3: parseInt(data[39]), - }, - }, - }, - }; + return { + name: SELECTORS.FleeFailed, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + seed: parseInt(data[32]), + id: parseInt(data[33]), + beastSpecs: { + tier: parseInt(data[34]), + itemType: parseInt(data[35]), + level: parseInt(data[36]), + specials: { + special1: parseInt(data[37]), + special2: parseInt(data[38]), + special3: parseInt(data[39]), + }, + }, + }, + }; } export function parseFleeSucceededEvent(data: string[]): { - name: string; - event: EventTypes.FleeSucceededEvent; + name: string; + event: EventTypes.FleeSucceededEvent; } { - return { - name: SELECTORS.FleeSucceeded, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - seed: parseInt(data[32]), - id: parseInt(data[33]), - beastSpecs: { - tier: parseInt(data[34]), - itemType: parseInt(data[35]), - level: parseInt(data[36]), - specials: { - special1: parseInt(data[37]), - special2: parseInt(data[38]), - special3: parseInt(data[39]), - }, - }, - }, - }; + return { + name: SELECTORS.FleeSucceeded, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + seed: parseInt(data[32]), + id: parseInt(data[33]), + beastSpecs: { + tier: parseInt(data[34]), + itemType: parseInt(data[35]), + level: parseInt(data[36]), + specials: { + special1: parseInt(data[37]), + special2: parseInt(data[38]), + special3: parseInt(data[39]), + }, + }, + }, + }; } export function parsePurchasedItemsEvent(data: string[]): { - name: string; - event: EventTypes.PurchasedItemsEvent; + name: string; + event: EventTypes.PurchasedItemsEvent; } { - return { - name: SELECTORS.PurchasedItems, - event: { - adventurerStateWithBag: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - bag: parseBag(data.slice(32, 63)), - }, - purchases: parseItems(data.slice(64)), - }, - }; + return { + name: SELECTORS.PurchasedItems, + event: { + adventurerStateWithBag: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + bag: parseBag(data.slice(32, 63)), + }, + purchases: parseItems(data.slice(64)), + }, + }; } export function parsePurchasedPotionsEvent(data: string[]): { - name: string; - event: EventTypes.PurchasedPotionsEvent; + name: string; + event: EventTypes.PurchasedPotionsEvent; } { - return { - name: SELECTORS.PurchasedPotions, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - quantity: parseInt(data[32]), - cost: parseInt(data[33]), - health: parseInt(data[34]), - }, - }; + return { + name: SELECTORS.PurchasedPotions, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + quantity: parseInt(data[32]), + cost: parseInt(data[33]), + health: parseInt(data[34]), + }, + }; } export function parseEquippedItemsEvent(data: string[]): { - name: string; - event: EventTypes.EquippedItemsEvent; + name: string; + event: EventTypes.EquippedItemsEvent; } { - const { equippedItems, unequippedItems } = parseEquippedItems(data.slice(63)); - return { - name: SELECTORS.EquippedItems, - event: { - adventurerStateWithBag: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - bag: parseBag(data.slice(32, 63)), - }, - equippedItems, - unequippedItems, - }, - }; + const { equippedItems, unequippedItems } = parseEquippedItems( + data.slice(63) + ); + return { + name: SELECTORS.EquippedItems, + event: { + adventurerStateWithBag: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + bag: parseBag(data.slice(32, 63)), + }, + equippedItems, + unequippedItems, + }, + }; } export function parseDroppedItemsEvent(data: string[]): { - name: string; - event: EventTypes.DroppedItemsEvent; + name: string; + event: EventTypes.DroppedItemsEvent; } { - const itemIds = data.slice(64).map((id) => parseInt(id)); - return { - name: SELECTORS.DroppedItems, - event: { - adventurerStateWithBag: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - bag: parseBag(data.slice(32, 63)), - }, - itemIds, - }, - }; + const itemIds = data.slice(64).map((id) => parseInt(id)); + return { + name: SELECTORS.DroppedItems, + event: { + adventurerStateWithBag: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + bag: parseBag(data.slice(32, 63)), + }, + itemIds, + }, + }; } export function parseGreatnessIncreasedEvent(data: string[]): { - name: string; - event: EventTypes.GreatnessIncreasedEvent; + name: string; + event: EventTypes.GreatnessIncreasedEvent; } { - return { - name: SELECTORS.GreatnessIncreased, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - itemId: parseInt(data[32]), - previousLevel: parseInt(data[33]), - newLevel: parseInt(data[34]), - }, - }; + return { + name: SELECTORS.GreatnessIncreased, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + itemId: parseInt(data[32]), + previousLevel: parseInt(data[33]), + newLevel: parseInt(data[34]), + }, + }; } export function parseItemsLeveledUpEvent(data: string[]): { - name: string; - event: EventTypes.ItemsLeveledUpEvent; + name: string; + event: EventTypes.ItemsLeveledUpEvent; } { - return { - name: SELECTORS.ItemsLeveledUp, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - items: parseItemLevels(data.slice(33)), - }, - }; + return { + name: SELECTORS.ItemsLeveledUp, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + items: parseItemLevels(data.slice(33)), + }, + }; } export function parseNewHighScoreEvent(data: string[]): { - name: string; - event: EventTypes.NewHighScoreEvent; + name: string; + event: EventTypes.NewHighScoreEvent; } { - return { - name: SELECTORS.NewHighScore, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - rank: parseInt(data[32]), - }, - }; + return { + name: SELECTORS.NewHighScore, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + rank: parseInt(data[32]), + }, + }; } export function parseAdventurerDiedEvent(data: string[]): { - name: string; - event: EventTypes.AdventurerDiedEvent; + name: string; + event: EventTypes.AdventurerDiedEvent; } { - return { - name: SELECTORS.AdventurerDied, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - killedByBeast: parseInt(data[32]), - killedByObstacle: parseInt(data[33]), - callerAddress: data[34], - }, - }; + return { + name: SELECTORS.AdventurerDied, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + killedByBeast: parseInt(data[32]), + killedByObstacle: parseInt(data[33]), + callerAddress: data[34], + }, + }; } export function parseAdventurerLeveledUpEvent(data: string[]): { - name: string; - event: EventTypes.AdventurerLeveledUpEvent; + name: string; + event: EventTypes.AdventurerLeveledUpEvent; } { - return { - name: SELECTORS.AdventurerLeveledUp, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - previousLevel: parseInt(data[32]), - newLevel: parseInt(data[33]), - }, - }; + return { + name: SELECTORS.AdventurerLeveledUp, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + previousLevel: parseInt(data[32]), + newLevel: parseInt(data[33]), + }, + }; } export function parseUpgradesAvailableEvent(data: string[]): { - name: string; - event: EventTypes.UpgradesAvailableEvent; + name: string; + event: EventTypes.UpgradesAvailableEvent; } { - const newItemsIds = data.slice(33).map((id) => parseInt(id)); - return { - name: SELECTORS.UpgradesAvailable, - event: { - adventurerState: parseAdventurerState(data.slice(0, 31)), - items: newItemsIds, - }, - }; + const newItemsIds = data.slice(33).map((id) => parseInt(id)); + return { + name: SELECTORS.UpgradesAvailable, + event: { + adventurerState: parseAdventurerState(data.slice(0, 31)), + items: newItemsIds, + }, + }; } export function getKeyFromValue( - data: Record, - value: string + data: Record, + value: string ): string | null { - for (const key in data) { - if (data[key] === value) { - return key; + for (const key in data) { + if (data[key] === value) { + return key; + } } - } - return null; + return null; } export function parseEvents( - receipt: InvokeTransactionReceiptResponse + receipt: InvokeTransactionReceiptResponse ): Array { - let events: Array = []; - if (!receipt.events) { - throw new Error(`No events found`); - } - - for (let raw of receipt.events) { - let eventName: string | null = getKeyFromValue( - HASHED_SELECTORS, - raw.keys[0] - ); - - if (!eventName) { - throw new Error(`Event name not found`); + let events: Array = []; + if (!receipt.events) { + throw new Error(`No events found`); } - switch (eventName) { - case HASHED_SELECTORS.StartGame: - events.push(parseStartGameEvent(raw.keys)); - break; - case HASHED_SELECTORS.AdventurerUpgraded: - events.push(parseAdventurerUpgradedEvent(raw.keys)); - break; - case HASHED_SELECTORS.DiscoveredHealth: - events.push(parseDiscoveredHealthEvent(raw.keys)); - break; - case HASHED_SELECTORS.DiscoveredGold: - events.push(parseDiscoveredGoldEvent(raw.keys)); - break; - case HASHED_SELECTORS.DiscoveredLoot: - events.push(parseDiscoveredLootEvent(raw.keys)); - break; - case HASHED_SELECTORS.DiscoveredXP: - events.push(parseDiscoveredXPEvent(raw.keys)); - break; - case HASHED_SELECTORS.EquipmentChanged: - events.push(parseEquipmentChangedEvent(raw.keys)); - break; - case HASHED_SELECTORS.DodgedObstacle: - events.push(parseDodgedObstacleEvent(raw.keys)); - break; - case HASHED_SELECTORS.HitByObstacle: - events.push(parseHitByObstacleEvent(raw.keys)); - break; - case HASHED_SELECTORS.DiscoveredBeast: - events.push(parseDiscoveredBeastEvent(raw.keys)); - break; - case HASHED_SELECTORS.AmbushedByBeast: - events.push(parseAmbushedByBeastEvent(raw.keys)); - break; - case HASHED_SELECTORS.AttackedBeast: - events.push(parseAttackedBeastEvent(raw.keys)); - break; - case HASHED_SELECTORS.AttackedByBeast: - events.push(parseAttackedByBeastEvent(raw.keys)); - break; - case HASHED_SELECTORS.SlayedBeast: - events.push(parseSlayedBeastEvent(raw.keys)); - break; - case HASHED_SELECTORS.FleeFailed: - events.push(parseFleeFailedEvent(raw.keys)); - break; - case HASHED_SELECTORS.FleeSucceeded: - events.push(parseFleeSucceededEvent(raw.keys)); - break; - case HASHED_SELECTORS.PurchasedItems: - events.push(parsePurchasedItemsEvent(raw.keys)); - break; - case HASHED_SELECTORS.PurchasedPotions: - events.push(parsePurchasedPotionsEvent(raw.keys)); - break; - case HASHED_SELECTORS.EquippedItems: - events.push(parseEquippedItemsEvent(raw.keys)); - break; - case HASHED_SELECTORS.DroppedItems: - events.push(parseDroppedItemsEvent(raw.keys)); - break; - case HASHED_SELECTORS.GreatnessIncreased: - events.push(parseGreatnessIncreasedEvent(raw.keys)); - break; - case HASHED_SELECTORS.ItemsLeveledUp: - events.push(parseItemsLeveledUpEvent(raw.keys)); - break; - case HASHED_SELECTORS.NewHighScore: - events.push(parseNewHighScoreEvent(raw.keys)); - break; - case HASHED_SELECTORS.AdventurerDied: - events.push(parseAdventurerDiedEvent(raw.keys)); - break; - case HASHED_SELECTORS.AdventurerLeveledUp: - events.push(parseAdventurerLeveledUpEvent(raw.keys)); - break; - case HASHED_SELECTORS.UpgradesAvailable: - events.push(parseUpgradesAvailableEvent(raw.keys)); - break; - - // TODO: Add transfer event - default: - throw new Error(`Unknown event name: ${eventName}`); + for (let raw of receipt.events) { + let eventName: string | null = getKeyFromValue( + HASHED_SELECTORS, + raw.keys[0] + ); + + if (!eventName) { + throw new Error(`Event name not found`); + } + + switch (eventName) { + case HASHED_SELECTORS.StartGame: + events.push(parseStartGameEvent(raw.keys)); + break; + case HASHED_SELECTORS.AdventurerUpgraded: + events.push(parseAdventurerUpgradedEvent(raw.keys)); + break; + case HASHED_SELECTORS.DiscoveredHealth: + events.push(parseDiscoveredHealthEvent(raw.keys)); + break; + case HASHED_SELECTORS.DiscoveredGold: + events.push(parseDiscoveredGoldEvent(raw.keys)); + break; + case HASHED_SELECTORS.DiscoveredLoot: + events.push(parseDiscoveredLootEvent(raw.keys)); + break; + case HASHED_SELECTORS.DiscoveredXP: + events.push(parseDiscoveredXPEvent(raw.keys)); + break; + case HASHED_SELECTORS.EquipmentChanged: + events.push(parseEquipmentChangedEvent(raw.keys)); + break; + case HASHED_SELECTORS.DodgedObstacle: + events.push(parseDodgedObstacleEvent(raw.keys)); + break; + case HASHED_SELECTORS.HitByObstacle: + events.push(parseHitByObstacleEvent(raw.keys)); + break; + case HASHED_SELECTORS.DiscoveredBeast: + events.push(parseDiscoveredBeastEvent(raw.keys)); + break; + case HASHED_SELECTORS.AmbushedByBeast: + events.push(parseAmbushedByBeastEvent(raw.keys)); + break; + case HASHED_SELECTORS.AttackedBeast: + events.push(parseAttackedBeastEvent(raw.keys)); + break; + case HASHED_SELECTORS.AttackedByBeast: + events.push(parseAttackedByBeastEvent(raw.keys)); + break; + case HASHED_SELECTORS.SlayedBeast: + events.push(parseSlayedBeastEvent(raw.keys)); + break; + case HASHED_SELECTORS.FleeFailed: + events.push(parseFleeFailedEvent(raw.keys)); + break; + case HASHED_SELECTORS.FleeSucceeded: + events.push(parseFleeSucceededEvent(raw.keys)); + break; + case HASHED_SELECTORS.PurchasedItems: + events.push(parsePurchasedItemsEvent(raw.keys)); + break; + case HASHED_SELECTORS.PurchasedPotions: + events.push(parsePurchasedPotionsEvent(raw.keys)); + break; + case HASHED_SELECTORS.EquippedItems: + events.push(parseEquippedItemsEvent(raw.keys)); + break; + case HASHED_SELECTORS.DroppedItems: + events.push(parseDroppedItemsEvent(raw.keys)); + break; + case HASHED_SELECTORS.GreatnessIncreased: + events.push(parseGreatnessIncreasedEvent(raw.keys)); + break; + case HASHED_SELECTORS.ItemsLeveledUp: + events.push(parseItemsLeveledUpEvent(raw.keys)); + break; + case HASHED_SELECTORS.NewHighScore: + events.push(parseNewHighScoreEvent(raw.keys)); + break; + case HASHED_SELECTORS.AdventurerDied: + events.push(parseAdventurerDiedEvent(raw.keys)); + break; + case HASHED_SELECTORS.AdventurerLeveledUp: + events.push(parseAdventurerLeveledUpEvent(raw.keys)); + break; + case HASHED_SELECTORS.UpgradesAvailable: + events.push(parseUpgradesAvailableEvent(raw.keys)); + break; + + // TODO: Add transfer event + default: + throw new Error(`Unknown event name: ${eventName}`); + } } - } - return events; + return events; } diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index 0a1090b..a289250 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -10,27 +10,27 @@ import { devtools } from "zustand/middleware"; import { Survivor } from "../objects/survivor"; export interface SurvivorStore { - survivor: Survivor | null; - updateSurvivor: (updates: Survivor) => void; - clearSurvivor: () => void; - newSurvivor: () => void; + survivor: Survivor | null; + updateSurvivor: (updates: Survivor) => void; + clearSurvivor: () => void; + newSurvivor: () => void; } export const useSurvivorStore = createStore()( - devtools( - (set) => ({ - survivor: null, - newSurvivor: () => { - set(() => { - return { survivor: new Survivor() }; - }); - }, - clearSurvivor: () => set({ survivor: null }), - updateSurvivor: (survivor: Survivor) => - set(() => { - return { survivor }; + devtools( + (set) => ({ + survivor: null, + newSurvivor: () => { + set(() => { + return { survivor: new Survivor() }; + }); + }, + clearSurvivor: () => set({ survivor: null }), + updateSurvivor: (survivor: Survivor) => + set(() => { + return { survivor }; + }), }), - }), - { name: "Survivor Store" } - ) + { name: "Survivor Store" } + ) ); diff --git a/packages/core/src/state/mock.ts b/packages/core/src/state/mock.ts index 145a100..ca1355e 100644 --- a/packages/core/src/state/mock.ts +++ b/packages/core/src/state/mock.ts @@ -2,485 +2,485 @@ import { shortString } from "starknet"; import * as EventTypes from "../type/events"; const encodeEventData = (event: any): string[] => { - return Object.values(event).flatMap((value) => { - if (typeof value === "object" && value !== null) { - return encodeEventData(value); - } - if (typeof value === "number") { - return ["0x" + value.toString(16)]; - } - if (typeof value === "boolean") { - return [value ? "0x1" : "0x0"]; - } - if (typeof value === "string" && value.startsWith("0x")) { - return [value]; - } - return [shortString.encodeShortString((value as any).toString())]; - }); + return Object.values(event).flatMap((value) => { + if (typeof value === "object" && value !== null) { + return encodeEventData(value); + } + if (typeof value === "number") { + return ["0x" + value.toString(16)]; + } + if (typeof value === "boolean") { + return [value ? "0x1" : "0x0"]; + } + if (typeof value === "string" && value.startsWith("0x")) { + return [value]; + } + return [shortString.encodeShortString((value as any).toString())]; + }); }; const createBagState = (itemIds: number[]): EventTypes.Bag => { - const bag: EventTypes.Bag = { - item1: { id: 0, xp: 0 }, - item2: { id: 0, xp: 0 }, - item3: { id: 0, xp: 0 }, - item4: { id: 0, xp: 0 }, - item5: { id: 0, xp: 0 }, - item6: { id: 0, xp: 0 }, - item7: { id: 0, xp: 0 }, - item8: { id: 0, xp: 0 }, - item9: { id: 0, xp: 0 }, - item10: { id: 0, xp: 0 }, - item11: { id: 0, xp: 0 }, - item12: { id: 0, xp: 0 }, - item13: { id: 0, xp: 0 }, - item14: { id: 0, xp: 0 }, - item15: { id: 0, xp: 0 }, - mutated: false, - }; - - itemIds.forEach((id, index) => { - if (index < 15) { - const key = `item${index + 1}` as keyof EventTypes.Bag; - (bag[key] as any) = { id, xp: 0 }; - } - }); - return bag; + const bag: EventTypes.Bag = { + item1: { id: 0, xp: 0 }, + item2: { id: 0, xp: 0 }, + item3: { id: 0, xp: 0 }, + item4: { id: 0, xp: 0 }, + item5: { id: 0, xp: 0 }, + item6: { id: 0, xp: 0 }, + item7: { id: 0, xp: 0 }, + item8: { id: 0, xp: 0 }, + item9: { id: 0, xp: 0 }, + item10: { id: 0, xp: 0 }, + item11: { id: 0, xp: 0 }, + item12: { id: 0, xp: 0 }, + item13: { id: 0, xp: 0 }, + item14: { id: 0, xp: 0 }, + item15: { id: 0, xp: 0 }, + mutated: false, + }; + + itemIds.forEach((id, index) => { + if (index < 15) { + const key = `item${index + 1}` as keyof EventTypes.Bag; + (bag[key] as any) = { id, xp: 0 }; + } + }); + return bag; }; // Helper function to create a basic AdventurerState const createBasicAdventurerState = ( - id: number + id: number ): EventTypes.AdventurerState => ({ - owner: "0x1234567890123456789012345678901234567890", - adventurerId: id, - adventurerEntropy: "someEntropyString", - adventurer: { - health: 100, - xp: 0, - gold: 0, - beastHealth: 0, - statUpgradesAvailable: 0, - stats: { - strength: 10, - dexterity: 10, - vitality: 10, - intelligence: 10, - wisdom: 10, - charisma: 10, - luck: 10, - }, - equipment: { - weapon: { id: 1, xp: 0 }, - chest: { id: 2, xp: 0 }, - head: { id: 3, xp: 0 }, - waist: { id: 4, xp: 0 }, - foot: { id: 5, xp: 0 }, - hand: { id: 6, xp: 0 }, - neck: { id: 7, xp: 0 }, - ring: { id: 8, xp: 0 }, + owner: "0x1234567890123456789012345678901234567890", + adventurerId: id, + adventurerEntropy: "someEntropyString", + adventurer: { + health: 100, + xp: 0, + gold: 0, + beastHealth: 0, + statUpgradesAvailable: 0, + stats: { + strength: 10, + dexterity: 10, + vitality: 10, + intelligence: 10, + wisdom: 10, + charisma: 10, + luck: 10, + }, + equipment: { + weapon: { id: 1, xp: 0 }, + chest: { id: 2, xp: 0 }, + head: { id: 3, xp: 0 }, + waist: { id: 4, xp: 0 }, + foot: { id: 5, xp: 0 }, + hand: { id: 6, xp: 0 }, + neck: { id: 7, xp: 0 }, + ring: { id: 8, xp: 0 }, + }, + mutated: false, }, - mutated: false, - }, }); // Create mock events for all event types export const mockStartGameEvent: EventTypes.StartGameEvent = { - adventurerState: createBasicAdventurerState(1), - adventurerMeta: { - startEntropy: "startEntropyString", - startingStats: { - strength: 10, - dexterity: 10, - vitality: 10, - intelligence: 10, - wisdom: 10, - charisma: 10, - luck: 10, + adventurerState: createBasicAdventurerState(1), + adventurerMeta: { + startEntropy: "startEntropyString", + startingStats: { + strength: 10, + dexterity: 10, + vitality: 10, + intelligence: 10, + wisdom: 10, + charisma: 10, + luck: 10, + }, + interfaceCamel: true, + name: 123456789, }, - interfaceCamel: true, - name: 123456789, - }, - revealBlock: 1000000, + revealBlock: 1000000, }; export const mockDiscoveredHealthEvent: EventTypes.DiscoveredHealthEvent = { - adventurerState: { - ...createBasicAdventurerState(1), - adventurer: { - ...createBasicAdventurerState(1).adventurer, - health: createBasicAdventurerState(1).adventurer.health + 20, + adventurerState: { + ...createBasicAdventurerState(1), + adventurer: { + ...createBasicAdventurerState(1).adventurer, + health: createBasicAdventurerState(1).adventurer.health + 20, + }, }, - }, - healthAmount: 20, + healthAmount: 20, }; export const mockDiscoveredGoldEvent: EventTypes.DiscoveredGoldEvent = { - adventurerState: { - ...createBasicAdventurerState(1), - adventurer: { - ...createBasicAdventurerState(1).adventurer, - gold: createBasicAdventurerState(1).adventurer.gold + 50, + adventurerState: { + ...createBasicAdventurerState(1), + adventurer: { + ...createBasicAdventurerState(1).adventurer, + gold: createBasicAdventurerState(1).adventurer.gold + 50, + }, }, - }, - goldAmount: 50, + goldAmount: 50, }; export const mockDiscoveredXPEvent: EventTypes.DiscoveredXPEvent = { - adventurerState: { - ...createBasicAdventurerState(1), - adventurer: { - ...createBasicAdventurerState(1).adventurer, - xp: createBasicAdventurerState(1).adventurer.xp + 100, + adventurerState: { + ...createBasicAdventurerState(1), + adventurer: { + ...createBasicAdventurerState(1).adventurer, + xp: createBasicAdventurerState(1).adventurer.xp + 100, + }, }, - }, - xpAmount: 100, + xpAmount: 100, }; export const mockDiscoveredLootEvent: EventTypes.DiscoveredLootEvent = { - adventurerState: createBasicAdventurerState(1), - itemId: 5, + adventurerState: createBasicAdventurerState(1), + itemId: 5, }; export const mockEquipmentChangedEvent: EventTypes.EquipmentChangedEvent = { - adventurerStateWithBag: { - adventurerState: { - ...createBasicAdventurerState(1), - adventurer: { - ...createBasicAdventurerState(1).adventurer, - equipment: { - ...createBasicAdventurerState(1).adventurer.equipment, - weapon: { id: 1, xp: 0 }, - chest: { id: 2, xp: 0 }, - head: { id: 3, xp: 0 }, + adventurerStateWithBag: { + adventurerState: { + ...createBasicAdventurerState(1), + adventurer: { + ...createBasicAdventurerState(1).adventurer, + equipment: { + ...createBasicAdventurerState(1).adventurer.equipment, + weapon: { id: 1, xp: 0 }, + chest: { id: 2, xp: 0 }, + head: { id: 3, xp: 0 }, + }, + }, + }, + bag: { + ...createBagState([1, 2, 3, 4]), + item1: { id: 4, xp: 0 }, + item2: { id: 5, xp: 0 }, }, - }, - }, - bag: { - ...createBagState([1, 2, 3, 4]), - item1: { id: 4, xp: 0 }, - item2: { id: 5, xp: 0 }, }, - }, - equippedItems: [1, 2, 3], - baggedItems: [4, 5], - droppedItems: [6, 7], + equippedItems: [1, 2, 3], + baggedItems: [4, 5], + droppedItems: [6, 7], }; export const mockDodgedObstacleEvent: EventTypes.DodgedObstacleEvent = { - adventurerState: createBasicAdventurerState(1), - id: 3, - level: 2, - damageTaken: 5, - damageLocation: 1, - xpEarnedAdventurer: 10, - xpEarnedItems: 5, + adventurerState: createBasicAdventurerState(1), + id: 3, + level: 2, + damageTaken: 5, + damageLocation: 1, + xpEarnedAdventurer: 10, + xpEarnedItems: 5, }; export const mockHitByObstacleEvent: EventTypes.HitByObstacleEvent = { - adventurerState: createBasicAdventurerState(1), - id: 4, - level: 3, - damageTaken: 15, - damageLocation: 2, - xpEarnedAdventurer: 20, - xpEarnedItems: 10, + adventurerState: createBasicAdventurerState(1), + id: 4, + level: 3, + damageTaken: 15, + damageLocation: 2, + xpEarnedAdventurer: 20, + xpEarnedItems: 10, }; export const mockAmbushedByBeastEvent: EventTypes.AmbushedByBeastEvent = { - adventurerState: createBasicAdventurerState(1), - seed: 987654321, - id: 5, - beastSpecs: { - tier: EventTypes.Tier.T2, - itemType: EventTypes.Type.Blade_or_Hide, - level: 3, - specials: { special1: 1, special2: 2, special3: 3 }, - }, - damage: 15, - criticalHit: false, - location: 2, + adventurerState: createBasicAdventurerState(1), + seed: 987654321, + id: 5, + beastSpecs: { + tier: EventTypes.Tier.T2, + itemType: EventTypes.Type.Blade_or_Hide, + level: 3, + specials: { special1: 1, special2: 2, special3: 3 }, + }, + damage: 15, + criticalHit: false, + location: 2, }; export const mockDiscoveredBeastEvent: EventTypes.DiscoveredBeastEvent = { - adventurerState: createBasicAdventurerState(1), - seed: 123456789, - id: 6, - beastSpecs: { - tier: EventTypes.Tier.T3, - itemType: EventTypes.Type.Magic_or_Cloth, - level: 4, - specials: { special1: 2, special2: 3, special3: 4 }, - }, + adventurerState: createBasicAdventurerState(1), + seed: 123456789, + id: 6, + beastSpecs: { + tier: EventTypes.Tier.T3, + itemType: EventTypes.Type.Magic_or_Cloth, + level: 4, + specials: { special1: 2, special2: 3, special3: 4 }, + }, }; export const mockAttackedBeastEvent: EventTypes.AttackedBeastEvent = { - adventurerState: createBasicAdventurerState(1), - seed: 246813579, - id: 7, - beastSpecs: { - tier: EventTypes.Tier.T4, - itemType: EventTypes.Type.Bludgeon_or_Metal, - level: 5, - specials: { special1: 3, special2: 4, special3: 5 }, - }, - damage: 25, - criticalHit: true, - location: 3, + adventurerState: createBasicAdventurerState(1), + seed: 246813579, + id: 7, + beastSpecs: { + tier: EventTypes.Tier.T4, + itemType: EventTypes.Type.Bludgeon_or_Metal, + level: 5, + specials: { special1: 3, special2: 4, special3: 5 }, + }, + damage: 25, + criticalHit: true, + location: 3, }; export const mockAttackedByBeastEvent: EventTypes.AttackedByBeastEvent = { - adventurerState: createBasicAdventurerState(1), - seed: 135792468, - id: 8, - beastSpecs: { - tier: EventTypes.Tier.T5, - itemType: EventTypes.Type.Necklace, - level: 6, - specials: { special1: 4, special2: 5, special3: 6 }, - }, - damage: 30, - criticalHit: false, - location: 4, + adventurerState: createBasicAdventurerState(1), + seed: 135792468, + id: 8, + beastSpecs: { + tier: EventTypes.Tier.T5, + itemType: EventTypes.Type.Necklace, + level: 6, + specials: { special1: 4, special2: 5, special3: 6 }, + }, + damage: 30, + criticalHit: false, + location: 4, }; export const mockSlayedBeastEvent: EventTypes.SlayedBeastEvent = { - adventurerState: createBasicAdventurerState(1), - seed: 369258147, - id: 9, - beastSpecs: { - tier: EventTypes.Tier.T5, - itemType: EventTypes.Type.Ring, - level: 7, - specials: { special1: 5, special2: 6, special3: 7 }, - }, - damageDealt: 50, - criticalHit: true, - xpEarnedAdventurer: 200, - xpEarnedItems: 100, - goldEarned: 75, + adventurerState: createBasicAdventurerState(1), + seed: 369258147, + id: 9, + beastSpecs: { + tier: EventTypes.Tier.T5, + itemType: EventTypes.Type.Ring, + level: 7, + specials: { special1: 5, special2: 6, special3: 7 }, + }, + damageDealt: 50, + criticalHit: true, + xpEarnedAdventurer: 200, + xpEarnedItems: 100, + goldEarned: 75, }; export const mockFleeFailedEvent: EventTypes.FleeFailedEvent = { - adventurerState: createBasicAdventurerState(1), - seed: 951753852, - id: 10, - beastSpecs: { - tier: EventTypes.Tier.T3, - itemType: EventTypes.Type.Blade_or_Hide, - level: 4, - specials: { special1: 2, special2: 3, special3: 4 }, - }, + adventurerState: createBasicAdventurerState(1), + seed: 951753852, + id: 10, + beastSpecs: { + tier: EventTypes.Tier.T3, + itemType: EventTypes.Type.Blade_or_Hide, + level: 4, + specials: { special1: 2, special2: 3, special3: 4 }, + }, }; export const mockFleeSucceededEvent: EventTypes.FleeSucceededEvent = { - adventurerState: createBasicAdventurerState(1), - seed: 753951852, - id: 11, - beastSpecs: { - tier: EventTypes.Tier.T4, - itemType: EventTypes.Type.Magic_or_Cloth, - level: 5, - specials: { special1: 3, special2: 4, special3: 5 }, - }, + adventurerState: createBasicAdventurerState(1), + seed: 753951852, + id: 11, + beastSpecs: { + tier: EventTypes.Tier.T4, + itemType: EventTypes.Type.Magic_or_Cloth, + level: 5, + specials: { special1: 3, special2: 4, special3: 5 }, + }, }; export const mockPurchasedItemsEvent: EventTypes.PurchasedItemsEvent = { - adventurerStateWithBag: { - adventurerState: createBasicAdventurerState(1), - bag: createBagState([1, 2]), // Add a proper bag structure if needed - }, - purchases: [ - { - item: { - id: 1, - tier: EventTypes.Tier.T1, - itemType: EventTypes.Type.Blade_or_Hide, - slot: EventTypes.Slot.Weapon, - }, - price: 100, - }, - { - item: { - id: 2, - tier: EventTypes.Tier.T2, - itemType: EventTypes.Type.Magic_or_Cloth, - slot: EventTypes.Slot.Chest, - }, - price: 150, + adventurerStateWithBag: { + adventurerState: createBasicAdventurerState(1), + bag: createBagState([1, 2]), // Add a proper bag structure if needed }, - ], + purchases: [ + { + item: { + id: 1, + tier: EventTypes.Tier.T1, + itemType: EventTypes.Type.Blade_or_Hide, + slot: EventTypes.Slot.Weapon, + }, + price: 100, + }, + { + item: { + id: 2, + tier: EventTypes.Tier.T2, + itemType: EventTypes.Type.Magic_or_Cloth, + slot: EventTypes.Slot.Chest, + }, + price: 150, + }, + ], }; export const mockPurchasedPotionsEvent: EventTypes.PurchasedPotionsEvent = { - adventurerState: createBasicAdventurerState(1), - quantity: 3, - cost: 75, - health: 50, + adventurerState: createBasicAdventurerState(1), + quantity: 3, + cost: 75, + health: 50, }; export const mockEquippedItemsEvent: EventTypes.EquippedItemsEvent = { - adventurerStateWithBag: { - adventurerState: createBasicAdventurerState(1), - bag: createBagState([1, 2, 3]), // Add a proper bag structure if needed - }, - equippedItems: [1, 2, 3], - unequippedItems: [4, 5], + adventurerStateWithBag: { + adventurerState: createBasicAdventurerState(1), + bag: createBagState([1, 2, 3]), // Add a proper bag structure if needed + }, + equippedItems: [1, 2, 3], + unequippedItems: [4, 5], }; export const mockDroppedItemsEvent: EventTypes.DroppedItemsEvent = { - adventurerStateWithBag: { - adventurerState: createBasicAdventurerState(1), - bag: createBagState([1, 2, 3]), // Add a proper bag structure if needed - }, - itemIds: [6, 7, 8], + adventurerStateWithBag: { + adventurerState: createBasicAdventurerState(1), + bag: createBagState([1, 2, 3]), // Add a proper bag structure if needed + }, + itemIds: [6, 7, 8], }; export const mockGreatnessIncreasedEvent: EventTypes.GreatnessIncreasedEvent = { - adventurerState: createBasicAdventurerState(1), - itemId: 3, - previousLevel: 2, - newLevel: 3, + adventurerState: createBasicAdventurerState(1), + itemId: 3, + previousLevel: 2, + newLevel: 3, }; export const mockItemsLeveledUpEvent: EventTypes.ItemsLeveledUpEvent = { - adventurerState: createBasicAdventurerState(1), - items: [ - { - itemId: 1, - previousLevel: 1, - newLevel: 2, - suffixUnlocked: true, - prefixesUnlocked: false, - specials: { special1: 1, special2: 2, special3: 3 }, - }, - { - itemId: 2, - previousLevel: 2, - newLevel: 3, - suffixUnlocked: false, - prefixesUnlocked: true, - specials: { special1: 2, special2: 3, special3: 4 }, - }, - ], + adventurerState: createBasicAdventurerState(1), + items: [ + { + itemId: 1, + previousLevel: 1, + newLevel: 2, + suffixUnlocked: true, + prefixesUnlocked: false, + specials: { special1: 1, special2: 2, special3: 3 }, + }, + { + itemId: 2, + previousLevel: 2, + newLevel: 3, + suffixUnlocked: false, + prefixesUnlocked: true, + specials: { special1: 2, special2: 3, special3: 4 }, + }, + ], }; export const mockNewHighScoreEvent: EventTypes.NewHighScoreEvent = { - adventurerState: createBasicAdventurerState(1), - rank: 5, + adventurerState: createBasicAdventurerState(1), + rank: 5, }; export const mockAdventurerDiedEvent: EventTypes.AdventurerDiedEvent = { - adventurerState: createBasicAdventurerState(1), - killedByBeast: 7, - killedByObstacle: 0, - callerAddress: "0x9876543210987654321098765432109876543210", + adventurerState: createBasicAdventurerState(1), + killedByBeast: 7, + killedByObstacle: 0, + callerAddress: "0x9876543210987654321098765432109876543210", }; export const mockAdventurerLeveledUpEvent: EventTypes.AdventurerLeveledUpEvent = - { - adventurerState: createBasicAdventurerState(1), - previousLevel: 1, - newLevel: 2, - }; + { + adventurerState: createBasicAdventurerState(1), + previousLevel: 1, + newLevel: 2, + }; export const mockUpgradesAvailableEvent: EventTypes.UpgradesAvailableEvent = { - adventurerState: createBasicAdventurerState(1), - items: [1, 2, 3], + adventurerState: createBasicAdventurerState(1), + items: [1, 2, 3], }; export const mockAdventurerUpgradedEvent: EventTypes.AdventurerUpgradedEvent = { - adventurerStateWithBag: { - adventurerState: createBasicAdventurerState(1), - bag: createBagState([1, 2, 3]), // Add a proper bag structure if needed - }, - strengthIncrease: 1, - dexterityIncrease: 1, - vitalityIncrease: 1, - intelligenceIncrease: 1, - wisdomIncrease: 1, - charismaIncrease: 1, + adventurerStateWithBag: { + adventurerState: createBasicAdventurerState(1), + bag: createBagState([1, 2, 3]), // Add a proper bag structure if needed + }, + strengthIncrease: 1, + dexterityIncrease: 1, + vitalityIncrease: 1, + intelligenceIncrease: 1, + wisdomIncrease: 1, + charismaIncrease: 1, }; export const mockERC721TransferEvent: EventTypes.ERC721TransferEvent = { - from: "0x1111111111111111111111111111111111111111", - to: "0x2222222222222222222222222222222222222222", - tokenId: { low: 123, high: 0 }, + from: "0x1111111111111111111111111111111111111111", + to: "0x2222222222222222222222222222222222222222", + tokenId: { low: 123, high: 0 }, }; export const encodedMockStartGameEvent = encodeEventData(mockStartGameEvent); export const encodedMockDiscoveredHealthEvent = encodeEventData( - mockDiscoveredHealthEvent + mockDiscoveredHealthEvent ); export const encodedMockDiscoveredGoldEvent = encodeEventData( - mockDiscoveredGoldEvent + mockDiscoveredGoldEvent ); export const encodedMockDiscoveredXPEvent = encodeEventData( - mockDiscoveredXPEvent + mockDiscoveredXPEvent ); export const encodedMockDiscoveredLootEvent = encodeEventData( - mockDiscoveredLootEvent + mockDiscoveredLootEvent ); export const encodedMockEquipmentChangedEvent = encodeEventData( - mockEquipmentChangedEvent + mockEquipmentChangedEvent ); export const encodedMockDodgedObstacleEvent = encodeEventData( - mockDodgedObstacleEvent + mockDodgedObstacleEvent ); export const encodedMockHitByObstacleEvent = encodeEventData( - mockHitByObstacleEvent + mockHitByObstacleEvent ); export const encodedMockAmbushedByBeastEvent = encodeEventData( - mockAmbushedByBeastEvent + mockAmbushedByBeastEvent ); export const encodedMockDiscoveredBeastEvent = encodeEventData( - mockDiscoveredBeastEvent + mockDiscoveredBeastEvent ); export const encodedMockAttackedBeastEvent = encodeEventData( - mockAttackedBeastEvent + mockAttackedBeastEvent ); export const encodedMockAttackedByBeastEvent = encodeEventData( - mockAttackedByBeastEvent + mockAttackedByBeastEvent ); export const encodedMockSlayedBeastEvent = - encodeEventData(mockSlayedBeastEvent); + encodeEventData(mockSlayedBeastEvent); export const encodedMockFleeFailedEvent = encodeEventData(mockFleeFailedEvent); export const encodedMockFleeSucceededEvent = encodeEventData( - mockFleeSucceededEvent + mockFleeSucceededEvent ); export const encodedMockPurchasedItemsEvent = encodeEventData( - mockPurchasedItemsEvent + mockPurchasedItemsEvent ); export const encodedMockPurchasedPotionsEvent = encodeEventData( - mockPurchasedPotionsEvent + mockPurchasedPotionsEvent ); export const encodedMockEquippedItemsEvent = encodeEventData( - mockEquippedItemsEvent + mockEquippedItemsEvent ); export const encodedMockDroppedItemsEvent = encodeEventData( - mockDroppedItemsEvent + mockDroppedItemsEvent ); export const encodedMockGreatnessIncreasedEvent = encodeEventData( - mockGreatnessIncreasedEvent + mockGreatnessIncreasedEvent ); export const encodedMockItemsLeveledUpEvent = encodeEventData( - mockItemsLeveledUpEvent + mockItemsLeveledUpEvent ); export const encodedMockNewHighScoreEvent = encodeEventData( - mockNewHighScoreEvent + mockNewHighScoreEvent ); export const encodedMockAdventurerDiedEvent = encodeEventData( - mockAdventurerDiedEvent + mockAdventurerDiedEvent ); export const encodedMockAdventurerLeveledUpEvent = encodeEventData( - mockAdventurerLeveledUpEvent + mockAdventurerLeveledUpEvent ); export const encodedMockUpgradesAvailableEvent = encodeEventData( - mockUpgradesAvailableEvent + mockUpgradesAvailableEvent ); export const encodedMockAdventurerUpgradedEvent = encodeEventData( - mockAdventurerUpgradedEvent + mockAdventurerUpgradedEvent ); export const encodedMockERC721TransferEvent = encodeEventData( - mockERC721TransferEvent + mockERC721TransferEvent ); diff --git a/packages/core/src/state/state.test.ts b/packages/core/src/state/state.test.ts index 05def26..fa12f9b 100644 --- a/packages/core/src/state/state.test.ts +++ b/packages/core/src/state/state.test.ts @@ -1,11 +1,11 @@ import { describe, it, expect, beforeEach, vi } from "vitest"; import { - formatBeastData, - parseAdventurerMeta, - parseAdventurerState, - parseBag, - parseStartGameEvent, + formatBeastData, + parseAdventurerMeta, + parseAdventurerState, + parseBag, + parseStartGameEvent, } from "./format"; import * as mockEvents from "./mock"; @@ -13,554 +13,554 @@ import { useSurvivorStore } from "."; import { Survivor } from "../objects/survivor"; describe("updateState with events", () => { - beforeEach(() => { - useSurvivorStore.setState({ survivor: new Survivor() }); - }); + beforeEach(() => { + useSurvivorStore.setState({ survivor: new Survivor() }); + }); - it("should update state with StartGameEvent", () => { - const { survivor } = useSurvivorStore.getState(); + it("should update state with StartGameEvent", () => { + const { survivor } = useSurvivorStore.getState(); - survivor?.updateFromEvent( - parseStartGameEvent(mockEvents.encodedMockStartGameEvent) - ); + survivor?.updateFromEvent( + parseStartGameEvent(mockEvents.encodedMockStartGameEvent) + ); - const updatedState = useSurvivorStore.getState().survivor; + const updatedState = useSurvivorStore.getState().survivor; - expect(updatedState?.name).toEqual( - parseStartGameEvent(mockEvents.encodedMockStartGameEvent).event - .adventurerMeta.name - ); + expect(updatedState?.name).toEqual( + parseStartGameEvent(mockEvents.encodedMockStartGameEvent).event + .adventurerMeta.name + ); - expect(updatedState?.gold).toEqual( - parseStartGameEvent(mockEvents.encodedMockStartGameEvent).event - .adventurerState.adventurer.gold - ); - }); - // it("should update health with DiscoveredHealthEvent", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockDiscoveredHealthEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update gold with DiscoveredGoldEvent", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockDiscoveredGoldEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer?.gold).toEqual( - // adventurerState.adventurer.gold - // ); - // }); - // it("should update XP with DiscoveredXPEvent", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockDiscoveredXPEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer?.xp).toEqual(adventurerState.adventurer.xp); - // }); - // it("should update adventurer state after discovering loot", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockDiscoveredLootEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after equipment change", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockEquipmentChangedEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after dodging obstacle", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockDodgedObstacleEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after being hit by obstacle", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockHitByObstacleEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after being ambushed by beast", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockAmbushedByBeastEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after discovering a beast", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockDiscoveredBeastEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after attacking a beast", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockAttackedBeastEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after being attacked by a beast", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockAttackedByBeastEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after slaying a beast", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockSlayedBeastEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after failing to flee", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockFleeFailedEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after successfully fleeing", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockFleeSucceededEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after purchasing items", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockPurchasedItemsEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after purchasing potions", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockPurchasedPotionsEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after equipping items", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockEquippedItemsEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after dropping items", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockDroppedItemsEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after greatness increase", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockGreatnessIncreasedEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after items level up", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockItemsLeveledUpEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after achieving new high score", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockNewHighScoreEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after adventurer dies", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockAdventurerDiedEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after leveling up", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockAdventurerLeveledUpEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state when upgrades are available", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockUpgradesAvailableEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state after upgrading", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockAdventurerUpgradedEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // }); - // it("should update adventurer state and bag after equipment change", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockEquipmentChangedEvent.slice(0, 31) - // ); - // const bag = parseBag( - // mockEvents.encodedMockEquipmentChangedEvent.slice(31, 62) - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // bag, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.bag).toEqual(bag); - // }); - // it("should update adventurer state and bag after purchasing items", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockPurchasedItemsEvent.slice(0, 31) - // ); - // const bag = parseBag( - // mockEvents.encodedMockPurchasedItemsEvent.slice(31, 62) - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // bag, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.bag).toEqual(bag); - // }); - // it("should update adventurer state and bag after equipping items", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockEquippedItemsEvent.slice(0, 31) - // ); - // const bag = parseBag( - // mockEvents.encodedMockEquippedItemsEvent.slice(31, 62) - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // bag, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.bag).toEqual(bag); - // }); - // it("should update adventurer state and bag after dropping items", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockDroppedItemsEvent.slice(0, 31) - // ); - // const bag = parseBag(mockEvents.encodedMockDroppedItemsEvent.slice(31, 62)); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // bag, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.bag).toEqual(bag); - // }); - // it("should update adventurer state and bag after upgrading", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockAdventurerUpgradedEvent.slice(0, 31) - // ); - // const bag = parseBag( - // mockEvents.encodedMockAdventurerUpgradedEvent.slice(31, 62) - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // bag, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.bag).toEqual(bag); - // }); - // it("should update beast state when discovering a beast", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockDiscoveredBeastEvent - // ); - // const formattedBeast = formatBeastData(mockEvents.mockDiscoveredBeastEvent); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // beast: formattedBeast, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.beast).toEqual(formattedBeast); - // }); - // it("should update beast state when ambushed by a beast", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockAmbushedByBeastEvent - // ); - // const formattedBeast = formatBeastData(mockEvents.mockAmbushedByBeastEvent); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // beast: formattedBeast, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.beast).toEqual(formattedBeast); - // }); - // it("should update beast state when attacking a beast", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockAttackedBeastEvent - // ); - // const formattedBeast = formatBeastData(mockEvents.mockAttackedBeastEvent); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // beast: formattedBeast, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.beast).toEqual(formattedBeast); - // }); - // it("should clear beast state when slaying a beast", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockSlayedBeastEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // beast: undefined, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.beast).toBeUndefined(); - // }); - // it("should clear beast state when successfully fleeing", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const adventurerState = parseAdventurerState( - // mockEvents.encodedMockFleeSucceededEvent - // ); - // updateGameState({ - // adventurer: adventurerState.adventurer, - // beast: undefined, - // }); - // const updatedState = useGameStateStore.getState().gameState; - // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); - // expect(updatedState.beast).toBeUndefined(); - // }); - // }); - // describe("updateState with multiple sequential events", () => { - // beforeEach(() => { - // useGameStateStore.setState({ gameState: {} }); - // }); - // it("should correctly update state after a sequence of events", () => { - // const { updateGameState } = useGameStateStore.getState(); - // const getState = () => useGameStateStore.getState().gameState; - // // Start game - // const initialAdventurer = - // mockEvents.mockStartGameEvent.adventurerState.adventurer; - // updateGameState({ adventurer: initialAdventurer }); - // expect(getState().adventurer).toEqual(initialAdventurer); - // // Discover gold - // const goldEvent = mockEvents.mockDiscoveredGoldEvent; - // updateGameState({ - // adventurer: { - // ...getState().adventurer!, - // gold: (getState().adventurer?.gold || 0) + goldEvent.goldAmount, - // }, - // }); - // expect(getState().adventurer?.gold).toEqual( - // initialAdventurer.gold + goldEvent.goldAmount - // ); - // // Discover XP - // const xpEvent = mockEvents.mockDiscoveredXPEvent; - // updateGameState({ - // adventurer: { - // ...getState().adventurer!, - // xp: (getState().adventurer?.xp || 0) + xpEvent.xpAmount, - // }, - // }); - // expect(getState().adventurer?.xp).toEqual( - // initialAdventurer.xp + xpEvent.xpAmount - // ); - // // Encounter a beast - // const beastEvent = mockEvents.mockDiscoveredBeastEvent; - // const beast = formatBeastData(beastEvent); - // updateGameState({ beast }); - // expect(getState().beast).toEqual(beast); - // // Attack the beast - // const attackEvent = mockEvents.mockAttackedBeastEvent; - // updateGameState({ - // adventurer: { - // ...getState().adventurer!, - // beastHealth: beast.health - attackEvent.damage, - // }, - // beast: { - // ...getState().beast!, - // health: beast.health - attackEvent.damage, - // }, - // }); - // expect(getState().adventurer?.beastHealth).toEqual( - // beast.health - attackEvent.damage - // ); - // expect(getState().beast?.health).toEqual(beast.health - attackEvent.damage); - // // Slay the beast - // const slayEvent = mockEvents.mockSlayedBeastEvent; - // updateGameState({ - // adventurer: { - // ...getState().adventurer!, - // gold: (getState().adventurer?.gold || 0) + slayEvent.goldEarned, - // xp: (getState().adventurer?.xp || 0) + slayEvent.xpEarnedAdventurer, - // beastHealth: 0, - // }, - // beast: undefined, - // }); - // expect(getState().adventurer?.gold).toEqual( - // initialAdventurer.gold + goldEvent.goldAmount + slayEvent.goldEarned - // ); - // expect(getState().adventurer?.xp).toEqual( - // initialAdventurer.xp + xpEvent.xpAmount + slayEvent.xpEarnedAdventurer - // ); - // expect(getState().adventurer?.beastHealth).toEqual(0); - // expect(getState().beast).toBeUndefined(); - // // Level up - // const levelUpEvent = mockEvents.mockAdventurerUpgradedEvent; - // updateGameState({ - // adventurer: - // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer, - // }); - // const finalState = getState().adventurer; - // expect(finalState?.stats).toEqual( - // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer.stats - // ); - // expect(finalState?.statUpgradesAvailable).toEqual( - // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer - // .statUpgradesAvailable - // ); - // // TODO: e2e test. We need to increment the mock data to include the next event - // // // Final state check - // // expect(finalState).toMatchObject({ - // // gold: - // // initialAdventurer.gold + goldEvent.goldAmount + slayEvent.goldEarned, - // // xp: - // // initialAdventurer.xp + xpEvent.xpAmount + slayEvent.xpEarnedAdventurer, - // // beastHealth: 0, - // // stats: - // // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer.stats, - // // statUpgradesAvailable: - // // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer - // // .statUpgradesAvailable, - // // }); - // }); + expect(updatedState?.gold).toEqual( + parseStartGameEvent(mockEvents.encodedMockStartGameEvent).event + .adventurerState.adventurer.gold + ); + }); + // it("should update health with DiscoveredHealthEvent", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockDiscoveredHealthEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update gold with DiscoveredGoldEvent", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockDiscoveredGoldEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer?.gold).toEqual( + // adventurerState.adventurer.gold + // ); + // }); + // it("should update XP with DiscoveredXPEvent", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockDiscoveredXPEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer?.xp).toEqual(adventurerState.adventurer.xp); + // }); + // it("should update adventurer state after discovering loot", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockDiscoveredLootEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after equipment change", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockEquipmentChangedEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after dodging obstacle", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockDodgedObstacleEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after being hit by obstacle", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockHitByObstacleEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after being ambushed by beast", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockAmbushedByBeastEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after discovering a beast", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockDiscoveredBeastEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after attacking a beast", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockAttackedBeastEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after being attacked by a beast", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockAttackedByBeastEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after slaying a beast", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockSlayedBeastEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after failing to flee", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockFleeFailedEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after successfully fleeing", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockFleeSucceededEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after purchasing items", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockPurchasedItemsEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after purchasing potions", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockPurchasedPotionsEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after equipping items", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockEquippedItemsEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after dropping items", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockDroppedItemsEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after greatness increase", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockGreatnessIncreasedEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after items level up", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockItemsLeveledUpEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after achieving new high score", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockNewHighScoreEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after adventurer dies", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockAdventurerDiedEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after leveling up", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockAdventurerLeveledUpEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state when upgrades are available", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockUpgradesAvailableEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state after upgrading", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockAdventurerUpgradedEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // }); + // it("should update adventurer state and bag after equipment change", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockEquipmentChangedEvent.slice(0, 31) + // ); + // const bag = parseBag( + // mockEvents.encodedMockEquipmentChangedEvent.slice(31, 62) + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // bag, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.bag).toEqual(bag); + // }); + // it("should update adventurer state and bag after purchasing items", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockPurchasedItemsEvent.slice(0, 31) + // ); + // const bag = parseBag( + // mockEvents.encodedMockPurchasedItemsEvent.slice(31, 62) + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // bag, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.bag).toEqual(bag); + // }); + // it("should update adventurer state and bag after equipping items", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockEquippedItemsEvent.slice(0, 31) + // ); + // const bag = parseBag( + // mockEvents.encodedMockEquippedItemsEvent.slice(31, 62) + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // bag, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.bag).toEqual(bag); + // }); + // it("should update adventurer state and bag after dropping items", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockDroppedItemsEvent.slice(0, 31) + // ); + // const bag = parseBag(mockEvents.encodedMockDroppedItemsEvent.slice(31, 62)); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // bag, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.bag).toEqual(bag); + // }); + // it("should update adventurer state and bag after upgrading", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockAdventurerUpgradedEvent.slice(0, 31) + // ); + // const bag = parseBag( + // mockEvents.encodedMockAdventurerUpgradedEvent.slice(31, 62) + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // bag, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.bag).toEqual(bag); + // }); + // it("should update beast state when discovering a beast", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockDiscoveredBeastEvent + // ); + // const formattedBeast = formatBeastData(mockEvents.mockDiscoveredBeastEvent); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // beast: formattedBeast, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.beast).toEqual(formattedBeast); + // }); + // it("should update beast state when ambushed by a beast", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockAmbushedByBeastEvent + // ); + // const formattedBeast = formatBeastData(mockEvents.mockAmbushedByBeastEvent); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // beast: formattedBeast, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.beast).toEqual(formattedBeast); + // }); + // it("should update beast state when attacking a beast", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockAttackedBeastEvent + // ); + // const formattedBeast = formatBeastData(mockEvents.mockAttackedBeastEvent); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // beast: formattedBeast, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.beast).toEqual(formattedBeast); + // }); + // it("should clear beast state when slaying a beast", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockSlayedBeastEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // beast: undefined, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.beast).toBeUndefined(); + // }); + // it("should clear beast state when successfully fleeing", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const adventurerState = parseAdventurerState( + // mockEvents.encodedMockFleeSucceededEvent + // ); + // updateGameState({ + // adventurer: adventurerState.adventurer, + // beast: undefined, + // }); + // const updatedState = useGameStateStore.getState().gameState; + // expect(updatedState.adventurer).toEqual(adventurerState.adventurer); + // expect(updatedState.beast).toBeUndefined(); + // }); + // }); + // describe("updateState with multiple sequential events", () => { + // beforeEach(() => { + // useGameStateStore.setState({ gameState: {} }); + // }); + // it("should correctly update state after a sequence of events", () => { + // const { updateGameState } = useGameStateStore.getState(); + // const getState = () => useGameStateStore.getState().gameState; + // // Start game + // const initialAdventurer = + // mockEvents.mockStartGameEvent.adventurerState.adventurer; + // updateGameState({ adventurer: initialAdventurer }); + // expect(getState().adventurer).toEqual(initialAdventurer); + // // Discover gold + // const goldEvent = mockEvents.mockDiscoveredGoldEvent; + // updateGameState({ + // adventurer: { + // ...getState().adventurer!, + // gold: (getState().adventurer?.gold || 0) + goldEvent.goldAmount, + // }, + // }); + // expect(getState().adventurer?.gold).toEqual( + // initialAdventurer.gold + goldEvent.goldAmount + // ); + // // Discover XP + // const xpEvent = mockEvents.mockDiscoveredXPEvent; + // updateGameState({ + // adventurer: { + // ...getState().adventurer!, + // xp: (getState().adventurer?.xp || 0) + xpEvent.xpAmount, + // }, + // }); + // expect(getState().adventurer?.xp).toEqual( + // initialAdventurer.xp + xpEvent.xpAmount + // ); + // // Encounter a beast + // const beastEvent = mockEvents.mockDiscoveredBeastEvent; + // const beast = formatBeastData(beastEvent); + // updateGameState({ beast }); + // expect(getState().beast).toEqual(beast); + // // Attack the beast + // const attackEvent = mockEvents.mockAttackedBeastEvent; + // updateGameState({ + // adventurer: { + // ...getState().adventurer!, + // beastHealth: beast.health - attackEvent.damage, + // }, + // beast: { + // ...getState().beast!, + // health: beast.health - attackEvent.damage, + // }, + // }); + // expect(getState().adventurer?.beastHealth).toEqual( + // beast.health - attackEvent.damage + // ); + // expect(getState().beast?.health).toEqual(beast.health - attackEvent.damage); + // // Slay the beast + // const slayEvent = mockEvents.mockSlayedBeastEvent; + // updateGameState({ + // adventurer: { + // ...getState().adventurer!, + // gold: (getState().adventurer?.gold || 0) + slayEvent.goldEarned, + // xp: (getState().adventurer?.xp || 0) + slayEvent.xpEarnedAdventurer, + // beastHealth: 0, + // }, + // beast: undefined, + // }); + // expect(getState().adventurer?.gold).toEqual( + // initialAdventurer.gold + goldEvent.goldAmount + slayEvent.goldEarned + // ); + // expect(getState().adventurer?.xp).toEqual( + // initialAdventurer.xp + xpEvent.xpAmount + slayEvent.xpEarnedAdventurer + // ); + // expect(getState().adventurer?.beastHealth).toEqual(0); + // expect(getState().beast).toBeUndefined(); + // // Level up + // const levelUpEvent = mockEvents.mockAdventurerUpgradedEvent; + // updateGameState({ + // adventurer: + // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer, + // }); + // const finalState = getState().adventurer; + // expect(finalState?.stats).toEqual( + // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer.stats + // ); + // expect(finalState?.statUpgradesAvailable).toEqual( + // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer + // .statUpgradesAvailable + // ); + // // TODO: e2e test. We need to increment the mock data to include the next event + // // // Final state check + // // expect(finalState).toMatchObject({ + // // gold: + // // initialAdventurer.gold + goldEvent.goldAmount + slayEvent.goldEarned, + // // xp: + // // initialAdventurer.xp + xpEvent.xpAmount + slayEvent.xpEarnedAdventurer, + // // beastHealth: 0, + // // stats: + // // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer.stats, + // // statUpgradesAvailable: + // // levelUpEvent.adventurerStateWithBag.adventurerState.adventurer + // // .statUpgradesAvailable, + // // }); + // }); }); diff --git a/packages/core/src/type/events.ts b/packages/core/src/type/events.ts index 09f616f..ec1d51f 100644 --- a/packages/core/src/type/events.ts +++ b/packages/core/src/type/events.ts @@ -1,6 +1,6 @@ export type u256 = { - low: u128; - high: u128; + low: u128; + high: u128; }; export type u128 = number; @@ -9,333 +9,333 @@ export type u16 = number; export type u8 = number; export type AdventurerMetadata = { - startEntropy: string; - startingStats: Stats; - interfaceCamel: boolean; - name: u128; + startEntropy: string; + startingStats: Stats; + interfaceCamel: boolean; + name: u128; }; export type Stats = { - strength: u8; - dexterity: u8; - vitality: u8; - intelligence: u8; - wisdom: u8; - charisma: u8; - luck: u8; + strength: u8; + dexterity: u8; + vitality: u8; + intelligence: u8; + wisdom: u8; + charisma: u8; + luck: u8; }; export type Equipment = { - weapon: LootStatistics; - chest: LootStatistics; - head: LootStatistics; - waist: LootStatistics; - foot: LootStatistics; - hand: LootStatistics; - neck: LootStatistics; - ring: LootStatistics; + weapon: LootStatistics; + chest: LootStatistics; + head: LootStatistics; + waist: LootStatistics; + foot: LootStatistics; + hand: LootStatistics; + neck: LootStatistics; + ring: LootStatistics; }; export type LootStatistics = { - id: u8; - xp: u16; + id: u8; + xp: u16; }; export type Adventurer = { - health: u16; - xp: u16; - gold: u16; - beastHealth: u16; - statUpgradesAvailable: u8; - stats: Stats; - equipment: Equipment; - mutated: boolean; + health: u16; + xp: u16; + gold: u16; + beastHealth: u16; + statUpgradesAvailable: u8; + stats: Stats; + equipment: Equipment; + mutated: boolean; }; export type Bag = { - item1: LootStatistics; - item2: LootStatistics; - item3: LootStatistics; - item4: LootStatistics; - item5: LootStatistics; - item6: LootStatistics; - item7: LootStatistics; - item8: LootStatistics; - item9: LootStatistics; - item10: LootStatistics; - item11: LootStatistics; - item12: LootStatistics; - item13: LootStatistics; - item14: LootStatistics; - item15: LootStatistics; - mutated: boolean; + item1: LootStatistics; + item2: LootStatistics; + item3: LootStatistics; + item4: LootStatistics; + item5: LootStatistics; + item6: LootStatistics; + item7: LootStatistics; + item8: LootStatistics; + item9: LootStatistics; + item10: LootStatistics; + item11: LootStatistics; + item12: LootStatistics; + item13: LootStatistics; + item14: LootStatistics; + item15: LootStatistics; + mutated: boolean; }; export type LootWithPrice = { - item: Loot; - price: u16; + item: Loot; + price: u16; }; export enum Tier { - None, - T1, - T2, - T3, - T4, - T5, + None, + T1, + T2, + T3, + T4, + T5, } export enum Slot { - None, - Weapon, - Chest, - Head, - Waist, - Foot, - Hand, - Neck, - Ring, + None, + Weapon, + Chest, + Head, + Waist, + Foot, + Hand, + Neck, + Ring, } export enum Type { - None, - Magic_or_Cloth, - Blade_or_Hide, - Bludgeon_or_Metal, - Necklace, - Ring, + None, + Magic_or_Cloth, + Blade_or_Hide, + Bludgeon_or_Metal, + Necklace, + Ring, } export type Loot = { - id: u8; - tier: Tier; - itemType: Type; - slot: Slot; + id: u8; + tier: Tier; + itemType: Type; + slot: Slot; }; export type ContractAddress = string; export type AdventurerState = { - owner: ContractAddress; - adventurerId: u128; - adventurerEntropy: string; - adventurer: Adventurer; + owner: ContractAddress; + adventurerId: u128; + adventurerEntropy: string; + adventurer: Adventurer; }; export type SpecialPowers = { - special1: u8; - special2: u8; - special3: u8; + special1: u8; + special2: u8; + special3: u8; }; export type CombatSpec = { - tier: Tier; - itemType: Type; - level: u16; - specials: SpecialPowers; + tier: Tier; + itemType: Type; + level: u16; + specials: SpecialPowers; }; export type AdventurerStateWithBag = { - adventurerState: AdventurerState; - bag: Bag; + adventurerState: AdventurerState; + bag: Bag; }; // Events export type StartGameEvent = { - adventurerState: AdventurerState; - adventurerMeta: AdventurerMetadata; - revealBlock: u64; + adventurerState: AdventurerState; + adventurerMeta: AdventurerMetadata; + revealBlock: u64; }; export type DiscoveredHealthEvent = { - adventurerState: AdventurerState; - healthAmount: u16; + adventurerState: AdventurerState; + healthAmount: u16; }; export type DiscoveredGoldEvent = { - adventurerState: AdventurerState; - goldAmount: u16; + adventurerState: AdventurerState; + goldAmount: u16; }; export type DiscoveredXPEvent = { - adventurerState: AdventurerState; - xpAmount: u16; + adventurerState: AdventurerState; + xpAmount: u16; }; export type DiscoveredLootEvent = { - adventurerState: AdventurerState; - itemId: u16; + adventurerState: AdventurerState; + itemId: u16; }; export type EquipmentChangedEvent = { - adventurerStateWithBag: AdventurerStateWithBag; - equippedItems: u8[]; - baggedItems: u8[]; - droppedItems: u8[]; + adventurerStateWithBag: AdventurerStateWithBag; + equippedItems: u8[]; + baggedItems: u8[]; + droppedItems: u8[]; }; export type DodgedObstacleEvent = { - adventurerState: AdventurerState; - id: u8; - level: u16; - damageTaken: u16; - damageLocation: u8; - xpEarnedAdventurer: u16; - xpEarnedItems: u16; + adventurerState: AdventurerState; + id: u8; + level: u16; + damageTaken: u16; + damageLocation: u8; + xpEarnedAdventurer: u16; + xpEarnedItems: u16; }; export type HitByObstacleEvent = { - adventurerState: AdventurerState; - id: u8; - level: u16; - damageTaken: u16; - damageLocation: u8; - xpEarnedAdventurer: u16; - xpEarnedItems: u16; + adventurerState: AdventurerState; + id: u8; + level: u16; + damageTaken: u16; + damageLocation: u8; + xpEarnedAdventurer: u16; + xpEarnedItems: u16; }; export type AmbushedByBeastEvent = { - adventurerState: AdventurerState; - seed: u128; - id: u8; - beastSpecs: CombatSpec; - damage: u16; - criticalHit: boolean; - location: u8; + adventurerState: AdventurerState; + seed: u128; + id: u8; + beastSpecs: CombatSpec; + damage: u16; + criticalHit: boolean; + location: u8; }; export type DiscoveredBeastEvent = { - adventurerState: AdventurerState; - seed: u128; - id: u8; - beastSpecs: CombatSpec; + adventurerState: AdventurerState; + seed: u128; + id: u8; + beastSpecs: CombatSpec; }; export type AttackedBeastEvent = { - adventurerState: AdventurerState; - seed: u128; - id: u8; - beastSpecs: CombatSpec; - damage: u16; - criticalHit: boolean; - location: u8; + adventurerState: AdventurerState; + seed: u128; + id: u8; + beastSpecs: CombatSpec; + damage: u16; + criticalHit: boolean; + location: u8; }; export type AttackedByBeastEvent = { - adventurerState: AdventurerState; - seed: u128; - id: u8; - beastSpecs: CombatSpec; - damage: u16; - criticalHit: boolean; - location: u8; + adventurerState: AdventurerState; + seed: u128; + id: u8; + beastSpecs: CombatSpec; + damage: u16; + criticalHit: boolean; + location: u8; }; export type SlayedBeastEvent = { - adventurerState: AdventurerState; - seed: u128; - id: u8; - beastSpecs: CombatSpec; - damageDealt: u16; - criticalHit: boolean; - xpEarnedAdventurer: u16; - xpEarnedItems: u16; - goldEarned: u16; + adventurerState: AdventurerState; + seed: u128; + id: u8; + beastSpecs: CombatSpec; + damageDealt: u16; + criticalHit: boolean; + xpEarnedAdventurer: u16; + xpEarnedItems: u16; + goldEarned: u16; }; export type FleeFailedEvent = { - adventurerState: AdventurerState; - seed: u128; - id: u8; - beastSpecs: CombatSpec; + adventurerState: AdventurerState; + seed: u128; + id: u8; + beastSpecs: CombatSpec; }; export type FleeSucceededEvent = { - adventurerState: AdventurerState; - seed: u128; - id: u8; - beastSpecs: CombatSpec; + adventurerState: AdventurerState; + seed: u128; + id: u8; + beastSpecs: CombatSpec; }; export type PurchasedItemsEvent = { - adventurerStateWithBag: AdventurerStateWithBag; - purchases: LootWithPrice[]; + adventurerStateWithBag: AdventurerStateWithBag; + purchases: LootWithPrice[]; }; export type PurchasedPotionsEvent = { - adventurerState: AdventurerState; - quantity: u8; - cost: u16; - health: u16; + adventurerState: AdventurerState; + quantity: u8; + cost: u16; + health: u16; }; export type EquippedItemsEvent = { - adventurerStateWithBag: AdventurerStateWithBag; - equippedItems: u8[]; - unequippedItems: u8[]; + adventurerStateWithBag: AdventurerStateWithBag; + equippedItems: u8[]; + unequippedItems: u8[]; }; export type DroppedItemsEvent = { - adventurerStateWithBag: AdventurerStateWithBag; - itemIds: u8[]; + adventurerStateWithBag: AdventurerStateWithBag; + itemIds: u8[]; }; export type GreatnessIncreasedEvent = { - adventurerState: AdventurerState; - itemId: u8; - previousLevel: u8; - newLevel: u8; + adventurerState: AdventurerState; + itemId: u8; + previousLevel: u8; + newLevel: u8; }; export type ItemLeveledUp = { - itemId: u8; - previousLevel: u8; - newLevel: u8; - suffixUnlocked: boolean; - prefixesUnlocked: boolean; - specials: SpecialPowers; + itemId: u8; + previousLevel: u8; + newLevel: u8; + suffixUnlocked: boolean; + prefixesUnlocked: boolean; + specials: SpecialPowers; }; export type ItemsLeveledUpEvent = { - adventurerState: AdventurerState; - items: ItemLeveledUp[]; + adventurerState: AdventurerState; + items: ItemLeveledUp[]; }; export type NewHighScoreEvent = { - adventurerState: AdventurerState; - rank: u8; + adventurerState: AdventurerState; + rank: u8; }; export type AdventurerDiedEvent = { - adventurerState: AdventurerState; - killedByBeast: u8; - killedByObstacle: u8; - callerAddress: ContractAddress; + adventurerState: AdventurerState; + killedByBeast: u8; + killedByObstacle: u8; + callerAddress: ContractAddress; }; export type AdventurerLeveledUpEvent = { - adventurerState: AdventurerState; - previousLevel: u8; - newLevel: u8; + adventurerState: AdventurerState; + previousLevel: u8; + newLevel: u8; }; export type UpgradesAvailableEvent = { - adventurerState: AdventurerState; - items: number[]; + adventurerState: AdventurerState; + items: number[]; }; export type AdventurerUpgradedEvent = { - adventurerStateWithBag: AdventurerStateWithBag; - strengthIncrease: u8; - dexterityIncrease: u8; - vitalityIncrease: u8; - intelligenceIncrease: u8; - wisdomIncrease: u8; - charismaIncrease: u8; + adventurerStateWithBag: AdventurerStateWithBag; + strengthIncrease: u8; + dexterityIncrease: u8; + vitalityIncrease: u8; + intelligenceIncrease: u8; + wisdomIncrease: u8; + charismaIncrease: u8; }; export type ERC721TransferEvent = { - from: ContractAddress; - to: ContractAddress; - tokenId: u256; + from: ContractAddress; + to: ContractAddress; + tokenId: u256; }; diff --git a/packages/core/src/type/index.ts b/packages/core/src/type/index.ts index b83079d..59727b0 100644 --- a/packages/core/src/type/index.ts +++ b/packages/core/src/type/index.ts @@ -4,785 +4,790 @@ import { Adventurer, AdventurerMetadata, Bag } from "./events"; export * from "./events"; export interface Stats { - strength: number; - dexterity: number; - vitality: number; - intelligence: number; - wisdom: number; - charisma: number; - luck: number; + strength: number; + dexterity: number; + vitality: number; + intelligence: number; + wisdom: number; + charisma: number; + luck: number; } export interface ItemPurchase { - item_id: number; - equip: boolean; + item_id: number; + equip: boolean; } export type MulticallEntry = - | { - entrypoint: "new_game"; - calldata: [string, number, string, bigint, bigint]; - } - | { entrypoint: "explore"; calldata: [string, number] } - | { entrypoint: "attack"; calldata: [string, number] } - | { entrypoint: "flee"; calldata: [string, number] } - | { entrypoint: "equip"; calldata: [string, ...number[]] } - | { entrypoint: "drop"; calldata: [string, ...number[]] } - | { - entrypoint: "upgrade"; - calldata: [string, number, ...number[]]; - } - | { entrypoint: "update_cost_to_play"; calldata: [] }; + | { + entrypoint: "new_game"; + calldata: [string, number, string, bigint, bigint]; + } + | { entrypoint: "explore"; calldata: [string, number] } + | { entrypoint: "attack"; calldata: [string, number] } + | { entrypoint: "flee"; calldata: [string, number] } + | { entrypoint: "equip"; calldata: [string, ...number[]] } + | { entrypoint: "drop"; calldata: [string, ...number[]] } + | { + entrypoint: "upgrade"; + calldata: [string, number, ...number[]]; + } + | { entrypoint: "update_cost_to_play"; calldata: [] }; export interface Beast { - adventurerId: number; - beast: string; - createdTime: string; - health: number; - lastUpdatedTime: string; - level: number; - seed: string; - slainOnTime: string | null; - special1: number; - special2: number; - special3: number; - timestamp: string; + adventurerId: number; + beast: string; + createdTime: string; + health: number; + lastUpdatedTime: string; + level: number; + seed: string; + slainOnTime: string | null; + special1: number; + special2: number; + special3: number; + timestamp: string; } export interface BeastEncounter { - encounter: string; - id: bigint; - type: string; - tier: number; - level: number; - health: number; - location: string; - dodgeRoll: number; - nextXp: number; - specialName: string; - criticalMultiplier: number; - damage: number; + encounter: string; + id: bigint; + type: string; + tier: number; + level: number; + health: number; + location: string; + dodgeRoll: number; + nextXp: number; + specialName: string; + criticalMultiplier: number; + damage: number; } export interface Item { - slot: string; - item?: string; - type?: string; - xp?: number; - level?: number; - tier?: number; - cost?: number; - purchaseTime?: string; - equipped?: string; - special1?: string; - special2?: string; - special3?: string; + slot: string; + item?: string; + type?: string; + xp?: number; + level?: number; + tier?: number; + cost?: number; + purchaseTime?: string; + equipped?: string; + special1?: string; + special2?: string; + special3?: string; } export interface Encounter { - encounter: string; - id?: bigint; - type: string; - tier: string | number; - level?: number; - health?: number; - location?: string; - dodgeRoll?: number; - nextXp: number; - specialName?: string; - criticalMultiplier?: number; - damage?: number; + encounter: string; + id?: bigint; + type: string; + tier: string | number; + level?: number; + health?: number; + location?: string; + dodgeRoll?: number; + nextXp: number; + specialName?: string; + criticalMultiplier?: number; + damage?: number; } export interface CombatResult { - totalDamage: number; - isCriticalHit: boolean; + totalDamage: number; + isCriticalHit: boolean; } export interface BattleEvent { - type: string; - totalDamage: number; - isCriticalHit: boolean; - beastDamageType?: string; - location?: string; + type: string; + totalDamage: number; + isCriticalHit: boolean; + beastDamageType?: string; + location?: string; } export interface AdventurerComplete { - id: number; - beastHealth: number; - charisma: number; - chest: string; - createdTime: string; - dexterity: number; - entropy: string; - foot: string; - gold: number; - hand: string; - head: string; - health: number; - intelligence: number; - lastUpdatedTime: string; - luck: number; - name: string; - neck: string; - owner: string; - revealBlock: number; - ring: string; - startEntropy: string; - statUpgrades: number; - strength: number; - timestamp: string; - vitality: number; - waist: string; - weapon: string; - wisdom: number; - xp: number; + id: number; + beastHealth: number; + charisma: number; + chest: string; + createdTime: string; + dexterity: number; + entropy: string; + foot: string; + gold: number; + hand: string; + head: string; + health: number; + intelligence: number; + lastUpdatedTime: string; + luck: number; + name: string; + neck: string; + owner: string; + revealBlock: number; + ring: string; + startEntropy: string; + statUpgrades: number; + strength: number; + timestamp: string; + vitality: number; + waist: string; + weapon: string; + wisdom: number; + xp: number; } export interface Battle { - attacker: string; - adventurerId: number; - beast: string; - beastHealth: number; - beastLevel: number; - blockTime: string; - criticalHit: boolean; - damageDealt: number; - damageLocation: string; - damageTaken: number; - discoveryTime: string; - fled: boolean; - goldEarned: number; - seed: string; - special1: number; - special2: number; - special3: number; - timestamp: string; - txHash: string; - xpEarnedAdventurer: number; - xpEarnedItems: number; + attacker: string; + adventurerId: number; + beast: string; + beastHealth: number; + beastLevel: number; + blockTime: string; + criticalHit: boolean; + damageDealt: number; + damageLocation: string; + damageTaken: number; + discoveryTime: string; + fled: boolean; + goldEarned: number; + seed: string; + special1: number; + special2: number; + special3: number; + timestamp: string; + txHash: string; + xpEarnedAdventurer: number; + xpEarnedItems: number; } export interface Discovery { - adventurerHealth: number; - adventurerId: number; - ambushed: boolean; - damageLocation: string; - damageTaken: number; - discoveryTime: string; - discoveryType: string; - dodgedObstacle: boolean; - entity: string; - entityHealth: number; - entityLevel: number; - obstacle: string; - obstacleLevel: number; - outputAmount: number; - seed: string; - special1: number; - special2: number; - special3: number; - subDiscoveryType: string; - timestamp: string; - txHash: string; - xpEarnedAdventurer: number; - xpEarnedItems: number; + adventurerHealth: number; + adventurerId: number; + ambushed: boolean; + damageLocation: string; + damageTaken: number; + discoveryTime: string; + discoveryType: string; + dodgedObstacle: boolean; + entity: string; + entityHealth: number; + entityLevel: number; + obstacle: string; + obstacleLevel: number; + outputAmount: number; + seed: string; + special1: number; + special2: number; + special3: number; + subDiscoveryType: string; + timestamp: string; + txHash: string; + xpEarnedAdventurer: number; + xpEarnedItems: number; } export interface Score { - adventurerId: number; - timestamp: string; - totalPayout: number; + adventurerId: number; + timestamp: string; + totalPayout: number; } export interface GameState { - beast: Beast; - adventurer: Adventurer; - adventurerMetadata: AdventurerMetadata; - currentBattle: Battle; - lastDiscovery: Discovery; - bag: Bag; - revealBlock: number; + beast: Beast; + adventurer: Adventurer; + adventurerMetadata: AdventurerMetadata; + currentBattle: Battle; + lastDiscovery: Discovery; + bag: Bag; + revealBlock: number; } export enum Beasts { - Warlock = 1, - Typhon = 2, - Jiangshi = 3, - Anansi = 4, - Basilisk = 5, - Gorgon = 6, - Kitsune = 7, - Lich = 8, - Chimera = 9, - Wendigo = 10, - Rakshasa = 11, - Werewolf = 12, - Banshee = 13, - Draugr = 14, - Vampire = 15, - Goblin = 16, - Ghoul = 17, - Wraith = 18, - Sprite = 19, - Kappa = 20, - Fairy = 21, - Leprechaun = 22, - Kelpie = 23, - Pixie = 24, - Gnome = 25, - Griffin = 26, - Manticore = 27, - Phoenix = 28, - Dragon = 29, - Minotaur = 30, - Qilin = 31, - Ammit = 32, - Nue = 33, - Skinwalker = 34, - Chupacabra = 35, - Weretiger = 36, - Wyvern = 37, - Roc = 38, - Harpy = 39, - Pegasus = 40, - Hippogriff = 41, - Fenrir = 42, - Jaguar = 43, - Satori = 44, - DireWolf = 45, - Bear = 46, - Wolf = 47, - Mantis = 48, - Spider = 49, - Rat = 50, - Kraken = 51, - Colossus = 52, - Balrog = 53, - Leviathan = 54, - Tarrasque = 55, - Titan = 56, - Nephilim = 57, - Behemoth = 58, - Hydra = 59, - Juggernaut = 60, - Oni = 61, - Jotunn = 62, - Ettin = 63, - Cyclops = 64, - Giant = 65, - NemeanLion = 66, - Berserker = 67, - Yeti = 68, - Golem = 69, - Ent = 70, - Troll = 71, - Bigfoot = 72, - Ogre = 73, - Orc = 74, - Skeleton = 75, + Warlock = 1, + Typhon = 2, + Jiangshi = 3, + Anansi = 4, + Basilisk = 5, + Gorgon = 6, + Kitsune = 7, + Lich = 8, + Chimera = 9, + Wendigo = 10, + Rakshasa = 11, + Werewolf = 12, + Banshee = 13, + Draugr = 14, + Vampire = 15, + Goblin = 16, + Ghoul = 17, + Wraith = 18, + Sprite = 19, + Kappa = 20, + Fairy = 21, + Leprechaun = 22, + Kelpie = 23, + Pixie = 24, + Gnome = 25, + Griffin = 26, + Manticore = 27, + Phoenix = 28, + Dragon = 29, + Minotaur = 30, + Qilin = 31, + Ammit = 32, + Nue = 33, + Skinwalker = 34, + Chupacabra = 35, + Weretiger = 36, + Wyvern = 37, + Roc = 38, + Harpy = 39, + Pegasus = 40, + Hippogriff = 41, + Fenrir = 42, + Jaguar = 43, + Satori = 44, + DireWolf = 45, + Bear = 46, + Wolf = 47, + Mantis = 48, + Spider = 49, + Rat = 50, + Kraken = 51, + Colossus = 52, + Balrog = 53, + Leviathan = 54, + Tarrasque = 55, + Titan = 56, + Nephilim = 57, + Behemoth = 58, + Hydra = 59, + Juggernaut = 60, + Oni = 61, + Jotunn = 62, + Ettin = 63, + Cyclops = 64, + Giant = 65, + NemeanLion = 66, + Berserker = 67, + Yeti = 68, + Golem = 69, + Ent = 70, + Troll = 71, + Bigfoot = 72, + Ogre = 73, + Orc = 74, + Skeleton = 75, } export enum BeastType { - Magical = "Magical", - Hunter = "Hunter", - Brute = "Brute", + Magical = "Magical", + Hunter = "Hunter", + Brute = "Brute", } export enum AttackType { - Magic = "Magic", - Blade = "Blade", - Bludgeon = "Bludgeon", + Magic = "Magic", + Blade = "Blade", + Bludgeon = "Bludgeon", } export enum ArmorType { - Cloth = "Cloth", - Hide = "Hide", - Metal = "Metal", + Cloth = "Cloth", + Hide = "Hide", + Metal = "Metal", } export enum Loot { - Pendant = 1, - Necklace = 2, - Amulet = 3, - SilverRing = 4, - BronzeRing = 5, - PlatinumRing = 6, - TitaniumRing = 7, - GoldRing = 8, - GhostWand = 9, - GraveWand = 10, - BoneWand = 11, - Wand = 12, - Grimoire = 13, - Chronicle = 14, - Tome = 15, - Book = 16, - DivineRobe = 17, - SilkRobe = 18, - LinenRobe = 19, - Robe = 20, - Shirt = 21, - Crown = 22, - DivineHood = 23, - SilkHood = 24, - LinenHood = 25, - Hood = 26, - BrightsilkSash = 27, - SilkSash = 28, - WoolSash = 29, - LinenSash = 30, - Sash = 31, - DivineSlippers = 32, - SilkSlippers = 33, - WoolShoes = 34, - LinenShoes = 35, - Shoes = 36, - DivineGloves = 37, - SilkGloves = 38, - WoolGloves = 39, - LinenGloves = 40, - Gloves = 41, - Katana = 42, - Falchion = 43, - Scimitar = 44, - LongSword = 45, - ShortSword = 46, - DemonHusk = 47, - DragonskinArmor = 48, - StuddedLeatherArmor = 49, - HardLeatherArmor = 50, - LeatherArmor = 51, - DemonCrown = 52, - DragonsCrown = 53, - WarCap = 54, - LeatherCap = 55, - Cap = 56, - DemonhideBelt = 57, - DragonskinBelt = 58, - StuddedLeatherBelt = 59, - HardLeatherBelt = 60, - LeatherBelt = 61, - DemonhideBoots = 62, - DragonskinBoots = 63, - StuddedLeatherBoots = 64, - HardLeatherBoots = 65, - LeatherBoots = 66, - DemonsHands = 67, - DragonskinGloves = 68, - StuddedLeatherGloves = 69, - HardLeatherGloves = 70, - LeatherGloves = 71, - Warhammer = 72, - Quarterstaff = 73, - Maul = 74, - Mace = 75, - Club = 76, - HolyChestplate = 77, - OrnateChestplate = 78, - PlateMail = 79, - ChainMail = 80, - RingMail = 81, - AncientHelm = 82, - OrnateHelm = 83, - GreatHelm = 84, - FullHelm = 85, - Helm = 86, - OrnateBelt = 87, - WarBelt = 88, - PlatedBelt = 89, - MeshBelt = 90, - HeavyBelt = 91, - HolyGreaves = 92, - OrnateGreaves = 93, - Greaves = 94, - ChainBoots = 95, - HeavyBoots = 96, - HolyGauntlets = 97, - OrnateGauntlets = 98, - Gauntlets = 99, - ChainGloves = 100, - HeavyGloves = 101, + Pendant = 1, + Necklace = 2, + Amulet = 3, + SilverRing = 4, + BronzeRing = 5, + PlatinumRing = 6, + TitaniumRing = 7, + GoldRing = 8, + GhostWand = 9, + GraveWand = 10, + BoneWand = 11, + Wand = 12, + Grimoire = 13, + Chronicle = 14, + Tome = 15, + Book = 16, + DivineRobe = 17, + SilkRobe = 18, + LinenRobe = 19, + Robe = 20, + Shirt = 21, + Crown = 22, + DivineHood = 23, + SilkHood = 24, + LinenHood = 25, + Hood = 26, + BrightsilkSash = 27, + SilkSash = 28, + WoolSash = 29, + LinenSash = 30, + Sash = 31, + DivineSlippers = 32, + SilkSlippers = 33, + WoolShoes = 34, + LinenShoes = 35, + Shoes = 36, + DivineGloves = 37, + SilkGloves = 38, + WoolGloves = 39, + LinenGloves = 40, + Gloves = 41, + Katana = 42, + Falchion = 43, + Scimitar = 44, + LongSword = 45, + ShortSword = 46, + DemonHusk = 47, + DragonskinArmor = 48, + StuddedLeatherArmor = 49, + HardLeatherArmor = 50, + LeatherArmor = 51, + DemonCrown = 52, + DragonsCrown = 53, + WarCap = 54, + LeatherCap = 55, + Cap = 56, + DemonhideBelt = 57, + DragonskinBelt = 58, + StuddedLeatherBelt = 59, + HardLeatherBelt = 60, + LeatherBelt = 61, + DemonhideBoots = 62, + DragonskinBoots = 63, + StuddedLeatherBoots = 64, + HardLeatherBoots = 65, + LeatherBoots = 66, + DemonsHands = 67, + DragonskinGloves = 68, + StuddedLeatherGloves = 69, + HardLeatherGloves = 70, + LeatherGloves = 71, + Warhammer = 72, + Quarterstaff = 73, + Maul = 74, + Mace = 75, + Club = 76, + HolyChestplate = 77, + OrnateChestplate = 78, + PlateMail = 79, + ChainMail = 80, + RingMail = 81, + AncientHelm = 82, + OrnateHelm = 83, + GreatHelm = 84, + FullHelm = 85, + Helm = 86, + OrnateBelt = 87, + WarBelt = 88, + PlatedBelt = 89, + MeshBelt = 90, + HeavyBelt = 91, + HolyGreaves = 92, + OrnateGreaves = 93, + Greaves = 94, + ChainBoots = 95, + HeavyBoots = 96, + HolyGauntlets = 97, + OrnateGauntlets = 98, + Gauntlets = 99, + ChainGloves = 100, + HeavyGloves = 101, } export enum ItemType { - Necklace = "Necklace", - Ring = "Ring", - Magic = "Magic", - Cloth = "Cloth", - Blade = "Blade", - Hide = "Hide", - Bludgeon = "Bludgeon", - Metal = "Metal", + Necklace = "Necklace", + Ring = "Ring", + Magic = "Magic", + Cloth = "Cloth", + Blade = "Blade", + Hide = "Hide", + Bludgeon = "Bludgeon", + Metal = "Metal", } export enum ItemSlot { - Neck = "Neck", - Ring = "Ring", - Weapon = "Weapon", - Chest = "Chest", - Head = "Head", - Waist = "Waist", - Foot = "Foot", - Hand = "Hand", + Neck = "Neck", + Ring = "Ring", + Weapon = "Weapon", + Chest = "Chest", + Head = "Head", + Waist = "Waist", + Foot = "Foot", + Hand = "Hand", } export const ITEM_SLOT_TO_NUMBER: Record = { - [ItemSlot.Weapon]: 1, - [ItemSlot.Chest]: 2, - [ItemSlot.Head]: 3, - [ItemSlot.Waist]: 4, - [ItemSlot.Foot]: 5, - [ItemSlot.Hand]: 6, - [ItemSlot.Neck]: 7, - [ItemSlot.Ring]: 8, + [ItemSlot.Weapon]: 1, + [ItemSlot.Chest]: 2, + [ItemSlot.Head]: 3, + [ItemSlot.Waist]: 4, + [ItemSlot.Foot]: 5, + [ItemSlot.Hand]: 6, + [ItemSlot.Neck]: 7, + [ItemSlot.Ring]: 8, }; export const NUMBER_TO_ITEM_SLOT: Record = { - 1: ItemSlot.Weapon, - 2: ItemSlot.Chest, - 3: ItemSlot.Head, - 4: ItemSlot.Waist, - 5: ItemSlot.Foot, - 6: ItemSlot.Hand, - 7: ItemSlot.Neck, - 8: ItemSlot.Ring, + 1: ItemSlot.Weapon, + 2: ItemSlot.Chest, + 3: ItemSlot.Head, + 4: ItemSlot.Waist, + 5: ItemSlot.Foot, + 6: ItemSlot.Hand, + 7: ItemSlot.Neck, + 8: ItemSlot.Ring, }; export enum StatType { - Strength = 0, - Dexterity = 1, - Vitality = 2, - Intelligence = 3, - Wisdom = 4, - Charisma = 5, - Luck = 6, + Strength = 0, + Dexterity = 1, + Vitality = 2, + Intelligence = 3, + Wisdom = 4, + Charisma = 5, + Luck = 6, } export const STATS_ABBREVIATIONS: Record = { - [StatType.Strength]: "STR", - [StatType.Dexterity]: "DEX", - [StatType.Vitality]: "VIT", - [StatType.Intelligence]: "INT", - [StatType.Wisdom]: "WIS", - [StatType.Charisma]: "CHA", - [StatType.Luck]: "LUCK", + [StatType.Strength]: "STR", + [StatType.Dexterity]: "DEX", + [StatType.Vitality]: "VIT", + [StatType.Intelligence]: "INT", + [StatType.Wisdom]: "WIS", + [StatType.Charisma]: "CHA", + [StatType.Luck]: "LUCK", }; export enum Obstacles { - // Magical Obstacles - DemonicAlter = 1, - VortexOfDespair = 2, - EldritchBarrier = 3, - SoulTrap = 4, - PhantomVortex = 5, - EctoplasmicWeb = 6, - SpectralChains = 7, - InfernalPact = 8, - ArcaneExplosion = 9, - HypnoticEssence = 10, - MischievousSprites = 11, - SoulDrainingStatue = 12, - PetrifyingGaze = 13, - SummoningCircle = 14, - EtherealVoid = 15, - MagicLock = 16, - BewitchingFog = 17, - IllusionaryMaze = 18, - SpellboundMirror = 19, - EnsnaringShadow = 20, - DarkMist = 21, - Curse = 22, - HauntingEcho = 23, - Hex = 24, - GhostlyWhispers = 25, - - // Sharp Obstacles - PendulumBlades = 26, - IcyRazorWinds = 27, - AcidicThorns = 28, - DragonsBreath = 29, - PendulumScythe = 30, - FlameJet = 31, - PiercingIceDarts = 32, - GlassSandStorm = 33, - PoisonedDartWall = 34, - SpinningBladeWheel = 35, - PoisonDart = 36, - SpikedTumbleweed = 37, - Thunderbolt = 38, - GiantBearTrap = 39, - SteelNeedleRain = 40, - SpikedPit = 41, - DiamondDustStorm = 42, - TrapdoorScorpionPit = 43, - BladedFan = 44, - BearTrap = 45, - PorcupineQuill = 46, - HiddenArrow = 47, - GlassShard = 48, - ThornBush = 49, - JaggedRocks = 50, - - // Crushing Obstacles - SubterraneanTremor = 51, - Rockslide = 52, - FlashFlood = 53, - ClingingRoots = 54, - CollapsingCavern = 55, - CrushingWalls = 56, - SmashingPillars = 57, - RumblingCatacomb = 58, - WhirlingCyclone = 59, - EruptingEarth = 60, - SubterraneanTremor2 = 61, - FallingChandelier = 62, - CollapsingBridge = 63, - RagingSandstorm = 64, - AvalanchingRocks = 65, - TumblingBoulders = 66, - SlammingIronGate = 67, - ShiftingSandtrap = 68, - EruptingMudGeyser = 69, - CrumblingStaircase = 70, - SwingingLogs = 71, - UnstableCliff = 72, - TopplingStatue = 73, - TumblingBarrels = 74, - RollingBoulder = 75, + // Magical Obstacles + DemonicAlter = 1, + VortexOfDespair = 2, + EldritchBarrier = 3, + SoulTrap = 4, + PhantomVortex = 5, + EctoplasmicWeb = 6, + SpectralChains = 7, + InfernalPact = 8, + ArcaneExplosion = 9, + HypnoticEssence = 10, + MischievousSprites = 11, + SoulDrainingStatue = 12, + PetrifyingGaze = 13, + SummoningCircle = 14, + EtherealVoid = 15, + MagicLock = 16, + BewitchingFog = 17, + IllusionaryMaze = 18, + SpellboundMirror = 19, + EnsnaringShadow = 20, + DarkMist = 21, + Curse = 22, + HauntingEcho = 23, + Hex = 24, + GhostlyWhispers = 25, + + // Sharp Obstacles + PendulumBlades = 26, + IcyRazorWinds = 27, + AcidicThorns = 28, + DragonsBreath = 29, + PendulumScythe = 30, + FlameJet = 31, + PiercingIceDarts = 32, + GlassSandStorm = 33, + PoisonedDartWall = 34, + SpinningBladeWheel = 35, + PoisonDart = 36, + SpikedTumbleweed = 37, + Thunderbolt = 38, + GiantBearTrap = 39, + SteelNeedleRain = 40, + SpikedPit = 41, + DiamondDustStorm = 42, + TrapdoorScorpionPit = 43, + BladedFan = 44, + BearTrap = 45, + PorcupineQuill = 46, + HiddenArrow = 47, + GlassShard = 48, + ThornBush = 49, + JaggedRocks = 50, + + // Crushing Obstacles + SubterraneanTremor = 51, + Rockslide = 52, + FlashFlood = 53, + ClingingRoots = 54, + CollapsingCavern = 55, + CrushingWalls = 56, + SmashingPillars = 57, + RumblingCatacomb = 58, + WhirlingCyclone = 59, + EruptingEarth = 60, + SubterraneanTremor2 = 61, + FallingChandelier = 62, + CollapsingBridge = 63, + RagingSandstorm = 64, + AvalanchingRocks = 65, + TumblingBoulders = 66, + SlammingIronGate = 67, + ShiftingSandtrap = 68, + EruptingMudGeyser = 69, + CrumblingStaircase = 70, + SwingingLogs = 71, + UnstableCliff = 72, + TopplingStatue = 73, + TumblingBarrels = 74, + RollingBoulder = 75, } export enum DiscoveryType { - Beast = 1, - Obstacle = 2, - Item = 3, + Beast = 1, + Obstacle = 2, + Item = 3, } export enum ItemDiscoveryType { - Health = 1, - Gold = 2, - XP = 3, + Health = 1, + Gold = 2, + XP = 3, } export enum ItemNamePrefix { - Agony = 1, - Apocalypse = 2, - Armageddon = 3, - Beast = 4, - Behemoth = 5, - Blight = 6, - Blood = 7, - Bramble = 8, - Brimstone = 9, - Brood = 10, - Carrion = 11, - Cataclysm = 12, - Chimeric = 13, - Corpse = 14, - Corruption = 15, - Damnation = 16, - Death = 17, - Demon = 18, - Dire = 19, - Dragon = 20, - Dread = 21, - Doom = 22, - Dusk = 23, - Eagle = 24, - Empyrean = 25, - Fate = 26, - Foe = 27, - Gale = 28, - Ghoul = 29, - Gloom = 30, - Glyph = 31, - Golem = 32, - Grim = 33, - Hate = 34, - Havoc = 35, - Honour = 36, - Horror = 37, - Hypnotic = 38, - Kraken = 39, - Loath = 40, - Maelstrom = 41, - Mind = 42, - Miracle = 43, - Morbid = 44, - Oblivion = 45, - Onslaught = 46, - Pain = 47, - Pandemonium = 48, - Phoenix = 49, - Plague = 50, - Rage = 51, - Rapture = 52, - Rune = 53, - Skull = 54, - Sol = 55, - Soul = 56, - Sorrow = 57, - Spirit = 58, - Storm = 59, - Tempest = 60, - Torment = 61, - Vengeance = 62, - Victory = 63, - Viper = 64, - Vortex = 65, - Woe = 66, - Wrath = 67, - Lights = 68, - Shimmering = 69, + Agony = 1, + Apocalypse = 2, + Armageddon = 3, + Beast = 4, + Behemoth = 5, + Blight = 6, + Blood = 7, + Bramble = 8, + Brimstone = 9, + Brood = 10, + Carrion = 11, + Cataclysm = 12, + Chimeric = 13, + Corpse = 14, + Corruption = 15, + Damnation = 16, + Death = 17, + Demon = 18, + Dire = 19, + Dragon = 20, + Dread = 21, + Doom = 22, + Dusk = 23, + Eagle = 24, + Empyrean = 25, + Fate = 26, + Foe = 27, + Gale = 28, + Ghoul = 29, + Gloom = 30, + Glyph = 31, + Golem = 32, + Grim = 33, + Hate = 34, + Havoc = 35, + Honour = 36, + Horror = 37, + Hypnotic = 38, + Kraken = 39, + Loath = 40, + Maelstrom = 41, + Mind = 42, + Miracle = 43, + Morbid = 44, + Oblivion = 45, + Onslaught = 46, + Pain = 47, + Pandemonium = 48, + Phoenix = 49, + Plague = 50, + Rage = 51, + Rapture = 52, + Rune = 53, + Skull = 54, + Sol = 55, + Soul = 56, + Sorrow = 57, + Spirit = 58, + Storm = 59, + Tempest = 60, + Torment = 61, + Vengeance = 62, + Victory = 63, + Viper = 64, + Vortex = 65, + Woe = 66, + Wrath = 67, + Lights = 68, + Shimmering = 69, } export enum ItemNameSuffix { - Bane = 1, - Root = 2, - Bite = 3, - Song = 4, - Roar = 5, - Grasp = 6, - Instrument = 7, - Glow = 8, - Bender = 9, - Shadow = 10, - Whisper = 11, - Shout = 12, - Growl = 13, - Tear = 14, - Peak = 15, - Form = 16, - Sun = 17, - Moon = 18, + Bane = 1, + Root = 2, + Bite = 3, + Song = 4, + Roar = 5, + Grasp = 6, + Instrument = 7, + Glow = 8, + Bender = 9, + Shadow = 10, + Whisper = 11, + Shout = 12, + Growl = 13, + Tear = 14, + Peak = 15, + Form = 16, + Sun = 17, + Moon = 18, } export enum ItemSuffix { - OfPower = 1, - OfGiant = 2, - OfTitans = 3, - OfSkill = 4, - OfPerfection = 5, - OfBrilliance = 6, - OfEnlightenment = 7, - OfProtection = 8, - OfAnger = 9, - OfRage = 10, - OfFury = 11, - OfVitriol = 12, - OfTheFox = 13, - OfDetection = 14, - OfReflection = 15, - OfTheTwins = 16, + OfPower = 1, + OfGiant = 2, + OfTitans = 3, + OfSkill = 4, + OfPerfection = 5, + OfBrilliance = 6, + OfEnlightenment = 7, + OfProtection = 8, + OfAnger = 9, + OfRage = 10, + OfFury = 11, + OfVitriol = 12, + OfTheFox = 13, + OfDetection = 14, + OfReflection = 15, + OfTheTwins = 16, } export interface StatBoost { - [StatType.Strength]?: number; - [StatType.Dexterity]?: number; - [StatType.Vitality]?: number; - [StatType.Intelligence]?: number; - [StatType.Wisdom]?: number; - [StatType.Charisma]?: number; + [StatType.Strength]?: number; + [StatType.Dexterity]?: number; + [StatType.Vitality]?: number; + [StatType.Intelligence]?: number; + [StatType.Wisdom]?: number; + [StatType.Charisma]?: number; } export const ITEM_SUFFIX_BOOST: Record = { - [ItemSuffix.OfPower]: { [StatType.Strength]: 3 }, - [ItemSuffix.OfGiant]: { [StatType.Vitality]: 3 }, - [ItemSuffix.OfTitans]: { [StatType.Strength]: 2, [StatType.Charisma]: 1 }, - [ItemSuffix.OfSkill]: { [StatType.Dexterity]: 3 }, - [ItemSuffix.OfPerfection]: { - [StatType.Strength]: 1, - [StatType.Dexterity]: 1, - [StatType.Vitality]: 1, - }, - [ItemSuffix.OfBrilliance]: { [StatType.Intelligence]: 3 }, - [ItemSuffix.OfEnlightenment]: { [StatType.Wisdom]: 3 }, - [ItemSuffix.OfProtection]: { - [StatType.Vitality]: 2, - [StatType.Dexterity]: 1, - }, - [ItemSuffix.OfAnger]: { [StatType.Strength]: 2, [StatType.Dexterity]: 1 }, - [ItemSuffix.OfRage]: { - [StatType.Wisdom]: 1, - [StatType.Strength]: 1, - [StatType.Charisma]: 1, - }, - [ItemSuffix.OfFury]: { - [StatType.Vitality]: 1, - [StatType.Charisma]: 1, - [StatType.Intelligence]: 1, - }, - [ItemSuffix.OfVitriol]: { [StatType.Intelligence]: 2, [StatType.Wisdom]: 1 }, - [ItemSuffix.OfTheFox]: { [StatType.Dexterity]: 2, [StatType.Charisma]: 1 }, - [ItemSuffix.OfDetection]: { [StatType.Wisdom]: 2, [StatType.Dexterity]: 1 }, - [ItemSuffix.OfReflection]: { - [StatType.Wisdom]: 2, - [StatType.Intelligence]: 1, - }, - [ItemSuffix.OfTheTwins]: { [StatType.Charisma]: 3 }, + [ItemSuffix.OfPower]: { [StatType.Strength]: 3 }, + [ItemSuffix.OfGiant]: { [StatType.Vitality]: 3 }, + [ItemSuffix.OfTitans]: { [StatType.Strength]: 2, [StatType.Charisma]: 1 }, + [ItemSuffix.OfSkill]: { [StatType.Dexterity]: 3 }, + [ItemSuffix.OfPerfection]: { + [StatType.Strength]: 1, + [StatType.Dexterity]: 1, + [StatType.Vitality]: 1, + }, + [ItemSuffix.OfBrilliance]: { [StatType.Intelligence]: 3 }, + [ItemSuffix.OfEnlightenment]: { [StatType.Wisdom]: 3 }, + [ItemSuffix.OfProtection]: { + [StatType.Vitality]: 2, + [StatType.Dexterity]: 1, + }, + [ItemSuffix.OfAnger]: { [StatType.Strength]: 2, [StatType.Dexterity]: 1 }, + [ItemSuffix.OfRage]: { + [StatType.Wisdom]: 1, + [StatType.Strength]: 1, + [StatType.Charisma]: 1, + }, + [ItemSuffix.OfFury]: { + [StatType.Vitality]: 1, + [StatType.Charisma]: 1, + [StatType.Intelligence]: 1, + }, + [ItemSuffix.OfVitriol]: { + [StatType.Intelligence]: 2, + [StatType.Wisdom]: 1, + }, + [ItemSuffix.OfTheFox]: { [StatType.Dexterity]: 2, [StatType.Charisma]: 1 }, + [ItemSuffix.OfDetection]: { [StatType.Wisdom]: 2, [StatType.Dexterity]: 1 }, + [ItemSuffix.OfReflection]: { + [StatType.Wisdom]: 2, + [StatType.Intelligence]: 1, + }, + [ItemSuffix.OfTheTwins]: { [StatType.Charisma]: 3 }, }; export enum Status { - Closed = 0, - Open = 1, + Closed = 0, + Open = 1, } export enum SELECTORS { - StartGame = "StartGame", - AdventurerUpgraded = "AdventurerUpgraded", - DiscoveredHealth = "DiscoveredHealth", - DiscoveredGold = "DiscoveredGold", - DiscoveredLoot = "DiscoveredLoot", - DiscoveredXP = "DiscoveredXP", - EquipmentChanged = "EquipmentChanged", - DodgedObstacle = "DodgedObstacle", - HitByObstacle = "HitByObstacle", - DiscoveredBeast = "DiscoveredBeast", - AmbushedByBeast = "AmbushedByBeast", - AttackedBeast = "AttackedBeast", - AttackedByBeast = "AttackedByBeast", - SlayedBeast = "SlayedBeast", - FleeFailed = "FleeFailed", - FleeSucceeded = "FleeSucceeded", - PurchasedItems = "PurchasedItems", - PurchasedPotions = "PurchasedPotions", - EquippedItems = "EquippedItems", - DroppedItems = "DroppedItems", - GreatnessIncreased = "GreatnessIncreased", - ItemsLeveledUp = "ItemsLeveledUp", - NewHighScore = "NewHighScore", - AdventurerDied = "AdventurerDied", - AdventurerLeveledUp = "AdventurerLeveledUp", - UpgradesAvailable = "UpgradesAvailable", - Transfer = "Transfer", + StartGame = "StartGame", + AdventurerUpgraded = "AdventurerUpgraded", + DiscoveredHealth = "DiscoveredHealth", + DiscoveredGold = "DiscoveredGold", + DiscoveredLoot = "DiscoveredLoot", + DiscoveredXP = "DiscoveredXP", + EquipmentChanged = "EquipmentChanged", + DodgedObstacle = "DodgedObstacle", + HitByObstacle = "HitByObstacle", + DiscoveredBeast = "DiscoveredBeast", + AmbushedByBeast = "AmbushedByBeast", + AttackedBeast = "AttackedBeast", + AttackedByBeast = "AttackedByBeast", + SlayedBeast = "SlayedBeast", + FleeFailed = "FleeFailed", + FleeSucceeded = "FleeSucceeded", + PurchasedItems = "PurchasedItems", + PurchasedPotions = "PurchasedPotions", + EquippedItems = "EquippedItems", + DroppedItems = "DroppedItems", + GreatnessIncreased = "GreatnessIncreased", + ItemsLeveledUp = "ItemsLeveledUp", + NewHighScore = "NewHighScore", + AdventurerDied = "AdventurerDied", + AdventurerLeveledUp = "AdventurerLeveledUp", + UpgradesAvailable = "UpgradesAvailable", + Transfer = "Transfer", } export const HASHED_SELECTORS = { - StartGame: hash.getSelectorFromName(SELECTORS.StartGame), - AdventurerUpgraded: hash.getSelectorFromName(SELECTORS.AdventurerUpgraded), - DiscoveredHealth: hash.getSelectorFromName(SELECTORS.DiscoveredHealth), - DiscoveredGold: hash.getSelectorFromName(SELECTORS.DiscoveredGold), - DiscoveredLoot: hash.getSelectorFromName(SELECTORS.DiscoveredLoot), - DiscoveredXP: hash.getSelectorFromName(SELECTORS.DiscoveredXP), - EquipmentChanged: hash.getSelectorFromName(SELECTORS.EquipmentChanged), - DodgedObstacle: hash.getSelectorFromName(SELECTORS.DodgedObstacle), - HitByObstacle: hash.getSelectorFromName(SELECTORS.HitByObstacle), - DiscoveredBeast: hash.getSelectorFromName(SELECTORS.DiscoveredBeast), - AmbushedByBeast: hash.getSelectorFromName(SELECTORS.AmbushedByBeast), - AttackedBeast: hash.getSelectorFromName(SELECTORS.AttackedBeast), - AttackedByBeast: hash.getSelectorFromName(SELECTORS.AttackedByBeast), - SlayedBeast: hash.getSelectorFromName(SELECTORS.SlayedBeast), - FleeFailed: hash.getSelectorFromName(SELECTORS.FleeFailed), - FleeSucceeded: hash.getSelectorFromName(SELECTORS.FleeSucceeded), - PurchasedItems: hash.getSelectorFromName(SELECTORS.PurchasedItems), - PurchasedPotions: hash.getSelectorFromName(SELECTORS.PurchasedPotions), - EquippedItems: hash.getSelectorFromName(SELECTORS.EquippedItems), - DroppedItems: hash.getSelectorFromName(SELECTORS.DroppedItems), - GreatnessIncreased: hash.getSelectorFromName(SELECTORS.GreatnessIncreased), - ItemsLeveledUp: hash.getSelectorFromName(SELECTORS.ItemsLeveledUp), - NewHighScore: hash.getSelectorFromName(SELECTORS.NewHighScore), - AdventurerDied: hash.getSelectorFromName(SELECTORS.AdventurerDied), - AdventurerLeveledUp: hash.getSelectorFromName(SELECTORS.AdventurerLeveledUp), - UpgradesAvailable: hash.getSelectorFromName(SELECTORS.UpgradesAvailable), - Transfer: hash.getSelectorFromName(SELECTORS.Transfer), + StartGame: hash.getSelectorFromName(SELECTORS.StartGame), + AdventurerUpgraded: hash.getSelectorFromName(SELECTORS.AdventurerUpgraded), + DiscoveredHealth: hash.getSelectorFromName(SELECTORS.DiscoveredHealth), + DiscoveredGold: hash.getSelectorFromName(SELECTORS.DiscoveredGold), + DiscoveredLoot: hash.getSelectorFromName(SELECTORS.DiscoveredLoot), + DiscoveredXP: hash.getSelectorFromName(SELECTORS.DiscoveredXP), + EquipmentChanged: hash.getSelectorFromName(SELECTORS.EquipmentChanged), + DodgedObstacle: hash.getSelectorFromName(SELECTORS.DodgedObstacle), + HitByObstacle: hash.getSelectorFromName(SELECTORS.HitByObstacle), + DiscoveredBeast: hash.getSelectorFromName(SELECTORS.DiscoveredBeast), + AmbushedByBeast: hash.getSelectorFromName(SELECTORS.AmbushedByBeast), + AttackedBeast: hash.getSelectorFromName(SELECTORS.AttackedBeast), + AttackedByBeast: hash.getSelectorFromName(SELECTORS.AttackedByBeast), + SlayedBeast: hash.getSelectorFromName(SELECTORS.SlayedBeast), + FleeFailed: hash.getSelectorFromName(SELECTORS.FleeFailed), + FleeSucceeded: hash.getSelectorFromName(SELECTORS.FleeSucceeded), + PurchasedItems: hash.getSelectorFromName(SELECTORS.PurchasedItems), + PurchasedPotions: hash.getSelectorFromName(SELECTORS.PurchasedPotions), + EquippedItems: hash.getSelectorFromName(SELECTORS.EquippedItems), + DroppedItems: hash.getSelectorFromName(SELECTORS.DroppedItems), + GreatnessIncreased: hash.getSelectorFromName(SELECTORS.GreatnessIncreased), + ItemsLeveledUp: hash.getSelectorFromName(SELECTORS.ItemsLeveledUp), + NewHighScore: hash.getSelectorFromName(SELECTORS.NewHighScore), + AdventurerDied: hash.getSelectorFromName(SELECTORS.AdventurerDied), + AdventurerLeveledUp: hash.getSelectorFromName( + SELECTORS.AdventurerLeveledUp + ), + UpgradesAvailable: hash.getSelectorFromName(SELECTORS.UpgradesAvailable), + Transfer: hash.getSelectorFromName(SELECTORS.Transfer), }; diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index b16dce2..6bd540e 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -1,21 +1,21 @@ { - "compilerOptions": { - "target": "esnext", - "module": "ESNext", - "declaration": true, - "outDir": "./dist", - "sourceMap": true, - "noImplicitAny": true, - "noUnusedLocals": true, - "moduleResolution": "bundler", - "noUnusedParameters": true, - "resolveJsonModule": true, + "compilerOptions": { + "target": "esnext", + "module": "ESNext", + "declaration": true, + "outDir": "./dist", + "sourceMap": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "moduleResolution": "bundler", + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "strictNullChecks": true, + "esModuleInterop": true + }, + "include": ["src/**/*.ts", "src/provider/QueryProvider.tsx"], "skipLibCheck": true, - "strict": true, - "strictNullChecks": true, - "esModuleInterop": true - }, - "include": ["src/**/*.ts", "src/provider/QueryProvider.tsx"], - "skipLibCheck": true, - "exclude": ["node_modules", "dist", "**/*.test.ts"] + "exclude": ["node_modules", "dist", "**/*.test.ts"] } diff --git a/packages/core/tsup.config.ts b/packages/core/tsup.config.ts index 52bdfce..30da7a9 100644 --- a/packages/core/tsup.config.ts +++ b/packages/core/tsup.config.ts @@ -1,11 +1,11 @@ import { defineConfig } from "tsup"; export default defineConfig({ - entry: ["src/index.ts"], - target: "esnext", - format: ["esm"], - dts: true, - sourcemap: true, - clean: true, - minify: true, + entry: ["src/index.ts"], + target: "esnext", + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + minify: true, }); diff --git a/packages/react/package.json b/packages/react/package.json index 723bac6..f9047d1 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,40 +1,40 @@ { - "name": "@lootsurvivor/react", - "version": "0.0.1", - "description": "Useful React hooks for Survivor", - "source": "src/index.ts", - "main": "dist/index.js", - "type": "module", - "scripts": { - "build": "tsup --dts-resolve", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "license": "MIT", - "peerDependencies": { - "react": "^18.2.0", - "starknet": "6.11.0" - }, - "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" + "name": "@lootsurvivor/react", + "version": "0.0.1", + "description": "Useful React hooks for Survivor", + "source": "src/index.ts", + "main": "dist/index.js", + "type": "module", + "scripts": { + "build": "tsup --dts-resolve", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "license": "MIT", + "peerDependencies": { + "react": "^18.2.0", + "starknet": "6.11.0" + }, + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "devDependencies": { + "@babel/core": "^7.21.4", + "@babel/preset-env": "^7.21.4", + "@types/js-cookie": "^3.0.3", + "@types/node": "^18.15.11", + "@types/react": "^18.2.33", + "@types/react-dom": "^18.0.11", + "@types/web": "^0.0.114", + "tsup": "^8.0.1", + "typescript": "^5.0.3" + }, + "dependencies": { + "get-starknet-core": "^3.2.0", + "graphql-request": "^7.1.0", + "@lootsurvivor/core": "workspace:^", + "@tanstack/react-query": "^5.51.15" } - }, - "devDependencies": { - "@babel/core": "^7.21.4", - "@babel/preset-env": "^7.21.4", - "@types/js-cookie": "^3.0.3", - "@types/node": "^18.15.11", - "@types/react": "^18.2.33", - "@types/react-dom": "^18.0.11", - "@types/web": "^0.0.114", - "tsup": "^8.0.1", - "typescript": "^5.0.3" - }, - "dependencies": { - "get-starknet-core": "^3.2.0", - "graphql-request": "^7.1.0", - "@lootsurvivor/core": "workspace:^", - "@tanstack/react-query": "^5.51.15" - } } diff --git a/packages/react/src/providers/index.tsx b/packages/react/src/providers/index.tsx index 5abf36f..1da875b 100644 --- a/packages/react/src/providers/index.tsx +++ b/packages/react/src/providers/index.tsx @@ -4,7 +4,9 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const queryClient = new QueryClient(); export function QueryProvider({ children }: { children: React.ReactNode }) { - return ( - {children} - ); + return ( + + {children} + + ); } diff --git a/packages/react/src/queries/index.ts b/packages/react/src/queries/index.ts index e5a493a..1146a1a 100644 --- a/packages/react/src/queries/index.ts +++ b/packages/react/src/queries/index.ts @@ -1,205 +1,205 @@ import { useQuery, UseQueryResult } from "@tanstack/react-query"; import { request } from "graphql-request"; import { - getAdventurer, - getDiscoveries, - getLatestDiscoveries, - getLastDiscovery, - getLastBeastDiscovery, - getDiscoveryByTxHash, - getAdventurersByOwner, - getAdventurerById, - getAdventurersInList, - getAdventurersInListByXp, - getAdventurerByGold, - getBeastsByAdventurer, - getBeast, - getKilledBeasts, - getLatestBattlesByAdventurer, - getBattlesByBeast, - getLastBattleByAdventurer, - getBattlesByAdventurer, - getBattleByTxHash, - getItems, - getItemsByTokenId, - getLatestMarketItems, - getItemsByOwner, - getItemsByAdventurer, - getAdventurerByXP, - getAdventurersByXPPaginated, - getTopScores, - getScoresInList, - getGoldenTokensByOwner, - getScoresAndAdventurers, + getAdventurer, + getDiscoveries, + getLatestDiscoveries, + getLastDiscovery, + getLastBeastDiscovery, + getDiscoveryByTxHash, + getAdventurersByOwner, + getAdventurerById, + getAdventurersInList, + getAdventurersInListByXp, + getAdventurerByGold, + getBeastsByAdventurer, + getBeast, + getKilledBeasts, + getLatestBattlesByAdventurer, + getBattlesByBeast, + getLastBattleByAdventurer, + getBattlesByAdventurer, + getBattleByTxHash, + getItems, + getItemsByTokenId, + getLatestMarketItems, + getItemsByOwner, + getItemsByAdventurer, + getAdventurerByXP, + getAdventurersByXPPaginated, + getTopScores, + getScoresInList, + getGoldenTokensByOwner, + getScoresAndAdventurers, } from "@lootsurvivor/core"; import { AdventurerComplete } from "@lootsurvivor/core"; const DEFAULT_ENDPOINT = "https://ls-indexer-sepolia.provable.games/graphql"; function createQueryHook( - queryKey: string[], - query: string + queryKey: string[], + query: string ) { - return (variables?: TVariables, endpoint?: string): UseQueryResult => - useQuery({ - queryKey: [...queryKey, variables], - queryFn: async () => { - const data = await request( - endpoint || DEFAULT_ENDPOINT, - query, - variables || {} - ); - return data; - }, - }); + return (variables?: TVariables, endpoint?: string): UseQueryResult => + useQuery({ + queryKey: [...queryKey, variables], + queryFn: async () => { + const data = await request( + endpoint || DEFAULT_ENDPOINT, + query, + variables || {} + ); + return data; + }, + }); } export const useAdventurer = createQueryHook< - { adventurers: Array }, - { owner: string } + { adventurers: Array }, + { owner: string } >(["adventurer"], getAdventurer); export const useDiscoveries = createQueryHook< - { discoveries: Array }, - { id: string } + { discoveries: Array }, + { id: string } >(["discoveries"], getDiscoveries); export const useLatestDiscoveries = createQueryHook< - { discoveries: Array }, - { id: string } + { discoveries: Array }, + { id: string } >(["latestDiscoveries"], getLatestDiscoveries); export const useLastDiscovery = createQueryHook< - { discoveries: Array }, - { adventurerId: string } + { discoveries: Array }, + { adventurerId: string } >(["lastDiscovery"], getLastDiscovery); export const useLastBeastDiscovery = createQueryHook< - { discoveries: Array }, - { id: string } + { discoveries: Array }, + { id: string } >(["lastBeastDiscovery"], getLastBeastDiscovery); export const useDiscoveryByTxHash = createQueryHook< - { discoveries: Array }, - { txHash: string } + { discoveries: Array }, + { txHash: string } >(["discoveryByTxHash"], getDiscoveryByTxHash); export const useItems = createQueryHook<{ items: Array }, {}>( - ["items"], - getItems + ["items"], + getItems ); export const useAdventurersByOwner = createQueryHook< - { adventurers: Array }, - { owner: string } + { adventurers: Array }, + { owner: string } >(["adventurersByOwner"], getAdventurersByOwner); export const useAdventurerById = createQueryHook< - { adventurers: Array }, - { id: string } + { adventurers: Array }, + { id: string } >(["adventurerById"], getAdventurerById); export const useAdventurersInList = createQueryHook< - { adventurers: Array }, - { ids: string[] } + { adventurers: Array }, + { ids: string[] } >(["adventurersInList"], getAdventurersInList); export const useAdventurersInListByXp = createQueryHook< - { adventurers: Array }, - { ids: string[] } + { adventurers: Array }, + { ids: string[] } >(["adventurersInListByXp"], getAdventurersInListByXp); export const useAdventurerByGold = createQueryHook< - { adventurers: Array }, - {} + { adventurers: Array }, + {} >(["adventurerByGold"], getAdventurerByGold); export const useAdventurerByXP = createQueryHook< - { adventurers: Array }, - {} + { adventurers: Array }, + {} >(["adventurerByXP"], getAdventurerByXP); export const useAdventurersByXPPaginated = createQueryHook< - { adventurers: Array }, - { skip: number } + { adventurers: Array }, + { skip: number } >(["adventurersByXPPaginated"], getAdventurersByXPPaginated); export const useBeast = createQueryHook< - { beasts: Array }, - { beast: string; adventurerId: string; seed: string } + { beasts: Array }, + { beast: string; adventurerId: string; seed: string } >(["beast"], getBeast); export const useKilledBeasts = createQueryHook<{ beasts: Array }, {}>( - ["killedBeasts"], - getKilledBeasts + ["killedBeasts"], + getKilledBeasts ); export const useBeastsByAdventurer = createQueryHook< - { discoveries: Array }, - { id: string } + { discoveries: Array }, + { id: string } >(["beastsByAdventurer"], getBeastsByAdventurer); export const useLatestBattlesByAdventurer = createQueryHook< - { battles: Array }, - { adventurerId: string } + { battles: Array }, + { adventurerId: string } >(["latestBattlesByAdventurer"], getLatestBattlesByAdventurer); export const useBattlesByAdventurer = createQueryHook< - { battles: Array }, - { adventurerId: string } + { battles: Array }, + { adventurerId: string } >(["battlesByAdventurer"], getBattlesByAdventurer); export const useBattlesByBeast = createQueryHook< - { battles: Array }, - { adventurerId: string; beast: string; seed: string } + { battles: Array }, + { adventurerId: string; beast: string; seed: string } >(["battlesByBeast"], getBattlesByBeast); export const useLastBattleByAdventurer = createQueryHook< - { battles: Array }, - { adventurerId: string } + { battles: Array }, + { adventurerId: string } >(["lastBattleByAdventurer"], getLastBattleByAdventurer); export const useBattleByTxHash = createQueryHook< - { battles: Array }, - { txHash: string } + { battles: Array }, + { txHash: string } >(["battleByTxHash"], getBattleByTxHash); export const useItemsByTokenId = createQueryHook< - { items: Array }, - { item: string } + { items: Array }, + { item: string } >(["itemsByTokenId"], getItemsByTokenId); export const useLatestMarketItems = createQueryHook< - { items: Array }, - { id: string } + { items: Array }, + { id: string } >(["latestMarketItems"], getLatestMarketItems); export const useItemsByAdventurer = createQueryHook< - { items: Array }, - { id: string } + { items: Array }, + { id: string } >(["itemsByAdventurer"], getItemsByAdventurer); export const useItemsByOwner = createQueryHook< - { items: Array }, - { owner: string } + { items: Array }, + { owner: string } >(["itemsByOwner"], getItemsByOwner); export const useTopScores = createQueryHook<{ scores: Array }, {}>( - ["topScores"], - getTopScores + ["topScores"], + getTopScores ); export const useScoresInList = createQueryHook< - { scores: Array }, - { ids: number[] } + { scores: Array }, + { ids: number[] } >(["scoresInList"], getScoresInList); export const useGoldenTokensByOwner = createQueryHook< - { getERC721Tokens: Array }, - { contractAddress: string; owner: string } + { getERC721Tokens: Array }, + { contractAddress: string; owner: string } >(["goldenTokensByOwner"], getGoldenTokensByOwner); export const useScoresAndAdventurers = createQueryHook< - { scores: Array; adventurers: Array }, - {} + { scores: Array; adventurers: Array }, + {} >(["scoresAndAdventurers"], getScoresAndAdventurers); diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json index ef400fb..c33a6fb 100644 --- a/packages/react/tsconfig.json +++ b/packages/react/tsconfig.json @@ -1,30 +1,30 @@ { - "compilerOptions": { - "outDir": "./dist", - "module": "ESNext", - "moduleResolution": "bundler", - "esModuleInterop": true, - "isolatedModules": true, - "forceConsistentCasingInFileNames": true, - "jsx": "react-jsx", - "lib": ["esnext", "DOM"], - "sourceMap": true, - "declaration": true, - "noImplicitAny": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "strictNullChecks": true, - "target": "esnext", - "types": ["node", "web"], - "baseUrl": ".", - "rootDir": "src", - "paths": { - "~/*": ["src/*"] - } - }, - "include": ["src/**/*.ts", "src/providers/index.tsx"], - "exclude": ["node_modules", "dist", "**/*.test.ts"] + "compilerOptions": { + "outDir": "./dist", + "module": "ESNext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "isolatedModules": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "lib": ["esnext", "DOM"], + "sourceMap": true, + "declaration": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "strictNullChecks": true, + "target": "esnext", + "types": ["node", "web"], + "baseUrl": ".", + "rootDir": "src", + "paths": { + "~/*": ["src/*"] + } + }, + "include": ["src/**/*.ts", "src/providers/index.tsx"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] } diff --git a/packages/react/tsup.config.ts b/packages/react/tsup.config.ts index 52bdfce..30da7a9 100644 --- a/packages/react/tsup.config.ts +++ b/packages/react/tsup.config.ts @@ -1,11 +1,11 @@ import { defineConfig } from "tsup"; export default defineConfig({ - entry: ["src/index.ts"], - target: "esnext", - format: ["esm"], - dts: true, - sourcemap: true, - clean: true, - minify: true, + entry: ["src/index.ts"], + target: "esnext", + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + minify: true, }); diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index cdce547..b493be1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,3 @@ packages: - - "packages/*" - - "clients/*" + - "packages/*" + - "clients/*" diff --git a/readme.md b/readme.md index 834bb15..208bd4b 100644 --- a/readme.md +++ b/readme.md @@ -10,24 +10,24 @@ Currently the only package is the `core` which is vanilla.js of the following: ## Features -- [x] Execution client -- [x] Game Constants -- [x] Hosted Images -- [x] Zustand State -- [] Abstracted Graphql queries for deep information -- [] gPRC provider -- [] React Package +- [x] Execution client +- [x] Game Constants +- [x] Hosted Images +- [x] Zustand State +- [] Abstracted Graphql queries for deep information +- [] gPRC provider +- [] React Package ## Usage ```js // Full Provider which exposes all the Managers along with an execution client const survivor = new LootSurvivor( - nodeUrl, - lootSurvivorAddress, - beastsAddress, - goldenTokenAddress, - account + nodeUrl, + lootSurvivorAddress, + beastsAddress, + goldenTokenAddress, + account ); // usage