contract ABToken is IERC721, iABToken, ERC165 {
    using SafeMath for uint256;
    using SafeMath for uint8;
    using Address for address;
    uint256 public totalTokens;
    
    //Mapping or which IDs each address owns
    mapping(address => uint256[]) public ownerABTokenCollection;
    

    // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
    // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
    bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;

    // Mapping from token ID to owner
    mapping (uint256 => address) private _tokenOwner;

    // Mapping from token ID to approved address
    mapping (uint256 => address) private _tokenApprovals;

    // Mapping from owner to number of owned token
    mapping (address => uint256) private _ownedTokensCount;

    // Mapping from owner to operator approvals
    mapping (address => mapping (address => bool)) private _operatorApprovals;

    bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
    /*
     * 0x80ac58cd ===
     *     bytes4(keccak256('balanceOf(address)')) ^
     *     bytes4(keccak256('ownerOf(uint256)')) ^
     *     bytes4(keccak256('approve(address,uint256)')) ^
     *     bytes4(keccak256('getApproved(uint256)')) ^
     *     bytes4(keccak256('setApprovalForAll(address,bool)')) ^
     *     bytes4(keccak256('isApprovedForAll(address,address)')) ^
     *     bytes4(keccak256('transferFrom(address,address,uint256)')) ^
     *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
     *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
     */
     
     
       //current and max numbers of issued tokens for each series 
    uint32[100] public currentTokenNumbers;
    uint32[100] public maxTokenNumbers;
    
    //current price of each angel and accessory series
    uint[24] public angelPrice;
    uint[18] public accessoryPrice;
 
     address proxyRegistryAddress; 
     
   //  Main data structure for each token 
struct ABCard {
    uint256 tokenId;       
        uint8 cardSeriesId;
        //This is 0 to 23 for angels, 24 to 42 for pets, 43 to 60 for accessories, 61 to 72 for medals
        //address owner; 
        //already accounted in mapping. 
        uint16 power;
        //This number is luck for pets and battlepower for angels
        uint16 auraRed;
        uint16 auraYellow;
        uint16 auraBlue;
        string name;
        uint16 experience;
        uint64 lastBattleTime;
        uint64 lastBreedingTime;
        uint16 lastBattleResult;
        uint16 oldId; //for cards transfered from the first version of the game. 
    }
     //Main mapping storing an ABCard for each token ID
      mapping(uint256 => ABCard) public ABTokenCollection;
  
    constructor() public {
        // register the supported interfaces to conform to ERC721 via ERC165
        _registerInterface(_INTERFACE_ID_ERC721);
    }

    /**
     * @dev Gets the balance of the specified address
     * @param owner address to query the balance of
     * @return uint256 representing the amount owned by the passed address
     */
    function balanceOf(address owner) public view returns (uint256) {
        require(owner != address(0));
        return _ownedTokensCount[owner];
    }

 function totalSupply() external view returns (uint256) {
     return totalTokens;
 }
    /**
     * @dev Gets the owner of the specified token ID
     * @param tokenId uint256 ID of the token to query the owner of
     * @return owner address currently marked as the owner of the given token ID
     */
    function ownerOf(uint256 tokenId) public view returns (address) {
        address owner = _tokenOwner[tokenId];
        require(owner != address(0));
        return owner;
    }
    
    //Initial function to set the maximum numbers of each angel card

function setMaxAngels() external onlyCREATOR {
    uint i =0;
   
     //Angels 0 and 1 have no max
     //Lucifer and Michael have max numbers 250
     maxTokenNumbers[2] = 250;
     maxTokenNumbers[3] = 250;
     maxTokenNumbers[4] = 45;
     maxTokenNumbers[5] = 50;
     
    for (i=6; i<15; i++) {
        maxTokenNumbers[i]= 45;
    }
     for (i=15; i<24; i++) {
        maxTokenNumbers[i]= 65;
    }
   
    
}

//Initial function to set the maximum number of accessories
function setMaxAccessories() external onlyCREATOR {
     uint i = 0;
     for (i=43; i<60; i++) {
        maxTokenNumbers[i]= 200;
    }
}

//Initial function to set the max number of medals
  function setMaxMedals() onlyCREATOR external  {
      maxTokenNumbers[61] = 5000;
      maxTokenNumbers[62] = 5000;
      maxTokenNumbers[63] = 5000;
      maxTokenNumbers[64] = 5000;
      maxTokenNumbers[65] = 500;
      maxTokenNumbers[66] = 500;
      maxTokenNumbers[67] = 200;
      maxTokenNumbers[68] = 200;
      maxTokenNumbers[69] = 200;
      maxTokenNumbers[70] = 100;
      maxTokenNumbers[71] = 100;
      maxTokenNumbers[72] = 50;
  }
    //Function called once at the beginning to set the prices of all the angel cards. 
    function initAngelPrices() external onlyCREATOR {
       angelPrice[0] = 0;
       angelPrice[1] = 30000000000000000;
       angelPrice[2] = 666000000000000000;
       angelPrice[3] = 800000000000000000;
       angelPrice[4] = 10000000000000000;
       angelPrice[5] = 10000000000000000;
       angelPrice[6] = 20000000000000000;
       angelPrice[7] = 25000000000000000;
       angelPrice[8] = 16000000000000000;
       angelPrice[9] = 18000000000000000;
       angelPrice[10] = 14000000000000000;
       angelPrice[11] = 20000000000000000;
       angelPrice[12] = 24000000000000000;
       angelPrice[13] = 28000000000000000;
       angelPrice[14] = 40000000000000000;
       angelPrice[15] = 50000000000000000;
       angelPrice[16] = 53000000000000000;
       angelPrice[17] = 60000000000000000;
       angelPrice[18] = 65000000000000000;
       angelPrice[19] = 70000000000000000;
       angelPrice[20] = 75000000000000000;
       angelPrice[21] = 80000000000000000;
       angelPrice[22] = 85000000000000000;
       angelPrice[23] = 90000000000000000;
      
    }
    
        //Function called once at the beginning to set the prices of all the accessory cards. 
    function initAccessoryPrices() external onlyCREATOR {
       accessoryPrice[0] = 20000000000000000;
       accessoryPrice[1] = 60000000000000000;
       accessoryPrice[2] = 40000000000000000;
       accessoryPrice[3] = 90000000000000000;
       accessoryPrice[4] = 80000000000000000;
       accessoryPrice[5] = 160000000000000000;
       accessoryPrice[6] = 60000000000000000;
       accessoryPrice[7] = 120000000000000000;
       accessoryPrice[8] = 60000000000000000;
       accessoryPrice[9] = 120000000000000000;
       accessoryPrice[10] = 60000000000000000;
       accessoryPrice[11] = 120000000000000000;
       accessoryPrice[12] = 200000000000000000;
       accessoryPrice[13] = 200000000000000000;
       accessoryPrice[14] = 200000000000000000;
       accessoryPrice[15] = 200000000000000000;
       accessoryPrice[16] = 500000000000000000;
       accessoryPrice[17] = 600000000000000000;
    }
   
    
    // Developer function to change the price (in wei) for a card series. 
    function setCardSeriesPrice(uint8 _cardSeriesId, uint _newPrice) external onlyCREATOR {
        if (_cardSeriesId <24) {angelPrice[_cardSeriesId] = _newPrice;} else {
        if ((_cardSeriesId >42) && (_cardSeriesId < 61)) {accessoryPrice[(_cardSeriesId-43)] = _newPrice;}
        }
        
        
    }

   function withdrawEther() external onlyCREATOR {
    creatorAddress.transfer(address(this).balance);
}

    /**
     * @dev Approves another address to transfer the given token ID
     * The zero address indicates there is no approved address.
     * There can only be one approved address per token at a given time.
     * Can only be called by the token owner or an approved operator.
     * @param to address to be approved for the given token ID
     * @param tokenId uint256 ID of the token to be approved
     */
    function approve(address to, uint256 tokenId) public {
        address owner = ownerOf(tokenId);
        require(to != owner);
        require(msg.sender == owner || isApprovedForAll(owner, msg.sender));

        _tokenApprovals[tokenId] = to;
        emit Approval(owner, to, tokenId);
    }
    
        function getRandomNumber(uint16 maxRandom, uint8 min, address privateAddress) view public returns(uint8) {
        uint256 genNum = uint256(blockhash(block.number-1)) + uint256(privateAddress);
        return uint8(genNum % (maxRandom - min + 1)+min);
    }

   /**
   * @dev Returns an URI for a given token ID
   */
  function tokenURI(uint256 _tokenId) public pure returns (string memory) {
    return Strings.strConcat(
        baseTokenURI(),
        Strings.uint2str(_tokenId)
    );
  }
  
  function baseTokenURI() public pure returns (string memory) {
    return "https://www.angelbattles.com/URI/";
  }
  
   /// @notice A descriptive name for a collection of NFTs in this contract
    function name() external pure returns (string memory _name) {
        return "Angel Battle Token";
    }

    /// @notice An abbreviated name for NFTs in this contract
    function symbol() external pure returns (string memory _symbol) {
        return "ABT";
    }
  
  
    

    /**
     * @dev Gets the approved address for a token ID, or zero if no address set
     * Reverts if the token ID does not exist.
     * @param tokenId uint256 ID of the token to query the approval of
     * @return address currently approved for the given token ID
     */
    function getApproved(uint256 tokenId) public view returns (address) {
        require(_exists(tokenId));
        return _tokenApprovals[tokenId];
    }

    /**
     * @dev Sets or unsets the approval of a given operator
     * An operator is allowed to transfer all tokens of the sender on their behalf
     * @param to operator address to set the approval
     * @param approved representing the status of the approval to be set
     */
    function setApprovalForAll(address to, bool approved) public {
        require(to != msg.sender);
        _operatorApprovals[msg.sender][to] = approved;
        emit ApprovalForAll(msg.sender, to, approved);
    }

    /**
     * @dev Tells whether an operator is approved by a given owner
     * @param owner owner address which you want to query the approval of
     * @param operator operator address which you want to query the approval of
     * @return bool whether the given operator is approved by the given owner
     */
    function isApprovedForAll(address owner, address operator) public view returns (bool) {
        return _operatorApprovals[owner][operator];
    }
    
    /**
   * Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings.
   */
 


    /**
     * @dev Transfers the ownership of a given token ID to another address
     * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
     * Requires the msg sender to be the owner, approved, or operator
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
    */
    function transferFrom(address from, address to, uint256 tokenId) public {
        require(_isApprovedOrOwner(msg.sender, tokenId));

        _transferFrom(from, to, tokenId);
    }

    /**
     * @dev Safely transfers the ownership of a given token ID to another address
     * If the target address is a contract, it must implement `onERC721Received`,
     * which is called upon a safe transfer, and return the magic value
     * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
     * the transfer is reverted.
     *
     * Requires the msg sender to be the owner, approved, or operator
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
    */
    function safeTransferFrom(address from, address to, uint256 tokenId) public {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev Safely transfers the ownership of a given token ID to another address
     * If the target address is a contract, it must implement `onERC721Received`,
     * which is called upon a safe transfer, and return the magic value
     * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
     * the transfer is reverted.
     * Requires the msg sender to be the owner, approved, or operator
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes data to send along with a safe transfer check
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
        transferFrom(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data));
    }

    /**
     * @dev Returns whether the specified token exists
     * @param tokenId uint256 ID of the token to query the existence of
     * @return whether the token exists
     */
    function _exists(uint256 tokenId) internal view returns (bool) {
        address owner = _tokenOwner[tokenId];
        return owner != address(0);
    }

    /**
     * @dev Returns whether the given spender can transfer a given token ID
     * @param spender address of the spender to query
     * @param tokenId uint256 ID of the token to be transferred
     * @return bool whether the msg.sender is approved for the given token ID,
     *    is an operator of the owner, or is the owner of the token
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
        address owner = ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Internal function to mint a new token
     * Reverts if the given token ID already exists
     * @param to The address that will own the minted token
     * @param tokenId uint256 ID of the token to be minted
     */
    function _mint(address to, uint256 tokenId) internal {
        require(to != address(0));
        require(!_exists(tokenId));

        _tokenOwner[tokenId] = to;
        _ownedTokensCount[to] = _ownedTokensCount[to].add(1);
        addABTokenIdMapping(to, tokenId);
        emit Transfer(address(0), to, tokenId);
    }
    function mintABToken(address owner, uint8 _cardSeriesId, uint16 _power, uint16 _auraRed, uint16 _auraYellow, uint16 _auraBlue, string memory _name, uint16 _experience, uint16 _oldId) public onlySERAPHIM {
        require((currentTokenNumbers[_cardSeriesId] < maxTokenNumbers[_cardSeriesId] || maxTokenNumbers[_cardSeriesId] == 0));
        require(_cardSeriesId <100);
           ABCard storage abcard = ABTokenCollection[totalTokens];
           abcard.power = _power;
           abcard.cardSeriesId= _cardSeriesId;
           abcard.auraRed = _auraRed;
           abcard.auraYellow= _auraYellow;
           abcard.auraBlue= _auraBlue;
           abcard.name = _name;
           abcard.experience = _experience;
           abcard.tokenId = totalTokens;
           abcard.lastBattleTime = uint64(now);
           abcard.lastBreedingTime = uint64(now);
           abcard.lastBattleResult = 0;
           abcard.oldId = _oldId;
           _mint(owner, totalTokens);
           totalTokens = totalTokens +1;
           currentTokenNumbers[_cardSeriesId] ++;
    }
    
    function addABTokenIdMapping(address _owner, uint256 _tokenId) private {
            uint256[] storage owners = ownerABTokenCollection[_owner];
            owners.push(_tokenId);
    }
    

    
    function getPrice(uint8 _cardSeriesId) public view returns (uint) {
        if (_cardSeriesId <24) {return angelPrice[_cardSeriesId];}
        if ((_cardSeriesId >42) && (_cardSeriesId < 61)) {return accessoryPrice[(_cardSeriesId-43)];}
        return 0;
    }
    
    function buyAngel(uint8 _angelSeriesId) public payable {
        //don't create another card if we are already at the max
        if ((maxTokenNumbers[_angelSeriesId] <= currentTokenNumbers[_angelSeriesId]) && (_angelSeriesId >1 )) {revert();}
        //don't create another card if they haven't sent enough money. 
        if (msg.value < angelPrice[_angelSeriesId]) {revert();} 
        //don't create an angel card if they are trying to create a different type of card. 
         if ((_angelSeriesId<0) || (_angelSeriesId > 23)) {revert();}
        uint8 auraRed;
        uint8 auraYellow;
        uint8 auraBlue;
        uint16 power;
        (auraRed, auraYellow, auraBlue) = getAura(_angelSeriesId);
        (power) = getAngelPower(_angelSeriesId);
    
       mintABToken(msg.sender, _angelSeriesId, power, auraRed, auraYellow, auraBlue,"", 0, 0);
       
    }
    
    
    function buyAccessory(uint8 _accessorySeriesId) public payable {
        //don't create another card if we are already at the max
        if (maxTokenNumbers[_accessorySeriesId] <= currentTokenNumbers[_accessorySeriesId]) {revert();}
        //don't create another card if they haven't sent enough money. 
        if (msg.value < accessoryPrice[_accessorySeriesId-43]) {revert();} 
        //don't create a card if they are trying to create a different type of card. 
        if ((_accessorySeriesId<43) || (_accessorySeriesId > 60)) {revert();}
        mintABToken(msg.sender,_accessorySeriesId, 0, 0, 0, 0, "",0, 0);
       
     
       
    }
    
    //Returns the Aura color of each angel
    function getAura(uint8 _angelSeriesId) pure public returns (uint8 auraRed, uint8 auraYellow, uint8 auraBlue) {
        if (_angelSeriesId == 0) {return(0,0,1);}
        if (_angelSeriesId == 1) {return(0,1,0);}
        if (_angelSeriesId == 2) {return(1,0,1);}
        if (_angelSeriesId == 3) {return(1,1,0);}
        if (_angelSeriesId == 4) {return(1,0,0);}
        if (_angelSeriesId == 5) {return(0,1,0);}
        if (_angelSeriesId == 6) {return(1,0,1);}
        if (_angelSeriesId == 7) {return(0,1,1);}
        if (_angelSeriesId == 8) {return(1,1,0);}
        if (_angelSeriesId == 9) {return(0,0,1);}
        if (_angelSeriesId == 10)  {return(1,0,0);}
        if (_angelSeriesId == 11) {return(0,1,0);}
        if (_angelSeriesId == 12) {return(1,0,1);}
        if (_angelSeriesId == 13) {return(0,1,1);}
        if (_angelSeriesId == 14) {return(1,1,0);}
        if (_angelSeriesId == 15) {return(0,0,1);}
        if (_angelSeriesId == 16)  {return(1,0,0);}
        if (_angelSeriesId == 17) {return(0,1,0);}
        if (_angelSeriesId == 18) {return(1,0,1);}
        if (_angelSeriesId == 19) {return(0,1,1);}
        if (_angelSeriesId == 20) {return(1,1,0);}
        if (_angelSeriesId == 21) {return(0,0,1);}
        if (_angelSeriesId == 22)  {return(1,0,0);}
        if (_angelSeriesId == 23) {return(0,1,1);}
    }
   
    function getAngelPower(uint8 _angelSeriesId) private view returns (uint16) {
        uint8 randomPower = getRandomNumber(10,0,msg.sender);
        if (_angelSeriesId >=4) {
        return (100 + 10 * ((_angelSeriesId - 4) + randomPower));
        }
        if (_angelSeriesId == 0 ) {
        return (50 + randomPower);
        }
         if (_angelSeriesId == 1) {
        return (120 + randomPower);
        }
         if (_angelSeriesId == 2) {
        return (250 + randomPower);
        }
        if (_angelSeriesId == 3) {
        return (300 + randomPower);
        }
        
    }
    
    function getCurrentTokenNumbers(uint8 _cardSeriesId) view public returns (uint32) {
        return currentTokenNumbers[_cardSeriesId];
}
       function getMaxTokenNumbers(uint8 _cardSeriesId) view public returns (uint32) {
        return maxTokenNumbers[_cardSeriesId];
}


    function getABToken(uint256 tokenId) view public returns(uint8 cardSeriesId, uint16 power, uint16 auraRed, uint16 auraYellow, uint16 auraBlue, string memory name, uint16 experience, uint64 lastBattleTime, uint16 lastBattleResult, address owner, uint16 oldId) {
        ABCard memory abcard = ABTokenCollection[tokenId];
        cardSeriesId = abcard.cardSeriesId;
        power = abcard.power;
        experience = abcard.experience;
        auraRed = abcard.auraRed;
        auraBlue = abcard.auraBlue;
        auraYellow = abcard.auraYellow;
        name = abcard.name;
        lastBattleTime = abcard.lastBattleTime;
        lastBattleResult = abcard.lastBattleResult;
        oldId = abcard.oldId;
        owner = ownerOf(tokenId);
    }
    
    
     function setAuras(uint256 tokenId, uint16 _red, uint16 _blue, uint16 _yellow) external onlySERAPHIM {
        ABCard storage abcard = ABTokenCollection[tokenId];
        if (abcard.tokenId == tokenId) {
            abcard.auraRed = _red;
            abcard.auraYellow = _yellow;
            abcard.auraBlue = _blue;
    }
    }
    
     function setName(uint256 tokenId,string memory namechange) public {
        ABCard storage abcard = ABTokenCollection[tokenId];
        if (msg.sender != ownerOf(tokenId)) {revert();}
        if (abcard.tokenId == tokenId) {
            abcard.name = namechange;
    }
    }
    
    function setExperience(uint256 tokenId, uint16 _experience) external onlySERAPHIM {
        ABCard storage abcard = ABTokenCollection[tokenId];
        if (abcard.tokenId == tokenId) {
            abcard.experience = _experience;
    }
    }
    
    function setLastBattleResult(uint256 tokenId, uint16 _result) external onlySERAPHIM {
        ABCard storage abcard = ABTokenCollection[tokenId];
        if (abcard.tokenId == tokenId) {
            abcard.lastBattleResult = _result;
    }
    }
    
     function setLastBattleTime(uint256 tokenId) external onlySERAPHIM {
        ABCard storage abcard = ABTokenCollection[tokenId];
        if (abcard.tokenId == tokenId) {
            abcard.lastBattleTime = uint64(now);
    }
    }
    
       function setLastBreedingTime(uint256 tokenId) external onlySERAPHIM {
        ABCard storage abcard = ABTokenCollection[tokenId];
        if (abcard.tokenId == tokenId) {
            abcard.lastBreedingTime = uint64(now);
    }
    }
    
      function setoldId(uint256 tokenId, uint16 _oldId) external onlySERAPHIM {
        ABCard storage abcard = ABTokenCollection[tokenId];
        if (abcard.tokenId == tokenId) {
            abcard.oldId = _oldId;
    }
    }
    
    
    function getABTokenByIndex(address _owner, uint64 _index) view external returns(uint256) {
        if (_index >= ownerABTokenCollection[_owner].length) {
            return 0; }
        return ownerABTokenCollection[_owner][_index];
    }

   /**
     * @dev external function to burn a specific token
     * Reverts if the token does not exist
     * @param tokenId uint256 ID of the token being burned
     * Only the owner can burn their token. 
     */
    function burn(uint256 tokenId) external {
        require(ownerOf(tokenId) == msg.sender);
        _clearApproval(tokenId);
        _ownedTokensCount[msg.sender] = _ownedTokensCount[msg.sender].sub(1);
        _tokenOwner[tokenId] = address(0);
        emit Transfer(msg.sender, address(0), tokenId);
    }
    
     /**
     * @dev external function to burn a specific token
     * Reverts if the token does not exist
     * @param tokenId uint256 ID of the token being burned
     * Only the owner can burn their token. 
     * This function allows a new token type to be reissued. This preserves rarity, while the burn functio increases rarity
     */
    function burnAndRecycle(uint256 tokenId) external {
        require(ownerOf(tokenId) == msg.sender);
        uint8 cardSeriesId;
        _clearApproval(tokenId);
        _ownedTokensCount[msg.sender] = _ownedTokensCount[msg.sender].sub(1);
        _tokenOwner[tokenId] = address(0);
        (cardSeriesId,,,,,,,,,,) = getABToken (tokenId);
        if (currentTokenNumbers[cardSeriesId] >= 1) {
            currentTokenNumbers[cardSeriesId] = currentTokenNumbers[cardSeriesId] - 1;
        }
        emit Transfer(msg.sender, address(0), tokenId);
    }


    /**
     * @dev Internal function to burn a specific token
     * Reverts if the token does not exist
     * Deprecated, use _burn(uint256) instead.
     * @param owner owner of the token to burn
     * @param tokenId uint256 ID of the token being burned
     */
    function _burn(address owner, uint256 tokenId) internal {
        require(ownerOf(tokenId) == owner);

        _clearApproval(tokenId);

        _ownedTokensCount[owner] = _ownedTokensCount[owner].sub(1);
        _tokenOwner[tokenId] = address(0);

        emit Transfer(owner, address(0), tokenId);
    }

    /**
     * @dev Internal function to burn a specific token
     * Reverts if the token does not exist
     * @param tokenId uint256 ID of the token being burned
     */
    function _burn(uint256 tokenId) internal {
        _burn(ownerOf(tokenId), tokenId);
    }

    /**
     * @dev Internal function to transfer ownership of a given token ID to another address.
     * As opposed to transferFrom, this imposes no restrictions on msg.sender.
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
    */
    function _transferFrom(address from, address to, uint256 tokenId) internal {
        require(ownerOf(tokenId) == from);
        require(to != address(0));

        _clearApproval(tokenId);

        _ownedTokensCount[from] = _ownedTokensCount[from].sub(1);
        _ownedTokensCount[to] = _ownedTokensCount[to].add(1);

        _tokenOwner[tokenId] = to;
        addABTokenIdMapping(to, tokenId);
        emit Transfer(from, to, tokenId);
    }

    /**
     * @dev Internal function to invoke `onERC721Received` on a target address
     * The call is not executed if the target address is not a contract
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
        internal returns (bool)
    {
        if (!to.isContract()) {
            return true;
        }

        bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
        return (retval == _ERC721_RECEIVED);
    }

    /**
     * @dev Private function to clear current approval of a given token ID
     * @param tokenId uint256 ID of the token to be transferred
     */
    function _clearApproval(uint256 tokenId) private {
        if (_tokenApprovals[tokenId] != address(0)) {
            _tokenApprovals[tokenId] = address(0);
        }
    }
}
/**
 * @title ERC721 Non-Fungible Token Standard basic implementation
 * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
 */
function initAccessoryPrices() external onlyCREATOR {
   accessoryPrice[0] = 20000000000000000;
   accessoryPrice[1] = 60000000000000000;
   accessoryPrice[2] = 40000000000000000;
   accessoryPrice[3] = 90000000000000000;
   accessoryPrice[4] = 80000000000000000;
   accessoryPrice[5] = 160000000000000000;
   accessoryPrice[6] = 60000000000000000;
   accessoryPrice[7] = 120000000000000000;
   accessoryPrice[8] = 60000000000000000;
   accessoryPrice[9] = 120000000000000000;
   accessoryPrice[10] = 60000000000000000;
   accessoryPrice[11] = 120000000000000000;
   accessoryPrice[12] = 200000000000000000;
   accessoryPrice[13] = 200000000000000000;
   accessoryPrice[14] = 200000000000000000;
   accessoryPrice[15] = 200000000000000000;
   accessoryPrice[16] = 500000000000000000;
   accessoryPrice[17] = 600000000000000000;
}

//Function called once at the beginning to set the prices of all the accessory cards. 