contract PVP is PausableBattle, PVPInterface {
	/* PVP BATLE */
	
    /** list of packed warrior data that will participate in next PVP session. 
     *  Fixed size arry, to evade constant remove and push operations,
     *  this approach reduces transaction costs involving queue modification. */
    uint256[100] public pvpQueue;
    //
    //queue size
    uint256 public pvpQueueSize = 0;
    
    // @dev A mapping from owner address to booty in WEI
    //  booty is acquired in PVP and Tournament battles and can be
    // withdrawn with grabBooty method by the owner of the loot
    mapping (address => uint256) public ownerToBooty;
    
    // @dev A mapping from warrior id to owners address
    mapping (uint256 => address) internal warriorToOwner;
    
    // An approximation of currently how many seconds are in between blocks.
    uint256 internal secondsPerBlock = 15;
    
    // Cut owner takes from, measured in basis points (1/100 of a percent).
    // Values 0-10,000 map to 0%-100%
    uint256 public pvpOwnerCut;
    
    // Values 0-10,000 map to 0%-100%
    //this % of the total bets will be sent as 
    //a reward to address, that triggered finishPVP method
    uint256 public pvpMaxIncentiveCut;
    
    /// @notice The payment base required to use startPVP().
    // pvpBattleFee * (warrior.level / POINTS_TO_LEVEL)
    uint256 internal pvpBattleFee = 10 finney;
    
    uint256 public constant PVP_INTERVAL = 15 minutes;
    
    uint256 public nextPVPBatleBlock = 0;
    //number of WEI in hands of warrior owners
    uint256 public totalBooty = 0;
    
    /* TOURNAMENT */
    uint256 public constant FUND_GATHERING_TIME = 24 hours;
    uint256 public constant ADMISSION_TIME = 12 hours;
    uint256 public constant RATING_EXPAND_INTERVAL = 1 hours;
    uint256 internal constant SAFETY_GAP = 5;
    
    uint256 internal constant MAX_INCENTIVE_REWARD = 200 finney;
    
    //tournamentContenders size
    uint256 public tournamentQueueSize = 0;
    
    // Values 0-10,000 map to 0%-100%
    uint256 public tournamentBankCut;
    
   /** tournamentEndBlock, tournament is eligible to be finished only
    *  after block.number >= tournamentEndBlock 
    *  it depends on FUND_GATHERING_TIME and ADMISSION_TIME */
    uint256 public tournamentEndBlock;
    
    //number of WEI in tournament bank
    uint256 public currentTournamentBank = 0;
    uint256 public nextTournamentBank = 0;
    
    PVPListenerInterface internal pvpListener;
    
    /* EVENTS */
    /** @dev TournamentScheduled event. Emitted every time a tournament is scheduled 
     *  @param tournamentEndBlock when block.number > tournamentEndBlock, then tournament 
     *         is eligible to be finished or rescheduled */
    event TournamentScheduled(uint256 tournamentEndBlock);
    
    /** @dev PVPScheduled event. Emitted every time a tournament is scheduled 
     *  @param nextPVPBatleBlock when block.number > nextPVPBatleBlock, then pvp battle 
     *         is eligible to be finished or rescheduled */
    event PVPScheduled(uint256 nextPVPBatleBlock);
    
    /** @dev PVPNewContender event. Emitted every time a warrior enqueues pvp battle
     *  @param owner Warrior owner
     *  @param warriorId Warrior ID that entered PVP queue
     *  @param entranceFee fee in WEI warrior owner payed to enter PVP
     */
    event PVPNewContender(address owner, uint256 warriorId, uint256 entranceFee);

    /** @dev PVPFinished event. Emitted every time a pvp battle is finished
     *  @param warriorsData array of pairs of pvp warriors packed to uint256, even => winners, odd => losers 
     *  @param owners array of warrior owners, 1 to 1 with warriorsData, even => winners, odd => losers 
     *  @param matchingCount total number of warriors that fought in current pvp session and got rewards,
     *  if matchingCount < participants.length then all IDs that are >= matchingCount will 
     *  remain in waiting room, until they are matched.
     */
    event PVPFinished(uint256[] warriorsData, address[] owners, uint256 matchingCount);
    
    /** @dev BootySendFailed event. Emitted every time address.send() function failed to transfer Ether to recipient
     *  in this case recipient Ether is recorded to ownerToBooty mapping, so recipient can withdraw their booty manually
     *  @param recipient address for whom send failed
     *  @param amount number of WEI we failed to send
     */
    event BootySendFailed(address recipient, uint256 amount);
    
    /** @dev BootyGrabbed event
     *  @param receiver address who grabbed his booty
     *  @param amount number of WEI
     */
    event BootyGrabbed(address receiver, uint256 amount);
    
    /** @dev PVPContenderRemoved event. Emitted every time warrior is removed from pvp queue by its owner.
     *  @param warriorId id of the removed warrior
     */
    event PVPContenderRemoved(uint256 warriorId, address owner);
    
    function PVP(uint256 _pvpCut, uint256 _tournamentBankCut, uint256 _pvpMaxIncentiveCut) public {
        require((_tournamentBankCut + _pvpCut + _pvpMaxIncentiveCut) <= 10000);
		pvpOwnerCut = _pvpCut;
		tournamentBankCut = _tournamentBankCut;
		pvpMaxIncentiveCut = _pvpMaxIncentiveCut;
    }
    
    /** @dev grabBooty sends to message sender his booty in WEI
     */
    function grabBooty() external {
        uint256 booty = ownerToBooty[msg.sender];
        require(booty > 0);
        require(totalBooty >= booty);
        
        ownerToBooty[msg.sender] = 0;
        totalBooty -= booty;
        
        msg.sender.transfer(booty);
        //emit event
        BootyGrabbed(msg.sender, booty);
    }
    
    function safeSend(address _recipient, uint256 _amaunt) internal {
		uint256 failedBooty = sendBooty(_recipient, _amaunt);
        if (failedBooty > 0) {
			totalBooty += failedBooty;
        }
    }
    
    function sendBooty(address _recipient, uint256 _amaunt) internal returns(uint256) {
        bool success = _recipient.send(_amaunt);
        if (!success && _amaunt > 0) {
            ownerToBooty[_recipient] += _amaunt;
            BootySendFailed(_recipient, _amaunt);
            return _amaunt;
        }
        return 0;
    }
    
    //@returns block number, after this block tournament is opened for admission
    function getTournamentAdmissionBlock() public view returns(uint256) {
        uint256 admissionInterval = (ADMISSION_TIME / secondsPerBlock);
        return tournamentEndBlock < admissionInterval ? 0 : tournamentEndBlock - admissionInterval;
    }
    
    
    //schedules next turnament time(block)
    function _scheduleTournament() internal {
        //we can chedule only if there is nobody in tournament queue and
        //time of tournament battle have passed
		if (tournamentQueueSize == 0 && tournamentEndBlock <= block.number) {
		    tournamentEndBlock = ((FUND_GATHERING_TIME / 2 + ADMISSION_TIME) / secondsPerBlock) + block.number;
		    TournamentScheduled(tournamentEndBlock);
		}
    }
    
    /// @dev Updates the minimum payment required for calling startPVP(). Can only
    ///  be called by the COO address, and only if pvp queue is empty.
    function setPVPEntranceFee(uint256 value) external onlyOwner {
        require(pvpQueueSize == 0);
        pvpBattleFee = value;
    }
    
    //@returns PVP entrance fee for specified warrior level 
    //@param _levelPoints NB!
    function getPVPEntranceFee(uint256 _levelPoints) external view returns(uint256) {
        return pvpBattleFee * CryptoUtils._getLevel(_levelPoints);
    }
    
    //level can only be > 0 and <= 25
    function _getPVPFeeByLevel(uint256 _level) internal view returns(uint256) {
        return pvpBattleFee * _level;
    }
    
	// @dev Computes warrior pvp reward
    // @param _totalBet - total bet from both competitors.
    function _computePVPReward(uint256 _totalBet, uint256 _contendersCut) internal pure returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because
        // _totalBet max value is 1000 finney, and _contendersCut aka
        // (10000 - pvpOwnerCut - tournamentBankCut - incentiveRewardCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). The result of this
        // function is always guaranteed to be <= _totalBet.
        return _totalBet * _contendersCut / 10000;
    }
    
    function _getPVPContendersCut(uint256 _incentiveCut) internal view returns (uint256) {
        // NOTE: We don't use SafeMath (or similar) in this function because
        // (pvpOwnerCut + tournamentBankCut + pvpMaxIncentiveCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). 
        // _incentiveCut is guaranteed to be >= 1 and <=  pvpMaxIncentiveCut
        return (10000 - pvpOwnerCut - tournamentBankCut - _incentiveCut);
    }
	
	// @dev Computes warrior pvp reward
    // @param _totalSessionLoot - total bets from all competitors.
    function _computeIncentiveReward(uint256 _totalSessionLoot, uint256 _incentiveCut) internal pure returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because
        // _totalSessionLoot max value is 37500 finney, and 
        // (pvpOwnerCut + tournamentBankCut + incentiveRewardCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). The result of this
        // function is always guaranteed to be <= _totalSessionLoot.
        return _totalSessionLoot * _incentiveCut / 10000;
    }
    
	///@dev computes incentive cut for specified loot, 
	/// Values 0-10,000 map to 0%-100%
	/// max incentive reward cut is 5%, if it exceeds MAX_INCENTIVE_REWARD,
	/// then cut is lowered to be equal to MAX_INCENTIVE_REWARD.
	/// minimum cut is 0.01%
    /// this % of the total bets will be sent as 
    /// a reward to address, that triggered finishPVP method
    function _computeIncentiveCut(uint256 _totalSessionLoot, uint256 maxIncentiveCut) internal pure returns(uint256) {
        uint256 result = _totalSessionLoot * maxIncentiveCut / 10000;
        result = result <= MAX_INCENTIVE_REWARD ? maxIncentiveCut : MAX_INCENTIVE_REWARD * 10000 / _totalSessionLoot;
        //min cut is 0.01%
        return result > 0 ? result : 1;
    }
    
    // @dev Computes warrior pvp reward
    // @param _totalSessionLoot - total bets from all competitors.
    function _computePVPBeneficiaryFee(uint256 _totalSessionLoot) internal view returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because
        // _totalSessionLoot max value is 37500 finney, and 
        // (pvpOwnerCut + tournamentBankCut + incentiveRewardCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). The result of this
        // function is always guaranteed to be <= _totalSessionLoot.
        return _totalSessionLoot * pvpOwnerCut / 10000;
    }
    
    // @dev Computes tournament bank cut
    // @param _totalSessionLoot - total session loot.
    function _computeTournamentCut(uint256 _totalSessionLoot) internal view returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because
        // _totalSessionLoot max value is 37500 finney, and 
        // (pvpOwnerCut + tournamentBankCut + incentiveRewardCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). The result of this
        // function is always guaranteed to be <= _totalSessionLoot.
        return _totalSessionLoot * tournamentBankCut / 10000;
    }

    function indexOf(uint256 _warriorId) internal view returns(int256) {
	    uint256 length = uint256(pvpQueueSize);
	    for(uint256 i = 0; i < length; i ++) {
	        if(CryptoUtils._unpackIdValue(pvpQueue[i]) == _warriorId) return int256(i);
	    }
	    return -1;
	}
    
    function getPVPIncentiveReward(uint256[] memory matchingIds, uint256 matchingCount) internal view returns(uint256) {
        uint256 sessionLoot = _computeTotalBooty(matchingIds, matchingCount);
        
        return _computeIncentiveReward(sessionLoot, _computeIncentiveCut(sessionLoot, pvpMaxIncentiveCut));
    }
    
    function maxPVPContenders() external view returns(uint256){
        return pvpQueue.length;
    }
    
    function getPVPState() external view returns
    (uint256 contendersCount, uint256 matchingCount, uint256 endBlock, uint256 incentiveReward)
    {
        uint256[] memory pvpData = _packPVPData();
        
    	contendersCount = pvpQueueSize;
    	matchingCount = CryptoUtils._getMatchingIds(pvpData, PVP_INTERVAL, _computeCycleSkip(), RATING_EXPAND_INTERVAL);
    	endBlock = nextPVPBatleBlock;   
    	incentiveReward = getPVPIncentiveReward(pvpData, matchingCount);
    }
    
    function canFinishPVP() external view returns(bool) {
        return nextPVPBatleBlock <= block.number &&
         CryptoUtils._getMatchingIds(_packPVPData(), PVP_INTERVAL, _computeCycleSkip(), RATING_EXPAND_INTERVAL) > 1;
    }
    
    function _clarifyPVPSchedule() internal {
        uint256 length = pvpQueueSize;
		uint256 currentBlock = block.number;
		uint256 nextBattleBlock = nextPVPBatleBlock;
		//if battle not scheduled, schedule battle
		if (nextBattleBlock <= currentBlock) {
		    //if queue not empty update cycles
		    if (length > 0) {
				uint256 packedWarrior;
				uint256 cycleSkip = _computeCycleSkip();
		        for(uint256 i = 0; i < length; i++) {
		            packedWarrior = pvpQueue[i];
		            //increase warrior iteration cycle
		            pvpQueue[i] = CryptoUtils._changeCycleValue(packedWarrior, CryptoUtils._unpackCycleValue(packedWarrior) + cycleSkip);
		        }
		    }
		    nextBattleBlock = (PVP_INTERVAL / secondsPerBlock) + currentBlock;
		    nextPVPBatleBlock = nextBattleBlock;
		    PVPScheduled(nextBattleBlock);
		//if pvp queue will be full and there is still too much time left, then let the battle begin! 
		} else if (length + 1 == pvpQueue.length && (currentBlock + SAFETY_GAP * 2) < nextBattleBlock) {
		    nextBattleBlock = currentBlock + SAFETY_GAP;
		    nextPVPBatleBlock = nextBattleBlock;
		    PVPScheduled(nextBattleBlock);
		}
    }
    
    /// @dev Internal utility function to initiate pvp battle, assumes that all battle
    ///  requirements have been checked.
    function _triggerNewPVPContender(address _owner, uint256 _packedWarrior, uint256 fee) internal {

		_clarifyPVPSchedule();
        //number of pvp cycles the warrior is waiting for suitable enemy match
        //increment every time when finishPVP is called and no suitable enemy match was found
        _packedWarrior = CryptoUtils._changeCycleValue(_packedWarrior, 0);
		
		//record contender data
		pvpQueue[pvpQueueSize++] = _packedWarrior;
		warriorToOwner[CryptoUtils._unpackIdValue(_packedWarrior)] = _owner;
		
		//Emit event
		PVPNewContender(_owner, CryptoUtils._unpackIdValue(_packedWarrior), fee);
    }
    
    function _noMatchingPairs() internal view returns(bool) {
        uint256 matchingCount = CryptoUtils._getMatchingIds(_packPVPData(), uint64(PVP_INTERVAL), _computeCycleSkip(), uint64(RATING_EXPAND_INTERVAL));
        return matchingCount == 0;
    }
    
    /*
     * @title startPVP enqueues specified warrior to PVP
     * 
     * @dev When the owner enqueues his warrior for PvP, the warrior enters the waiting room.
     * Once every 15 minutes, we check the warriors in the room and select pairs. 
     * For those warriors to whom we found couples, fighting is conducted and the results 
     * are recorded in the profile of the warrior. 
     */
    function addPVPContender(address _owner, uint256 _packedWarrior) external payable PVPNotPaused {
		// Caller must be pvpListener contract
        require(msg.sender == address(pvpListener));

        require(_owner != address(0));
        //contender can be added only while PVP is scheduled in future
        //or no matching warrior pairs found
        require(nextPVPBatleBlock > block.number || _noMatchingPairs());
        // Check that the warrior exists.
        require(_packedWarrior != 0);
        //owner must withdraw all loot before contending pvp
        require(ownerToBooty[_owner] == 0);
        //check that there is enough room for new participants
        require(pvpQueueSize < pvpQueue.length);
        // Checks for payment.
        uint256 fee = _getPVPFeeByLevel(CryptoUtils._unpackLevelValue(_packedWarrior));
        require(msg.value >= fee);
        //
        // All checks passed, put the warrior to the queue!
        _triggerNewPVPContender(_owner, _packedWarrior, fee);
    }
    
    function _packPVPData() internal view returns(uint256[] memory matchingIds) {
        uint256 length = pvpQueueSize;
        matchingIds = new uint256[](length);
        for(uint256 i = 0; i < length; i++) {
            matchingIds[i] = pvpQueue[i];
        }
        return matchingIds;
    }
    
    function _computeTotalBooty(uint256[] memory _packedWarriors, uint256 matchingCount) internal view returns(uint256) {
        //compute session booty
        uint256 sessionLoot = 0;
        for(uint256 i = 0; i < matchingCount; i++) {
            sessionLoot += _getPVPFeeByLevel(CryptoUtils._unpackLevelValue(_packedWarriors[i]));
        }
        return sessionLoot;
    }
    
    function _grandPVPRewards(uint256[] memory _packedWarriors, uint256 matchingCount) 
    internal returns(uint256)
    {
        uint256 booty = 0;
        uint256 packedWarrior;
        uint256 failedBooty = 0;
        
        uint256 sessionBooty = _computeTotalBooty(_packedWarriors, matchingCount);
        uint256 incentiveCut = _computeIncentiveCut(sessionBooty, pvpMaxIncentiveCut);
        uint256 contendersCut = _getPVPContendersCut(incentiveCut);
        
        for(uint256 id = 0; id < matchingCount; id++) {
            //give reward to warriors that fought hard
			//winner, even ids are winners!
			packedWarrior = _packedWarriors[id];
			//
			//give winner deserved booty 80% from both bets
			//must be computed before level reward!
			booty = _getPVPFeeByLevel(CryptoUtils._unpackLevelValue(packedWarrior)) + 
				_getPVPFeeByLevel(CryptoUtils._unpackLevelValue(_packedWarriors[id + 1]));
			
			//
			//send reward to warrior owner
			failedBooty += sendBooty(warriorToOwner[CryptoUtils._unpackIdValue(packedWarrior)], _computePVPReward(booty, contendersCut));
			//loser, they are odd...
			//skip them, as they deserve none!
			id ++;
        }
        failedBooty += sendBooty(pvpListener.getBeneficiary(), _computePVPBeneficiaryFee(sessionBooty));
        
        if (failedBooty > 0) {
            totalBooty += failedBooty;
        }
        //if tournament admission start time not passed
        //add tournament cut to current tournament bank,
        //otherwise to next tournament bank
        if (getTournamentAdmissionBlock() > block.number) {
            currentTournamentBank += _computeTournamentCut(sessionBooty);
        } else {
            nextTournamentBank += _computeTournamentCut(sessionBooty);
        }
        
        //compute incentive reward
        return _computeIncentiveReward(sessionBooty, incentiveCut);
    }
    
    function _increaseCycleAndTrimQueue(uint256[] memory matchingIds, uint256 matchingCount) internal {
        uint32 length = uint32(matchingIds.length - matchingCount);  
		uint256 packedWarrior;
		uint256 skipCycles = _computeCycleSkip();
        for(uint256 i = 0; i < length; i++) {
            packedWarrior = matchingIds[matchingCount + i];
            //increase warrior iteration cycle
            pvpQueue[i] = CryptoUtils._changeCycleValue(packedWarrior, CryptoUtils._unpackCycleValue(packedWarrior) + skipCycles);
        }
        //trim queue	
        pvpQueueSize = length;
    }
    
    function _computeCycleSkip() internal view returns(uint256) {
        uint256 number = block.number;
        return nextPVPBatleBlock > number ? 0 : (number - nextPVPBatleBlock) * secondsPerBlock / PVP_INTERVAL + 1;
    }
    
    function _getWarriorOwners(uint256[] memory pvpData) internal view returns (address[] memory owners){
        uint256 length = pvpData.length;
        owners = new address[](length);
        for(uint256 i = 0; i < length; i ++) {
            owners[i] = warriorToOwner[CryptoUtils._unpackIdValue(pvpData[i])];
        }
    }
    
    // @dev Internal utility function to initiate pvp battle, assumes that all battle
    ///  requirements have been checked.
    function _triggerPVPFinish(uint256[] memory pvpData, uint256 matchingCount) internal returns(uint256){
        //
		//compute battle results        
        CryptoUtils._getPVPBattleResults(pvpData, matchingCount, nextPVPBatleBlock);
        //
        //mark not fought warriors and trim queue 
        _increaseCycleAndTrimQueue(pvpData, matchingCount);
        //
        //schedule next battle time
        nextPVPBatleBlock = (PVP_INTERVAL / secondsPerBlock) + block.number;
        
        //
        //schedule tournament
        //if contendersCount is 0 and tournament not scheduled, schedule tournament
        //NB MUST be before _grandPVPRewards()
        _scheduleTournament();
        // compute and grand rewards to warriors,
        // put tournament cut to bank, not susceptible to reentry attack because of require(nextPVPBatleBlock <= block.number);
        // and require(number of pairs > 1);
        uint256 incentiveReward = _grandPVPRewards(pvpData, matchingCount);
        //
        //notify pvp listener contract
        pvpListener.pvpFinished(pvpData, matchingCount);
        
        //
        //fire event
		PVPFinished(pvpData, _getWarriorOwners(pvpData), matchingCount);
        PVPScheduled(nextPVPBatleBlock);
		
		return incentiveReward;
    }
    
    
    /**
     * @dev finishPVP this method finds matches of warrior pairs
     * in waiting room and computes result of their fights.
     * 
     * The winner gets +1 level, the loser gets +0.5 level
     * The winning player gets +130 rating
	 * The losing player gets -30 or 70 rating (if warrior levelUps after battle) .
     * can be called once in 15min.
     * NB If the warrior is not picked up in an hour, then we expand the range 
     * of selection by 25 rating each hour.
     */
    function finishPVP() public PVPNotPaused {
        // battle interval is over
        require(nextPVPBatleBlock <= block.number);
        //
	    //match warriors
        uint256[] memory pvpData = _packPVPData();
        //match ids and sort them according to matching
        uint256 matchingCount = CryptoUtils._getMatchingIds(pvpData, uint64(PVP_INTERVAL), _computeCycleSkip(), uint64(RATING_EXPAND_INTERVAL));
		// we have at least 1 matching battle pair
        require(matchingCount > 1);
        
        // When the all checks done, calculate actual battle result
        uint256 incentiveReward = _triggerPVPFinish(pvpData, matchingCount);
        
        //give reward for incentive
        safeSend(msg.sender, incentiveReward);
    }

    // @dev Removes specified warrior from PVP queue
    //  sets warrior free (IDLE) and returns pvp entrance fee to owner
    // @notice This is a state-modifying function that can
    //  be called while the contract is paused.
    // @param _warriorId - ID of warrior in PVP queue
    function removePVPContender(uint256 _warriorId) external{
        uint256 queueSize = pvpQueueSize;
        require(queueSize > 0);
        // Caller must be owner of the specified warrior
        require(warriorToOwner[_warriorId] == msg.sender);
        //warrior must be in pvp queue
        int256 warriorIndex = indexOf(_warriorId);
        require(warriorIndex >= 0);
        //grab warrior data
        uint256 warriorData = pvpQueue[uint32(warriorIndex)];
        //warrior cycle must be >= 4 (> than 1 hour)
        require((CryptoUtils._unpackCycleValue(warriorData) + _computeCycleSkip()) >= 4);
        
        //remove from queue
        if (uint256(warriorIndex) < queueSize - 1) {
	        pvpQueue[uint32(warriorIndex)] = pvpQueue[pvpQueueSize - 1];
        }
        pvpQueueSize --;
        //notify battle listener
        pvpListener.pvpContenderRemoved(_warriorId);
        //return pvp bet
        msg.sender.transfer(_getPVPFeeByLevel(CryptoUtils._unpackLevelValue(warriorData)));
        //Emit event
        PVPContenderRemoved(_warriorId, msg.sender);
    }
    
    function getPVPCycles(uint32[] warriorIds) external view returns(uint32[]){
        uint256 length = warriorIds.length;
        uint32[] memory cycles = new uint32[](length);
        int256 index;
        uint256 skipCycles = _computeCycleSkip();
	    for(uint256 i = 0; i < length; i ++) {
	        index = indexOf(warriorIds[i]);
	        cycles[i] = index >= 0 ? uint32(CryptoUtils._unpackCycleValue(pvpQueue[uint32(index)]) + skipCycles) : 0;
	    }
	    return cycles;
    }
    
    // @dev Remove all PVP contenders from PVP queue 
    //  and return all bets to warrior owners.
    //  NB: this is emergency method, used only in f%#^@up situation
    function removeAllPVPContenders() external onlyOwner PVPPaused {
        //remove all pvp contenders
        uint256 length = pvpQueueSize;
        
        uint256 warriorData;
        uint256 warriorId;
        uint256 failedBooty;
        address owner;
        
        pvpQueueSize = 0;
        
        for(uint256 i = 0; i < length; i++) {
	        //grab warrior data
	        warriorData = pvpQueue[i];
	        warriorId = CryptoUtils._unpackIdValue(warriorData);
	        //notify battle listener
	        pvpListener.pvpContenderRemoved(uint32(warriorId));
	        
	        owner = warriorToOwner[warriorId];
	        //return pvp bet
	        failedBooty += sendBooty(owner, _getPVPFeeByLevel(CryptoUtils._unpackLevelValue(warriorData)));
        }
        totalBooty += failedBooty;
    }
}
/*  @title Handles creating pvp battles every 15 min.*/
function _computePVPReward(uint256 _totalBet, uint256 _contendersCut) internal pure returns (uint256){
    // NOTE: We don't use SafeMath (or similar) in this function because
    // _totalBet max value is 1000 finney, and _contendersCut aka
    // (10000 - pvpOwnerCut - tournamentBankCut - incentiveRewardCut) <= 10000 (see the require()
    // statement in the BattleProvider constructor). The result of this
    // function is always guaranteed to be <= _totalBet.
    return _totalBet * _contendersCut / 10000;
}

// @dev Computes warrior pvp reward
// @param _totalBet - total bet from both competitors.