Spaces:
Runtime error
Runtime error
pragma solidity 0.4.23; | |
library SafeMath { | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
assert(b <= a); | |
return a - b; | |
} | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
assert(c >= a); | |
return c; | |
} | |
} | |
contract Ownable { | |
address internal contractOwner; | |
constructor () internal { | |
if(contractOwner == address(0)){ | |
contractOwner = msg.sender; | |
} | |
} | |
modifier onlyOwner() { | |
require(msg.sender == contractOwner); | |
_; | |
} | |
function transferOwnership(address newOwner) public onlyOwner { | |
require(newOwner != address(0)); | |
contractOwner = newOwner; | |
} | |
} | |
contract Pausable is Ownable { | |
bool private paused = false; | |
modifier whenNotPaused() { | |
if(paused == true && msg.value > 0){ | |
msg.sender.transfer(msg.value); | |
} | |
require(!paused); | |
_; | |
} | |
function triggerPause() onlyOwner external { | |
paused = !paused; | |
} | |
} | |
contract ChampFactory is Pausable{ | |
event NewChamp(uint256 champID, address owner); | |
using SafeMath for uint; | |
struct Champ { | |
uint256 id; | |
uint256 attackPower; | |
uint256 defencePower; | |
uint256 cooldownTime; | |
uint256 readyTime; | |
uint256 winCount; | |
uint256 lossCount; | |
uint256 position; | |
uint256 price; | |
uint256 withdrawCooldown; | |
uint256 eq_sword; | |
uint256 eq_shield; | |
uint256 eq_helmet; | |
bool forSale; | |
} | |
struct AddressInfo { | |
uint256 withdrawal; | |
uint256 champsCount; | |
uint256 itemsCount; | |
string name; | |
} | |
struct Item { | |
uint8 itemType; | |
uint8 itemRarity; | |
uint256 attackPower; | |
uint256 defencePower; | |
uint256 cooldownReduction; | |
uint256 price; | |
uint256 onChampId; | |
bool onChamp; | |
bool forSale; | |
} | |
mapping (address => AddressInfo) public addressInfo; | |
mapping (uint256 => address) public champToOwner; | |
mapping (uint256 => address) public itemToOwner; | |
mapping (uint256 => string) public champToName; | |
Champ[] public champs; | |
Item[] public items; | |
uint256[] public leaderboard; | |
uint256 internal createChampFee = 5 finney; | |
uint256 internal lootboxFee = 5 finney; | |
uint256 internal pendingWithdrawal = 0; | |
uint256 private randNonce = 0; | |
uint256 public champsForSaleCount; | |
uint256 public itemsForSaleCount; | |
modifier onlyOwnerOfChamp(uint256 _champId) { | |
require(msg.sender == champToOwner[_champId]); | |
_; | |
} | |
modifier onlyNotOwnerOfChamp(uint256 _champId) { | |
require(msg.sender != champToOwner[_champId]); | |
_; | |
} | |
modifier isPaid(uint256 _price){ | |
require(msg.value >= _price); | |
_; | |
} | |
modifier contractMinBalanceReached(){ | |
require( (address(this).balance).sub(pendingWithdrawal) > 1000000 ); | |
_; | |
} | |
modifier isChampWithdrawReady(uint256 _id){ | |
require(champs[_id].withdrawCooldown < block.timestamp); | |
_; | |
} | |
modifier distributeInput(address _affiliateAddress){ | |
uint256 contractOwnerWithdrawal = (msg.value / 100) * 50; | |
addressInfo[contractOwner].withdrawal += contractOwnerWithdrawal; | |
pendingWithdrawal += contractOwnerWithdrawal; | |
if(_affiliateAddress != address(0) && _affiliateAddress != msg.sender){ | |
uint256 affiliateBonus = (msg.value / 100) * 25; | |
addressInfo[_affiliateAddress].withdrawal += affiliateBonus; | |
pendingWithdrawal += affiliateBonus; | |
} | |
_; | |
} | |
function getChampsByOwner(address _owner) external view returns(uint256[]) { | |
uint256[] memory result = new uint256[](addressInfo[_owner].champsCount); | |
uint256 counter = 0; | |
for (uint256 i = 0; i < champs.length; i++) { | |
if (champToOwner[i] == _owner) { | |
result[counter] = i; | |
counter++; | |
} | |
} | |
return result; | |
} | |
function getChampsCount() external view returns(uint256){ | |
return champs.length; | |
} | |
function getChampReward(uint256 _position) public view returns(uint256) { | |
if(_position <= 800){ | |
uint256 rewardPercentage = uint256(2000).sub(2 * (_position - 1)); | |
uint256 availableWithdrawal = address(this).balance.sub(pendingWithdrawal); | |
return availableWithdrawal / 1000000 * rewardPercentage; | |
}else{ | |
return uint256(0); | |
} | |
} | |
function randMod(uint256 _modulus) internal returns(uint256) { | |
randNonce++; | |
return uint256(keccak256(randNonce, blockhash(block.number - 1))) % _modulus; | |
} | |
function createChamp(address _affiliateAddress) external payable | |
whenNotPaused | |
isPaid(createChampFee) | |
distributeInput(_affiliateAddress) | |
{ | |
uint256 id = champs.push(Champ(0, 2 + randMod(4), 1 + randMod(4), uint256(1 days) - uint256(randMod(9) * 1 hours), 0, 0, 0, leaderboard.length + 1, 0, uint256(block.timestamp), 0,0,0, false)) - 1; | |
champs[id].id = id; | |
leaderboard.push(id); | |
champToOwner[id] = msg.sender; | |
addressInfo[msg.sender].champsCount++; | |
emit NewChamp(id, msg.sender); | |
} | |
function setCreateChampFee(uint256 _fee) external onlyOwner { | |
createChampFee = _fee; | |
} | |
function changeChampsName(uint _champId, string _name) external | |
onlyOwnerOfChamp(_champId){ | |
champToName[_champId] = _name; | |
} | |
function changePlayersName(string _name) external { | |
addressInfo[msg.sender].name = _name; | |
} | |
function withdrawChamp(uint _id) external | |
onlyOwnerOfChamp(_id) | |
contractMinBalanceReached | |
isChampWithdrawReady(_id) | |
whenNotPaused { | |
Champ storage champ = champs[_id]; | |
require(champ.position <= 800); | |
champ.withdrawCooldown = block.timestamp + 1 days; | |
uint256 withdrawal = getChampReward(champ.position); | |
addressInfo[msg.sender].withdrawal += withdrawal; | |
pendingWithdrawal += withdrawal; | |
} | |
function withdrawToAddress(address _address) external | |
whenNotPaused { | |
address playerAddress = _address; | |
if(playerAddress == address(0)){ playerAddress = msg.sender; } | |
uint256 share = addressInfo[playerAddress].withdrawal; | |
require(share > 0); | |
addressInfo[playerAddress].withdrawal = 0; | |
pendingWithdrawal = pendingWithdrawal.sub(share); | |
playerAddress.transfer(share); | |
} | |
} | |
contract Items is ChampFactory { | |
event NewItem(uint256 itemID, address owner); | |
constructor () internal { | |
items.push(Item(0, 0, 0, 0, 0, 0, 0, false, false)); | |
} | |
modifier onlyOwnerOfItem(uint256 _itemId) { | |
require(_itemId != 0); | |
require(msg.sender == itemToOwner[_itemId]); | |
_; | |
} | |
modifier onlyNotOwnerOfItem(uint256 _itemId) { | |
require(msg.sender != itemToOwner[_itemId]); | |
_; | |
} | |
function hasChampSomethingOn(uint _champId, uint8 _type) internal view returns(bool){ | |
Champ storage champ = champs[_champId]; | |
if(_type == 1){ | |
return (champ.eq_sword == 0) ? false : true; | |
} | |
if(_type == 2){ | |
return (champ.eq_shield == 0) ? false : true; | |
} | |
if(_type == 3){ | |
return (champ.eq_helmet == 0) ? false : true; | |
} | |
} | |
function getItemsByOwner(address _owner) external view returns(uint256[]) { | |
uint256[] memory result = new uint256[](addressInfo[_owner].itemsCount); | |
uint256 counter = 0; | |
for (uint256 i = 0; i < items.length; i++) { | |
if (itemToOwner[i] == _owner) { | |
result[counter] = i; | |
counter++; | |
} | |
} | |
return result; | |
} | |
function takeOffItem(uint _champId, uint8 _type) public | |
onlyOwnerOfChamp(_champId) { | |
uint256 itemId; | |
Champ storage champ = champs[_champId]; | |
if(_type == 1){ | |
itemId = champ.eq_sword; | |
if (itemId > 0) { | |
champ.eq_sword = 0; | |
} | |
} | |
if(_type == 2){ | |
itemId = champ.eq_shield; | |
if(itemId > 0) { | |
champ.eq_shield = 0; | |
} | |
} | |
if(_type == 3){ | |
itemId = champ.eq_helmet; | |
if(itemId > 0) { | |
champ.eq_helmet = 0; | |
} | |
} | |
if(itemId > 0){ | |
items[itemId].onChamp = false; | |
} | |
} | |
function putOn(uint256 _champId, uint256 _itemId) external | |
onlyOwnerOfChamp(_champId) | |
onlyOwnerOfItem(_itemId) { | |
Champ storage champ = champs[_champId]; | |
Item storage item = items[_itemId]; | |
if(item.onChamp){ | |
takeOffItem(item.onChampId, item.itemType); | |
} | |
item.onChamp = true; | |
item.onChampId = _champId; | |
if(item.itemType == 1){ | |
if(champ.eq_sword > 0){ | |
takeOffItem(champ.id, 1); | |
} | |
champ.eq_sword = _itemId; | |
} | |
if(item.itemType == 2){ | |
if(champ.eq_shield > 0){ | |
takeOffItem(champ.id, 2); | |
} | |
champ.eq_shield = _itemId; | |
} | |
if(item.itemType == 3){ | |
if(champ.eq_helmet > 0){ | |
takeOffItem(champ.id, 3); | |
} | |
champ.eq_helmet = _itemId; | |
} | |
} | |
function openLootbox(address _affiliateAddress) external payable | |
whenNotPaused | |
isPaid(lootboxFee) | |
distributeInput(_affiliateAddress) { | |
uint256 pointToCooldownReduction; | |
uint256 randNum = randMod(1001); | |
uint256 pointsToShare; | |
uint256 itemID; | |
Item memory item = Item({ | |
itemType: uint8(uint256(randMod(3) + 1)), | |
itemRarity: uint8(0), | |
attackPower: 0, | |
defencePower: 0, | |
cooldownReduction: 0, | |
price: 0, | |
onChampId: 0, | |
onChamp: false, | |
forSale: false | |
}); | |
if(450 > randNum){ | |
pointsToShare = 25 + randMod(9); | |
item.itemRarity = uint8(1); | |
}else if(720 > randNum){ | |
pointsToShare = 42 + randMod(17); | |
item.itemRarity = uint8(2); | |
}else if(910 > randNum){ | |
pointsToShare = 71 + randMod(25); | |
item.itemRarity = uint8(3); | |
}else if(980 > randNum){ | |
pointsToShare = 119 + randMod(33); | |
item.itemRarity = uint8(4); | |
}else{ | |
pointsToShare = 235 + randMod(41); | |
item.itemRarity = uint8(5); | |
} | |
if(item.itemType == uint8(1)){ | |
item.attackPower = pointsToShare / 10 * 7; | |
pointsToShare -= item.attackPower; | |
item.defencePower = pointsToShare / 10 * randMod(6); | |
pointsToShare -= item.defencePower; | |
item.cooldownReduction = pointsToShare * uint256(1 minutes); | |
item.itemType = uint8(1); | |
} | |
if(item.itemType == uint8(2)){ | |
item.defencePower = pointsToShare / 10 * 7; | |
pointsToShare -= item.defencePower; | |
item.attackPower = pointsToShare / 10 * randMod(6); | |
pointsToShare -= item.attackPower; | |
item.cooldownReduction = pointsToShare * uint256(1 minutes); | |
item.itemType = uint8(2); | |
} | |
if(item.itemType == uint8(3)){ | |
pointToCooldownReduction = pointsToShare / 10 * 7; | |
item.cooldownReduction = pointToCooldownReduction * uint256(1 minutes); | |
pointsToShare -= pointToCooldownReduction; | |
item.attackPower = pointsToShare / 10 * randMod(6); | |
pointsToShare -= item.attackPower; | |
item.defencePower = pointsToShare; | |
item.itemType = uint8(3); | |
} | |
itemID = items.push(item) - 1; | |
itemToOwner[itemID] = msg.sender; | |
addressInfo[msg.sender].itemsCount++; | |
emit NewItem(itemID, msg.sender); | |
} | |
function setLootboxFee(uint _fee) external onlyOwner { | |
lootboxFee = _fee; | |
} | |
} | |
contract ItemMarket is Items { | |
event TransferItem(address from, address to, uint256 itemID); | |
modifier itemIsForSale(uint256 _id){ | |
require(items[_id].forSale); | |
_; | |
} | |
modifier itemIsNotForSale(uint256 _id){ | |
require(items[_id].forSale == false); | |
_; | |
} | |
modifier ifItemForSaleThenCancelSale(uint256 _itemID){ | |
Item storage item = items[_itemID]; | |
if(item.forSale){ | |
_cancelItemSale(item); | |
} | |
_; | |
} | |
modifier distributeSaleInput(address _owner) { | |
uint256 contractOwnerCommision; | |
uint256 playerShare; | |
if(msg.value > 100){ | |
contractOwnerCommision = (msg.value / 100); | |
playerShare = msg.value - contractOwnerCommision; | |
}else{ | |
contractOwnerCommision = 0; | |
playerShare = msg.value; | |
} | |
addressInfo[_owner].withdrawal += playerShare; | |
addressInfo[contractOwner].withdrawal += contractOwnerCommision; | |
pendingWithdrawal += playerShare + contractOwnerCommision; | |
_; | |
} | |
function getItemsForSale() view external returns(uint256[]){ | |
uint256[] memory result = new uint256[](itemsForSaleCount); | |
if(itemsForSaleCount > 0){ | |
uint256 counter = 0; | |
for (uint256 i = 0; i < items.length; i++) { | |
if (items[i].forSale == true) { | |
result[counter]=i; | |
counter++; | |
} | |
} | |
} | |
return result; | |
} | |
function _cancelItemSale(Item storage item) private { | |
item.forSale = false; | |
itemsForSaleCount--; | |
} | |
function transferItem(address _from, address _to, uint256 _itemID) internal | |
ifItemForSaleThenCancelSale(_itemID) { | |
Item storage item = items[_itemID]; | |
if(item.onChamp && _to != champToOwner[item.onChampId]){ | |
takeOffItem(item.onChampId, item.itemType); | |
} | |
addressInfo[_to].itemsCount++; | |
addressInfo[_from].itemsCount--; | |
itemToOwner[_itemID] = _to; | |
emit TransferItem(_from, _to, _itemID); | |
} | |
function giveItem(address _to, uint256 _itemID) public | |
onlyOwnerOfItem(_itemID) { | |
transferItem(msg.sender, _to, _itemID); | |
} | |
function cancelItemSale(uint256 _id) public | |
itemIsForSale(_id) | |
onlyOwnerOfItem(_id){ | |
Item storage item = items[_id]; | |
_cancelItemSale(item); | |
} | |
function setItemForSale(uint256 _id, uint256 _price) external | |
onlyOwnerOfItem(_id) | |
itemIsNotForSale(_id) { | |
Item storage item = items[_id]; | |
item.forSale = true; | |
item.price = _price; | |
itemsForSaleCount++; | |
} | |
function buyItem(uint256 _id) external payable | |
whenNotPaused | |
onlyNotOwnerOfItem(_id) | |
itemIsForSale(_id) | |
isPaid(items[_id].price) | |
distributeSaleInput(itemToOwner[_id]) | |
{ | |
transferItem(itemToOwner[_id], msg.sender, _id); | |
} | |
} | |
contract ItemForge is ItemMarket { | |
event Forge(uint256 forgedItemID); | |
function forgeItems(uint256 _parentItemID, uint256 _childItemID) external | |
onlyOwnerOfItem(_parentItemID) | |
onlyOwnerOfItem(_childItemID) | |
ifItemForSaleThenCancelSale(_parentItemID) | |
ifItemForSaleThenCancelSale(_childItemID) { | |
require(_parentItemID != _childItemID); | |
Item storage parentItem = items[_parentItemID]; | |
Item storage childItem = items[_childItemID]; | |
if(childItem.onChamp){ | |
takeOffItem(childItem.onChampId, childItem.itemType); | |
} | |
parentItem.attackPower = (parentItem.attackPower > childItem.attackPower) ? parentItem.attackPower : childItem.attackPower; | |
parentItem.defencePower = (parentItem.defencePower > childItem.defencePower) ? parentItem.defencePower : childItem.defencePower; | |
parentItem.cooldownReduction = (parentItem.cooldownReduction > childItem.cooldownReduction) ? parentItem.cooldownReduction : childItem.cooldownReduction; | |
parentItem.itemRarity = uint8(6); | |
transferItem(msg.sender, address(0), _childItemID); | |
emit Forge(_parentItemID); | |
} | |
} | |
contract ChampAttack is ItemForge { | |
event Attack(uint256 winnerChampID, uint256 defeatedChampID, bool didAttackerWin); | |
modifier isChampReady(uint256 _champId) { | |
require (champs[_champId].readyTime <= block.timestamp); | |
_; | |
} | |
modifier notSelfAttack(uint256 _champId, uint256 _targetId) { | |
require(_champId != _targetId); | |
_; | |
} | |
modifier targetExists(uint256 _targetId){ | |
require(champToOwner[_targetId] != address(0)); | |
_; | |
} | |
function getChampStats(uint256 _champId) public view returns(uint256,uint256,uint256){ | |
Champ storage champ = champs[_champId]; | |
Item storage sword = items[champ.eq_sword]; | |
Item storage shield = items[champ.eq_shield]; | |
Item storage helmet = items[champ.eq_helmet]; | |
uint256 totalAttackPower = champ.attackPower + sword.attackPower + shield.attackPower + helmet.attackPower; | |
uint256 totalDefencePower = champ.defencePower + sword.defencePower + shield.defencePower + helmet.defencePower; | |
uint256 totalCooldownReduction = sword.cooldownReduction + shield.cooldownReduction + helmet.cooldownReduction; | |
return (totalAttackPower, totalDefencePower, totalCooldownReduction); | |
} | |
function subAttack(uint256 _playerAttackPoints, uint256 _x) internal pure returns (uint256) { | |
return (_playerAttackPoints <= _x + 2) ? 2 : _playerAttackPoints - _x; | |
} | |
function subDefence(uint256 _playerDefencePoints, uint256 _x) internal pure returns (uint256) { | |
return (_playerDefencePoints <= _x) ? 1 : _playerDefencePoints - _x; | |
} | |
function _attackCompleted(Champ storage _winnerChamp, Champ storage _defeatedChamp, uint256 _pointsGiven, uint256 _pointsToAttackPower) private { | |
_winnerChamp.attackPower += _pointsToAttackPower; | |
_winnerChamp.defencePower += _pointsGiven - _pointsToAttackPower; | |
_defeatedChamp.attackPower = subAttack(_defeatedChamp.attackPower, _pointsToAttackPower); | |
_defeatedChamp.defencePower = subDefence(_defeatedChamp.defencePower, _pointsGiven - _pointsToAttackPower); | |
_winnerChamp.winCount++; | |
_defeatedChamp.lossCount++; | |
if(_winnerChamp.position > _defeatedChamp.position) { | |
uint256 winnerPosition = _winnerChamp.position; | |
uint256 loserPosition = _defeatedChamp.position; | |
_defeatedChamp.position = winnerPosition; | |
_winnerChamp.position = loserPosition; | |
leaderboard[winnerPosition - 1] = _defeatedChamp.id; | |
leaderboard[loserPosition - 1] = _winnerChamp.id; | |
} | |
} | |
function _getPoints(uint256 _pointsGiven) private returns (uint256 pointsGiven, uint256 pointsToAttackPower){ | |
return (_pointsGiven, randMod(_pointsGiven+1)); | |
} | |
function attack(uint256 _champId, uint256 _targetId) external | |
onlyOwnerOfChamp(_champId) | |
isChampReady(_champId) | |
notSelfAttack(_champId, _targetId) | |
targetExists(_targetId) { | |
Champ storage myChamp = champs[_champId]; | |
Champ storage enemyChamp = champs[_targetId]; | |
uint256 pointsGiven; | |
uint256 pointsToAttackPower; | |
uint256 myChampAttackPower; | |
uint256 enemyChampDefencePower; | |
uint256 myChampCooldownReduction; | |
(myChampAttackPower,,myChampCooldownReduction) = getChampStats(_champId); | |
(,enemyChampDefencePower,) = getChampStats(_targetId); | |
if (myChampAttackPower > enemyChampDefencePower) { | |
if(myChampAttackPower - enemyChampDefencePower < 5){ | |
(pointsGiven, pointsToAttackPower) = _getPoints(3); | |
}else if(myChampAttackPower - enemyChampDefencePower < 10){ | |
(pointsGiven, pointsToAttackPower) = _getPoints(2); | |
}else{ | |
(pointsGiven, pointsToAttackPower) = _getPoints(1); | |
} | |
_attackCompleted(myChamp, enemyChamp, pointsGiven, pointsToAttackPower); | |
emit Attack(myChamp.id, enemyChamp.id, true); | |
} else { | |
(pointsGiven, pointsToAttackPower) = _getPoints(1); | |
_attackCompleted(enemyChamp, myChamp, pointsGiven, pointsToAttackPower); | |
emit Attack(enemyChamp.id, myChamp.id, false); | |
} | |
myChamp.readyTime = uint256(block.timestamp + myChamp.cooldownTime - myChampCooldownReduction); | |
} | |
} | |
contract ChampMarket is ChampAttack { | |
event TransferChamp(address from, address to, uint256 champID); | |
modifier champIsForSale(uint256 _id){ | |
require(champs[_id].forSale); | |
_; | |
} | |
modifier champIsNotForSale(uint256 _id){ | |
require(champs[_id].forSale == false); | |
_; | |
} | |
modifier ifChampForSaleThenCancelSale(uint256 _champID){ | |
Champ storage champ = champs[_champID]; | |
if(champ.forSale){ | |
_cancelChampSale(champ); | |
} | |
_; | |
} | |
function getChampsForSale() view external returns(uint256[]){ | |
uint256[] memory result = new uint256[](champsForSaleCount); | |
if(champsForSaleCount > 0){ | |
uint256 counter = 0; | |
for (uint256 i = 0; i < champs.length; i++) { | |
if (champs[i].forSale == true) { | |
result[counter]=i; | |
counter++; | |
} | |
} | |
} | |
return result; | |
} | |
function _cancelChampSale(Champ storage champ) private { | |
champ.forSale = false; | |
champsForSaleCount--; | |
} | |
function transferChamp(address _from, address _to, uint256 _champId) internal ifChampForSaleThenCancelSale(_champId){ | |
Champ storage champ = champs[_champId]; | |
addressInfo[_to].champsCount++; | |
addressInfo[_from].champsCount--; | |
champToOwner[_champId] = _to; | |
if(champ.eq_sword != 0) { transferItem(_from, _to, champ.eq_sword); } | |
if(champ.eq_shield != 0) { transferItem(_from, _to, champ.eq_shield); } | |
if(champ.eq_helmet != 0) { transferItem(_from, _to, champ.eq_helmet); } | |
emit TransferChamp(_from, _to, _champId); | |
} | |
function cancelChampSale(uint256 _id) public | |
champIsForSale(_id) | |
onlyOwnerOfChamp(_id) { | |
Champ storage champ = champs[_id]; | |
_cancelChampSale(champ); | |
} | |
function giveChamp(address _to, uint256 _champId) external | |
onlyOwnerOfChamp(_champId) { | |
transferChamp(msg.sender, _to, _champId); | |
} | |
function setChampForSale(uint256 _id, uint256 _price) external | |
onlyOwnerOfChamp(_id) | |
champIsNotForSale(_id) { | |
Champ storage champ = champs[_id]; | |
champ.forSale = true; | |
champ.price = _price; | |
champsForSaleCount++; | |
} | |
function buyChamp(uint256 _id) external payable | |
whenNotPaused | |
onlyNotOwnerOfChamp(_id) | |
champIsForSale(_id) | |
isPaid(champs[_id].price) | |
distributeSaleInput(champToOwner[_id]) { | |
transferChamp(champToOwner[_id], msg.sender, _id); | |
} | |
} | |
contract MyCryptoChampCore is ChampMarket { | |
} |