contract DungeonBase is EjectableOwnable, Pausable, PullPayment, DungeonStructs {

    /* ======== TOKEN CONTRACTS ======== */

    /**
     * @dev The address of the ERC721 token contract managing all Dungeon tokens.
     */
    DungeonToken public dungeonTokenContract;

    /**
     * @dev The address of the ERC721 token contract managing all Hero tokens.
     */
    HeroToken public heroTokenContract;


    /* ======== CLOSED SOURCE CONTRACTS ======== */

    /**
     * @dev The address of the ChallengeScience contract that handles the floor generation mechanics after challenge success.
     */
    ChallengeScienceInterface challengeScienceContract;

    /**
     * @dev The address of the TrainingScience contract that handles the hero training mechanics.
     */
    TrainingScienceInterface trainingScienceContract;


    /* ======== CONSTANTS ======== */

    uint16[32] EQUIPMENT_POWERS = [
        1, 2, 4, 5, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0,
        4, 16, 32, 33, 0, 0, 0, 0, 32, 64, 0, 0, 128, 0, 0, 0
    ];

    uint SUPER_HERO_MULTIPLIER = 32;

    /* ======== SETTER FUNCTIONS ======== */

    /**
     * @dev Set the address of the dungeon token contract.
     * @param _newDungeonTokenContract An address of a DungeonToken contract.
     */
    function setDungeonTokenContract(address _newDungeonTokenContract) onlyOwner external {
        dungeonTokenContract = DungeonToken(_newDungeonTokenContract);
    }

    /**
     * @dev Set the address of the hero token contract.
     * @param _newHeroTokenContract An address of a HeroToken contract.
     */
    function setHeroTokenContract(address _newHeroTokenContract) onlyOwner external {
        heroTokenContract = HeroToken(_newHeroTokenContract);
    }

    /**
     * @dev Set the address of the secret dungeon challenge formula contract.
     * @param _newChallengeScienceAddress An address of a ChallengeScience contract.
     */
    function setChallengeScienceContract(address _newChallengeScienceAddress) onlyOwner external {
        challengeScienceContract = ChallengeScienceInterface(_newChallengeScienceAddress);
    }

    /**
     * @dev Set the address of the secret hero training formula contract.
     * @param _newTrainingScienceAddress An address of a TrainingScience contract.
     */
    function setTrainingScienceContract(address _newTrainingScienceAddress) onlyOwner external {
        trainingScienceContract = TrainingScienceInterface(_newTrainingScienceAddress);
    }


    /* ======== MODIFIERS ======== */

    /**
     * @dev Throws if _dungeonId is not created yet.
     */
    modifier dungeonExists(uint _dungeonId) {
        require(_dungeonId < dungeonTokenContract.totalSupply());
        _;
    }


    /* ======== HELPER FUNCTIONS ======== */

    /**
     * @dev An internal function to calculate the top 5 heroes power of a player.
     */
    function _getTop5HeroesPower(address _address, uint _dungeonId) internal view returns (uint) {
        uint heroCount = heroTokenContract.balanceOf(_address);

        if (heroCount == 0) {
            return 0;
        }

        // Compute all hero powers for further calculation.
        uint[] memory heroPowers = new uint[](heroCount);

        for (uint i = 0; i < heroCount; i++) {
            uint heroId = heroTokenContract.ownerTokens(_address, i);
            uint genes;
            (,,, genes) = heroTokenContract.heroes(heroId);
            // Power of dungeonId = 0 (no super hero boost).
            heroPowers[i] = _getHeroPower(genes, _dungeonId);
        }

        // Calculate the top 5 heroes power.
        uint result;
        uint curMax;
        uint curMaxIndex;

        for (uint j; j < 5; j++){
            for (uint k = 0; k < heroPowers.length; k++) {
                if (heroPowers[k] > curMax) {
                    curMax = heroPowers[k];
                    curMaxIndex = k;
                }
            }

            result += curMax;
            heroPowers[curMaxIndex] = 0;
            curMax = 0;
            curMaxIndex = 0;
        }

        return result;
    }

    /**
     * @dev An internal function to calculate the power of a hero,
     *  it calculates the base equipment power, stats power, and "Super" multiplier.
     */
    function _getHeroPower(uint _genes, uint _dungeonId) internal view returns (uint) {
        uint difficulty;
        (,, difficulty,,,,,,) = dungeonTokenContract.dungeons(_dungeonId);

        // Calculate total stats power.
        uint statsPower;

        for (uint i = 0; i < 4; i++) {
            statsPower += _genes % 32 + 1;
            _genes /= 32 ** 4;
        }

        // Calculate total equipment power.
        uint equipmentPower;
        uint superRank = _genes % 32;

        for (uint j = 4; j < 12; j++) {
            uint curGene = _genes % 32;
            equipmentPower += EQUIPMENT_POWERS[curGene];
            _genes /= 32 ** 4;

            if (superRank != curGene) {
                superRank = 0;
            }
        }

        // Calculate super power boost.
        bool isSuper = superRank >= 16;
        uint superBoost;

        if (isSuper) {
            superBoost = (difficulty - 1) * SUPER_HERO_MULTIPLIER;
        }

        return statsPower + equipmentPower + superBoost;
    }

    /**
     * @dev An internal function to calculate the difficulty of a dungeon floor.
     */
    function _getDungeonPower(uint _genes) internal view returns (uint) {
        // Calculate total dungeon power.
        uint dungeonPower;

        for (uint j = 0; j < 12; j++) {
            dungeonPower += EQUIPMENT_POWERS[_genes % 32];
            _genes /= 32 ** 4;
        }

        return dungeonPower;
    }

}
/**
 * @title DungeonBase
 * @dev Base contract for Ether Dungeon. It implements all necessary sub-classes,
 * holds all the base storage variables, and some commonly used functions.
 */
function setHeroTokenContract(address _newHeroTokenContract) onlyOwner external {
        heroTokenContract = HeroToken(_newHeroTokenContract);
    }
/**
 * @dev Set the address of the hero token contract.
 * @param _newHeroTokenContract An address of a HeroToken contract.
 */