Solidity Zombies study notes

A crypto zombie game for Ethereum . In order to facilitate memory, the output code of the tutorial is posted.

ZombieFactory.sol

pragma solidity ^0.4.19;
import "./ownable.sol";

contract ZombieFactory is Ownable {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;
    uint cooldownTime = 1 days;

    struct Zombie {
      string name;
      uint dna;
      uint32 level;
      uint32 readyTime;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string _name, uint _dna) internal {
        uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime))) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        require(ownerZombieCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        randDna = randDna - randDna % 100;
        _createZombie(_name, randDna);
    }

}

Ownable is the Ownable contract from the OpenZeppelin Solidity library. Events are a mechanism by which contracts and blockchains communicate. Your front-end application "listens" to certain events and reacts to them. Such as:

var abi = // abi是由编译器生成的  
var ZombieFactoryContract = web3.eth.contract(abi)  
var contractAddress = /// 发布之后在以太坊上生成的合约地址  
var ZombieFactory = ZombieFactoryContract.at(contractAddress)   
// ZombieFactory能访问公共的函数以及事件
>
// 监听NewZombie事件, 并且更新UI  
var event = ZombieFactory.NewZombie(function(error, result) {  
  if (error) return
  generateZombie(result.zombieId, result.name, result.dna)
})  

ZombieFeeding.sol

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

contract KittyInterface {  
  function getKitty(uint256 _id) external view returns (  
    bool isGestating,  
    bool isReady,  
    uint256 cooldownIndex,  
    uint256 nextActionAt,  
    uint256 siringWithId,  
    uint256 birthTime,  
    uint256 matronId,  
    uint256 sireId,  
    uint256 generation,  
    uint256 genes  
  );  
}

contract ZombieFeeding is ZombieFactory {

  KittyInterface kittyContract;

  modifier ownerOf(uint _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    _;
  }

  function setKittyContractAddress(address _address) external onlyOwner {
    kittyContract = KittyInterface(_address);
  }

  function _triggerCooldown(Zombie storage _zombie) internal {
    _zombie.readyTime = uint32(now + cooldownTime);
  }

  function _isReady(Zombie storage _zombie) internal view returns (bool) {
      return (_zombie.readyTime <= now);
  }

  function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal ownerOf(_zombieId) {
    Zombie storage myZombie = zombies[_zombieId];
    require(_isReady(myZombie));
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(_species) == keccak256("kitty")) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
    _triggerCooldown(myZombie);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }
}

In Solidity, there are some global variables that can be called by all functions. One of them is msg.sender, which refers to the address of the current caller (or smart contract).
Solidity uses its own local time unit.

The variable now will return the current unix timestamp (seconds elapsed since January 1, 1970). The unix time was 1515527488 when I wrote this.

Note: Unix time is traditionally stored as a 32-bit integer. This leads to the "2038" problem, when the 32-bit unix timestamp is not enough, overflows, and legacy systems that use this time are in trouble. So, if we want our DApp to run for 20 years, we can use 64-bit integers to represent time, but for that our users have to pay more gas. What a dilemma!

Solidity also includes time units such as seconds, minutes, hours, days, weeks, and years. They will be converted into the corresponding seconds into the uint. So 1 minute is 60, 1 hour is 3600 (60 seconds x 60 minutes), 1 day is 86400 (24 hours x 60 minutes x 60 seconds), and so on.

ZombieHelper.sol

pragma solidity ^0.4.19;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

  uint levelUpFee = 0.001 ether;

  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);
    _;
  }

  function withdraw() external onlyOwner {
    owner.transfer(this.balance);
  }

  function setLevelUpFee(uint _fee) external onlyOwner {
    levelUpFee = _fee;
  }

  function levelUp(uint _zombieId) external payable {
    require(msg.value == levelUpFee);
    zombies[_zombieId].level++;
  }

  function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) ownerOf(_zombieId) {
    zombies[_zombieId].name = _newName;
  }

  function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) ownerOf(_zombieId) {
    zombies[_zombieId].dna = _newDna;
  }

  function getZombiesByOwner(address _owner) external view returns(uint[]) {
    uint[] memory result = new uint[](ownerZombieCount[_owner]);
    uint counter = 0;
    for (uint i = 0; i < zombies.length; i++) {
      if (zombieToOwner[i] == _owner) {
        result[counter] = i;
        counter++;
      }
    }
    return result;
  }

}

msg.value is a way to see how much ether was sent to the contract, and ether is a built-in unit.
What's happening here is that some people will call this function from web3.js (from the front end of the DApp) like this:

// OnlineStoreAssume to point to your contract on Ethereum:
OnlineStore.buySomething().send(from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001))

ZombieBattle.sol

pragma solidity ^0.4.19;
import "./zombiehelper.sol";
contract ZombieBattle is ZombieHelper {
  uint randNonce = 0;
  uint attackVictoryProbability = 70;

  function randMod(uint _modulus) internal returns(uint) {
    randNonce++;
    return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
  }

  function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
    Zombie storage myZombie = zombies[_zombieId];
    Zombie storage enemyZombie = zombies[_targetId];
    uint rand = randMod(100);
    if (rand <= attackVictoryProbability) {
      myZombie.winCount++;
      myZombie.level++;
      enemyZombie.lossCount++;
      feedAndMultiply(_zombieId, enemyZombie.dna, "zombie");
    } else {
      myZombie.lossCount++;
      enemyZombie.winCount++;
      _triggerCooldown(myZombie);
    }
  }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325238327&siteId=291194637