contract ChainMonstersCore is ChainMonstersAuction, Ownable {
    // using a bool to enable us to prepare the game
    bool hasLaunched = false;

    // this address will hold future gamelogic in place
    address gameContract;

    // this contract
    address omegaContract;

    function ChainMonstersCore() public {
        adminAddress = msg.sender;

        _createArea(); // area 1
        _createArea(); // area 2
    }

    // we don't know the exact interfaces yet so use the lockedMonsterStats value to determine if the game is "ready"
    // see WhitePaper for explaination for our upgrade and development roadmap
    function setGameLogicContract(address _candidateContract) external onlyOwner {
        require(monsterCreator.lockedMonsterStatsCount() == 151);

        require(GameLogicContract(_candidateContract).isGameLogicContract());

        gameContract = _candidateContract;
    }

    function setOmegaContract(address _candidateContract) external onlyOwner {
        require(OmegaContract(_candidateContract).isOmegaContract());
        omegaContract = _candidateContract;
    }

    // omega contract takes care of all neccessary checks so assume that this is correct(!)
    function evolveMonster(uint256 _tokenId, uint16 _toMonsterId) external {
        require(msg.sender == omegaContract);

        // retrieve current monster struct
        Monster storage mon = monsters[_tokenId];

        // evolving only changes monster ID since this is responsible for base Stats
        // an evolved monster keeps its gender, generation, IVs and EVs
        mon.mID = _toMonsterId;
    }

    // only callable by gameContract after the full game is launched
    // since all additional monsters after the promo/gen0 ones need to use this coreContract
    // contract as well we have to prepare this core for our future updates where
    // players can freely roam the world and hunt ChainMonsters thus generating more
    function spawnMonster(uint256 _mId, address _owner) external {
        require(msg.sender == gameContract);

        uint8[7] memory ivs = uint8[7](monsterCreator.getMonsterIVs());

        bool gender = monsterCreator.getMonsterGender();

        bool shiny = false;
        if (ivs[6] == 1) {
            shiny = true;
        }
        
        // important to note that the IV generators do not use Gen0 methods and are Generation 1
        // this means there won't be more than the 10,000 Gen0 monsters sold during the development through the marketplace
        uint256 monsterId = _createMonster(1, _owner, _mId, false, gender, shiny);
        monsterIdToTradeable[monsterId] = true;

        monsterIdToIVs[monsterId] = ivs;
    }

    // used to add playable content to the game
    // monsters will only spawn in certain areas so some are locked on release
    // due to the game being in active development on "launch"
    // each monster has a maximum number of 3 areas where it can appear
    function createArea() public onlyAdmin {
        _createArea();
    }

    function createTrainer(string _username, uint16 _starterId) public {
        require(hasLaunched);

        // only one trainer/account per ethereum address
        require(addressToTrainer[msg.sender].owner == 0);

        // valid input check
        require(_starterId == 1 || _starterId == 2 || _starterId == 3);

        uint256 mon = _createTrainer(_username, _starterId, msg.sender);

        // due to stack limitations we have to assign the IVs here:
        monsterIdToIVs[mon] = monsterCreator.getMonsterIVs();
    }

    function changeUsername(string _name) public {
        require(addressToTrainer[msg.sender].owner == msg.sender);
        addressToTrainer[msg.sender].username = _name;
    }

    function changeMonsterNickname(uint256 _tokenId, string _name) public {
        // users won't be able to rename a monster that is part of an auction
        require(_owns(msg.sender, _tokenId));

        // some string checks...?
        monsterIdToNickname[_tokenId] = _name;
    }

    function moveToArea(uint16 _newArea) public {
        require(addressToTrainer[msg.sender].currArea > 0);

        // never allow anyone to move to area 0 or below since this is used
        // to determine if a trainer profile exists in another method!
        require(_newArea > 0);

        // make sure that this area exists yet!
        require(areas.length >= _newArea);

        // when player is not stuck doing something else he can move freely!
        _moveToArea(_newArea, msg.sender);
    }

    // to be changed to retrieve current stats!
    function getMonster(uint256 _id) external view returns (
        uint256 birthTime, uint256 generation, uint8[8] stats,
        uint256 mID, bool tradeable, uint256 uID)
    {
        Monster storage mon = monsters[_id];
        birthTime = uint256(mon.birthTime);
        generation = mon.generation; // hardcoding due to stack too deep error
        mID = uint256(mon.mID);
        tradeable = bool(mon.tradeable);

        // these values are retrieved from monsterCreator
        stats = uint8[8](monsterCreator.getMonsterStats(uint256(mon.mID)));

        // hack to overcome solidity's stack limitation in monster struct....
        uID = _id;
    }

    function isTrainer(address _check) external view returns (bool isTrainer) {
        Trainer storage trainer = addressToTrainer[_check];

        return (trainer.currArea > 0);
    }

    function withdrawBalance() external onlyOwner {
        uint256 balance = this.balance;

        owner.transfer(balance);
    }

    // after we have setup everything we can unlock the game
    // for public
    function launchGame() external onlyOwner {
        hasLaunched = true;
    }
}
function getMonster(uint256 _id) external view returns (
    uint256 birthTime, uint256 generation, uint8[8] stats,
    uint256 mID, bool tradeable, uint256 uID)
{
    Monster storage mon = monsters[_id];
    birthTime = uint256(mon.birthTime);
    generation = mon.generation; // hardcoding due to stack too deep error
    mID = uint256(mon.mID);
    tradeable = bool(mon.tradeable);

    // these values are retrieved from monsterCreator
    stats = uint8[8](monsterCreator.getMonsterStats(uint256(mon.mID)));

    // hack to overcome solidity's stack limitation in monster struct....
    uID = _id;
}

// to be changed to retrieve current stats!