Transaction Hash:
Block:
8051054 at Jun-29-2019 06:00:54 AM +UTC
Transaction Fee:
0.0004358868 ETH
$0.87
Gas Used:
189,516 Gas / 2.3 Gwei
Emitted Events:
| 85 |
0x8a6014227138556a259e7b2bf1dce668f9bdfd06.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000061d9557d6d259428976486e45ed0d8f47593ef5c, 0000000000000000000000000000000000000000000000000000000000000001 )
|
| 86 |
0x028b1e152d9fc838e5c0d480ed2d39b3d11ea2f0.0x716e3e1889ffd1c72a748db32820852fe1fd5b77a6455afb5dcdef6d7b5cbd5c( 0x716e3e1889ffd1c72a748db32820852fe1fd5b77a6455afb5dcdef6d7b5cbd5c, 00000000000000000000000061d9557d6d259428976486e45ed0d8f47593ef5c, 000000000000000000000000f8a8012dcf95914d5e052722775e473a887ef0e4, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000016, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000015, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000001 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x028b1E15...3d11EA2F0 | |||||
|
0x52bc44d5...b7d7bE3b5
Miner
| (Nanopool) | 5,218.078626250107602916 Eth | 5,218.079062136907602916 Eth | 0.0004358868 | |
| 0x61d9557d...47593eF5c |
0.0960029993 Eth
Nonce: 22
|
0.0955671125 Eth
Nonce: 23
| 0.0004358868 | ||
| 0x8a601422...8F9BdFd06 | |||||
| 0x98278eB7...42Ce53f2b | |||||
| 0xf936AA9e...b8716BA5e |
Execution Trace
0x028b1e152d9fc838e5c0d480ed2d39b3d11ea2f0.fe074e5e( )
-
Army.getArmiesPower( player=0x61d9557d6D259428976486E45eD0d8F47593eF5c, target=0xf8A8012DcF95914D5e052722775e473A887EF0e4 ) => ( playersAttack=6300, playersLooting=24000, targetsDefense=5600 ) -
Clans.getClanDetailsForAttack( player=0x61d9557d6D259428976486E45eD0d8F47593eF5c, target=0xf8A8012DcF95914D5e052722775e473A887EF0e4 ) => ( clanId=5, targetClanId=9, playerLootingBonus=40 )
-
0x8a6014227138556a259e7b2bf1dce668f9bdfd06.b131da81( )
Units.grantArmyExp( player=0x61d9557d6D259428976486E45eD0d8F47593eF5c, unitId=21, amount=100 ) => ( True )Army.increasePlayersArmyPowerTrio( player=0x61d9557d6D259428976486E45eD0d8F47593eF5c, attackGain=0, defenseGain=0, lootingGain=0 )-
Clans.increaseClanPower( player=0x61d9557d6D259428976486E45eD0d8F47593eF5c, amount=0 )
-
-
Units.unitsOwned( 0x61d9557d6D259428976486E45eD0d8F47593eF5c, 21 ) => ( units=0, factoryBuiltFlag=0 ) -
Units.unitsOwned( 0xf8A8012DcF95914D5e052722775e473A887EF0e4, 22 ) => ( units=0, factoryBuiltFlag=0 )
File 1 of 3: Army
File 2 of 3: Clans
File 3 of 3: Units
pragma solidity ^0.4.25;
/**
*
* World War Goo - Competitive Idle Game
*
* https://ethergoo.io
*
*/
contract Army {
GooToken constant goo = GooToken(0xdf0960778c6e6597f197ed9a25f12f5d971da86c);
Clans clans = Clans(0x0);
uint224 public totalArmyPower; // Global power of players (attack + defence)
uint224 public gooBankroll; // Goo dividends to be split over time between clans/players' army power
uint256 public nextSnapshotTime;
address public owner; // Minor management of game
mapping(address => mapping(uint256 => ArmyPower)) public armyPowerSnapshots; // Store player's army power for given day (snapshot)
mapping(address => mapping(uint256 => bool)) public armyPowerZeroedSnapshots; // Edgecase to determine difference between 0 army and an unused/inactive day.
mapping(address => uint256) public lastWarFundClaim; // Days (snapshot number)
mapping(address => uint256) public lastArmyPowerUpdate; // Days (last snapshot) player's army was updated
mapping(address => bool) operator;
uint224[] public totalArmyPowerSnapshots; // The total player army power for each prior day past
uint224[] public allocatedWarFundSnapshots; // Div pot (goo allocated to each prior day past)
uint224 public playerDivPercent = 2;
uint224 public clanDivPercent = 2;
struct ArmyPower {
uint80 attack;
uint80 defense;
uint80 looting;
}
constructor(uint256 firstSnapshotTime) public {
nextSnapshotTime = firstSnapshotTime;
owner = msg.sender;
}
function setClans(address clansContract) external {
require(msg.sender == owner);
clans = Clans(clansContract);
}
function setOperator(address gameContract, bool isOperator) external {
require(msg.sender == owner);
operator[gameContract] = isOperator;
}
function updateDailyDivPercents(uint224 newPlayersPercent, uint224 newClansPercent) external {
require(msg.sender == owner);
require(newPlayersPercent > 0 && newPlayersPercent <= 10); // 1-10% daily
require(newClansPercent > 0 && newClansPercent <= 10); // 1-10% daily
playerDivPercent = newPlayersPercent;
clanDivPercent = newClansPercent;
}
function depositSpentGoo(uint224 gooSpent) external {
require(operator[msg.sender]);
gooBankroll += gooSpent;
}
function getArmyPower(address player) external view returns (uint80, uint80, uint80) {
ArmyPower memory armyPower = armyPowerSnapshots[player][lastArmyPowerUpdate[player]];
return (armyPower.attack, armyPower.defense, armyPower.looting);
}
// Convenience function
function getArmiesPower(address player, address target) external view returns (uint80 playersAttack, uint80 playersLooting, uint80 targetsDefense) {
ArmyPower memory armyPower = armyPowerSnapshots[player][lastArmyPowerUpdate[player]];
playersAttack = armyPower.attack;
playersLooting = armyPower.looting;
targetsDefense = armyPowerSnapshots[target][lastArmyPowerUpdate[target]].defense;
}
function increasePlayersArmyPowerTrio(address player, uint80 attackGain, uint80 defenseGain, uint80 lootingGain) public {
require(operator[msg.sender]);
ArmyPower memory existingArmyPower = armyPowerSnapshots[player][lastArmyPowerUpdate[player]];
uint256 snapshotDay = allocatedWarFundSnapshots.length;
// Adjust army power (reusing struct)
existingArmyPower.attack += attackGain;
existingArmyPower.defense += defenseGain;
existingArmyPower.looting += lootingGain;
armyPowerSnapshots[player][snapshotDay] = existingArmyPower;
if (lastArmyPowerUpdate[player] != snapshotDay) {
lastArmyPowerUpdate[player] = snapshotDay;
}
totalArmyPower += (attackGain + defenseGain);
clans.increaseClanPower(player, attackGain + defenseGain);
}
function decreasePlayersArmyPowerTrio(address player, uint80 attackLoss, uint80 defenseLoss, uint80 lootingLoss) public {
require(operator[msg.sender]);
ArmyPower memory existingArmyPower = armyPowerSnapshots[player][lastArmyPowerUpdate[player]];
uint256 snapshotDay = allocatedWarFundSnapshots.length;
// Adjust army power (reusing struct)
existingArmyPower.attack -= attackLoss;
existingArmyPower.defense -= defenseLoss;
existingArmyPower.looting -= lootingLoss;
if (existingArmyPower.attack == 0 && existingArmyPower.defense == 0) { // Special case which tangles with "inactive day" snapshots (claiming divs)
armyPowerZeroedSnapshots[player][snapshotDay] = true;
delete armyPowerSnapshots[player][snapshotDay]; // 0
} else {
armyPowerSnapshots[player][snapshotDay] = existingArmyPower;
}
if (lastArmyPowerUpdate[player] != snapshotDay) {
lastArmyPowerUpdate[player] = snapshotDay;
}
totalArmyPower -= (attackLoss + defenseLoss);
clans.decreaseClanPower(player, attackLoss + defenseLoss);
}
function changePlayersArmyPowerTrio(address player, int attackChange, int defenseChange, int lootingChange) public {
require(operator[msg.sender]);
ArmyPower memory existingArmyPower = armyPowerSnapshots[player][lastArmyPowerUpdate[player]];
uint256 snapshotDay = allocatedWarFundSnapshots.length;
// Allow change to be positive or negative
existingArmyPower.attack = uint80(int(existingArmyPower.attack) + attackChange);
existingArmyPower.defense = uint80(int(existingArmyPower.defense) + defenseChange);
existingArmyPower.looting = uint80(int(existingArmyPower.looting) + lootingChange);
if (existingArmyPower.attack == 0 && existingArmyPower.defense == 0) { // Special case which tangles with "inactive day" snapshots (claiming divs)
armyPowerZeroedSnapshots[player][snapshotDay] = true;
delete armyPowerSnapshots[player][snapshotDay]; // 0
} else {
armyPowerSnapshots[player][snapshotDay] = existingArmyPower;
}
if (lastArmyPowerUpdate[player] != snapshotDay) {
lastArmyPowerUpdate[player] = snapshotDay;
}
changeTotalArmyPower(player, attackChange, defenseChange);
}
function changeTotalArmyPower(address player, int attackChange, int defenseChange) internal {
uint224 newTotal = uint224(int(totalArmyPower) + attackChange + defenseChange);
if (newTotal > totalArmyPower) {
clans.increaseClanPower(player, newTotal - totalArmyPower);
} else if (newTotal < totalArmyPower) {
clans.decreaseClanPower(player, totalArmyPower - newTotal);
}
totalArmyPower = newTotal;
}
// Allocate army power divs for the day (00:00 cron job)
function snapshotDailyWarFunding() external {
require(msg.sender == owner);
require(now + 6 hours > nextSnapshotTime);
totalArmyPowerSnapshots.push(totalArmyPower);
allocatedWarFundSnapshots.push((gooBankroll * playerDivPercent) / 100);
uint256 allocatedClanWarFund = (gooBankroll * clanDivPercent) / 100; // No daily snapshots needed for Clans (as below will also claim between the handful of clans)
gooBankroll -= (gooBankroll * (playerDivPercent + clanDivPercent)) / 100; // % of pool daily
uint256 numClans = clans.totalSupply();
uint256[] memory clanArmyPower = new uint256[](numClans);
// Get total power from all clans
uint256 todaysTotalClanPower;
for (uint256 i = 1; i <= numClans; i++) {
clanArmyPower[i-1] = clans.clanTotalArmyPower(i);
todaysTotalClanPower += clanArmyPower[i-1];
}
// Distribute goo divs to clans based on their relative power
for (i = 1; i <= numClans; i++) {
clans.depositGoo((allocatedClanWarFund * clanArmyPower[i-1]) / todaysTotalClanPower, i);
}
nextSnapshotTime = now + 24 hours;
}
function claimWarFundDividends(uint256 startSnapshot, uint256 endSnapShot) external {
require(startSnapshot <= endSnapShot);
require(startSnapshot >= lastWarFundClaim[msg.sender]);
require(endSnapShot < allocatedWarFundSnapshots.length);
uint224 gooShare;
ArmyPower memory previousArmyPower = armyPowerSnapshots[msg.sender][lastWarFundClaim[msg.sender] - 1]; // Underflow won't be a problem as armyPowerSnapshots[][0xffffffff] = 0;
for (uint256 i = startSnapshot; i <= endSnapShot; i++) {
// Slightly complex things by accounting for days/snapshots when user made no tx's
ArmyPower memory armyPowerDuringSnapshot = armyPowerSnapshots[msg.sender][i];
bool soldAllArmy = armyPowerZeroedSnapshots[msg.sender][i];
if (!soldAllArmy && armyPowerDuringSnapshot.attack == 0 && armyPowerDuringSnapshot.defense == 0) {
armyPowerDuringSnapshot = previousArmyPower;
} else {
previousArmyPower = armyPowerDuringSnapshot;
}
gooShare += (allocatedWarFundSnapshots[i] * (armyPowerDuringSnapshot.attack + armyPowerDuringSnapshot.defense)) / totalArmyPowerSnapshots[i];
}
ArmyPower memory endSnapshotArmyPower = armyPowerSnapshots[msg.sender][endSnapShot];
if (endSnapshotArmyPower.attack == 0 && endSnapshotArmyPower.defense == 0 && !armyPowerZeroedSnapshots[msg.sender][endSnapShot] && (previousArmyPower.attack + previousArmyPower.defense) > 0) {
armyPowerSnapshots[msg.sender][endSnapShot] = previousArmyPower; // Checkpoint for next claim
}
lastWarFundClaim[msg.sender] = endSnapShot + 1;
(uint224 clanFee, uint224 leaderFee, address leader, uint224 referalFee, address referer) = clans.getPlayerFees(msg.sender);
if (clanFee > 0) {
clanFee = (gooShare * clanFee) / 100; // Convert from percent to goo
leaderFee = (gooShare * leaderFee) / 100; // Convert from percent to goo
clans.mintGoo(msg.sender, clanFee);
goo.mintGoo(leaderFee, leader);
}
if (referer == address(0)) {
referalFee = 0;
} else if (referalFee > 0) {
referalFee = (gooShare * referalFee) / 100; // Convert from percent to goo
goo.mintGoo(referalFee, referer);
}
goo.mintGoo(gooShare - (clanFee + leaderFee + referalFee), msg.sender);
}
function getSnapshotDay() external view returns (uint256 snapshot) {
snapshot = allocatedWarFundSnapshots.length;
}
}
contract GooToken {
function transfer(address to, uint256 tokens) external returns (bool);
function increasePlayersGooProduction(address player, uint256 increase) external;
function decreasePlayersGooProduction(address player, uint256 decrease) external;
function updatePlayersGooFromPurchase(address player, uint224 purchaseCost) external;
function updatePlayersGoo(address player) external;
function mintGoo(uint224 amount, address player) external;
}
contract Clans {
mapping(uint256 => uint256) public clanTotalArmyPower;
function totalSupply() external view returns (uint256);
function depositGoo(uint256 amount, uint256 clanId) external;
function getPlayerFees(address player) external view returns (uint224 clansFee, uint224 leadersFee, address leader, uint224 referalsFee, address referer);
function getPlayersClanUpgrade(address player, uint256 upgradeClass) external view returns (uint224 upgradeGain);
function mintGoo(address player, uint256 amount) external;
function increaseClanPower(address player, uint256 amount) external;
function decreaseClanPower(address player, uint256 amount) external;
}
contract Factories {
uint256 public constant MAX_SIZE = 40;
function getFactories(address player) external returns (uint256[]);
function addFactory(address player, uint8 position, uint256 unitId) external;
}
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint224 a, uint224 b) internal pure returns (uint224) {
if (a == 0) {
return 0;
}
uint224 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
library SafeMath224 {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint224 a, uint224 b) internal pure returns (uint224) {
if (a == 0) {
return 0;
}
uint224 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint224 a, uint224 b) internal pure returns (uint224) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint224 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint224 a, uint224 b) internal pure returns (uint224) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint224 a, uint224 b) internal pure returns (uint224) {
uint224 c = a + b;
assert(c >= a);
return c;
}
}File 2 of 3: Clans
pragma solidity ^0.4.25;
/**
*
* World War Goo - Competitive Idle Game
*
* https://ethergoo.io
*
*/
interface ERC721 {
function totalSupply() external view returns (uint256 tokens);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function exists(uint256 tokenId) external view returns (bool tokenExists);
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address approvee);
function transferFrom(address from, address to, uint256 tokenId) external;
function tokensOf(address owner) external view returns (uint256[] tokens);
//function tokenByIndex(uint256 index) external view returns (uint256 token);
// Events
event Transfer(address from, address to, uint256 tokenId);
event Approval(address owner, address approved, uint256 tokenId);
}
interface ApproveAndCallFallBack {
function receiveApproval(address from, uint256 tokens, address token, bytes data) external;
}
contract Clans is ERC721, ApproveAndCallFallBack {
using SafeMath for uint256;
GooToken constant goo = GooToken(0xdf0960778c6e6597f197ed9a25f12f5d971da86c);
Army constant army = Army(0x98278eb74b388efd4d6fc81dd3f95b642ce53f2b);
WWGClanCoupons constant clanCoupons = WWGClanCoupons(0xe9fe4e530ebae235877289bd978f207ae0c8bb25); // For minting clans to initial owners (prelaunch buyers)
string public constant name = "Goo Clan";
string public constant symbol = "GOOCLAN";
uint224 numClans;
address owner; // Minor management
// ERC721 stuff
mapping (uint256 => address) public tokenOwner;
mapping (uint256 => address) public tokenApprovals;
mapping (address => uint256[]) public ownedTokens;
mapping(uint256 => uint256) public ownedTokensIndex;
mapping(address => UserClan) public userClan;
mapping(uint256 => uint224) public clanFee;
mapping(uint256 => uint224) public leaderFee;
mapping(uint256 => uint256) public clanMembers;
mapping(uint256 => mapping(uint256 => uint224)) public clanUpgradesOwned;
mapping(uint256 => uint256) public clanGoo;
mapping(uint256 => address) public clanToken; // i.e. BNB
mapping(uint256 => uint256) public baseTokenDenomination; // base value for token gains i.e. 0.000001 BNB
mapping(uint256 => uint256) public clanTotalArmyPower;
mapping(uint256 => uint224) public referalFee; // If invited to a clan how much % of player's divs go to referer
mapping(address => mapping(uint256 => address)) public clanReferer; // Address of who invited player to each clan
mapping(uint256 => Upgrade) public upgradeList;
mapping(address => bool) operator;
struct UserClan {
uint224 clanId;
uint32 clanJoinTime;
}
struct Upgrade {
uint256 upgradeId;
uint224 gooCost;
uint224 upgradeGain;
uint256 upgradeClass;
uint256 prerequisiteUpgrade;
}
// Events
event JoinedClan(uint256 clanId, address player, address referer);
event LeftClan(uint256 clanId, address player);
constructor() public {
owner = msg.sender;
}
function setOperator(address gameContract, bool isOperator) external {
require(msg.sender == owner);
operator[gameContract] = isOperator;
}
function totalSupply() external view returns (uint256) {
return numClans;
}
function balanceOf(address player) public view returns (uint256) {
return ownedTokens[player].length;
}
function ownerOf(uint256 clanId) external view returns (address) {
return tokenOwner[clanId];
}
function exists(uint256 clanId) public view returns (bool) {
return tokenOwner[clanId] != address(0);
}
function approve(address to, uint256 clanId) external {
require(tokenOwner[clanId] == msg.sender);
tokenApprovals[clanId] = to;
emit Approval(msg.sender, to, clanId);
}
function getApproved(uint256 clanId) external view returns (address) {
return tokenApprovals[clanId];
}
function tokensOf(address player) external view returns (uint256[] tokens) {
return ownedTokens[player];
}
function transferFrom(address from, address to, uint256 tokenId) public {
require(tokenApprovals[tokenId] == msg.sender || tokenOwner[tokenId] == msg.sender);
joinClanPlayer(to, uint224(tokenId), 0); // uint224 won't overflow due to tokenOwner check in removeTokenFrom()
removeTokenFrom(from, tokenId);
addTokenTo(to, tokenId);
delete tokenApprovals[tokenId]; // Clear approval
emit Transfer(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public {
transferFrom(from, to, tokenId);
checkERC721Recieved(from, to, tokenId, data);
}
function checkERC721Recieved(address from, address to, uint256 tokenId, bytes memory data) internal {
uint256 size;
assembly { size := extcodesize(to) }
if (size > 0) { // Recipient is contract so must confirm recipt
bytes4 successfullyRecieved = ERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data);
require(successfullyRecieved == bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")));
}
}
function removeTokenFrom(address from, uint256 tokenId) internal {
require(tokenOwner[tokenId] == from);
tokenOwner[tokenId] = address(0);
uint256 tokenIndex = ownedTokensIndex[tokenId];
uint256 lastTokenIndex = ownedTokens[from].length.sub(1);
uint256 lastToken = ownedTokens[from][lastTokenIndex];
ownedTokens[from][tokenIndex] = lastToken;
ownedTokens[from][lastTokenIndex] = 0;
ownedTokens[from].length--;
ownedTokensIndex[tokenId] = 0;
ownedTokensIndex[lastToken] = tokenIndex;
}
function addTokenTo(address to, uint256 tokenId) internal {
require(ownedTokens[to].length == 0); // Can't own multiple clans
tokenOwner[tokenId] = to;
ownedTokensIndex[tokenId] = ownedTokens[to].length;
ownedTokens[to].push(tokenId);
}
function updateClanFees(uint224 newClanFee, uint224 newLeaderFee, uint224 newReferalFee, uint256 clanId) external {
require(msg.sender == tokenOwner[clanId]);
require(newClanFee <= 25); // 25% max fee
require(newReferalFee <= 10); // 10% max refs
require(newLeaderFee <= newClanFee); // Clan gets fair cut
clanFee[clanId] = newClanFee;
leaderFee[clanId] = newLeaderFee;
referalFee[clanId] = newReferalFee;
}
function getPlayerFees(address player) external view returns (uint224 clansFee, uint224 leadersFee, address leader, uint224 referalsFee, address referer) {
uint256 usersClan = userClan[player].clanId;
clansFee = clanFee[usersClan];
leadersFee = leaderFee[usersClan];
leader = tokenOwner[usersClan];
referalsFee = referalFee[usersClan];
referer = clanReferer[player][usersClan];
}
function getPlayersClanUpgrade(address player, uint256 upgradeClass) external view returns (uint224 upgradeGain) {
upgradeGain = upgradeList[clanUpgradesOwned[userClan[player].clanId][upgradeClass]].upgradeGain;
}
function getClanUpgrade(uint256 clanId, uint256 upgradeClass) external view returns (uint224 upgradeGain) {
upgradeGain = upgradeList[clanUpgradesOwned[clanId][upgradeClass]].upgradeGain;
}
// Convienence function
function getClanDetailsForAttack(address player, address target) external view returns (uint256 clanId, uint256 targetClanId, uint224 playerLootingBonus) {
clanId = userClan[player].clanId;
targetClanId = userClan[target].clanId;
playerLootingBonus = upgradeList[clanUpgradesOwned[clanId][3]].upgradeGain; // class 3 = looting bonus
}
function joinClan(uint224 clanId, address referer) external {
require(exists(clanId));
joinClanPlayer(msg.sender, clanId, referer);
}
// Allows smarter invites/referals in future
function joinClanFromInvite(address player, uint224 clanId, address referer) external {
require(operator[msg.sender]);
joinClanPlayer(player, clanId, referer);
}
function joinClanPlayer(address player, uint224 clanId, address referer) internal {
require(ownedTokens[player].length == 0); // Owners can't join
(uint80 attack, uint80 defense,) = army.getArmyPower(player);
// Leave old clan
UserClan memory existingClan = userClan[player];
if (existingClan.clanId > 0) {
clanMembers[existingClan.clanId]--;
clanTotalArmyPower[existingClan.clanId] -= (attack + defense);
emit LeftClan(existingClan.clanId, player);
}
if (referer != address(0) && referer != player) {
require(userClan[referer].clanId == clanId);
clanReferer[player][clanId] = referer;
}
existingClan.clanId = clanId;
existingClan.clanJoinTime = uint32(now);
clanMembers[clanId]++;
clanTotalArmyPower[clanId] += (attack + defense);
userClan[player] = existingClan;
emit JoinedClan(clanId, player, referer);
}
function leaveClan() external {
require(ownedTokens[msg.sender].length == 0); // Owners can't leave
UserClan memory usersClan = userClan[msg.sender];
require(usersClan.clanId > 0);
(uint80 attack, uint80 defense,) = army.getArmyPower(msg.sender);
clanTotalArmyPower[usersClan.clanId] -= (attack + defense);
clanMembers[usersClan.clanId]--;
delete userClan[msg.sender];
emit LeftClan(usersClan.clanId, msg.sender);
// Cannot leave if player has unclaimed divs (edge case for clan fee abuse)
require(attack + defense == 0 || army.lastWarFundClaim(msg.sender) == army.getSnapshotDay());
require(usersClan.clanJoinTime + 24 hours < now);
}
function mintClan(address recipient, uint224 referalPercent, address clanTokenAddress, uint256 baseTokenReward) external {
require(operator[msg.sender]);
require(ERC20(clanTokenAddress).totalSupply() > 0);
numClans++;
uint224 clanId = numClans; // Starts from clanId 1
// Add recipient to clan
joinClanPlayer(recipient, clanId, 0);
require(tokenOwner[clanId] == address(0));
addTokenTo(recipient, clanId);
emit Transfer(address(0), recipient, clanId);
// Store clan token
clanToken[clanId] = clanTokenAddress;
baseTokenDenomination[clanId] = baseTokenReward;
referalFee[clanId] = referalPercent;
// Burn clan coupons from owner (prelaunch event)
if (clanCoupons.totalSupply() > 0) {
clanCoupons.burnCoupon(recipient, clanId);
}
}
function addUpgrade(uint256 id, uint224 gooCost, uint224 upgradeGain, uint256 upgradeClass, uint256 prereq) external {
require(operator[msg.sender]);
upgradeList[id] = Upgrade(id, gooCost, upgradeGain, upgradeClass, prereq);
}
// Incase an existing token becomes invalid (i.e. migrates away)
function updateClanToken(uint256 clanId, address newClanToken, bool shouldRetrieveOldTokens) external {
require(msg.sender == owner);
require(ERC20(newClanToken).totalSupply() > 0);
if (shouldRetrieveOldTokens) {
ERC20(clanToken[clanId]).transferFrom(this, owner, ERC20(clanToken[clanId]).balanceOf(this));
}
clanToken[clanId] = newClanToken;
}
// Incase need to tweak/balance attacking rewards (i.e. token moons so not viable to restock at current level)
function updateClanTokenGain(uint256 clanId, uint256 baseTokenReward) external {
require(msg.sender == owner);
baseTokenDenomination[clanId] = baseTokenReward;
}
// Clan member goo deposits
function receiveApproval(address player, uint256 amount, address, bytes) external {
uint256 clanId = userClan[player].clanId;
require(exists(clanId));
require(msg.sender == address(goo));
ERC20(msg.sender).transferFrom(player, address(0), amount);
clanGoo[clanId] += amount;
}
function buyUpgrade(uint224 upgradeId) external {
uint256 clanId = userClan[msg.sender].clanId;
require(msg.sender == tokenOwner[clanId]);
Upgrade memory upgrade = upgradeList[upgradeId];
require (upgrade.upgradeId > 0); // Valid upgrade
uint256 upgradeClass = upgrade.upgradeClass;
uint256 latestOwned = clanUpgradesOwned[clanId][upgradeClass];
require(latestOwned < upgradeId); // Haven't already purchased
require(latestOwned >= upgrade.prerequisiteUpgrade); // Own prequisite
// Clan discount
uint224 upgradeDiscount = clanUpgradesOwned[clanId][0]; // class 0 = upgrade discount
uint224 reducedUpgradeCost = upgrade.gooCost - ((upgrade.gooCost * upgradeDiscount) / 100);
clanGoo[clanId] = clanGoo[clanId].sub(reducedUpgradeCost);
army.depositSpentGoo(reducedUpgradeCost); // Transfer to goo bankroll
clanUpgradesOwned[clanId][upgradeClass] = upgradeId;
}
// Goo from divs etc.
function depositGoo(uint256 amount, uint256 clanId) external {
require(operator[msg.sender]);
require(exists(clanId));
clanGoo[clanId] += amount;
}
function increaseClanPower(address player, uint256 amount) external {
require(operator[msg.sender]);
uint256 clanId = userClan[player].clanId;
if (clanId > 0) {
clanTotalArmyPower[clanId] += amount;
}
}
function decreaseClanPower(address player, uint256 amount) external {
require(operator[msg.sender]);
uint256 clanId = userClan[player].clanId;
if (clanId > 0) {
clanTotalArmyPower[clanId] -= amount;
}
}
function stealGoo(address attacker, uint256 playerClanId, uint256 enemyClanId, uint80 lootingPower) external returns(uint256) {
require(operator[msg.sender]);
uint224 enemyGoo = uint224(clanGoo[enemyClanId]);
uint224 enemyGooStolen = (lootingPower > enemyGoo) ? enemyGoo : lootingPower;
clanGoo[enemyClanId] = clanGoo[enemyClanId].sub(enemyGooStolen);
uint224 clansShare = (enemyGooStolen * clanFee[playerClanId]) / 100;
uint224 referersFee = referalFee[playerClanId];
address referer = clanReferer[attacker][playerClanId];
if (clansShare > 0 || (referersFee > 0 && referer != address(0))) {
uint224 leaderShare = (enemyGooStolen * leaderFee[playerClanId]) / 100;
uint224 refsShare;
if (referer != address(0)) {
refsShare = (enemyGooStolen * referersFee) / 100;
goo.mintGoo(refsShare, referer);
}
clanGoo[playerClanId] += clansShare;
goo.mintGoo(leaderShare, tokenOwner[playerClanId]);
goo.mintGoo(enemyGooStolen - (clansShare + leaderShare + refsShare), attacker);
} else {
goo.mintGoo(enemyGooStolen, attacker);
}
return enemyGooStolen;
}
function rewardTokens(address attacker, uint256 playerClanId, uint80 lootingPower) external returns(uint256) {
require(operator[msg.sender]);
uint256 amount = baseTokenDenomination[playerClanId] * lootingPower;
ERC20(clanToken[playerClanId]).transfer(attacker, amount);
return amount;
}
// Daily clan dividends
function mintGoo(address player, uint256 amount) external {
require(operator[msg.sender]);
clanGoo[userClan[player].clanId] += amount;
}
}
contract ERC20 {
function transferFrom(address from, address to, uint tokens) external returns (bool success);
function transfer(address to, uint tokens) external returns (bool success);
function totalSupply() external constant returns (uint);
function balanceOf(address tokenOwner) external constant returns (uint balance);
}
contract GooToken {
function mintGoo(uint224 amount, address player) external;
function updatePlayersGooFromPurchase(address player, uint224 purchaseCost) external;
}
contract Army {
mapping(address => uint256) public lastWarFundClaim; // Days (snapshot number)
function depositSpentGoo(uint224 amount) external;
function getArmyPower(address player) external view returns (uint80, uint80, uint80);
function getSnapshotDay() external view returns (uint256 snapshot);
}
contract WWGClanCoupons {
function totalSupply() external view returns (uint256);
function burnCoupon(address clanOwner, uint256 tokenId) external;
}
contract ERC721Receiver {
function onERC721Received(address operator, address from, uint256 tokenId, bytes data) external returns(bytes4);
}
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}File 3 of 3: Units
pragma solidity ^0.4.25;
/**
*
* World War Goo - Competitive Idle Game
*
* https://ethergoo.io
*
*/
contract Units {
GooToken constant goo = GooToken(0xdf0960778c6e6597f197ed9a25f12f5d971da86c);
Army army = Army(0x0);
Clans clans = Clans(0x0);
Factories constant factories = Factories(0xc81068cd335889736fc485592e4d73a82403d44b);
mapping(address => mapping(uint256 => UnitsOwned)) public unitsOwned;
mapping(address => mapping(uint256 => UnitExperience)) public unitExp;
mapping(address => mapping(uint256 => uint256)) private unitMaxCap;
mapping(address => mapping(uint256 => UnitUpgrades)) private unitUpgrades;
mapping(address => mapping(uint256 => UpgradesOwned)) public upgradesOwned; // For each unitId, which upgrades owned (3 columns of uint64)
mapping(uint256 => Unit) public unitList;
mapping(uint256 => Upgrade) public upgradeList;
mapping(address => bool) operator;
address owner;
constructor() public {
owner = msg.sender;
}
struct UnitsOwned {
uint80 units;
uint8 factoryBuiltFlag; // Incase user sells units, we still want to keep factory
}
struct UnitExperience {
uint224 experience;
uint32 level;
}
struct UnitUpgrades {
uint32 prodIncrease;
uint32 prodMultiplier;
uint32 attackIncrease;
uint32 attackMultiplier;
uint32 defenseIncrease;
uint32 defenseMultiplier;
uint32 lootingIncrease;
uint32 lootingMultiplier;
}
struct UpgradesOwned {
uint64 column0;
uint64 column1;
uint64 column2;
}
// Unit & Upgrade data:
struct Unit {
uint256 unitId;
uint224 gooCost;
uint256 baseProduction;
uint80 attack;
uint80 defense;
uint80 looting;
}
struct Upgrade {
uint256 upgradeId;
uint224 gooCost;
uint256 unitId;
uint256 column; // Columns of upgrades (1st & 2nd are unit specific, then 3rd is capacity)
uint256 prerequisiteUpgrade;
uint256 unitMaxCapacityGain;
uint32 prodIncrease;
uint32 prodMultiplier;
uint32 attackIncrease;
uint32 attackMultiplier;
uint32 defenseIncrease;
uint32 defenseMultiplier;
uint32 lootingIncrease;
uint32 lootingMultiplier;
}
function setArmy(address armyContract) external {
require(msg.sender == owner);
army = Army(armyContract);
}
function setClans(address clansContract) external {
require(msg.sender == owner);
clans = Clans(clansContract);
}
function setOperator(address gameContract, bool isOperator) external {
require(msg.sender == owner);
operator[gameContract] = isOperator;
}
function mintUnitExternal(uint256 unit, uint80 amount, address player, uint8 chosenPosition) external {
require(operator[msg.sender]);
mintUnit(unit, amount, player, chosenPosition);
}
function mintUnit(uint256 unit, uint80 amount, address player, uint8 chosenPosition) internal {
UnitsOwned storage existingUnits = unitsOwned[player][unit];
if (existingUnits.factoryBuiltFlag == 0) {
// Edge case to create factory for player (on empty tile) where it is their first unit
uint256[] memory existingFactories = factories.getFactories(player);
uint256 length = existingFactories.length;
// Provided position is not valid so find valid factory position
if (chosenPosition >= factories.MAX_SIZE() || (chosenPosition < length && existingFactories[chosenPosition] > 0)) {
chosenPosition = 0;
while (chosenPosition < length && existingFactories[chosenPosition] > 0) {
chosenPosition++;
}
}
factories.addFactory(player, chosenPosition, unit);
unitsOwned[player][unit] = UnitsOwned(amount, 1); // 1 = Flag to say factory exists
} else {
existingUnits.units += amount;
}
(uint80 attackStats, uint80 defenseStats, uint80 lootingStats) = getUnitsCurrentBattleStats(player, unit);
if (attackStats > 0 || defenseStats > 0 || lootingStats > 0) {
army.increasePlayersArmyPowerTrio(player, attackStats * amount, defenseStats * amount, lootingStats * amount);
} else {
uint256 prodIncrease = getUnitsCurrentProduction(player, unit) * amount;
goo.increasePlayersGooProduction(player, prodIncrease / 100);
}
}
function deleteUnitExternal(uint80 amount, uint256 unit, address player) external {
require(operator[msg.sender]);
deleteUnit(amount, unit, player);
}
function deleteUnit(uint80 amount, uint256 unit, address player) internal {
(uint80 attackStats, uint80 defenseStats, uint80 lootingStats) = getUnitsCurrentBattleStats(player, unit);
if (attackStats > 0 || defenseStats > 0 || lootingStats > 0) {
army.decreasePlayersArmyPowerTrio(player, attackStats * amount, defenseStats * amount, lootingStats * amount);
} else {
uint256 prodDecrease = getUnitsCurrentProduction(player, unit) * amount;
goo.decreasePlayersGooProduction(player, prodDecrease / 100);
}
unitsOwned[player][unit].units -= amount;
}
function getUnitsCurrentBattleStats(address player, uint256 unitId) internal view returns (uint80 attack, uint80 defense, uint80 looting) {
Unit memory unit = unitList[unitId];
UnitUpgrades memory existingUpgrades = unitUpgrades[player][unitId];
attack = (unit.attack + existingUpgrades.attackIncrease) * (100 + existingUpgrades.attackMultiplier);
defense = (unit.defense + existingUpgrades.defenseIncrease) * (100 + existingUpgrades.defenseMultiplier);
looting = (unit.looting + existingUpgrades.lootingIncrease) * (100 + existingUpgrades.lootingMultiplier);
}
function getUnitsCurrentProduction(address player, uint256 unitId) public view returns (uint256) {
UnitUpgrades memory existingUpgrades = unitUpgrades[player][unitId];
return (unitList[unitId].baseProduction + existingUpgrades.prodIncrease) * (100 + existingUpgrades.prodMultiplier);
}
function buyUnit(uint256 unitId, uint80 amount, uint8 position) external {
uint224 gooCost = SafeMath224.mul(unitList[unitId].gooCost, amount);
require(gooCost > 0); // Valid unit
uint80 newTotal = unitsOwned[msg.sender][unitId].units + amount;
if (newTotal > 99) {
require(newTotal < 99 + unitMaxCap[msg.sender][unitId]);
}
// Clan discount
uint224 unitDiscount = clans.getPlayersClanUpgrade(msg.sender, 1); // class 1 = unit discount
uint224 reducedGooCost = gooCost - ((gooCost * unitDiscount) / 100);
uint224 seventyFivePercentRefund = (gooCost * 3) / 4;
// Update players goo
goo.updatePlayersGooFromPurchase(msg.sender, reducedGooCost);
goo.mintGoo(seventyFivePercentRefund, this); // 75% refund is stored (in this contract) for when player sells unit
army.depositSpentGoo(reducedGooCost - seventyFivePercentRefund); // Upto 25% Goo spent goes to divs (Remaining is discount + 75% player gets back when selling unit)
mintUnit(unitId, amount, msg.sender, position);
}
function sellUnit(uint256 unitId, uint80 amount) external {
require(unitsOwned[msg.sender][unitId].units >= amount && amount > 0);
uint224 gooCost = unitList[unitId].gooCost;
require(gooCost > 0);
goo.updatePlayersGoo(msg.sender);
deleteUnit(amount, unitId, msg.sender);
goo.transfer(msg.sender, (gooCost * amount * 3) / 4); // Refund 75%
}
function grantArmyExp(address player, uint256 unitId, uint224 amount) external returns(bool) {
require(operator[msg.sender]);
UnitExperience memory existingExp = unitExp[player][unitId];
uint224 expRequirement = (existingExp.level + 1) * 80; // Lvl 1: 80; Lvl 2: 160, Lvl 3: 240 (480 in total) etc.
if (existingExp.experience + amount >= expRequirement) {
existingExp.experience = (existingExp.experience + amount) - expRequirement;
existingExp.level++;
unitExp[player][unitId] = existingExp;
// Grant buff to unit (5% additive multiplier)
UnitUpgrades memory existingUpgrades = unitUpgrades[player][unitId];
existingUpgrades.attackMultiplier += 5;
existingUpgrades.defenseMultiplier += 5;
existingUpgrades.lootingMultiplier += 5;
unitUpgrades[player][unitId] = existingUpgrades;
// Increase player's army power
uint80 multiplierGain = unitsOwned[player][unitId].units * 5;
Unit memory unit = unitList[unitId];
uint80 attackGain = multiplierGain * (unit.attack + existingUpgrades.attackIncrease);
uint80 defenseGain = multiplierGain * (unit.defense + existingUpgrades.defenseIncrease);
uint80 lootingGain = multiplierGain * (unit.looting + existingUpgrades.lootingIncrease);
army.increasePlayersArmyPowerTrio(player, attackGain, defenseGain, lootingGain);
return true;
} else {
unitExp[player][unitId].experience += amount;
return false;
}
}
function increaseUnitCapacity(address player, uint256 upgradeGain, uint256 unitId) external {
require(operator[msg.sender]);
unitMaxCap[player][unitId] += upgradeGain;
}
function decreaseUnitCapacity(address player, uint256 upgradeGain, uint256 unitId) external {
require(operator[msg.sender]);
unitMaxCap[player][unitId] -= upgradeGain;
}
function increaseUpgradesExternal(address player, uint256 unitId, uint32 prodIncrease, uint32 prodMultiplier, uint32 attackIncrease, uint32 attackMultiplier, uint32 defenseIncrease, uint32 defenseMultiplier, uint32 lootingIncrease, uint32 lootingMultiplier) external {
require(operator[msg.sender]);
Upgrade memory upgrade = Upgrade(0,0,0,0,0,0, prodIncrease, prodMultiplier, attackIncrease, attackMultiplier, defenseIncrease, defenseMultiplier, lootingIncrease, lootingMultiplier);
increaseUpgrades(player, upgrade, unitId);
}
function increaseUpgrades(address player, Upgrade upgrade, uint256 unitId) internal {
uint80 units = unitsOwned[player][unitId].units;
UnitUpgrades memory existingUpgrades = unitUpgrades[player][unitId];
Unit memory unit = unitList[unitId];
if (unit.baseProduction > 0) {
// Increase goo production
uint256 prodGain = units * upgrade.prodMultiplier * (unit.baseProduction + existingUpgrades.prodIncrease); // Multiplier gains
prodGain += units * upgrade.prodIncrease * (100 + existingUpgrades.prodMultiplier); // Base prod gains
goo.updatePlayersGoo(player);
goo.increasePlayersGooProduction(player, prodGain / 100);
} else {
// Increase army power
uint80 attackGain = units * upgrade.attackMultiplier * (unit.attack + existingUpgrades.attackIncrease); // Multiplier gains
uint80 defenseGain = units * upgrade.defenseMultiplier * (unit.defense + existingUpgrades.defenseIncrease); // Multiplier gains
uint80 lootingGain = units * upgrade.lootingMultiplier * (unit.looting + existingUpgrades.lootingIncrease); // Multiplier gains
attackGain += units * upgrade.attackIncrease * (100 + existingUpgrades.attackMultiplier); // + Base gains
defenseGain += units * upgrade.defenseIncrease * (100 + existingUpgrades.defenseMultiplier); // + Base gains
lootingGain += units * upgrade.lootingIncrease * (100 + existingUpgrades.lootingMultiplier); // + Base gains
army.increasePlayersArmyPowerTrio(player, attackGain, defenseGain, lootingGain);
}
existingUpgrades.prodIncrease += upgrade.prodIncrease;
existingUpgrades.prodMultiplier += upgrade.prodMultiplier;
existingUpgrades.attackIncrease += upgrade.attackIncrease;
existingUpgrades.attackMultiplier += upgrade.attackMultiplier;
existingUpgrades.defenseIncrease += upgrade.defenseIncrease;
existingUpgrades.defenseMultiplier += upgrade.defenseMultiplier;
existingUpgrades.lootingIncrease += upgrade.lootingIncrease;
existingUpgrades.lootingMultiplier += upgrade.lootingMultiplier;
unitUpgrades[player][unitId] = existingUpgrades;
}
function decreaseUpgradesExternal(address player, uint256 unitId, uint32 prodIncrease, uint32 prodMultiplier, uint32 attackIncrease, uint32 attackMultiplier, uint32 defenseIncrease, uint32 defenseMultiplier, uint32 lootingIncrease, uint32 lootingMultiplier) external {
require(operator[msg.sender]);
Upgrade memory upgrade = Upgrade(0,0,0,0,0,0, prodIncrease, prodMultiplier, attackIncrease, attackMultiplier, defenseIncrease, defenseMultiplier, lootingIncrease, lootingMultiplier);
decreaseUpgrades(player, upgrade, unitId);
}
function decreaseUpgrades(address player, Upgrade upgrade, uint256 unitId) internal {
uint80 units = unitsOwned[player][unitId].units;
UnitUpgrades memory existingUpgrades = unitUpgrades[player][unitId];
Unit memory unit = unitList[unitId];
if (unit.baseProduction > 0) {
// Decrease goo production
uint256 prodLoss = units * upgrade.prodMultiplier * (unit.baseProduction + existingUpgrades.prodIncrease); // Multiplier losses
prodLoss += units * upgrade.prodIncrease * (100 + existingUpgrades.prodMultiplier); // Base prod losses
goo.updatePlayersGoo(player);
goo.decreasePlayersGooProduction(player, prodLoss / 100);
} else {
// Decrease army power
uint80 attackLoss = units * upgrade.attackMultiplier * (unit.attack + existingUpgrades.attackIncrease); // Multiplier losses
uint80 defenseLoss = units * upgrade.defenseMultiplier * (unit.defense + existingUpgrades.defenseIncrease); // Multiplier losses
uint80 lootingLoss = units * upgrade.lootingMultiplier * (unit.looting + existingUpgrades.lootingIncrease); // Multiplier losses
attackLoss += units * upgrade.attackIncrease * (100 + existingUpgrades.attackMultiplier); // + Base losses
defenseLoss += units * upgrade.defenseIncrease * (100 + existingUpgrades.defenseMultiplier); // + Base losses
lootingLoss += units * upgrade.lootingIncrease * (100 + existingUpgrades.lootingMultiplier); // + Base losses
army.decreasePlayersArmyPowerTrio(player, attackLoss, defenseLoss, lootingLoss);
}
existingUpgrades.prodIncrease -= upgrade.prodIncrease;
existingUpgrades.prodMultiplier -= upgrade.prodMultiplier;
existingUpgrades.attackIncrease -= upgrade.attackIncrease;
existingUpgrades.attackMultiplier -= upgrade.attackMultiplier;
existingUpgrades.defenseIncrease -= upgrade.defenseIncrease;
existingUpgrades.defenseMultiplier -= upgrade.defenseMultiplier;
existingUpgrades.lootingIncrease -= upgrade.lootingIncrease;
existingUpgrades.lootingMultiplier -= upgrade.lootingMultiplier;
unitUpgrades[player][unitId] = existingUpgrades;
}
function swapUpgradesExternal(address player, uint256 unitId, uint32[8] upgradeGains, uint32[8] upgradeLosses) external {
require(operator[msg.sender]);
UnitUpgrades memory existingUpgrades = unitUpgrades[player][unitId];
Unit memory unit = unitList[unitId];
if (unit.baseProduction > 0) {
// Change goo production
gooProductionChange(player, unitId, existingUpgrades, unit.baseProduction, upgradeGains, upgradeLosses);
} else {
// Change army power
armyPowerChange(player, existingUpgrades, unit, upgradeGains, upgradeLosses);
}
}
function armyPowerChange(address player, UnitUpgrades existingUpgrades, Unit unit, uint32[8] upgradeGains, uint32[8] upgradeLosses) internal {
int256 existingAttack = int256((unit.attack + existingUpgrades.attackIncrease) * (100 + existingUpgrades.attackMultiplier));
int256 existingDefense = int256((unit.defense + existingUpgrades.defenseIncrease) * (100 + existingUpgrades.defenseMultiplier));
int256 existingLooting = int256((unit.looting + existingUpgrades.lootingIncrease) * (100 + existingUpgrades.lootingMultiplier));
existingUpgrades.attackIncrease = uint32(int(existingUpgrades.attackIncrease) + (int32(upgradeGains[2]) - int32(upgradeLosses[2])));
existingUpgrades.attackMultiplier = uint32(int(existingUpgrades.attackMultiplier) + (int32(upgradeGains[3]) - int32(upgradeLosses[3])));
existingUpgrades.defenseIncrease = uint32(int(existingUpgrades.defenseIncrease) + (int32(upgradeGains[4]) - int32(upgradeLosses[4])));
existingUpgrades.defenseMultiplier = uint32(int(existingUpgrades.defenseMultiplier) + (int32(upgradeGains[5]) - int32(upgradeLosses[5])));
existingUpgrades.lootingIncrease = uint32(int(existingUpgrades.lootingIncrease) + (int32(upgradeGains[6]) - int32(upgradeLosses[6])));
existingUpgrades.lootingMultiplier = uint32(int(existingUpgrades.lootingMultiplier) + (int32(upgradeGains[7]) - int32(upgradeLosses[7])));
int256 attackChange = ((int256(unit.attack) + existingUpgrades.attackIncrease) * (100 + existingUpgrades.attackMultiplier)) - existingAttack;
int256 defenseChange = ((int256(unit.defense) + existingUpgrades.defenseIncrease) * (100 + existingUpgrades.defenseMultiplier)) - existingDefense;
int256 lootingChange = ((int256(unit.looting) + existingUpgrades.lootingIncrease) * (100 + existingUpgrades.lootingMultiplier)) - existingLooting;
uint256 unitId = unit.unitId;
int256 units = int256(unitsOwned[player][unitId].units);
army.changePlayersArmyPowerTrio(player, units * attackChange, units * defenseChange, units * lootingChange);
unitUpgrades[player][unitId] = existingUpgrades;
}
function gooProductionChange(address player, uint256 unitId, UnitUpgrades existingUpgrades, uint256 baseProduction, uint32[8] upgradeGains, uint32[8] upgradeLosses) internal {
goo.updatePlayersGoo(player);
int256 existingProd = int256((baseProduction + existingUpgrades.prodIncrease) * (100 + existingUpgrades.prodMultiplier));
existingUpgrades.prodIncrease = uint32(int(existingUpgrades.prodIncrease) + (int32(upgradeGains[0]) - int32(upgradeLosses[0])));
existingUpgrades.prodMultiplier = uint32(int(existingUpgrades.prodMultiplier) + (int32(upgradeGains[1]) - int32(upgradeLosses[1])));
int256 prodChange = ((int256(baseProduction) + existingUpgrades.prodIncrease) * (100 + existingUpgrades.prodMultiplier)) - existingProd;
if (prodChange > 0) {
goo.increasePlayersGooProduction(player, (unitsOwned[player][unitId].units * uint256(prodChange)) / 100);
} else {
goo.decreasePlayersGooProduction(player, (unitsOwned[player][unitId].units * uint256(-prodChange)) / 100);
}
unitUpgrades[player][unitId] = existingUpgrades;
}
function addUnit(uint256 id, uint224 baseGooCost, uint256 baseGooProduction, uint80 baseAttack, uint80 baseDefense, uint80 baseLooting) external {
require(operator[msg.sender]);
unitList[id] = Unit(id, baseGooCost, baseGooProduction, baseAttack, baseDefense, baseLooting);
}
function addUpgrade(uint256 id, uint224 gooCost, uint256 unit, uint256 column, uint256 prereq, uint256 unitMaxCapacityGain, uint32[8] upgradeGains) external {
require(operator[msg.sender]);
upgradeList[id] = Upgrade(id, gooCost, unit, column, prereq, unitMaxCapacityGain, upgradeGains[0], upgradeGains[1], upgradeGains[2], upgradeGains[3], upgradeGains[4], upgradeGains[5], upgradeGains[6], upgradeGains[7]);
}
function buyUpgrade(uint64 upgradeId) external {
Upgrade memory upgrade = upgradeList[upgradeId];
uint256 unitId = upgrade.unitId;
UpgradesOwned memory ownedUpgrades = upgradesOwned[msg.sender][unitId];
uint64 latestUpgradeOwnedForColumn;
if (upgrade.column == 0) {
latestUpgradeOwnedForColumn = ownedUpgrades.column0;
ownedUpgrades.column0 = upgradeId; // Update upgradesOwned
} else if (upgrade.column == 1) {
latestUpgradeOwnedForColumn = ownedUpgrades.column1;
ownedUpgrades.column1 = upgradeId; // Update upgradesOwned
} else if (upgrade.column == 2) {
latestUpgradeOwnedForColumn = ownedUpgrades.column2;
ownedUpgrades.column2 = upgradeId; // Update upgradesOwned
}
upgradesOwned[msg.sender][unitId] = ownedUpgrades;
require(unitId > 0); // Valid upgrade
require(latestUpgradeOwnedForColumn < upgradeId); // Haven't already purchased
require(latestUpgradeOwnedForColumn >= upgrade.prerequisiteUpgrade); // Own prequisite
// Clan discount
uint224 upgradeDiscount = clans.getPlayersClanUpgrade(msg.sender, 0); // class 0 = upgrade discount
uint224 reducedUpgradeCost = upgrade.gooCost - ((upgrade.gooCost * upgradeDiscount) / 100);
// Update players goo
goo.updatePlayersGooFromPurchase(msg.sender, reducedUpgradeCost);
army.depositSpentGoo(reducedUpgradeCost); // Transfer to goo bankroll
// Update stats for upgrade
if (upgrade.column == 2) {
unitMaxCap[msg.sender][unitId] += upgrade.unitMaxCapacityGain;
} else if (upgrade.column == 1) {
increaseUpgrades(msg.sender, upgrade, unitId);
} else if (upgrade.column == 0) {
increaseUpgrades(msg.sender, upgrade, unitId);
}
}
}
contract GooToken {
function transfer(address to, uint256 tokens) external returns (bool);
function increasePlayersGooProduction(address player, uint256 increase) external;
function decreasePlayersGooProduction(address player, uint256 decrease) external;
function updatePlayersGooFromPurchase(address player, uint224 purchaseCost) external;
function updatePlayersGoo(address player) external;
function mintGoo(uint224 amount, address player) external;
}
contract Army {
function depositSpentGoo(uint224 gooSpent) external;
function increasePlayersArmyPowerTrio(address player, uint80 attackGain, uint80 defenseGain, uint80 lootingGain) public;
function decreasePlayersArmyPowerTrio(address player, uint80 attackLoss, uint80 defenseLoss, uint80 lootingLoss) public;
function changePlayersArmyPowerTrio(address player, int attackChange, int defenseChange, int lootingChange) public;
}
contract Clans {
mapping(uint256 => uint256) public clanTotalArmyPower;
function totalSupply() external view returns (uint256);
function depositGoo(uint256 amount, uint256 clanId) external;
function getPlayerFees(address player) external view returns (uint224 clansFee, uint224 leadersFee, address leader, uint224 referalsFee, address referer);
function getPlayersClanUpgrade(address player, uint256 upgradeClass) external view returns (uint224 upgradeGain);
function mintGoo(address player, uint256 amount) external;
function increaseClanPower(address player, uint256 amount) external;
function decreaseClanPower(address player, uint256 amount) external;
}
contract Factories {
uint256 public constant MAX_SIZE = 40;
function getFactories(address player) external returns (uint256[]);
function addFactory(address player, uint8 position, uint256 unitId) external;
}
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint224 a, uint224 b) internal pure returns (uint224) {
if (a == 0) {
return 0;
}
uint224 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
library SafeMath224 {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint224 a, uint224 b) internal pure returns (uint224) {
if (a == 0) {
return 0;
}
uint224 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint224 a, uint224 b) internal pure returns (uint224) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint224 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint224 a, uint224 b) internal pure returns (uint224) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint224 a, uint224 b) internal pure returns (uint224) {
uint224 c = a + b;
assert(c >= a);
return c;
}
}