diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index a33ebf3a..26c88e3d 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -16,7 +16,9 @@ export default defineWorld({ schema: { adminAddress: "address", tokenAddress: "address", + escapedStumpTokenAddress: "address", globalSpawnIndex: "uint32", // Global index for all players + globalEscapeIndex: "uint32", // Global index for all players scaleDown: "uint32", // Used to scale down the amounts in the UI flowRate: "uint32", // Amount flowing from the inlet depotCapacity: "uint32", // Amount of material that can be stored in a depot @@ -34,6 +36,23 @@ export default defineWorld({ BuildIndex: "uint32", // Build index of a particular machine type in a particular pod BuildTracker: "uint32[]", // How many machines of each type have been built in pod since its creation? SpawnIndex: "uint32", // How many players have spawned? + EscapeIndex: "uint32", // How many players have escaped? + EscapeIndexRanked: { + key: ["completedOrdersRank", "pointsRank"], + schema: { + completedOrdersRank: "uint256", + pointsRank: "uint256", + value: "uint32" + } + }, + EscapeRankName: { + key: ["completedOrdersRank", "pointsRank"], + schema: { + completedOrdersRank: "uint256", + pointsRank: "uint256", + value: "string" + } + }, Tutorial: "bool", TutorialLevel: "uint32", Order: { diff --git a/packages/contracts/script/PostDeploy.s.sol b/packages/contracts/script/PostDeploy.s.sol index f5a29819..8e60f0b2 100644 --- a/packages/contracts/script/PostDeploy.s.sol +++ b/packages/contracts/script/PostDeploy.s.sol @@ -6,14 +6,18 @@ import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { IERC20Mintable } from "@latticexyz/world-modules/src/modules/erc20-puppet/IERC20Mintable.sol"; import { registerERC20 } from "@latticexyz/world-modules/src/modules/erc20-puppet/registerERC20.sol"; import { ERC20MetadataData } from "@latticexyz/world-modules/src/modules/erc20-puppet/tables/ERC20Metadata.sol"; +import { IERC721Mintable } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721Mintable.sol"; +import { registerERC721 } from "@latticexyz/world-modules/src/modules/erc721-puppet/registerERC721.sol"; +import { ERC721MetadataData } from "@latticexyz/world-modules/src/modules/erc721-puppet/tables/ERC721Metadata.sol"; import { IWorld } from "../src/codegen/world/IWorld.sol"; +import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; import { ROOT_NAMESPACE_ID } from "@latticexyz/world/src/constants.sol"; -import { NamespaceOwner } from "@latticexyz/world/src/codegen/tables/NamespaceOwner.sol"; +import { NamespaceOwner } from "@latticexyz/world/src/codegen/index.sol"; import { MATERIAL_TYPE } from "../src/codegen/common.sol"; -import { LibOrder, LibInitRecipes, LibInit, LibOffer } from "../src/libraries/Libraries.sol"; +import { LibOrder, LibInitRecipes, LibInitEscapeRankNames, LibInit, LibOffer } from "../src/libraries/Libraries.sol"; import { ONE_MINUTE, ONE_DAY, ONE_HOUR } from "../src/constants.sol"; uint256 constant POOL_SUPPLY = 1_000_000 wei; @@ -38,13 +42,27 @@ contract PostDeploy is Script { token.mint(worldAddress, POOL_SUPPLY); + // Register ERC721 escaped stump token + IERC721Mintable escapedStumpToken = registerERC721( + world, + "EscapedStumpT", + ERC721MetadataData({ name: "TCM", symbol: "TCM", baseURI: "" }) + ); + + // Transfer token namespaces to World + world.transferOwnership(WorldResourceIdLib.encodeNamespace("Token"), worldAddress); + world.transferOwnership(WorldResourceIdLib.encodeNamespace("EscapedStumpT"), worldAddress); + // Initialize gameConfig and tutorial levels // Root namespace owner is admin - LibInit.init(NamespaceOwner.get(ROOT_NAMESPACE_ID), address(token)); + LibInit.init(NamespaceOwner.get(ROOT_NAMESPACE_ID), address(token), address(escapedStumpToken)); // Initialize recipes LibInitRecipes.init(); + // Initialize rank names for escaped pod NFTs + LibInitEscapeRankNames.init(); + // Create offer LibOffer.create(MATERIAL_TYPE.BUG, 10000, 100); // 1:1 ratio : 100 $BUG => 10000 Bug (Shown as 100 Bugs with scale-down in UI) diff --git a/packages/contracts/src/codegen/index.sol b/packages/contracts/src/codegen/index.sol index 4a854a39..c99f7994 100644 --- a/packages/contracts/src/codegen/index.sol +++ b/packages/contracts/src/codegen/index.sol @@ -13,6 +13,9 @@ import { CarriedBy } from "./tables/CarriedBy.sol"; import { BuildIndex } from "./tables/BuildIndex.sol"; import { BuildTracker } from "./tables/BuildTracker.sol"; import { SpawnIndex } from "./tables/SpawnIndex.sol"; +import { EscapeIndex } from "./tables/EscapeIndex.sol"; +import { EscapeIndexRanked } from "./tables/EscapeIndexRanked.sol"; +import { EscapeRankName } from "./tables/EscapeRankName.sol"; import { Tutorial } from "./tables/Tutorial.sol"; import { TutorialLevel } from "./tables/TutorialLevel.sol"; import { Order, OrderData } from "./tables/Order.sol"; diff --git a/packages/contracts/src/codegen/tables/EscapeIndex.sol b/packages/contracts/src/codegen/tables/EscapeIndex.sol new file mode 100644 index 00000000..5c8e19d1 --- /dev/null +++ b/packages/contracts/src/codegen/tables/EscapeIndex.sol @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +library EscapeIndex { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "", name: "EscapeIndex", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x74620000000000000000000000000000457363617065496e6465780000000000); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0004010004000000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (bytes32) + Schema constant _keySchema = Schema.wrap(0x002001005f000000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (uint32) + Schema constant _valueSchema = Schema.wrap(0x0004010003000000000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](1); + keyNames[0] = "id"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "value"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get value. + */ + function getValue(bytes32 id) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function _getValue(bytes32 id) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function get(bytes32 id) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function _get(bytes32 id) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Set value. + */ + function setValue(bytes32 id, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _setValue(bytes32 id, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function set(bytes32 id, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _set(bytes32 id, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(uint32 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(uint32 value) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData = encodeStatic(value); + + EncodedLengths _encodedLengths; + bytes memory _dynamicData; + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(bytes32 id) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + return _keyTuple; + } +} diff --git a/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol b/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol new file mode 100644 index 00000000..a30f0aeb --- /dev/null +++ b/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +library EscapeIndexRanked { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "", name: "EscapeIndexRanke", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x74620000000000000000000000000000457363617065496e64657852616e6b65); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0004010004000000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (uint256, uint256) + Schema constant _keySchema = Schema.wrap(0x004002001f1f0000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (uint32) + Schema constant _valueSchema = Schema.wrap(0x0004010003000000000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](2); + keyNames[0] = "completedOrdersRank"; + keyNames[1] = "pointsRank"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "value"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get value. + */ + function getValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function _getValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function get(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function _get(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Set value. + */ + function setValue(uint256 completedOrdersRank, uint256 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _setValue(uint256 completedOrdersRank, uint256 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function set(uint256 completedOrdersRank, uint256 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _set(uint256 completedOrdersRank, uint256 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(uint32 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(uint32 value) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData = encodeStatic(value); + + EncodedLengths _encodedLengths; + bytes memory _dynamicData; + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(uint256 completedOrdersRank, uint256 pointsRank) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + return _keyTuple; + } +} diff --git a/packages/contracts/src/codegen/tables/EscapeRankName.sol b/packages/contracts/src/codegen/tables/EscapeRankName.sol new file mode 100644 index 00000000..b4d080c4 --- /dev/null +++ b/packages/contracts/src/codegen/tables/EscapeRankName.sol @@ -0,0 +1,502 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +library EscapeRankName { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "", name: "EscapeRankName", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x7462000000000000000000000000000045736361706552616e6b4e616d650000); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0000000100000000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (uint256, uint256) + Schema constant _keySchema = Schema.wrap(0x004002001f1f0000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (string) + Schema constant _valueSchema = Schema.wrap(0x00000001c5000000000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](2); + keyNames[0] = "completedOrdersRank"; + keyNames[1] = "pointsRank"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "value"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get value. + */ + function getValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (string memory value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Get value. + */ + function _getValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (string memory value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Get value. + */ + function get(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (string memory value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Get value. + */ + function _get(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (string memory value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Set value. + */ + function setValue(uint256 completedOrdersRank, uint256 pointsRank, string memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 0, bytes((value))); + } + + /** + * @notice Set value. + */ + function _setValue(uint256 completedOrdersRank, uint256 pointsRank, string memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.setDynamicField(_tableId, _keyTuple, 0, bytes((value))); + } + + /** + * @notice Set value. + */ + function set(uint256 completedOrdersRank, uint256 pointsRank, string memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 0, bytes((value))); + } + + /** + * @notice Set value. + */ + function _set(uint256 completedOrdersRank, uint256 pointsRank, string memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.setDynamicField(_tableId, _keyTuple, 0, bytes((value))); + } + + /** + * @notice Get the length of value. + */ + function lengthValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of value. + */ + function _lengthValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of value. + */ + function length(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of value. + */ + function _length(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get an item of value. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItemValue( + uint256 completedOrdersRank, + uint256 pointsRank, + uint256 _index + ) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of value. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItemValue( + uint256 completedOrdersRank, + uint256 pointsRank, + uint256 _index + ) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of value. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItem( + uint256 completedOrdersRank, + uint256 pointsRank, + uint256 _index + ) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of value. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItem( + uint256 completedOrdersRank, + uint256 pointsRank, + uint256 _index + ) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Push a slice to value. + */ + function pushValue(uint256 completedOrdersRank, uint256 pointsRank, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Push a slice to value. + */ + function _pushValue(uint256 completedOrdersRank, uint256 pointsRank, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Push a slice to value. + */ + function push(uint256 completedOrdersRank, uint256 pointsRank, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Push a slice to value. + */ + function _push(uint256 completedOrdersRank, uint256 pointsRank, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Pop a slice from value. + */ + function popValue(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Pop a slice from value. + */ + function _popValue(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Pop a slice from value. + */ + function pop(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Pop a slice from value. + */ + function _pop(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Update a slice of value at `_index`. + */ + function updateValue(uint256 completedOrdersRank, uint256 pointsRank, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of value at `_index`. + */ + function _updateValue( + uint256 completedOrdersRank, + uint256 pointsRank, + uint256 _index, + string memory _slice + ) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of value at `_index`. + */ + function update(uint256 completedOrdersRank, uint256 pointsRank, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of value at `_index`. + */ + function _update(uint256 completedOrdersRank, uint256 pointsRank, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack dynamic data lengths using this table's schema. + * @return _encodedLengths The lengths of the dynamic fields (packed into a single bytes32 value). + */ + function encodeLengths(string memory value) internal pure returns (EncodedLengths _encodedLengths) { + // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits + unchecked { + _encodedLengths = EncodedLengthsLib.pack(bytes(value).length); + } + } + + /** + * @notice Tightly pack dynamic (variable length) data using this table's schema. + * @return The dynamic data, encoded into a sequence of bytes. + */ + function encodeDynamic(string memory value) internal pure returns (bytes memory) { + return abi.encodePacked(bytes((value))); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(string memory value) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData; + EncodedLengths _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(uint256 completedOrdersRank, uint256 pointsRank) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + return _keyTuple; + } +} diff --git a/packages/contracts/src/codegen/tables/GameConfig.sol b/packages/contracts/src/codegen/tables/GameConfig.sol index 1878406f..66bcc489 100644 --- a/packages/contracts/src/codegen/tables/GameConfig.sol +++ b/packages/contracts/src/codegen/tables/GameConfig.sol @@ -19,7 +19,9 @@ import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; struct GameConfigData { address adminAddress; address tokenAddress; + address escapedStumpTokenAddress; uint32 globalSpawnIndex; + uint32 globalEscapeIndex; uint32 scaleDown; uint32 flowRate; uint32 depotCapacity; @@ -30,12 +32,12 @@ library GameConfig { ResourceId constant _tableId = ResourceId.wrap(0x7462000000000000000000000000000047616d65436f6e666967000000000000); FieldLayout constant _fieldLayout = - FieldLayout.wrap(0x0038060014140404040400000000000000000000000000000000000000000000); + FieldLayout.wrap(0x0050080014141404040404040000000000000000000000000000000000000000); // Hex-encoded key schema of () Schema constant _keySchema = Schema.wrap(0x0000000000000000000000000000000000000000000000000000000000000000); - // Hex-encoded value schema of (address, address, uint32, uint32, uint32, uint32) - Schema constant _valueSchema = Schema.wrap(0x0038060061610303030300000000000000000000000000000000000000000000); + // Hex-encoded value schema of (address, address, address, uint32, uint32, uint32, uint32, uint32) + Schema constant _valueSchema = Schema.wrap(0x0050080061616103030303030000000000000000000000000000000000000000); /** * @notice Get the table's key field names. @@ -50,13 +52,15 @@ library GameConfig { * @return fieldNames An array of strings with the names of value fields. */ function getFieldNames() internal pure returns (string[] memory fieldNames) { - fieldNames = new string[](6); + fieldNames = new string[](8); fieldNames[0] = "adminAddress"; fieldNames[1] = "tokenAddress"; - fieldNames[2] = "globalSpawnIndex"; - fieldNames[3] = "scaleDown"; - fieldNames[4] = "flowRate"; - fieldNames[5] = "depotCapacity"; + fieldNames[2] = "escapedStumpTokenAddress"; + fieldNames[3] = "globalSpawnIndex"; + fieldNames[4] = "globalEscapeIndex"; + fieldNames[5] = "scaleDown"; + fieldNames[6] = "flowRate"; + fieldNames[7] = "depotCapacity"; } /** @@ -149,13 +153,51 @@ library GameConfig { StoreCore.setStaticField(_tableId, _keyTuple, 1, abi.encodePacked((tokenAddress)), _fieldLayout); } + /** + * @notice Get escapedStumpTokenAddress. + */ + function getEscapedStumpTokenAddress() internal view returns (address escapedStumpTokenAddress) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Get escapedStumpTokenAddress. + */ + function _getEscapedStumpTokenAddress() internal view returns (address escapedStumpTokenAddress) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Set escapedStumpTokenAddress. + */ + function setEscapedStumpTokenAddress(address escapedStumpTokenAddress) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((escapedStumpTokenAddress)), _fieldLayout); + } + + /** + * @notice Set escapedStumpTokenAddress. + */ + function _setEscapedStumpTokenAddress(address escapedStumpTokenAddress) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((escapedStumpTokenAddress)), _fieldLayout); + } + /** * @notice Get globalSpawnIndex. */ function getGlobalSpawnIndex() internal view returns (uint32 globalSpawnIndex) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -165,7 +207,7 @@ library GameConfig { function _getGlobalSpawnIndex() internal view returns (uint32 globalSpawnIndex) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -175,7 +217,7 @@ library GameConfig { function setGlobalSpawnIndex(uint32 globalSpawnIndex) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((globalSpawnIndex)), _fieldLayout); + StoreSwitch.setStaticField(_tableId, _keyTuple, 3, abi.encodePacked((globalSpawnIndex)), _fieldLayout); } /** @@ -184,7 +226,45 @@ library GameConfig { function _setGlobalSpawnIndex(uint32 globalSpawnIndex) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreCore.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((globalSpawnIndex)), _fieldLayout); + StoreCore.setStaticField(_tableId, _keyTuple, 3, abi.encodePacked((globalSpawnIndex)), _fieldLayout); + } + + /** + * @notice Get globalEscapeIndex. + */ + function getGlobalEscapeIndex() internal view returns (uint32 globalEscapeIndex) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get globalEscapeIndex. + */ + function _getGlobalEscapeIndex() internal view returns (uint32 globalEscapeIndex) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Set globalEscapeIndex. + */ + function setGlobalEscapeIndex(uint32 globalEscapeIndex) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 4, abi.encodePacked((globalEscapeIndex)), _fieldLayout); + } + + /** + * @notice Set globalEscapeIndex. + */ + function _setGlobalEscapeIndex(uint32 globalEscapeIndex) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setStaticField(_tableId, _keyTuple, 4, abi.encodePacked((globalEscapeIndex)), _fieldLayout); } /** @@ -193,7 +273,7 @@ library GameConfig { function getScaleDown() internal view returns (uint32 scaleDown) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -203,7 +283,7 @@ library GameConfig { function _getScaleDown() internal view returns (uint32 scaleDown) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -213,7 +293,7 @@ library GameConfig { function setScaleDown(uint32 scaleDown) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setStaticField(_tableId, _keyTuple, 3, abi.encodePacked((scaleDown)), _fieldLayout); + StoreSwitch.setStaticField(_tableId, _keyTuple, 5, abi.encodePacked((scaleDown)), _fieldLayout); } /** @@ -222,7 +302,7 @@ library GameConfig { function _setScaleDown(uint32 scaleDown) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreCore.setStaticField(_tableId, _keyTuple, 3, abi.encodePacked((scaleDown)), _fieldLayout); + StoreCore.setStaticField(_tableId, _keyTuple, 5, abi.encodePacked((scaleDown)), _fieldLayout); } /** @@ -231,7 +311,7 @@ library GameConfig { function getFlowRate() internal view returns (uint32 flowRate) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 6, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -241,7 +321,7 @@ library GameConfig { function _getFlowRate() internal view returns (uint32 flowRate) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 6, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -251,7 +331,7 @@ library GameConfig { function setFlowRate(uint32 flowRate) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setStaticField(_tableId, _keyTuple, 4, abi.encodePacked((flowRate)), _fieldLayout); + StoreSwitch.setStaticField(_tableId, _keyTuple, 6, abi.encodePacked((flowRate)), _fieldLayout); } /** @@ -260,7 +340,7 @@ library GameConfig { function _setFlowRate(uint32 flowRate) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreCore.setStaticField(_tableId, _keyTuple, 4, abi.encodePacked((flowRate)), _fieldLayout); + StoreCore.setStaticField(_tableId, _keyTuple, 6, abi.encodePacked((flowRate)), _fieldLayout); } /** @@ -269,7 +349,7 @@ library GameConfig { function getDepotCapacity() internal view returns (uint32 depotCapacity) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 7, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -279,7 +359,7 @@ library GameConfig { function _getDepotCapacity() internal view returns (uint32 depotCapacity) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 7, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -289,7 +369,7 @@ library GameConfig { function setDepotCapacity(uint32 depotCapacity) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setStaticField(_tableId, _keyTuple, 5, abi.encodePacked((depotCapacity)), _fieldLayout); + StoreSwitch.setStaticField(_tableId, _keyTuple, 7, abi.encodePacked((depotCapacity)), _fieldLayout); } /** @@ -298,7 +378,7 @@ library GameConfig { function _setDepotCapacity(uint32 depotCapacity) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreCore.setStaticField(_tableId, _keyTuple, 5, abi.encodePacked((depotCapacity)), _fieldLayout); + StoreCore.setStaticField(_tableId, _keyTuple, 7, abi.encodePacked((depotCapacity)), _fieldLayout); } /** @@ -335,7 +415,9 @@ library GameConfig { function set( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity @@ -343,7 +425,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( adminAddress, tokenAddress, + escapedStumpTokenAddress, globalSpawnIndex, + globalEscapeIndex, scaleDown, flowRate, depotCapacity @@ -363,7 +447,9 @@ library GameConfig { function _set( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity @@ -371,7 +457,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( adminAddress, tokenAddress, + escapedStumpTokenAddress, globalSpawnIndex, + globalEscapeIndex, scaleDown, flowRate, depotCapacity @@ -392,7 +480,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( _table.adminAddress, _table.tokenAddress, + _table.escapedStumpTokenAddress, _table.globalSpawnIndex, + _table.globalEscapeIndex, _table.scaleDown, _table.flowRate, _table.depotCapacity @@ -413,7 +503,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( _table.adminAddress, _table.tokenAddress, + _table.escapedStumpTokenAddress, _table.globalSpawnIndex, + _table.globalEscapeIndex, _table.scaleDown, _table.flowRate, _table.depotCapacity @@ -438,7 +530,9 @@ library GameConfig { returns ( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity @@ -448,13 +542,17 @@ library GameConfig { tokenAddress = (address(Bytes.getBytes20(_blob, 20))); - globalSpawnIndex = (uint32(Bytes.getBytes4(_blob, 40))); + escapedStumpTokenAddress = (address(Bytes.getBytes20(_blob, 40))); + + globalSpawnIndex = (uint32(Bytes.getBytes4(_blob, 60))); + + globalEscapeIndex = (uint32(Bytes.getBytes4(_blob, 64))); - scaleDown = (uint32(Bytes.getBytes4(_blob, 44))); + scaleDown = (uint32(Bytes.getBytes4(_blob, 68))); - flowRate = (uint32(Bytes.getBytes4(_blob, 48))); + flowRate = (uint32(Bytes.getBytes4(_blob, 72))); - depotCapacity = (uint32(Bytes.getBytes4(_blob, 52))); + depotCapacity = (uint32(Bytes.getBytes4(_blob, 76))); } /** @@ -471,7 +569,9 @@ library GameConfig { ( _table.adminAddress, _table.tokenAddress, + _table.escapedStumpTokenAddress, _table.globalSpawnIndex, + _table.globalEscapeIndex, _table.scaleDown, _table.flowRate, _table.depotCapacity @@ -503,12 +603,24 @@ library GameConfig { function encodeStatic( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity ) internal pure returns (bytes memory) { - return abi.encodePacked(adminAddress, tokenAddress, globalSpawnIndex, scaleDown, flowRate, depotCapacity); + return + abi.encodePacked( + adminAddress, + tokenAddress, + escapedStumpTokenAddress, + globalSpawnIndex, + globalEscapeIndex, + scaleDown, + flowRate, + depotCapacity + ); } /** @@ -520,7 +632,9 @@ library GameConfig { function encode( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity @@ -528,7 +642,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( adminAddress, tokenAddress, + escapedStumpTokenAddress, globalSpawnIndex, + globalEscapeIndex, scaleDown, flowRate, depotCapacity diff --git a/packages/contracts/src/codegen/world/IRewardSystem.sol b/packages/contracts/src/codegen/world/IEscapeSystem.sol similarity index 72% rename from packages/contracts/src/codegen/world/IRewardSystem.sol rename to packages/contracts/src/codegen/world/IEscapeSystem.sol index 14234850..66a823ae 100644 --- a/packages/contracts/src/codegen/world/IRewardSystem.sol +++ b/packages/contracts/src/codegen/world/IEscapeSystem.sol @@ -4,12 +4,10 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ /** - * @title IRewardSystem + * @title IEscapeSystem * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. */ -interface IRewardSystem { - function reward() external; - - function charge() external; +interface IEscapeSystem { + function escapePod() external; } diff --git a/packages/contracts/src/codegen/world/IWorld.sol b/packages/contracts/src/codegen/world/IWorld.sol index 77b9ef9e..db683e9f 100644 --- a/packages/contracts/src/codegen/world/IWorld.sol +++ b/packages/contracts/src/codegen/world/IWorld.sol @@ -15,6 +15,7 @@ import { IResolveSystem } from "./IResolveSystem.sol"; import { IDepotSystem } from "./IDepotSystem.sol"; import { IOfferSystem } from "./IOfferSystem.sol"; import { IOrderSystem } from "./IOrderSystem.sol"; +import { IEscapeSystem } from "./IEscapeSystem.sol"; import { INameSystem } from "./INameSystem.sol"; import { ISpawnSystem } from "./ISpawnSystem.sol"; import { IStartSystem } from "./IStartSystem.sol"; @@ -38,6 +39,7 @@ interface IWorld is IDepotSystem, IOfferSystem, IOrderSystem, + IEscapeSystem, INameSystem, ISpawnSystem, IStartSystem diff --git a/packages/contracts/src/constants.sol b/packages/contracts/src/constants.sol index 2415bc30..a05e1d62 100644 --- a/packages/contracts/src/constants.sol +++ b/packages/contracts/src/constants.sol @@ -10,3 +10,14 @@ uint32 constant ONE_DAY = 24 * ONE_HOUR; uint32 constant FLOW_RATE = 1000; uint32 constant NUMBER_OF_DEPOTS = 3; uint32 constant DEPOT_CAPACITY = 50000; + +function COMPLETED_ORDERS_THRESHOLDS() pure returns (uint256[4] memory) { + return [uint256(10), 15, 20, 25]; +} + +function POINTS_THRESHOLDS() pure returns (uint256[4] memory) { + return [uint256(10_000), 15_000, 20_000, 25_000]; +} + +// For the purpose of image retrieval, ranked indexes start at 1 and end at this cap inclusively +uint32 constant ESCAPE_INDEX_RANKED_IMAGE_CAP = 11; diff --git a/packages/contracts/src/libraries/LibEscape.sol b/packages/contracts/src/libraries/LibEscape.sol new file mode 100644 index 00000000..2ffedc25 --- /dev/null +++ b/packages/contracts/src/libraries/LibEscape.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; +import { WorldContextConsumerLib } from "@latticexyz/world/src/WorldContext.sol"; + +import { IERC721Mintable } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721Mintable.sol"; + +import { LibEscapedStumpTokenURI } from "./LibEscapedStumpTokenURI.sol"; +import { GameConfig, EscapeIndex, EscapeIndexRanked } from "../codegen/index.sol"; +import { COMPLETED_ORDERS_THRESHOLDS, POINTS_THRESHOLDS } from "../constants.sol"; + +library LibEscape { + function incrementIndexesAndMint(bytes32 _playerEntity, uint256 _completedOrders, uint256 _points) internal { + // Increment global escape index + uint32 newEscapeIndex = GameConfig.getGlobalEscapeIndex() + 1; + GameConfig.setGlobalEscapeIndex(newEscapeIndex); + // Mark the player entity as escaped + EscapeIndex.set(_playerEntity, newEscapeIndex); + + // Calculate ranks + uint256 completedOrdersRank = _rank(COMPLETED_ORDERS_THRESHOLDS(), _completedOrders); + uint256 pointsRank = _rank(POINTS_THRESHOLDS(), _points); + + // Increment ranked escape index (separate for each combination of ranks) + uint32 newEscapeIndexRanked = EscapeIndexRanked.get(completedOrdersRank, pointsRank) + 1; + EscapeIndexRanked.set(completedOrdersRank, pointsRank, newEscapeIndexRanked); + + // Derive tokenId from the global escape index, which is unique + uint256 tokenId = newEscapeIndex; + + // Generate and set TokenURI for this token + LibEscapedStumpTokenURI.initTokenURI( + tokenId, + _playerEntity, + _completedOrders, + _points, + completedOrdersRank, + pointsRank, + newEscapeIndexRanked + ); + + // Mint the NFT + IERC721Mintable escapedStumpToken = IERC721Mintable(GameConfig.getEscapedStumpTokenAddress()); + escapedStumpToken.safeMint(WorldContextConsumerLib._msgSender(), tokenId); + } + + /** + * @notice Rank is index+1 of the highest threshold less-than-or-equal the provided value. + * 0 if value doesn't exceed any thresholds. + * @param _thresholds Ascending thresholds. + * @param _value Value to compare to thresholds. + */ + function _rank(uint256[4] memory _thresholds, uint256 _value) private pure returns (uint256) { + uint256 i = _thresholds.length; + while (i > 0) { + i--; + if (_value >= _thresholds[i]) { + return i + 1; + } + } + return 0; + } +} diff --git a/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol b/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol new file mode 100644 index 00000000..25dafc14 --- /dev/null +++ b/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; +import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; + +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +import { WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; + +import { Puppet } from "@latticexyz/world-modules/src/modules/puppet/Puppet.sol"; +import { TokenURI } from "@latticexyz/world-modules/src/modules/erc721-puppet/tables/TokenURI.sol"; +import { _tokenUriTableId } from "@latticexyz/world-modules/src/modules/erc721-puppet/utils.sol"; + +import { GameConfig, Name, EscapeRankName, Completed } from "../codegen/index.sol"; +import { ESCAPE_INDEX_RANKED_IMAGE_CAP } from "../constants.sol"; + +library LibEscapedStumpTokenURI { + using WorldResourceIdInstance for ResourceId; + + function initTokenURI( + uint256 _tokenId, + bytes32 _playerEntity, + uint256 _completedOrders, + uint256 _points, + uint256 _completedOrdersRank, + uint256 _pointsRank, + uint32 _escapeIndexRanked + ) internal { + address token = GameConfig.getEscapedStumpTokenAddress(); + ResourceId systemId = Puppet(token).systemId(); + ResourceId tableId = _tokenUriTableId(systemId.getNamespace()); + + string memory uri = _uri( + _playerEntity, + _completedOrders, + _points, + _completedOrdersRank, + _pointsRank, + _escapeIndexRanked + ); + TokenURI.set(tableId, _tokenId, uri); + } + + function _getImageName( + uint256 _completedOrdersRank, + uint256 _pointsRank, + uint32 _escapeIndexRanked + ) private pure returns (string memory) { + uint32 cappedIndex = _escapeIndexRanked; + if (cappedIndex > ESCAPE_INDEX_RANKED_IMAGE_CAP) { + cappedIndex = ESCAPE_INDEX_RANKED_IMAGE_CAP; + } + return + string.concat( + Strings.toString(_completedOrdersRank), + "_", + Strings.toString(_pointsRank), + "_", + Strings.toString(cappedIndex) + ); + } + + function _uri( + bytes32 _playerEntity, + uint256 _completedOrders, + uint256 _points, + uint256 _completedOrdersRank, + uint256 _pointsRank, + uint32 _escapeIndexRanked + ) private view returns (string memory) { + string memory playerName = Name.get(_playerEntity); + string memory rankName = EscapeRankName.get(_completedOrdersRank, _pointsRank); + string memory imageName = _getImageName(_completedOrdersRank, _pointsRank, _escapeIndexRanked); + + // prettier-ignore + // (manual formatting to make json contents more readable) + string memory json = Base64.encode(abi.encodePacked( + // TODO should this be the player name or sth else? + '{"name": "', playerName, '",', + // TODO custom descriptions? + '"description": "Escaped stump",', + '"attributes": [' + '{' + '"trait_type": "Player Name",' + '"value": "', playerName, '"' + '},' + '{' + '"trait_type": "Rank Name",' + '"value": "', rankName, '"' + '},' + '{' + '"trait_type": "Points",' + '"value": ', _points, + '},' + '{' + '"trait_type": "Completed Orders",' + '"value": ', _completedOrders, + '},' + '{' + '"trait_type": "Escape Block Number",' + '"value": ', block.number, + '}' + '],' + // TODO real image url + '"image": "https://PLACEHOLDER_IMAGE_URL/', imageName, '"}' + )); + + return string.concat("data:application/json;base64,", json); + } +} diff --git a/packages/contracts/src/libraries/Libraries.sol b/packages/contracts/src/libraries/Libraries.sol index 210dd000..5485962c 100644 --- a/packages/contracts/src/libraries/Libraries.sol +++ b/packages/contracts/src/libraries/Libraries.sol @@ -11,5 +11,8 @@ import { LibOrder } from "./LibOrder.sol"; import { LibReset } from "./LibReset.sol"; import { LibOffer } from "./LibOffer.sol"; import { LibToken } from "./LibToken.sol"; +import { LibEscape } from "./LibEscape.sol"; +import { LibEscapedStumpTokenURI } from "./LibEscapedStumpTokenURI.sol"; import { LibInit } from "./init/LibInit.sol"; import { LibInitRecipes } from "./init/LibInitRecipes.sol"; +import { LibInitEscapeRankNames } from "./init/LibInitEscapeRankNames.sol"; diff --git a/packages/contracts/src/libraries/init/LibInit.sol b/packages/contracts/src/libraries/init/LibInit.sol index 64834537..5427ab29 100644 --- a/packages/contracts/src/libraries/init/LibInit.sol +++ b/packages/contracts/src/libraries/init/LibInit.sol @@ -12,13 +12,15 @@ library LibInit { * @param _adminAddress The address of the admin * @param _tokenAddress The address of the token */ - function init(address _adminAddress, address _tokenAddress) internal { + function init(address _adminAddress, address _tokenAddress, address _escapedStumpTokenAddress) internal { // Set game config GameConfig.set( GameConfigData({ adminAddress: _adminAddress, tokenAddress: _tokenAddress, + escapedStumpTokenAddress: _escapedStumpTokenAddress, globalSpawnIndex: 0, + globalEscapeIndex: 0, scaleDown: 100, flowRate: FLOW_RATE, depotCapacity: DEPOT_CAPACITY diff --git a/packages/contracts/src/libraries/init/LibInitEscapeRankNames.sol b/packages/contracts/src/libraries/init/LibInitEscapeRankNames.sol new file mode 100644 index 00000000..f2f65621 --- /dev/null +++ b/packages/contracts/src/libraries/init/LibInitEscapeRankNames.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; +import { EscapeRankName } from "../../codegen/index.sol"; + +library LibInitEscapeRankNames { + function init() internal { + // These represent the names of 25 grid cells, which determine the coolness of an escaped stump NFT + // The first key is completed orders rank, higher is better, 5 in total + // The second key is points spent rank, higher is better, 5 in total + + // TODO replace name placeholders + EscapeRankName.set(0, 0, "few orders, few points"); + EscapeRankName.set(0, 1, "0_1"); + EscapeRankName.set(0, 2, "0_2"); + EscapeRankName.set(0, 3, "0_3"); + EscapeRankName.set(0, 4, "few orders, many points"); + + EscapeRankName.set(1, 0, "1_0"); + EscapeRankName.set(1, 1, "1_1"); + EscapeRankName.set(1, 2, "1_2"); + EscapeRankName.set(1, 3, "1_3"); + EscapeRankName.set(1, 4, "1_4"); + + EscapeRankName.set(2, 0, "2_0"); + EscapeRankName.set(2, 1, "2_1"); + EscapeRankName.set(2, 2, "2_2"); + EscapeRankName.set(2, 3, "2_3"); + EscapeRankName.set(2, 4, "2_4"); + + EscapeRankName.set(3, 0, "3_0"); + EscapeRankName.set(3, 1, "3_1"); + EscapeRankName.set(3, 2, "3_2"); + EscapeRankName.set(3, 3, "3_3"); + EscapeRankName.set(3, 4, "3_4"); + + EscapeRankName.set(4, 0, "many orders, few points"); + EscapeRankName.set(4, 1, "4_1"); + EscapeRankName.set(4, 2, "4_2"); + EscapeRankName.set(4, 3, "4_3"); + EscapeRankName.set(4, 4, "many orders, many points"); + } +} diff --git a/packages/contracts/src/systems/progression/EscapeSystem.sol b/packages/contracts/src/systems/progression/EscapeSystem.sol new file mode 100644 index 00000000..30c3cbb2 --- /dev/null +++ b/packages/contracts/src/systems/progression/EscapeSystem.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; +import { System } from "@latticexyz/world/src/System.sol"; +import { LibToken, LibUtils, LibEscape } from "../../libraries/Libraries.sol"; +import { CarriedBy, Completed, EscapeIndex, GameConfig, Tutorial, Name } from "../../codegen/index.sol"; + +contract EscapeSystem is System { + function escapePod() public { + bytes32 playerEntity = LibUtils.addressToEntityKey(_msgSender()); + + // Tutorial must be completed + require(Tutorial.get(playerEntity) == false, "not completed"); + // Must be named before escape + require(bytes(Name.get(playerEntity)).length > 0, "name empty"); + // Must not have already escaped + require(EscapeIndex.get(playerEntity) == 0, "already escaped"); + // TODO determine and add more escape conditions if needed + + // TODO validate podEntity in other systems (preferable), or delete entity and machine types here + CarriedBy.deleteRecord(playerEntity); + + // Get the number of completed orders + uint256 completedOrders = Completed.length(playerEntity); + + // Convert all tokens to points, transferring the tokens to world + uint256 points = LibToken.getTokenBalance(_msgSender()); + LibToken.transferToken(_world(), points); + + // Update indexes and mint NFT for _msgSender() + LibEscape.incrementIndexesAndMint(playerEntity, completedOrders, points); + } +} diff --git a/packages/contracts/test/systems/progression/EscapeSystem.t.sol b/packages/contracts/test/systems/progression/EscapeSystem.t.sol new file mode 100644 index 00000000..e7f76345 --- /dev/null +++ b/packages/contracts/test/systems/progression/EscapeSystem.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity >=0.8.24; +import { IERC721 } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721.sol"; +import { ROOT_NAMESPACE_ID } from "@latticexyz/world/src/constants.sol"; +import { NamespaceOwner } from "@latticexyz/world/src/codegen/tables/NamespaceOwner.sol"; +import { BaseTest } from "../../BaseTest.sol"; +import "../../../src/codegen/index.sol"; +import "../../../src/libraries/Libraries.sol"; +import { COMPLETED_ORDERS_THRESHOLDS, POINTS_THRESHOLDS } from "../../../src/constants.sol"; + +contract EscapeSystemTest is BaseTest { + function testAllRankNamesExist() public { + for ( + uint256 completedOrdersRank; + completedOrdersRank <= COMPLETED_ORDERS_THRESHOLDS().length; + completedOrdersRank++ + ) { + for (uint256 pointsRank; pointsRank <= POINTS_THRESHOLDS().length; pointsRank++) { + string memory rankName = EscapeRankName.get(completedOrdersRank, pointsRank); + assertGt(bytes(rankName).length, 0); + } + } + } + + function testEscape() public { + vm.startPrank(alice); + bytes32 playerEntity = world.spawn(); + world.start(); + vm.stopPrank(); + + _skipTutorial(playerEntity); + + vm.startPrank(alice); + world.name("test name"); + world.escapePod(); + vm.stopPrank(); + + assertEq(CarriedBy.get(playerEntity), bytes32(0)); + + uint256 tokenId = uint256(GameConfig.getGlobalEscapeIndex()); + IERC721 escapedStumpToken = IERC721(GameConfig.getEscapedStumpTokenAddress()); + assertEq(escapedStumpToken.ownerOf(tokenId), alice); + assertEq(EscapeIndexRanked.get(0, 0), 1); + } + + function _skipTutorial(bytes32 _playerEntity) private adminPrank { + TutorialLevel.set(_playerEntity, 3); + } +}