diff --git a/README.md b/README.md index 0b2bdc010..2e3a80468 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,12 @@ This code is open, but not open source. It is not licensed, which means you cann ## Currency Setup 1. Install [Ganache](https://www.trufflesuite.com/ganache). -1. For Ganache, choose Quickstart Ethereum. -1. Increase the gas limit in the workspace to `99999999` (or some other high number so you can deploy). -1. Install [MetaMask](https://metamask.io/). -1. Create a new connection to connect to Ganache with these settings: http://localhost:7545, any name, any chain id -1. In Ganache, click the key icon on the right side of any address and grab the private key. -1. In MetaMask, create a new account, import from private key, and paste the key in there. +2. For Ganache, choose Quickstart Ethereum. +3. Increase the gas limit in the workspace to `99999999` (or some other high number so you can deploy). +4. Install [MetaMask](https://metamask.io/). +5. Create a new connection to connect to Ganache with these settings: http://localhost:7545, any name, any chain id +6. In Ganache, click the key icon on the right side of any address and grab the private key. Make sure that chainID is 5777. +7. In MetaMask, create a new account, import from private key, and paste the key in there. You should now have 100 fake eth! You're now fake rich. @@ -118,7 +118,6 @@ If you get any issues during deployment, run: - Language is loaded on startup and added to the language drop-down of the Options page. - The value for the drop-down is "name" at the root of the json map. - ### i18n Manager App - Adding translations is easier with the use of [i18n-manager](https://www.electronjs.org/apps/i18n-manager) diff --git a/contracts/TokensManager.sol b/contracts/TokensManager.sol index a3b5ac448..9c4e08580 100644 --- a/contracts/TokensManager.sol +++ b/contracts/TokensManager.sol @@ -28,9 +28,7 @@ contract TokensManager is Initializable, AccessControlUpgradeable { require(hasRole(GAME_ADMIN, msg.sender)); } - function initialize( - address gameContract - ) public initializer { + function initialize(address gameContract) public initializer { __AccessControl_init_unchained(); _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); @@ -39,25 +37,73 @@ contract TokensManager is Initializable, AccessControlUpgradeable { offsetSlippage = 5; } - receive() external payable restricted { - } + receive() external payable restricted {} + + function teamFight( + uint256[] calldata char, + uint32[] calldata target, + uint8[] calldata fightMultiplier + ) external payable { + uint256 totalTokens; + uint256 totalExpectedTokens; + for (uint256 i = 0; i < char.length; i++) { + (uint256 tokens, uint256 expectedTokens) = game.fight( + msg.sender, + char[i], + target[i], + fightMultiplier[i] + ); + totalTokens += tokens; + totalExpectedTokens += expectedTokens; + } + //The following can be put outside the loop? I'm not 100% sure this is correct + uint256 offset = ABDKMath64x64.mulu( + getSkillToNativeRatio(), + totalExpectedTokens.mul(combatTokenChargePercent).div(100) + ); - function fight(uint256 char, uint32 target, uint8 fightMultiplier) external payable { - (uint256 tokens, uint256 expectedTokens) = game.fight(msg.sender, char, target, fightMultiplier); + require( + msg.value >= offset.mul(100 - offsetSlippage).div(100) && + msg.value <= offset.mul(100 + offsetSlippage).div(100), + "Offset error" + ); + if (totalTokens == 0) { + payable(msg.sender).transfer(msg.value); + } + } - uint256 offset = ABDKMath64x64.mulu(getSkillToNativeRatio(), expectedTokens.mul(combatTokenChargePercent).div(100)); + function fight( + uint256 char, + uint32 target, + uint8 fightMultiplier + ) external payable { + (uint256 tokens, uint256 expectedTokens) = game.fight( + msg.sender, + char, + target, + fightMultiplier + ); + + uint256 offset = ABDKMath64x64.mulu( + getSkillToNativeRatio(), + expectedTokens.mul(combatTokenChargePercent).div(100) + ); require( - msg.value >= offset.mul(100 - offsetSlippage).div(100) && msg.value <= offset.mul(100 + offsetSlippage).div(100), - 'Offset error' - ); + msg.value >= offset.mul(100 - offsetSlippage).div(100) && + msg.value <= offset.mul(100 + offsetSlippage).div(100), + "Offset error" + ); if (tokens == 0) { payable(msg.sender).transfer(msg.value); } } - function retrieve(address addressToTransferTo, uint256 amount) external restricted { + function retrieve( + address addressToTransferTo, + uint256 amount + ) external restricted { payable(addressToTransferTo).transfer(amount); } @@ -80,4 +126,4 @@ contract TokensManager is Initializable, AccessControlUpgradeable { function setOffsetSlippage(uint8 slippage) external restricted { offsetSlippage = slippage; } -} \ No newline at end of file +} diff --git a/frontend/src/store/combat/index.ts b/frontend/src/store/combat/index.ts index 1b1f15d7a..f7a21e509 100644 --- a/frontend/src/store/combat/index.ts +++ b/frontend/src/store/combat/index.ts @@ -150,12 +150,13 @@ const combat = { if (!TokensManager || !CryptoBlades || !rootState.defaultAccount) return; const res = await TokensManager.methods - .fight( - characterId, - targetString, - fightMultiplier - ) - .send({ from: rootState.defaultAccount, gasPrice: getGasPrice(), gas: '300000', value: +offsetCost * fightMultiplier }); + .fight(characterId, targetString, fightMultiplier) + .send({ + from: rootState.defaultAccount, + gasPrice: getGasPrice(), + gas: '300000', + value: +offsetCost * fightMultiplier, + }); let playerRoll = ''; let enemyRoll = ''; @@ -167,20 +168,125 @@ const combat = { toBlock: res.blockNumber, fromBlock: res.blockNumber }); - if (fightOutcomeEvents.length) { - playerRoll = fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.playerRoll; - enemyRoll = fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.enemyRoll; - xpGain = fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.xpGain; - skillGain = fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.skillGain; + playerRoll = + fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues + .playerRoll; + enemyRoll = + fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues + .enemyRoll; + xpGain = + fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.xpGain; + skillGain = + fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues + .skillGain; } - const {gasPrice} = await rootState.web3.eth.getTransaction(res.transactionHash); + const { gasPrice } = await rootState.web3.eth.getTransaction( + res.transactionHash + ); const bnbGasUsed = gasUsedToBnb(res.gasUsed, gasPrice); - await Promise.all([ - dispatch('combat/fetchTargets', {characterId}), - ]); + await Promise.all([dispatch('combat/fetchTargets', { characterId })]); + + return { + isVictory: parseInt(playerRoll, 10) >= parseInt(enemyRoll, 10), + playerRoll, + enemyRoll, + xpGain, + skillGain, + bnbGasUsed, + }; + }, + + async doEncountersPayNative( + { rootState, dispatch }: { rootState: IState; dispatch: Dispatch }, + { + charactersId, + targetsString, + fightMultiplier, + offsetCost, + }: { + charactersId: number[]; + targetsString: number[]; + fightMultiplier: number[]; + offsetCost: BigNumber; + } + ) { + const { TokensManager, CryptoBlades } = rootState.contracts(); + if (!TokensManager || !CryptoBlades || !rootState.defaultAccount) return; + const multiplier: string[] = []; + const characters: string[] = []; + const targets: string[] = []; + let totalOffset: BigNumber = offsetCost; + for (let i = 0; i < targetsString.length; i++) { + characters.push(charactersId[i].toString()); + targets.push(targetsString[i].toString()); + multiplier.push(fightMultiplier[i].toString()); + totalOffset = totalOffset.multipliedBy(fightMultiplier[i]); + } + + const res = await TokensManager.methods + .teamFight(characters, targets, multiplier) + .send({ + from: rootState.defaultAccount, + gasPrice: getGasPrice(), + gas: '800000', + //TODO this should have all the fightMultipliers + value: +offsetCost * fightMultiplier[0], + }); + + let playerRoll = ''; + let enemyRoll = ''; + let xpGain = 0; + let skillGain = 0; + + for (let i = 0; i < characters.length; i++) { + const fightOutcomeEvents = await CryptoBlades.getPastEvents( + 'FightOutcome', + { + filter: { + owner: rootState.defaultAccount!, + character: charactersId[i], + }, + toBlock: res.blockNumber, + fromBlock: res.blockNumber, + } + ); + if (fightOutcomeEvents.length) { + playerRoll += + fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues + .playerRoll; + enemyRoll += + fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues + .enemyRoll; + + xpGain += parseInt( + fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues + .xpGain, 10 + ); + + skillGain += parseInt( + fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues + .skillGain, 10 + ); + } + + if (i < charactersId.length - 1) { + playerRoll += ', '; + enemyRoll += ', '; + } + } + + const { gasPrice } = await rootState.web3.eth.getTransaction( + res.transactionHash + ); + + const bnbGasUsed = gasUsedToBnb(res.gasUsed, gasPrice); + + for (let i = 0; i < charactersId.length; i++) { + await dispatch('combat/fetchTargets', { characterId: charactersId[i] }); + } return { isVictory: parseInt(playerRoll, 10) >= parseInt(enemyRoll, 10), @@ -188,7 +294,7 @@ const combat = { enemyRoll, xpGain, skillGain, - bnbGasUsed + bnbGasUsed, }; }, diff --git a/frontend/src/views/Combat.vue b/frontend/src/views/Combat.vue index 68088e221..b4ef373db 100644 --- a/frontend/src/views/Combat.vue +++ b/frontend/src/views/Combat.vue @@ -140,6 +140,7 @@ +