From 079eab45df09cc5b3c527a9bb6d63b07951d27de Mon Sep 17 00:00:00 2001 From: xingxinglian <71866430+xingxinglian@users.noreply.github.com> Date: Mon, 16 Oct 2023 18:04:13 +0800 Subject: [PATCH] chore: add loot system --- packages/contracts/mud.config.ts | 18 ++ packages/contracts/src/systems/GMSystem.sol | 2 +- packages/contracts/src/systems/LootSystem.sol | 220 ++++++++++++++++ .../src/systems/library/LootSuit.sol | 238 ++++++++++++++++++ 4 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 packages/contracts/src/systems/LootSystem.sol create mode 100644 packages/contracts/src/systems/library/LootSuit.sol diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index dc63e8b0..03f8df31 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -166,6 +166,24 @@ export default mudConfig({ opened: "bool", owner: "address", } + }, + Loot : { + keySchema: { + tokenId: "uint256" + }, + schema: { + randomId: "uint256", + owner: "address", + state: "RandomState", + weapon: "string", + chest: "string", + head: "string", + waist: "string", + foot: "string", + hand: "string", + neck: "string", + ring: "string", + } } } }); diff --git a/packages/contracts/src/systems/GMSystem.sol b/packages/contracts/src/systems/GMSystem.sol index a1a66788..5ba4a5e3 100644 --- a/packages/contracts/src/systems/GMSystem.sol +++ b/packages/contracts/src/systems/GMSystem.sol @@ -5,7 +5,7 @@ import { System } from "@latticexyz/world/src/System.sol"; import { Season, GameConfig, BoxList} from "../codegen/Tables.sol"; import { GAME_CONFIG_KEY } from "../Constants.sol"; -contract GMSystem { +contract GMSystem is System { bytes32 constant MAP_KEY = keccak256("Season-Key"); // season diff --git a/packages/contracts/src/systems/LootSystem.sol b/packages/contracts/src/systems/LootSystem.sol new file mode 100644 index 00000000..f56d9b61 --- /dev/null +++ b/packages/contracts/src/systems/LootSystem.sol @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { System } from "@latticexyz/world/src/System.sol"; +import { Season, GameConfig, BoxList} from "../codegen/Tables.sol"; +import { GAME_CONFIG_KEY } from "../Constants.sol"; + +contract LootSystem is System { + // constructor( + // string memory _desc, + // string memory symbol, + // string memory name, + // string memory _notRevealedInfo, + // uint256 _waitBlockCount + // ) ERC721(symbol, name) { + // desc = _desc; + // owner = msg.sender; + // waitBlockCount = _waitBlockCount; + // notRevealedInfo = _notRevealedInfo; + // } + + + uint256 public tokenId; + uint256 public waitBlockCount; + + + address owner; + string desc; + string notRevealedInfo; + + // mapping(uint256 => Loot) public lootList; + + // modifier onlyOwner() { + // require(msg.sender == owner); + // _; + // } + + + function tokenURI( + uint256 _tokenId + ) public view override returns (string memory) { + string[17] memory parts; + Loot memory loot = lootList[_tokenId]; + if(loot.state == RandomState.Pending){ + string memory r = string( + abi.encodePacked( + "data:application/json;base64,", + notRevealedInfo + ) + ); + return r; + } + require(loot.state != RandomState.Inited, "Box not existed"); + + parts[ + 0 + ] = ''; + + parts[1] = loot.Weapon; + + parts[2] = ''; + + parts[3] = loot.Chest; + + parts[4] = ''; + + parts[5] = loot.Head; + + parts[6] = ''; + + parts[7] = loot.Waist; + + parts[8] = ''; + + parts[9] = loot.Foot; + + parts[10] = ''; + + parts[11] = loot.Hand; + + parts[12] = ''; + + parts[13] = loot.Neck; + + parts[14] = ''; + + parts[15] = loot.Ring; + + parts[16] = ""; + + string memory output = string( + abi.encodePacked( + parts[0], + parts[1], + parts[2], + parts[3], + parts[4], + parts[5], + parts[6], + parts[7], + parts[8] + ) + ); + output = string( + abi.encodePacked( + output, + parts[9], + parts[10], + parts[11], + parts[12], + parts[13], + parts[14], + parts[15], + parts[16] + ) + ); + + string memory json = Base64.encode( + bytes( + string( + abi.encodePacked( + '{"name": "MLoot #', + _tokenId.toString(), + '", "description":"', + desc, + '","image": "data:image/svg+xml;base64,', + Base64.encode(bytes(output)), + '"}' + ) + ) + ) + ); + output = string( + abi.encodePacked("data:application/json;base64,", json) + ); + + return output; + } + + function luck( + uint8 rand, + string[] memory sourceArray + ) internal view returns (string memory) { + string memory output = sourceArray[rand % sourceArray.length]; + + uint256 greatness = rand % 21; + if (greatness > 14) { + output = string( + abi.encodePacked(output, " ", suffixes[rand % suffixes.length]) + ); + } + if (greatness >= 19) { + string[2] memory name; + name[0] = namePrefixes[rand % namePrefixes.length]; + name[1] = nameSuffixes[rand % nameSuffixes.length]; + if (greatness == 19) { + output = string( + abi.encodePacked('"', name[0], " ", name[1], '" ', output) + ); + } else { + output = string( + abi.encodePacked( + '"', + name[0], + " ", + name[1], + '" ', + output, + " +1" + ) + ); + } + } + return output; + } + + function revealNFT(uint256 _tokenId) external { + Loot storage loot = lootList[_tokenId]; + require(loot.owner == msg.sender, "only owner can reveal the box"); + uint8[] memory random_numbers = getRandom(loot.randomId, 8,waitBlockCount); + loot.Weapon = luck(random_numbers[0], weapons); + loot.Chest = luck(random_numbers[1], chestArmor); + loot.Head = luck(random_numbers[2], headArmor); + loot.Waist = luck(random_numbers[3], waistArmor); + loot.Foot = luck(random_numbers[4], footArmor); + loot.Hand = luck(random_numbers[5], handArmor); + loot.Neck = luck(random_numbers[6], necklaces); + loot.Ring = luck(random_numbers[7], rings); + loot.state = RandomState.Confirmed; + } + + function mint() external { + // init loot box + Loot storage loot = lootList[tokenId]; + loot.owner = msg.sender; + loot.state = RandomState.Pending; + loot.randomId = randomId; + requestRandom(randomId); + _mint(msg.sender, tokenId); + tokenId++; + randomId++; + } + + + + + function withdraw(address to) public { + uint256 balance = address(this).balance; + require(balance > 0, "sufficient funds"); + payable(to).transfer(balance); + } + + function withdrawErc20( + address _targetAddress, + address _contractAddress, + uint256 _amount + ) external onlyOwner { + IERC20(_contractAddress).transfer(_targetAddress, _amount); + } +} diff --git a/packages/contracts/src/systems/library/LootSuit.sol b/packages/contracts/src/systems/library/LootSuit.sol new file mode 100644 index 00000000..792a6a13 --- /dev/null +++ b/packages/contracts/src/systems/library/LootSuit.sol @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract LootSuit { + + string[] internal weapons = [ + "Warhammer", + "Quarterstaff", + "Maul", + "Mace", + "Club", + "Katana", + "Falchion", + "Scimitar", + "Long Sword", + "Short Sword", + "Ghost Wand", + "Grave Wand", + "Bone Wand", + "Wand", + "Grimoire", + "Chronicle", + "Tome", + "Book" + ]; + + string[] internal chestArmor = [ + "Divine Robe", + "Silk Robe", + "Linen Robe", + "Robe", + "Shirt", + "Demon Husk", + "Dragonskin Armor", + "Studded Leather Armor", + "Hard Leather Armor", + "Leather Armor", + "Holy Chestplate", + "Ornate Chestplate", + "Plate Mail", + "Chain Mail", + "Ring Mail" + ]; + + string[] internal headArmor = [ + "Ancient Helm", + "Ornate Helm", + "Great Helm", + "Full Helm", + "Helm", + "Demon Crown", + "Dragon's Crown", + "War Cap", + "Leather Cap", + "Cap", + "Crown", + "Divine Hood", + "Silk Hood", + "Linen Hood", + "Hood" + ]; + + string[] internal waistArmor = [ + "Ornate Belt", + "War Belt", + "Plated Belt", + "Mesh Belt", + "Heavy Belt", + "Demonhide Belt", + "Dragonskin Belt", + "Studded Leather Belt", + "Hard Leather Belt", + "Leather Belt", + "Brightsilk Sash", + "Silk Sash", + "Wool Sash", + "Linen Sash", + "Sash" + ]; + + string[] internal footArmor = [ + "Holy Greaves", + "Ornate Greaves", + "Greaves", + "Chain Boots", + "Heavy Boots", + "Demonhide Boots", + "Dragonskin Boots", + "Studded Leather Boots", + "Hard Leather Boots", + "Leather Boots", + "Divine Slippers", + "Silk Slippers", + "Wool Shoes", + "Linen Shoes", + "Shoes" + ]; + + string[] internal handArmor = [ + "Holy Gauntlets", + "Ornate Gauntlets", + "Gauntlets", + "Chain Gloves", + "Heavy Gloves", + "Demon's Hands", + "Dragonskin Gloves", + "Studded Leather Gloves", + "Hard Leather Gloves", + "Leather Gloves", + "Divine Gloves", + "Silk Gloves", + "Wool Gloves", + "Linen Gloves", + "Gloves" + ]; + + string[] internal necklaces = ["Necklace", "Amulet", "Pendant"]; + + string[] internal rings = [ + "Gold Ring", + "Silver Ring", + "Bronze Ring", + "Platinum Ring", + "Titanium Ring" + ]; + + string[] internal suffixes = [ + "of Power", + "of Giants", + "of Titans", + "of Skill", + "of Perfection", + "of Brilliance", + "of Enlightenment", + "of Protection", + "of Anger", + "of Rage", + "of Fury", + "of Vitriol", + "of the Fox", + "of Detection", + "of Reflection", + "of the Twins" + ]; + + string[] internal namePrefixes = [ + "Agony", + "Apocalypse", + "Armageddon", + "Beast", + "Behemoth", + "Blight", + "Blood", + "Bramble", + "Brimstone", + "Brood", + "Carrion", + "Cataclysm", + "Chimeric", + "Corpse", + "Corruption", + "Damnation", + "Death", + "Demon", + "Dire", + "Dragon", + "Dread", + "Doom", + "Dusk", + "Eagle", + "Empyrean", + "Fate", + "Foe", + "Gale", + "Ghoul", + "Gloom", + "Glyph", + "Golem", + "Grim", + "Hate", + "Havoc", + "Honour", + "Horror", + "Hypnotic", + "Kraken", + "Loath", + "Maelstrom", + "Mind", + "Miracle", + "Morbid", + "Oblivion", + "Onslaught", + "Pain", + "Pandemonium", + "Phoenix", + "Plague", + "Rage", + "Rapture", + "Rune", + "Skull", + "Sol", + "Soul", + "Sorrow", + "Spirit", + "Storm", + "Tempest", + "Torment", + "Vengeance", + "Victory", + "Viper", + "Vortex", + "Woe", + "Wrath", + "Light's", + "Shimmering" + ]; + + string[] internal nameSuffixes = [ + "Bane", + "Root", + "Bite", + "Song", + "Roar", + "Grasp", + "Instrument", + "Glow", + "Bender", + "Shadow", + "Whisper", + "Shout", + "Growl", + "Tear", + "Peak", + "Form", + "Sun", + "Moon" + ]; +} \ No newline at end of file