Blockchain Game Development (2): Zombie War

Source of this project https://cryptozombies.io/

0. Preface

In the previous section, we only realized the generation of zombies, but did not specify a specific owner for each zombie. Today, we mainly added the user authentication function to the project, so as to support multiple users. At the same time, the attack fusion function is added, which can fuse two zombies to form a new zombie, and at the same time call the chain code of CryptoKitties to complete the fusion of CryptoKitties and zombies.

1. Key knowledge

  • Mapping and Address . The address is a unique data type in the Solidity language, which marks an Ethereum user (it can be an ordinary user or a contract user). A map is essentially a key-value pair for storing and looking up data, declared as follows:
 //声明了一个地址到uint类型的映射。
mapping (address => uint) public accountBalance;
//声明了一个uint类型到字符串类型的映射。
mapping (uint => string) userIdToName;
  • msg.sender : 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). The following is an example of using msg.sender to update a mapping:
//声明一个映射。
mapping (address => uint) favoriteNumber;
function setMyNumber(uint _myNumber) public {
  // 更新我们的 `favoriteNumber` 映射来将 `_myNumber`存储在 `msg.sender`名下
  favoriteNumber[msg.sender] = _myNumber;
  // 存储数据至映射的方法和将数据存储在数组相似
}

function whatIsMyNumber() public view returns (uint) {
  // 拿到存储在调用者地址名下的值
  // 若调用者还没调用 setMyNumber, 则值为 `0`
  return favoriteNumber[msg.sender];
}
  • require : Make the function throw an error when certain conditions are not met during execution , and stop execution , as shown below:
function sayHiToVitalik(string _name) public returns (string) {
  // 比较 _name 是否等于 "Vitalik". 如果不成立,抛出异常并终止程序
  // (敲黑板: Solidity 并不支持原生的字符串比较, 我们只能通过比较
  // 两字符串的 keccak256 哈希值来进行判断)
  require(keccak256(_name) == keccak256("Vitalik"));
  // 如果返回 true, 运行如下语句
  return "Hi!";
}
  • Support inheritance and introduction . After inheriting the parent class, you can use the functions of the parent class. Similarly, you can also use the contract functions inside after quoting. Reasonable use of inheritance and introduction can make the code more concise.
  • Two forms of storage variables: storage or memory . Storage variables refer to variables that are permanently stored in the blockchain. The Memory variable is temporary. When the external function calls a contract, the memory variable is removed.
  • internal and external modifiers: internal and private are similar, however, if a contract inherits from its parent contract, the contract can access the "internal" functions defined in the parent contract. .
    external is similar to public, except that these functions can only be called from outside the contract - they cannot be called by other functions inside the contract. We'll discuss when to use external and public later.
  • Interact with other contracts: You need to define an interface first. If there is this contract on the blockchain:
contract LuckyNumber {
  mapping(address => uint) numbers;
  function setNum(uint _num) public {
    numbers[msg.sender] = _num;
  }
  function getNum(address _myAddress) public view returns (uint) {
    return numbers[_myAddress];
  }
}

We can declare an interface:

contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

When we use it like this:

contract MyContract {
  address NumberInterfaceAddress = 0xab38...;
  // ^ 这是FavoriteNumber合约在以太坊上的地址
  NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);
  // 现在变量 `numberContract` 指向另一个合约对象
  function someFunction() public {
    // 现在我们可以调用在那个合约中声明的 `getNum`函数:
    uint num = numberContract.getNum(msg.sender);
    // ...在这儿使用 `num`变量做些什么
  }
}

2. Code

The programming knowledge involved in the code is above.

//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 {

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  KittyInterface kittyContract = KittyInterface(ckAddress);

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

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

}

Guess you like

Origin blog.csdn.net/doreen211/article/details/129170456