diff --git a/bun.lock b/bun.lock index 6749cc7d..b0030f63 100644 --- a/bun.lock +++ b/bun.lock @@ -19,14 +19,14 @@ "name": "@lore/client", "version": "0.0.0", "dependencies": { - "@cartridge/controller": "^0.7.13", + "@cartridge/controller": "^0.9.2", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@dojoengine/core": "^1.5.10", - "@dojoengine/create-burner": "^1.5.10", - "@dojoengine/sdk": "^1.5.10", - "@dojoengine/utils": "^1.5.10", + "@dojoengine/core": "^1.6.0", + "@dojoengine/create-burner": "^1.6.0", + "@dojoengine/sdk": "^1.6.0", + "@dojoengine/utils": "^1.6.0", "@lore/contracts": "workspace:*", "@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-context-menu": "^2.2.6", @@ -45,7 +45,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "sonner": "^2.0.3", - "starknet": "^6.23.1", + "starknet": "^7.6.4", "tailwind-merge": "^3.0.1", "tailwindcss": "^4.0.3", "tw-animate-css": "^1.2.5", @@ -157,14 +157,12 @@ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.0-beta.1", "", { "os": "win32", "cpu": "x64" }, "sha512-o8W6+DX0YRjt1kS8Y3ismq6EkjwiVDv7X0TEpfnFywoVG8HoJ7G7/m9r8LM1yE46WI3maPH2A0MoVpQ1ZNG++A=="], - "@cartridge/account-wasm": ["@cartridge/account-wasm@0.7.13", "", {}, "sha512-2/cFS96jN31N9piK2Hs/Egxz6cstJ9gV8pgBrqo5vSPckFv2yD9u/8euqDD26MJtLnDa59OYefNJBbKgsK9vHg=="], + "@cartridge/controller": ["@cartridge/controller@0.9.2", "", { "dependencies": { "@cartridge/controller-wasm": "^0.2.3", "@cartridge/penpal": "^6.2.4", "@starknet-io/types-js": "^0.8.4", "@telegram-apps/sdk": "^2.4.0", "@turnkey/sdk-browser": "^4.0.0", "@walletconnect/ethereum-provider": "^2.20.0", "cbor-x": "^1.5.0", "ethers": "^6.13.5", "mipd": "^0.0.7" }, "peerDependencies": { "@metamask/sdk": "^0.32.1", "@solana/web3.js": "^1.98.0", "open": "^10.1.0", "starknet": "^7.6.2", "starknetkit": "^2.6.1" } }, "sha512-bfU0U3/M3idpEllysZckxhLU/9PdslvNW3JrBcsDFZcfm7lko7LQv1dStGHmA/7hzZdPCrrUYIGRimOBzYBbrQ=="], - "@cartridge/controller": ["@cartridge/controller@0.7.13", "", { "dependencies": { "@cartridge/account-wasm": "0.7.13", "@cartridge/penpal": "^6.2.4", "@cartridge/utils": "0.7.13", "@starknet-io/types-js": "^0.7.7", "@telegram-apps/sdk": "^2.4.0", "@turnkey/sdk-browser": "^4.0.0", "@walletconnect/ethereum-provider": "^2.20.0", "cbor-x": "^1.5.0", "ethers": "^6.13.5", "mipd": "^0.0.7" }, "peerDependencies": { "@metamask/sdk": "^0.32.1", "@solana/web3.js": "^1.98.0", "open": "^10.1.0", "starknet": "^6.21.0", "starknetkit": "^2.6.1" } }, "sha512-LBor+FxYtAETgj8+m+23BDVhwXEZ+FqtwaLAVBEen6oz4//o3pU+wP2xBl8YAtcAL6mJ8gYcOhfqdHK/Ux5rwQ=="], + "@cartridge/controller-wasm": ["@cartridge/controller-wasm@0.2.4", "", {}, "sha512-v3CNvWc5DAu87ieSO1v5DwAfy+bQOFAev1HNZDGGlgdew/xorZkNHCkR+eyqdTNBbkJoy9Yrj8w6tHh1zr6+6Q=="], "@cartridge/penpal": ["@cartridge/penpal@6.2.4", "", {}, "sha512-tdpOnSJJBFMlgLZ1+z9Ho5e6cG5EgMAb1Cmmh1lGT2tmplogU/XPMjLE6CwvKAPDoe6a38iMnbH+ySTAWWIOKA=="], - "@cartridge/utils": ["@cartridge/utils@0.7.13", "", { "dependencies": { "posthog-js-lite": "3.2.1", "react": "^18.2.0", "react-query": "^3.39.2", "starknet": "^6.21.0", "swr": "^2.2.5", "viem": "^2.21.32" } }, "sha512-gNoDw9TCvBaOPh3swGyGY383Am4e/gBFR9mMFq0nkRyfqYOiMlnnxZkGGDYbFSz5LAqbyB80JizmgaTVtKsi4w=="], - "@cbor-extract/cbor-extract-darwin-arm64": ["@cbor-extract/cbor-extract-darwin-arm64@2.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w=="], "@cbor-extract/cbor-extract-darwin-x64": ["@cbor-extract/cbor-extract-darwin-x64@2.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w=="], @@ -189,19 +187,19 @@ "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="], - "@dojoengine/core": ["@dojoengine/core@1.5.11", "", { "dependencies": { "@dojoengine/recs": "2.0.13", "starknet": "6.23.1", "zod": "^3.23.8" } }, "sha512-Vo1/0MYG7vJnIjBwasLOHe3Zj3wZ5r8/oGhIszXqcri+PX74PhRqCsd2vjIG8iBn7HMIfE4aBCpHxBmqeDJS2w=="], + "@dojoengine/core": ["@dojoengine/core@1.6.0", "", { "dependencies": { "@dojoengine/recs": "2.0.13", "starknet": "^7.6.2", "zod": "^3.23.8" } }, "sha512-r9pefQDCCb3D3fOOn14mXlaWXXRd65NLZsy+YXjOrOb9nUO5yK1V6+BUIFIi4oaBEUNLaO5Pk2WRsSoBPpyGCA=="], - "@dojoengine/create-burner": ["@dojoengine/create-burner@1.5.11", "", { "dependencies": { "@dojoengine/core": "1.5.11", "@scure/bip32": "^1.5.0", "@starknet-react/core": "^3.6.2", "encoding": "^0.1.13", "get-starknet-core": "^4.0.0", "js-cookie": "^3.0.5" }, "peerDependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "starknet": "6.23.1" } }, "sha512-Uo1eL62BgllImNd3dhXbA6Ql9Wm5QgzEwSr9/TwHyzb4xs9CLdgvw9uWJyiCdXpsWncRRwVtIXxIb1uQypdhPA=="], + "@dojoengine/create-burner": ["@dojoengine/create-burner@1.6.0", "", { "dependencies": { "@dojoengine/core": "1.6.0", "@scure/bip32": "^1.5.0", "@starknet-io/get-starknet-core": "4.0.7", "@starknet-react/core": "^4.0.1-beta.4", "encoding": "^0.1.13", "js-cookie": "^3.0.5" }, "peerDependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "starknet": "^7.6.2" } }, "sha512-U727Up3bKaWrDQdJ1isGmaGm+E1Vwx6flsN9yQOODcPE4eltbMWoq5SocGUblXISB8id2isMxlFHC3dxCXwD4A=="], "@dojoengine/recs": ["@dojoengine/recs@2.0.13", "", { "dependencies": { "@latticexyz/schema-type": "2.0.12", "@latticexyz/utils": "2.0.12", "mobx": "^6.7.0", "rxjs": "7.5.5" } }, "sha512-Cgz4Unlnk2FSDoFTYKrJexX/KiSYPMFMxftxQkC+9LUKS5yNGkgFQM7xu4/L1HvpDAenL7NjUmH6ynRAS7Iifw=="], - "@dojoengine/sdk": ["@dojoengine/sdk@1.5.11", "", { "dependencies": { "@dojoengine/core": "1.5.11", "@dojoengine/torii-client": "1.5.11", "@dojoengine/torii-wasm": "1.5.11", "@dojoengine/utils": "1.5.11", "@starknet-react/chains": "^3.1.0", "@starknet-react/core": "^3.6.2", "immer": "^10.1.1", "neverthrow": "^8.2.0", "zustand": "^4.5.6" }, "peerDependencies": { "@tanstack/react-query": "^5.62.16", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", "react": "^18.3.1", "react-dom": "^18.3.1", "starknet": "6.23.1" } }, "sha512-ZM7Uy8O83Y0oNf58kGvifmdFhwJCSfHk52FyYRuKwGTpJWft3w3v26OjgLw38gyGWt+LWtFznEDIXRIaOR6e3g=="], + "@dojoengine/sdk": ["@dojoengine/sdk@1.6.0", "", { "dependencies": { "@dojoengine/core": "1.6.0", "@dojoengine/torii-client": "1.6.0", "@dojoengine/torii-wasm": "1.6.0", "@dojoengine/utils": "1.6.0", "@starknet-react/chains": "^4.0.1-beta.3", "@starknet-react/core": "^4.0.1-beta.4", "effect": "^3.17.1", "immer": "^10.1.1", "neverthrow": "^8.2.0", "zustand": "^4.5.6" }, "peerDependencies": { "@tanstack/react-query": "^5.62.16", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", "react": "^18.3.1", "react-dom": "^18.3.1", "starknet": "^7.6.2" } }, "sha512-MQcB+s2O989ns0gisCFL0HuEegn/Byfi5JDlxsYQDDEEatAFqrDyUX/gd+NQfNIJ2flQtptqTJaS3w0fElBNkA=="], - "@dojoengine/torii-client": ["@dojoengine/torii-client@1.5.11", "", { "dependencies": { "@dojoengine/torii-wasm": "1.5.11" } }, "sha512-5fVn4LUYWHXzaWKvTaBm6wvWsatXHEXoxV2P/fwjuRN9cKeU+yuFdlsdkDgNOttkBzkNijAjSI/DQepIlY38Kg=="], + "@dojoengine/torii-client": ["@dojoengine/torii-client@1.6.0", "", { "dependencies": { "@dojoengine/torii-wasm": "1.6.0" } }, "sha512-orKpFwbYrJ2Jv5I7KN4+rCKLlRMe+x7/OQT6cYvozGhwHBQrnzf3yki9SjWNtU0VYC9RgUs4asFMVlYgPDWOeA=="], - "@dojoengine/torii-wasm": ["@dojoengine/torii-wasm@1.5.11", "", {}, "sha512-GKd+LhNcFshj7RvV+2fHA016wqXHLpBJmmV5WxPKVA9lepO88v/sZzTpDhGTuIy8qiMRjzmQslZ/ruH7rWl0Fg=="], + "@dojoengine/torii-wasm": ["@dojoengine/torii-wasm@1.6.0", "", {}, "sha512-57GuoZGVOpBSR298b/B0NbaVIS9CUOudyKW1PpE4CkI4MsL5R+F2LN51nnoHF5EaHU4/XICoBNw64jpHAejkOA=="], - "@dojoengine/utils": ["@dojoengine/utils@1.5.11", "", { "dependencies": { "@dojoengine/recs": "2.0.13", "@latticexyz/utils": "^2.2.8", "mathjs": "^12.4.3", "micro-starknet": "^0.2.3" }, "peerDependencies": { "starknet": "6.23.1" } }, "sha512-DgbA0ewHiNjxZ/wD1Ukafxlnu6tsyVrHP0TZVzC8nGhBw3hZDZyHSdvrYoF1VPUD7wtHQEXQPs1xaVxQ/QxhaQ=="], + "@dojoengine/utils": ["@dojoengine/utils@1.6.0", "", { "dependencies": { "@dojoengine/recs": "2.0.13", "@latticexyz/utils": "^2.2.8", "mathjs": "^12.4.3", "micro-starknet": "^0.2.3" }, "peerDependencies": { "starknet": "^7.6.2" } }, "sha512-WAVZhHPEq5GhZZwtBkuwrm5HBXElN8zJbI67k8POBdwbsM/PTlAmiP0DbVozZOqENBFHwfzPT+zKSDPra+Nslw=="], "@ecies/ciphers": ["@ecies/ciphers@0.2.3", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-tapn6XhOueMwht3E2UzY0ZZjYokdaw9XtL9kEyjhQ/Fb9vL9xTFbOaI+fV0AWvTpYu4BNloC6getKW6NtSg4mA=="], @@ -515,15 +513,21 @@ "@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@starknet-io/get-starknet": ["@starknet-io/get-starknet@4.0.7", "", { "dependencies": { "@starknet-io/get-starknet-core": "4.0.7", "bowser": "^2.11.0" } }, "sha512-env/ZN5EmDJ6vDtIgjOjsEvvzdKBDaWZ0aLe79IVJ7lq2icqKbX86yR1/CUf4AU31AFYZHWZ3daOaGFT4nHSUQ=="], "@starknet-io/get-starknet-core": ["@starknet-io/get-starknet-core@4.0.7", "", { "dependencies": { "@module-federation/runtime": "^0.1.2", "@starknet-io/types-js": "^0.7.7", "async-mutex": "^0.5.0" } }, "sha512-ocwQTdDvGa+0CvjGygyaTuFkda2R82dofydts8uXr9p0Diy/bmYW1fkuqJfi1nC5M+YcvvuEpl6wFvwXM1og5w=="], - "@starknet-io/types-js": ["@starknet-io/types-js@0.7.10", "", {}, "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w=="], + "@starknet-io/starknet-types-07": ["@starknet-io/types-js@0.7.10", "", {}, "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w=="], + + "@starknet-io/starknet-types-08": ["@starknet-io/types-js@0.8.4", "", {}, "sha512-0RZ3TZHcLsUTQaq1JhDSCM8chnzO4/XNsSCozwDET64JK5bjFDIf2ZUkta+tl5Nlbf4usoU7uZiDI/Q57kt2SQ=="], + + "@starknet-io/types-js": ["@starknet-io/types-js@0.8.4", "", {}, "sha512-0RZ3TZHcLsUTQaq1JhDSCM8chnzO4/XNsSCozwDET64JK5bjFDIf2ZUkta+tl5Nlbf4usoU7uZiDI/Q57kt2SQ=="], - "@starknet-react/chains": ["@starknet-react/chains@3.1.2", "", {}, "sha512-/Oldb4AVYdvHXzlBRxu01s0WwdOjqL2Q47BAmiv07/aBRU7mSZl6r/6l+bYKNrChSZl7WVnlpb+ojfwhRpbGcw=="], + "@starknet-react/chains": ["@starknet-react/chains@4.0.3", "", {}, "sha512-tuKjBS4a0EZUFquN3CWc4GznTx1+nA0062BPlO55Sqqk/hfEhJO1Ez02cft47FkIlr52xiuSsa1ruluSpogmww=="], - "@starknet-react/core": ["@starknet-react/core@3.7.2", "", { "dependencies": { "@starknet-io/types-js": "^0.7.7", "@starknet-react/chains": "^3.1.2", "@tanstack/react-query": "^5.25.0", "abi-wan-kanabi": "^2.2.4", "eventemitter3": "^5.0.1", "viem": "^2.21.1", "zod": "^3.22.4" }, "peerDependencies": { "get-starknet-core": "^4.0.0", "react": "^18.0", "starknet": "^6.11.0" } }, "sha512-5l6IlXqYzbnInVPwL2VdSwmIiaNnTjm4iYnEGSSVUUPyklHr3bdXaZoILJWcIQ0NaF2ebhNhoKYL8GDHiSKTPw=="], + "@starknet-react/core": ["@starknet-react/core@4.0.3", "", { "dependencies": { "@starknet-io/types-js": "^0.7.10", "@starknet-react/chains": "^4.0.3", "@tanstack/react-query": "^5.25.0", "abi-wan-kanabi": "^2.2.4", "eventemitter3": "^5.0.1", "viem": "^2.21.1", "zod": "^3.22.4" }, "peerDependencies": { "get-starknet-core": "^4.0.0", "react": "^18.0", "starknet": "^7.6.4" } }, "sha512-/4gMPll1I4yJJKFHw9UWiArgswOLrskoswH+jRJME21E+VRn4dR/QDYUhm+ZqVFbjfq8vvzw4Ojx/OdaRF+qdA=="], "@swc/core": ["@swc/core@1.11.8", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.19" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.11.8", "@swc/core-darwin-x64": "1.11.8", "@swc/core-linux-arm-gnueabihf": "1.11.8", "@swc/core-linux-arm64-gnu": "1.11.8", "@swc/core-linux-arm64-musl": "1.11.8", "@swc/core-linux-x64-gnu": "1.11.8", "@swc/core-linux-x64-musl": "1.11.8", "@swc/core-win32-arm64-msvc": "1.11.8", "@swc/core-win32-ia32-msvc": "1.11.8", "@swc/core-win32-x64-msvc": "1.11.8" }, "peerDependencies": { "@swc/helpers": "*" }, "optionalPeers": ["@swc/helpers"] }, "sha512-UAL+EULxrc0J73flwYHfu29mO8CONpDJiQv1QPDXsyCvDUcEhqAqUROVTgC+wtJCFFqMQdyr4stAA5/s0KSOmA=="], @@ -705,7 +709,7 @@ "abi-wan-kanabi": ["abi-wan-kanabi@2.2.4", "", { "dependencies": { "ansicolors": "^0.3.2", "cardinal": "^2.1.1", "fs-extra": "^10.0.0", "yargs": "^17.7.2" }, "bin": { "generate": "dist/generate.js" } }, "sha512-0aA81FScmJCPX+8UvkXLki3X1+yPQuWxEkqXBVKltgPAK79J+NB+Lp5DouMXa7L6f+zcRlIA/6XO7BN/q9fnvg=="], - "abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], + "abitype": ["abitype@1.0.0", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ=="], "aes-js": ["aes-js@4.0.0-beta.5", "", {}, "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q=="], @@ -733,14 +737,10 @@ "axios": ["axios@1.8.4", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw=="], - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "big-integer": ["big-integer@1.6.52", "", {}, "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="], - "big.js": ["big.js@6.2.2", "", {}, "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ=="], "bignumber.js": ["bignumber.js@9.1.2", "", {}, "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="], @@ -751,10 +751,6 @@ "bowser": ["bowser@2.11.0", "", {}, "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="], - "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], - - "broadcast-channel": ["broadcast-channel@3.7.0", "", { "dependencies": { "@babel/runtime": "^7.7.2", "detect-node": "^2.1.0", "js-sha3": "0.8.0", "microseconds": "0.2.0", "nano-time": "1.0.0", "oblivious-set": "1.0.0", "rimraf": "3.0.2", "unload": "2.2.0" } }, "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg=="], - "browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="], "bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], @@ -807,8 +803,6 @@ "complex.js": ["complex.js@2.4.2", "", {}, "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g=="], - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], @@ -849,8 +843,6 @@ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], - "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], - "derive-valtio": ["derive-valtio@0.1.0", "", { "peerDependencies": { "valtio": "*" } }, "sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A=="], "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], @@ -859,8 +851,6 @@ "detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], - "detect-node": ["detect-node@2.1.0", "", {}, "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="], - "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], "dettle": ["dettle@1.0.5", "", {}, "sha512-ZVyjhAJ7sCe1PNXEGveObOH9AC8QvMga3HJIghHawtG7mE4K5pW9nz/vDGAr/U7a3LWgdOzEE7ac9MURnyfaTA=="], @@ -875,6 +865,8 @@ "eciesjs": ["eciesjs@0.4.15", "", { "dependencies": { "@ecies/ciphers": "^0.2.3", "@noble/ciphers": "^1.3.0", "@noble/curves": "^1.9.1", "@noble/hashes": "^1.8.0" } }, "sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA=="], + "effect": ["effect@3.17.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-FbFMr6xBXPII5Od8QJnkHz+2GTmQgq+8NPQev6C2k9cf1lcUjQ4vpw1kjbMc2X0UkjIkIWe0EtNK2RV6bl34UQ=="], + "electron-to-chromium": ["electron-to-chromium@1.5.136", "", {}, "sha512-kL4+wUTD7RSA5FHx5YwWtjDnEEkIIikFgWHR4P6fqjw1PPLlqYkxeOb++wAauAssat0YClCy8Y3C5SxgSkjibQ=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -931,6 +923,8 @@ "eyes": ["eyes@0.1.8", "", {}, "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ=="], + "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-redact": ["fast-redact@3.5.0", "", {}, "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="], @@ -955,8 +949,6 @@ "fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], - "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -973,8 +965,6 @@ "get-starknet-core": ["get-starknet-core@4.0.0", "", { "dependencies": { "@starknet-io/types-js": "^0.7.7" } }, "sha512-6pLmidQZkC3wZsrHY99grQHoGpuuXqkbSP65F8ov1/JsEI8DDLkhsAuLCKFzNOK56cJp+f1bWWfTJ57e9r5eqQ=="], - "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], @@ -1007,8 +997,6 @@ "immer": ["immer@10.1.1", "", {}, "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw=="], - "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], @@ -1051,8 +1039,6 @@ "js-cookie": ["js-cookie@3.0.5", "", {}, "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="], - "js-sha3": ["js-sha3@0.8.0", "", {}, "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], @@ -1107,8 +1093,6 @@ "lucide-react": ["lucide-react@0.487.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-aKqhOQ+YmFnwq8dWgGjOuLc8V1R9/c/yOd+zDY4+ohsR2Jo05lSGc3WsstYPIzcTpeosN7LoCkLReUUITvaIvw=="], - "match-sorter": ["match-sorter@6.4.0", "", { "dependencies": { "@babel/runtime": "^7.23.8", "remove-accents": "0.5.0" } }, "sha512-d4664ahzdL1QTTvmK1iI0JsrxWeJ6gn33qkYtnPg3mcn+naBLtXSgSPOe+X2vUgtgGwaAk3eiaj7gwKjjMAq+Q=="], - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "mathjs": ["mathjs@12.4.3", "", { "dependencies": { "@babel/runtime": "^7.24.4", "complex.js": "^2.1.1", "decimal.js": "^10.4.3", "escape-latex": "^1.2.0", "fraction.js": "4.3.4", "javascript-natural-sort": "^0.7.1", "seedrandom": "^3.0.5", "tiny-emitter": "^2.1.0", "typed-function": "^4.1.1" }, "bin": { "mathjs": "bin/cli.js" } }, "sha512-oHdGPDbp7gO873xxG90RLq36IuicuKvbpr/bBG5g9c8Obm/VsKVrK9uoRZZHUodohzlnmCEqfDzbR3LH6m+aAQ=="], @@ -1117,14 +1101,10 @@ "micro-starknet": ["micro-starknet@0.2.3", "", { "dependencies": { "@noble/curves": "~1.0.0", "@noble/hashes": "~1.3.0" } }, "sha512-6XBcC+GerlwJSR4iA0VaeXtS2wrayWFcA4PEzrJPMuFmWCaUtuGIq5K/DB5F/XgnL54/zl2Bxo690Lj7mYVA8A=="], - "microseconds": ["microseconds@0.2.0", "", {}, "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA=="], - "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "mipd": ["mipd@0.0.7", "", { "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg=="], "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], @@ -1139,8 +1119,6 @@ "multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], - "nano-time": ["nano-time@1.0.0", "", { "dependencies": { "big-integer": "^1.6.16" } }, "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA=="], - "nanoid": ["nanoid@3.3.9", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg=="], "neverthrow": ["neverthrow@8.2.0", "", { "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.24.0" } }, "sha512-kOCT/1MCPAxY5iUV3wytNFUMUolzuwd/VF/1KCx7kf6CutrOsTie+84zTGTpgQycjvfLdBBdvBvFLqFD2c0wkQ=="], @@ -1161,8 +1139,6 @@ "obj-multiplex": ["obj-multiplex@1.0.0", "", { "dependencies": { "end-of-stream": "^1.4.0", "once": "^1.4.0", "readable-stream": "^2.3.3" } }, "sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA=="], - "oblivious-set": ["oblivious-set@1.0.0", "", {}, "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw=="], - "ofetch": ["ofetch@1.4.1", "", { "dependencies": { "destr": "^2.0.3", "node-fetch-native": "^1.6.4", "ufo": "^1.5.4" } }, "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw=="], "on-exit-leak-free": ["on-exit-leak-free@0.2.0", "", {}, "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg=="], @@ -1187,8 +1163,6 @@ "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -1207,8 +1181,6 @@ "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], - "posthog-js-lite": ["posthog-js-lite@3.2.1", "", {}, "sha512-F+S+T9XiQr2rOkD0UFTr8yu8qScm4aM2lR5PmyknxxqlkC5gpRKS+Hi8cq5CYda7Q3BODh0h7pdW1P2XfZjEag=="], - "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], "process-warning": ["process-warning@1.0.0", "", {}, "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q=="], @@ -1241,8 +1213,6 @@ "react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="], - "react-query": ["react-query@3.39.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "broadcast-channel": "^3.4.1", "match-sorter": "^6.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g=="], - "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="], "react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="], @@ -1263,16 +1233,12 @@ "regexparam": ["regexparam@3.0.0", "", {}, "sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q=="], - "remove-accents": ["remove-accents@0.5.0", "", {}, "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="], - "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], "require-main-filename": ["require-main-filename@2.0.0", "", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="], "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], - "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], - "rollup": ["rollup@4.35.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.35.0", "@rollup/rollup-android-arm64": "4.35.0", "@rollup/rollup-darwin-arm64": "4.35.0", "@rollup/rollup-darwin-x64": "4.35.0", "@rollup/rollup-freebsd-arm64": "4.35.0", "@rollup/rollup-freebsd-x64": "4.35.0", "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", "@rollup/rollup-linux-arm-musleabihf": "4.35.0", "@rollup/rollup-linux-arm64-gnu": "4.35.0", "@rollup/rollup-linux-arm64-musl": "4.35.0", "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", "@rollup/rollup-linux-riscv64-gnu": "4.35.0", "@rollup/rollup-linux-s390x-gnu": "4.35.0", "@rollup/rollup-linux-x64-gnu": "4.35.0", "@rollup/rollup-linux-x64-musl": "4.35.0", "@rollup/rollup-win32-arm64-msvc": "4.35.0", "@rollup/rollup-win32-ia32-msvc": "4.35.0", "@rollup/rollup-win32-x64-msvc": "4.35.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg=="], "rpc-websockets": ["rpc-websockets@9.1.1", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA=="], @@ -1321,7 +1287,7 @@ "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], - "starknet": ["starknet@6.23.1", "", { "dependencies": { "@noble/curves": "1.7.0", "@noble/hashes": "1.6.0", "@scure/base": "1.2.1", "@scure/starknet": "1.1.0", "abi-wan-kanabi": "^2.2.3", "fetch-cookie": "~3.0.0", "isomorphic-fetch": "~3.0.0", "lossless-json": "^4.0.1", "pako": "^2.0.4", "starknet-types-07": "npm:@starknet-io/types-js@^0.7.10", "ts-mixer": "^6.0.3" } }, "sha512-vQV9luXpmwZZs9RVZaRwm2iD8T0PYx1AzgZeQsCvD89tR0HwUF0paty27ZzuJrdPe0CmAs/ipAYFCE55jbj0RQ=="], + "starknet": ["starknet@7.6.4", "", { "dependencies": { "@noble/curves": "1.7.0", "@noble/hashes": "1.6.0", "@scure/base": "1.2.1", "@scure/starknet": "1.1.0", "@starknet-io/starknet-types-07": "npm:@starknet-io/types-js@~0.7.10", "@starknet-io/starknet-types-08": "npm:@starknet-io/types-js@~0.8.4", "abi-wan-kanabi": "2.2.4", "lossless-json": "^4.0.1", "pako": "^2.0.4", "ts-mixer": "^6.0.3" } }, "sha512-FB20IaLCDbh/XomkB+19f5jmNxG+RzNdRO7QUhm7nfH81UPIt2C/MyWAlHCYkbv2wznSEb73wpxbp9tytokTgQ=="], "starknet-types-07": ["@starknet-io/types-js@0.7.10", "", {}, "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w=="], @@ -1347,8 +1313,6 @@ "svelte-forms": ["svelte-forms@2.3.1", "", { "dependencies": { "is-promise": "^4.0.0" } }, "sha512-ExX9PM0JgvdOWlHl2ztD7XzLNPOPt9U5hBKV8sUAisMfcYWpPRnyz+6EFmh35BOBGJJmuhTDBGm5/7seLjOTIA=="], - "swr": ["swr@2.3.3", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A=="], - "tailwind-merge": ["tailwind-merge@3.0.2", "", {}, "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw=="], "tailwindcss": ["tailwindcss@4.1.3", "", {}, "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g=="], @@ -1389,8 +1353,6 @@ "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], - "unload": ["unload@2.2.0", "", { "dependencies": { "@babel/runtime": "^7.6.2", "detect-node": "^2.0.4" } }, "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA=="], - "unstorage": ["unstorage@1.16.0", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", "h3": "^1.15.2", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.6", "ofetch": "^1.4.1", "ufo": "^1.6.1" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3 || ^7.0.0", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-WQ37/H5A7LcRPWfYOrDa1Ys02xAbpPJq6q5GkO88FBXVSQzHd7+BjEwfRqyaSWCv9MbsJy058GWjjPjcJ16GGA=="], "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], @@ -1479,8 +1441,6 @@ "@hpke/dhkem-x448/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], - "@latticexyz/schema-type/abitype": ["abitype@1.0.0", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ=="], - "@latticexyz/schema-type/viem": ["viem@2.9.20", "", { "dependencies": { "@adraffy/ens-normalize": "1.10.0", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.2", "@scure/bip32": "1.3.2", "@scure/bip39": "1.2.1", "abitype": "1.0.0", "isows": "1.0.3", "ws": "8.13.0" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-PHb1MrBHMrSZ8Ayuk3Y/6wUTcMbzlACQaM6AJBSv9kRKX3xYSZ/kehi+gvS0swQJeAlTQ4eZM7jsHQJNAOarmg=="], "@metamask/rpc-errors/@metamask/utils": ["@metamask/utils@9.3.0", "", { "dependencies": { "@ethereumjs/tx": "^4.2.0", "@metamask/superstruct": "^3.1.0", "@noble/hashes": "^1.3.1", "@scure/base": "^1.1.3", "@types/debug": "^4.1.7", "debug": "^4.3.4", "pony-cause": "^2.1.10", "semver": "^7.5.4", "uuid": "^9.0.1" } }, "sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g=="], @@ -1529,7 +1489,9 @@ "@solana/web3.js/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], - "@starknet-react/core/viem": ["viem@2.23.9", "", { "dependencies": { "@noble/curves": "1.8.1", "@noble/hashes": "1.7.1", "@scure/bip32": "1.6.2", "@scure/bip39": "1.5.4", "abitype": "1.0.8", "isows": "1.0.6", "ox": "0.6.9", "ws": "8.18.1" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-y8VLPfKukrstZKTerS9bm45ajZ22wUyStF+VquK3I2OovWLOyXSbQmJWei8syMFhp1uwhxh1tb0fAdx0WSRZWg=="], + "@starknet-io/get-starknet-core/@starknet-io/types-js": ["@starknet-io/types-js@0.7.10", "", {}, "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w=="], + + "@starknet-react/core/@starknet-io/types-js": ["@starknet-io/types-js@0.7.10", "", {}, "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w=="], "@swc/helpers/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -1609,6 +1571,10 @@ "ethers/tslib": ["tslib@2.7.0", "", {}, "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="], + "fast-check/pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], + + "get-starknet-core/@starknet-io/types-js": ["@starknet-io/types-js@0.7.10", "", {}, "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w=="], + "hpke-js/@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="], "jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], @@ -1631,6 +1597,8 @@ "ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "ox/abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], + "qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="], "rpc-websockets/@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], @@ -1643,6 +1611,10 @@ "socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + "starknetkit/@starknet-io/types-js": ["@starknet-io/types-js@0.7.10", "", {}, "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w=="], + + "starknetkit/starknet": ["starknet@6.23.1", "", { "dependencies": { "@noble/curves": "1.7.0", "@noble/hashes": "1.6.0", "@scure/base": "1.2.1", "@scure/starknet": "1.1.0", "abi-wan-kanabi": "^2.2.3", "fetch-cookie": "~3.0.0", "isomorphic-fetch": "~3.0.0", "lossless-json": "^4.0.1", "pako": "^2.0.4", "starknet-types-07": "npm:@starknet-io/types-js@^0.7.10", "ts-mixer": "^6.0.3" } }, "sha512-vQV9luXpmwZZs9RVZaRwm2iD8T0PYx1AzgZeQsCvD89tR0HwUF0paty27ZzuJrdPe0CmAs/ipAYFCE55jbj0RQ=="], + "string_decoder/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], "tough-cookie/universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="], @@ -1655,6 +1627,8 @@ "viem/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "viem/abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], + "viem/ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], "@latticexyz/schema-type/viem/@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.10.0", "", {}, "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q=="], @@ -1697,20 +1671,6 @@ "@reown/appkit/bs58/base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="], - "@starknet-react/core/viem/@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="], - - "@starknet-react/core/viem/@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="], - - "@starknet-react/core/viem/@scure/bip32": ["@scure/bip32@1.6.2", "", { "dependencies": { "@noble/curves": "~1.8.1", "@noble/hashes": "~1.7.1", "@scure/base": "~1.2.2" } }, "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw=="], - - "@starknet-react/core/viem/@scure/bip39": ["@scure/bip39@1.5.4", "", { "dependencies": { "@noble/hashes": "~1.7.1", "@scure/base": "~1.2.4" } }, "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA=="], - - "@starknet-react/core/viem/isows": ["isows@1.0.6", "", { "peerDependencies": { "ws": "*" } }, "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw=="], - - "@starknet-react/core/viem/ox": ["ox@0.6.9", "", { "dependencies": { "@adraffy/ens-normalize": "^1.10.1", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@scure/bip32": "^1.5.0", "@scure/bip39": "^1.4.0", "abitype": "^1.0.6", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug=="], - - "@starknet-react/core/viem/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="], - "@turnkey/api-key-stamper/@noble/curves/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], "@turnkey/crypto/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], @@ -1721,6 +1681,8 @@ "@walletconnect/utils/viem/@scure/bip39": ["@scure/bip39@1.5.4", "", { "dependencies": { "@noble/hashes": "~1.7.1", "@scure/base": "~1.2.4" } }, "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA=="], + "@walletconnect/utils/viem/abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], + "@walletconnect/utils/viem/isows": ["isows@1.0.6", "", { "peerDependencies": { "ws": "*" } }, "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw=="], "@walletconnect/utils/viem/ox": ["ox@0.6.7", "", { "dependencies": { "@adraffy/ens-normalize": "^1.10.1", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@scure/bip32": "^1.5.0", "@scure/bip39": "^1.4.0", "abitype": "^1.0.6", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA=="], @@ -1783,12 +1745,6 @@ "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem": ["viem@2.23.2", "", { "dependencies": { "@noble/curves": "1.8.1", "@noble/hashes": "1.7.1", "@scure/bip32": "1.6.2", "@scure/bip39": "1.5.4", "abitype": "1.0.8", "isows": "1.0.6", "ox": "0.6.7", "ws": "8.18.0" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA=="], - "@starknet-react/core/viem/@scure/bip32/@scure/base": ["@scure/base@1.2.4", "", {}, "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ=="], - - "@starknet-react/core/viem/@scure/bip39/@scure/base": ["@scure/base@1.2.4", "", {}, "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ=="], - - "@starknet-react/core/viem/ox/@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.0", "", {}, "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg=="], - "@walletconnect/utils/viem/@scure/bip32/@scure/base": ["@scure/base@1.2.4", "", {}, "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ=="], "@walletconnect/utils/viem/@scure/bip39/@scure/base": ["@scure/base@1.2.4", "", {}, "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ=="], @@ -1811,6 +1767,8 @@ "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/viem/@scure/bip39": ["@scure/bip39@1.5.4", "", { "dependencies": { "@noble/hashes": "~1.7.1", "@scure/base": "~1.2.4" } }, "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA=="], + "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/viem/abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], + "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/viem/isows": ["isows@1.0.6", "", { "peerDependencies": { "ws": "*" } }, "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw=="], "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/viem/ox": ["ox@0.6.7", "", { "dependencies": { "@adraffy/ens-normalize": "^1.10.1", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@scure/bip32": "^1.5.0", "@scure/bip39": "^1.4.0", "abitype": "^1.0.6", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA=="], @@ -1823,6 +1781,8 @@ "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/viem/@scure/bip39": ["@scure/bip39@1.5.4", "", { "dependencies": { "@noble/hashes": "~1.7.1", "@scure/base": "~1.2.4" } }, "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA=="], + "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/viem/abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], + "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/viem/isows": ["isows@1.0.6", "", { "peerDependencies": { "ws": "*" } }, "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw=="], "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/viem/ox": ["ox@0.6.7", "", { "dependencies": { "@adraffy/ens-normalize": "^1.10.1", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@scure/bip32": "^1.5.0", "@scure/bip39": "^1.4.0", "abitype": "^1.0.6", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA=="], @@ -1833,6 +1793,8 @@ "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem/@scure/bip39": ["@scure/bip39@1.5.4", "", { "dependencies": { "@noble/hashes": "~1.7.1", "@scure/base": "~1.2.4" } }, "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA=="], + "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem/abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], + "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem/isows": ["isows@1.0.6", "", { "peerDependencies": { "ws": "*" } }, "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw=="], "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem/ox": ["ox@0.6.7", "", { "dependencies": { "@adraffy/ens-normalize": "^1.10.1", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@scure/bip32": "^1.5.0", "@scure/bip39": "^1.4.0", "abitype": "^1.0.6", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA=="], diff --git a/mprocs.local.yaml b/mprocs.local.yaml index 72632e95..af85e50f 100644 --- a/mprocs.local.yaml +++ b/mprocs.local.yaml @@ -7,7 +7,7 @@ procs: shell: bun run dev torii: cwd: packages/contracts - shell: wait-port 5050 && torii --world "0xfea62706381254891e4e1a685a1c5b7c91ce4dddcd1390fd229691654019c4" --http.cors_origins "*" + shell: wait-port 5050 && torii --world "0x545eeac1e37165b8aebc1af423f823b5dd941d82f1d16208eb4a26f562e3aa6" --http.cors_origins "*" katana: cwd: packages/contracts - shell: katana --dev --dev.no-fee --http.cors_origins "*" + shell: katana --dev --dev.no-fee --http.cors_origins "*" --cartridge.controllers --cartridge.paymaster diff --git a/packages/client/.env.development b/packages/client/.env.development index a1f6b974..d3691bfd 100644 --- a/packages/client/.env.development +++ b/packages/client/.env.development @@ -1,8 +1,9 @@ -VITE_CONTROLLER_CHAINID=0x57505f5448454f52554747494e545241494c -VITE_TOKEN_HTTP_RPC=https://api.cartridge.gg/x/lore-v2/katana +VITE_CONTROLLER_CHAINID=0x4b4154414e41 +VITE_TOKEN_HTTP_RPC=https://api.cartridge.gg/x/lore-v3/katana VITE_TOKEN_CONTRACT_ADDRESS=0x050ab7cbc80f8c7ee18f859dcc81e7ae4213e08da243851a8889d48c2ed7f765 VITE_KATANA_HTTP_RPC=http://localhost:5050 VITE_TORII_HTTP_RPC=http://localhost:8080 VITE_TORII_WS_RPC=ws://localhost:8080 VITE_BURNER_ADDRESS=0x6677fe62ee39c7b07401f754138502bab7fac99d2d3c5d37df7d1c6fab10819 -VITE_BURNER_PRIVATE_KEY=0x3e3979c1ed728490308054fe357a9f49cf67f80f9721f44cc57235129e090f4 \ No newline at end of file +VITE_BURNER_PRIVATE_KEY=0x3e3979c1ed728490308054fe357a9f49cf67f80f9721f44cc57235129e090f4 +VITE_SLOT=lore-v3 \ No newline at end of file diff --git a/packages/client/package.json b/packages/client/package.json index 41b71577..c4a7b2a7 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -18,14 +18,14 @@ "start": "bunx serve -s /dist --port 8080" }, "dependencies": { - "@cartridge/controller": "^0.7.13", + "@cartridge/controller": "^0.9.2", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@dojoengine/core": "^1.5.10", - "@dojoengine/create-burner": "^1.5.10", - "@dojoengine/sdk": "^1.5.10", - "@dojoengine/utils": "^1.5.10", + "@dojoengine/core": "^1.6.0", + "@dojoengine/create-burner": "^1.6.0", + "@dojoengine/sdk": "^1.6.0", + "@dojoengine/utils": "^1.6.0", "@lore/contracts": "workspace:*", "@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-context-menu": "^2.2.6", @@ -44,7 +44,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "sonner": "^2.0.3", - "starknet": "^6.23.1", + "starknet": "^7.6.4", "tailwind-merge": "^3.0.1", "tailwindcss": "^4.0.3", "tw-animate-css": "^1.2.5", diff --git a/packages/client/src/lib/config.ts b/packages/client/src/lib/config.ts index 3e89fa6c..b7c1cef2 100644 --- a/packages/client/src/lib/config.ts +++ b/packages/client/src/lib/config.ts @@ -2,7 +2,7 @@ import { schema } from "@lib/dojo_bindings/typescript/models.gen"; import manifestJson from "@lore/contracts/manifest"; import type manifestJsonType from "@lore/contracts/manifest_dev.json"; import { cleanEnv, str, url } from "envalid"; -import { Account, Contract, RpcProvider } from "starknet"; +import { Account, Contract, RpcProvider, provider } from "starknet"; const getOrFail = (value: T | undefined, name?: string): T => { if (value === undefined || value === null) { @@ -11,7 +11,7 @@ const getOrFail = (value: T | undefined, name?: string): T => { return value; }; -const slotEnv = import.meta.env.MODE === "slot" ? { VITE_SLOT: str() } : {}; +// const slotEnv = import.meta.env.MODE === "slot" ? { VITE_SLOT: str() } : {}; const isLocalhost = window.location.hostname === "localhost"; const isEditor = window.location.pathname.startsWith("/editor"); @@ -24,7 +24,8 @@ const env = cleanEnv(import.meta.env, { VITE_TORII_WS_RPC: str(), VITE_BURNER_ADDRESS: str(), VITE_BURNER_PRIVATE_KEY: str(), - ...slotEnv, + VITE_SLOT: str(), + //...slotEnv, }); const endpoints = { @@ -104,7 +105,8 @@ export const LORE_CONFIG = { contract_address: env.VITE_TOKEN_CONTRACT_ADDRESS, erc20: ["0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"], }, - useController: import.meta.env.MODE === "slot", + useController: true, + // import.meta.env.MODE === "slot", env: env, LOCALHOST: isLocalhost, EDITOR_MODE: isEditor, diff --git a/packages/client/src/lib/dojo_bindings/typescript/contracts.gen.ts b/packages/client/src/lib/dojo_bindings/typescript/contracts.gen.ts index 218a47e7..3c940354 100644 --- a/packages/client/src/lib/dojo_bindings/typescript/contracts.gen.ts +++ b/packages/client/src/lib/dojo_bindings/typescript/contracts.gen.ts @@ -1,5 +1,5 @@ import { DojoProvider, DojoCall } from "@dojoengine/core"; -import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet"; +import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum } from "starknet"; import * as models from "./models.gen"; export function setupWorld(provider: DojoProvider) { @@ -571,7 +571,7 @@ export function setupWorld(provider: DojoProvider) { } }; - const build_prompt_prompt_calldata = (cmd: ByteArray): DojoCall => { + const build_prompt_prompt_calldata = (cmd: string): DojoCall => { return { contractName: "prompt", entrypoint: "prompt", @@ -579,7 +579,7 @@ export function setupWorld(provider: DojoProvider) { }; }; - const prompt_prompt = async (snAccount: Account | AccountInterface, cmd: ByteArray) => { + const prompt_prompt = async (snAccount: Account | AccountInterface, cmd: string) => { try { return await provider.execute( snAccount, diff --git a/packages/client/src/lib/dojo_bindings/typescript/models.gen.ts b/packages/client/src/lib/dojo_bindings/typescript/models.gen.ts index 6d750701..eff1b37d 100644 --- a/packages/client/src/lib/dojo_bindings/typescript/models.gen.ts +++ b/packages/client/src/lib/dojo_bindings/typescript/models.gen.ts @@ -9,12 +9,6 @@ export interface Area { is_spawn_point: boolean; } -// Type definition for `lore::components::area::AreaValue` struct -export interface AreaValue { - is_area: boolean; - is_spawn_point: boolean; -} - // Type definition for `lore::components::container::ActionMapContainer` struct export interface ActionMapContainer { action: string; @@ -33,16 +27,6 @@ export interface Container { action_map: Array; } -// Type definition for `lore::components::container::ContainerValue` struct -export interface ContainerValue { - is_container: boolean; - can_be_opened: boolean; - can_receive_items: boolean; - is_open: boolean; - num_slots: BigNumberish; - action_map: Array; -} - // Type definition for `lore::components::exit::ActionMapExit` struct export interface ActionMapExit { action: string; @@ -60,15 +44,6 @@ export interface Exit { action_map: Array; } -// Type definition for `lore::components::exit::ExitValue` struct -export interface ExitValue { - is_exit: boolean; - is_enterable: boolean; - leads_to: BigNumberish; - direction_type: DirectionEnum; - action_map: Array; -} - // Type definition for `lore::components::inspectable::ActionMapInspectable` struct export interface ActionMapInspectable { action: string; @@ -88,16 +63,6 @@ export interface Inspectable { new_entry: string; } -// Type definition for `lore::components::inspectable::InspectableValue` struct -export interface InspectableValue { - is_inspectable: boolean; - is_visible: boolean; - description: Array; - action_map: Array; - already_shown: boolean; - new_entry: string; -} - // Type definition for `lore::components::inventoryItem::ActionMapInventoryItem` struct export interface ActionMapInventoryItem { action: string; @@ -117,17 +82,6 @@ export interface InventoryItem { multiple_use: boolean; } -// Type definition for `lore::components::inventoryItem::InventoryItemValue` struct -export interface InventoryItemValue { - is_inventory_item: boolean; - owner_id: BigNumberish; - can_be_picked_up: boolean; - can_go_in_container: boolean; - action_map: Array; - already_used: boolean; - multiple_use: boolean; -} - // Type definition for `lore::components::player::Player` struct export interface Player { inst: BigNumberish; @@ -143,19 +97,6 @@ export interface PlayerStory { story: Array; } -// Type definition for `lore::components::player::PlayerStoryValue` struct -export interface PlayerStoryValue { - story: Array; -} - -// Type definition for `lore::components::player::PlayerValue` struct -export interface PlayerValue { - is_player: boolean; - address: string; - location: BigNumberish; - use_debug: boolean; -} - // Type definition for `lore::lib::actions::Action` struct export interface Action { inst: BigNumberish; @@ -172,20 +113,6 @@ export interface Action { success_response: Array; } -// Type definition for `lore::lib::actions::ActionValue` struct -export interface ActionValue { - name: string; - description: string; - is_enabled: boolean; - trigger: Array<[BigNumberish, BigNumberish]>; - conditions: Array<[BigNumberish, BigNumberish]>; - effects: Array<[BigNumberish, BigNumberish]>; - tags: Array; - executed: boolean; - failing_response: Array; - success_response: Array; -} - // Type definition for `lore::lib::condition::Condition` struct export interface Condition { inst: BigNumberish; @@ -197,15 +124,6 @@ export interface Condition { value: Array; } -// Type definition for `lore::lib::condition::ConditionValue` struct -export interface ConditionValue { - target: BigNumberish; - component: ComponentsEnum; - property: string; - operator: OperatorEnum; - value: Array; -} - // Type definition for `lore::lib::dictionary::Dict` struct export interface Dict { dict_key: BigNumberish; @@ -214,13 +132,6 @@ export interface Dict { n_value: BigNumberish; } -// Type definition for `lore::lib::dictionary::DictValue` struct -export interface DictValue { - word: string; - tokenType: TokenTypeEnum; - n_value: BigNumberish; -} - // Type definition for `lore::lib::effect::Effect` struct export interface Effect { inst: BigNumberish; @@ -241,29 +152,12 @@ export interface EffectExecution { error_message: string; } -// Type definition for `lore::lib::effect::EffectExecutionValue` struct -export interface EffectExecutionValue { - effect_key: BigNumberish; - timestamp: BigNumberish; - parameters: Array; - status: ExecutionStatusEnum; - error_message: string; -} - // Type definition for `lore::lib::effect::EffectParameter` struct export interface EffectParameter { name: string; value: BigNumberish; } -// Type definition for `lore::lib::effect::EffectValue` struct -export interface EffectValue { - target: BigNumberish; - component: ComponentsEnum; - property: string; - value: Array; -} - // Type definition for `lore::lib::entity::Entity` struct export interface Entity { inst: BigNumberish; @@ -273,14 +167,6 @@ export interface Entity { actions_keys: Array; } -// Type definition for `lore::lib::entity::EntityValue` struct -export interface EntityValue { - is_entity: boolean; - name: string; - alt_names: Array; - actions_keys: Array; -} - // Type definition for `lore::lib::relations::ChildToParent` struct export interface ChildToParent { inst: BigNumberish; @@ -288,12 +174,6 @@ export interface ChildToParent { parent: BigNumberish; } -// Type definition for `lore::lib::relations::ChildToParentValue` struct -export interface ChildToParentValue { - is_child: boolean; - parent: BigNumberish; -} - // Type definition for `lore::lib::relations::ParentToChildren` struct export interface ParentToChildren { inst: BigNumberish; @@ -301,12 +181,6 @@ export interface ParentToChildren { children: Array; } -// Type definition for `lore::lib::relations::ParentToChildrenValue` struct -export interface ParentToChildrenValue { - is_parent: boolean; - children: Array; -} - // Type definition for `lore::lib::trigger::Trigger` struct export interface Trigger { inst: BigNumberish; @@ -325,27 +199,12 @@ export interface TriggerIndex { trigger_id: Array<[BigNumberish, BigNumberish]>; } -// Type definition for `lore::lib::trigger::TriggerIndexValue` struct -export interface TriggerIndexValue { - trigger_id: Array<[BigNumberish, BigNumberish]>; -} - // Type definition for `lore::lib::trigger::TriggerParameter` struct export interface TriggerParameter { name: string; value: BigNumberish; } -// Type definition for `lore::lib::trigger::TriggerValue` struct -export interface TriggerValue { - name: string; - trigger_type: TriggerTypeEnum; - parameters: Array; - is_enabled: boolean; - is_once: boolean; - was_triggered: boolean; -} - // Type definition for `lore::lib::variable_property::ComponentProperty` struct export interface ComponentProperty { name: string; @@ -363,26 +222,12 @@ export interface ComponentVariable { last_updated: BigNumberish; } -// Type definition for `lore::lib::variable_property::ComponentVariableValue` struct -export interface ComponentVariableValue { - component_type: ComponentsEnum; - entity_id: BigNumberish; - property_name: string; - value: string; - last_updated: BigNumberish; -} - // Type definition for `lore::lib::variable_property::PropertyRegistry` struct export interface PropertyRegistry { component_type: ComponentsEnum; properties: Array; } -// Type definition for `lore::lib::variable_property::PropertyRegistryValue` struct -export interface PropertyRegistryValue { - properties: Array; -} - // Type definition for `lore::components::Components` enum export const components = [ 'Area', @@ -515,50 +360,31 @@ export type PropertyTypeEnum = CairoCustomEnum; export interface SchemaType extends ISchemaType { lore: { Area: Area, - AreaValue: AreaValue, ActionMapContainer: ActionMapContainer, Container: Container, - ContainerValue: ContainerValue, ActionMapExit: ActionMapExit, Exit: Exit, - ExitValue: ExitValue, ActionMapInspectable: ActionMapInspectable, Inspectable: Inspectable, - InspectableValue: InspectableValue, ActionMapInventoryItem: ActionMapInventoryItem, InventoryItem: InventoryItem, - InventoryItemValue: InventoryItemValue, Player: Player, PlayerStory: PlayerStory, - PlayerStoryValue: PlayerStoryValue, - PlayerValue: PlayerValue, Action: Action, - ActionValue: ActionValue, Condition: Condition, - ConditionValue: ConditionValue, Dict: Dict, - DictValue: DictValue, Effect: Effect, EffectExecution: EffectExecution, - EffectExecutionValue: EffectExecutionValue, EffectParameter: EffectParameter, - EffectValue: EffectValue, Entity: Entity, - EntityValue: EntityValue, ChildToParent: ChildToParent, - ChildToParentValue: ChildToParentValue, ParentToChildren: ParentToChildren, - ParentToChildrenValue: ParentToChildrenValue, Trigger: Trigger, TriggerIndex: TriggerIndex, - TriggerIndexValue: TriggerIndexValue, TriggerParameter: TriggerParameter, - TriggerValue: TriggerValue, ComponentProperty: ComponentProperty, ComponentVariable: ComponentVariable, - ComponentVariableValue: ComponentVariableValue, PropertyRegistry: PropertyRegistry, - PropertyRegistryValue: PropertyRegistryValue, }, } export const schema: SchemaType = { @@ -568,10 +394,6 @@ export const schema: SchemaType = { is_area: false, is_spawn_point: false, }, - AreaValue: { - is_area: false, - is_spawn_point: false, - }, ActionMapContainer: { action: "", inst: 0, @@ -592,17 +414,6 @@ export const schema: SchemaType = { Close: undefined, Check: undefined, }), }], }, - ContainerValue: { - is_container: false, - can_be_opened: false, - can_receive_items: false, - is_open: false, - num_slots: 0, - action_map: [{ action: "", inst: 0, action_fn: new CairoCustomEnum({ - Open: "", - Close: undefined, - Check: undefined, }), }], - }, ActionMapExit: { action: "", inst: 0, @@ -625,21 +436,6 @@ export const schema: SchemaType = { action_map: [{ action: "", inst: 0, action_fn: new CairoCustomEnum({ UseExit: "", }), }], }, - ExitValue: { - is_exit: false, - is_enterable: false, - leads_to: 0, - direction_type: new CairoCustomEnum({ - None: "", - North: undefined, - South: undefined, - East: undefined, - West: undefined, - Up: undefined, - Down: undefined, }), - action_map: [{ action: "", inst: 0, action_fn: new CairoCustomEnum({ - UseExit: "", }), }], - }, ActionMapInspectable: { action: "", inst: 0, @@ -663,18 +459,6 @@ export const schema: SchemaType = { already_shown: false, new_entry: "", }, - InspectableValue: { - is_inspectable: false, - is_visible: false, - description: [""], - action_map: [{ action: "", inst: 0, action_fn: new CairoCustomEnum({ - SetVisible: "", - ReadRandomDescription: undefined, - ReadFirstDescription: undefined, - ReadSpecificDescription: undefined, }), entrypoint: 0, }], - already_shown: false, - new_entry: "", - }, ActionMapInventoryItem: { action: "", inst: 0, @@ -700,20 +484,6 @@ export const schema: SchemaType = { already_used: false, multiple_use: false, }, - InventoryItemValue: { - is_inventory_item: false, - owner_id: 0, - can_be_picked_up: false, - can_go_in_container: false, - action_map: [{ action: "", inst: 0, action_fn: new CairoCustomEnum({ - UseItem: "", - PickupItem: undefined, - DropItem: undefined, - PutItem: undefined, - TakeOutItem: undefined, }), }], - already_used: false, - multiple_use: false, - }, Player: { inst: 0, is_player: false, @@ -725,15 +495,6 @@ export const schema: SchemaType = { inst: 0, story: [""], }, - PlayerStoryValue: { - story: [""], - }, - PlayerValue: { - is_player: false, - address: "", - location: 0, - use_debug: false, - }, Action: { inst: 0, key: 0, @@ -748,18 +509,6 @@ export const schema: SchemaType = { failing_response: [""], success_response: [""], }, - ActionValue: { - name: "", - description: "", - is_enabled: false, - trigger: [[0, 0]], - conditions: [[0, 0]], - effects: [[0, 0]], - tags: [""], - executed: false, - failing_response: [""], - success_response: [""], - }, Condition: { inst: 0, key: 0, @@ -778,22 +527,6 @@ export const schema: SchemaType = { NotEquals: undefined, }), value: [0], }, - ConditionValue: { - target: 0, - component: new CairoCustomEnum({ - Area: "", - Container: undefined, - Entity: undefined, - Exit: undefined, - Inspectable: undefined, - InventoryItem: undefined, - Player: undefined, }), - property: "", - operator: new CairoCustomEnum({ - Equals: "", - NotEquals: undefined, }), - value: [0], - }, Dict: { dict_key: 0, word: "", @@ -811,22 +544,6 @@ export const schema: SchemaType = { System: undefined, }), n_value: 0, }, - DictValue: { - word: "", - tokenType: new CairoCustomEnum({ - Unknown: "", - Verb: undefined, - Direction: undefined, - Article: undefined, - Preposition: undefined, - Pronoun: undefined, - Adjective: undefined, - Noun: undefined, - Quantifier: undefined, - Interrogative: undefined, - System: undefined, }), - n_value: 0, - }, Effect: { inst: 0, key: 0, @@ -852,32 +569,10 @@ export const schema: SchemaType = { Failure: undefined, }), error_message: "", }, - EffectExecutionValue: { - effect_key: 0, - timestamp: 0, - parameters: [{ name: "", value: 0, }], - status: new CairoCustomEnum({ - Success: "", - Failure: undefined, }), - error_message: "", - }, EffectParameter: { name: "", value: 0, }, - EffectValue: { - target: 0, - component: new CairoCustomEnum({ - Area: "", - Container: undefined, - Entity: undefined, - Exit: undefined, - Inspectable: undefined, - InventoryItem: undefined, - Player: undefined, }), - property: "", - value: [""], - }, Entity: { inst: 0, is_entity: false, @@ -885,30 +580,16 @@ export const schema: SchemaType = { alt_names: [""], actions_keys: [0], }, - EntityValue: { - is_entity: false, - name: "", - alt_names: [""], - actions_keys: [0], - }, ChildToParent: { inst: 0, is_child: false, parent: 0, }, - ChildToParentValue: { - is_child: false, - parent: 0, - }, ParentToChildren: { inst: 0, is_parent: false, children: [0], }, - ParentToChildrenValue: { - is_parent: false, - children: [0], - }, Trigger: { inst: 0, key: 0, @@ -931,25 +612,10 @@ export const schema: SchemaType = { UseItem: undefined, }), trigger_id: [[0, 0]], }, - TriggerIndexValue: { - trigger_id: [[0, 0]], - }, TriggerParameter: { name: "", value: 0, }, - TriggerValue: { - name: "", - trigger_type: new CairoCustomEnum({ - None: "", - PlayerEntersArea: undefined, - PlayerLeavesArea: undefined, - UseItem: undefined, }), - parameters: [{ name: "", value: 0, }], - is_enabled: false, - is_once: false, - was_triggered: false, - }, ComponentProperty: { name: "", property_type: new CairoCustomEnum({ @@ -981,20 +647,6 @@ export const schema: SchemaType = { value: "", last_updated: 0, }, - ComponentVariableValue: { - component_type: new CairoCustomEnum({ - Area: "", - Container: undefined, - Entity: undefined, - Exit: undefined, - Inspectable: undefined, - InventoryItem: undefined, - Player: undefined, }), - entity_id: 0, - property_name: "", - value: "", - last_updated: 0, - }, PropertyRegistry: { component_type: new CairoCustomEnum({ Area: "", @@ -1017,78 +669,45 @@ export const schema: SchemaType = { WriteOnly: undefined, ReadWrite: undefined, }), }], }, - PropertyRegistryValue: { - properties: [{ name: "", property_type: new CairoCustomEnum({ - Boolean: "", - Integer: undefined, - Felt252: undefined, - Direction: undefined, - ContractAddress: undefined, - String: undefined, - ByteArray: undefined, - Enum: undefined, }), access_flags: new CairoCustomEnum({ - ReadOnly: "", - WriteOnly: undefined, - ReadWrite: undefined, }), }], - }, }, }; export enum ModelsMapping { Components = 'lore-Components', Area = 'lore-Area', - AreaValue = 'lore-AreaValue', ActionMapContainer = 'lore-ActionMapContainer', Container = 'lore-Container', ContainerActions = 'lore-ContainerActions', - ContainerValue = 'lore-ContainerValue', ActionMapExit = 'lore-ActionMapExit', Exit = 'lore-Exit', ExitActions = 'lore-ExitActions', - ExitValue = 'lore-ExitValue', ActionMapInspectable = 'lore-ActionMapInspectable', Inspectable = 'lore-Inspectable', InspectableActions = 'lore-InspectableActions', - InspectableValue = 'lore-InspectableValue', ActionMapInventoryItem = 'lore-ActionMapInventoryItem', InventoryItem = 'lore-InventoryItem', InventoryItemActions = 'lore-InventoryItemActions', - InventoryItemValue = 'lore-InventoryItemValue', Player = 'lore-Player', PlayerStory = 'lore-PlayerStory', - PlayerStoryValue = 'lore-PlayerStoryValue', - PlayerValue = 'lore-PlayerValue', Direction = 'lore-Direction', TokenType = 'lore-TokenType', Action = 'lore-Action', - ActionValue = 'lore-ActionValue', Condition = 'lore-Condition', - ConditionValue = 'lore-ConditionValue', Operator = 'lore-Operator', Dict = 'lore-Dict', - DictValue = 'lore-DictValue', Effect = 'lore-Effect', EffectExecution = 'lore-EffectExecution', - EffectExecutionValue = 'lore-EffectExecutionValue', EffectParameter = 'lore-EffectParameter', - EffectValue = 'lore-EffectValue', ExecutionStatus = 'lore-ExecutionStatus', Entity = 'lore-Entity', - EntityValue = 'lore-EntityValue', ChildToParent = 'lore-ChildToParent', - ChildToParentValue = 'lore-ChildToParentValue', ParentToChildren = 'lore-ParentToChildren', - ParentToChildrenValue = 'lore-ParentToChildrenValue', Trigger = 'lore-Trigger', TriggerIndex = 'lore-TriggerIndex', - TriggerIndexValue = 'lore-TriggerIndexValue', TriggerParameter = 'lore-TriggerParameter', TriggerType = 'lore-TriggerType', - TriggerValue = 'lore-TriggerValue', ComponentProperty = 'lore-ComponentProperty', ComponentVariable = 'lore-ComponentVariable', - ComponentVariableValue = 'lore-ComponentVariableValue', PropertyAccess = 'lore-PropertyAccess', PropertyRegistry = 'lore-PropertyRegistry', - PropertyRegistryValue = 'lore-PropertyRegistryValue', PropertyType = 'lore-PropertyType', } \ No newline at end of file diff --git a/packages/client/src/lib/stores/wallet.store.ts b/packages/client/src/lib/stores/wallet.store.ts index 9c53434e..ef68282f 100644 --- a/packages/client/src/lib/stores/wallet.store.ts +++ b/packages/client/src/lib/stores/wallet.store.ts @@ -50,6 +50,10 @@ const setupController = async () => { name: APP_EDITOR_DATA.title, // Optional, can be added if you want a name description: `Aprove submitting transactions to ${APP_EDITOR_DATA.title}`, methods: [ + { + entrypoint: "register_property_registry", + description: `The terminal endpoint for ${APP_EDITOR_DATA.title}`, + }, { entrypoint: "create_player", description: `The terminal endpoint for ${APP_EDITOR_DATA.title}`, diff --git a/packages/client/vite.config.ts b/packages/client/vite.config.ts index d014836f..0d27e057 100644 --- a/packages/client/vite.config.ts +++ b/packages/client/vite.config.ts @@ -8,6 +8,7 @@ import oxlintPlugin from "vite-plugin-oxlint"; import topLevelAwait from "vite-plugin-top-level-await"; import wasm from "vite-plugin-wasm"; import { patchBindings } from "./scripts/vite-fix-bindings"; +import fs from "node:fs"; //TODO: https://github.com/nksaraf/vinxi // https://www.npmjs.com/package/wouter @@ -60,10 +61,15 @@ export default defineConfig(async ({ mode }) => { // }, }, server: { + https: { + key: fs.readFileSync(path.resolve(__dirname, "ssl/dev.pem")), + cert: fs.readFileSync(path.resolve(__dirname, "ssl/cert.pem")), + }, proxy: { "/katana": { target: process.env.VITE_KATANA_HTTP_RPC, changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/katana/, ""), }, }, cors: false, diff --git a/packages/contracts/Scarb.lock b/packages/contracts/Scarb.lock index 8011ae5d..5c24d2af 100644 --- a/packages/contracts/Scarb.lock +++ b/packages/contracts/Scarb.lock @@ -3,16 +3,16 @@ version = 1 [[package]] name = "dojo" -version = "1.5.0" -source = "git+https://github.com/dojoengine/dojo?tag=v1.5.0#812f17c9c57fd057d0bf1e648a591ea0ca9ea718" +version = "1.6.1" +source = "git+https://github.com/dojoengine/dojo?tag=v1.6.1#a4f9445cf925e999d97038f596806ec307981a9b" dependencies = [ "dojo_plugin", ] [[package]] name = "dojo_cairo_test" -version = "1.0.12" -source = "git+https://github.com/dojoengine/dojo?tag=v1.5.0#812f17c9c57fd057d0bf1e648a591ea0ca9ea718" +version = "1.6.1" +source = "git+https://github.com/dojoengine/dojo?tag=v1.6.1#a4f9445cf925e999d97038f596806ec307981a9b" dependencies = [ "dojo", ] @@ -20,7 +20,7 @@ dependencies = [ [[package]] name = "dojo_plugin" version = "2.10.1" -source = "git+https://github.com/dojoengine/dojo?tag=v1.5.0#812f17c9c57fd057d0bf1e648a591ea0ca9ea718" +source = "git+https://github.com/dojoengine/dojo?tag=v1.6.1#a4f9445cf925e999d97038f596806ec307981a9b" [[package]] name = "lore" @@ -34,4 +34,4 @@ dependencies = [ [[package]] name = "origami_random" version = "1.1.2" -source = "git+https://github.com/dojoengine/origami?tag=v1.5.0#1aba33f09e326d2f2645ff93fac1e9febc64ac1d" +source = "git+https://github.com/dojoengine/origami?tag=v1.6.1#81e49e9db43e8528cbb5d914e4da3479a9f02fb8" diff --git a/packages/contracts/Scarb.toml b/packages/contracts/Scarb.toml index e4e986d3..57496b80 100644 --- a/packages/contracts/Scarb.toml +++ b/packages/contracts/Scarb.toml @@ -12,15 +12,15 @@ sierra-replace-ids = true migrate = "sozo build && sozo migrate apply" [dependencies] -dojo = { git = "https://github.com/dojoengine/dojo", tag = "v1.5.0" } -origami_random = { git = "https://github.com/dojoengine/origami", tag = "v1.5.0" } +dojo = { git = "https://github.com/dojoengine/dojo", tag = "v1.6.1" } +origami_random = { git = "https://github.com/dojoengine/origami", tag = "v1.6.1" } # not used right now but initialised in dojo_init when used for deli interop # planetary_interface = { path = "/Users/tims/DATA/BB/dojo/planetary/dojo/planetary_interface" } # starknet = "2.8.4" [dev-dependencies] cairo_test = "=2.10.1" -dojo_cairo_test = { git = "https://github.com/dojoengine/dojo", tag = "v1.5.0" } +dojo_cairo_test = { git = "https://github.com/dojoengine/dojo", tag = "v1.6.1" } [[target.starknet-contract]] build-external-contracts = ["dojo::world::world_contract::world"] diff --git a/packages/contracts/dojo_dev.toml b/packages/contracts/dojo_dev.toml index 38196d0b..0bec98a9 100644 --- a/packages/contracts/dojo_dev.toml +++ b/packages/contracts/dojo_dev.toml @@ -8,10 +8,11 @@ default = "lore" [env] rpc_url = "http://localhost:5050/" +slot_name = "lore-v3" account_address = "0x6677fe62ee39c7b07401f754138502bab7fac99d2d3c5d37df7d1c6fab10819" private_key = "0x3e3979c1ed728490308054fe357a9f49cf67f80f9721f44cc57235129e090f4" -world_address = "0xfea62706381254891e4e1a685a1c5b7c91ce4dddcd1390fd229691654019c4" +world_address = "0x545eeac1e37165b8aebc1af423f823b5dd941d82f1d16208eb4a26f562e3aa6" [writers] "lore" = ["lore-prompt", "lore-designer"] diff --git a/packages/contracts/dojo_slot.toml b/packages/contracts/dojo_slot.toml index 98c3491d..408a2107 100644 --- a/packages/contracts/dojo_slot.toml +++ b/packages/contracts/dojo_slot.toml @@ -12,7 +12,7 @@ slot_name = "lore-v3" account_address = "0x6677fe62ee39c7b07401f754138502bab7fac99d2d3c5d37df7d1c6fab10819" private_key = "0x3e3979c1ed728490308054fe357a9f49cf67f80f9721f44cc57235129e090f4" -world_address = "0xfea62706381254891e4e1a685a1c5b7c91ce4dddcd1390fd229691654019c4" +world_address = "0x545eeac1e37165b8aebc1af423f823b5dd941d82f1d16208eb4a26f562e3aa6" [writers] "lore" = ["lore-prompt", "lore-designer"] diff --git a/packages/contracts/manifest_dev.json b/packages/contracts/manifest_dev.json index b5dd0e4a..4a9224aa 100644 --- a/packages/contracts/manifest_dev.json +++ b/packages/contracts/manifest_dev.json @@ -1,7 +1,7 @@ { "world": { - "class_hash": "0x4c60dc46a8ca8bb47675b7b914053cef769afbf0e340523187336b72bd71d1f", - "address": "0xfea62706381254891e4e1a685a1c5b7c91ce4dddcd1390fd229691654019c4", + "class_hash": "0x13d92333361bb7049c1232c0a5404e2c19082ededc3a73d75cb33fa952adec7", + "address": "0x545eeac1e37165b8aebc1af423f823b5dd941d82f1d16208eb4a26f562e3aa6", "seed": "lore", "name": ">LORE", "entrypoints": [ @@ -1328,7 +1328,7 @@ }, "contracts": [ { - "address": "0x1f7c2ac67b86c07d3c90d4175d32dbe022ac0ed9135a926e8f2912746ea24f6", + "address": "0x6279c732d93918001bfcce2d2cab5d10a9402a7def48c404cb97a7179ed2f95", "class_hash": "0x42469e17180b773f2e99d209f0ad120576dadd869a6803564281d8b31558447", "abi": [ { @@ -2548,7 +2548,7 @@ ] }, { - "address": "0x77ccba5aa13d6d3d14867784073498094766d4fc7956cf0c690960c2634c702", + "address": "0x521fd5bbd6abd0edd550accfd2464946f016e11ff4ac25e66c05f295b827f7d", "class_hash": "0x652ee26dd17ff1fbc4f193f411625d9de8dcfb83e1a3d1012a522ae425af380", "abi": [ { diff --git a/packages/contracts/src/lib.cairo b/packages/contracts/src/lib.cairo index 7c6f8e49..b491cfb7 100644 --- a/packages/contracts/src/lib.cairo +++ b/packages/contracts/src/lib.cairo @@ -1,9 +1,10 @@ pub mod systems { pub mod designer; pub mod prompt; + pub mod game_instance; } -pub mod components; +// pub mod components; pub mod constants; @@ -11,17 +12,50 @@ pub mod lib { pub mod a_lexer; pub mod dictionary; pub mod c_handler; - pub mod entity; + //pub mod entity; pub mod random; pub mod relations; pub mod utils; pub mod level_test; - pub mod trigger; - pub mod condition; + //pub mod trigger; + //pub mod condition; pub mod variable_property; pub mod variable_property_helper; - pub mod effect; - pub mod actions; + //pub mod effect; +//pub mod actions; +} + +pub mod models { + pub mod index; + pub mod components; + pub mod container; + pub mod area; + pub mod exit; + pub mod inspectable; + pub mod inventoryItem; + pub mod player; +} + +pub mod new_components { + pub mod action_trait; + pub mod condition_trait; + pub mod container_trait; + pub mod effect_trait; + pub mod entity_trait; + pub mod exit_trait; + pub mod inspectable_trait; + pub mod inventoryItem_trait; + pub mod player_trait; + pub mod trigger_trait; +} + + +pub mod types { + pub mod action_type; + pub mod command_type; + pub mod component_type; + pub mod direction_type; + pub mod property_type; } #[cfg(test)] diff --git a/packages/contracts/src/lib/a_lexer.cairo b/packages/contracts/src/lib/a_lexer.cairo index 768e91e8..e9be77ef 100644 --- a/packages/contracts/src/lib/a_lexer.cairo +++ b/packages/contracts/src/lib/a_lexer.cairo @@ -1,59 +1,9 @@ use core::array::{ArrayTrait, ArrayImpl, Array}; -use lore::{components::{player::{PlayerImpl}}}; +use lore::{ + new_components::player_trait::PlayerImpl, types::command_type::{Command, Token, TokenType}, +}; -#[derive(Serde, Copy, Drop, Debug, Introspect, PartialEq)] -pub enum TokenType { - Unknown, - Verb, // go, take, drop, look, inventory, spawn - Direction, // north, south, east, west - Article, // the, a - Preposition, // in, on, at, to - Pronoun, // they, it, me, you - Adjective, // good, bad, happy, sad - Noun, // noun, object - Quantifier, // number, quantity - Interrogative, // who, what, where, why, how - System // -} - -pub impl TokenTypeFelt252 of Into { - fn into(self: TokenType) -> felt252 { - match self { - TokenType::Unknown => 0, - TokenType::Verb => 1, - TokenType::Direction => 2, - TokenType::Article => 3, - TokenType::Preposition => 4, - TokenType::Pronoun => 5, - TokenType::Adjective => 6, - TokenType::Noun => 7, - TokenType::Quantifier => 8, - TokenType::Interrogative => 9, - TokenType::System => 252, - } - } -} - -#[derive(Clone, Drop, Serde, Debug)] -pub struct Token { - pub position: u32, // Token position in the command - pub text: ByteArray, // The token text as ByteArray - pub token_type: TokenType, // Type of token (using TokenType enum as u8) - pub token_value: felt252, // Value of token (ie directionId, obj inst) - pub target: felt252 // Target object INST for token -} - -#[derive(Clone, Drop, Serde, Debug)] -pub struct Command { - #[key] - pub command_id: felt252, // Unique ID of this command - pub text: ByteArray, // Full command text - pub words: Array, // Split words - pub token_count: u8, // Number of tokens in the command - pub action_type: u8, // Type of action (using ActionType enum as u8) - pub tokens: Array // Array of tokens in the command -} #[generate_trait] pub impl CommandImpl of CommandTrait { @@ -141,16 +91,16 @@ pub impl CommandImpl of CommandTrait { pub mod lexer { use super::CommandTrait; - use super::super::entity::EntityTrait; use dojo::world::IWorldDispatcherTrait; use core::array::{ArrayTrait, ArrayImpl, Array}; - use super::{TokenType, Command, Token, CommandImpl}; + use super::{CommandImpl}; use dojo::{world::WorldStorage}; use lore::{ - components::{player::{Player, PlayerImpl}}, // - constants::errors::Error, // + models::index::{Player}, + new_components::{entity_trait::EntityImpl, player_trait::PlayerImpl}, + types::command_type::{Command, Token, TokenType}, constants::errors::Error, lib::{ utils::{ByteArrayTraitExt, ClousureTraitImp}, dictionary::{get_dict_entry, initialize_dictionary}, @@ -159,7 +109,7 @@ pub mod lexer { pub fn parse( - message: ByteArray, world: WorldStorage, player: Player, + message: ByteArray, world: WorldStorage, player: Player, game_instance: felt252, ) -> Result { initialize_dictionary(world); let words = message.split_into_words(); @@ -173,7 +123,7 @@ pub mod lexer { action_type: 0, tokens, }; - command = match_player_context(world, player, command); + command = match_player_context(world, player, command, game_instance); command = post_process_command(world, player, command); command.pretty_print(); Result::Ok(command) @@ -207,9 +157,9 @@ pub mod lexer { tokens } - fn match_player_context(world: WorldStorage, player: Player, mut command: Command) -> Command { - // get player for their context (room + room objects + inventory) - let context = player.get_full_context(@world); + fn match_player_context(world: WorldStorage, player: Player, mut command: Command, game_instance: felt252) -> Command { + // get player for their context (room + room objects + inventory) - scoped to game instance + let context = player.get_full_context(@world, game_instance); let mut newTokens: Array = array![]; for i in 0..command.tokens.len() { let mut token = command.tokens.at(i).clone(); @@ -261,10 +211,10 @@ pub mod lexer { mod tests { use super::lexer; use super::CommandImpl; - use super::TokenType; use lore::{ - tests::helpers, lib::{level_test::create_test_level, a_lexer::{TokenTypeFelt252}}, - components::{player::{PlayerImpl, caller_as_player}}, lib::dictionary::{add_to_dictionary}, + models::player::caller_as_player, new_components::player_trait::PlayerImpl, + types::command_type::{TokenType, IntoTokenTypeFelt252}, tests::helpers, + lib::{level_test::create_test_level, dictionary::{add_to_dictionary}}, }; #[test] diff --git a/packages/contracts/src/lib/c_handler.cairo b/packages/contracts/src/lib/c_handler.cairo index 44885184..33990b65 100644 --- a/packages/contracts/src/lib/c_handler.cairo +++ b/packages/contracts/src/lib/c_handler.cairo @@ -1,30 +1,28 @@ -use super::a_lexer::CommandTrait; -use super::super::components::player::PlayerTrait; use dojo::{world::WorldStorage}; - -use lore::{ // - lib::{ // - entity::{EntityImpl}, // - a_lexer::{Command, CommandImpl, TokenType, Token}, - utils::ByteArrayTraitExt, dictionary::{init_dictionary, add_to_dictionary}, - level_test::{create_test_level}, // - actions::{ActionImpl} // - }, // - constants::errors::Error, // - components::{ - player::{Player, PlayerImpl}, area::{AreaComponent}, exit::{Exit, ExitComponent}, Component, - inspectable::{Inspectable, InspectableImpl, InspectableComponent}, - inventoryItem::{InventoryItemComponent}, - container::{Container, ContainerImpl, ContainerComponent}, - } // +use lore::{ + models::{ + index::{Inspectable, Exit, Container, Player}, area::AreaComponent, exit::ExitComponent, + inspectable::InspectableComponent, inventoryItem::InventoryItemComponent, + container::ContainerComponent, player::PlayerComponent, components::Component, + }, + new_components::{ + entity_trait::EntityImpl, player_trait::PlayerImpl, inspectable_trait::InspectableImpl, + container_trait::ContainerImpl, condition_trait::ConditionImpl, action_trait::ActionImpl, + }, + types::command_type::{Command, TokenType, Token}, + lib::{ + a_lexer::CommandImpl, utils::ByteArrayTraitExt, + dictionary::{init_dictionary, add_to_dictionary}, level_test::{create_test_level}, + }, + constants::errors::Error, }; pub fn handle_command( - mut command: Command, world: WorldStorage, player: Player, + mut command: Command, world: WorldStorage, player: Player, game_instance: felt252, ) -> Result { let sys_command = command.is_system_command(); if sys_command { - return system_command(command.clone(), world, player); + return system_command(command.clone(), world, player, game_instance); } let verbs = command.get_verbs(); if verbs.len() == 0 { @@ -97,7 +95,7 @@ pub fn handle_command( }; }; } else if directions.len() > 0 { - let context = player.get_context(@world); + let context = player.get_context(@world, game_instance); for item in context { let exit: Option = Component::get_component(world, item.inst); @@ -182,7 +180,7 @@ pub fn init_system_dictionary(world: WorldStorage) { } fn system_command( - mut command: Command, world: WorldStorage, player: Player, + mut command: Command, world: WorldStorage, player: Player, game_instance: felt252, ) -> Result { let mut system_command: ByteArray = ""; for token in command.clone().tokens { @@ -244,7 +242,7 @@ fn system_command( } if (system_command == "g_look") { player.say(world, "+sys+you see this:"); - let context = player.get_context(@world); + let context = player.get_context(@world, game_instance); let room = player.get_room(@world); if room.is_none() { return Result::Err(Error::ActionFailed); @@ -268,9 +266,10 @@ fn system_command( mod tests { use super::*; use lore::tests::helpers; - use lore::components::player::{caller_as_player}; - use lore::lib::a_lexer::{Token, TokenType, Command}; - use lore::lib::utils::ByteArrayTraitExt; + use lore::{ + models::player::caller_as_player, types::command_type::{Command, Token, TokenType}, + lib::utils::ByteArrayTraitExt, + }; #[test] fn CHandler_test_g_command_handling() { diff --git a/packages/contracts/src/lib/dictionary.cairo b/packages/contracts/src/lib/dictionary.cairo index e54039d4..c9768cdd 100644 --- a/packages/contracts/src/lib/dictionary.cairo +++ b/packages/contracts/src/lib/dictionary.cairo @@ -1,23 +1,10 @@ use dojo::{world::WorldStorage, model::ModelStorage}; use lore::{ - constants::errors::Error, - lib::{ - c_handler::{init_system_dictionary}, a_lexer::{TokenType, TokenTypeFelt252}, - utils::ByteArrayTraitExt, - }, + models::index::Dict, types::command_type::{TokenType, IntoTokenTypeFelt252}, + constants::errors::Error, lib::{c_handler::{init_system_dictionary}, utils::ByteArrayTraitExt}, }; use core::result::{Result, ResultTrait}; -#[derive(Clone, Drop, Serde, Introspect, Debug)] -#[dojo::model] -pub struct Dict { - #[key] - pub dict_key: felt252, - pub word: ByteArray, - pub tokenType: TokenType, - pub n_value: felt252, -} - pub fn add_to_dictionary( mut world: WorldStorage, word: ByteArray, tokenType: TokenType, n_value: felt252, ) -> Result<(), Error> { @@ -203,7 +190,7 @@ pub fn init_dictionary(world: WorldStorage) { mod tests { use lore::tests::helpers; use super::*; - use lore::lib::a_lexer::{TokenType, TokenTypeFelt252}; + use lore::types::command_type::{TokenType, IntoTokenTypeFelt252}; #[test] fn Dictionary_test_init() { diff --git a/packages/contracts/src/lib/entity.cairo b/packages/contracts/src/lib/entity.cairo index c0f1d933..8cfb581f 100644 --- a/packages/contracts/src/lib/entity.cairo +++ b/packages/contracts/src/lib/entity.cairo @@ -38,6 +38,7 @@ pub impl EntityImpl of EntityTrait { world.write_model(@entity); let mut player: Player = Component::add_component(world, address.into()); player.address = address; + player.game_instance = 0; // Initialize to 0 (no game instance) world.write_model(@player); let mut inspectable: Inspectable = Component::add_component(world, address.into()); inspectable.description = array!["Looks like a visitor"]; diff --git a/packages/contracts/src/lib/level_test.cairo b/packages/contracts/src/lib/level_test.cairo index 97671328..f5b4573a 100644 --- a/packages/contracts/src/lib/level_test.cairo +++ b/packages/contracts/src/lib/level_test.cairo @@ -1,9 +1,11 @@ -use super::entity::EntityTrait; use dojo::{world::WorldStorage, model::ModelStorage}; -use lore::components::{ - Component, inspectable::{Inspectable, InspectableComponent}, area::{Area}, exit::{Exit}, +use lore::{ + models::{ + index::{Entity, Area, Exit, Inspectable, DescriptionText}, components::Component, + area::AreaComponent, exit::ExitComponent, inspectable::InspectableComponent, + }, + new_components::entity_trait::EntityImpl, }; -use lore::lib::{entity::{Entity, EntityImpl}}; pub fn create_test_level(mut world: WorldStorage) { room_start(world); @@ -13,6 +15,7 @@ pub fn create_test_level(mut world: WorldStorage) { fn room_start(mut world: WorldStorage) { let obj = Entity { inst: 2826, + game_instance: 0, // Default game instance for test level is_entity: true, name: "The Bang", alt_names: array!["bang", "explosion"], @@ -20,12 +23,15 @@ fn room_start(mut world: WorldStorage) { }; world.write_model(@obj); let mut inspectable: Inspectable = Component::add_component(world, obj.inst); - inspectable - .description = - array![ - "The first thing you've ever seen, it's pretty wild, flaring colors like flower petals but kaleidoscopically distorted", - "Pretty colors", - ]; + let descr1 = DescriptionText { + inst: 2826, + key: 0, + text: "The first thing you've ever seen, it's pretty wild, flaring colors like flower petals but kaleidoscopically distorted", + }; + let descr2 = DescriptionText { inst: 2826, key: 1, text: "Pretty colors" }; + world.write_model(@descr1); + world.write_model(@descr2); + inspectable.description = array![0, 1]; inspectable.store(world); let _: Area = Component::add_component(world, obj.inst); object_room_one(world, obj); @@ -34,6 +40,7 @@ fn room_start(mut world: WorldStorage) { fn object_room_one(mut world: WorldStorage, parent: Entity) { let obj = Entity { inst: 9999, + game_instance: 0, // Default game instance for test level is_entity: true, name: "a portal", alt_names: array!["portal", "door"], @@ -41,7 +48,13 @@ fn object_room_one(mut world: WorldStorage, parent: Entity) { }; world.write_model(@obj); let mut inspectable: Inspectable = Component::add_component(world, obj.inst); - inspectable.description = array!["A swirling circle of colors, it doesn't seem solid"]; + let descr1 = DescriptionText { inst: 9999, key: 0, text: "A portal" }; + let descr2 = DescriptionText { + inst: 9999, key: 1, text: "A swirling circle of colors, it doesn't seem solid", + }; + world.write_model(@descr1); + world.write_model(@descr2); + inspectable.description = array![0, 1]; inspectable.store(world); let mut exit: Exit = Component::add_component(world, obj.inst); exit.leads_to = 1234; @@ -56,12 +69,15 @@ fn room_two(mut world: WorldStorage) { entity.alt_names = array!["garden"]; world.write_model(@entity); let mut inspectable: Inspectable = Component::add_component(world, entity.inst); - inspectable - .description = - array![ - "Just suddenly it's all flowers and trees and grass", - "Still pretty colors, but now it all has definition", - ]; + let descr1 = DescriptionText { + inst: 1234, key: 0, text: "Just suddenly it's all flowers and trees and grass", + }; + let descr2 = DescriptionText { + inst: 1234, key: 1, text: "Still pretty colors, but now it all has definition", + }; + world.write_model(@descr1); + world.write_model(@descr2); + inspectable.description = array![0, 1]; inspectable.store(world); let _: Area = Component::add_component(world, entity.inst); } diff --git a/packages/contracts/src/lib/utils.cairo b/packages/contracts/src/lib/utils.cairo index 49ca3d3a..db9ca00e 100644 --- a/packages/contracts/src/lib/utils.cairo +++ b/packages/contracts/src/lib/utils.cairo @@ -1,6 +1,7 @@ use core::traits::{TryInto, Into, DivRem}; use core::result::{Result}; -use lore::constants::constants::Direction; +use lore::types::direction_type::Direction; +use starknet::get_block_timestamp; #[generate_trait] pub impl ByteArrayTraitExt of ByteArrayTrait { @@ -177,11 +178,14 @@ pub impl ByteArrayTraitExt of ByteArrayTrait { fn byte_array_from_direction(direction: Direction) -> ByteArray { match direction { - Direction::None => "none", Direction::North => "north", Direction::South => "south", Direction::East => "east", Direction::West => "west", + Direction::NorthEast => "north-east", + Direction::SouthEast => "south-east", + Direction::NorthWest => "north-west", + Direction::SouthWest => "south-west", Direction::Up => "up", Direction::Down => "down", } @@ -189,14 +193,17 @@ pub impl ByteArrayTraitExt of ByteArrayTrait { fn direction_from_felt252(direction: felt252) -> Direction { match direction { - 0 => Direction::None, - 1 => Direction::North, - 2 => Direction::South, - 3 => Direction::East, - 4 => Direction::West, - 5 => Direction::Up, - 6 => Direction::Down, - _ => Direction::None, + 0 => Direction::North, + 1 => Direction::South, + 2 => Direction::East, + 3 => Direction::West, + 4 => Direction::NorthEast, + 5 => Direction::SouthEast, + 6 => Direction::NorthWest, + 7 => Direction::SouthWest, + 8 => Direction::Up, + 9 => Direction::Down, + _ => Direction::North, } } @@ -246,6 +253,14 @@ pub impl ByteArrayTraitExt of ByteArrayTrait { return result.rev(); } + + fn generate_unique_id() -> felt252 { + // Simple unique ID generation using timestamp and a counter + // In a real implementation, you might want to use a more sophisticated approach + let timestamp: felt252 = get_block_timestamp().into(); + let random_offset: felt252 = 12345; // Simple offset for uniqueness + timestamp + random_offset + } } #[generate_trait] @@ -288,7 +303,7 @@ pub impl ClousureTraitImp of ClousureTrait { mod tests { use super::ByteArrayTraitExt; use super::ClousureTrait; - use lore::constants::constants::Direction; + use lore::types::direction_type::{Direction, IntoDirectionFelt252}; #[test] fn ByteArrayExt_to_felt252_word() { @@ -465,8 +480,8 @@ mod tests { #[test] fn test_direction_from_felt252() { let direction: Direction = Direction::North; - let felt252: felt252 = 1; - let result: Direction = ByteArrayTraitExt::direction_from_felt252(felt252); + let felt252Dir: felt252 = direction.into(); + let result: Direction = ByteArrayTraitExt::direction_from_felt252(felt252Dir); assert_eq!(result, direction, "results should be equal"); } diff --git a/packages/contracts/src/lib/variable_property.cairo b/packages/contracts/src/lib/variable_property.cairo index 6db819ae..b42f8114 100644 --- a/packages/contracts/src/lib/variable_property.cairo +++ b/packages/contracts/src/lib/variable_property.cairo @@ -1,91 +1,35 @@ use dojo::{world::{WorldStorage}, model::ModelStorage}; use lore::{ - components::{ - area::{Area, AreaComponent}, exit::{Exit, ExitComponent}, - inspectable::{Inspectable, InspectableComponent}, - inventoryItem::{InventoryItem, InventoryItemComponent}, - container::{Container, ContainerComponent}, player::{Player, PlayerComponent}, Components, + models::{ + index::{Area, Exit, Inspectable, InventoryItem, Container, Player, PropertyRegistry}, + area::AreaComponent, exit::ExitComponent, inspectable::InspectableComponent, + inventoryItem::InventoryItemComponent, container::ContainerComponent, + player::PlayerComponent, }, + types::{property_type::PropertyAccess, component_type::ComponentType}, lib::{utils::ByteArrayTraitExt, variable_property_helper::VariablePropertyHelperTrait}, constants::errors::Error, }; -// ========== VARIABLE PROXY MODEL ========== -// DONT KNOW IF THIS IS NEEDED YET -#[derive(Clone, Drop, Serde, Debug)] -#[dojo::model] -pub struct ComponentVariable { - #[key] - pub key: felt252, - pub component_type: Components, - pub entity_id: felt252, - pub property_name: ByteArray, - pub value: ByteArray, - pub last_updated: u64, -} - -// ========== REGISTRY STRUCTS ========== -#[derive(Clone, Drop, Serde, Introspect, Debug, PartialEq)] -#[dojo::model] -pub struct PropertyRegistry { - #[key] - pub component_type: Components, - pub properties: Array, -} - -#[derive(Clone, Drop, Serde, Introspect, Debug, PartialEq)] -pub struct ComponentProperty { - pub name: ByteArray, - pub property_type: PropertyType, - pub access_flags: PropertyAccess, -} - -// ========== ENUMS & TRAITS ========== -#[derive(Serde, Copy, Drop, Debug, PartialEq, Introspect)] -pub enum PropertyType { - Boolean, - Integer, - Felt252, - Direction, - ContractAddress, - String, - ByteArray, - Enum, -} - -#[derive(Serde, Copy, Drop, Debug, PartialEq, Introspect)] -pub enum PropertyAccess { - ReadOnly, - WriteOnly, - ReadWrite, -} - -#[derive(Serde, Copy, Drop, Debug, PartialEq, Introspect)] -pub enum ComponentType { - Area, - Exit, - Container, - Inspectable, - InventoryItem, - Player, - //etc -} #[generate_trait] pub impl VariablePropertyImp of VariablePropertyTrait { - fn register_component_properties(world: WorldStorage, component: Components) { + fn register_component_properties(world: WorldStorage, component: ComponentType) { VariablePropertyHelperTrait::register_properties(world, component); } fn get_property( - world: @WorldStorage, key: @felt252, property_name: @ByteArray, component_type: Components, + world: @WorldStorage, + key: @felt252, + property_name: @ByteArray, + component_type: ComponentType, ) -> (Option>, Option) { let property_registry: PropertyRegistry = world.read_model((component_type)); let mut property_value: Option> = Option::None; let mut access: Option = Option::None; match property_registry.component_type.clone() { - Components::Area => { + ComponentType::Area => { let component: Area = world.read_model(*key); let prop_text = property_name.clone(); let (property_value_opt, access_opt) = @@ -95,7 +39,7 @@ pub impl VariablePropertyImp of VariablePropertyTrait { property_value = property_value_opt; access = access_opt; }, - Components::Exit => { + ComponentType::Exit => { let component: Exit = world.read_model(*key); let prop_text = property_name.clone(); let (property_value_opt, access_opt) = @@ -105,17 +49,17 @@ pub impl VariablePropertyImp of VariablePropertyTrait { property_value = property_value_opt; access = access_opt; }, - Components::Inspectable => { + ComponentType::Inspectable => { let component: Inspectable = world.read_model(*key); let prop_text = property_name.clone(); let (property_value_opt, access_opt) = VariablePropertyHelperTrait::get_inspectable_property( - component, @prop_text, @property_registry, + component, @prop_text, @property_registry, *world, ); property_value = property_value_opt; access = access_opt; }, - Components::InventoryItem => { + ComponentType::InventoryItem => { let component: InventoryItem = world.read_model(*key); let prop_text = property_name.clone(); let (property_value_opt, access_opt) = @@ -125,7 +69,7 @@ pub impl VariablePropertyImp of VariablePropertyTrait { property_value = property_value_opt; access = access_opt; }, - Components::Container => { + ComponentType::Container => { let component: Container = world.read_model(*key); let prop_text = property_name.clone(); let (property_value_opt, access_opt) = @@ -135,7 +79,7 @@ pub impl VariablePropertyImp of VariablePropertyTrait { property_value = property_value_opt; access = access_opt; }, - Components::Player => { + ComponentType::Player => { let component: Player = world.read_model(*key); let prop_text = property_name.clone(); let (property_value_opt, access_opt) = @@ -155,14 +99,14 @@ pub impl VariablePropertyImp of VariablePropertyTrait { mut world: @WorldStorage, key: @felt252, property_name: @ByteArray, - new_value: @Array, - component_type: Components, + new_value: @Array<(ByteArray, u32)>, + component_type: ComponentType, ) -> Result<(), Error> { let property_registry: PropertyRegistry = world.read_model((component_type)); let mut success: bool = false; let mut result: Result::<(), Error> = Result::Err((Error::EffectFailed)); match component_type.clone() { - Components::Area => { + ComponentType::Area => { let component: Area = world.read_model(*key); let prop_text = property_name.clone(); let (result_p, success_p) = VariablePropertyHelperTrait::set_area_property( @@ -171,7 +115,7 @@ pub impl VariablePropertyImp of VariablePropertyTrait { result = result_p; success = success_p; }, - Components::Exit => { + ComponentType::Exit => { let component: Exit = world.read_model(*key); let prop_text = property_name.clone(); let (result_p, success_p) = VariablePropertyHelperTrait::set_exit_property( @@ -180,7 +124,7 @@ pub impl VariablePropertyImp of VariablePropertyTrait { result = result_p; success = success_p; }, - Components::Inspectable => { + ComponentType::Inspectable => { let component: Inspectable = world.read_model(*key); let prop_text = property_name.clone(); let (result_p, success_p) = VariablePropertyHelperTrait::set_inspectable_property( @@ -189,7 +133,7 @@ pub impl VariablePropertyImp of VariablePropertyTrait { result = result_p; success = success_p; }, - Components::InventoryItem => { + ComponentType::InventoryItem => { let component: InventoryItem = world.read_model(*key); let prop_text = property_name.clone(); let (result_p, success_p) = @@ -199,7 +143,7 @@ pub impl VariablePropertyImp of VariablePropertyTrait { result = result_p; success = success_p; }, - Components::Container => { + ComponentType::Container => { let component: Container = world.read_model(*key); let prop_text = property_name.clone(); let (result_p, success_p) = VariablePropertyHelperTrait::set_container_property( @@ -208,7 +152,7 @@ pub impl VariablePropertyImp of VariablePropertyTrait { result = result_p; success = success_p; }, - Components::Player => { + ComponentType::Player => { let component: Player = world.read_model(*key); let prop_text = property_name.clone(); let (result_p, success_p) = VariablePropertyHelperTrait::set_player_property( diff --git a/packages/contracts/src/lib/variable_property_helper.cairo b/packages/contracts/src/lib/variable_property_helper.cairo index 970837f6..01db685f 100644 --- a/packages/contracts/src/lib/variable_property_helper.cairo +++ b/packages/contracts/src/lib/variable_property_helper.cairo @@ -1,23 +1,27 @@ use dojo::{world::{WorldStorage}, model::ModelStorage}; use lore::{ - components::{ - area::{Area, AreaComponent}, exit::{Exit, ExitComponent}, - inspectable::{Inspectable, InspectableComponent}, - inventoryItem::{InventoryItem, InventoryItemComponent}, - container::{Container, ContainerComponent}, player::{Player, PlayerComponent}, Components, + models::{ + index::{ + Area, Exit, Inspectable, DescriptionText, InventoryItem, Container, Player, + PropertyRegistry, + }, + area::AreaComponent, exit::ExitComponent, inspectable::InspectableComponent, + inventoryItem::InventoryItemComponent, container::ContainerComponent, + player::PlayerComponent, }, - lib::{ - utils::ByteArrayTraitExt, - variable_property::{PropertyRegistry, PropertyAccess, ComponentProperty, PropertyType}, + types::{ + property_type::{ComponentProperty, PropertyType, PropertyAccess}, + component_type::ComponentType, + direction_type::{Direction, IntoDirectionByteArray, IntoFelt252Direction}, }, - constants::errors::Error, + lib::{utils::ByteArrayTraitExt}, constants::errors::Error, }; use core::traits::{Into}; #[generate_trait] pub impl VariablePropertyHelper of VariablePropertyHelperTrait { // Register Component Properties - fn register_properties(mut world: WorldStorage, component: Components) { + fn register_properties(mut world: WorldStorage, component: ComponentType) { let pos_property_registry: PropertyRegistry = world.read_model(component); if pos_property_registry.properties.len() > 0 { // Registry already exists, skip @@ -25,7 +29,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { } let props = match component { - Components::Area => array![ + ComponentType::Area => array![ ComponentProperty { name: "is_area", property_type: PropertyType::Boolean, @@ -37,7 +41,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { access_flags: PropertyAccess::ReadWrite, }, ], - Components::Inspectable => array![ + ComponentType::Inspectable => array![ ComponentProperty { name: "is_inspectable", property_type: PropertyType::Boolean, @@ -50,7 +54,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { }, ComponentProperty { name: "description", - property_type: PropertyType::String, + property_type: PropertyType::ByteArray, access_flags: PropertyAccess::ReadWrite, }, ComponentProperty { @@ -60,11 +64,11 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { }, ComponentProperty { name: "new_entry", - property_type: PropertyType::String, + property_type: PropertyType::ByteArray, access_flags: PropertyAccess::ReadWrite, }, ], - Components::Exit => array![ + ComponentType::Exit => array![ ComponentProperty { name: "is_exit", property_type: PropertyType::Boolean, @@ -82,11 +86,11 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { }, ComponentProperty { name: "direction_type", - property_type: PropertyType::Direction, + property_type: PropertyType::Enum, access_flags: PropertyAccess::ReadWrite, }, ], - Components::InventoryItem => array![ + ComponentType::InventoryItem => array![ ComponentProperty { name: "owner_id", property_type: PropertyType::Felt252, @@ -113,7 +117,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { access_flags: PropertyAccess::ReadWrite, }, ], - Components::Container => array![ + ComponentType::Container => array![ ComponentProperty { name: "is_container", property_type: PropertyType::Boolean, @@ -136,11 +140,11 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { }, ComponentProperty { name: "num_slots", - property_type: PropertyType::Integer, + property_type: PropertyType::U8, access_flags: PropertyAccess::ReadWrite, }, ], - Components::Player => array![ + ComponentType::Player => array![ ComponentProperty { name: "location", property_type: PropertyType::Felt252, @@ -206,11 +210,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { } else if name == @direction_type { arr .append( - ByteArrayTraitExt::to_felt252_word( - @ByteArrayTraitExt::byte_array_from_direction( - component.direction_type, - ), - ) + ByteArrayTraitExt::to_felt252_word(@component.direction_type.into()) .unwrap(), ); } @@ -223,7 +223,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { } fn get_inspectable_property( - component: Inspectable, name: @ByteArray, property: @PropertyRegistry, + component: Inspectable, name: @ByteArray, property: @PropertyRegistry, world: WorldStorage, ) -> (Option>, Option) { // Define expected property names let is_visible: ByteArray = "is_visible"; @@ -242,8 +242,9 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { } else if name == @description { let desc = component.description; for i in 0..desc.len() { - let part: ByteArray = desc.at(i).clone(); - let felt = ByteArrayTraitExt::to_felt252_word(@part).unwrap(); + let key: u32 = desc.at(i).clone(); + let descText: DescriptionText = world.read_model((component.inst, key)); + let felt = ByteArrayTraitExt::to_felt252_word(@descText.text).unwrap(); arr.append(felt); } } @@ -351,7 +352,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { mut world: WorldStorage, name: @ByteArray, property: @PropertyRegistry, - new_value: @Array, + new_value: @Array<(ByteArray, u32)>, ) -> (Result::<(), Error>, bool) { // Define expected property names let is_area: ByteArray = "is_area"; @@ -367,12 +368,10 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { result = Result::Err(Error::ReadOnlyVariable); } }, - PropertyAccess::WriteOnly | PropertyAccess::ReadWrite => { if name == @is_spawn_point { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.is_spawn_point = new_var_value; success = true; } @@ -390,7 +389,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { mut world: WorldStorage, name: @ByteArray, property: @PropertyRegistry, - new_value: @Array, + new_value: @Array<(ByteArray, u32)>, ) -> (Result::<(), Error>, bool) { // Define expected property names let is_exit: ByteArray = "is_exit"; @@ -404,32 +403,31 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { if prop.name == name.clone() { match prop.access_flags { PropertyAccess::ReadOnly => { result = Result::Err(Error::ReadOnlyVariable); }, - PropertyAccess::WriteOnly | PropertyAccess::ReadWrite => { if name == @is_exit { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.is_exit = new_var_value; success = true; } else if name == @is_enterable { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.is_enterable = new_var_value; success = true; } else if name == @leads_to { - component - .leads_to = - ByteArrayTraitExt::to_felt252_word(@new_value[0].clone()) - .unwrap(); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::to_felt252_word(@value).unwrap(); + component.leads_to = new_var_value; success = true; } else if name == @direction_type { - let new_dir = ByteArrayTraitExt::direction_from_felt252( - ByteArrayTraitExt::to_felt252_word( - @ByteArrayTraitExt::to_lowercase(new_value[0].clone()), - ) - .unwrap(), + let (value, _index) = new_value[0].clone(); + let lowercased = ByteArrayTraitExt::to_lowercase(value); + let to_felt252_direction = ByteArrayTraitExt::to_felt252_word( + @lowercased, + ) + .unwrap(); + let new_dir: Direction = IntoFelt252Direction::into( + to_felt252_direction, ); component.direction_type = new_dir; success = true; @@ -448,7 +446,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { mut world: WorldStorage, name: @ByteArray, property: @PropertyRegistry, - new_value: @Array, + new_value: @Array<(ByteArray, u32)>, ) -> (Result::<(), Error>, bool) { // Define expected property names let is_visible: ByteArray = "is_visible"; @@ -460,32 +458,43 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { if prop.name == name.clone() { match prop.access_flags { PropertyAccess::ReadOnly => { result = Result::Err(Error::ReadOnlyVariable); }, - PropertyAccess::WriteOnly | PropertyAccess::ReadWrite => { if name == @is_visible { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.is_visible = new_var_value; success = true; } else if name == @is_inspectable { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.is_inspectable = new_var_value; success = true; } else if name == @description { - // FOR NOW REPLACES THE WHOLE ARRAY, - // TODO: implement a way that supports updating/replacing/removing at - // certain indices Build a temporary mutable copy of description - let mut new_description = array![]; + for (value, index) in new_value.clone() { + let mut found: bool = false; - // Copy the original description - for item in new_value.clone() { - let new_byte = item; - new_description.append(new_byte.clone()); + // Check if index is already in component.description + for key in component.description.clone() { + if key == index { + // Update existing description + let mut descText: DescriptionText = world + .read_model((component.inst.clone(), index)); + descText.text = value.clone(); + world.write_model(@descText); + found = true; + break; + } + }; + + if !found { + // Add new description + let new_desc = DescriptionText { + inst: component.inst, key: index, text: value, + }; + world.write_model(@new_desc); + component.description.append(index); + }; }; - component.description = new_description; success = true; } }, @@ -502,7 +511,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { mut world: WorldStorage, name: @ByteArray, property: @PropertyRegistry, - new_value: @Array, + new_value: @Array<(ByteArray, u32)>, ) -> (Result::<(), Error>, bool) { // Define expected property names let owner_id: ByteArray = "owner_id"; @@ -517,33 +526,29 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { if prop.name == name.clone() { match prop.access_flags { PropertyAccess::ReadOnly => { result = Result::Err(Error::ReadOnlyVariable); }, - PropertyAccess::WriteOnly | PropertyAccess::ReadWrite => { if name == @owner_id { - component.owner_id = new_value[0].to_felt252_word().unwrap(); + let (value, _index) = new_value[0].clone(); + component.owner_id = value.to_felt252_word().unwrap(); success = true; } else if name == @can_be_picked_up { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.can_be_picked_up = new_var_value; success = true; } else if name == @can_go_in_container { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.can_go_in_container = new_var_value; success = true; } else if name == @already_used { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.already_used = new_var_value; success = true; } else if name == @multiple_use { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.multiple_use = new_var_value; success = true; } @@ -561,7 +566,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { mut world: WorldStorage, name: @ByteArray, property: @PropertyRegistry, - new_value: @Array, + new_value: @Array<(ByteArray, u32)>, ) -> (Result::<(), Error>, bool) { // Define expected property names let is_container: ByteArray = "is_container"; @@ -576,36 +581,30 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { if prop.name == name.clone() { match prop.access_flags { PropertyAccess::ReadOnly => { result = Result::Err(Error::ReadOnlyVariable); }, - PropertyAccess::WriteOnly | PropertyAccess::ReadWrite => { if name == @is_container { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.is_container = new_var_value; success = true; } else if name == @can_be_opened { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.can_be_opened = new_var_value; success = true; } else if name == @can_receive_items { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.can_receive_items = new_var_value; success = true; } else if name == @is_open { - let new_var_value = ByteArrayTraitExt::bool_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::bool_from_byte_array(value); component.is_open = new_var_value; success = true; } else if name == @num_slots { - let new_var_value = ByteArrayTraitExt::u32_from_byte_array( - new_value[0].clone(), - ); + let (value, _index) = new_value[0].clone(); + let new_var_value = ByteArrayTraitExt::u32_from_byte_array(value); component.num_slots = new_var_value; success = true; } @@ -623,7 +622,7 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { mut world: WorldStorage, name: @ByteArray, property: @PropertyRegistry, - new_value: @Array, + new_value: @Array<(ByteArray, u32)>, ) -> (Result::<(), Error>, bool) { // Define expected property names let location: ByteArray = "location"; @@ -634,11 +633,11 @@ pub impl VariablePropertyHelper of VariablePropertyHelperTrait { if prop.name == name.clone() { match prop.access_flags { PropertyAccess::ReadOnly => { result = Result::Err(Error::ReadOnlyVariable); }, - PropertyAccess::WriteOnly | PropertyAccess::ReadWrite => { if name == @location { + let (value, _index) = new_value[0].clone(); component - .location = ByteArrayTraitExt::to_felt252_word(new_value[0]) + .location = ByteArrayTraitExt::to_felt252_word(@value) .unwrap(); success = true; } diff --git a/packages/contracts/src/models/area.cairo b/packages/contracts/src/models/area.cairo new file mode 100644 index 00000000..9a62f837 --- /dev/null +++ b/packages/contracts/src/models/area.cairo @@ -0,0 +1,53 @@ +use dojo::{world::WorldStorage, model::ModelStorage}; +use lore::{ + models::{index::{Area, Player}, components::Component}, types::{command_type::Command}, + constants::errors::Error, +}; + +pub impl AreaComponent of Component { + type ComponentType = Area; + + fn inst(self: @Area) -> @felt252 { + self.inst + } + + fn has_component(self: @Area, world: WorldStorage, inst: felt252) -> bool { + let area: Area = world.read_model(inst); + area.is_area + } + + fn add_component(mut world: WorldStorage, inst: felt252) -> Area { + let mut area: Area = world.read_model(inst); + area.inst = inst; + area.is_area = true; + world.write_model(@area); + // Return the component + area + } + + fn get_component(world: WorldStorage, inst: felt252) -> Option { + let area: Area = world.read_model(inst); + if (!area.has_component(world, inst)) { + return Option::None; + } + let area: Area = world.read_model(inst); + Option::Some(area) + } + + fn can_use_command( + self: @Area, world: WorldStorage, player: @Player, command: @Command, + ) -> bool { + true + } + + fn execute_command( + self: Area, world: WorldStorage, player: @Player, command: @Command, + ) -> Result<(), Error> { + println!("Area execute_command"); + Result::Err(Error::Unimplemented) + } + + fn store(self: @Area, mut world: WorldStorage) { + world.write_model(self); + } +} diff --git a/packages/contracts/src/models/components.cairo b/packages/contracts/src/models/components.cairo new file mode 100644 index 00000000..d8496086 --- /dev/null +++ b/packages/contracts/src/models/components.cairo @@ -0,0 +1,34 @@ +// Here you can find the generic methods for all the components. + +use dojo::{world::WorldStorage, model::{Model}}; +use lore::{ + models::index::{Entity, Player}, new_components::entity_trait::EntityImpl, + types::{command_type::Command}, constants::errors::Error, +}; + + +pub trait Component> { + type ComponentType; + fn get_component(world: WorldStorage, inst: felt252) -> Option; + + fn inst(self: @T) -> @felt252; + + fn entity( + self: @T, world: @WorldStorage, + ) -> Entity { + EntityImpl::get_entity(world, Self::inst(self)).unwrap() + } + + fn has_component(self: @T, world: WorldStorage, inst: felt252) -> bool; + + fn add_component(world: WorldStorage, inst: felt252) -> T; + + fn can_use_command(self: @T, world: WorldStorage, player: @Player, command: @Command) -> bool; + + fn execute_command( + self: T, world: WorldStorage, player: @Player, command: @Command, + ) -> Result<(), Error>; + + fn store(self: @T, world: WorldStorage); +} + diff --git a/packages/contracts/src/models/container.cairo b/packages/contracts/src/models/container.cairo new file mode 100644 index 00000000..31e28fa4 --- /dev/null +++ b/packages/contracts/src/models/container.cairo @@ -0,0 +1,130 @@ +use dojo::{world::{WorldStorage}, model::ModelStorage}; +use lore::{ + models::{index::{Entity, Container, Player}, components::Component}, + new_components::{ + entity_trait::EntityImpl, player_trait::PlayerImpl, container_trait::ContainerImpl, + }, + types::{command_type::{Command, Token}, component_type::{ContainerActions, ActionMapContainer}}, + lib::{a_lexer::CommandImpl}, constants::errors::Error, +}; + +pub impl ContainerComponent of Component { + type ComponentType = Container; + + fn inst(self: @Container) -> @felt252 { + self.inst + } + + fn entity(self: @Container, world: @WorldStorage) -> Entity { + EntityImpl::get_entity(world, self.inst).unwrap() + } + + fn has_component(self: @Container, world: WorldStorage, inst: felt252) -> bool { + let container: Container = world.read_model(inst); + container.is_container + } + + fn add_component(mut world: WorldStorage, inst: felt252) -> Container { + let mut container: Container = world.read_model(inst); + container.inst = inst; + container.is_container = true; + container.can_be_opened = true; + container.can_receive_items = true; + container.is_open = true; + container.num_slots = 0; + container + .action_map = + array![ + ActionMapContainer { + action: "open", inst: 0, action_fn: ContainerActions::Open, + }, + ActionMapContainer { + action: "close", inst: 0, action_fn: ContainerActions::Close, + }, + ActionMapContainer { + action: "check", inst: 0, action_fn: ContainerActions::Check, + }, + ]; + container.store(world); + // Return the component + container + } + + fn get_component(world: WorldStorage, inst: felt252) -> Option { + let container: Container = world.read_model(inst); + if (!container.has_component(world, inst)) { + return Option::None; + } + Option::Some(container) + } + + fn can_use_command( + self: @Container, world: WorldStorage, player: @Player, command: @Command, + ) -> bool { + get_action_token(self, world, command).is_some() + } + + fn execute_command( + mut self: Container, mut world: WorldStorage, player: @Player, command: @Command, + ) -> Result<(), Error> { + // println!("Container execute_command"); + let (action, _token) = get_action_token(@self, world, command).unwrap(); + let nouns = command.get_nouns(); + match action.action_fn { + ContainerActions::Open => { + if (self.is_open) { + player + .say( + world, + format!("The {} is already open.", self.clone().entity(@world).name), + ); + } else { + player.say(world, format!("You open {}", self.clone().entity(@world).name)); + self.set_open(world, true); + } + return Result::Ok(()); + }, + ContainerActions::Close => { + if (!self.is_open) { + player + .say( + world, + format!("The {} is already closed.", self.clone().entity(@world).name), + ); + } else { + player.say(world, format!("You close {}", self.clone().entity(@world).name)); + self.set_open(world, false); + } + return Result::Ok(()); + }, + ContainerActions::Check => { + // Check container status + let doneChecking = self.check_container(@world, player, nouns[0].text); + if (doneChecking) { + return Result::Ok(()); + } + }, + } + Result::Err(Error::ActionFailed) + } + + fn store(self: @Container, mut world: WorldStorage) { + world.write_model(self); + } +} + +// @dev: wip how to access tokens +fn get_action_token( + self: @Container, world: WorldStorage, command: @Command, +) -> Option<(ActionMapContainer, Token)> { + let mut action_token: Option<(ActionMapContainer, Token)> = Option::None; + for token in command.tokens.clone() { + for action in self.action_map.clone() { + if (token.text == action.action) { + action_token = Option::Some((action, token)); + break; + } + } + }; + action_token +} diff --git a/packages/contracts/src/models/exit.cairo b/packages/contracts/src/models/exit.cairo new file mode 100644 index 00000000..78a42a2d --- /dev/null +++ b/packages/contracts/src/models/exit.cairo @@ -0,0 +1,184 @@ +use dojo::{world::WorldStorage, model::ModelStorage}; +use lore::{ + models::{index::{Entity, Exit, Player, Action}, components::Component}, + new_components::{ + entity_trait::EntityImpl, exit_trait::ExitImpl, player_trait::PlayerImpl, + action_trait::ActionImpl, + }, + types::{ + component_type::{ExitActions, ActionMapExit}, command_type::{Command, Token}, + action_type::TriggerContext, direction_type::{IntoDirectionByteArray}, + }, + lib::{a_lexer::CommandImpl, utils::ByteArrayTraitExt}, constants::errors::Error, constants, +}; + +pub impl ExitComponent of Component { + type ComponentType = Exit; + + fn inst(self: @Exit) -> @felt252 { + self.inst + } + + fn entity(self: @Exit, world: @WorldStorage) -> Entity { + EntityImpl::get_entity(world, self.inst).unwrap() + } + + fn has_component(self: @Exit, world: WorldStorage, inst: felt252) -> bool { + let exit: Exit = world.read_model(inst); + exit.is_exit + } + + fn add_component(mut world: WorldStorage, inst: felt252) -> Exit { + let mut exit: Exit = world.read_model(inst); + exit.inst = inst; + exit.is_exit = true; + exit + .action_map = + array![ + ActionMapExit { action: "go", inst: 0, action_fn: ExitActions::UseExit }, + ActionMapExit { action: "enter", inst: 0, action_fn: ExitActions::UseExit }, + ActionMapExit { action: "use", inst: 0, action_fn: ExitActions::UseExit }, + ]; + exit.store(world); + // Return the component + exit + } + + fn get_component(world: WorldStorage, inst: felt252) -> Option { + let exit: Exit = world.read_model(inst); + if (!exit.has_component(world, inst)) { + return Option::None; + } + let exit: Exit = world.read_model(inst); + Option::Some(exit) + } + + fn can_use_command( + self: @Exit, world: WorldStorage, player: @Player, command: @Command, + ) -> bool { + get_action_token(self, world, command).is_some() + } + + fn execute_command( + mut self: Exit, mut world: WorldStorage, player: @Player, command: @Command, + ) -> Result<(), Error> { + println!("Exit execute_command"); + let (action, _token) = get_action_token(@self, world, command).unwrap(); + let direction_tokens = command.get_directions(); + + let mut destination_inst: felt252 = 0; + match action.action_fn { + ExitActions::UseExit => { + if *player.use_debug { + player.say(world, format!("You go to {:?}", self)); + } + + let mut matchesName = false; + let nouns = command.get_nouns(); + let names = self.entity(@world).get_names(); + for noun in nouns { + for name in names.clone() { + if noun.text == name { + matchesName = true; + break; + } + } + }; + + let mut matchesDirection = false; + if (direction_tokens.len() > 0 + && matches_direction(@self, world, player, @direction_tokens).is_some()) { + matchesDirection = true; + } + + // we need to either match by name or by direction + if (!(matchesName || matchesDirection)) { + return Result::Err(Error::ActionFailed); + } + // if the exit is not enterable, we can't go there + if (!self.clone().can_player_enter()) { + player.say(world, format!("You can't go there yet.")); + return Result::Err(Error::ActionFailed); + } + // Move player to room + destination_inst = self.leads_to; + player.clone().move_to_room(world, destination_inst); + + // Do action + // Check if the entity of the exit has an action + let pos_entity = EntityImpl::get_entity(@world, @self.inst); + if pos_entity.is_none() { + return Result::Err(Error::NoTargetEntity); + } + let pos_entity = pos_entity.unwrap(); + let pos_actions = pos_entity.actions_keys; + if pos_actions.len() > 0 { + let mut actions: Array = ArrayTrait::new(); + // For each action, execute it + for key in pos_actions { + let mut action: Action = world.read_model((pos_entity.inst, key)); + actions.append(action); + }; + if actions.len() == 0 { + // No actions found, just return + return Result::Ok(()); + } + // execute actions + for action in actions { + // context is not being used inside evaluations or processing. + let context = TriggerContext { + doer: *player.inst, + target1: self.leads_to, // would be the room that the player moved to + target2: 0, + inventory_object: 0, + }; + + let (_trig_res, _cond_res, _eff_res) = ActionImpl::process_action( + action, world, @context, + ); + }; + } + // Describe room + let _ = player.describe_room(world); + return Result::Ok(()); + }, + } + Result::Err(Error::ActionFailed) + } + + fn store(self: @Exit, mut world: WorldStorage) { + world.write_model(self); + } +} + + +fn matches_direction( + self: @Exit, world: WorldStorage, player: @Player, directions_token: @Array, +) -> Option { + if (directions_token.len() == 0) { + return Option::None; + } + let exit_dir = ByteArrayTraitExt::byte_array_from_direction(*self.direction_type); + let dir_text = constants::direction_one_letter(directions_token[0].text); + // println!("area_dir: {:?}", directions_token[0]); + if (exit_dir == dir_text) { + return Option::Some(*self.leads_to); + } + Option::None +} + +// @dev: wip how to access tokens +fn get_action_token( + self: @Exit, world: WorldStorage, command: @Command, +) -> Option<(ActionMapExit, Token)> { + let mut action_token: Option<(ActionMapExit, Token)> = Option::None; + for token in command.tokens.clone() { + for action in self.action_map.clone() { + if (token.text == action.action) { + action_token = Option::Some((action, token)); + break; + } + } + }; + action_token +} diff --git a/packages/contracts/src/models/index.cairo b/packages/contracts/src/models/index.cairo new file mode 100644 index 00000000..98d275cf --- /dev/null +++ b/packages/contracts/src/models/index.cairo @@ -0,0 +1,383 @@ +// Here you can find all the defined models. + +use starknet::ContractAddress; +use lore::{ + types::{ + action_type::{TriggerType, Operator}, command_type::{TokenType}, + component_type::{ + ComponentType, ActionMapInspectable, ActionMapExit, ActionMapContainer, + ActionMapInventoryItem, + }, + direction_type::Direction, property_type::{ComponentProperty}, + }, +}; + +#[derive(Clone, Drop, Serde, Introspect)] +#[dojo::model] +pub struct ParentToChildren { + #[key] + pub inst: felt252, + pub is_parent: bool, + /// Properties /// + /// The children entities + pub children: Array, +} + +#[derive(Clone, Drop, Serde, Introspect)] +#[dojo::model] +pub struct ChildToParent { + #[key] + pub inst: felt252, + pub is_child: bool, + /// Properties /// + /// The parent entity + pub parent: felt252, +} + +#[derive(Clone, Drop, Serde, Introspect, Debug)] +#[dojo::model] +pub struct Dict { + #[key] + pub dict_key: felt252, + pub word: ByteArray, + pub tokenType: TokenType, + pub n_value: felt252, +} + +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct Entity { + #[key] + pub inst: felt252, + #[key] + pub game_instance: felt252, + pub is_entity: bool, + /// Properties /// + /// Name of the entity + pub name: ByteArray, + /// Alternative names of the entity + pub alt_names: Array, + /// Holds the keys of the actions that are attached to this entity + pub actions_keys: Array, +} + +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct Area { + #[key] + pub inst: felt252, + #[key] + pub game_instance: felt252, + pub is_area: bool, + /// Properties /// + /// If the area is a spawn point for players + pub is_spawn_point: bool, +} + +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct Inspectable { + #[key] + pub inst: felt252, + #[key] + pub game_instance: felt252, + pub is_inspectable: bool, + /// Properties /// + /// If the inspectable is visible + pub is_visible: bool, + /// Array of descriptions for the inspectable + pub description: Array, + /// Array of action maps for the inspectable + pub action_map: Array, + /// For the first description, if we want to show a different one + pub already_shown: bool, + /// New first description + pub new_entry: ByteArray, +} + +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct DescriptionText { + /// Unique identifier from the Entity it is attached to + #[key] + pub inst: felt252, + /// Unique identifier of the description + #[key] + pub key: u32, + /// Description text + pub text: ByteArray, +} + +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct Exit { + #[key] + pub inst: felt252, + #[key] + pub game_instance: felt252, + pub is_exit: bool, + /// Properties /// + /// If the exit is enterable + pub is_enterable: bool, + /// The leads to entity + pub leads_to: felt252, + /// The direction type + pub direction_type: Direction, + /// Array of action maps for the exit + pub action_map: Array, +} + +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct Container { + #[key] + pub inst: felt252, + #[key] + pub game_instance: felt252, + pub is_container: bool, + /// Properties /// + /// If the container can be opened + pub can_be_opened: bool, + /// If the container can receive items + pub can_receive_items: bool, + /// If the container is open + pub is_open: bool, + /// Total number of slots of the container + pub num_slots: u32, + // pub accept_tags: Array, + pub action_map: Array, +} + +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct InventoryItem { + #[key] + pub inst: felt252, + #[key] + pub game_instance: felt252, + pub is_inventory_item: bool, + /// Properties /// + /// The owner of the inventory item + pub owner_id: felt252, + /// If the inventory item can be picked up + pub can_be_picked_up: bool, + /// If the inventory item can go in a container + pub can_go_in_container: bool, + /// Array of action maps for the inventory item + pub action_map: Array, + /// If the inventory item has already been used + pub already_used: bool, + /// If the inventory item can be used multiple times + pub multiple_use: bool, +} + +#[derive(Copy, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct Player { + #[key] + pub inst: felt252, + pub is_player: bool, + /// Properties /// + /// The address of the player + pub address: ContractAddress, + /// The game instance this player belongs to + pub game_instance: felt252, + /// The location of the player + pub location: felt252, + /// Current story line + pub story_line: CounterType, + /// If the player is in debug mode + pub use_debug: bool, +} + +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct PlayerStory { + #[key] + pub inst: felt252, + /// Properties /// + /// Array of story lines (story lines keys) + pub story: Array, +} + +pub type CounterType = u64; +#[derive(Clone, Drop, Serde, Debug, Introspect, PartialEq)] +#[dojo::model] +pub struct StoryLine { + /// Unique identifier (Player or PlayerStory) + #[key] + pub inst: felt252, + /// Unique identifier of the line + #[key] + pub key: CounterType, + /// Story line + pub line: ByteArray, +} + +#[derive(Clone, Drop, Serde, Introspect, Debug, PartialEq)] +#[dojo::model] +pub struct PropertyRegistry { + #[key] + pub component_type: ComponentType, + pub properties: Array, +} + +#[derive(Clone, Drop, Serde, Debug, Introspect, PartialEq)] +#[dojo::model] +pub struct Action { + /// Unique identifier attached to the entity + #[key] + pub inst: felt252, + /// Unique identifier of the action + #[key] + pub key: felt252, + /// Game instance this action belongs to + #[key] + pub game_instance: felt252, + /// Properties /// + /// Name of the action + pub name: ByteArray, + /// Optional description + pub description: ByteArray, + /// For toggling the entire action + pub is_enabled: bool, + /// When this action can occur, the id's of the triggers. Key is (trigger.inst, trigger.key) + pub trigger: Array<(felt252, felt252)>, + /// What must be true, the id's of the conditions. Key is (condition.inst, condition.key) + pub conditions: Array<(felt252, felt252)>, + /// What happens when triggered, the id's of the effects. Key is (effect.inst, effect.key) + pub effects: Array<(felt252, felt252)>, + /// For searching/filtering + pub tags: Array, + /// Whether the action has been executed + pub executed: bool, + /// In case action fails, need a response + pub failing_response: Array, + /// In case action succeeds, need a response + pub success_response: Array, +} + +#[derive(Clone, Drop, Serde, Debug, Introspect, PartialEq)] +#[dojo::model] +pub struct Trigger { + /// Unique identifier from the entity that is attached to + #[key] + pub inst: felt252, + /// Unique identifier of the trigger + #[key] + pub key: felt252, + /// Game instance this trigger belongs to + #[key] + pub game_instance: felt252, + /// Trigger name + pub name: ByteArray, + /// The type of trigger + pub trigger_type: TriggerType, + /// Whether the trigger is enabled + pub is_enabled: bool, + /// Whether the trigger only triggers once + pub is_once: bool, + /// Whether the trigger has already been triggered + pub was_triggered: bool, +} + +#[derive(Clone, Drop, Serde, Debug)] +#[dojo::model] +pub struct TriggerIndex { + #[key] + pub trigger_type: TriggerType, + pub trigger_id: Array<(felt252, felt252)> // (inst, key) +} + +#[derive(Clone, Drop, Serde, Debug, Introspect, PartialEq)] +#[dojo::model] +pub struct Condition { + /// Unique identifier attached to the entity + #[key] + pub inst: felt252, + /// Unique identifier of the condition + #[key] + pub key: felt252, + /// Game instance this condition belongs to + #[key] + pub game_instance: felt252, + /// Condition name + pub name: ByteArray, + /// The target inst. + pub target: felt252, + /// Which component to check + pub component: ComponentType, + /// Which property of the component to check + pub property: ByteArray, + /// How to compare the values + pub operator: Operator, + /// Value to compare against + pub value: Array, +} + +#[derive(Clone, Drop, Serde, Debug, Introspect, PartialEq)] +#[dojo::model] +pub struct Effect { + /// Unique identifier attached to the entity + #[key] + pub inst: felt252, + /// Unique identifier of the effect + #[key] + pub key: felt252, + /// Game instance this effect belongs to + #[key] + pub game_instance: felt252, + /// Effect name + pub name: ByteArray, + /// Target entity + pub target: felt252, + /// Component to affect + pub component: ComponentType, + /// Property to modify + pub property: ByteArray, + /// New value to set, needs to be tuple array. First element is the value, second is the index + /// (for texts). + pub value: Array<(ByteArray, u32)>, +} + +/// NOT USED YET /// + +// ========== VARIABLE PROXY MODEL ========== +// DONT KNOW IF THIS IS NEEDED YET // +#[derive(Clone, Drop, Serde, Debug)] +#[dojo::model] +pub struct ComponentVariable { + #[key] + pub inst: felt252, // Entity inst + #[key] + pub key: felt252, // Component key + #[key] + pub id: felt252, // unique id + /// Properties /// + /// The component type + pub component_type: ComponentType, + /// The property name + pub property_name: ByteArray, + /// The property value + pub value: ByteArray, + /// The last time the property was updated + pub last_updated: u64, +} + +// ========== GAME INSTANCE MODEL ========== +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +#[dojo::model] +pub struct GameInstance { + #[key] + pub inst: felt252, + pub is_game_instance: bool, + /// Properties /// + /// The owner/creator of this game instance + pub owner: ContractAddress, + /// Game instance name + pub name: ByteArray, + /// Whether the instance is active + pub is_active: bool, + /// Creation timestamp + pub created_at: u64, +} diff --git a/packages/contracts/src/models/inspectable.cairo b/packages/contracts/src/models/inspectable.cairo new file mode 100644 index 00000000..a9737dee --- /dev/null +++ b/packages/contracts/src/models/inspectable.cairo @@ -0,0 +1,219 @@ +use dojo::{world::WorldStorage, model::ModelStorage}; +use lore::{ + models::{index::{Entity, Inspectable, Player}, components::Component}, + new_components::{ + entity_trait::EntityImpl, inspectable_trait::InspectableImpl, player_trait::PlayerImpl, + }, + types::{ + command_type::{Command, Token}, component_type::{InspectableActions, ActionMapInspectable}, + }, + constants::errors::Error, +}; + + +pub impl InspectableComponent of Component { + type ComponentType = Inspectable; + + fn inst(self: @Inspectable) -> @felt252 { + self.inst + } + + fn entity(self: @Inspectable, world: @WorldStorage) -> Entity { + EntityImpl::get_entity(world, self.inst).unwrap() + } + + fn has_component(self: @Inspectable, world: WorldStorage, inst: felt252) -> bool { + let inspectable: Inspectable = world.read_model(inst); + inspectable.is_inspectable + } + + fn add_component(mut world: WorldStorage, inst: felt252) -> Inspectable { + let mut inspectable: Inspectable = world.read_model(inst); + inspectable.inst = inst; + inspectable.is_inspectable = true; + inspectable.is_visible = true; + inspectable + .action_map = + array![ + ActionMapInspectable { + action: "look", + inst: 0, + action_fn: InspectableActions::ReadRandomDescription, + entrypoint: 0, + }, + ActionMapInspectable { + action: "stare", + inst: 0, + action_fn: InspectableActions::ReadFirstDescription, + entrypoint: 1, + }, + ActionMapInspectable { + action: "read", + inst: 0, + action_fn: InspectableActions::ReadSpecificDescription, + entrypoint: 2, + }, + ]; + inspectable.already_shown = false; + inspectable.new_entry = ""; + inspectable.store(world); + // Return the component + inspectable + } + + fn get_component(world: WorldStorage, inst: felt252) -> Option { + let inspectable: Inspectable = world.read_model(inst); + if (!inspectable.has_component(world, inst)) { + return Option::None; + } + let inspectable: Inspectable = world.read_model(inst); + Option::Some(inspectable) + } + + fn can_use_command( + self: @Inspectable, world: WorldStorage, player: @Player, command: @Command, + ) -> bool { + get_action_token(self, world, command).is_some() + } + + fn execute_command( + mut self: Inspectable, mut world: WorldStorage, player: @Player, command: @Command, + ) -> Result<(), Error> { + println!("Inspectable execute_command"); + let (action, _token) = get_action_token(@self, world, command).unwrap(); + match action.action_fn { + InspectableActions::SetVisible => { + self.is_visible = !self.is_visible; + world.write_model(@self); + return Result::Ok(()); + }, + InspectableActions::ReadRandomDescription => { + player.say(world, self.get_random_description(world)); + return Result::Ok(()); + }, + InspectableActions::ReadFirstDescription => { + player.say(world, self.get_first_description(world)); + return Result::Ok(()); + }, + InspectableActions::ReadSpecificDescription => { + // Get idx from the action map entrypoint + let idx: u32 = action.entrypoint.try_into().unwrap(); + // Say the description + player.say(world, InspectableImpl::get_specific_description(@self, idx, world)); + return Result::Ok(()); + }, + } + Result::Err(Error::ActionFailed) + } + + fn store(self: @Inspectable, mut world: WorldStorage) { + world.write_model(self); + } +} + +// @dev: wip how to access tokens +fn get_action_token( + self: @Inspectable, world: WorldStorage, command: @Command, +) -> Option<(ActionMapInspectable, Token)> { + let mut action_token: Option<(ActionMapInspectable, Token)> = Option::None; + for token in command.tokens.clone() { + for action in self.action_map.clone() { + if (token.text == action.action) { + action_token = Option::Some((action, token)); + break; + } + } + }; + action_token +} + + +#[cfg(test)] +mod tests { + use starknet::ContractAddress; + use dojo::{world::WorldStorage, model::ModelStorage}; + use super::*; + use lore::tests::helpers; + use lore::{ + models::index::{Inspectable, DescriptionText}, + new_components::inspectable_trait::InspectableImpl, + }; + + fn Inspectable_create_prefab() -> ( + Inspectable, WorldStorage, ContractAddress, ContractAddress, + ) { + let (mut world, _, _, player_1, player_2) = helpers::setup_core(); + + let descr1 = DescriptionText { inst: 42, key: 0, text: "hello" }; + let descr2 = DescriptionText { inst: 42, key: 1, text: "world" }; + let descr3 = DescriptionText { inst: 42, key: 2, text: "how big is a rock" }; + let descr4 = DescriptionText { inst: 42, key: 3, text: "what's up with the rock" }; + let descr5 = DescriptionText { inst: 42, key: 4, text: "let's talk about the rock" }; + let descr6 = DescriptionText { inst: 42, key: 5, text: "the rock is from the moon" }; + world.write_model(@descr1); + world.write_model(@descr2); + world.write_model(@descr3); + world.write_model(@descr4); + world.write_model(@descr5); + world.write_model(@descr6); + let prefab = Inspectable { + inst: 42, + is_inspectable: true, + is_visible: true, + description: array![0, 1, 2, 3, 4, 5], + action_map: array![ + ActionMapInspectable { + action: "show", + inst: 0, + action_fn: InspectableActions::SetVisible, + entrypoint: 0, + }, + ActionMapInspectable { + action: "look", + inst: 0, + action_fn: InspectableActions::ReadRandomDescription, + entrypoint: 1, + }, + ActionMapInspectable { + action: "read", + inst: 0, + action_fn: InspectableActions::ReadSpecificDescription, + entrypoint: 5, + }, + ], + already_shown: false, + new_entry: "", + }; + world.write_model(@prefab); + (prefab, world, player_1, player_2) + } + + #[test] + fn Inspectable_test_create_inspectable() { + let (prefab, world, _, _) = Inspectable_create_prefab(); + let read_inspectable: Inspectable = Component::get_component(world, prefab.inst).unwrap(); + // println!("read_inspectable: {:?}", read_inspectable); + assert(read_inspectable.is_inspectable, 'inspectable is inspectable'); + let mut res = array![]; + for _ in 0..10_u8 { + res.append(read_inspectable.clone().get_random_description(world)); + }; + // println!("inspectable: {:?}", res); + } + + #[test] + fn Inspectable_test_get_component() { + let (prefab, world, _, _) = Inspectable_create_prefab(); + let i: Inspectable = Component::get_component(world, prefab.inst).unwrap(); + assert(i.is_inspectable, 'inspectable is inspectable'); + } + + #[test] + fn Inspectable_test_read_specific_description() { + let (prefab, world, _, _) = Inspectable_create_prefab(); + let i: Inspectable = Component::get_component(world, prefab.inst).unwrap(); + let idx: u32 = 5; + let res = InspectableImpl::get_specific_description(@i, idx, world); + assert(res == "the rock is from the moon", 'description should be the moon'); + } +} diff --git a/packages/contracts/src/models/inventoryItem.cairo b/packages/contracts/src/models/inventoryItem.cairo new file mode 100644 index 00000000..f7d9e9cf --- /dev/null +++ b/packages/contracts/src/models/inventoryItem.cairo @@ -0,0 +1,302 @@ +use dojo::{world::{WorldStorage}, model::ModelStorage}; +use lore::{ + models::{ + index::{Entity, InventoryItem, Container, Player, Action}, components::Component, + area::AreaComponent, container::ContainerComponent, inspectable::InspectableComponent, + }, + new_components::{ + entity_trait::EntityImpl, inventoryItem_trait::InventoryItemImpl, + container_trait::ContainerImpl, player_trait::PlayerImpl, action_trait::ActionImpl, + }, + types::{ + command_type::{Command, Token}, + component_type::{InventoryItemActions, ActionMapInventoryItem}, action_type::TriggerContext, + }, + constants::errors::Error, lib::{a_lexer::CommandImpl, utils::ByteArrayTraitExt}, +}; + +pub impl InventoryItemComponent of Component { + type ComponentType = InventoryItem; + + fn inst(self: @InventoryItem) -> @felt252 { + self.inst + } + + fn entity(self: @InventoryItem, world: @WorldStorage) -> Entity { + EntityImpl::get_entity(world, self.inst).unwrap() + } + + fn has_component(self: @InventoryItem, world: WorldStorage, inst: felt252) -> bool { + let inventory_item: InventoryItem = world.read_model(inst); + inventory_item.is_inventory_item + } + + fn add_component(mut world: WorldStorage, inst: felt252) -> InventoryItem { + let mut inventory_item: InventoryItem = world.read_model(inst); + inventory_item.inst = inst; + inventory_item.is_inventory_item = true; + inventory_item + .action_map = + array![ + ActionMapInventoryItem { + action: "pickup", inst: 0, action_fn: InventoryItemActions::PickupItem, + }, + ActionMapInventoryItem { + action: "drop", inst: 0, action_fn: InventoryItemActions::DropItem, + }, + ActionMapInventoryItem { + action: "put", inst: 0, action_fn: InventoryItemActions::PutItem, + }, + ActionMapInventoryItem { + action: "take", inst: 0, action_fn: InventoryItemActions::TakeOutItem, + }, + ActionMapInventoryItem { + action: "use", inst: 0, action_fn: InventoryItemActions::UseItem, + }, + ]; + inventory_item.already_used = false; + inventory_item.store(world); + // Return the component + inventory_item + } + + fn get_component(world: WorldStorage, inst: felt252) -> Option { + let inventory_item: InventoryItem = world.read_model(inst); + if (!inventory_item.has_component(world, inst)) { + return Option::None; + } + let inventory_item: InventoryItem = world.read_model(inst); + Option::Some(inventory_item) + } + + fn can_use_command( + self: @InventoryItem, world: WorldStorage, player: @Player, command: @Command, + ) -> bool { + get_action_token(self, world, command).is_some() + } + + fn execute_command( + mut self: InventoryItem, mut world: WorldStorage, player: @Player, command: @Command, + ) -> Result<(), Error> { + println!("InventoryItem execute_command"); + let (action, _token) = get_action_token(@self, world, command).unwrap(); + let nouns = command.get_nouns(); + match action.action_fn { + InventoryItemActions::UseItem => { + player.say(world, format!("You are trying to use: {}", nouns[0].text)); + let mut resultUse: Result<(), Error> = Result::Ok(()); + // HERE SHOULD GO THE LOGIC FOR HANDLING THE COMMAND + // LIKE USE ITEM + // Ex: "use the key on the door" + // V: Use, N1: key, N2: door (target) + // Get target entity to get the actions and execute it + if *player.use_debug { + player.say(world, format!("Your target is: {}", nouns[1].text)); + } + + let target_entity = EntityImpl::get_entity(@world, nouns[1].target); + if target_entity.is_none() { + return Result::Err(Error::NoTargetEntity); + } + let target_entity = target_entity.unwrap(); + let target_actions = target_entity.actions_keys; + if target_actions.len() == 0 { + // No actions found, just return + return Result::Ok(()); + } + let mut actions: Array = ArrayTrait::new(); + // For each action, execute it + for key in target_actions { + let mut action: Action = world.read_model((target_entity.inst, key)); + actions.append(action); + }; + if actions.len() == 0 { + // No actions found, just return + return Result::Ok(()); + } + // execute actions + for action in actions { + // context is not being used inside evaluations or processing. + let context = TriggerContext { + doer: *player.inst, + target1: target_entity.inst, + target2: 0, + inventory_object: self.inst, + }; + player + .say( + world, + format!( + "Using {} trigger's something at {}", nouns[0].text, nouns[1].text, + ), + ); + if *player.use_debug { + player + .say( + world, + format!( + "Using: {:?} trigger's the action: {:?} at: {:?} as the target", + nouns[0].text, + action, + nouns[1].text, + ), + ); + } + let (trig_res, cond_res, eff_res) = ActionImpl::process_action( + action, world, @context, + ); + if *player.use_debug { + player.say(world, format!("Trigger result: {:?}", trig_res)); + player.say(world, format!("Condition result: {:?}", cond_res)); + player.say(world, format!("Effect result: {:?}", eff_res)); + } + // If all are ok, set used to true + if trig_res.is_ok() && cond_res && eff_res.is_ok() { + self.already_used = true; + world.write_model(@self); + resultUse = Result::Ok(()); + break; + } + }; + return resultUse; + }, + InventoryItemActions::PickupItem => { + // This is for the player's personal inventory container + // Ex: "pickup the sword" + let personal_container = player.get_personal_container(@world); + if personal_container.is_none() { + return Result::Err(Error::ActionFailed); + } + let container_component: Container = personal_container.unwrap(); + container_component.put_item_in(world, self.clone()); + + return Result::Ok(()); + }, + InventoryItemActions::DropItem => { + // This is for taking an item from the player's personal inventory + // Ex:: "drop the sword" + let personal_container = player.get_personal_container(@world); + if personal_container.is_none() { + return Result::Err(Error::ActionFailed); + } + let container_component: Container = personal_container.unwrap(); + container_component.put_item_out(world, self.clone(), player); + return Result::Ok(()); + }, + InventoryItemActions::PutItem => { + // This is for a specific container + // Ex: "put the sword in the bag" + // Get the player's container + let player_container = get_player_container(@world, player, nouns.clone()); + if player_container.is_none() { + // if it is not in the player, it means it is in an entity container + // that is on the room Ex: "put the sword in the box" + let entity_container = get_entity_container(@world, player, nouns); + if entity_container.is_none() { + return Result::Err(Error::ActionFailed); + } + let container_component: Container = entity_container.unwrap(); + container_component.put_item_in(world, self.clone()); + return Result::Ok(()); + } + let container_component: Container = player_container.unwrap(); + container_component.put_item_in(world, self.clone()); + return Result::Ok(()); + }, + InventoryItemActions::TakeOutItem => { + // This is for taking an item from a specific container + // Ex: "take out the sword from the bag" + // Get the player's container + let player_container = get_player_container(@world, player, nouns.clone()); + if player_container.is_none() { + // if it is not in the player, it means it is in an entity container + // that is on the room Ex: "take out the sword from the box" + let entity_container = get_entity_container(@world, player, nouns); + if entity_container.is_none() { + return Result::Err(Error::ActionFailed); + } + let container_component: Container = entity_container.unwrap(); + container_component.put_item_out(world, self.clone(), player); + return Result::Ok(()); + } + return Result::Ok(()); + }, + } + Result::Err(Error::ActionFailed) + } + + fn store(self: @InventoryItem, mut world: WorldStorage) { + world.write_model(self); + } +} + +// @dev: wip how to access tokens +fn get_action_token( + self: @InventoryItem, world: WorldStorage, command: @Command, +) -> Option<(ActionMapInventoryItem, Token)> { + let mut action_token: Option<(ActionMapInventoryItem, Token)> = Option::None; + for token in command.tokens.clone() { + for action in self.action_map.clone() { + if (token.text == action.action) { + action_token = Option::Some((action, token)); + break; + } + } + }; + action_token +} + +// @dev: wip get player's container +// This can be the an entity container attached to the player +// Ex: a bag in the player's personalinventory +fn get_player_container( + world: @WorldStorage, player: @Player, nouns: Array, +) -> Option { + let player_entity: Entity = EntityImpl::get_entity(world, player.inst).unwrap(); + let player_children = player_entity.get_children(world); + let mut container: Option = Option::None; + let mut player_container: Option = Option::None; + // match the noun wth the child name or alt_name + for child in player_children { + if (@child.name == nouns[1].text || child.clone().name_is(nouns[1].text.clone())) { + container = Option::Some(child); + break; + } + }; + if container.is_none() { + return Option::None; + } + // get container component + player_container = ContainerComponent::get_component(*world, container.unwrap().inst); + return player_container; +} + +// @dev: wip get entity's container +// This can be the an entity container attached to the room +// Ex: a chest in the room +fn get_entity_container( + world: @WorldStorage, player: @Player, nouns: Array, +) -> Option { + // get room + let room = player.get_room(world); + if room.is_none() { + return Option::None; + } + let room_entity: Entity = EntityImpl::get_entity(world, @room.unwrap().inst).unwrap(); + let room_children = room_entity.get_children(world); + let mut container: Option = Option::None; + let mut room_container: Option = Option::None; + // match the noun wth the child name or alt_name + for child in room_children { + if (@child.name == nouns[1].text || child.clone().name_is(nouns[1].text.clone())) { + container = Option::Some(child); + break; + } + }; + if container.is_none() { + return Option::None; + } + // get container component + room_container = ContainerComponent::get_component(*world, container.unwrap().inst); + return room_container; +} diff --git a/packages/contracts/src/models/player.cairo b/packages/contracts/src/models/player.cairo new file mode 100644 index 00000000..8182318d --- /dev/null +++ b/packages/contracts/src/models/player.cairo @@ -0,0 +1,109 @@ +use dojo::{world::WorldStorage, model::ModelStorage}; +use starknet::ContractAddress; +use lore::{ + models::{index::{Player}, components::Component}, + new_components::{entity_trait::EntityImpl, inspectable_trait::InspectableImpl}, + types::{command_type::Command}, constants::errors::Error, +}; + +pub impl PlayerComponent of Component { + type ComponentType = Player; + + fn inst(self: @Player) -> @felt252 { + self.inst + } + + fn has_component(self: @Player, world: WorldStorage, inst: felt252) -> bool { + let player: Player = world.read_model(inst); + player.is_player + } + + fn add_component(mut world: WorldStorage, inst: felt252) -> Player { + let mut player: Player = world.read_model(inst); + player.inst = inst; + player.is_player = true; + player.store(world); + // Return the component + player + } + + fn get_component(world: WorldStorage, inst: felt252) -> Option { + let player: Player = world.read_model(inst); + if (!player.has_component(world, inst)) { + return Option::None; + } + let player: Player = world.read_model(inst); + Option::Some(player) + } + + fn can_use_command( + self: @Player, world: WorldStorage, player: @Player, command: @Command, + ) -> bool { + true + } + + fn execute_command( + self: Player, world: WorldStorage, player: @Player, command: @Command, + ) -> Result<(), Error> { + println!("Player execute_command"); + Result::Err(Error::Unimplemented) + } + + fn store(self: @Player, mut world: WorldStorage) { + world.write_model(self); + } +} + + +pub fn create_player(mut world: WorldStorage, address: ContractAddress) -> Player { + EntityImpl::create_player_entity(world, address) +} + +pub fn get_player(world: WorldStorage, address: ContractAddress) -> Option { + let inst: felt252 = address.into(); + let player: Player = world.read_model(inst); + if (!player.is_player) { + return Option::None; + } + Option::Some(player) +} + +pub fn caller_as_player(world: WorldStorage, address: ContractAddress) -> Player { + match get_player(world, address) { + Option::Some(player) => player, + Option::None => create_player(world, address), + } +} + +#[cfg(test)] +mod tests { + use dojo::{model::ModelStorage}; + use lore::{ + models::{index::{Player, PlayerStory, StoryLine}, player::caller_as_player}, + new_components::player_trait::{PlayerImpl}, tests::helpers, + }; + + #[test] + fn Player_test_create_player() { + let (world, _, _, player_1, _) = helpers::setup_core(); + let player: Player = caller_as_player(world, player_1); + assert(player.is_player, 'player is player'); + } + + #[test] + fn Player_test_story_time() { + let (world, _, _, player_1, _) = helpers::setup_core(); + let player: Player = caller_as_player(world, player_1); + assert(player.is_player, 'player is player'); + + player.say(world, "hello"); + let story: PlayerStory = world.read_model(player.inst); + // ("story: {:?}", story); + assert(story.story.len() == 2, 'story has two entries'); // first entry is intro text + let test_text: ByteArray = "hello"; + + let story_key: u64 = *story.story.at(story.story.len() - 1); + let story_line: StoryLine = world.read_model((story.inst, story_key)); + assert(story_line.line == test_text, 'story has "hello"'); + } +} diff --git a/packages/contracts/src/new_components/action_trait.cairo b/packages/contracts/src/new_components/action_trait.cairo new file mode 100644 index 00000000..a066326d --- /dev/null +++ b/packages/contracts/src/new_components/action_trait.cairo @@ -0,0 +1,788 @@ +use dojo::{world::WorldStorage, model::ModelStorage}; + +use lore::{ + models::{ + index::{Entity, Action, Player, Trigger, Condition, Effect}, area::AreaComponent, + exit::ExitComponent, inspectable::InspectableComponent, + inventoryItem::InventoryItemComponent, container::ContainerComponent, + player::PlayerComponent, + }, + new_components::{ + entity_trait::EntityImpl, trigger_trait::TriggerImpl, condition_trait::ConditionImpl, + effect_trait::EffectImpl, player_trait::PlayerImpl, + }, + types::action_type::{TriggerContext}, + lib::{utils::ByteArrayTraitExt, variable_property::{VariablePropertyImp}}, + constants::{errors::Error}, +}; + + +#[generate_trait] +pub impl ActionImpl of ActionTrait { + fn register_action(mut world: WorldStorage, action: Action) -> Result<(), Error> { + // 0. Check if action is already in the entity array + let maybe_entity = EntityImpl::get_entity(@world, @action.inst); + match maybe_entity { + Option::Some(mut entity) => { + // Check if action is already registered + let mut found = false; + for pos_action in entity.actions_keys.clone() { + if (action.key == pos_action) { + found = true; + break; + } + }; + if found { + // If found just update the action + world.write_model(@action); + return Result::Ok(()); + } + }, + Option::None => {}, + } + // 1. Register action key in the entity + let mut entity: Entity = EntityImpl::get_entity(@world, @action.inst).unwrap(); + entity.actions_keys.append(action.key); + // 2. Update the entity + world.write_model(@entity); + // 3. Write the action + world.write_model(@action); + Result::Ok(()) + } + + fn unregister_action(mut world: WorldStorage, action: Action) -> Result<(), Error> { + // 1. Remove the action + world.erase_model(@action); + Result::Ok(()) + } + + fn process_action( + mut action: Action, mut world: WorldStorage, context: @TriggerContext, + ) -> (Result<(), Error>, bool, Result<(), Error>) { + let player_inst: felt252 = *context.doer; + let player: Player = world.read_model(player_inst); + if action.executed { + // Action has already been executed, don't do anything + // return condition as false. + if player.use_debug { + player.say(world, format!("Action has already been executed")); + } + return (Result::Ok(()), false, Result::Ok(())); + } + // Trigger + let mut result_t: Result<(), Error> = Result::Ok(()); + //Conditions. We want to have the bool value as true in case there is no condition + let mut result: bool = true; + //Effects + let mut result_e: Result<(), Error> = Result::Ok(()); + + // First check if the trigger/s are valid + for trigger_key in action.trigger.clone() { + let trigger: Trigger = world.read_model(trigger_key); + let result_opt = TriggerImpl::evaluate_trigger(world, trigger.clone()); + if player.use_debug { + player + .say(world, format!("Result for trigger: {:?}, is: {:?}", trigger, result_opt)); + } + if result_opt.is_err() { + result_t = result_opt; + break; + } + }; + + // Then evaluate all conditions + for condition_key in action.conditions.clone() { + let condition: Condition = world.read_model(condition_key); + result = condition.evaluate_condition(@world, context.clone()); + if player.use_debug { + player + .say(world, format!("Result for condition: {:?}, is: {:?}", condition, result)); + } + if !result { + break; // If a single condition fails, break out of the loop + } + }; + + // Finally execute all effects if conditions are met + if result { + for effect_key in action.effects.clone() { + let effect: Effect = world.read_model(effect_key); + let result_pos = effect.apply_effect(world, *context); + if player.use_debug { + player + .say( + world, format!("Result for effect: {:?}, is: {:?}", effect, result_pos), + ); + } + if result_pos.is_err() { + result_e = result_pos; + } + }; + } else { + result_e = Result::Err(Error::ConditionFailed); + // result_e = Result::Ok(()); // -> THIS IS FOR TESTING ONLY + // println!("result_effect: {:?}, but effect is not applied as condition failed", + // result_e); + } + // If all conditions are met, mark action as executed + if (result_t.is_ok() && result && result_e.is_ok()) { + action.executed = true; + world.write_model(@action); + for response in action.success_response.clone() { + player.say(world, response); + } + } else { + for response in action.failing_response.clone() { + player.say(world, response); + } + } + (result_t, result, result_e) + } + + fn enable_action(mut self: Action, mut world: WorldStorage) { + self.is_enabled = true; + world.write_model(@self); + } + + fn disable_action(mut self: Action, mut world: WorldStorage) { + self.is_enabled = false; + world.write_model(@self); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use dojo::{model::ModelStorage, world::WorldStorage}; + use lore::tests::helpers; + use lore::{ + models::{ + index::{ + Entity, Area, Exit, Inspectable, DescriptionText, InventoryItem, Container, Trigger, + Condition, Effect, Action, + }, + area::AreaComponent, exit::ExitComponent, inspectable::InspectableComponent, + inventoryItem::InventoryItemComponent, container::ContainerComponent, + player::{PlayerComponent, caller_as_player}, components::Component, + }, + new_components::{ + entity_trait::EntityImpl, player_trait::{PlayerImpl}, trigger_trait::TriggerImpl, + effect_trait::EffectImpl, action_trait::ActionImpl, + }, + types::{ + component_type::{ + ComponentType, ExitActions, ActionMapExit, InspectableActions, ActionMapInspectable, + InventoryItemActions, ActionMapInventoryItem, + }, + action_type::{TriggerType, TriggerContext, Operator}, direction_type::Direction, + }, + lib::{variable_property::VariablePropertyImp, utils::ByteArrayTraitExt}, + }; + + fn create_rooms(mut world: WorldStorage) -> (Entity, Entity) { + // create room entity 1 + let mut room_entity_1 = EntityImpl::create_entity(world); + world.write_model(@room_entity_1); + // create room entity 2 + let mut room_entity_2 = EntityImpl::create_entity(world); + world.write_model(@room_entity_2); + + // ROOM 1 // + // add area component to room entity 1 + let mut area_component: Area = Component::add_component(world, room_entity_1.inst); + area_component.is_area = true; + area_component.store(world); + // add exit component to room entity + let mut exit_component_1: Exit = Component::add_component(world, room_entity_1.inst); + exit_component_1.is_enterable = true; + exit_component_1.leads_to = room_entity_2.inst; + exit_component_1.direction_type = Direction::North; + exit_component_1.store(world); + + // ROOM 2 // + // add area component to room entity 2 + let mut area_component_2: Area = Component::add_component(world, room_entity_2.inst); + area_component_2.is_area = true; + area_component_2.store(world); + + // return room entities + (room_entity_1, room_entity_2) + } + + fn create_door(mut world: WorldStorage, leads_to: felt252, direction: Direction) -> Entity { + // create door entity + let mut door = EntityImpl::create_entity(world); + door.name = "door"; + world.write_model(@door); + // add inspectable component to door + let mut inspectable: Inspectable = Component::add_component(world, door.inst); + let desc1: DescriptionText = DescriptionText { inst: door.inst, key: 0, text: "A door" }; + world.write_model(@desc1); + inspectable.is_inspectable = true; + inspectable.is_visible = true; + inspectable.description = array![0]; + inspectable + .action_map = + array![ + ActionMapInspectable { + action: "show", + inst: 0, + action_fn: InspectableActions::SetVisible, + entrypoint: 0, + }, + ActionMapInspectable { + action: "look", + inst: 0, + action_fn: InspectableActions::ReadRandomDescription, + entrypoint: 1, + }, + ]; + inspectable.store(world); + // add exit component to door + let mut exit_component: Exit = Component::add_component(world, door.inst); + exit_component.is_exit = true; + exit_component.is_enterable = false; + exit_component.leads_to = leads_to; + exit_component.direction_type = direction; + exit_component + .action_map = + array![ + ActionMapExit { action: "go", inst: 0, action_fn: ExitActions::UseExit }, + ActionMapExit { action: "enter", inst: 0, action_fn: ExitActions::UseExit }, + ActionMapExit { action: "use", inst: 0, action_fn: ExitActions::UseExit }, + ]; + exit_component.store(world); + + // return door entity + door + } + + fn create_item(mut world: WorldStorage, owner_id: felt252) -> Entity { + // create item entity + let mut item = EntityImpl::create_entity(world); + item.name = "ball"; + item.alt_names = array!["ball"]; + world.write_model(@item); + // add inspectable component to item + let mut inspectable: Inspectable = Component::add_component(world, item.inst); + let desc1: DescriptionText = DescriptionText { inst: item.inst, key: 0, text: "A ball" }; + world.write_model(@desc1); + inspectable.is_inspectable = true; + inspectable.is_visible = true; + inspectable.description = array![0]; + inspectable + .action_map = + array![ + ActionMapInspectable { + action: "show", + inst: 0, + action_fn: InspectableActions::SetVisible, + entrypoint: 0, + }, + ActionMapInspectable { + action: "look", + inst: 0, + action_fn: InspectableActions::ReadRandomDescription, + entrypoint: 1, + }, + ]; + inspectable.store(world); + // add inventory item component to item + let mut inventory_item: InventoryItem = Component::add_component(world, item.inst); + inventory_item.owner_id = owner_id; + inventory_item.is_inventory_item = true; + inventory_item.can_be_picked_up = true; + inventory_item.can_go_in_container = true; + inventory_item + .action_map = + array![ + ActionMapInventoryItem { + action: "pickup", inst: 0, action_fn: InventoryItemActions::PickupItem, + }, + ActionMapInventoryItem { + action: "drop", inst: 0, action_fn: InventoryItemActions::DropItem, + }, + ActionMapInventoryItem { + action: "put", inst: 0, action_fn: InventoryItemActions::PutItem, + }, + ActionMapInventoryItem { + action: "take", inst: 0, action_fn: InventoryItemActions::TakeOutItem, + }, + ActionMapInventoryItem { + action: "use", inst: 0, action_fn: InventoryItemActions::UseItem, + }, + ]; + inventory_item.already_used = false; + inventory_item.multiple_use = true; + inventory_item.store(world); + + // return item entity + item + } + + fn create_test_trigger( + inst: felt252, key: felt252, nameT: ByteArray, trigger_type: TriggerType, + ) -> Trigger { + Trigger { + inst, + key, + name: nameT, + trigger_type, + is_enabled: true, + is_once: false, + was_triggered: false, + } + } + + fn create_test_condition( + inst: felt252, + key: felt252, + name: ByteArray, + target: felt252, + component: ComponentType, + property: ByteArray, + operator: Operator, + value: Array, + ) -> Condition { + Condition { inst, key, name, target, component, property, operator, value } + } + + fn create_test_effect( + inst: felt252, + key: felt252, + name: ByteArray, + target: felt252, + component: ComponentType, + property: ByteArray, + value: Array<(ByteArray, u32)>, + ) -> Effect { + Effect { inst, key, name, target, component, property, value } + } + + fn create_test_action( + inst: felt252, + key: felt252, + name: ByteArray, + description: ByteArray, + is_enabled: bool, + trigger: Array<(felt252, felt252)>, + conditions: Array<(felt252, felt252)>, + effects: Array<(felt252, felt252)>, + tags: Array, + executed: bool, + failing_response: Array, + success_response: Array, + ) -> Action { + Action { + inst, + key, + name, + description, + is_enabled, + trigger, + conditions, + effects, + tags, + executed, + failing_response, + success_response, + } + } + + fn create_test_trigger_context( + doer: felt252, target1: felt252, target2: felt252, inventory_object: felt252, + ) -> TriggerContext { + TriggerContext { doer, target1, target2, inventory_object } + } + + fn register_variable_properties(world: WorldStorage) { + VariablePropertyImp::register_component_properties(world, ComponentType::Area); + VariablePropertyImp::register_component_properties(world, ComponentType::Exit); + VariablePropertyImp::register_component_properties(world, ComponentType::Inspectable); + VariablePropertyImp::register_component_properties(world, ComponentType::InventoryItem); + VariablePropertyImp::register_component_properties(world, ComponentType::Container); + VariablePropertyImp::register_component_properties(world, ComponentType::Player); + VariablePropertyImp::register_component_properties(world, ComponentType::Area); + VariablePropertyImp::register_component_properties(world, ComponentType::Container); + VariablePropertyImp::register_component_properties(world, ComponentType::Inspectable); + } + + #[test] + // Trigger: player1 enters room 2 + // Condition: player1 has item + // Effect: door description and exit.is_enterable changes + // However player1 does not have item + // Result: door description and exit.is_enterable should not be updated + fn test_enter_room_without_item() { + let (mut world, _, _, player_1, _) = helpers::setup_core(); + + // create rooms + let (room_1, room_2) = create_rooms(world); + + // create door entity in room 2 that leads to room 1 via south + let mut door = create_door(world, room_1.inst, Direction::South); + door.set_parent(world, @room_2); + let old_insp_door: Inspectable = world.read_model(door.inst); + let old_key: u32 = *old_insp_door.description.at(0); + let _old_txt: DescriptionText = world.read_model((door.inst, old_key)); + + // create item that is in room 1 + let mut item = create_item(world, room_1.inst); + item.set_parent(world, @room_1); + + // create player + let mut player1 = caller_as_player(world, player_1); + world.write_model(@player1); + + // Register variable properties + register_variable_properties(world); + + // TRIGGER that jumps when an action is executed + let t_key: felt252 = 1; + // create trigger for when entering room 2 + let mut trigger = create_test_trigger( + room_2.inst, t_key, "TestTrigger", TriggerType::OnEnter, + ); + let _result = TriggerImpl::register_trigger(world, trigger.clone()); + + // CONDITION that checks if player has item + let property: ByteArray = "owner_id"; + let c_key: felt252 = 1; + let c_name1: ByteArray = "Condition name1"; + let mut array: Array = ArrayTrait::new(); + array.append(player1.inst); + // create condition for when player has item + let mut condition = create_test_condition( + room_2.inst, + c_key, + c_name1, + item.inst, + ComponentType::InventoryItem, + property, + Operator::Equals, + array, + ); + world.write_model(@condition); + + // EFFECT TO APPLY WHEN PLAYER1 HAS ITEM + // New Description door -> Inspectable + // 1. New description as bytearray + let new_txt1: ByteArray = "A door that is open"; + let idx1: u32 = 0; + let new_txt2: ByteArray = "Looks that it leads somewhere"; + let idx2: u32 = 1; + // 2. Create array of the new description + let new_description: Array<(ByteArray, u32)> = array![ + (new_txt1.clone(), idx1), (new_txt2.clone(), idx2), + ]; + + // New is_enterable -> Exit + // 1. New value as bytearray + let enterable: ByteArray = "true"; + // 2. Create array of the new value + let new_enterable: Array<(ByteArray, u32)> = array![(enterable, 0)]; + + // Properties as bytearray + let property: ByteArray = "description"; + let property2: ByteArray = "is_enterable"; + + let e_key: felt252 = 1; + let e_key2: felt252 = 2; + + let name: ByteArray = "Effect name"; + let name2: ByteArray = "Effect name2"; + + // Create effects + let mut effect = create_test_effect( + door.inst, + e_key, + name, + door.inst, + ComponentType::Inspectable, + property, + new_description.clone(), + ); + let mut effect2 = create_test_effect( + door.inst, e_key2, name2, door.inst, ComponentType::Exit, property2, new_enterable, + ); + world.write_model(@effect); + world.write_model(@effect2); + + // TODO: create action + let a_key: felt252 = 90527; + let act_name: ByteArray = "TestAction"; + let act_desc: ByteArray = "TestActionDesc"; + let mut triggers: Array<(felt252, felt252)> = ArrayTrait::new(); + let mut conditions: Array<(felt252, felt252)> = ArrayTrait::new(); + let mut effects: Array<(felt252, felt252)> = ArrayTrait::new(); + triggers.append((trigger.inst, trigger.key)); + conditions.append((condition.inst, condition.key)); + effects.append((effect.inst, effect.key)); + effects.append((effect2.inst, effect2.key)); + let tags: Array = array!["TestAction"]; + let mut failing_response: Array = array![ + "Testing failure response", "Testing failure response 2", + ]; + let mut success_response: Array = array![ + "Testing success response", "Testing success response 2", + ]; + let mut action = create_test_action( + room_2.inst, + a_key, + act_name, + act_desc, + true, + triggers, + conditions, + effects, + tags, + false, + failing_response, + success_response, + ); + // Register the action + let _res = ActionImpl::register_action(world, action.clone()); + + // create trigger context + let mut context: TriggerContext = create_test_trigger_context( + player1.inst, door.inst, 0, item.inst, + ); + + // EXECUTE ACTION + // 1. move player to room 2 + player1.move_to_room(world, room_2.inst); + // 2. Execute action + let (trig_res, cond_res, eff_res) = ActionImpl::process_action(action, world, @context); + // // The one below are for testing individually + //let trig_res = TriggerImpl::evaluate_trigger(world, @trigger); + //let cond_res = condition.evaluate_condition(@world, context); + //let eff_res1 = effect.apply_effect(world, context); + //let eff_res2 = effect2.apply_effect(world, context); + + // ASSERT // + // 1. Trigger should jump + assert(trig_res.is_ok(), 'Trigger should jump'); + // 2. Condition should fail as player does not have item + assert((cond_res == false), 'Condition should fail'); + // 3. Effects should fail as condition is not met + assert(eff_res.is_err(), 'Effects should fail'); + //assert(eff_res1.is_err(), 'Effects should fail'); + //assert(eff_res2.is_err(), 'Effects2 shoul fail'); + + // 3. Effects should not be update + let upd_door: Inspectable = world.read_model(door.inst); + let upd_door_exit: Exit = world.read_model(door.inst); + let key1: u32 = *upd_door.description.at(0); + // let key2: u32 = *new_description.at(1); + let new_text1: DescriptionText = world.read_model((upd_door.inst, key1)); + // let _new_text2: DescriptionText = world.read_model((upd_door.inst, key2)); + assert_ne!(new_txt1.clone(), new_text1.text.clone(), "Description1 should not be updated"); + // This one fails as there is no index 1 in the array + //assert_ne!(upd_door.description[1].clone(), new_text2, "Description2 should not be + //updated"); + assert(upd_door_exit.is_enterable == false, 'Exit should not be updated'); + } + + #[test] + // Trigger: player1 enters room 2 + // Condition: player1 has item + // Effect: door description and exit.is_enterable changes + fn test_enter_room_with_item() { + let (mut world, _, _, player_1, _) = helpers::setup_core(); + + // create rooms + let (room_1, room_2) = create_rooms(world); + + // create door entity in room 2 that leads to room 1 via south + let mut door = create_door(world, room_1.inst, Direction::South); + door.set_parent(world, @room_2); + let _old_inspectable: Inspectable = world.read_model(door.inst); + let _old_exit: Exit = world.read_model(door.inst); + + // create item that is in room 1 + let mut item = create_item(world, room_1.inst); + item.set_parent(world, @room_1); + + // create player + let mut player1 = caller_as_player(world, player_1); + world.write_model(@player1); + let player_entity: Entity = EntityImpl::get_entity(@world, @player1.inst).unwrap(); + let mut player_container: Container = Component::add_component(world, player_entity.inst); + player_container.is_container = true; + player_container.can_be_opened = true; + player_container.can_receive_items = true; + player_container.is_open = true; + player_container.num_slots = 2; + player_container.store(world); + + // Register variable properties + register_variable_properties(world); + + // TRIGGER that jumps when an action is executed + // create trigger for when entering room 2 + let t_key: felt252 = 1; + let mut trigger = create_test_trigger( + room_2.inst, t_key, "TestTrigger", TriggerType::OnEnter, + ); + let _result = TriggerImpl::register_trigger(world, trigger.clone()); + + // CONDITION that checks if player has item + let property: ByteArray = "owner_id"; + let c_key: felt252 = 1; + let c_name1: ByteArray = "Condition name1"; + let mut array: Array = ArrayTrait::new(); + array.append(player1.inst); + // create condition for when player has item + let mut condition = create_test_condition( + room_2.inst, + c_key, + c_name1, + item.inst, + ComponentType::InventoryItem, + property, + Operator::Equals, + array, + ); + world.write_model(@condition); + + // EFFECT TO APPLY WHEN PLAYER1 HAS ITEM + // New Description door -> Inspectable + // 1. New description as bytearray + let new_txt1: ByteArray = "A door that is open"; + let idx1: u32 = 0; + let new_txt2: ByteArray = "Looks that it leads somewhere"; + let idx2: u32 = 1; + // 2. Create array of the new description + let new_description: Array<(ByteArray, u32)> = array![ + (new_txt1.clone(), idx1), (new_txt2.clone(), idx2), + ]; + + // New is_enterable -> Exit + // 1. New value as ByteArray + let enterable: ByteArray = "true"; + // 2. Create array of the new value + let new_enterable: Array<(ByteArray, u32)> = array![(enterable, 0)]; + + // Properties as bytearray + let property: ByteArray = "description"; + let property2: ByteArray = "is_enterable"; + + let e_key: felt252 = 1; + let e_key2: felt252 = 2; + let name: ByteArray = "Effect name"; + let name2: ByteArray = "Effect name2"; + + // Create effects + let mut effect = create_test_effect( + door.inst, + e_key, + name, + door.inst, + ComponentType::Inspectable, + property, + new_description.clone(), + ); + let mut effect2 = create_test_effect( + door.inst, + e_key2, + name2, + door.inst, + ComponentType::Exit, + property2, + new_enterable.clone(), + ); + world.write_model(@effect); + world.write_model(@effect2); + + // TODO: create action + let a_key: felt252 = 90527; + let act_name: ByteArray = "TestAction"; + let act_desc: ByteArray = "TestActionDesc"; + let mut triggers: Array<(felt252, felt252)> = ArrayTrait::new(); + let mut conditions: Array<(felt252, felt252)> = ArrayTrait::new(); + let mut effects: Array<(felt252, felt252)> = ArrayTrait::new(); + triggers.append((trigger.inst, trigger.key)); + conditions.append((condition.inst, condition.key)); + effects.append((effect.inst, effect.key)); + effects.append((effect2.inst, effect2.key)); + let tags: Array = array!["TestAction"]; + let mut failing_response: Array = array![ + "Testing failure response", "Testing failure response 2", + ]; + let mut success_response: Array = array![ + "Testing success response", "Testing success response 2", + ]; + let mut action = create_test_action( + room_1.inst, + a_key, + act_name, + act_desc, + true, + triggers, + conditions, + effects, + tags, + false, + failing_response, + success_response, + ); + // Register the action + let _result = ActionImpl::register_action(world, action.clone()); + + // create trigger context + let mut context: TriggerContext = create_test_trigger_context( + player1.inst, door.inst, 0, item.inst, + ); + + // EXECUTE ACTION + // 1. move player to room 1 + player1.location = room_1.inst; + player1.store(world); + player1.move_to_room(world, room_1.inst); + let player_entity: Entity = EntityImpl::get_entity(@world, @player1.inst).unwrap(); + + // 2. Pickup item + item.set_parent(world, @player_entity); + let player_container: Container = world.read_model(player1.inst); + let mut itemInv: InventoryItem = world.read_model(item.inst); + itemInv.owner_id = player_container.inst; + itemInv.store(world); + // 3. Check if item is owned by player1 + assert(itemInv.owner_id == player_container.inst, 'Item should be owned by player1'); + // 4. Move player to room 2 + player1.move_to_room(world, room_2.inst); + // 5. Execute action + let (trig_res, cond_res, eff_res) = ActionImpl::process_action(action, world, @context); + // // The one below are for testing individually + //let trig_res = TriggerImpl::evaluate_trigger(world, @trigger); + //let cond_res = condition.evaluate_condition(@world, context); + //let eff_res1 = effect.apply_effect(world, context); + //let eff_res2 = effect2.apply_effect(world, context); + + // ASSERT // + // 1. Trigger should jump + assert(trig_res.is_ok(), 'Trigger should jump'); + // 2. Condition should fail as player does not have item + assert((cond_res == true), 'Condition should be true'); + // 3. Effects should fail as condition is not met + assert(eff_res.is_ok(), 'Effects should pass'); + //assert(eff_res1.is_err(), 'Effects should fail'); + //assert(eff_res2.is_err(), 'Effects2 shoul fail'); + + // 3. Effects should be update + let upd_door: Inspectable = world.read_model(door.inst); + let upd_door_exit: Exit = world.read_model(door.inst); + let key1: u32 = *upd_door.description.at(0); + let key2: u32 = *upd_door.description.at(1); + let new_text1: DescriptionText = world.read_model((upd_door.inst, key1)); + let new_text2: DescriptionText = world.read_model((upd_door.inst, key2)); + assert_eq!(new_txt1, new_text1.text.clone(), "Description1 should be updated"); + assert_eq!(new_txt2, new_text2.text.clone(), "Description2 should be updated"); + assert(upd_door_exit.is_enterable == true, 'Exit should be updated'); + // println!("Old description: {:?}", old_inspectable.description); + // println!("New description: {:?}", array![new_text1, new_text2]); + // println!("Old is_enterable: {:?}", old_exit.is_enterable); + // println!("New is_enterable: {:?}", new_enterable); + } +} + diff --git a/packages/contracts/src/new_components/condition_trait.cairo b/packages/contracts/src/new_components/condition_trait.cairo new file mode 100644 index 00000000..d5fba4b1 --- /dev/null +++ b/packages/contracts/src/new_components/condition_trait.cairo @@ -0,0 +1,368 @@ +use dojo::{world::WorldStorage}; + +use lore::{ + models::{ + index::Condition, area::AreaComponent, exit::ExitComponent, + inspectable::InspectableComponent, inventoryItem::InventoryItemComponent, + container::ContainerComponent, player::PlayerComponent, + }, + new_components::{entity_trait::EntityImpl, trigger_trait::TriggerImpl}, + types::{component_type::ComponentType, action_type::{TriggerContext, Operator}}, + lib::{utils::ByteArrayTraitExt, variable_property::{VariablePropertyTrait}}, +}; + +#[generate_trait] +pub impl ConditionImpl of ConditionTrait { + fn evaluate_condition(self: @Condition, world: @WorldStorage, context: TriggerContext) -> bool { + let target = *self.target; + let mut component_value: Option> = Option::None; + let mut eval_result: bool = false; + match self.component { + ComponentType::Area => { + let container_opt = AreaComponent::get_component(*world, target); + if container_opt.is_none() { + return false; + } + let area = OptionTrait::unwrap(container_opt); + let (b_component_value, _) = VariablePropertyTrait::get_property( + world, @area.inst, self.property, *self.component, + ); + component_value = b_component_value; + }, + ComponentType::Exit => { + let container_opt = ExitComponent::get_component(*world, target); + if container_opt.is_none() { + return false; + } + let exit = OptionTrait::unwrap(container_opt); + let (b_component_value, _) = VariablePropertyTrait::get_property( + world, @exit.inst, self.property, *self.component, + ); + component_value = b_component_value; + }, + ComponentType::Inspectable => { + let inspectable_opt = InspectableComponent::get_component(*world, target); + if inspectable_opt.is_none() { + return false; + } + let inspectable = OptionTrait::unwrap(inspectable_opt); + let (b_component_value, _) = VariablePropertyTrait::get_property( + world, @inspectable.inst, self.property, *self.component, + ); + component_value = b_component_value; + }, + ComponentType::InventoryItem => { + let inventoryItem_opt = InventoryItemComponent::get_component(*world, target); + if inventoryItem_opt.is_none() { + return false; + } + let inventoryItem = OptionTrait::unwrap(inventoryItem_opt); + let (b_component_value, _) = VariablePropertyTrait::get_property( + world, @inventoryItem.inst, self.property, *self.component, + ); + component_value = b_component_value; + }, + ComponentType::Container => { + let container_opt = ContainerComponent::get_component(*world, target); + if container_opt.is_none() { + return false; + } + let container = OptionTrait::unwrap(container_opt); + let (b_component_value, _) = VariablePropertyTrait::get_property( + world, @container.inst, self.property, *self.component, + ); + component_value = b_component_value; + }, + ComponentType::Player => { + let container_opt = PlayerComponent::get_component(*world, target); + if container_opt.is_none() { + return false; + } + let player = OptionTrait::unwrap(container_opt); + let (b_component_value, _) = VariablePropertyTrait::get_property( + world, @player.inst, self.property, *self.component, + ); + component_value = b_component_value; + }, + _ => { + // Return false if no match + return false; + }, + }; + + // Check if the component value is none + if component_value.is_none() { + // println!("component value is none"); + return false; + } + + // Compare values using the operator + eval_result = self.compare(component_value.unwrap()); + return eval_result; + } + + fn compare(self: @Condition, component_value: Array) -> bool { + // Get the condition value, which is also an array of felt252 + let condition_value: Array = self.value.clone(); + // Bool variable + let mut result: bool = true; + + match self.operator { + Operator::Equals => { + // First, check if the lengths of the two arrays are equal. + // If not, they can't be equal, so return false immediately. + if component_value.len() != condition_value.len() { + result = false; + } + + // Iterate through each index and compare the corresponding elements. + // If any pair of elements differ, return false. + for i in 0..component_value.len() { + if component_value.at(i) != condition_value.at(i) { + result = false; + }; + }; + + // If we get here, all elements matched, so return true. + return result; + }, + Operator::NotEquals => { + // If the lengths are different, arrays are not equal, + // so return true for NotEquals. + if component_value.len() != condition_value.len() { + result = false; + } + + // Iterate through each element and check if any pair differs. + // If so, return true, indicating arrays are not equal. + for i in 0..component_value.len() { + if component_value.at(i) == condition_value.at(i) { + result = false; + }; + }; + + // If all elements matched and lengths are equal, arrays are equal, + // so return false for NotEquals. + return result; + }, + _ => { // Do nothing + }, + } + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + use dojo::{model::ModelStorage}; + use lore::tests::helpers; + use lore::{ + models::{ + index::{Inspectable, DescriptionText, Condition}, components::Component, + inspectable::InspectableComponent, + }, + new_components::entity_trait::EntityImpl, + types::{component_type::{ComponentType, ActionMapInspectable, InspectableActions}}, + lib::{variable_property::VariablePropertyImp}, + }; + + fn create_test_condition( + inst: felt252, + key: felt252, + name: ByteArray, + target: felt252, + component: ComponentType, + property: ByteArray, + operator: Operator, + value: Array, + ) -> Condition { + Condition { inst, key, name, target, component, property, operator, value } + } + + #[test] + fn Condition_test_evaluate_condition() { + let (mut world, _, _, _, _) = helpers::setup_core(); + // Create entity and attach InspectableComponent + let mut door = EntityImpl::create_entity(world); + door.name = "door"; + world.write_model(@door); + + let new_entry: ByteArray = "A door"; + let mut inspectable: Inspectable = Component::add_component(world, door.inst); + let desc1: DescriptionText = DescriptionText { + inst: door.inst, key: 0, text: new_entry.clone(), + }; + world.write_model(@desc1); + inspectable.is_inspectable = true; + inspectable.is_visible = true; + inspectable.already_shown = false; + inspectable.description = array![0]; + inspectable.new_entry = new_entry; + inspectable + .action_map = + array![ + ActionMapInspectable { + action: "show", + inst: 0, + action_fn: InspectableActions::SetVisible, + entrypoint: 0, + }, + ActionMapInspectable { + action: "look", + inst: 0, + action_fn: InspectableActions::ReadRandomDescription, + entrypoint: 1, + }, + ]; + inspectable.store(world); + + // Register component variable properties + VariablePropertyImp::register_component_properties(world, ComponentType::Inspectable); + + // Test: is_inspectable == true (should pass) + let key2: felt252 = 2; + let name2: ByteArray = "Condition name2"; + let mut array_true = ArrayTrait::new(); + array_true.append(1); + let mut condition = create_test_condition( + door.inst, + key2, + name2, + door.inst, + ComponentType::Inspectable, + "is_inspectable", + Operator::Equals, + array_true, + ); + world.write_model(@condition); + assert( + condition + .evaluate_condition( + @world, TriggerContext { doer: 0, target1: 0, target2: 0, inventory_object: 0 }, + ), + 'is_inspectable should be true', + ); + + // Test: is_inspectable == false (should fail) + let key3: felt252 = 3; + let name3: ByteArray = "Condition name3"; + let mut array_false = ArrayTrait::new(); + array_false.append(0); + let mut condition2 = create_test_condition( + door.inst, + key3, + name3, + door.inst, + ComponentType::Inspectable, + "is_inspectable", + Operator::Equals, + array_false, + ); + world.write_model(@condition2); + assert( + !condition2 + .evaluate_condition( + @world, TriggerContext { doer: 0, target1: 0, target2: 0, inventory_object: 0 }, + ), + 'is_inspectable should be false', + ); + + // Test: is_visible == true (should pass) + let key4: felt252 = 4; + let name4: ByteArray = "Condition name4"; + let mut array3 = ArrayTrait::new(); + array3.append(1); + let mut condition3 = create_test_condition( + door.inst, + key4, + name4, + door.inst, + ComponentType::Inspectable, + "is_visible", + Operator::Equals, + array3, + ); + world.write_model(@condition3); + assert( + condition3 + .evaluate_condition( + @world, TriggerContext { doer: 0, target1: 0, target2: 0, inventory_object: 0 }, + ), + 'is_visible should be true', + ); + + // Test: is_visible == false (should fail) + let key5: felt252 = 5; + let name5: ByteArray = "Condition name5"; + let mut array4 = ArrayTrait::new(); + array4.append(0); + let mut condition4 = create_test_condition( + door.inst, + key5, + name5, + door.inst, + ComponentType::Inspectable, + "is_visible", + Operator::Equals, + array4, + ); + world.write_model(@condition4); + assert( + !condition4 + .evaluate_condition( + @world, TriggerContext { doer: 0, target1: 0, target2: 0, inventory_object: 0 }, + ), + 'is_visible should be false', + ); + + // Test: is_visible != 0 (should pass) + let key6: felt252 = 6; + let name6: ByteArray = "Condition name6"; + let mut not_eq_array = ArrayTrait::new(); + not_eq_array.append(0); + let mut condition5 = create_test_condition( + door.inst, + key6, + name6, + door.inst, + ComponentType::Inspectable, + "is_visible", + Operator::NotEquals, + not_eq_array, + ); + world.write_model(@condition5); + assert( + condition5 + .evaluate_condition( + @world, TriggerContext { doer: 0, target1: 0, target2: 0, inventory_object: 0 }, + ), + 'should not be equal', + ); + + // Test: is_visible != 1 (should fail) + let key7: felt252 = 7; + let name7: ByteArray = "Condition name7"; + let mut not_eq_array2 = ArrayTrait::new(); + not_eq_array2.append(1); + let mut condition6 = create_test_condition( + door.inst, + key7, + name7, + door.inst, + ComponentType::Inspectable, + "is_visible", + Operator::NotEquals, + not_eq_array2, + ); + world.write_model(@condition6); + assert( + !condition6 + .evaluate_condition( + @world, TriggerContext { doer: 0, target1: 0, target2: 0, inventory_object: 0 }, + ), + 'should not be false', + ); + } +} + diff --git a/packages/contracts/src/new_components/container_trait.cairo b/packages/contracts/src/new_components/container_trait.cairo new file mode 100644 index 00000000..c0c6ea18 --- /dev/null +++ b/packages/contracts/src/new_components/container_trait.cairo @@ -0,0 +1,189 @@ +use dojo::{world::WorldStorage, model::ModelStorage}; +use lore::{ + models::{ + index::{Entity, Container, InventoryItem, Player}, components::Component, + container::ContainerComponent, + }, + new_components::{entity_trait::EntityImpl, player_trait::PlayerImpl}, + lib::{a_lexer::CommandImpl}, +}; + +#[generate_trait] +pub impl ContainerImpl of ContainerTrait { + fn is_container(self: Container) -> bool { + self.is_container + } + + fn get_item_ids(self: Container, world: @WorldStorage) -> Array { + let mut item_ids: Array = ArrayTrait::new(); + let items = self.entity(world).get_children(world); + for item in items { + item_ids.append(item.inst); + }; + item_ids + } + + fn set_open(self: Container, mut world: WorldStorage, opened: bool) { + let mut model: Container = world.read_model(self); + model.is_open = opened; + world.write_model(@model); + } + + fn set_can_be_opened(self: Container, mut world: WorldStorage, can_be_opened: bool) { + let mut model: Container = world.read_model(self); + model.can_be_opened = can_be_opened; + world.write_model(@model); + } + + fn set_can_receive_items(self: Container, mut world: WorldStorage, can_receive_items: bool) { + let mut model: Container = world.read_model(self); + model.can_receive_items = can_receive_items; + world.write_model(@model); + } + + fn is_full(self: @Container, world: @WorldStorage) -> bool { + let itemAmount: u32 = self.clone().get_item_ids(world).len().try_into().unwrap(); + return itemAmount >= *self.num_slots; + } + + fn is_empty(self: Container, world: @WorldStorage) -> bool { + return self.clone().get_item_ids(world).len() == 0; + } + + fn can_put_item(self: @Container, world: @WorldStorage, item: @InventoryItem) -> bool { + let mut can_put_item = false; + // check if container is open + if (!*self.is_open) { + return can_put_item; + } + // check if container is full + if (self.clone().is_full(world)) { + return can_put_item; + } + // check if container can receive items + if (!*self.can_receive_items) { + return can_put_item; + } + // check if item can be picked up + if (!*item.can_be_picked_up) { + return can_put_item; + } + // check if item can go into the container + if (!*item.can_go_in_container) { + return can_put_item; + } + // check if item is already in the container + if (self.contains(*item.inst, world)) { + return can_put_item; + } + // if checks pass, container can receive item + can_put_item = true; + can_put_item + } + + fn put_item_in(self: Container, mut world: WorldStorage, mut item: InventoryItem) { + // get container + let mut container: Container = world.read_model(self.inst); + + // get item entity + let item_entity: Entity = world.read_model(item.inst); + + // check if item can be put in container + if (!container.clone().can_put_item(@world, @item.clone())) { + return; + } + // set parent to be the container's entity + item_entity.set_parent(world, @container.entity(@world)); + item.owner_id = container.inst; + // update container + world.write_model(@container); + // update item + world.write_model(@item); + } + + + fn put_item_out( + self: Container, mut world: WorldStorage, mut item: InventoryItem, player: @Player, + ) { + // get container + let mut container: Container = world.read_model(self.inst); + // get item entity + let item_entity: Entity = world.read_model(item.inst); + // get room + let room: Entity = player.get_room(@world).unwrap(); + + // check if the item is in the container + if (!container.clone().contains(item_entity.inst, @world)) { + return; + } + // remove item from container: + // set parent to be the room's entity + item_entity.set_parent(world, @room); + item.owner_id = room.inst; + //item_entity.remove_from_parent(world, @container); + // update container + world.write_model(@container); + // update item + world.write_model(@item); + } + + fn contains(self: @Container, itemID: felt252, world: @WorldStorage) -> bool { + let mut already_inside = false; + // check if item is already in container + for item_id in self.clone().get_item_ids(world) { + if (item_id == itemID) { + already_inside = true; + break; + } + }; + already_inside + } + + fn check_container( + self: Container, world: @WorldStorage, player: @Player, object: @ByteArray, + ) -> bool { + // check if container is open + // we also check if the container is the player's personal inventory + if (!self.is_open) { + if (self.inst == *player.inst) { + player.say(*world, format!("{} personal inventory is close", object)); + return true; + } else { + player.say(*world, format!("The {} is closed", object)); + return true; + } + } else { + if (self.inst == *player.inst) { + player.say(*world, format!("{} personal inventory is open", object)); + } else { + player.say(*world, format!("{} is open", object)); + } + } + // check if container is full + if (self.clone().is_full(world)) { + player.say(*world, ("It is full.")); + } else { + player.say(*world, ("It is not full.")); + } + // check if container can receive items + if (!self.can_receive_items) { + player.say(*world, ("It cannot receive items")); + } else { + player.say(*world, ("It can receive items")); + } + // check if container is empty + if (self.clone().is_empty(world)) { + player.say(*world, ("It is empty.")); + } else { + // Say what it contains + player.say(*world, format!("It contains:")); + let items_id = self.get_item_ids(world); + for item_id in items_id { + let item = EntityImpl::get_entity(world, @item_id).unwrap(); + player.say(*world, format!("{}", item.name)); + }; + } + + return true; + } +} diff --git a/packages/contracts/src/new_components/effect_trait.cairo b/packages/contracts/src/new_components/effect_trait.cairo new file mode 100644 index 00000000..9cadedbd --- /dev/null +++ b/packages/contracts/src/new_components/effect_trait.cairo @@ -0,0 +1,221 @@ +use dojo::{world::WorldStorage}; + +use lore::{ + models::{ + index::{Effect}, area::AreaComponent, exit::ExitComponent, + inspectable::InspectableComponent, inventoryItem::InventoryItemComponent, + container::ContainerComponent, player::PlayerComponent, + }, + types::{action_type::TriggerContext, component_type::ComponentType}, + lib::{utils::ByteArrayTraitExt, variable_property::{VariablePropertyImp}}, + constants::errors::Error, +}; + +#[generate_trait] +pub impl EffectImpl of EffectTrait { + fn apply_effect( + self: @Effect, mut world: WorldStorage, context: TriggerContext, + ) -> Result<(), Error> { + let zero: felt252 = 0; + let mut result: Result::<(), Error> = Result::Err(Error::EffectFailed); + // Resolve target: use explicit target, fallback to context + let actual_target = if self.target == @zero { + @context.target1 + } else { + self.target + }; + + match self.component { + ComponentType::Area => { + let area_opt = AreaComponent::get_component(world, *actual_target); + if area_opt.is_none() { + result = Result::Err(Error::NoAreaComponent); + } + let mut area = area_opt.unwrap(); + // Direct modification to component + result = + VariablePropertyImp::set_property( + @world, @area.inst, self.property, self.value, self.component.clone(), + ); + }, + ComponentType::Exit => { + let exit_opt = ExitComponent::get_component(world, *actual_target); + if exit_opt.is_none() { + result = Result::Err(Error::NoExitComponent); + } + let mut exit = exit_opt.unwrap(); + result = + VariablePropertyImp::set_property( + @world, @exit.inst, self.property, self.value, self.component.clone(), + ); + }, + ComponentType::Inspectable => { + let inspect_opt = InspectableComponent::get_component(world, *actual_target); + if inspect_opt.is_none() { + result = Result::Err(Error::NoInspectableComponent); + } + let mut inspectable = inspect_opt.unwrap(); + result = + VariablePropertyImp::set_property( + @world, + @inspectable.inst, + self.property, + self.value, + self.component.clone(), + ); + }, + ComponentType::InventoryItem => { + let item_opt = InventoryItemComponent::get_component(world, *actual_target); + if item_opt.is_none() { + result = Result::Err(Error::NoInventoryItemComponent); + } + let mut item = item_opt.unwrap(); + result = + VariablePropertyImp::set_property( + @world, @item.inst, self.property, self.value, self.component.clone(), + ); + }, + ComponentType::Container => { + let cont_opt = ContainerComponent::get_component(world, *actual_target); + if cont_opt.is_none() { + result = Result::Err(Error::NoContainerComponent); + } + let mut container = cont_opt.unwrap(); + result = + VariablePropertyImp::set_property( + @world, @container.inst, self.property, self.value, self.component.clone(), + ); + }, + ComponentType::Player => { + let player_opt = PlayerComponent::get_component(world, *actual_target); + if player_opt.is_none() { + result = Result::Err(Error::NoPlayerComponent); + } + let mut player = player_opt.unwrap(); + result = + VariablePropertyImp::set_property( + @world, @player.inst, self.property, self.value, self.component.clone(), + ); + }, + _ => { result = Result::Err(Error::NoComponent); }, + } + + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + use dojo::{model::ModelStorage}; + use lore::tests::helpers; + use lore::{ + models::{ + index::{Inspectable, DescriptionText, Player}, components::Component, + area::AreaComponent, inspectable::InspectableComponent, + player::{PlayerComponent, caller_as_player}, + }, + new_components::{ + entity_trait::EntityImpl, player_trait::PlayerImpl, trigger_trait::TriggerImpl, + }, + types::{ + action_type::TriggerContext, + component_type::{ComponentType, ActionMapInspectable, InspectableActions}, + }, + lib::{variable_property::VariablePropertyImp}, + }; + + fn create_test_effect( + inst: felt252, + key: felt252, + name: ByteArray, + target: felt252, + component: ComponentType, + property: ByteArray, + value: Array<(ByteArray, u32)>, + ) -> Effect { + Effect { inst, key, name, target, component, property, value } + } + + fn create_trigger_context( + doer: felt252, target1: felt252, target2: felt252, inventory_object: felt252, + ) -> TriggerContext { + TriggerContext { doer, target1, target2, inventory_object } + } + + #[test] + fn Effect_test_apply_effect() { + let (mut world, _, _, player_1, _) = helpers::setup_core(); + // create door entity + let mut door = EntityImpl::create_entity(world); + door.name = "door"; + world.write_model(@door); + let mut inspectable: Inspectable = Component::add_component(world, door.inst); + let desc1: DescriptionText = DescriptionText { inst: door.inst, key: 0, text: "A door" }; + world.write_model(@desc1); + inspectable.is_inspectable = true; + inspectable.is_visible = true; + inspectable.description = array![0]; + inspectable + .action_map = + array![ + ActionMapInspectable { + action: "show", + inst: 0, + action_fn: InspectableActions::SetVisible, + entrypoint: 0, + }, + ActionMapInspectable { + action: "look", + inst: 0, + action_fn: InspectableActions::ReadRandomDescription, + entrypoint: 1, + }, + ]; + inspectable.store(world); + let old_insp_door: Inspectable = world.read_model(door.inst); + let old_key: u32 = *old_insp_door.description.at(0); + let old_txt: DescriptionText = world.read_model((door.inst, old_key)); + + // Create player + let mut player: Player = caller_as_player(world, player_1); + world.write_model(@player); + + // Create trigger context + let mut context = create_trigger_context(player.inst, door.inst, 0, 0); + + // register variable properties + VariablePropertyImp::register_component_properties(world, ComponentType::Inspectable); + + // Test description new value + let new_value: Array<(ByteArray, u32)> = array![ + ("A door that is open", 0), ("Looks that it leads somewhere", 1), + ]; + let key: felt252 = 1; + let name: ByteArray = "Effect name"; + let mut effect = create_test_effect( + door.inst, + key, + name, + door.inst, + ComponentType::Inspectable, + "description", + new_value.clone(), + ); + world.write_model(@effect); + let result = effect.apply_effect(world, context); + + let new_inspectable: Inspectable = world.read_model(door.inst); + let key: u32 = *new_inspectable.description.at(0); + let key2: u32 = *new_inspectable.description.at(1); + let new_txt1: DescriptionText = world.read_model((door.inst, key)); + let new_txt2: DescriptionText = world.read_model((door.inst, key2)); + + let (defTxt2, _defKey2) = new_value.at(1); + + assert_ne!(old_txt.text, new_txt1.text.clone(), "Effect should update description"); + assert_eq!(new_txt2.text.clone(), defTxt2.clone(), "Effect should update description"); + assert_eq!(result.is_ok(), true, "Effect should apply successfully"); + } +} + diff --git a/packages/contracts/src/new_components/entity_trait.cairo b/packages/contracts/src/new_components/entity_trait.cairo new file mode 100644 index 00000000..91183ab9 --- /dev/null +++ b/packages/contracts/src/new_components/entity_trait.cairo @@ -0,0 +1,146 @@ +use starknet::ContractAddress; +use dojo::{world::{WorldStorage, IWorldDispatcherTrait}, model::ModelStorage}; + +use lore::{ + models::{ + index::{Entity, Inspectable, DescriptionText, Player, ChildToParent, ParentToChildren}, + components::Component, player::PlayerComponent, inspectable::InspectableComponent, + }, + new_components::player_trait::PlayerImpl, +}; + + +#[generate_trait] +pub impl EntityImpl of EntityTrait { + fn create_entity(mut world: WorldStorage) -> Entity { + let mut entity: Entity = world.read_model(0); + entity.inst = world.dispatcher.uuid().try_into().unwrap(); + entity.is_entity = true; + world.write_model(@entity); + entity + } + + fn create_player_entity(mut world: WorldStorage, address: ContractAddress) -> Player { + let mut entity: Entity = world.read_model(0); + entity.name = "Player"; + entity.inst = address.into(); + entity.is_entity = true; + world.write_model(@entity); + let mut player: Player = Component::add_component(world, address.into()); + player.address = address; + world.write_model(@player); + let mut inspectable: Inspectable = Component::add_component(world, address.into()); + let descr1 = DescriptionText { inst: address.into(), key: 0, text: "Looks like a visitor" }; + world.write_model(@descr1); + inspectable.description = array![0]; + world.write_model(@inspectable); + player.say(world, "You feel light, and shiny, in the head"); + player + } + + fn get_names(self: @Entity) -> Array { + let mut names = self.alt_names.clone(); + names.append(self.clone().name); + names + } + + fn name_is(self: Entity, name: ByteArray) -> bool { + if (self.name == name) { + return true; + } + let mut has_name = false; + for alt_name in self.alt_names { + if (alt_name == name) { + has_name = true; + break; + } + }; + has_name + } + + fn get_entity(world: @WorldStorage, inst: @felt252) -> Option { + let entity: Entity = world.read_model(*inst); + if (!entity.is_entity) { + return Option::None; + } + Option::Some(entity) + } + + fn is_entity(world: @WorldStorage, inst: @felt252) -> bool { + let mut entity: Entity = world.read_model(*inst); + entity.is_entity + } + + fn has_parent(self: @Entity, world: @WorldStorage) -> bool { + let child_to_parent: ChildToParent = world.read_model(*self.inst); + return child_to_parent.is_child; + } + + fn get_parent(self: @Entity, world: @WorldStorage) -> Option { + let child_to_parent: ChildToParent = world.read_model(*self.inst); + if (child_to_parent.is_child) { + let parent_entity: Entity = world.read_model(child_to_parent.parent); + return Option::Some(parent_entity); + } + return Option::None; + } + + fn get_children(self: @Entity, world: @WorldStorage) -> Array { + let mut children: Array = ArrayTrait::::new(); + let parent_to_children: ParentToChildren = world.read_model(*self.inst); + if (parent_to_children.is_parent) { + for childKey in parent_to_children.children { + let child: Entity = world.read_model(childKey); + children.append(child); + } + } + children + } + + fn remove_from_parent(self: @Entity, mut world: WorldStorage, parent: @Entity) { + let mut parent_relation: ParentToChildren = world.read_model(*parent.inst); + assert(parent_relation.is_parent, 'Parent is not a parent'); + + let mut new_children: Array = ArrayTrait::::new(); + for child_inst in parent_relation.children { + if (child_inst != *self.inst) { + new_children.append(child_inst); + } + }; + parent_relation.children = new_children; + if parent_relation.children.len() == 0 { + world.erase_model(@parent_relation); + } else { + world.write_model(@parent_relation); + } + let child: ChildToParent = world.read_model(self.inst.clone()); + world.erase_model(@child); + } + + // @DEV: the cloning and writing in between is very dangerous, this might need a revision and at + // least good tests + fn set_parent(self: @Entity, mut world: WorldStorage, parent: @Entity) { + if (self.has_parent(@world)) { + self.remove_from_parent(world, @self.get_parent(@world).unwrap()); + } + let mut parent_relation: ParentToChildren = world.read_model(*parent.inst); + let mut is_child = false; + for child_inst in parent_relation.children.clone() { + if (child_inst == *self.inst) { + is_child = true; + break; + } + }; + + if (!is_child) { + parent_relation.children.append(*self.inst); + parent_relation.is_parent = true; + world.write_model(@parent_relation); + world + .write_model( + @ChildToParent { inst: *self.inst, is_child: true, parent: *parent.inst }, + ); + } + } +} + diff --git a/packages/contracts/src/new_components/exit_trait.cairo b/packages/contracts/src/new_components/exit_trait.cairo new file mode 100644 index 00000000..26580436 --- /dev/null +++ b/packages/contracts/src/new_components/exit_trait.cairo @@ -0,0 +1,13 @@ +use lore::models::index::Exit; + + +#[generate_trait] +pub impl ExitImpl of ExitTrait { + fn is_exit(self: Exit) -> bool { + self.is_exit + } + + fn can_player_enter(self: Exit) -> bool { + self.is_enterable + } +} diff --git a/packages/contracts/src/new_components/inspectable_trait.cairo b/packages/contracts/src/new_components/inspectable_trait.cairo new file mode 100644 index 00000000..9eab73fb --- /dev/null +++ b/packages/contracts/src/new_components/inspectable_trait.cairo @@ -0,0 +1,44 @@ +use dojo::{model::ModelStorage, world::{WorldStorage, IWorldDispatcherTrait}}; +use lore::{models::index::{Inspectable, DescriptionText}, lib::random}; + + +#[generate_trait] +pub impl InspectableImpl of InspectableTrait { + fn look_at(self: @Inspectable, world: WorldStorage) -> ByteArray { + self.get_random_description(world) + } + + fn get_random_description(self: @Inspectable, world: WorldStorage) -> ByteArray { + if self.description.len() == 0 { + return ""; + } + let rng: u32 = random::random_u16(world.dispatcher.uuid().try_into().unwrap()) + .try_into() + .unwrap(); + let description_len: u32 = self.description.len().try_into().unwrap(); + let entryRandom: u32 = (rng % description_len).try_into().unwrap(); + let key: u32 = self.description.at(entryRandom).clone(); + let descriptionText: DescriptionText = world.read_model((*self.inst, key)); + descriptionText.text + } + + fn get_first_description(self: @Inspectable, world: WorldStorage) -> ByteArray { + if self.description.len() == 0 { + return ""; + } + let key: u32 = self.description.at(0).clone(); + let descriptionText: DescriptionText = world.read_model((self.inst.clone(), key)); + descriptionText.text + } + + fn get_specific_description( + inspectable: @Inspectable, index: u32, world: WorldStorage, + ) -> ByteArray { + if inspectable.description.len() == 0 { + return ""; + } + let key: u32 = inspectable.description.at(index).clone(); + let descriptionText: DescriptionText = world.read_model((inspectable.inst.clone(), key)); + descriptionText.text + } +} diff --git a/packages/contracts/src/new_components/inventoryItem_trait.cairo b/packages/contracts/src/new_components/inventoryItem_trait.cairo new file mode 100644 index 00000000..673676a1 --- /dev/null +++ b/packages/contracts/src/new_components/inventoryItem_trait.cairo @@ -0,0 +1,16 @@ +use lore::models::index::InventoryItem; + +#[generate_trait] +pub impl InventoryItemImpl of InventoryItemTrait { + fn is_inventory_item(self: InventoryItem) -> bool { + self.is_inventory_item + } + + fn set_can_be_picked_up(ref self: InventoryItem, can_be_picked_up: bool) { + self.can_be_picked_up = can_be_picked_up; + } + + fn set_can_go_in_container(ref self: InventoryItem, can_go_in_container: bool) { + self.can_go_in_container = can_go_in_container; + } +} diff --git a/packages/contracts/src/new_components/player_trait.cairo b/packages/contracts/src/new_components/player_trait.cairo new file mode 100644 index 00000000..545e5b26 --- /dev/null +++ b/packages/contracts/src/new_components/player_trait.cairo @@ -0,0 +1,164 @@ +use dojo::{world::WorldStorage, model::ModelStorage}; +use lore::{ + models::{ + index::{Entity, Inspectable, Container, Player, PlayerStory, StoryLine}, + components::Component, inspectable::InspectableComponent, container::ContainerComponent, + }, + new_components::{entity_trait::EntityImpl, inspectable_trait::InspectableImpl}, + constants::errors::Error, +}; + + +#[generate_trait] +pub impl PlayerImpl of PlayerTrait { + fn describe_room(mut self: @Player, mut world: WorldStorage) -> Result<(), Error> { + let context = self.get_context(@world, *self.game_instance); + let room = self.get_room(@world); + if room.is_none() { + return Result::Err(Error::ActionFailed); + } + self.say(world, format!("{}", room.unwrap().name)); + for item in context { + // Don't add the player to the description + if (item.inst == *self.inst) { + continue; + } + let inspectable_opt: Option = Component::get_component(world, item.inst); + if inspectable_opt.is_some() { + let mut inspectable = inspectable_opt.unwrap(); + if inspectable.already_shown { + self.say(world, format!("{}", inspectable.new_entry)); + } else { + let description = inspectable.get_first_description(world); + self.say(world, format!("{}", description)); + inspectable.already_shown = true; + inspectable.store(world); + } + } + }; + Result::Ok(()) + } + + fn move_to_room(mut self: Player, mut world: WorldStorage, room_id: felt252) { + self.location = room_id; + let ent: Entity = EntityImpl::get_entity(@world, @self.inst).unwrap(); + let room = EntityImpl::get_entity(@world, @room_id).unwrap(); + ent.set_parent(world, @room); + world.write_model(@self); + if self.use_debug { + self.clone().say(world, format!("You {:?} enter {:?}", ent, room)); + } + } + + // TODO: improve name and better description + fn say(mut self: @Player, mut world: WorldStorage, text: ByteArray) { + // Read counter from player + let mut counter = *self.story_line; + // Increase counter + let increase: u64 = 1; + counter += increase; + // Create new story line + let mut story_line = StoryLine { inst: *self.inst, key: counter, line: text }; + // Write story line to world + world.write_model(@story_line); + // Add story line to player story + let mut player_story: PlayerStory = world.read_model(*self.inst); + player_story.story.append(counter); + // update player_story.story + world.write_model(@player_story); + // try to store only the story variable but doesn't work + //world.write_member(Model::::ptr_from_keys(self.inst), selector!("story"), + //@player_story.story); + } + + + fn add_command_text(mut self: @Player, mut world: WorldStorage, text: ByteArray) { + // read counter from player + let mut counter = *self.story_line; + // increase counter + let increase: u64 = 1; + counter += increase; + // create new story line + let mut story_line = StoryLine { inst: *self.inst, key: counter, line: text }; + // write story line to world + world.write_model(@story_line); + // add story line to player story + let mut player_story: PlayerStory = world.read_model(*self.inst); + player_story.story.append(counter); + // update player_story.story + world.write_model(@player_story); + // let mut playerStory: PlayerStory = world.read_model(*self.inst); + // let mut storyLine = playerStory.story.clone(); + // if (storyLine.len() > 10) { + // let _ = storyLine.pop_front(); + // } + // storyLine.append(format!("> {}", text)); + // world.write_model(@PlayerStory { inst: *self.inst, story: storyLine }); + } + + fn get_room(self: @Player, world: @WorldStorage) -> Option { + let player_entity: Entity = EntityImpl::get_entity(world, self.inst).unwrap(); + let parent = player_entity.get_parent(world); + if parent.is_none() { + return Option::None; + } + parent + } + + // Get the 1st level context of the room - scoped to game instance + fn get_context(self: @Player, world: @WorldStorage, game_instance: felt252) -> Array { + match self.get_room(world) { + Option::Some(room) => { + let mut context: Array = array![]; + context.append(room.clone()); + let children = room.get_children(world); + // Go over 1st level children + for child in children.clone() { + context.append(child.clone()); + }; + context + }, + Option::None => array![], + } + } + + // Get the full context of the room - scoped to game instance + fn get_full_context(self: @Player, world: @WorldStorage, game_instance: felt252) -> Array { + match self.get_room(world) { + Option::Some(room) => { + let mut context: Array = array![]; + context.append(room.clone()); + let children = room.get_children(world); + // Go over 1st level children + for child in children.clone() { + context.append(child.clone()); + // Go over 2nd level children + let children_2 = child.get_children(world); + for child_2 in children_2 { + context.append(child_2.clone()); + // Go over 3rd level children + let children_3 = child_2.get_children(world); + for child_3 in children_3 { + context.append(child_3); + } + }; + }; + context + }, + Option::None => array![], + } + } + + // Get the player personal inventory container component + fn get_personal_container(self: @Player, world: @WorldStorage) -> Option { + let mut personal_container: Option = Option::None; + match ContainerComponent::get_component(*world, *self.inst) { + Option::Some(c) => { personal_container = Option::Some(c); }, + Option::None => { + personal_container = Option::None; + self.say(*world, format!("You don't have a personal inventory container")); + }, + } + personal_container + } +} diff --git a/packages/contracts/src/new_components/trigger_trait.cairo b/packages/contracts/src/new_components/trigger_trait.cairo new file mode 100644 index 00000000..b1226285 --- /dev/null +++ b/packages/contracts/src/new_components/trigger_trait.cairo @@ -0,0 +1,397 @@ +use dojo::{world::{WorldStorage}, model::ModelStorage}; + +use lore::{ + models::{ + index::{Trigger, TriggerIndex}, area::AreaComponent, exit::ExitComponent, + inspectable::InspectableComponent, inventoryItem::InventoryItemComponent, + container::ContainerComponent, player::PlayerComponent, + }, + new_components::entity_trait::EntityImpl, + types::action_type::{TriggerType, IntoTriggerTypeFelt252}, constants::errors::Error, +}; + +#[generate_trait] +pub impl TriggerImpl of TriggerTrait { + fn register_trigger(mut world: WorldStorage, trigger: Trigger) -> Result<(), Error> { + // 0. Check if trigger is already in the index + let maybe_index = Self::get_triggerIndex(@world, @trigger.trigger_type); + match maybe_index { + Option::Some(mut trigger_index) => { + // Check if trigger is already registered + let mut found = false; + for pos_trigger in trigger_index.trigger_id.clone() { + if ((trigger.inst, trigger.key) == (pos_trigger)) { + found = true; + break; + } + }; + if found { + // If found just update the trigger + // println!("Trigger already registered, updating"); + world.write_model(@trigger); + return Result::Ok(()); + } + }, + Option::None => {}, + } + // 1. Register trigger + // Optional check: ensure name is short enough + if (trigger.name.clone().len() >= 31) { + return Result::Err(Error::NameTooLong); + } + // Store in Dojo world state + world.write_model(@trigger); + + // 2. Update trigger index + let result: Result = Self::update_triggerIndex(world, trigger.clone()); + if result.is_err() { + return Result::Err(Error::FailedToUpdateTriggerIndex); + } + // Done + Result::Ok(()) + } + + fn unregister_trigger(mut world: WorldStorage, trigger: Trigger) -> Result<(), Error> { + // 1. Remove the trigger + world.erase_model(@trigger); + Result::Ok(()) + } + + fn get_triggerIndex(world: @WorldStorage, trigger_type: @TriggerType) -> Option { + let inst: felt252 = trigger_type.clone().into(); + let trigger_index: TriggerIndex = world.read_model(inst); + if trigger_index.trigger_id.len() == 0 { + return Option::None; + } + Option::Some(trigger_index) + } + + fn update_triggerIndex(mut world: WorldStorage, trigger: Trigger) -> Result<(), Error> { + // Get the trigger index option + let maybe_index = Self::get_triggerIndex(@world, @trigger.trigger_type); + + match maybe_index { + Option::None => { + // Create new trigger index + let mut trigger_ids = ArrayTrait::<(felt252, felt252)>::new(); + trigger_ids.append((trigger.inst, trigger.key)); + let trigger_index = TriggerIndex { + trigger_type: trigger.trigger_type, trigger_id: trigger_ids, + }; + world.write_model(@trigger_index); + Result::Ok(()) + }, + Option::Some(mut trigger_index) => { + // Append trigger key to trigger index + trigger_index.trigger_id.append((trigger.inst, trigger.key)); + world.write_model(@trigger_index); + Result::Ok(()) + }, + } + } + + fn enable_trigger(mut world: WorldStorage, trigger_key: (felt252, felt252)) { + let mut trigger: Trigger = world.read_model(trigger_key); + // Enable trigger + trigger.is_enabled = true; + world.write_model(@trigger); + } + + fn disable_trigger(mut world: WorldStorage, trigger_key: (felt252, felt252)) { + let mut trigger: Trigger = world.read_model(trigger_key); + // Disable trigger + trigger.is_enabled = false; + world.write_model(@trigger); + } + + fn evaluate_trigger(mut world: WorldStorage, mut trigger: Trigger) -> Result<(), Error> { + let mut result: Result::<(), Error> = Result::Ok(()); + // Evaluate trigger + if !trigger.is_enabled { + return result; // If not enable is not an error. + } + + // Check if trigger is only triggered once + if trigger.is_once.clone() { + // Check if it has already been triggered + if trigger.was_triggered.clone() { + return Result::Err(Error::OnceUseOnly); + } + } + + match trigger.trigger_type { + TriggerType::OnEnter => { + // Get entity that trigger is attached to + let ent_opt = EntityImpl::get_entity(@world, @trigger.inst); + if ent_opt.is_none() { + return Result::Err(Error::EntityNotFound); + } + // Check if entity has an area component + let ent = ent_opt.unwrap(); + let area_opt = AreaComponent::get_component(world, ent.inst); + if area_opt.is_none() { + return Result::Err(Error::NoAreaComponent); + } + // Check if entity has player as a child + let children = ent.get_children(@world); + let mut player_found = false; + for child in children { + let child_player = PlayerComponent::get_component(world, child.inst); + if child_player.is_some() { + player_found = true; + break; + } + }; + if !player_found { + result = Result::Err(Error::TriggerNotMeetConditions); + return result; + } + return result; + }, + TriggerType::OnExit => { + // Get entity that trigger is attached to + let ent_opt = EntityImpl::get_entity(@world, @trigger.inst); + if ent_opt.is_none() { + return Result::Err(Error::EntityNotFound); + } + // Check if entity has an area component + let ent = ent_opt.unwrap(); + let area_opt = AreaComponent::get_component(world, ent.inst); + if area_opt.is_none() { + return Result::Err(Error::NoAreaComponent); + } + // Check if the entity does not have a player as a child + let children = ent.get_children(@world); + let mut player_found = false; + for child in children { + let child_player = PlayerComponent::get_component(world, child.inst); + if child_player.is_some() { + player_found = true; + break; + } + }; + if player_found { + return Result::Err(Error::TriggerNotMeetConditions); + } + return result; + }, + TriggerType::OnUse => { + // Get entity that trigger is attached to + let ent_opt = EntityImpl::get_entity(@world, @trigger.inst); + if ent_opt.is_none() { + return Result::Err(Error::EntityNotFound); + } + // Check if entity has an inventory item component + let ent = ent_opt.unwrap(); + let inventory_item_opt = InventoryItemComponent::get_component(world, ent.inst); + if inventory_item_opt.is_none() { + return Result::Err(Error::NoInventoryItemComponent); + } + let inventory_item = inventory_item_opt.unwrap(); + // Check that item has not been used + if inventory_item.already_used { + // If used check if multiple use is allowed + if inventory_item.multiple_use { + // If allowed, return ok + return Result::Ok(()); + } + return Result::Err(Error::OnceUseOnly); + } + // If not used, return ok + return Result::Ok(()); + }, + _ => { // Do nothing + }, + } + // Set trigger as triggered + trigger.was_triggered = true; + world.write_model(@trigger); + // Return result + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + use dojo::{model::ModelStorage}; + use lore::tests::helpers; + use lore::{ + models::{ + index::{Entity, Player, Trigger}, area::AreaComponent, exit::ExitComponent, + player::{PlayerComponent, caller_as_player}, + }, + new_components::{ + entity_trait::EntityImpl, player_trait::PlayerImpl, trigger_trait::TriggerImpl, + }, + types::{action_type::TriggerType, direction_type::Direction}, + }; + + fn create_test_trigger( + inst: felt252, key: felt252, nameT: ByteArray, trigger_type: TriggerType, + ) -> Trigger { + Trigger { + inst, + key, + name: nameT, + trigger_type, + is_enabled: true, + is_once: false, + was_triggered: false, + } + } + + #[test] + fn test_trigger_register_and_index() { + let (mut world, _, _, _, _) = helpers::setup_core(); + + let key: felt252 = 1; + let trigger = create_test_trigger(1, key, "TestTrigger", TriggerType::OnEnter); + + let result = TriggerImpl::register_trigger(world, trigger.clone()); + assert(result.is_ok(), 'Trig not register successfully'); + + let stored: Trigger = world.read_model(trigger.clone()); + assert(stored.inst == 1, 'Trigger inst should match'); + assert(stored.name == "TestTrigger", 'Trigger name should match'); + + let _key: felt252 = trigger.trigger_type.clone().into(); + let index: TriggerIndex = world.read_model(trigger.trigger_type); + assert(index.trigger_id.len() == 1, 'Trig index should have one ID'); + assert( + index.trigger_id[0] == @(trigger.inst.clone(), trigger.key.clone()), + 'Idx must have the trigger keys', + ); + } + + #[test] + fn test_trigger_name_too_long() { + let (mut world, _, _, _, _) = helpers::setup_core(); + let long_name = "Aakldjflkajdflkjldafljaldfjldjsdfdfdf"; + + // Create entity + let mut player = EntityImpl::create_entity(world); + player.name = "player"; + world.write_model(@player); + + // Create trigger + let key: felt252 = 2; + let trigger = create_test_trigger(1, key, long_name, TriggerType::OnExit); + world.write_model(@trigger); + + let result = TriggerImpl::register_trigger(world, trigger); + assert(result.is_err(), 'Trig name too long should fail'); + } + + #[test] + fn test_trigger_enable_disable() { + let (mut world, _, _, _, _) = helpers::setup_core(); + + // Create trigger + let key: felt252 = 3; + let mut trigger = create_test_trigger(1, key, "TestTrigger", TriggerType::OnExit); + world.write_model(@trigger); + + TriggerImpl::enable_trigger(world, (trigger.inst.clone(), trigger.key.clone())); + let enable_trigger: Trigger = world.read_model((trigger.inst.clone(), trigger.key.clone())); + assert(enable_trigger.is_enabled, 'Trigger should be enabled'); + + TriggerImpl::disable_trigger(world, (trigger.inst.clone(), trigger.key.clone())); + let disable_trigger: Trigger = world + .read_model((trigger.inst.clone(), trigger.key.clone())); + assert_eq!(disable_trigger.is_enabled, false, "Trigger should be disabled"); + } + + #[test] + fn test_trigger_index_append_multiple() { + let (mut world, _, _, _, _) = helpers::setup_core(); + let id_1: felt252 = 10; + let id_2: felt252 = 11; + + let trigger1 = create_test_trigger(1, id_1, "TestTrigger", TriggerType::OnEnter); + let trigger2 = create_test_trigger(2, id_2, "TestTrigger", TriggerType::OnEnter); + + let result1 = TriggerImpl::update_triggerIndex(world, trigger1.clone()); + // message: 1st trigger index insert didn't succeed + assert(result1.is_ok(), '1 trig idx insert nt succ'); + + let result2 = TriggerImpl::update_triggerIndex(world, trigger2.clone()); + // message: 2nd trigger index insert didn't succeed + assert(result2.is_ok(), '2 trig idx insert nt succ'); + + let index: TriggerIndex = world.read_model(TriggerType::OnEnter); + assert(index.trigger_id.len() == 2, 'Two triggers should be indexed'); + assert_eq!( + index.trigger_id[0], + @(trigger1.inst.clone(), trigger1.key.clone()), + "First ID should match", + ); + assert_eq!( + index.trigger_id[1], + @(trigger2.inst.clone(), trigger2.key.clone()), + "Second ID should match", + ); + } + + #[test] + fn test_evaluate_trigger() { + let (mut world, _, _, player_1, _) = helpers::setup_core(); + // create room entity 1 + let mut room_entity_1 = EntityImpl::create_entity(world); + // create room entity 2 + let mut room_entity_2 = EntityImpl::create_entity(world); + + // add area component to room entity 1 + let mut area_component_1 = AreaComponent::add_component(world, room_entity_1.inst); + world.write_model(@area_component_1); + // add exit component to room entity 1 + let mut exit_component_1 = ExitComponent::add_component(world, room_entity_1.inst); + // update exit component + exit_component_1.leads_to = room_entity_2.inst; + exit_component_1.direction_type = Direction::North; + world.write_model(@exit_component_1); + + // add area component to room entity 2 + let mut area_component_2 = AreaComponent::add_component(world, room_entity_2.inst); + world.write_model(@area_component_2); + // add exit component to room entity 2 + let mut exit_component_2 = ExitComponent::add_component(world, room_entity_2.inst); + // update exit component + exit_component_2.leads_to = room_entity_1.inst; + exit_component_2.direction_type = Direction::South; + world.write_model(@exit_component_2); + + let mut player: Player = caller_as_player(world, player_1); + player.location = room_entity_2.inst; + world.write_model(@player); + + let mut player_entity: Entity = EntityImpl::get_entity(@world, @player.inst).unwrap(); + // add player to room entity 2 + player_entity.set_parent(world, @room_entity_2); + + // set trigger to room entity 1 + let key: felt252 = 1; + let mut trigger = create_test_trigger( + room_entity_1.inst, key, "TestTrigger", TriggerType::OnEnter, + ); + let _result = TriggerImpl::register_trigger(world, trigger.clone()); + + // move player to room entity 1 + let mut playerR1: Player = world.read_model(player.inst); + playerR1.move_to_room(world, room_entity_1.inst); + + let result = TriggerImpl::evaluate_trigger(world, trigger.clone()); + if result.is_ok() { // println!("Trigger jumps successfully"); + }; + assert(result.is_ok(), 'Trigger should jump'); + + // move player to room entity 2 + player.move_to_room(world, room_entity_2.inst); + player_entity.set_parent(world, @room_entity_2); + + let result2 = TriggerImpl::evaluate_trigger(world, trigger); + if result2.is_err() { // println!("Trigger does not jump"); + }; + assert(result2.is_err(), 'Trigger should not jump'); + } +} diff --git a/packages/contracts/src/systems/designer.cairo b/packages/contracts/src/systems/designer.cairo index a3caddbf..98ed2056 100644 --- a/packages/contracts/src/systems/designer.cairo +++ b/packages/contracts/src/systems/designer.cairo @@ -1,10 +1,10 @@ -use lore::components::{ - inspectable::{Inspectable}, area::Area, exit::Exit, inventoryItem::InventoryItem, - container::Container, player::Player, -}; -use lore::lib::{ - entity::Entity, relations::{ParentToChildren, ChildToParent}, trigger::Trigger, - condition::Condition, actions::Action, effect::Effect, +use lore::{ + models::{ + index::{ + Entity, Area, Exit, Inspectable, InventoryItem, Container, Player, Trigger, Condition, + Effect, Action, ParentToChildren, ChildToParent, + }, + }, }; #[starknet::interface] @@ -43,18 +43,24 @@ pub trait IDesigner { #[dojo::contract] pub mod designer { use super::IDesigner; - use lore::components::{ - inspectable::{Inspectable}, area::Area, exit::Exit, inventoryItem::InventoryItem, - container::Container, player::Player, Components, - }; - use lore::lib::{ - entity::{Entity, EntityImpl}, relations::{ParentToChildren, ChildToParent}, - trigger::{Trigger, TriggerImpl}, condition::Condition, actions::{Action, ActionImpl}, - effect::Effect, variable_property::{VariablePropertyImp}, - dictionary::{add_to_dictionary, get_dict_entry}, a_lexer::{TokenType}, - utils::{ByteArrayTraitExt}, - }; use dojo::{model::ModelStorage, world::WorldStorage}; + use lore::{ + models::{ + index::{ + Entity, Area, Exit, Inspectable, InventoryItem, Container, Player, Trigger, + Condition, Effect, Action, ParentToChildren, ChildToParent, + }, + }, + new_components::{ + entity_trait::EntityImpl, trigger_trait::TriggerImpl, effect_trait::EffectImpl, + action_trait::ActionImpl, + }, + types::{component_type::ComponentType, command_type::TokenType}, + lib::{ + dictionary::{add_to_dictionary, get_dict_entry}, utils::{ByteArrayTraitExt}, + variable_property::{VariablePropertyImp}, + }, + }; #[abi(embed_v0)] pub impl DesignerImpl of IDesigner { @@ -63,18 +69,20 @@ pub mod designer { let world: WorldStorage = self.world(@"lore"); for d in done { if d { - VariablePropertyImp::register_component_properties(world, Components::Area); - VariablePropertyImp::register_component_properties(world, Components::Exit); + VariablePropertyImp::register_component_properties(world, ComponentType::Area); + VariablePropertyImp::register_component_properties(world, ComponentType::Exit); + VariablePropertyImp::register_component_properties( + world, ComponentType::Inspectable, + ); VariablePropertyImp::register_component_properties( - world, Components::Inspectable, + world, ComponentType::InventoryItem, ); VariablePropertyImp::register_component_properties( - world, Components::InventoryItem, + world, ComponentType::Container, ); VariablePropertyImp::register_component_properties( - world, Components::Container, + world, ComponentType::Player, ); - VariablePropertyImp::register_component_properties(world, Components::Player); } } } @@ -109,7 +117,7 @@ pub mod designer { fn create_player(ref self: ContractState, t: Array) { let mut world = self.world(@"lore"); let mut worldSt: WorldStorage = self.world(@"lore"); - VariablePropertyImp::register_component_properties(worldSt, Components::Player); + VariablePropertyImp::register_component_properties(worldSt, ComponentType::Player); for o in t { world.write_model(@o); } @@ -118,7 +126,7 @@ pub mod designer { fn create_inspectable(ref self: ContractState, t: Array) { let mut world = self.world(@"lore"); let mut worldSt: WorldStorage = self.world(@"lore"); - VariablePropertyImp::register_component_properties(worldSt, Components::Inspectable); + VariablePropertyImp::register_component_properties(worldSt, ComponentType::Inspectable); for o in t { world.write_model(@o); } @@ -127,7 +135,7 @@ pub mod designer { fn create_area(ref self: ContractState, t: Array) { let mut world = self.world(@"lore"); let mut worldSt: WorldStorage = self.world(@"lore"); - VariablePropertyImp::register_component_properties(worldSt, Components::Area); + VariablePropertyImp::register_component_properties(worldSt, ComponentType::Area); for o in t { world.write_model(@o); } @@ -136,7 +144,7 @@ pub mod designer { fn create_exit(ref self: ContractState, t: Array) { let mut world = self.world(@"lore"); let mut worldSt: WorldStorage = self.world(@"lore"); - VariablePropertyImp::register_component_properties(worldSt, Components::Exit); + VariablePropertyImp::register_component_properties(worldSt, ComponentType::Exit); for o in t { world.write_model(@o); } @@ -145,7 +153,9 @@ pub mod designer { fn create_inventory_item(ref self: ContractState, t: Array) { let mut world = self.world(@"lore"); let mut worldSt: WorldStorage = self.world(@"lore"); - VariablePropertyImp::register_component_properties(worldSt, Components::InventoryItem); + VariablePropertyImp::register_component_properties( + worldSt, ComponentType::InventoryItem, + ); for o in t { world.write_model(@o); } @@ -154,7 +164,7 @@ pub mod designer { fn create_container(ref self: ContractState, t: Array) { let mut world = self.world(@"lore"); let mut worldSt: WorldStorage = self.world(@"lore"); - VariablePropertyImp::register_component_properties(worldSt, Components::Container); + VariablePropertyImp::register_component_properties(worldSt, ComponentType::Container); for o in t { world.write_model(@o); } diff --git a/packages/contracts/src/systems/game_instance.cairo b/packages/contracts/src/systems/game_instance.cairo new file mode 100644 index 00000000..de1c1228 --- /dev/null +++ b/packages/contracts/src/systems/game_instance.cairo @@ -0,0 +1,142 @@ +use core::option::{OptionTraitImpl}; + +#[starknet::interface] +pub trait IGameInstance { + fn create_game_instance(ref self: T, name: ByteArray); + fn join_game_instance(ref self: T, game_instance_id: felt252); +} + +#[dojo::contract] +pub mod game_instance { + use super::IGameInstance; + use starknet::{get_caller_address, get_block_timestamp}; + use dojo::{world::WorldStorage}; + use lore::{ + models::{index::{GameInstance}, player::caller_as_player}, + new_components::player_trait::PlayerImpl, + lib::{utils::ByteArrayTraitExt}, +}; + + #[constructor] + fn constructor(ref self: ContractState) { + // Constructor can be empty for now + } + + #[abi(embed_v0)] + pub impl GameInstanceImpl of IGameInstance { + fn create_game_instance(ref self: ContractState, name: ByteArray) { + let mut world: WorldStorage = self.world(@"lore"); + let caller = get_caller_address(); + + // Create new game instance + let game_instance_id = 12345; // Simple ID for now + let game_instance = GameInstance { + inst: game_instance_id, + is_game_instance: true, + owner: caller, + name, + is_active: true, + created_at: get_block_timestamp(), + }; + + // TODO: Fix world.write_model when Dojo API is available + + // Initialize basic world for this instance + initialize_basic_world(world, game_instance_id); + + // Auto-join the creator + join_game_instance_internal(world, caller, game_instance_id); + } + + fn join_game_instance(ref self: ContractState, game_instance_id: felt252) { + let mut world: WorldStorage = self.world(@"lore"); + let caller = get_caller_address(); + + // TODO: Verify game instance exists and is active when Dojo API is available + // let game_instance: GameInstance = world.read_model(game_instance_id); + // assert(game_instance.is_game_instance, 'Game instance does not exist'); + // assert(game_instance.is_active, 'Game instance is not active'); + + // Join the game instance + join_game_instance_internal(world, caller, game_instance_id); + } + } + + fn join_game_instance_internal(mut world: WorldStorage, caller: starknet::ContractAddress, game_instance_id: felt252) { + // Get or create player and assign to game instance + let mut player = caller_as_player(world, caller); + player.game_instance = game_instance_id; + // TODO: Fix world.write_model when Dojo API is available + } + + fn initialize_basic_world(mut world: WorldStorage, game_instance_id: felt252) { + // Create a basic spawn area for this instance + create_basic_spawn_area(world, game_instance_id); + + // Create a few basic entities for this instance + create_basic_entities(world, game_instance_id); + } + + fn create_basic_spawn_area(mut world: WorldStorage, game_instance_id: felt252) { + // Create a basic spawn area + let spawn_area_id = 12346; // Simple ID for now + let spawn_area = lore::models::index::Area { + inst: spawn_area_id, + game_instance: game_instance_id, + is_area: true, + is_spawn_point: true, + }; + + // TODO: Fix world.write_model when Dojo API is available + + // Create corresponding entity + let spawn_entity = lore::models::index::Entity { + inst: spawn_area_id, + game_instance: game_instance_id, + is_entity: true, + name: "Spawn Area", + alt_names: array![], + actions_keys: array![], + }; + + // TODO: Fix world.write_model when Dojo API is available + } + + fn create_basic_entities(mut world: WorldStorage, game_instance_id: felt252) { + // Create a basic inspectable object + let inspectable_id = 12347; // Simple ID for now + let inspectable = lore::models::index::Inspectable { + inst: inspectable_id, + game_instance: game_instance_id, + is_inspectable: true, + is_visible: true, + description: array![1], + action_map: array![], + already_shown: false, + new_entry: "", + }; + + // TODO: Fix world.write_model when Dojo API is available + + // Create corresponding entity + let inspectable_entity = lore::models::index::Entity { + inst: inspectable_id, + game_instance: game_instance_id, + is_entity: true, + name: "Mysterious Object", + alt_names: array!["object", "thing"], + actions_keys: array![], + }; + + // TODO: Fix world.write_model when Dojo API is available + + // Create description text + let description_text = lore::models::index::DescriptionText { + inst: inspectable_id, + key: 1, + text: "You see a mysterious object. It seems to be waiting for something.", + }; + + // TODO: Fix world.write_model when Dojo API is available + } +} diff --git a/packages/contracts/src/systems/prompt.cairo b/packages/contracts/src/systems/prompt.cairo index cc96ad12..2a9931e7 100644 --- a/packages/contracts/src/systems/prompt.cairo +++ b/packages/contracts/src/systems/prompt.cairo @@ -10,9 +10,10 @@ pub mod prompt { use super::{IPrompt}; use starknet::{get_caller_address}; use dojo::{world::{WorldStorage}}; - use lore::components::{player::{PlayerImpl, caller_as_player}}; - use lore::lib::{a_lexer::{lexer}, random::{random_text}, c_handler::{handle_command} // - // dictionary::{init_dictionary}, + use lore::{ + models::{player::{PlayerComponent, caller_as_player}}, + new_components::player_trait::PlayerImpl, + lib::{a_lexer::{lexer}, random::{random_text}, c_handler::{handle_command}}, }; #[constructor] @@ -31,10 +32,19 @@ pub mod prompt { let mut world: WorldStorage = self.world(@"lore"); let player = caller_as_player(world, get_caller_address()); + // Get the player's game instance context + let game_instance = player.game_instance; + + // Verify player is in a valid game instance + if game_instance == 0 { + player.say(world, "You need to join a game instance first. Use 'create_game_instance' or 'join_game_instance'."); + return; + } + player.add_command_text(world, cmd.clone()); - match (lexer::parse(cmd, world, player)) { + match (lexer::parse(cmd, world, player, game_instance)) { Result::Ok(result) => { - let res = handle_command(result, world, player); + let res = handle_command(result, world, player, game_instance); if !res.is_ok() { player.say(world, random_text(world, random_error())); } diff --git a/packages/contracts/src/tests/entity_test.cairo b/packages/contracts/src/tests/entity_test.cairo index 3338b1a3..dae0d624 100644 --- a/packages/contracts/src/tests/entity_test.cairo +++ b/packages/contracts/src/tests/entity_test.cairo @@ -1,6 +1,5 @@ -use lore::tests::helpers; -use lore::lib::entity::{Entity, EntityTrait, EntityImpl}; use dojo::{model::ModelStorage}; +use lore::{models::index::Entity, new_components::entity_trait::EntityImpl, tests::helpers}; #[cfg(test)] mod tests { diff --git a/packages/contracts/src/tests/helpers.cairo b/packages/contracts/src/tests/helpers.cairo index ce3ecebc..adefba72 100644 --- a/packages/contracts/src/tests/helpers.cairo +++ b/packages/contracts/src/tests/helpers.cairo @@ -6,31 +6,16 @@ use dojo_cairo_test::{ }; use lore::{ - systems::{ // - designer::{designer, IDesignerDispatcher}, // - prompt::{prompt, IPromptDispatcher} // - }, - components::{ // - area::{m_Area}, // - inspectable::{m_Inspectable}, // - player::{m_Player, m_PlayerStory}, // - exit::{m_Exit}, // - container::{m_Container}, // - inventoryItem::{m_InventoryItem} // - }, - constants::{errors::{}}, - lib::{ - entity::{m_Entity}, // - a_lexer::{TokenTypeFelt252}, // - dictionary::{m_Dict, initialize_dictionary}, // - utils::{ByteArrayTraitExt}, // - relations::{m_ParentToChildren, m_ChildToParent}, // - trigger::{m_Trigger, m_TriggerIndex}, // - condition::{m_Condition}, // - variable_property::{m_PropertyRegistry}, // - effect::{m_Effect}, // - actions::{m_Action} // + systems::{designer::{designer, IDesignerDispatcher}, prompt::{prompt, IPromptDispatcher}}, + models::{ + index::{ + m_Dict, m_Entity, m_Area, m_Exit, m_Inspectable, m_InventoryItem, m_Container, m_Player, + m_PlayerStory, m_ParentToChildren, m_ChildToParent, m_Trigger, m_TriggerIndex, + m_Condition, m_PropertyRegistry, m_Effect, m_Action, m_StoryLine, m_DescriptionText, + }, }, + types::{command_type::IntoTokenTypeFelt252}, constants::{errors::{}}, + lib::{dictionary::{initialize_dictionary}, utils::{ByteArrayTraitExt}}, }; @@ -60,8 +45,10 @@ fn namespace_def() -> NamespaceDef { TestResource::Model(m_Dict::TEST_CLASS_HASH), TestResource::Model(m_Player::TEST_CLASS_HASH), TestResource::Model(m_PlayerStory::TEST_CLASS_HASH), + TestResource::Model(m_StoryLine::TEST_CLASS_HASH), TestResource::Model(m_Entity::TEST_CLASS_HASH), TestResource::Model(m_Inspectable::TEST_CLASS_HASH), + TestResource::Model(m_DescriptionText::TEST_CLASS_HASH), TestResource::Model(m_Area::TEST_CLASS_HASH), TestResource::Model(m_Exit::TEST_CLASS_HASH), TestResource::Model(m_Container::TEST_CLASS_HASH), diff --git a/packages/contracts/src/tests/prompt.cairo b/packages/contracts/src/tests/prompt.cairo index cc96ad12..e516cfca 100644 --- a/packages/contracts/src/tests/prompt.cairo +++ b/packages/contracts/src/tests/prompt.cairo @@ -10,9 +10,9 @@ pub mod prompt { use super::{IPrompt}; use starknet::{get_caller_address}; use dojo::{world::{WorldStorage}}; - use lore::components::{player::{PlayerImpl, caller_as_player}}; - use lore::lib::{a_lexer::{lexer}, random::{random_text}, c_handler::{handle_command} // - // dictionary::{init_dictionary}, + use lore::{ + new_components::player_trait::{PlayerImpl, caller_as_player}, + lib::{a_lexer::{lexer}, random::{random_text}, c_handler::{handle_command}}, }; #[constructor] diff --git a/packages/contracts/src/types/action_type.cairo b/packages/contracts/src/types/action_type.cairo new file mode 100644 index 00000000..b3733285 --- /dev/null +++ b/packages/contracts/src/types/action_type.cairo @@ -0,0 +1,205 @@ +// Here you can find the trigger, condition, effect types and structs + +#[derive(Copy, Drop, Serde, Debug, PartialEq, Introspect)] +pub struct TriggerContext { + /// The entity that triggered the action (usually the player) + pub doer: felt252, + /// Primary target of the action (e.g., item being picked up, area being entered) + pub target1: felt252, + /// Secondary target (e.g., container being opened, item being used on) + pub target2: felt252, + /// Inventory object involved (e.g., item being moved to/from inventory) + pub inventory_object: felt252, +} + +#[derive(Serde, Copy, Drop, Debug, PartialEq, Introspect)] +pub enum TriggerType { + OnEnter, + OnExit, + OnInteract, + OnInspect, + OnUse, + OnTimer, + OnCondition, +} + + +#[derive(Serde, Copy, Drop, Debug, PartialEq, Introspect)] +pub enum ConditionType { + HasItem, + InLocation, + PropertyEquals, + TimeRange, + RandomChance, + Custom, +} + +#[derive(Serde, Copy, Drop, Debug, PartialEq, Introspect)] +pub enum Operator { + Equals, + NotEquals, + GreaterThan, + LessThan, +} + +#[derive(Drop, Serde)] +enum EffectType { + ModifyProperty, + AddItem, + RemoveItem, + MoveEntity, + SendMessage, + TriggerAction, +} + + +// Implementation into U8 // + +pub impl IntoTriggerTypeU8 of core::traits::Into { + #[inline] + fn into(self: TriggerType) -> u8 { + match self { + TriggerType::OnEnter => 0, + TriggerType::OnExit => 1, + TriggerType::OnInteract => 2, + TriggerType::OnInspect => 3, + TriggerType::OnUse => 4, + TriggerType::OnTimer => 5, + TriggerType::OnCondition => 6, + } + } +} + +pub impl IntoOperatorU8 of core::traits::Into { + #[inline] + fn into(self: Operator) -> u8 { + match self { + Operator::Equals => 0, + Operator::NotEquals => 1, + Operator::GreaterThan => 2, + Operator::LessThan => 3, + } + } +} + +pub impl IntoConditionTypeU8 of core::traits::Into { + #[inline] + fn into(self: ConditionType) -> u8 { + match self { + ConditionType::HasItem => 0, + ConditionType::InLocation => 1, + ConditionType::PropertyEquals => 2, + ConditionType::TimeRange => 3, + ConditionType::RandomChance => 4, + ConditionType::Custom => 5, + } + } +} + +pub impl IntoEffectTypeU8 of core::traits::Into { + #[inline] + fn into(self: EffectType) -> u8 { + match self { + EffectType::ModifyProperty => 0, + EffectType::AddItem => 1, + EffectType::RemoveItem => 2, + EffectType::MoveEntity => 3, + EffectType::SendMessage => 4, + EffectType::TriggerAction => 5, + } + } +} + +// Implementation into Felt252 // + +pub impl IntoTriggerTypeFelt252 of core::traits::Into { + #[inline] + fn into(self: TriggerType) -> felt252 { + match self { + TriggerType::OnEnter => 0, + TriggerType::OnExit => 1, + TriggerType::OnInteract => 2, + TriggerType::OnInspect => 3, + TriggerType::OnUse => 4, + TriggerType::OnTimer => 5, + TriggerType::OnCondition => 6, + } + } +} + +// Implementation into Types // + +pub impl IntoU8TriggerType of core::traits::Into { + #[inline] + fn into(self: u8) -> TriggerType { + match self { + 0 => TriggerType::OnEnter, + 1 => TriggerType::OnExit, + 2 => TriggerType::OnInteract, + 3 => TriggerType::OnInspect, + 4 => TriggerType::OnUse, + 5 => TriggerType::OnTimer, + 6 => TriggerType::OnCondition, + _ => TriggerType::OnEnter, + } + } +} + +pub impl IntoU8Operator of core::traits::Into { + #[inline] + fn into(self: u8) -> Operator { + match self { + 0 => Operator::Equals, + 1 => Operator::NotEquals, + 2 => Operator::GreaterThan, + 3 => Operator::LessThan, + _ => Operator::Equals, + } + } +} + +pub impl IntoU8ConditionType of core::traits::Into { + #[inline] + fn into(self: u8) -> ConditionType { + match self { + 0 => ConditionType::HasItem, + 1 => ConditionType::InLocation, + 2 => ConditionType::PropertyEquals, + 3 => ConditionType::TimeRange, + 4 => ConditionType::RandomChance, + 5 => ConditionType::Custom, + _ => ConditionType::HasItem, + } + } +} + +pub impl IntoU8EffectType of core::traits::Into { + #[inline] + fn into(self: u8) -> EffectType { + match self { + 0 => EffectType::ModifyProperty, + 1 => EffectType::AddItem, + 2 => EffectType::RemoveItem, + 3 => EffectType::MoveEntity, + 4 => EffectType::SendMessage, + 5 => EffectType::TriggerAction, + _ => EffectType::ModifyProperty, + } + } +} + +pub impl IntoFelt252TriggerType of core::traits::Into { + #[inline] + fn into(self: felt252) -> TriggerType { + match self { + 0 => TriggerType::OnEnter, + 1 => TriggerType::OnExit, + 2 => TriggerType::OnInteract, + 3 => TriggerType::OnInspect, + 4 => TriggerType::OnUse, + 5 => TriggerType::OnTimer, + 6 => TriggerType::OnCondition, + _ => TriggerType::OnEnter, + } + } +} diff --git a/packages/contracts/src/types/command_type.cairo b/packages/contracts/src/types/command_type.cairo new file mode 100644 index 00000000..8dc0e3ed --- /dev/null +++ b/packages/contracts/src/types/command_type.cairo @@ -0,0 +1,90 @@ +// Here you can find the command, token structs and token types. + +#[derive(Clone, Drop, Serde, Debug)] +pub struct Command { + #[key] + pub command_id: felt252, // Unique ID of this command + /// Full command text + pub text: ByteArray, + /// Command splited into words + pub words: Array, + /// Number of tokens in the command + pub token_count: u8, + /// Type of action (using ActionType enum as u8) + pub action_type: u8, + /// Array of tokens in the command + pub tokens: Array, +} + +#[derive(Clone, Drop, Serde, Debug)] +pub struct Token { + /// Token position in the command + pub position: u32, + /// The token text as ByteArray + pub text: ByteArray, + /// Type of token (using TokenType enum as u8) + pub token_type: TokenType, + /// Value of token (ie directionId, obj inst) + pub token_value: felt252, + /// Target object (inst for token) + pub target: felt252, +} + + +#[derive(Serde, Copy, Drop, Introspect, PartialEq, Debug)] +pub enum TokenType { + Unknown, + Verb, // go, take, drop, look, inventory, spawn + Direction, // north, south, east, west + Article, // the, a + Preposition, // in, on, at, to + Pronoun, // they, it, me, you + Adjective, // good, bad, happy, sad + Noun, // noun, object + Quantifier, // number, quantity + Interrogative, // who, what, where, why, how + System, +} + +// Implementation into Felt252 // + +pub impl IntoTokenTypeFelt252 of core::traits::Into { + #[inline] + fn into(self: TokenType) -> felt252 { + match self { + TokenType::Unknown => 0, + TokenType::Verb => 1, + TokenType::Direction => 2, + TokenType::Article => 3, + TokenType::Preposition => 4, + TokenType::Pronoun => 5, + TokenType::Adjective => 6, + TokenType::Noun => 7, + TokenType::Quantifier => 8, + TokenType::Interrogative => 9, + TokenType::System => 10, + } + } +} + +// Implementation into TokenType // +pub impl IntoFelt252TokenType of core::traits::Into { + #[inline] + fn into(self: felt252) -> TokenType { + match self { + 0 => TokenType::Unknown, + 1 => TokenType::Verb, + 2 => TokenType::Direction, + 3 => TokenType::Article, + 4 => TokenType::Preposition, + 5 => TokenType::Pronoun, + 6 => TokenType::Adjective, + 7 => TokenType::Noun, + 8 => TokenType::Quantifier, + 9 => TokenType::Interrogative, + 10 => TokenType::System, + _ => TokenType::Unknown, + } + } +} + diff --git a/packages/contracts/src/types/component_type.cairo b/packages/contracts/src/types/component_type.cairo new file mode 100644 index 00000000..0cea7256 --- /dev/null +++ b/packages/contracts/src/types/component_type.cairo @@ -0,0 +1,234 @@ +// Here you can find the components types +// As well the components structs as well their corresponding action types. + +// Components // +#[derive(Serde, Copy, Drop, Debug, PartialEq, Introspect)] +pub enum ComponentType { + None, + Area, + Container, + Entity, + Exit, + Inspectable, + InventoryItem, + Player, + Trigger, + Condition, + Effect, + Action, +} + +// Inspectable // +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +pub struct ActionMapInspectable { + /// The action verb + pub action: ByteArray, + /// The inst of the component that the action is attached to + pub inst: felt252, + /// The types of actions + pub action_fn: InspectableActions, + /// The entrypoint to match the action with the description index + pub entrypoint: u32, +} + +#[derive(Serde, Copy, Drop, Introspect, PartialEq, Debug)] +pub enum InspectableActions { + SetVisible, + ReadRandomDescription, + ReadFirstDescription, + ReadSpecificDescription, +} + + +// Exit // +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +pub struct ActionMapExit { + /// The action verb + pub action: ByteArray, + /// The inst of the component that the action is attached to + pub inst: felt252, + /// The types of actions + pub action_fn: ExitActions, +} + +#[derive(Serde, Copy, Drop, Introspect, PartialEq, Debug)] +pub enum ExitActions { + UseExit, +} + +// Container // +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +pub struct ActionMapContainer { + /// The action verb + pub action: ByteArray, + /// The inst of the component that the action is attached to + pub inst: felt252, + /// The types of actions + pub action_fn: ContainerActions, +} + +#[derive(Serde, Copy, Drop, Introspect, PartialEq, Debug)] +pub enum ContainerActions { + Open, + Close, + Check, +} + +// InventoryItem // +#[derive(Clone, Drop, Serde, Introspect, PartialEq, Debug)] +pub struct ActionMapInventoryItem { + /// The action verb + pub action: ByteArray, + /// The inst of the component that the action is attached to + pub inst: felt252, + /// The types of actions + pub action_fn: InventoryItemActions, +} + +#[derive(Serde, Copy, Drop, Introspect, PartialEq, Debug)] +pub enum InventoryItemActions { + UseItem, + PickupItem, + DropItem, + PutItem, + TakeOutItem, +} + +// Implementations into U8 // + +pub impl IntoComponentTypeU8 of core::traits::Into { + #[inline] + fn into(self: ComponentType) -> u8 { + match self { + ComponentType::None => 0, + ComponentType::Area => 1, + ComponentType::Container => 2, + ComponentType::Entity => 3, + ComponentType::Exit => 4, + ComponentType::Inspectable => 5, + ComponentType::InventoryItem => 6, + ComponentType::Player => 7, + ComponentType::Trigger => 8, + ComponentType::Condition => 9, + ComponentType::Effect => 10, + ComponentType::Action => 11, + } + } +} + +pub impl IntoInspectableActionsU8 of core::traits::Into { + #[inline] + fn into(self: InspectableActions) -> u8 { + match self { + InspectableActions::SetVisible => 0, + InspectableActions::ReadRandomDescription => 1, + InspectableActions::ReadFirstDescription => 2, + InspectableActions::ReadSpecificDescription => 3, + } + } +} + +pub impl IntoExitActionsU8 of core::traits::Into { + #[inline] + fn into(self: ExitActions) -> u8 { + match self { + ExitActions::UseExit => 0, + } + } +} + +pub impl IntoContainerActionsU8 of core::traits::Into { + #[inline] + fn into(self: ContainerActions) -> u8 { + match self { + ContainerActions::Open => 0, + ContainerActions::Close => 1, + ContainerActions::Check => 2, + } + } +} + +pub impl IntoInventoryItemActionsU8 of core::traits::Into { + #[inline] + fn into(self: InventoryItemActions) -> u8 { + match self { + InventoryItemActions::UseItem => 0, + InventoryItemActions::PickupItem => 1, + InventoryItemActions::DropItem => 2, + InventoryItemActions::PutItem => 3, + InventoryItemActions::TakeOutItem => 4, + } + } +} + +// Implementations into Actions // + +pub impl IntoU8ComponentType of core::traits::Into { + #[inline] + fn into(self: u8) -> ComponentType { + match self { + 0 => ComponentType::None, + 1 => ComponentType::Area, + 2 => ComponentType::Container, + 3 => ComponentType::Entity, + 4 => ComponentType::Exit, + 5 => ComponentType::Inspectable, + 6 => ComponentType::InventoryItem, + 7 => ComponentType::Player, + 8 => ComponentType::Trigger, + 9 => ComponentType::Condition, + 10 => ComponentType::Effect, + 11 => ComponentType::Action, + _ => ComponentType::None, + } + } +} + +pub impl IntoU8InspectableAction of core::traits::Into { + #[inline] + fn into(self: u8) -> InspectableActions { + match self { + 0 => InspectableActions::SetVisible, + 1 => InspectableActions::ReadRandomDescription, + 2 => InspectableActions::ReadFirstDescription, + 3 => InspectableActions::ReadSpecificDescription, + _ => InspectableActions::SetVisible, + } + } +} + +pub impl IntoU8ExitAction of core::traits::Into { + #[inline] + fn into(self: u8) -> ExitActions { + match self { + 0 => ExitActions::UseExit, + _ => ExitActions::UseExit, + } + } +} + +pub impl IntoU8ContainerAction of core::traits::Into { + #[inline] + fn into(self: u8) -> ContainerActions { + match self { + 0 => ContainerActions::Open, + 1 => ContainerActions::Close, + 2 => ContainerActions::Check, + _ => ContainerActions::Open, + } + } +} + +pub impl IntoU8InventoryItemAction of core::traits::Into { + #[inline] + fn into(self: u8) -> InventoryItemActions { + match self { + 0 => InventoryItemActions::UseItem, + 1 => InventoryItemActions::PickupItem, + 2 => InventoryItemActions::DropItem, + 3 => InventoryItemActions::PutItem, + 4 => InventoryItemActions::TakeOutItem, + _ => InventoryItemActions::UseItem, + } + } +} diff --git a/packages/contracts/src/types/direction_type.cairo b/packages/contracts/src/types/direction_type.cairo new file mode 100644 index 00000000..d1912a4b --- /dev/null +++ b/packages/contracts/src/types/direction_type.cairo @@ -0,0 +1,112 @@ +// Here you can find the direction type + +#[derive(Serde, Copy, Drop, Debug, Introspect, PartialEq)] +pub enum Direction { + North, + South, + East, + West, + NorthEast, + SouthEast, + NorthWest, + SouthWest, + Up, + Down, +} + +// Implementation into U8 // + +pub impl IntoDirectionU8 of core::traits::Into { + #[inline] + fn into(self: Direction) -> u8 { + match self { + Direction::North => 0, + Direction::South => 1, + Direction::East => 2, + Direction::West => 3, + Direction::NorthEast => 4, + Direction::SouthEast => 5, + Direction::NorthWest => 6, + Direction::SouthWest => 7, + Direction::Up => 8, + Direction::Down => 9, + } + } +} + +// Implementation into felt252 // +pub impl IntoDirectionFelt252 of core::traits::Into { + #[inline] + fn into(self: Direction) -> felt252 { + match self { + Direction::North => 0, + Direction::South => 1, + Direction::East => 2, + Direction::West => 3, + Direction::NorthEast => 4, + Direction::SouthEast => 5, + Direction::NorthWest => 6, + Direction::SouthWest => 7, + Direction::Up => 8, + Direction::Down => 9, + } + } +} + +// Implementation into ByteArray // +pub impl IntoDirectionByteArray of core::traits::Into { + #[inline] + fn into(self: Direction) -> ByteArray { + match self { + Direction::North => "north", + Direction::South => "south", + Direction::East => "east", + Direction::West => "west", + Direction::NorthEast => "north-east", + Direction::SouthEast => "south-east", + Direction::NorthWest => "north-west", + Direction::SouthWest => "south-west", + Direction::Up => "up", + Direction::Down => "down", + } + } +} + +// Implementation into Direction // +pub impl IntoU8Direction of core::traits::Into { + #[inline] + fn into(self: u8) -> Direction { + match self { + 0 => Direction::North, + 1 => Direction::South, + 2 => Direction::East, + 3 => Direction::West, + 4 => Direction::NorthEast, + 5 => Direction::SouthEast, + 6 => Direction::NorthWest, + 7 => Direction::SouthWest, + 8 => Direction::Up, + 9 => Direction::Down, + _ => Direction::North, + } + } +} + +pub impl IntoFelt252Direction of core::traits::Into { + #[inline] + fn into(self: felt252) -> Direction { + match self { + 0 => Direction::North, + 1 => Direction::South, + 2 => Direction::East, + 3 => Direction::West, + 4 => Direction::NorthEast, + 5 => Direction::SouthEast, + 6 => Direction::NorthWest, + 7 => Direction::SouthWest, + 8 => Direction::Up, + 9 => Direction::Down, + _ => Direction::North, + } + } +} diff --git a/packages/contracts/src/types/property_type.cairo b/packages/contracts/src/types/property_type.cairo new file mode 100644 index 00000000..5fbf6885 --- /dev/null +++ b/packages/contracts/src/types/property_type.cairo @@ -0,0 +1,89 @@ +// Here you can find the ComponentProperty struct, +// the property types and the property access types + +#[derive(Clone, Drop, Serde, Introspect, Debug, PartialEq)] +pub struct ComponentProperty { + pub name: ByteArray, + pub property_type: PropertyType, + pub access_flags: PropertyAccess, +} + +#[derive(Serde, Copy, Drop, Debug, PartialEq, Introspect)] +pub enum PropertyType { + Boolean, + Felt252, + U8, + U32, + Enum, + ByteArray, + ContractAddress, + ArrayFelt252, + ArrayByteArray, +} + +#[derive(Serde, Copy, Drop, Debug, PartialEq, Introspect)] +pub enum PropertyAccess { + ReadOnly, + ReadWrite, +} + +// Implementation into U8 // + +pub impl IntoPropertyTypeU8 of core::traits::Into { + #[inline] + fn into(self: PropertyType) -> u8 { + match self { + PropertyType::Boolean => 0, + PropertyType::Felt252 => 1, + PropertyType::U8 => 2, + PropertyType::U32 => 3, + PropertyType::Enum => 4, + PropertyType::ByteArray => 5, + PropertyType::ContractAddress => 6, + PropertyType::ArrayFelt252 => 7, + PropertyType::ArrayByteArray => 8, + } + } +} + +pub impl IntoPropertyAccessU8 of core::traits::Into { + fn into(self: PropertyAccess) -> u8 { + match self { + PropertyAccess::ReadOnly => 0, + PropertyAccess::ReadWrite => 1, + } + } +} + +// Implementation into PropertyType // + +pub impl IntoU8PropertyType of core::traits::Into { + #[inline] + fn into(self: u8) -> PropertyType { + match self { + 0 => PropertyType::Boolean, + 1 => PropertyType::Felt252, + 2 => PropertyType::U8, + 3 => PropertyType::U32, + 4 => PropertyType::Enum, + 5 => PropertyType::ByteArray, + 6 => PropertyType::ContractAddress, + 7 => PropertyType::ArrayFelt252, + 8 => PropertyType::ArrayByteArray, + _ => PropertyType::Boolean, + } + } +} + +// Implementation into PropertyAccess // + +pub impl IntoU8PropertyAccess of core::traits::Into { + #[inline] + fn into(self: u8) -> PropertyAccess { + match self { + 0 => PropertyAccess::ReadOnly, + 1 => PropertyAccess::ReadWrite, + _ => PropertyAccess::ReadOnly, + } + } +}