cryptozombies全攻略四
第一章.可支付
先总结一下之前的函数修饰符
1.private 合约内部调用
2.public 任何地方调用
3.internal 能被继承的合约调用
4.external 只能从合约外部调用
5.view 不会更改和保存任何数据
6.pure 不写数据,也不读数据
7.自定义modifier
然后我们来学一个新的函数修饰符payable
payable方法是一种可以接收以太的特殊函数
在以太坊中,因为钱(以太),数据(事务负载)以及合约代码
本身都存在于以太坊,
所以可以同时调用函数并付钱给另一个合约
举个例子
contract OnlineStore {
function buySomething() external payable {
// 检查以确定0.001以太发送出去来运行函数:
require(msg.value == 0.001 ether);
// 如果为真,一些用来向函数调用者发送数字内容的逻辑
transferThing(msg.sender);
}
}
我们来写一个payable函数
uint levelUpFee = 0.001 ether;
function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level++;
}
这里我们定义一个uint,为0.001以太
然后写一个payable方法
如果数额满足0.001以太
那么就根据zombieId找到zombie
然后让它的level+1
第二章.提现
我们需要写一个函数从合约中提现以太
举个例子
contract GetPaid is Ownable {
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
}
这里的this.balance将返回这个合约里存储了多少以太
然后通过transfer
我们可以通过transfer向任何以太坊地址付钱
比如
msg.sender.transfer(msg.value);
我们创建一个withdraw函数用来提现
再创建一个函数,来设置levelUpFee来修改这个费用
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
第三章.新合约
pragma solidity ^0.4.19;
import "./zombiehelper.sol";
contract ZombieBattle is ZombieHelper {
}
第四章.随机数
我们还是通过keccak256来制造一些随机数
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
通过时间,地址和randNonce来生成随机数
contract ZombieBattle is ZombieHelper {
uint randNonce = 0;
function randMod(uint _modulus) internal returns(uint) {
randNonce++;
return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
}
}
我们定义一个randNonce
然后创建一个randMod函数
传入一个uint _modulus,返回一个uint
每次执行函数的时候
randNonce++
然后我们根据now,msg.sender,randNonce计算出一个keccak256值
再用这个值% _modulus
得到一个伪随机数.
第五章.对战
定义一个 uint attackVictoryProbability = 70;
再创建一个函数
function attack(uint _zombieId, uint _targetId) external {
}
传入一个zombieId,再传入一个targetId,也就是要攻击的僵尸的id
第六章.重构通用逻辑
在我们执行attack方法的时候
需要去检查一下传入的zombieId的拥有者是否是当前用户
所以我们需要执行
require(msg.sender == zombieToOwner[_zombieId]);
我们发现再很多方法里面我们都需要执行这个方法
那么我们可以把这行代码
放到modifier里面,变成一个函数修饰符
modifier ownerOf(uint _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
_;
}
第七章.重构
然后我们把其他的一些方法加上ownerOf修饰符
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 attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
Zombie storage myZombie = zombies[_zombieId];
Zombie storage enemyZombie = zombies[_targetId];
uint rand = randMod(100);
}
我们根据_zombieId从zombies里面拿到myZombie对象
再根据_targetId从zombies里面拿到enemyZombie对象
然后给randMod方法传入一个modulus,100
拿到一个rand随机数
第九章.输赢
我们需要给Zombie结构体增加两个属性
struct Zombie {
string name;
uint dna;
uint32 level;
uint32 readyTime;
uint16 winCount;
uint16 lossCount;
}
第十章.胜利
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");
}
如果rand随机数小于等于attackVictoryProbability
也就是小于等于70的时候,就是胜利了
那么winCount++
level++
然后输家lossCount++
然后调用feedAndMultiply
第十一章.失败
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);
}
}
失败后
自身lossCount++;
赢家winCount++;
然后执行triggerCooldown方法