// File: ColorTeam.sol



pragma solidity 0.4.24;
import "./SafeMath.sol";
import "./Modifiers.sol";

contract ColorTeam is Modifiers {

    using SafeMath for uint;

    //функция формирующая команду цвета из последних 100 участников выигравшим цветом
    function formColorTeam(uint _winnerColor) private returns (uint) {
        uint paintsCurrentRoundForColor = paintsCounterForColor[_winnerColor];
         if (paintsCurrentRoundForColor>1000)
        {
            paintsCurrentRoundForColor = 1000;
        }
        for (uint i =paintsCurrentRoundForColor; i > 0; i--) {
            uint teamMembersCounter;
            if (isInCBT[cbIteration][counterToPainterForColor[_winnerColor][i]] == false) {
                
                if (paintsCurrentRoundForColor > 100) {
                    if (teamMembersCounter >= 100)   
                        break;
                }
            
                else {
                    if (teamMembersCounter >= paintsCurrentRoundForColor)
                        break;
                }
                
                cbTeam[cbIteration].push(counterToPainterForColor[_winnerColor][i]);
                teamMembersCounter = teamMembersCounter.add(1);
                isInCBT[cbIteration][counterToPainterForColor[_winnerColor][i]] = true;
            }
        }
        return cbTeam[cbIteration].length;
    }
    
    function calculateCBP(uint _winnerColor) private {

        uint length = formColorTeam(_winnerColor);
        address painter;
        uint totalPaintsForTeam; //засунуть в функцию calculateCBP

        for (uint i = 0; i < length; i++) {
            painter = cbTeam[cbIteration][i];
            totalPaintsForTeam += colorBankShare[cbIteration][_winnerColor][painter];
        }
        
        for (i = 0; i < length; i++) {
            painter = cbTeam[cbIteration][i];
            painterToCBP[cbIteration][painter] = (colorBankShare[cbIteration][_winnerColor][painter].mul(colorBankForRound[currentRound])).div(totalPaintsForTeam);
        }

    }

    function distributeCBP() external canDistributeCBP() {
        require(isCBPTransfered[cbIteration] == false, "Color Bank Prizes already transferred for this cbIteration");
        address painter;
        calculateCBP(winnerColorForRound[currentRound]);
        painterToCBP[cbIteration][winnerOfRound[currentRound]] += colorBankForRound[currentRound];
        uint length = cbTeam[cbIteration].length;
        for (uint i = 0; i < length; i++) {
            painter = cbTeam[cbIteration][i];
            if (painterToCBP[cbIteration][painter] != 0) {
                uint prize = painterToCBP[cbIteration][painter];
                painter.transfer(prize);
                emit CBPDistributed(currentRound, cbIteration, painter, prize);
            }
        }
        isCBPDistributable = false;
        isCBPTransfered[cbIteration] = true;
        currentRound = currentRound.add(1); //следующий раунд 
        cbIteration = cbIteration.add(1); //инкрементируем итерацию для банка цвета
        isGamePaused = false;
    }
    
}
// File: DividendsDistributor.sol



pragma solidity 0.4.24;
import "./SafeMath.sol";
import "./IColor.sol";
import "./Modifiers.sol";

contract DividendsDistributor is Modifiers {
    using SafeMath for uint;
    
    function claimDividends() external {
        //функция не может быть вызвана, если баланс для вывода пользователя равен нулю
        require(pendingWithdrawals[msg.sender] != 0, "Your withdrawal balance is zero.");
        claimId = claimId.add(1);
        Claim memory c;
        c.id = claimId;
        c.claimer = msg.sender;
        c.isResolved = false;
        c.timestamp = now;
        claims.push(c);
        emit DividendsClaimed(msg.sender, claimId, now);
    }

    function approveClaim(uint _claimId) public onlyAdmin() {
        
        Claim storage claim = claims[_claimId];
        
        require(!claim.isResolved);
        
        address claimer = claim.claimer;

        //Checks-Effects-Interactions pattern
        uint withdrawalAmount = pendingWithdrawals[claimer];

        //обнуляем баланс для вывода для пользователя
        pendingWithdrawals[claimer] = 0;

        //перевести пользователю баланс для вывода
        claimer.transfer(withdrawalAmount);
        
        //устанавливаем время последнего вывода средств для пользователя
        addressToLastWithdrawalTime[claimer] = now;
        emit DividendsWithdrawn(claimer, _claimId, withdrawalAmount);

        claim.isResolved = true;
    }

}
// File: ERC1538Delegate.sol



pragma solidity 0.4.24;
import "./IERC1538.sol";
import "./ERC1538QueryDelegates.sol";

/******************************************************************************\
* Implementation of ERC1538.
* Function signatures are stored in an array so functions can be queried.
/******************************************************************************/

contract ERC1538Delegate is IERC1538, ERC1538QueryDelegates {

    function updateContract(address _delegate, string _functionSignatures, string commitMessage) external onlyOwner {
        // pos is first used to check the size of the delegate contract.
        // After that pos is the current memory location of _functionSignatures.
        // It is used to move through the characters of _functionSignatures
        uint256 pos;
        if(_delegate != address(0)) {
            assembly {
                pos := extcodesize(_delegate)
            }
            require(pos > 0, "_delegate address is not a contract and is not address(0)");
        }
        // creates a bytes vesion of _functionSignatures
        bytes memory signatures = bytes(_functionSignatures);
        // stores the position in memory where _functionSignatures ends.
        uint256 signaturesEnd;
        // stores the starting position of a function signature in _functionSignatures
        uint256 start;
        assembly {
            pos := add(signatures,32)
            start := pos
            signaturesEnd := add(pos,mload(signatures))
        }
        // the function id of the current function signature
        bytes4 funcId;
        // the delegate address that is being replaced or address(0) if removing functions
        address oldDelegate;
        // the length of the current function signature in _functionSignatures
        uint256 num;
        // the current character in _functionSignatures
        uint256 char;
        // the position of the current function signature in the funcSignatures array
        uint256 index;
        // the last position in the funcSignatures array
        uint256 lastIndex;
        // parse the _functionSignatures string and handle each function
        for (; pos < signaturesEnd; pos++) {
            assembly {char := byte(0,mload(pos))}
            // 0x29 == )
            if (char == 0x29) {
                pos++;
                num = (pos - start);
                start = pos;
                assembly {
                    mstore(signatures,num)
                }
                funcId = bytes4(keccak256(signatures));
                oldDelegate = delegates[funcId];
                if(_delegate == address(0)) {
                    index = funcSignatureToIndex[signatures];
                    require(index != 0, "Function does not exist.");
                    index--;
                    lastIndex = funcSignatures.length - 1;
                    if (index != lastIndex) {
                        funcSignatures[index] = funcSignatures[lastIndex];
                        funcSignatureToIndex[funcSignatures[lastIndex]] = index + 1;
                    }
                    funcSignatures.length--;
                    delete funcSignatureToIndex[signatures];
                    delete delegates[funcId];
                    emit FunctionUpdate(funcId, oldDelegate, address(0), string(signatures));
                }
                else if (funcSignatureToIndex[signatures] == 0) {
                    require(oldDelegate == address(0), "Funcion id clash.");
                    delegates[funcId] = _delegate;
                    funcSignatures.push(signatures);
                    funcSignatureToIndex[signatures] = funcSignatures.length;
                    emit FunctionUpdate(funcId, address(0), _delegate, string(signatures));
                }
                else if (delegates[funcId] != _delegate) {
                    delegates[funcId] = _delegate;
                    emit FunctionUpdate(funcId, oldDelegate, _delegate, string(signatures));

                }
                assembly {signatures := add(signatures,num)}
            }
        }
        emit CommitMessage(commitMessage);
    }
}
// File: ERC1538QueryDelegates.sol



pragma solidity 0.4.24;
/******************************************************************************\
* 
* Contains functions for retrieving function signatures and delegate contract
* addresses.
/******************************************************************************/

import "./StorageV0.sol";
import "./IERC1538Query.sol";

contract ERC1538QueryDelegates is IERC1538Query, StorageV0 {

    function totalFunctions() external view returns(uint256) {
        return funcSignatures.length;
    }

    function functionByIndex(uint256 _index) external view returns(string memory functionSignature, bytes4 functionId, address delegate) {
        require(_index < funcSignatures.length, "functionSignatures index does not exist.");
        bytes memory signature = funcSignatures[_index];
        functionId = bytes4(keccak256(signature));
        delegate = delegates[functionId];
        return (string(signature), functionId, delegate);
    }

    function functionExists(string _functionSignature) external view returns(bool) {
        return funcSignatureToIndex[bytes(_functionSignature)] != 0;
    }

    function functionSignatures() external view returns(string) {
        uint256 signaturesLength;
        bytes memory signatures;
        bytes memory signature;
        uint256 functionIndex;
        uint256 charPos;
        uint256 funcSignaturesNum = funcSignatures.length;
        bytes[] memory memoryFuncSignatures = new bytes[](funcSignaturesNum);
        for(; functionIndex < funcSignaturesNum; functionIndex++) {
            signature = funcSignatures[functionIndex];
            signaturesLength += signature.length;
            memoryFuncSignatures[functionIndex] = signature;
        }
        signatures = new bytes(signaturesLength);
        functionIndex = 0;
        for(; functionIndex < funcSignaturesNum; functionIndex++) {
            signature = memoryFuncSignatures[functionIndex];
            for(uint256 i = 0; i < signature.length; i++) {
                signatures[charPos] = signature[i];
                charPos++;
            }
        }
        return string(signatures);
    }

    function delegateFunctionSignatures(address _delegate) external view returns(string) {
        uint256 funcSignaturesNum = funcSignatures.length;
        bytes[] memory delegateSignatures = new bytes[](funcSignaturesNum);
        uint256 delegateSignaturesPos;
        uint256 signaturesLength;
        bytes memory signatures;
        bytes memory signature;
        uint256 functionIndex;
        uint256 charPos;
        for(; functionIndex < funcSignaturesNum; functionIndex++) {
            signature = funcSignatures[functionIndex];
            if(_delegate == delegates[bytes4(keccak256(signature))]) {
                signaturesLength += signature.length;
                delegateSignatures[delegateSignaturesPos] = signature;
                delegateSignaturesPos++;
            }

        }
        signatures = new bytes(signaturesLength);
        functionIndex = 0;
        for(; functionIndex < delegateSignatures.length; functionIndex++) {
            signature = delegateSignatures[functionIndex];
            if(signature.length == 0) {
                break;
            }
            for(uint256 i = 0; i < signature.length; i++) {
                signatures[charPos] = signature[i];
                charPos++;
            }
        }
        return string(signatures);
    }

    function delegateAddress(string _functionSignature) external view returns(address) {
        require(funcSignatureToIndex[bytes(_functionSignature)] != 0, "Function signature not found.");
        return delegates[bytes4(keccak256(bytes(_functionSignature)))];
    }

    function functionById(bytes4 _functionId) external view returns(string signature, address delegate) {
        for(uint256 i = 0; i < funcSignatures.length; i++) {
            if(_functionId == bytes4(keccak256(funcSignatures[i]))) {
                return (string(funcSignatures[i]), delegates[_functionId]);
            }
        }
        revert("functionId not found");
    }

    function delegateAddresses() external view returns(address[]) {
        uint256 funcSignaturesNum = funcSignatures.length;
        address[] memory delegatesBucket = new address[](funcSignaturesNum);
        uint256 numDelegates;
        uint256 functionIndex;
        bool foundDelegate;
        address delegate;
        for(; functionIndex < funcSignaturesNum; functionIndex++) {
            delegate = delegates[bytes4(keccak256(funcSignatures[functionIndex]))];
            for(uint256 i = 0; i < numDelegates; i++) {
                if(delegate == delegatesBucket[i]) {
                    foundDelegate = true;
                    break;
                }
            }
            if(foundDelegate == false) {
                delegatesBucket[numDelegates] = delegate;
                numDelegates++;
            }
            else {
                foundDelegate = false;
            }
        }
        address[] memory delegates_ = new address[](numDelegates);
        functionIndex = 0;
        for(; functionIndex < numDelegates; functionIndex++) {
            delegates_[functionIndex] = delegatesBucket[functionIndex];
        }
        return delegates_;
    }
}
// File: Game.sol



pragma solidity 0.4.24;
import "./PaintsPool.sol";
import "./PaintDiscount.sol";
import "./Modifiers.sol";
import "./Utils.sol";

contract Game is PaintDiscount, PaintsPool, Modifiers {
    using SafeMath for uint;

     //функция оценивающая сколько будет стоить функция закрашивания
    function estimateCallPrice(uint[] _pixels, uint _color) public view returns (uint totalCallPrice) {

        uint moneySpent = moneySpentByUserForColor[_color][msg.sender];
        bool hasDiscount = hasPaintDiscountForColor[_color][msg.sender];
        uint discount = usersPaintDiscountForColor[_color][msg.sender];
        
        for (uint i = 0; i < _pixels.length; i++) {
            
            uint discountCallPrice = (nextCallPriceForColor[_color].mul(100 - discount)).div(100);
            
            if (hasDiscount == true) 
                uint price = discountCallPrice;
            else
                price = nextCallPriceForColor[_color]; 

            totalCallPrice += price;
            moneySpent += price;

            if (moneySpent >= 1 ether) {
                
                hasDiscount = true;
                discount = moneySpent / 1 ether;
                
                if (moneySpent >= 10 ether)
                    discount = 10;
            }
            
        }   
    }

    function drawTimeBank() public {

        uint lastPaintTime = lastPaintTimeForRound[currentRound];
        require ((now - lastPaintTime) > 20 minutes && lastPaintTime != 0, "20 minutes have not passed yet.");

        //распределяем банк времени команде раунда
        //победитель текущего раунда - последний закрасивший пиксель пользователь за этот раунд
        winnerOfRound[currentRound] = lastPainterForRound[currentRound];

        //разыгранный банк этого раунда = банк времени (1)
        winnerBankForRound[currentRound] = 1; 
        //10% банка времени переходит в следующий раунд
        timeBankForRound[currentRound + 1] = timeBankForRound[currentRound].div(10); 
        //45% банка времени распределится между всей командой участников раунда
        timeBankForRound[currentRound] = timeBankForRound[currentRound].mul(45).div(100); 
        //банк цвета переносится на следующий раунд
        colorBankForRound[currentRound + 1] = colorBankForRound[currentRound]; 
        //в этом раунде банк цвета обнуляется
        colorBankForRound[currentRound] = 0; 
        //ивент - был разыгран банк времени (победитель, раунд)
        emit TimeBankPlayed(winnerOfRound[currentRound], currentRound);

        isTBPDistributable = true;
        isGamePaused = true;
        timeBankDrawnForRound[currentRound] = true;

    }

    
    function paint(uint[] _pixels, uint _color, string _refLink) external payable isRegistered(_refLink) isLiveGame() {

        require(msg.value == estimateCallPrice(_pixels, _color), "Wrong call price");
        require(_color > 0 && _color <= totalColorsNumber, "The color with such id does not exist."); 

        // bytes32 refLink32 = Utils.toBytes32(_refLink);
        // require(keccak256(abi.encodePacked(_refLink)) == keccak256(abi.encodePacked()) || refLinkExists[refLink32] == true, "No such referral link exists.");
        
       //проверяем не прошло ли 20 минут с последней раскраски для розыгрыша банка времени
        if ((now - lastPaintTimeForRound[currentRound]) > 20 minutes && 
            lastPaintTimeForRound[currentRound] != 0 && 
            timeBankDrawnForRound[currentRound] == false) {

            drawTimeBank();
            msg.sender.transfer(msg.value);

        }
        
        else {
            //распределяем ставку по банкам
            _setBanks(_color);

            //закрашиваем пиксели
            for (uint i = 0; i < _pixels.length; i++) {
                _paint(_pixels[i], _color);
            }
            
            //распределяем дивиденды (пассивный доход) бенефециариам
            _distributeDividends(_color, _refLink);
        
            //сохраняем значение потраченных пользователем денег на покупку краски данного цвета
            _setMoneySpentByUserForColor(_color); 
            
            //сохраняем значение скидки на покупку краски данного цвета для пользователя
            _setUsersPaintDiscountForColor(_color);

            if (paintsCounterForColor[_color] == 0) {
                paintGenToEndTimeForColor[_color][currentPaintGenForColor[_color] - 1] = now;
            }

            paintsCounter++; //счетчик закрашивания любым цветом
            paintsCounterForColor[_color] ++; //счетчик закрашивания конкретным цветом
            counterToPainter[paintsCounter] = msg.sender; //счетчик закрашивания => пользователь
            counterToPainterForColor[_color][paintsCounterForColor[_color]] = msg.sender; //счетчик закрашивания конкретным цветом => пользователь

            if (isUserCountedForRound[currentRound][msg.sender] == false) {
                usersCounterForRound[currentRound] = usersCounterForRound[currentRound].add(1);
                isUserCountedForRound[currentRound][msg.sender] = true;
            }
        }

    }   

    //функция закрашивания пикселя цветом
    function _paint(uint _pixel, uint _color) internal {

        //устанавливаем значения для краски в пуле и цену вызова функции paint
        _fillPaintsPool(_color);
        
        require(msg.sender == tx.origin);

        require(_pixel > 0 && _pixel <= totalPixelsNumber, "The pixel with such id does not exist.");

       
        //берем предыдущий цвет данного пикселя
        uint oldColor = pixelToColorForRound[currentRound][_pixel];
    
        //перекрашиваем в новый цвет
        pixelToColorForRound[currentRound][_pixel] = _color; 
            
        //cохраняем предыдущий цвет в маппинге
        pixelToOldColorForRound[currentRound][_pixel] = oldColor; 
                
        //время последнего закрашивания во всем игровом поле в этом раунде
        lastPaintTimeForRound[currentRound] = now; 
    
        //самый последний разукрасивший пользователь на всем игровом поле в этом раунде
        lastPainterForRound[currentRound] = msg.sender;
                
        //если счетчик старого цвета положительный, уменьшаем его значение
        if (colorToPaintedPixelsAmountForRound[currentRound][oldColor] > 0) 
            colorToPaintedPixelsAmountForRound[currentRound][oldColor] = colorToPaintedPixelsAmountForRound[currentRound][oldColor].sub(1); 
    
        //при каждой раскраске пикселя, увеличиваем счетчик цвета
        colorToPaintedPixelsAmountForRound[currentRound][_color] = colorToPaintedPixelsAmountForRound[currentRound][_color].add(1); 

        //увеличиваем значение общего количества разукрашиваний данным цветом для итерации команды цвета
        colorToTotalPaintsForCBIteration[cbIteration][_color] = colorToTotalPaintsForCBIteration[cbIteration][_color].add(1);

        //увеличиваем значение общего количества разукрашиваний любым цветом для всего раунда
        totalPaintsForRound[currentRound] = totalPaintsForRound[currentRound].add(1); 

        pixelToPaintTimeForRound[currentRound][_pixel] = now;

        //если пользователь делал ставку данным цветом в течение 24 часов, повышаем его долю участия в команде времени
        if (lastPaintTimeOfUser[msg.sender] != 0 && now - lastPaintTimeOfUser[msg.sender] < 24 hours) 
            timeBankShare[tbIteration][msg.sender]++;
            
        else    
            timeBankShare[tbIteration][msg.sender] = 1;

        //если пользователь делал ставку данным цветом в течение 24 часов, повышаем его долю участия в команде цвета
        if (lastPaintTimeOfUserForColor[_color][msg.sender] != 0 && now - lastPaintTimeOfUserForColor[_color][msg.sender] < 24 hours) 
            colorBankShare[cbIteration][_color][msg.sender]++;

        else 
            colorBankShare[cbIteration][_color][msg.sender] = 1;

        lastPaintTimeOfUser[msg.sender] = now;
        lastPaintTimeOfUserForColor[_color][msg.sender] = now;
                
        //с каждым закрашиванием декреминтируем на 1 ед краски
        paintGenToAmountForColor[_color][currentPaintGenForColor[_color]] = paintGenToAmountForColor[_color][currentPaintGenForColor[_color]].sub(1);
        
        //сохраняем значени поседнего закрашенного пикселя за раунд
        lastPaintedPixelForRound[currentRound] = _pixel;
        
        //ивент - закрашивание пикселя (пиксель, цвет, закрасивший пользователь)
        emit Paint(_pixel, _color, msg.sender, currentRound, now);    

        //устанавливаем значение последнего сыгранного раунда для пользователя равным текущему раунду
        lastPlayedRound[msg.sender] = currentRound;
            
        //проверяем не закрасилось ли все игровое поле данным цветом для розыгрыша банка цвета
        if (colorToPaintedPixelsAmountForRound[currentRound][_color] == totalPixelsNumber) {

            //цвет победивший в текущем раунде
            winnerColorForRound[currentRound] = _color;

            //распределяем банк цвета команде цвета
            winnerOfRound[currentRound] = lastPainterForRound[currentRound];        
            
            winnerBankForRound[currentRound] = 2;//разыгранный банк этого раунда = банк цвета (2)

            //50% банка цвета распределится между командой цвета раунда
            colorBankForRound[currentRound] = colorBankForRound[currentRound].div(2);

            timeBankForRound[currentRound + 1] = timeBankForRound[currentRound];//банк времени переносится на следующий раунд
            timeBankForRound[currentRound] = 0;//банк времени в текущем раунде обнуляется      
            emit ColorBankPlayed(winnerOfRound[currentRound], currentRound);  
            
            isGamePaused = true;
            isCBPDistributable = true;
            //distributeCBP();
        }
    }

    //функция распределения ставки
    function _setBanks(uint _color) private {
        
        colorBankToColorForRound[currentRound][_color] = colorBankToColorForRound[currentRound][_color].add(msg.value.mul(40).div(100));

        //40% ставки идет в банк цвета
        colorBankForRound[currentRound] = colorBankForRound[currentRound].add(msg.value.mul(40).div(100));

        //40% ставки идет в банк времени
        timeBankForRound[currentRound] = timeBankForRound[currentRound].add(msg.value.mul(40).div(100));

        //20% ставки идет на пассивные доходы бенефециариев
        dividendsBank = dividendsBank.add(msg.value.div(5)); 
    }

    //функция распределения дивидендов (пассивных доходов) - будет работать после подключения инстансов контрактов Цвета и Пикселя
    function _distributeDividends(uint _color, string _refLink) internal {
        
        //require(ownerOfColor[_color] != address(0), "There is no such color");
        bytes32 refLink32 = Utils.toBytes16(_refLink);
    
        //if  reflink provided
        if (refLinkExists[refLink32] == true) { 

            //25% дивидендов распределяем организаторам (может быть смарт контракт)
            pendingWithdrawals[founders] = pendingWithdrawals[founders].add(dividendsBank.div(4)); 

            //25% дивидендов распределяем бенефециарию цвета
            pendingWithdrawals[ownerOfColor[_color]] += dividendsBank.div(4);

            pendingWithdrawals[ownerOfPixel] += dividendsBank.div(4);

            //25% дивидендов распределяем реферу
            pendingWithdrawals[refLinkToUser[refLink32]] += dividendsBank.div(4);
            dividendsBank = 0;
        }

        else {

            pendingWithdrawals[founders] = pendingWithdrawals[founders].add(dividendsBank.div(3)); 
            pendingWithdrawals[ownerOfColor[_color]] += dividendsBank.div(3);
            pendingWithdrawals[ownerOfPixel] += dividendsBank.div(3);
            dividendsBank = 0;
        }
    }

    modifier isRegistered(string _refLink) {
        //если пользователь еще не зарегистрирован
        if (isRegisteredUser[msg.sender] != true) {
            bytes32 refLink32 = Utils.toBytes16(_refLink);
            //если такая реф ссылка действительно существует 
            if (refLinkExists[refLink32]) { 
                address referrer = refLinkToUser[refLink32];
                referrerToReferrals[referrer].push(msg.sender);
                referralToReferrer[msg.sender] = referrer;
                hasReferrer[msg.sender] = true;
            }
            uniqueUsersCount = uniqueUsersCount.add(1);
            newUserToCounter[msg.sender] = uniqueUsersCount;
            registrationTimeForUser[msg.sender] = now;
            isRegisteredUser[msg.sender] = true;
        }
        _;
    }

}
// File: GameStateController.sol



pragma solidity 0.4.24;
import "./Roles.sol";
import "./Modifiers.sol";

contract GameStateController is Modifiers {

    function pauseGame() external onlyAdmin() {
        require (isGamePaused == false, "Game is already paused");
        isGamePaused = true;
    }

    function resumeGame() external onlyAdmin() {
        require (isGamePaused == true, "Game is already live");
        isGamePaused = false;
    }

    function withdrawEther() external onlyAdmin() returns (bool) {
        require (isGamePaused == true, "Can withdraw when game is live");
        uint balance = address(this).balance;
        uint colorBank = colorBankForRound[currentRound];
        uint timeBank = timeBankForRound[currentRound];
        owner().transfer(balance);
        colorBankForRound[currentRound]= 0;
        timeBankForRound[currentRound]= 0;
        emit EtherWithdrawn(balance, colorBank, timeBank, now);
        return true;
    }
    
}

// File: Helpers.sol



pragma solidity 0.4.24;
import "./Modifiers.sol";

contract Helpers is Modifiers {

    //возвращает цвет пикселя в этом раунде (на данный момент)
    function getPixelColor(uint _pixel) external view returns (uint) {
        return pixelToColorForRound[currentRound][_pixel];
    }

    //функция добавления нового цвета в игру (после mint-инга нового цвета)
    function addNewColor() external onlyAdmin() {
        totalColorsNumber++; //TODO - Check whether this line should be put in the end or here
        currentPaintGenForColor[totalColorsNumber] = 1;
        callPriceForColor[totalColorsNumber] = 0.01 ether;
        nextCallPriceForColor[totalColorsNumber] = callPriceForColor[totalColorsNumber];
        paintGenToAmountForColor[totalColorsNumber][currentPaintGenForColor[totalColorsNumber]] = maxPaintsInPool;
        paintGenStartedForColor[totalColorsNumber][currentPaintGenForColor[totalColorsNumber]] = true;
        paintGenToEndTimeForColor[totalColorsNumber][currentPaintGenForColor[totalColorsNumber] - 1] = now;
        paintGenToStartTimeForColor[totalColorsNumber][currentPaintGenForColor[totalColorsNumber]] = now;
    }

}
// File: IColor.sol



pragma solidity 0.4.24;

interface Color {
    function totalSupply() external view returns (uint);
    function ownerOf(uint _tokenId) external view returns (address);
}


// File: IERC1538.sol



pragma solidity 0.4.24;

interface IERC1538 {
    event CommitMessage(string message);
    event FunctionUpdate(bytes4 indexed functionId, address indexed oldDelegate, address indexed newDelegate, string functionSignature);
    function updateContract(address _delegate, string _functionSignatures, string commitMessage) external;
}
// File: IERC1538Query.sol



pragma solidity 0.4.24;

interface IERC1538Query {
    function totalFunctions() external view returns(uint256);
    function functionByIndex(uint256 _index) external view returns(string memory functionSignature, bytes4 functionId, address delegate);
    function functionExists(string _functionSignature) external view returns(bool);
    function functionSignatures() external view returns(string);
    function delegateFunctionSignatures(address _delegate) external view returns(string);
    function delegateAddress(string _functionSignature) external view returns(address);
    function functionById(bytes4 _functionId) external view returns(string signature, address delegate);
    function delegateAddresses() external view returns(address[]);
}
// File: Initializer.sol



pragma solidity 0.4.24;
import "./StorageV1.sol";

contract Initializer is StorageV1 {

    //constructor
    function _initializer() internal {

        
        refLinkPrice  = 0.1 ether;
        totalColorsNumber = 8;
        totalPixelsNumber = 49;
        
        isAdmin[msg.sender] = true;
        maxPaintsInPool = totalPixelsNumber; 
        currentRound = 1;
        cbIteration = 1;
        tbIteration = 1;
        
        for (uint i = 1; i <= totalColorsNumber; i++) {
            currentPaintGenForColor[i] = 1;
            callPriceForColor[i] = 0.005 ether;
            nextCallPriceForColor[i] = callPriceForColor[i];
            paintGenToAmountForColor[i][currentPaintGenForColor[i]] = maxPaintsInPool;
            paintGenStartedForColor[i][currentPaintGenForColor[i]] = true;
            //paintGenToEndTimeForColor[i][currentPaintGenForColor[i] - 1] = now;
            paintGenToStartTimeForColor[i][currentPaintGenForColor[i]] = now;
        }
        
    }
}

// File: IPixel.sol



pragma solidity 0.4.24;

interface Pixel {
    function totalSupply() external view returns (uint);
    function ownerOf(uint _tokenId) external view returns (address);
}


// File: Migrations.sol



pragma solidity ^0.4.23;

contract Migrations {
  address public owner;
  uint public last_completed_migration;

  constructor() public {
    owner = msg.sender;
  }

  modifier restricted() {
    if (msg.sender == owner) _;
  }

  function setCompleted(uint completed) public restricted {
    last_completed_migration = completed;
  }

  function upgrade(address new_address) public restricted {
    Migrations upgraded = Migrations(new_address);
    upgraded.setCompleted(last_completed_migration);
  }
}

// File: Modifiers.sol



pragma solidity 0.4.24;
import "./SafeMath.sol";
import "./StorageV1.sol";

contract Modifiers is StorageV1 {
    using SafeMath for uint;

    modifier onlyAdmin() {
        require(isAdmin[msg.sender] == true, "You don't have admin rights.");
        _;
    }

    modifier isLiveGame() {
        require(isGamePaused == false, "Game is paused.");
        _;
    }

    modifier canDistributeCBP() {
        require(isCBPDistributable == true, "Cannot distribute color bank prize at the moment.");
        _;
    }

    modifier canDistributeTBP() {
        require(isTBPDistributable == true, "Cannot distribute time bank prize at the moment.");
        _;
    }

    //модификатор проверяющий длину реф. ссылки (длина должна быть в диапазоне от 4 до 8 символов)
    modifier isValidRefLink(string _str) {
        require(bytes(_str).length >= 4, "Ref link should be of length [4,8]");
        require(bytes(_str).length <= 8, "Ref link should be of length [4,8]");
        _;
    }
    
}
// File: Ownable.sol



pragma solidity 0.4.24;

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address private _owner;

  event OwnershipTransferred(
    address indexed previousOwner,
    address indexed newOwner
  );

  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  constructor() internal {
    _owner = msg.sender;
    emit OwnershipTransferred(address(0), _owner);
  }

  /**
   * @return the address of the owner.
   */
  function owner() public view returns(address) {
    return _owner;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(isOwner());
    _;
  }

  /**
   * @return true if `msg.sender` is the owner of the contract.
   */
  function isOwner() public view returns(bool) {
    return msg.sender == _owner;
  }

  /**
   * @dev Allows the current owner to relinquish control of the contract.
   * @notice Renouncing to ownership will leave the contract without an owner.
   * It will not be possible to call the functions with the `onlyOwner`
   * modifier anymore.
   */
  function renounceOwnership() public onlyOwner {
    emit OwnershipTransferred(_owner, address(0));
    _owner = address(0);
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    _transferOwnership(newOwner);
  }

  /**
   * @dev Transfers control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function _transferOwnership(address newOwner) internal {
    require(newOwner != address(0));
    emit OwnershipTransferred(_owner, newOwner);
    _owner = newOwner;
  }
}

// File: PaintDiscount.sol



pragma solidity 0.4.24;
import "./SafeMath.sol";
import "./StorageV1.sol";

contract PaintDiscount is StorageV1 {
    using SafeMath for uint;
    
    //функция сохраняющая скидку на покупку краски определенного цвета для пользователя
    function _setUsersPaintDiscountForColor(uint _color) internal {
        
        //за каждый потраченный 1 ETH даем скидку 1%
        usersPaintDiscountForColor[_color][msg.sender] = moneySpentByUserForColor[_color][msg.sender] / 1 ether;
        
        //максимальная скидка может равняться 10%
        if (moneySpentByUserForColor[_color][msg.sender] >= 10 ether)
            usersPaintDiscountForColor[_color][msg.sender] = 10;
        
    }
    
    //функция сохраняющая общюю сумму потраченную пользователем на покупку краски определенного цвета за все время
    function _setMoneySpentByUserForColor(uint _color) internal {
        
        moneySpentByUserForColor[_color][msg.sender] += msg.value;
        moneySpentByUser[msg.sender] += msg.value;

        if (moneySpentByUserForColor[_color][msg.sender] >= 1 ether)
            hasPaintDiscountForColor[_color][msg.sender] = true;
    }
}
// File: PaintsPool.sol



pragma solidity ^0.4.24;
import "./SafeMath.sol";
import "./StorageV1.sol";

contract PaintsPool is StorageV1 {
    using SafeMath for uint;

    //функция обновления цены вызова функции закрашивания (paint)
    function _updateCallPrice(uint _color) private {
        
        //увеличиваем цену вызова на 5% (используем для отображения на фронте)
        nextCallPriceForColor[_color] = callPriceForColor[_color].mul(105).div(100);
        
        //вызываем ивент о том, что цена вызова функции paint обновлена
        emit CallPriceUpdated(callPriceForColor[_color]);
    

    }
    //функция пополнения пула краски
    function _fillPaintsPool(uint _color) internal {

        //следующее поколение краски
        uint nextPaintGen = currentPaintGenForColor[_color].add(1);
        //каждые полторы минуты пул дополняется новой краской
        if (now - paintGenToEndTimeForColor[_color][currentPaintGenForColor[_color] - 1] >= 1.5 minutes) { 
            
            //сколько краски остается в поколении
            uint paintsRemain = paintGenToAmountForColor[_color][currentPaintGenForColor[_color]]; 
            
            //если прошло полторы минуты и след. поколение краски все еще не создано     
            if (paintGenStartedForColor[_color][nextPaintGen] == false) {
                
                //создаем новое поколение краски на недостающее количество единиц
                paintGenToAmountForColor[_color][nextPaintGen] = maxPaintsInPool.sub(paintsRemain); 
                
                //новое поколение создалось сейчас
                paintGenToStartTimeForColor[_color][nextPaintGen] = now; 

                paintGenStartedForColor[_color][nextPaintGen] = true;
            }
            
            if (paintGenToAmountForColor[_color][currentPaintGenForColor[_color]] == 1) {
                
                //обновляем цену вызова закрашивания краской следующего поколения
                _updateCallPrice(_color);
                
                //краска текущего поколения закончилась сейчас
                paintGenToEndTimeForColor[_color][currentPaintGenForColor[_color]] = now;
            }
               
            //как только не осталось краски текущего поколения
            if (paintGenToAmountForColor[_color][currentPaintGenForColor[_color]] == 0) {
                
                //цена вызова закрашивания краской текущего поколения
                callPriceForColor[_color] = nextCallPriceForColor[_color];

                if (paintGenToAmountForColor[_color][nextPaintGen] == 0) {
                    paintGenToAmountForColor[_color][nextPaintGen] = maxPaintsInPool;
                }
                //переходим на использование следующего поколения краски
                currentPaintGenForColor[_color] = nextPaintGen;
            }
        }
        ///если не прошло полторы минуты
        else {

            if (paintGenToAmountForColor[_color][currentPaintGenForColor[_color]] == 0) {
               
                paintGenToAmountForColor[_color][nextPaintGen] = maxPaintsInPool;
                //переходим на использование следующего поколения краски
                currentPaintGenForColor[_color] = nextPaintGen;
            }

        }
    }
}
// File: Referral.sol



pragma solidity 0.4.24;
import "./SafeMath.sol";
import "./Modifiers.sol";
import "./Utils.sol";

contract Referral is Modifiers {
    using SafeMath for uint;
    
    //функция для покупки реферальной ссылки для пользователя (длина в диапазоне от 4 до 8 символов)
    function buyRefLink(string _refLink) isValidRefLink (_refLink) external payable {
        require(msg.value == refLinkPrice, "Setting referral link costs 0.1 ETH.");
        require(hasRefLink[msg.sender] == false, "You have already generated your ref link.");
        bytes32 refLink32 = Utils.toBytes16(_refLink);
        require(refLinkExists[refLink32] != true, "This referral link already exists, try different one.");
        hasRefLink[msg.sender] = true;
        userToRefLink[msg.sender] = _refLink;
        refLinkExists[refLink32] = true;
        refLinkToUser[refLink32] = msg.sender;
        owner().transfer(msg.value);
    }

    //функция возвращающая список реферралов для пользователя _user
    function getReferralsForUser(address _user) external view returns (address[]) {
        return referrerToReferrals[_user];
    }

    function getReferralData(address _user) external view returns (uint registrationTime, uint moneySpent) {
        registrationTime = registrationTimeForUser[_user];
        moneySpent = moneySpentByUser[_user];
    }
    
}
// File: Roles.sol



pragma solidity 0.4.24;
import "./Modifiers.sol";

contract Roles is Modifiers {
    
    function addAdmin(address _new) external onlyOwner() {
        isAdmin[_new] = true;
    }
    
    function removeAdmin(address _admin) external onlyOwner() {
        isAdmin[_admin] = false;
    }

    function renounceAdmin() external onlyAdmin() {
        isAdmin[msg.sender] = false;
    }

}
// File: Router.sol



pragma solidity 0.4.24;
pragma experimental "v0.5.0";
import "./Initializer.sol";

contract Router is Initializer {
    
    event CommitMessage(string message);
    event FunctionUpdate(bytes4 indexed functionId, address indexed oldDelegate, address indexed newDelegate, string functionSignature);

    constructor(address _erc1538Delegate) public  {

        //Adding ERC1538 updateContract function
        bytes memory signature = "updateContract(address,string,string)";
        bytes4 funcId = bytes4(keccak256(signature));
        delegates[funcId] = _erc1538Delegate;
        funcSignatures.push(signature);
        funcSignatureToIndex[signature] = funcSignatures.length;
        emit FunctionUpdate(funcId, address(0), _erc1538Delegate, string(signature));
        emit CommitMessage("Added ERC1538 updateContract function at contract creation");
    
        _initializer();
    }

    function() external payable {
        address delegate = delegates[msg.sig];
        require(delegate != address(0), "Function does not exist.");
        assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize)
            let result := delegatecall(gas, delegate, ptr, calldatasize, 0, 0)
            let size := returndatasize
            returndatacopy(ptr, 0, size)
            switch result
            case 0 {revert(ptr, size)}
            default {return (ptr, size)}
        }
    }
}
// File: SafeMath.sol



pragma solidity 0.4.24;

/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert on error
 */
library SafeMath {

    /**
    * @dev Multiplies two numbers, reverts on overflow.
    */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b);

        return c;
    }

    /**
    * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
    */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0); // Solidity only automatically asserts 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 Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
    */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a);
        uint256 c = a - b;

        return c;
    }

    /**
    * @dev Adds two numbers, reverts on overflow.
    */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a);

        return c;
    }

    /**
    * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
    * reverts when dividing by zero.
    */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0);
        return a % b;
    }
}
// File: StorageV0.sol



pragma solidity 0.4.24;
import "./Ownable.sol";

contract StorageV0 is Ownable {

    // maps functions to the delegate contracts that execute the functions
    // funcId => delegate contract
    mapping(bytes4 => address) internal delegates;

    // array of function signatures supported by the contract
    bytes[] internal funcSignatures;

    // maps each function signature to its position in the funcSignatures array.
    // signature => index+1
    mapping(bytes => uint256) internal funcSignatureToIndex;

}
// File: StorageV1.sol



pragma solidity 0.4.24;
import "./StorageV0.sol";
import "./IColor.sol";
import "./IPixel.sol";

contract StorageV1 is StorageV0 {

    //цвет клетки в раунде (pаунд => пиксель => цвет)
    mapping (uint => mapping (uint => uint)) public pixelToColorForRound; 

    //предыдущий цвет клетки в раунде (pаунд => пиксель => цвет)
    mapping (uint => mapping (uint => uint)) public pixelToOldColorForRound; 

    //маппинг цвета на количество клеток закрашенных этим цветом за раунд (раунд => цвет => количество пикселей)
    mapping (uint => mapping (uint => uint)) public colorToPaintedPixelsAmountForRound; 

    //банк цвета за раунд (раунд => банк цвета)
    mapping (uint => uint) public colorBankForRound; 

    //банк цвета для отдельного цвета за раунд (раунд => цвет => банк цвета)
    mapping (uint => mapping (uint => uint)) public colorBankToColorForRound; 

    //банк времени за раунд (раунд => банк времени)
    mapping (uint => uint) public timeBankForRound; 
    
    //время самого последнего закрашивания игрового поля за раунд (раунд => таймстэмп)
    mapping (uint => uint) public lastPaintTimeForRound; 

    //последний закрасивший любой пиксель пользователь за раунд (раунд => адрес)
    mapping (uint => address) public lastPainterForRound; 

    //последний разукрашенный пиксель за раунд
    mapping (uint => uint) public lastPaintedPixelForRound;

    //цвет который выиграл (которым заполнились все пиксели) за раунд (раунд => цвет) 
    mapping (uint => uint) public winnerColorForRound; 

    //значение общего количества разукрашиваний данным цветом за весь раунд (раунд => цвет => количество разукрашиваний)
    mapping (uint => mapping (uint => uint)) public colorToTotalPaintsForCBIteration; 

    //победитель раунда (раунд => адрес)
    mapping (uint => address) public winnerOfRound; 

    //банк который был разыгран в раунде (раунд => разыгранный банк) (1 = банк времени, 2 = банк цвета)
    mapping (uint => uint) public winnerBankForRound; 

    //время закрашивания для каждого пикселя за раунд (0 = не закрашено)
    mapping (uint => mapping (uint => uint)) public pixelToPaintTimeForRound;

    //сколько всего было разукрашиваний в этом раунде любым цветом
    mapping (uint => uint) public totalPaintsForRound;
        
    //поколение краски на ее количество
    mapping (uint => mapping (uint => uint)) public paintGenToAmountForColor;
    
    //время когда краска определенного поколения добавилась в пул
    mapping (uint => mapping (uint => uint)) public paintGenToStartTimeForColor;
    
    //время когда краска определенного поколения закончилась в пуле
    mapping (uint => mapping (uint => uint)) public paintGenToEndTimeForColor;
    
    //булевое значение - о том что, поколение краски началось
    mapping (uint => mapping (uint => bool)) public paintGenStartedForColor;

    //текущее поколение краски которое расходуется в данный момент
    mapping (uint => uint) public currentPaintGenForColor;
    
    //стоимость вызова функции paint
    mapping (uint => uint) public callPriceForColor;
    
    //стоимость следующего вызова функции paint
    mapping (uint => uint) public nextCallPriceForColor;
    
    //общее количество денег потраченных пользователем на покупку краски данного цвета
    mapping (uint => mapping (address => uint)) public moneySpentByUserForColor;

    //общее количество денег потраченных пользователем на покупку краски любого цвета
    mapping (address => uint) public moneySpentByUser;
    
    //маппинг хранящий булевое значение о том, имеет ли пользователь какую либо скидку на покупку краски определенного цвета
    mapping (uint => mapping (address => bool)) public hasPaintDiscountForColor;
    
    //скидка пользователя на покупку краски определенного цвета (в процентах)
    mapping (uint => mapping (address => uint)) public usersPaintDiscountForColor;

     //зарегистрированный пользователь
    mapping (address => bool) public isRegisteredUser;
    
    //пользователь имеет свою реферальную ссылку (аккредитованный для получения дивидендов рефера)
    mapping (address => bool) public hasRefLink;

    //маппинг реферала к реферу
    mapping (address => address) public referralToReferrer;

    //маппинг реферера к его рефералам
    mapping (address => address[]) public referrerToReferrals;
    
    //маппинг пользователя на наличие рефера
    mapping (address => bool) public hasReferrer;
    
    //маппинг пользователя к его реф ссылке
    mapping (address => string) public userToRefLink;
    
    //маппинг реф ссылки к пользователю - владельцу этой реф ссылки
    mapping (bytes32 => address) public refLinkToUser;
    
    //маппинг проверяющий существование (наличие в базе) реф ссылки
    mapping (bytes32 => bool) public refLinkExists;
    
    //маппинг пользователь к счетчику уникальных зарегистрированных пользователей 
    mapping (address => uint) public newUserToCounter;
    
    //счетчик уникальных пользователей
    uint public uniqueUsersCount;

    //количество единиц краски в общем пуле (225)
    uint public maxPaintsInPool;

    //текущий раунд
    uint public currentRound;

    //итерация банка времени
    uint public tbIteration;

    //итерация банка цвета
    uint public cbIteration;

    //счетчик закрашиваний любым цветом за все время
    uint public paintsCounter; 

    //Time Bank Iteration => Painter => Painter's Share in Time Team
    mapping (uint => mapping (address => uint)) public timeBankShare;

    //Color Bank Iteration => Color => Painter => Painter's Share in Time Team
    mapping (uint => mapping (uint => mapping (address => uint))) public colorBankShare;

    //счетчик закрашиваний конкретным цветом за все время
    mapping (uint => uint) public paintsCounterForColor; 

    //cbIteration => команда цвета
    mapping (uint => address[]) public cbTeam; 

    //tbIteration => команда цвета
    mapping (uint => address[]) public tbTeam;

     //cчетчик => пользователь
    mapping (uint => address) public counterToPainter;

    //цвет => cчетчик => пользователь    
    mapping (uint => mapping (uint => address)) public counterToPainterForColor; 

    //cbIteration => пользователь !should not be public
    mapping (uint => mapping (address => bool)) public isInCBT; 

    //tbIteration => пользователь !should not be public
    mapping (uint => mapping (address => bool)) public isInTBT; 

    //cbIteration => painter => color bank prize
    mapping (uint => mapping (address => uint)) public painterToCBP; 

    //tbIteration => painter => time bank prize
    mapping (uint => mapping (address => uint)) public painterToTBP; 

    //сbIteration => булевое значение
    mapping (uint => bool) public isCBPTransfered;

    //tbIteration => булевое значент
    mapping (uint => bool) public isTBPTransfered;

    //последний раунд в котором пользователь принимал участие
    mapping (address => uint) public lastPlayedRound;
    
    //Dividends Distribution
    mapping (uint => address) public ownerOfColor;

     //балансы доступные для вывода (накопленный пассивный доход за все раунды)
    mapping (address => uint) public pendingWithdrawals; 
    
    //время последнего вывода пассивного дохода для адреса для любого раунда (адрес => время)
    mapping (address => uint) public addressToLastWithdrawalTime; 
    
    //банк пассивных доходов
    uint public dividendsBank;

    struct Claim {
        uint id;
        address claimer;
        bool isResolved;
        uint timestamp;
    }

    uint public claimId;

    Claim[] public claims;

    // захардкоженные адреса для тестирования функции claimDividens()
    // в продакшене это будут адреса бенефециариев Цветов и Пикселей : pendingWithdrawals[ownerOf(_pixel)], pendingWithdrawals[ownerOf(_color)]
    address public ownerOfPixel = 0xca35b7d915458ef540ade6068dfe2f44e8fa733c;
    address public founders = 0xe04f921cf3d6c882C0FAa79d0810a50B1101e2D4; 

    bool public isGamePaused;
    
    bool public isCBPDistributable;
    bool public isTBPDistributable;
    
    mapping(address => bool) public isAdmin;

    Color public colorInstance;
    Pixel public pixelInstance;

    uint public totalColorsNumber; // 8
    uint public totalPixelsNumber; //225 in V1

    //цена приобретения реферальной ссылки
    uint public refLinkPrice; 

    //время регистрации пользователя
    mapping (address => uint) public registrationTimeForUser;

    mapping (address => uint) public lastPaintTimeOfUser;
    mapping (uint => mapping (address => uint)) public lastPaintTimeOfUserForColor;

    mapping (uint => bool) public timeBankDrawnForRound;

    //количество пользователей принимавших участие за раунд
    mapping (uint => uint) public usersCounterForRound;
    //входит ли пользователь в число учтенных пользователей за раунд
    mapping (uint => mapping (address => bool)) public isUserCountedForRound;

   // Events

    event CBPDistributed(uint indexed round, uint indexed cbIteration, address indexed winner, uint prize);
    event DividendsWithdrawn(address indexed withdrawer, uint indexed claimId, uint indexed amount);
    event DividendsClaimed(address indexed claimer, uint indexed claimId, uint indexed currentTime);
    event Paint(uint indexed pixelId, uint colorId, address indexed painter, uint indexed round, uint timestamp);
    event ColorBankPlayed(address winnerOfRound, uint indexed round);
    event TimeBankPlayed(address winnerOfRound, uint indexed round);
    event CallPriceUpdated(uint indexed newCallPrice);
    event TBPDistributed(uint indexed round, uint indexed tbIteration, address indexed winner, uint prize);
    event EtherWithdrawn(uint balance, uint colorBank, uint timeBank, uint timestamp);
}
// File: TimeTeam.sol



pragma solidity 0.4.24;
import "./SafeMath.sol";
import "./Modifiers.sol";

contract TimeTeam is Modifiers {
    using SafeMath for uint;

    //функция формирующая команду времени из последних 100 участников любым цветом
    function formTimeTeam() private returns (uint) {
    uint paintsCurrentRound = paintsCounter; 
  	  if (paintsCurrentRound>1000)
        {
            paintsCurrentRound = 1000;
        }
        
        for (uint i = paintsCurrentRound; i > 0; i--) {
            uint teamMembersCounter;
            if (isInTBT[tbIteration][counterToPainter[i]] == false) {
                
                  if (teamMembersCounter >= paintsCurrentRound)
                        break;
              
                else {
                    if (teamMembersCounter >= 50)   
                        break;
                }
                
                
                tbTeam[tbIteration].push(counterToPainter[i]);
                teamMembersCounter = teamMembersCounter.add(1);
                isInTBT[tbIteration][counterToPainter[i]] = true;
            }
        }
        return tbTeam[tbIteration].length;
    }
    
    function calculateTBP() private {

        uint length = formTimeTeam();
        address painter;
        uint totalPaintsForTeam; 

        for (uint i = 0; i < length; i++) {
            painter = tbTeam[tbIteration][i];
            totalPaintsForTeam += timeBankShare[tbIteration][painter];
        }

        for (i = 0; i < length; i++) {
            painter = tbTeam[tbIteration][i];
            painterToTBP[tbIteration][painter] = (timeBankShare[tbIteration][painter].mul(timeBankForRound[currentRound])).div(totalPaintsForTeam);
        }

    }

   function resetPaintsPool() internal {
        
    
    for (uint i = 1; i<totalColorsNumber;i++){
      
        callPriceForColor[i] = 0.005 ether;
        nextCallPriceForColor[i] = callPriceForColor[i];
        currentPaintGenForColor[i]= 1;
        
        paintGenToAmountForColor[i][currentPaintGenForColor[i]] = maxPaintsInPool;
        paintGenStartedForColor[i][currentPaintGenForColor[i]] = true;
        paintGenToStartTimeForColor[i][currentPaintGenForColor[i]] = now;
    }
    
    }

    function distributeTBP() external canDistributeTBP() {
        require(isTBPTransfered[tbIteration] == false, "Time Bank Prizes already transferred for this tbIteration");
        address painter;
        calculateTBP();
        painterToTBP[tbIteration][winnerOfRound[currentRound]] += timeBankForRound[currentRound];
        uint length = tbTeam[tbIteration].length;
        for (uint i = 0; i < length; i++) {
            painter = tbTeam[tbIteration][i];
            if (painterToTBP[tbIteration][painter] != 0) {
                uint prize = painterToTBP[tbIteration][painter];
                painter.transfer(prize);
                emit TBPDistributed(currentRound, tbIteration, painter, prize);
            }
        }
        isTBPDistributable = false;
        isTBPTransfered[tbIteration] = true;
        resetPaintsPool();
        currentRound = currentRound.add(1); //следующий раунд 
        tbIteration = tbIteration.add(1); //инкрементируем итерацию для банка цвета
        isGamePaused = false;
    }
}
// File: Utils.sol



pragma solidity 0.4.24;

library Utils {
    
    // convert a string less than 32 characters long to bytes32
    function toBytes16(string _string) pure internal returns (bytes16) {
        // make sure that the string isn't too long for this function
        // will work but will cut off the any characters past the 32nd character
        bytes16 _stringBytes;
        string memory str = _string;
    
        // simplest way to convert 32 character long string
        assembly {
          // load the memory pointer of string with an offset of 32
          // 32 passes over non-core data parts of string such as length of text
          _stringBytes := mload(add(str, 32))
        }
        return _stringBytes;
    }

    
    
}
// File: Wrapper.sol



pragma solidity 0.4.24;
import "./Modifiers.sol";

/**
** Wrapper for Router Contract to interact with all the functions' signatures
**/

contract Wrapper is Modifiers {

    //ColorTeam.sol
    function distributeCBP() external {}

    //TimeTeam.sol
    function distributeTBP() external {}

    //DividendsDistributor.sol
    function claimDividends() external {}
    function approveClaim(uint _claimId) public {}

    //GameStateController.sol
    function pauseGame() external {}
    function resumeGame() external {}
    function withdrawEther() external returns (bool) {}

    //Referral.sol
    function buyRefLink(string _refLink) external payable {}
    function getReferralsForUser(address _user) external view returns (address[]) {}
    function getReferralData(address _user) external view returns (uint registrationTime, uint moneySpent) {}

    //Roles.sol
    function addAdmin(address _new) external {}
    function removeAdmin(address _admin) external {}
    function renounceAdmin() external {}

    //Game.sol
    function estimateCallPrice(uint[] _pixels, uint _color) public view returns (uint totalCallPrice) {}
    function paint(uint[] _pixels, uint _color, string _refLink) external payable {}
    function drawTimeBank() public {}

    //ERC1538.sol
    function updateContract(address _delegate, string _functionSignatures, string commitMessage) external {}

    //GameMock.sol
    function mock() external {}
    function mock2() external {}
    function mock3(uint _winnerColor) external {}
    function mockMaxPaintsInPool() external {}

    //Helpers.sol
    function getPixelColor(uint _pixel) external view returns (uint) {}
    function addNewColor() external {}

}