diff --git a/public/manifest.json b/public/manifest.json index 3c8b1f2b..f2ee4063 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -2,27 +2,47 @@ "characters":[ { "name": "Anata", - "description": "Anata", + "description": "Owned Customizer", "portrait": "./assets/portraitImages/anata.png", "manifest":"./character-assets/anata/manifest.json", "icon": "./assets/icons/class-neural-hacker.svg", - "format": "vrm" + "format": "vrm", + "collectionLock":"the-anata-nft", + "manifestAppend":[ + { + "name": "Loot", + "description": "Adventurere's loot", + "manifest":"./loot-assets/anata/female/manifest.json", + "collectionLock":"lootproject" + } + ] }, { - "name": "Anata Male", - "description": "Anata Male", + "name": "Anata", + "description": "Full Customizer", "portrait": "./assets/portraitImages/anata_male.png", "manifest":"./character-assets/anata_male/manifest.json", - "icon": "|", - "format": "vrm" + "icon": "./assets/icons/class-neural-hacker.svg", + "format": "vrm", + "collectionLock":"the-anata-nft", + "fullTraits":true }, { - "name": "Anata Male", - "description": "Anata Male", - "portrait": "./assets/portraitImages/anata_male.png", - "manifest":"./character-assets/test.json", - "icon": "|", - "format": "vrm" + "name": "Tubbies", + "description": "Tubbies", + "portrait": "./assets/portraitImages/anata.png", + "manifest":"./tubby/manifest.json", + "icon": "./assets/icons/class-neural-hacker.svg", + "format": "vrm", + "manifestAppend":[ + { + "name": "Loot", + "description": "Adventurere's loot", + "manifest":"./loot-assets/tubbycats/manifest.json", + "collectionLock":"lootproject", + "fullTraits":true + } + ] } ], "loras":[ @@ -59,26 +79,26 @@ "defaultAnimations":[ { "name": "T-Pose", - "description": "T-Pose", + "description": "1_T-Pose", "location":"./animations/T-Pose.fbx", "icon": "|" }, { - "name": "Dancing", + "name": "Idle", "description": "Basic Dance Animation", - "location":"./animations/Dancing.fbx", + "location":"./animations/2_Idle.fbx", "icon": "|" }, { "name": "Walking", "description": "Basic Walk Animation", - "location":"./animations/Walking.fbx", + "location":"./animations/3_Walking.fbx", "icon": "|" }, { "name": "Waving", "description": "Basic Waving Animation", - "location":"./animations/Waving.fbx", + "location":"./animations/4_Waving.fbx", "icon": "|" } ] diff --git a/src/library/CharacterManifestData.js b/src/library/CharacterManifestData.js index 40796001..ade283dc 100644 --- a/src/library/CharacterManifestData.js +++ b/src/library/CharacterManifestData.js @@ -1,7 +1,7 @@ import { getAsArray } from "./utils"; export class CharacterManifestData{ - constructor(manifest){ + constructor(manifest, unlockedTraits = null){ const { assetsLocation, traitsDirectory, @@ -99,19 +99,19 @@ export class CharacterManifestData{ } defaultOptions(); - + // create texture and color traits first this.textureTraits = []; this.textureTraitsMap = null; - this.createTextureTraits(textureCollections); + this.createTextureTraits(textureCollections, false, unlockedTraits); this.colorTraits = []; this.colorTraitsMap = null; - this.createColorTraits(colorCollections); + this.createColorTraits(colorCollections, false, unlockedTraits); this.modelTraits = []; this.modelTraitsMap = null; - this.createModelTraits(traits); + this.createModelTraits(traits, false, unlockedTraits); } appendManifestData(manifestData, replaceExisting){ manifestData.textureTraits.forEach(newTextureTraitGroup => { @@ -361,11 +361,10 @@ export class CharacterManifestData{ // Given an array of traits, saves an array of TraitModels - createModelTraits(modelTraits, replaceExisting = false){ + createModelTraits(modelTraits, replaceExisting = false, unlockedTraits = null){ if (replaceExisting) this.modelTraits = []; - getAsArray(modelTraits).forEach(traitObject => { - this.modelTraits.push(new TraitModelsGroup(this, traitObject)) + this.modelTraits.push(new TraitModelsGroup(this, traitObject, unlockedTraits)) }); this.modelTraitsMap = new Map(this.modelTraits.map(item => [item.trait, item])); @@ -382,21 +381,21 @@ export class CharacterManifestData{ }); } - createTextureTraits(textureTraits, replaceExisting = false){ + createTextureTraits(textureTraits, replaceExisting = false, unlockedTraits = null){ if (replaceExisting) this.textureTraits = []; getAsArray(textureTraits).forEach(traitObject => { - this.textureTraits.push(new TraitTexturesGroup(this, traitObject)) + this.textureTraits.push(new TraitTexturesGroup(this, traitObject, unlockedTraits)) }); this.textureTraitsMap = new Map(this.textureTraits.map(item => [item.trait, item])); } - createColorTraits(colorTraits, replaceExisting = false){ + createColorTraits(colorTraits, replaceExisting = false, unlockedTraits = null){ if (replaceExisting) this.colorTraits = []; getAsArray(colorTraits).forEach(traitObject => { - this.colorTraits.push(new TraitColorsGroup(this, traitObject)) + this.colorTraits.push(new TraitColorsGroup(this, traitObject, unlockedTraits)) }); this.colorTraitsMap = new Map(this.colorTraits.map(item => [item.trait, item])); @@ -407,7 +406,7 @@ export class CharacterManifestData{ // Must be created AFTER color collections and texture collections have been created class TraitModelsGroup{ - constructor(manifestData, options){ + constructor(manifestData, options, unlockedTraits = null){ const { trait, name, @@ -420,7 +419,7 @@ class TraitModelsGroup{ restrictedTypes = [] } = options; this.manifestData = manifestData; - + this.isRequired = manifestData.requiredTraits.indexOf(trait) !== -1; this.trait = trait; this.name = name; @@ -436,7 +435,15 @@ class TraitModelsGroup{ this.collection = []; this.collectionMap = null; - this.createCollection(collection); + if (unlockedTraits == null){ + this.createCollection(collection); + } + else{ + const finalUnlockedTraits = Array.isArray(unlockedTraits) ? unlockedTraits : unlockedTraits[trait] + console.log("UNLOCKED", finalUnlockedTraits,); + this.createCollection(collection, false, finalUnlockedTraits || []); + } + } appendCollection(modelTraitGroup, replaceExisting){ @@ -467,12 +474,21 @@ class TraitModelsGroup{ } } - createCollection(itemCollection, replaceExisting = false){ + createCollection(itemCollection, replaceExisting = false, unlockedTraits = null){ if (replaceExisting) this.collection = []; - getAsArray(itemCollection).forEach(item => { - this.collection.push(new ModelTrait(this, item)) - }); + if (unlockedTraits == null){ + getAsArray(itemCollection).forEach(item => { + this.collection.push(new ModelTrait(this, item)) + }); + } + else{ + getAsArray(itemCollection).forEach(item => { + if (unlockedTraits.includes(item.id)){ + this.collection.push(new ModelTrait(this, item)) + } + }); + } this.collectionMap = new Map(this.collection.map(item => [item.id, item])); } @@ -506,7 +522,7 @@ class TraitModelsGroup{ } class TraitTexturesGroup{ - constructor(manifestData, options){ + constructor(manifestData, options, unlockedTraits = null){ const { trait, collection @@ -516,7 +532,13 @@ class TraitTexturesGroup{ this.collection = []; this.collectionMap = null; - this.createCollection(collection); + + if (unlockedTraits == null){ + this.createCollection(collection); + } + else{ + this.createCollection(collection, false, unlockedTraits[trait] || []); + } } @@ -543,12 +565,21 @@ class TraitTexturesGroup{ } }); } - createCollection(itemCollection, replaceExisting = false){ + createCollection(itemCollection, replaceExisting = false, unlockedTraits = null){ if (replaceExisting) this.collection = []; - getAsArray(itemCollection).forEach(item => { - this.collection.push(new TextureTrait(this, item)) - }); + if (unlockedTraits == null){ + getAsArray(itemCollection).forEach(item => { + this.collection.push(new TextureTrait(this, item)) + }); + } + else{ + getAsArray(itemCollection).forEach(item => { + if (unlockedTraits.includes(item.id)){ + this.collection.push(new TextureTrait(this, item)) + } + }); + } this.collectionMap = new Map(this.collection.map(item => [item.id, item])); } @@ -567,7 +598,7 @@ class TraitTexturesGroup{ } } class TraitColorsGroup{ - constructor(manifestData, options){ + constructor(manifestData, options, unlockedTraits = null){ const { trait, collection @@ -577,7 +608,13 @@ class TraitColorsGroup{ this.collection = []; this.collectionMap = null; - this.createCollection(collection); + + if (unlockedTraits == null){ + this.createCollection(collection); + } + else{ + this.createCollection(collection, false, unlockedTraits[trait] || []); + } } appendCollection(colorTraitGroup, replaceExisting){ @@ -602,12 +639,21 @@ class TraitColorsGroup{ } }); } - createCollection(itemCollection, replaceExisting = false){ + createCollection(itemCollection, replaceExisting = false, unlockedTraits = null){ if (replaceExisting) this.collection = []; - getAsArray(itemCollection).forEach(item => { - this.collection.push(new ColorTrait(this, item)) - }); + if (unlockedTraits == null){ + getAsArray(itemCollection).forEach(item => { + this.collection.push(new ColorTrait(this, item)) + }); + } + else{ + getAsArray(itemCollection).forEach(item => { + if (unlockedTraits.includes(item.id)){ + this.collection.push(new ColorTrait(this, item)) + } + }); + } this.collectionMap = new Map(this.collection.map(item => [item.id, item])); } diff --git a/src/library/characterManager.js b/src/library/characterManager.js index 045afbaa..d1559eac 100644 --- a/src/library/characterManager.js +++ b/src/library/characterManager.js @@ -702,10 +702,11 @@ export class CharacterManager { * Sets an existing manifest data for the character. * * @param {object} manifest - The loaded mmanifest object. + * @param {Array} unlockedTraits - Optional string array of the traits that will be unlocked, if none set, all traits will be unlocked. * @returns {Promise} A Promise that resolves when the manifest is successfully loaded, * or rejects with an error message if loading fails. */ - setManifest(manifest){ + setManifest(manifest, unlockedTraits = null){ this.removeCurrentCharacter(); return new Promise(async (resolve, reject) => { try{ @@ -713,7 +714,7 @@ export class CharacterManager { this.manifest = manifest; if (this.manifest) { // Create a CharacterManifestData instance based on the fetched manifest - this.manifestData = new CharacterManifestData(this.manifest); + this.manifestData = new CharacterManifestData(this.manifest, unlockedTraits); // If an animation manager is available, set it up if (this.animationManager) { @@ -745,7 +746,7 @@ export class CharacterManager { }) } - appendManifest(manifest, replaceExisting){ + appendManifest(manifest, replaceExisting, unlockedTraits= null){ return new Promise(async (resolve, reject) => { try{ if (replaceExisting) @@ -754,7 +755,7 @@ export class CharacterManager { this.manifest = {manifest, ...(this.manifest || {})}; // Create a CharacterManifestData instance based on the fetched manifest - const manifestData = new CharacterManifestData(manifest); + const manifestData = new CharacterManifestData(manifest, unlockedTraits); this.manifestData.appendManifestData(manifestData); // Resolve the Promise (without a value, as you mentioned it's not needed) @@ -772,17 +773,18 @@ export class CharacterManager { * Loads the manifest data for the character. * * @param {string} url - The URL of the manifest. + * @param {Array} unlockedTraits - Optional string array of the traits that will be unlocked, if none set, all traits will be unlocked. * @returns {Promise} A Promise that resolves when the manifest is successfully loaded, * or rejects with an error message if loading fails. */ - loadManifest(url) { + loadManifest(url, unlockedTraits= null) { // remove in case character was loaded return new Promise(async (resolve, reject) => { try { // Fetch the manifest data asynchronously const manifest = await this._fetchManifest(url); - this.setManifest(manifest).then(()=>{ + this.setManifest(manifest, unlockedTraits).then(()=>{ resolve(); }) @@ -801,14 +803,14 @@ export class CharacterManager { * @returns {Promise} A Promise that resolves when the manifest is successfully loaded, * or rejects with an error message if loading fails. */ - loadAppendManifest(url, replaceExisting){ + loadAppendManifest(url, replaceExisting, unlockedTraits= null){ // remove in case character was loaded return new Promise(async (resolve, reject) => { try { // Fetch the manifest data asynchronously const manifest = await this._fetchManifest(url); - this.appendManifest(manifest, replaceExisting).then(()=>{ + this.appendManifest(manifest, replaceExisting, unlockedTraits).then(()=>{ resolve(); }) @@ -858,7 +860,7 @@ export class CharacterManager { } async _loadTraits(options, fullAvatarReplace = false){ - console.log("laoded traits:", options) + console.log("loaded traits:", options) await this.traitLoadManager.loadTraitOptions(getAsArray(options)).then(loadedData=>{ if (fullAvatarReplace){ // add null loaded options to existingt traits to remove them; diff --git a/src/library/mint-utils.js b/src/library/mint-utils.js index e29db9f7..b6a8bbae 100644 --- a/src/library/mint-utils.js +++ b/src/library/mint-utils.js @@ -13,6 +13,46 @@ const chainId = "0x89"; let tokenPrice; +// setTimeout(() => { +// console.log("t") +// getContract("0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7"); +// }, 5000); + + + +async function getContract(address) { + // const contractAddress = address; // Loot NFT contract address + // const tokenId = 1; // Replace with the desired token ID + + // // ABI for a typical ERC721 contract (simplified) + // const abi = [ + // "function tokenURI(uint256 tokenId) view returns (string)" + // ]; + + // const key = await import.meta.env.ALCHEMY_API_KEY; + // const defaultProvider = new ethers.providers.AlchemyProvider('mainnet', key); + + // //const defaultProvider = new ethers.providers.AlchemyProvider('mainnet', key); + // // Use Ethereum mainnet provider + // //const defaultProvider = ethers.getDefaultProvider('mainnet'); + // //const defaultProvider = new ethers.providers.StaticJsonRpcProvider('https://polygon-rpc.com/') + // console.log(defaultProvider); + // try { + // // Connect to the contract + // const contract = new ethers.Contract(contractAddress, abi, defaultProvider); + // console.log("Contract instance:", contract); + + // // Fetch the token URI (metadata URL) + // const tokenURI = await contract.tokenURI(tokenId); + // console.log("Token URI:", tokenURI); + + // // Handle the metadata (your existing logic continues here) + // } catch (error) { + // console.error("Error fetching metadata:", error); + // } +} + + async function getTokenPrice(){ if (tokenPrice != null) return tokenPrice @@ -31,14 +71,14 @@ async function getTokenPrice(){ * @returns {Promise} A Promise that resolves with the JSON response from the Opensea API. */ export function getOpenseaCollection(address, collection) { + console.log("GETTING COLLECTION", collection); const options = { method: 'GET', headers: { accept: 'application/json', 'x-api-key': opensea_Key }, }; - console.log(options); // Returning a Promise return new Promise((resolve, reject) => { - fetch('https://api.opensea.io/api/v2/chain/ethereum/account/' + address + '/nfts?collection=' + collection, options) + fetch('https://api.opensea.io/api/v2/chain/ethereum/account/' + address + '/nfts?limit=200&collection=' + collection, options) .then(response => { // Check if the response status is ok (2xx range) if (response.ok) { @@ -59,6 +99,46 @@ export function getOpenseaCollection(address, collection) { }); } +export function ownsCollection(address, collection){ + const options = { + method: 'GET', + headers: { accept: 'application/json', 'x-api-key': opensea_Key }, + }; + // Returning a Promise + return new Promise((resolve, reject) => { + fetch('https://api.opensea.io/api/v2/chain/ethereum/account/' + address + '/nfts?limit=1&collection=' + collection, options) + .then(response => { + // Check if the response status is ok (2xx range) + if (response.ok) { + return response.json(); + } else { + // If the response status is not ok, reject the Promise with an error message + reject('Failed to fetch data from Opensea API'); + } + }) + .then(response => { + // Resolve the Promise with the JSON response + resolve(response.nfts.length>0); + }) + .catch(err => { + // Reject the Promise with the error encountered during the fetch + reject(err); + }); + }); +} + +export async function currentWallet(){ + console.log("get") + const chain = await window.ethereum.request({ method: 'eth_chainId' }) + if (parseInt(chain, 16) == parseInt(chainId, 16)) { + const addressArray = await window.ethereum.request({ + method: 'eth_requestAccounts', + }) + console.log(addressArray); + return addressArray.length > 0 ? addressArray[0] : "" + } + return ""; +} // ready to test export async function connectWallet(){ @@ -70,6 +150,7 @@ export async function connectWallet(){ const addressArray = await window.ethereum.request({ method: 'eth_requestAccounts', }) + console.log(addressArray); return addressArray.length > 0 ? addressArray[0] : "" } else { try { diff --git a/src/pages/Create.jsx b/src/pages/Create.jsx index e96ba901..ed37abde 100644 --- a/src/pages/Create.jsx +++ b/src/pages/Create.jsx @@ -9,6 +9,12 @@ import { SceneContext } from "../context/SceneContext" import { SoundContext } from "../context/SoundContext" import { AudioContext } from "../context/AudioContext" +import { local } from "../library/store" + +import { connectWallet, getOpenseaCollection, ownsCollection, currentWallet } from "../library/mint-utils" + +import { getAsArray } from "../library/utils" + function Create() { // Translate hook @@ -19,10 +25,41 @@ function Create() { const { isMute } = React.useContext(AudioContext) const { manifest, characterManager } = React.useContext(SceneContext) const [ classes, setClasses ] = useState([]) + const [ collections, setCollections ] = useState([]) + const [ currentAddress, setCurrentAddress] = useState(""); + const [ ownedCollections, setOwnedCollections] = useState(null); + const [ enabledRefresh, setEnabledRefresh] = useState(true); + let loaded = false + let [isLoaded, setIsLoaded] = useState(false) + let [requireWalletConnect, setRequireWalletConnect] = useState(false) + + useEffect(()=>{ + if (requireWalletConnect == true){ + if (loaded || isLoaded) return + setIsLoaded(true) + loaded = true; + if (currentAddress == ""){ + const getWallet = async ()=>{ + const wallet = await currentWallet(); + setCurrentAddress(wallet); + if (wallet != ""){ + await fetchWalletNFTS(); + } + } + getWallet(); + } + } + },[requireWalletConnect]) useEffect(() => { if (manifest?.characters != null){ + let requiresConnect = false; const manifestClasses = manifest.characters.map((c) => { + console.log("APPEND", c.manifestAppend); + let enabled = c.collectionLock == null ? false : true; + if (c.collectionLock != null) + requiresConnect = true; + console.log(c.collectionLock) return { name:c.name, image:c.portrait, @@ -30,10 +67,25 @@ function Create() { manifest: c.manifest, icon:c.icon, format:c.format, - disabled:false + disabled:enabled, + collection: getAsArray(c.collectionLock), + manifestAppend: getAsArray(c.manifestAppend), + fullTraits: c.fullTraits || false } }) + + const nonRepeatingCollections = []; + const seenCollections = new Set(); + manifest.characters.forEach((c) => { + if (c.collectionLock != null && !seenCollections.has(c.collectionLock)) { + nonRepeatingCollections.push(c.collectionLock); + seenCollections.add(c.collectionLock); + } + }); + setCollections(nonRepeatingCollections); setClasses(manifestClasses); + setRequireWalletConnect(requiresConnect); + } }, [manifest]) @@ -42,11 +94,102 @@ function Create() { !isMute && playSound('backNextButton'); } + const getNftsMeta = async(nfts) =>{ + const nftsMeta = []; + const promises = nfts.map(nft => { + return new Promise((resolve)=>{ + fetch(nft.metadata_url) + .then(response=>{ + response.json() + .then(metadata=>{ + nftsMeta.push(metadata); + resolve (); + }) + .catch(err=>{ + console.warn("error converting to json"); + console.error(err); + resolve () + }) + }) + .catch(err=>{ + // resolve even if it fails, to avoid complete freeze + console.warn("error getting " + nft.metadata_url + ", skpping") + console.error(err); + resolve () + }) + }) + + } + ); + + await Promise.all(promises); + console.log("result", nfts, nftsMeta); + return nftsMeta; + } + const selectClass = async (index) => { setIsLoading(true) + console.log(classes[index]); // Load manifest first - characterManager.loadManifest(manifest.characters[index].manifest).then(()=>{ + let unlockedTraits = null; + const selectedClass = classes[index]; + if (selectedClass.collection.length > 0 && selectedClass.fullTraits == false){ + console.log("got 1") + const address = await connectWallet(); + const result = await getOpenseaCollection(address,selectedClass.collection[0]) + const nfts = getAsArray(result?.nfts); + console.log("nfts", result?.nfts); + + const nftsMeta = await getNftsMeta(nfts); + console.log(nftsMeta); + + unlockedTraits = {}; + const getTraitsFromNFTsArray = (arr) =>{ + const nftArr = getAsArray(arr); + nftArr.forEach(nft => { + nft.attributes.forEach(attr => { + if (unlockedTraits[attr.trait_type] == null) + unlockedTraits[attr.trait_type] = [] + if (!unlockedTraits[attr.trait_type].includes(attr.value)) + unlockedTraits[attr.trait_type].push(attr.value); + }); + + }); + } + getTraitsFromNFTsArray(nftsMeta); + + console.log(unlockedTraits) + // unlockedTraits + } + + characterManager.loadManifest(manifest.characters[index].manifest, unlockedTraits).then(async()=>{ setViewMode(ViewMode.APPEARANCE) + + if (selectedClass.manifestAppend.length > 0){ + console.log("getting only first one for now") + const address = await connectWallet(); + const addressTest = "0x2333FCc3833D2E951Ce8e821235Ed3B729141996"; + const result = await getOpenseaCollection(addressTest,selectedClass.manifestAppend[0].collectionLock) + const nfts = getAsArray(result?.nfts); + console.log("append nfts", nfts); + const nftsMeta = await getNftsMeta(nfts); + const decodedSVG = atob(nftsMeta[0].image.split(",")[1]); + // Parse the decoded SVG + const parser = new DOMParser(); + const svgDoc = parser.parseFromString(decodedSVG, "image/svg+xml"); + + // Extract text content from the SVG + const texts = [...svgDoc.querySelectorAll("text")].map(text => text.textContent); + + console.log("selclass", selectedClass.manifestAppend[0]) + //characterManager.loadAppendManifest(selectedClass.manifestAppend[0].manifest, false) + const allowAllTraits = selectedClass.manifestAppend[0].fullTraits || false; + console.log(allowAllTraits) + characterManager.loadAppendManifest(selectedClass.manifestAppend[0].manifest, false, allowAllTraits ? null : texts) + console.log(texts); + } + + // When Manifest is Loaded, load initial traits from given manifest characterManager.loadInitialTraits().then(()=>{ setIsLoading(false) @@ -55,7 +198,78 @@ function Create() { !isMute && playSound('classSelect'); } - const hoverClass = () => { + useEffect(()=>{ + if (ownedCollections != null){ + + const editedClasses = classes.map((c) => { + let locked = c.collection.length > 0 ? true : false; + for (let i =0; i < c.collection.length;i++){ + const collection = c.collection[i]; + if (ownedCollections[collection] == true ){ + locked = false; + break; + } + } + return { + name:c.name, + image:c.image, + description: c.description, + manifest: c.manifest, + icon:c.icon, + format:c.format, + disabled:locked, + collection: c.collection, + manifestAppend: c.manifestAppend, + fullTraits: c.fullTraits + }}); + + console.log(editedClasses) + setClasses(editedClasses); + } + }, [ownedCollections]) + + const fetchWalletNFTS = async(getLocal = true)=>{ + const address = await connectWallet() + if (address != ""){ + //console.log(local[address + "collections"]); + if (getLocal && local[address + "collections"] != null){ + setOwnedCollections(local[address + "collections"]); + } + else{ + // get it from opensea + console.log("from opensea") + setEnabledRefresh(false) + const owned = {}; + const promises = collections.map(collection => + ownsCollection(address, collection).then(result => { + owned[collection] = result; + }) + ); + + await Promise.all(promises); + + local[address + "collections"] = owned; + + setOwnedCollections(owned); + + setTimeout(() => { + setEnabledRefresh(true); + }, 5000); + } + + + // getAllCollections(address).then((result)=>{ + // // setWalletNFTs(result.nfts); + // console.log(result); + // }) + // getOpenseaCollection(address,'the-anata-nft').then((result)=>{ + // // setWalletNFTs(result.nfts); + // console.log(result.nfts); + // }) + } + } + + const hoverSound = () => { !isMute && playSound('classMouseOver'); } @@ -64,6 +278,8 @@ function Create() {
{t('pageTitles.chooseClass')}
+ +
@@ -78,13 +294,13 @@ function Create() { } onClick={ characterClass["disabled"] - ? null + ? () => fetchWalletNFTS() : () => selectClass(i) } onMouseOver={ characterClass["disabled"] - ? null - : () => hoverClass() + ? () => hoverSound() + : () => hoverSound() } >
+
+
fetchWalletNFTS(false): + ()=>{} + } + onMouseOver={ + () => hoverSound() + }/> +
+
{ - {opensea_Key && opensea_Key != "" && + { + // opensea_Key && opensea_Key != "" && } {/*