Transaction Hash:
Block:
4984477 at Jan-27-2018 11:00:12 PM +UTC
Transaction Fee:
0.000177604 ETH
$0.36
Gas Used:
177,604 Gas / 1 Gwei
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x180bA8f7...fd936E617
Miner
| 294.032234891102309655 Eth | 294.032412495102309655 Eth | 0.000177604 | ||
| 0x69aAE7a2...9d16360B3 |
0.058804287777 Eth
Nonce: 582
|
0.057626683777 Eth
Nonce: 583
| 0.001177604 | ||
| 0xABC1c404...81D18Eb3E | (Etheremon: Data) | ||||
| 0xcaEF67F7...5b7dE8De5 | (Etheremon: Gym) | 4.549 Eth | 4.55 Eth | 0.001 |
Execution Trace
ETH 0.001
EtheremonGym.startTraining( _objId=1556, _trainerLevel=4, _t1=2, _t2=4, _t3=6 )
-
EtheremonData.getMonsterObj( _objId=1556 ) => ( objId=1556, classId=7, trainer=0x69aAE7a2969d5ef1A6521ED2F2CC68b9d16360B3, exp=586, createIndex=52, lastClaimIndex=345, createTime=1513756143 ) -
EtheremonData.getElementInArrayType( _type=3, _id=1556, _index=0 ) => ( 72 ) -
EtheremonData.getElementInArrayType( _type=3, _id=1556, _index=1 ) => ( 49 ) -
EtheremonData.getElementInArrayType( _type=3, _id=1556, _index=2 ) => ( 49 ) -
EtheremonData.getElementInArrayType( _type=3, _id=1556, _index=3 ) => ( 74 ) -
EtheremonData.getElementInArrayType( _type=3, _id=1556, _index=4 ) => ( 67 ) -
EtheremonData.getElementInArrayType( _type=3, _id=1556, _index=5 ) => ( 63 ) EtheremonWorld.increaseMonsterExp( _objId=1556, amount=75 )-
EtheremonData.increaseMonsterExp( _objId=1556, amount=75 )
-
startTraining[EtheremonGym (ln:598)]
revert[EtheremonGym (ln:600)]revert[EtheremonGym (ln:602)]revert[EtheremonGym (ln:604)]revert[EtheremonGym (ln:606)]getObjInfo[EtheremonGym (ln:609)]getMonsterObj[EtheremonGym (ln:594)]getLevel[EtheremonGym (ln:595)]
revert[EtheremonGym (ln:611)]getElementInArrayType[EtheremonGym (ln:617)]safeMult[EtheremonGym (ln:620)]getRandom[EtheremonGym (ln:624)]blockhash[EtheremonGym (ln:458)]
attackTrainer[EtheremonGym (ln:625)]safeMult[EtheremonGym (ln:570)]getTypeSupport[EtheremonGym (ln:575)]attack[EtheremonGym (ln:586)]safeDeduct[EtheremonGym (ln:542)]calHpDeducted[EtheremonGym (ln:542)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
safeDeduct[EtheremonGym (ln:544)]calHpDeducted[EtheremonGym (ln:544)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
safeDeduct[EtheremonGym (ln:551)]calHpDeducted[EtheremonGym (ln:551)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
safeDeduct[EtheremonGym (ln:553)]calHpDeducted[EtheremonGym (ln:553)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
attackTrainer[EtheremonGym (ln:627)]safeMult[EtheremonGym (ln:570)]getTypeSupport[EtheremonGym (ln:575)]attack[EtheremonGym (ln:586)]safeDeduct[EtheremonGym (ln:542)]calHpDeducted[EtheremonGym (ln:542)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
safeDeduct[EtheremonGym (ln:544)]calHpDeducted[EtheremonGym (ln:544)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
safeDeduct[EtheremonGym (ln:551)]calHpDeducted[EtheremonGym (ln:551)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
safeDeduct[EtheremonGym (ln:553)]calHpDeducted[EtheremonGym (ln:553)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
attackTrainer[EtheremonGym (ln:629)]safeMult[EtheremonGym (ln:570)]getTypeSupport[EtheremonGym (ln:575)]attack[EtheremonGym (ln:586)]safeDeduct[EtheremonGym (ln:542)]calHpDeducted[EtheremonGym (ln:542)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
safeDeduct[EtheremonGym (ln:544)]calHpDeducted[EtheremonGym (ln:544)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
safeDeduct[EtheremonGym (ln:551)]calHpDeducted[EtheremonGym (ln:551)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
safeDeduct[EtheremonGym (ln:553)]calHpDeducted[EtheremonGym (ln:553)]safeDeduct[EtheremonGym (ln:520)]safeDeduct[EtheremonGym (ln:521)]
getGainExp[EtheremonGym (ln:632)]increaseMonsterExp[EtheremonGym (ln:634)]
File 1 of 3: EtheremonGym
File 2 of 3: EtheremonData
File 3 of 3: EtheremonWorld
pragma solidity ^0.4.16;
// copyright contact@Etheremon.com
contract SafeMath {
/* function assert(bool assertion) internal { */
/* if (!assertion) { */
/* throw; */
/* } */
/* } // assert no longer needed once solidity is on 0.4.10 */
function safeAdd(uint256 x, uint256 y) pure internal returns(uint256) {
uint256 z = x + y;
assert((z >= x) && (z >= y));
return z;
}
function safeSubtract(uint256 x, uint256 y) pure internal returns(uint256) {
assert(x >= y);
uint256 z = x - y;
return z;
}
function safeMult(uint256 x, uint256 y) pure internal returns(uint256) {
uint256 z = x * y;
assert((x == 0)||(z/x == y));
return z;
}
}
contract BasicAccessControl {
address public owner;
// address[] public moderators;
uint16 public totalModerators = 0;
mapping (address => bool) public moderators;
bool public isMaintaining = true;
function BasicAccessControl() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
modifier onlyModerators() {
require(msg.sender == owner || moderators[msg.sender] == true);
_;
}
modifier isActive {
require(!isMaintaining);
_;
}
function ChangeOwner(address _newOwner) onlyOwner public {
if (_newOwner != address(0)) {
owner = _newOwner;
}
}
function AddModerator(address _newModerator) onlyOwner public {
if (moderators[_newModerator] == false) {
moderators[_newModerator] = true;
totalModerators += 1;
}
}
function RemoveModerator(address _oldModerator) onlyOwner public {
if (moderators[_oldModerator] == true) {
moderators[_oldModerator] = false;
totalModerators -= 1;
}
}
function UpdateMaintaining(bool _isMaintaining) onlyOwner public {
isMaintaining = _isMaintaining;
}
}
contract EtheremonEnum {
enum ResultCode {
SUCCESS,
ERROR_CLASS_NOT_FOUND,
ERROR_LOW_BALANCE,
ERROR_SEND_FAIL,
ERROR_NOT_TRAINER,
ERROR_NOT_ENOUGH_MONEY,
ERROR_INVALID_AMOUNT,
ERROR_OBJ_NOT_FOUND,
ERROR_OBJ_INVALID_OWNERSHIP
}
enum ArrayType {
CLASS_TYPE,
STAT_STEP,
STAT_START,
STAT_BASE,
OBJ_SKILL
}
enum PropertyType {
ANCESTOR,
XFACTOR
}
enum BattleResult {
CASTLE_WIN,
CASTLE_LOSE,
CASTLE_DESTROYED
}
enum CacheClassInfoType {
CLASS_TYPE,
CLASS_STEP,
CLASS_ANCESTOR
}
}
contract EtheremonDataBase is EtheremonEnum, BasicAccessControl, SafeMath {
uint64 public totalMonster;
uint32 public totalClass;
// read
function getSizeArrayType(ArrayType _type, uint64 _id) constant public returns(uint);
function getElementInArrayType(ArrayType _type, uint64 _id, uint _index) constant public returns(uint8);
function getMonsterClass(uint32 _classId) constant public returns(uint32 classId, uint256 price, uint256 returnPrice, uint32 total, bool catchable);
function getMonsterObj(uint64 _objId) constant public returns(uint64 objId, uint32 classId, address trainer, uint32 exp, uint32 createIndex, uint32 lastClaimIndex, uint createTime);
function getMonsterName(uint64 _objId) constant public returns(string name);
function getExtraBalance(address _trainer) constant public returns(uint256);
function getMonsterDexSize(address _trainer) constant public returns(uint);
function getMonsterObjId(address _trainer, uint index) constant public returns(uint64);
function getExpectedBalance(address _trainer) constant public returns(uint256);
function getMonsterReturn(uint64 _objId) constant public returns(uint256 current, uint256 total);
}
contract EtheremonGateway is EtheremonEnum, BasicAccessControl {
// using for battle contract later
function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
// read
function isGason(uint64 _objId) constant external returns(bool);
function getObjBattleInfo(uint64 _objId) constant external returns(uint32 classId, uint32 exp, bool isGason,
uint ancestorLength, uint xfactorsLength);
function getClassPropertySize(uint32 _classId, PropertyType _type) constant external returns(uint);
function getClassPropertyValue(uint32 _classId, PropertyType _type, uint index) constant external returns(uint32);
}
contract EtheremonGym is EtheremonEnum, BasicAccessControl, SafeMath {
uint8 constant public STAT_COUNT = 6;
struct MonsterObjAcc {
uint64 monsterId;
uint32 classId;
address trainer;
string name;
uint32 exp;
uint32 createIndex;
uint32 lastClaimIndex;
uint createTime;
}
struct AttackData {
uint32 objClassId;
address trainee;
uint8 objLevel;
uint8 winCount;
uint32 winExp;
uint32 loseExp;
}
struct HpData {
uint16 aHpAttack;
uint16 aHpAttackCritical;
uint16 bHpAttack;
uint16 bHpAttackCritical;
}
struct GymTrainer {
uint32 classId;
uint8[6] statBases;
}
struct TrainingLog {
uint8[3] trainers;
uint8 trainerLevel;
uint64 objId;
uint8 objLevel;
uint8 ran;
}
struct CacheClassInfo {
uint8[] types;
uint8[] steps;
uint32[] ancestors;
}
mapping(uint8 => GymTrainer) public gymTrainers;
mapping(address => TrainingLog) public trainees;
mapping(uint8 => uint8) typeAdvantages;
mapping(uint32 => CacheClassInfo) cacheClasses;
mapping(uint8 => uint32) levelExps;
mapping(uint8 => uint32) levelExpGains;
uint256 public gymFee = 0.001 ether;
uint8 public maxTrainerLevel = 5;
uint8 public totalTrainer = 0;
uint8 public maxRandomRound = 4;
uint8 public typeBuffPercentage = 20;
uint8 public minHpDeducted = 10;
uint8 public expPercentage = 70;
// contract
address public worldContract;
address public dataContract;
// modifier
modifier requireDataContract {
require(dataContract != address(0));
_;
}
modifier requireWorldContract {
require(worldContract != address(0));
_;
}
// constructor
function EtheremonGym(address _dataContract, address _worldContract) public {
dataContract = _dataContract;
worldContract = _worldContract;
}
// admin & moderators
function setTypeAdvantages() onlyModerators external {
typeAdvantages[1] = 14;
typeAdvantages[2] = 16;
typeAdvantages[3] = 8;
typeAdvantages[4] = 9;
typeAdvantages[5] = 2;
typeAdvantages[6] = 11;
typeAdvantages[7] = 3;
typeAdvantages[8] = 5;
typeAdvantages[9] = 15;
typeAdvantages[11] = 18;
// skipp 10
typeAdvantages[12] = 7;
typeAdvantages[13] = 6;
typeAdvantages[14] = 17;
typeAdvantages[15] = 13;
typeAdvantages[16] = 12;
typeAdvantages[17] = 1;
typeAdvantages[18] = 4;
}
function setTypeAdvantage(uint8 _type1, uint8 _type2) onlyModerators external {
typeAdvantages[_type1] = _type2;
}
function setCacheClassInfo(uint32 _classId) onlyModerators requireDataContract requireWorldContract public {
EtheremonDataBase data = EtheremonDataBase(dataContract);
EtheremonGateway gateway = EtheremonGateway(worldContract);
uint i = 0;
CacheClassInfo storage classInfo = cacheClasses[_classId];
// add type
i = data.getSizeArrayType(ArrayType.CLASS_TYPE, uint64(_classId));
uint8[] memory aTypes = new uint8[](i);
for(; i > 0 ; i--) {
aTypes[i-1] = data.getElementInArrayType(ArrayType.CLASS_TYPE, uint64(_classId), i-1);
}
classInfo.types = aTypes;
// add steps
i = data.getSizeArrayType(ArrayType.STAT_STEP, uint64(_classId));
uint8[] memory steps = new uint8[](i);
for(; i > 0 ; i--) {
steps[i-1] = data.getElementInArrayType(ArrayType.STAT_STEP, uint64(_classId), i-1);
}
classInfo.steps = steps;
// add ancestor
i = gateway.getClassPropertySize(_classId, PropertyType.ANCESTOR);
uint32[] memory ancestors = new uint32[](i);
for(; i > 0 ; i--) {
ancestors[i-1] = gateway.getClassPropertyValue(_classId, PropertyType.ANCESTOR, i-1);
}
classInfo.ancestors = ancestors;
}
function fastSetCacheClassInfo(uint32 _classId1, uint32 _classId2, uint32 _classId3, uint32 _classId4) onlyModerators requireDataContract requireWorldContract external {
setCacheClassInfo(_classId1);
setCacheClassInfo(_classId2);
setCacheClassInfo(_classId3);
setCacheClassInfo(_classId4);
}
function presetGymTrainer() onlyModerators external {
GymTrainer storage trainer1 = gymTrainers[1];
trainer1.classId = 12;
trainer1.statBases[0] = 85;
trainer1.statBases[1] = 95;
trainer1.statBases[2] = 65;
trainer1.statBases[3] = 50;
trainer1.statBases[4] = 50;
trainer1.statBases[5] = 50;
GymTrainer storage trainer2 = gymTrainers[2];
trainer2.classId = 15;
trainer2.statBases[0] = 50;
trainer2.statBases[1] = 55;
trainer2.statBases[2] = 85;
trainer2.statBases[3] = 85;
trainer2.statBases[4] = 40;
trainer2.statBases[5] = 75;
GymTrainer storage trainer3 = gymTrainers[3];
trainer3.classId = 8;
trainer3.statBases[0] = 110;
trainer3.statBases[1] = 60;
trainer3.statBases[2] = 40;
trainer3.statBases[3] = 60;
trainer3.statBases[4] = 40;
trainer3.statBases[5] = 40;
GymTrainer storage trainer4 = gymTrainers[4];
trainer4.classId = 4;
trainer4.statBases[0] = 54;
trainer4.statBases[1] = 69;
trainer4.statBases[2] = 58;
trainer4.statBases[3] = 75;
trainer4.statBases[4] = 75;
trainer4.statBases[5] = 70;
GymTrainer storage trainer5 = gymTrainers[5];
trainer5.classId = 6;
trainer5.statBases[0] = 50;
trainer5.statBases[1] = 50;
trainer5.statBases[2] = 50;
trainer5.statBases[3] = 105;
trainer5.statBases[4] = 55;
trainer5.statBases[5] = 95;
GymTrainer storage trainer6 = gymTrainers[6];
trainer6.classId = 13;
trainer6.statBases[0] = 55;
trainer6.statBases[1] = 90;
trainer6.statBases[2] = 95;
trainer6.statBases[3] = 45;
trainer6.statBases[4] = 35;
trainer6.statBases[5] = 35;
GymTrainer storage trainer7 = gymTrainers[7];
trainer7.classId = 7;
trainer7.statBases[0] = 85;
trainer7.statBases[1] = 60;
trainer7.statBases[2] = 73;
trainer7.statBases[3] = 75;
trainer7.statBases[4] = 80;
trainer7.statBases[5] = 50;
GymTrainer storage trainer8 = gymTrainers[8];
trainer8.classId = 24;
trainer8.statBases[0] = 140;
trainer8.statBases[1] = 135;
trainer8.statBases[2] = 70;
trainer8.statBases[3] = 77;
trainer8.statBases[4] = 90;
trainer8.statBases[5] = 50;
GymTrainer storage trainer9 = gymTrainers[9];
trainer9.classId = 16;
trainer9.statBases[0] = 70;
trainer9.statBases[1] = 105;
trainer9.statBases[2] = 80;
trainer9.statBases[3] = 60;
trainer9.statBases[4] = 80;
trainer9.statBases[5] = 90;
totalTrainer = 9;
}
function setGymTrainer(uint8 _trainerId, uint32 _classId, uint8 _s0, uint8 _s1, uint8 _s2, uint8 _s3, uint8 _s4, uint8 _s5) onlyModerators external {
GymTrainer storage trainer = gymTrainers[_trainerId];
if (trainer.classId == 0)
totalTrainer += 1;
trainer.classId = _classId;
trainer.statBases[0] = _s0;
trainer.statBases[1] = _s1;
trainer.statBases[2] = _s2;
trainer.statBases[3] = _s3;
trainer.statBases[4] = _s4;
trainer.statBases[5] = _s5;
}
function setContract(address _dataContract, address _worldContract) onlyModerators external {
dataContract = _dataContract;
worldContract = _worldContract;
}
function setConfig(uint256 _gymFee, uint8 _maxTrainerLevel, uint8 _maxRandomRound, uint8 _typeBuffPercentage,
uint8 _minHpDeducted, uint8 _expPercentage) onlyModerators external {
gymFee = _gymFee;
maxTrainerLevel = _maxTrainerLevel;
maxRandomRound = _maxRandomRound;
typeBuffPercentage = _typeBuffPercentage;
minHpDeducted = _minHpDeducted;
expPercentage = _expPercentage;
}
function genLevelExp() onlyModerators external {
uint8 level = 1;
uint32 requirement = 100;
uint32 sum = requirement;
while(level <= 100) {
levelExps[level] = sum;
level += 1;
requirement = (requirement * 11) / 10 + 5;
sum += requirement;
}
}
function genLevelExpGain() onlyModerators external {
levelExpGains[1] = 31;
levelExpGains[2] = 33;
levelExpGains[3] = 34;
levelExpGains[4] = 36;
levelExpGains[5] = 38;
levelExpGains[6] = 40;
levelExpGains[7] = 42;
levelExpGains[8] = 44;
levelExpGains[9] = 46;
levelExpGains[10] = 48;
}
function setLevelExpGain(uint8 _level, uint32 _exp) onlyModerators external {
levelExpGains[_level] = _exp;
}
function withdrawEther(address _sendTo, uint _amount) onlyModerators external {
if (_amount > this.balance) {
revert();
}
_sendTo.transfer(_amount);
}
// public
function getCacheClassSize(uint32 _classId) constant public returns(uint, uint, uint) {
CacheClassInfo storage classInfo = cacheClasses[_classId];
return (classInfo.types.length, classInfo.steps.length, classInfo.ancestors.length);
}
function getTrainerInfo(uint8 _trainerId) constant external returns(uint32, uint8, uint8, uint8, uint8, uint8, uint8) {
GymTrainer memory trainer = gymTrainers[_trainerId];
return (trainer.classId, trainer.statBases[0], trainer.statBases[1], trainer.statBases[2], trainer.statBases[3],
trainer.statBases[4], trainer.statBases[5]);
}
function getRandom(uint8 maxRan, uint8 index) constant public returns(uint8) {
uint256 genNum = uint256(block.blockhash(block.number-1));
for (uint8 i = 0; i < index && i < 6; i ++) {
genNum /= 256;
}
return uint8(genNum % maxRan);
}
function getLevel(uint32 exp) view public returns (uint8) {
uint8 minIndex = 1;
uint8 maxIndex = 100;
uint8 currentIndex;
while (minIndex < maxIndex) {
currentIndex = (minIndex + maxIndex) / 2;
if (exp < levelExps[currentIndex])
maxIndex = currentIndex;
else
minIndex = currentIndex + 1;
}
return minIndex;
}
function getGainExp(uint8 xLevel, uint8 yLevel) constant public returns(uint32 winExp, uint32 loseExp){
winExp = levelExpGains[yLevel] * expPercentage / 100;
if (xLevel > yLevel) {
if (xLevel > yLevel + 10) {
winExp = 5;
} else {
winExp /= uint32(3) ** (xLevel - yLevel) / uint32(2) ** (xLevel - yLevel);
if (winExp < 5)
winExp = 5;
}
}
loseExp = winExp / 3;
}
function safeDeduct(uint16 a, uint16 b) pure private returns(uint16){
if (a > b) {
return a - b;
}
return 0;
}
function getTypeSupport(uint32 _aClassId, uint32 _bClassId) constant private returns (bool aHasAdvantage, bool bHasAdvantage) {
// check types
for (uint i = 0; i < cacheClasses[_aClassId].types.length; i++) {
for (uint j = 0; j < cacheClasses[_bClassId].types.length; j++) {
if (typeAdvantages[cacheClasses[_aClassId].types[i]] == cacheClasses[_bClassId].types[j]) {
aHasAdvantage = true;
}
if (typeAdvantages[cacheClasses[_bClassId].types[j]] == cacheClasses[_aClassId].types[i]) {
bHasAdvantage = true;
}
}
}
}
function calHpDeducted(uint16 _attack, uint16 _specialAttack, uint16 _defense, uint16 _specialDefense, bool _lucky) view public returns(uint16){
if (_lucky) {
_attack = _attack * 13 / 10;
_specialAttack = _specialAttack * 13 / 10;
}
uint16 hpDeducted = safeDeduct(_attack, _defense * 3 /4);
uint16 hpSpecialDeducted = safeDeduct(_specialAttack, _specialDefense* 3 / 4);
if (hpDeducted < minHpDeducted && hpSpecialDeducted < minHpDeducted)
return minHpDeducted;
if (hpDeducted > hpSpecialDeducted)
return hpDeducted;
return hpSpecialDeducted;
}
function attack(uint8 _index, uint8 _ran, uint16[6] _aStats, uint16[6] _bStats) constant public returns(bool win) {
if (_ran < _index * maxRandomRound)
_ran = maxRandomRound;
else
_ran = _ran - _index * maxRandomRound;
uint16 round = 0;
uint16 aHp = _aStats[0];
uint16 bHp = _bStats[0];
if (_aStats[5] > _bStats[5]) {
while (round < maxRandomRound && aHp > 0 && bHp > 0) {
if (round % 2 == 0) {
// a attack
bHp = safeDeduct(bHp, calHpDeducted(_aStats[1], _aStats[3], _bStats[2], _bStats[4], round==_ran));
} else {
aHp = safeDeduct(aHp, calHpDeducted(_bStats[1], _bStats[3], _aStats[2], _aStats[4], round==_ran));
}
round++;
}
} else {
while (round < maxRandomRound && aHp > 0 && bHp > 0) {
if (round % 2 != 0) {
bHp = safeDeduct(bHp, calHpDeducted(_aStats[1], _aStats[3], _bStats[2], _bStats[4], round==_ran));
} else {
aHp = safeDeduct(aHp, calHpDeducted(_bStats[1], _bStats[3], _aStats[2], _aStats[4], round==_ran));
}
round++;
}
}
win = aHp >= bHp;
}
function attackTrainer(uint8 _index, uint8 _ran, uint8 _trainerId, uint8 _trainerLevel, uint32 _objClassId, uint16[6] _objStats) constant public returns(bool result) {
GymTrainer memory trainer = gymTrainers[_trainerId];
uint16[6] memory trainerStats;
uint i = 0;
for (i=0; i < STAT_COUNT; i+=1) {
trainerStats[i] = trainer.statBases[i];
}
for (i=0; i < cacheClasses[trainer.classId].steps.length; i++) {
trainerStats[i] += uint16(safeMult(cacheClasses[trainer.classId].steps[i], _trainerLevel*3));
}
bool objHasAdvantage;
bool trainerHasAdvantage;
(objHasAdvantage, trainerHasAdvantage) = getTypeSupport(_objClassId, trainer.classId);
uint16 originAttack = _objStats[1];
uint16 originAttackSpecial = _objStats[3];
if (objHasAdvantage) {
_objStats[1] += _objStats[1] * typeBuffPercentage / 100;
_objStats[3] += _objStats[3] * typeBuffPercentage / 100;
}
if (trainerHasAdvantage) {
trainerStats[1] += trainerStats[1] * typeBuffPercentage / 100;
trainerStats[3] += trainerStats[3] * typeBuffPercentage / 100;
}
result = attack(_index, _ran, _objStats, trainerStats);
_objStats[1] = originAttack;
_objStats[3] = originAttackSpecial;
}
function getObjInfo(uint64 _objId) constant public returns(uint32 classId, address trainee, uint8 level) {
EtheremonDataBase data = EtheremonDataBase(dataContract);
MonsterObjAcc memory obj;
(obj.monsterId, classId, trainee, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
level = getLevel(obj.exp);
}
function startTraining(uint64 _objId, uint8 _trainerLevel, uint8 _t1, uint8 _t2, uint8 _t3) isActive requireDataContract requireWorldContract payable external {
if (_trainerLevel > maxTrainerLevel)
revert();
if (msg.value != gymFee)
revert();
if (_t1 == _t2 || _t1 == _t3 || _t2 == _t3)
revert();
if (_t1 == 0 || _t2 == 0 || _t3 == 0 || _t1 > totalTrainer || _t2 > totalTrainer || _t3 > totalTrainer)
revert();
AttackData memory att;
(att.objClassId, att.trainee, att.objLevel) = getObjInfo(_objId);
if (msg.sender != att.trainee)
revert();
uint i = 0;
uint16[6] memory objStats;
EtheremonDataBase data = EtheremonDataBase(dataContract);
for (i=0; i < STAT_COUNT; i+=1) {
objStats[i] = data.getElementInArrayType(ArrayType.STAT_BASE, _objId, i);
}
for (i=0; i < cacheClasses[att.objClassId].steps.length; i++) {
objStats[i] += uint16(safeMult(cacheClasses[att.objClassId].steps[i], att.objLevel*3));
}
att.winCount = 0;
uint8 ran = getRandom(maxRandomRound*3, 0);
if (attackTrainer(0, ran, _t1, _trainerLevel, att.objClassId, objStats))
att.winCount += 1;
if (attackTrainer(1, ran, _t2, _trainerLevel, att.objClassId, objStats))
att.winCount += 1;
if (attackTrainer(2, ran, _t3, _trainerLevel, att.objClassId, objStats))
att.winCount += 1;
(att.winExp, att.loseExp) = getGainExp(att.objLevel, _trainerLevel);
EtheremonGateway gateway = EtheremonGateway(worldContract);
gateway.increaseMonsterExp(_objId, att.winCount * att.winExp + (3 - att.winCount) * att.loseExp);
TrainingLog storage trainingLog = trainees[msg.sender];
trainingLog.trainers[0] = _t1;
trainingLog.trainers[1] = _t2;
trainingLog.trainers[2] = _t3;
trainingLog.trainerLevel = _trainerLevel;
trainingLog.objId = _objId;
trainingLog.objLevel = att.objLevel;
trainingLog.ran = ran;
}
function getTrainingLog(address _trainee) constant external returns(uint8, uint8, uint8, uint64, uint8, uint8, uint8) {
TrainingLog memory trainingLog = trainees[_trainee];
return (trainingLog.trainers[0], trainingLog.trainers[1], trainingLog.trainers[2],
trainingLog.objId, trainingLog.trainerLevel, trainingLog.objLevel, trainingLog.ran);
}
}File 2 of 3: EtheremonData
pragma solidity ^0.4.16;
// copyright contact@Etheremon.com
contract SafeMath {
/* function assert(bool assertion) internal { */
/* if (!assertion) { */
/* throw; */
/* } */
/* } // assert no longer needed once solidity is on 0.4.10 */
function safeAdd(uint256 x, uint256 y) pure internal returns(uint256) {
uint256 z = x + y;
assert((z >= x) && (z >= y));
return z;
}
function safeSubtract(uint256 x, uint256 y) pure internal returns(uint256) {
assert(x >= y);
uint256 z = x - y;
return z;
}
function safeMult(uint256 x, uint256 y) pure internal returns(uint256) {
uint256 z = x * y;
assert((x == 0)||(z/x == y));
return z;
}
}
contract BasicAccessControl {
address public owner;
address[] public moderators;
function BasicAccessControl() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
modifier onlyModerators() {
if (msg.sender != owner) {
bool found = false;
for (uint index = 0; index < moderators.length; index++) {
if (moderators[index] == msg.sender) {
found = true;
break;
}
}
require(found);
}
_;
}
function ChangeOwner(address _newOwner) onlyOwner public {
if (_newOwner != address(0)) {
owner = _newOwner;
}
}
function Kill() onlyOwner public {
selfdestruct(owner);
}
function AddModerator(address _newModerator) onlyOwner public {
if (_newModerator != address(0)) {
for (uint index = 0; index < moderators.length; index++) {
if (moderators[index] == _newModerator) {
return;
}
}
moderators.push(_newModerator);
}
}
function RemoveModerator(address _oldModerator) onlyOwner public {
uint foundIndex = 0;
for (; foundIndex < moderators.length; foundIndex++) {
if (moderators[foundIndex] == _oldModerator) {
break;
}
}
if (foundIndex < moderators.length) {
moderators[foundIndex] = moderators[moderators.length-1];
delete moderators[moderators.length-1];
moderators.length--;
}
}
}
contract EtheremonEnum {
enum ResultCode {
SUCCESS,
ERROR_CLASS_NOT_FOUND,
ERROR_LOW_BALANCE,
ERROR_SEND_FAIL,
ERROR_NOT_TRAINER,
ERROR_NOT_ENOUGH_MONEY,
ERROR_INVALID_AMOUNT
}
enum ArrayType {
CLASS_TYPE,
STAT_STEP,
STAT_START,
STAT_BASE,
OBJ_SKILL
}
}
contract EtheremonDataBase is EtheremonEnum, BasicAccessControl, SafeMath {
uint64 public totalMonster;
uint32 public totalClass;
// write
function addElementToArrayType(ArrayType _type, uint64 _id, uint8 _value) onlyModerators public returns(uint);
function updateIndexOfArrayType(ArrayType _type, uint64 _id, uint _index, uint8 _value) onlyModerators public returns(uint);
function setMonsterClass(uint32 _classId, uint256 _price, uint256 _returnPrice, bool _catchable) onlyModerators public returns(uint32);
function addMonsterObj(uint32 _classId, address _trainer, string _name) onlyModerators public returns(uint64);
function setMonsterObj(uint64 _objId, string _name, uint32 _exp, uint32 _createIndex, uint32 _lastClaimIndex) onlyModerators public;
function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
function removeMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public;
function addMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public;
function clearMonsterReturnBalance(uint64 _monsterId) onlyModerators public returns(uint256 amount);
function collectAllReturnBalance(address _trainer) onlyModerators public returns(uint256 amount);
function transferMonster(address _from, address _to, uint64 _monsterId) onlyModerators public returns(ResultCode);
function addExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256);
function deductExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256);
function setExtraBalance(address _trainer, uint256 _amount) onlyModerators public;
// read
function getSizeArrayType(ArrayType _type, uint64 _id) constant public returns(uint);
function getElementInArrayType(ArrayType _type, uint64 _id, uint _index) constant public returns(uint8);
function getMonsterClass(uint32 _classId) constant public returns(uint32 classId, uint256 price, uint256 returnPrice, uint32 total, bool catchable);
function getMonsterObj(uint64 _objId) constant public returns(uint64 objId, uint32 classId, address trainer, uint32 exp, uint32 createIndex, uint32 lastClaimIndex, uint createTime);
function getMonsterName(uint64 _objId) constant public returns(string name);
function getExtraBalance(address _trainer) constant public returns(uint256);
function getMonsterDexSize(address _trainer) constant public returns(uint);
function getMonsterObjId(address _trainer, uint index) constant public returns(uint64);
function getExpectedBalance(address _trainer) constant public returns(uint256);
function getMonsterReturn(uint64 _objId) constant public returns(uint256 current, uint256 total);
}
contract EtheremonData is EtheremonDataBase {
struct MonsterClass {
uint32 classId;
uint8[] types;
uint8[] statSteps;
uint8[] statStarts;
uint256 price;
uint256 returnPrice;
uint32 total;
bool catchable;
}
struct MonsterObj {
uint64 monsterId;
uint32 classId;
address trainer;
string name;
uint32 exp;
uint8[] statBases;
uint8[] skills;
uint32 createIndex;
uint32 lastClaimIndex;
uint createTime;
}
mapping(uint32 => MonsterClass) public monsterClass;
mapping(uint64 => MonsterObj) public monsterWorld;
mapping(address => uint64[]) public trainerDex;
mapping(address => uint256) public trainerExtraBalance;
// write access
function withdrawEther(address _sendTo, uint _amount) onlyOwner public returns(ResultCode) {
if (_amount > this.balance) {
return ResultCode.ERROR_INVALID_AMOUNT;
}
_sendTo.transfer(_amount);
return ResultCode.SUCCESS;
}
function addElementToArrayType(ArrayType _type, uint64 _id, uint8 _value) onlyModerators public returns(uint) {
uint8[] storage array = monsterWorld[_id].statBases;
if (_type == ArrayType.CLASS_TYPE) {
array = monsterClass[uint32(_id)].types;
} else if (_type == ArrayType.STAT_STEP) {
array = monsterClass[uint32(_id)].statSteps;
} else if (_type == ArrayType.STAT_START) {
array = monsterClass[uint32(_id)].statStarts;
} else if (_type == ArrayType.OBJ_SKILL) {
array = monsterWorld[_id].skills;
}
array.push(_value);
return array.length;
}
function updateIndexOfArrayType(ArrayType _type, uint64 _id, uint _index, uint8 _value) onlyModerators public returns(uint) {
uint8[] storage array = monsterWorld[_id].statBases;
if (_type == ArrayType.CLASS_TYPE) {
array = monsterClass[uint32(_id)].types;
} else if (_type == ArrayType.STAT_STEP) {
array = monsterClass[uint32(_id)].statSteps;
} else if (_type == ArrayType.STAT_START) {
array = monsterClass[uint32(_id)].statStarts;
} else if (_type == ArrayType.OBJ_SKILL) {
array = monsterWorld[_id].skills;
}
if (_index < array.length) {
if (_value == 255) {
// consider as delete
for(uint i = _index; i < array.length - 1; i++) {
array[i] = array[i+1];
}
delete array[array.length-1];
array.length--;
} else {
array[_index] = _value;
}
}
}
function setMonsterClass(uint32 _classId, uint256 _price, uint256 _returnPrice, bool _catchable) onlyModerators public returns(uint32) {
MonsterClass storage class = monsterClass[_classId];
if (class.classId == 0) {
totalClass += 1;
}
class.classId = _classId;
class.price = _price;
class.returnPrice = _returnPrice;
class.catchable = _catchable;
return totalClass;
}
function addMonsterObj(uint32 _classId, address _trainer, string _name) onlyModerators public returns(uint64) {
MonsterClass storage class = monsterClass[_classId];
if (class.classId == 0)
return 0;
// construct new monster
totalMonster += 1;
class.total += 1;
MonsterObj storage obj = monsterWorld[totalMonster];
obj.monsterId = totalMonster;
obj.classId = _classId;
obj.trainer = _trainer;
obj.name = _name;
obj.exp = 1;
obj.createIndex = class.total;
obj.lastClaimIndex = class.total;
obj.createTime = now;
// add to monsterdex
addMonsterIdMapping(_trainer, obj.monsterId);
return obj.monsterId;
}
function setMonsterObj(uint64 _objId, string _name, uint32 _exp, uint32 _createIndex, uint32 _lastClaimIndex) onlyModerators public {
MonsterObj storage obj = monsterWorld[_objId];
if (obj.monsterId == _objId) {
obj.name = _name;
obj.exp = _exp;
obj.createIndex = _createIndex;
obj.lastClaimIndex = _lastClaimIndex;
}
}
function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public {
MonsterObj storage obj = monsterWorld[_objId];
if (obj.monsterId == _objId) {
obj.exp = uint32(safeAdd(obj.exp, amount));
}
}
function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public {
MonsterObj storage obj = monsterWorld[_objId];
if (obj.monsterId == _objId) {
obj.exp = uint32(safeSubtract(obj.exp, amount));
}
}
function removeMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public {
uint foundIndex = 0;
uint64[] storage objIdList = trainerDex[_trainer];
for (; foundIndex < objIdList.length; foundIndex++) {
if (objIdList[foundIndex] == _monsterId) {
break;
}
}
if (foundIndex < objIdList.length) {
objIdList[foundIndex] = objIdList[objIdList.length-1];
delete objIdList[objIdList.length-1];
objIdList.length--;
MonsterObj storage monster = monsterWorld[_monsterId];
monster.trainer = 0;
}
}
function addMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public {
if (_trainer != address(0) && _monsterId > 0) {
uint64[] storage objIdList = trainerDex[_trainer];
for (uint i = 0; i < objIdList.length; i++) {
if (objIdList[i] == _monsterId) {
return;
}
}
objIdList.push(_monsterId);
MonsterObj storage monster = monsterWorld[_monsterId];
monster.trainer = _trainer;
}
}
function clearMonsterReturnBalance(uint64 _monsterId) onlyModerators public returns(uint256) {
MonsterObj storage monster = monsterWorld[_monsterId];
MonsterClass storage class = monsterClass[monster.classId];
if (monster.monsterId == 0 || class.classId == 0)
return 0;
uint256 amount = 0;
uint32 gap = uint32(safeSubtract(class.total, monster.lastClaimIndex));
if (gap > 0) {
monster.lastClaimIndex = class.total;
amount = safeMult(gap, class.returnPrice);
trainerExtraBalance[monster.trainer] = safeAdd(trainerExtraBalance[monster.trainer], amount);
}
return amount;
}
function collectAllReturnBalance(address _trainer) onlyModerators public returns(uint256 amount) {
uint64[] storage objIdList = trainerDex[_trainer];
for (uint i = 0; i < objIdList.length; i++) {
clearMonsterReturnBalance(objIdList[i]);
}
return trainerExtraBalance[_trainer];
}
function transferMonster(address _from, address _to, uint64 _monsterId) onlyModerators public returns(ResultCode) {
MonsterObj storage monster = monsterWorld[_monsterId];
if (monster.trainer != _from) {
return ResultCode.ERROR_NOT_TRAINER;
}
clearMonsterReturnBalance(_monsterId);
removeMonsterIdMapping(_from, _monsterId);
addMonsterIdMapping(_to, _monsterId);
return ResultCode.SUCCESS;
}
function addExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256) {
trainerExtraBalance[_trainer] = safeAdd(trainerExtraBalance[_trainer], _amount);
return trainerExtraBalance[_trainer];
}
function deductExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256) {
trainerExtraBalance[_trainer] = safeSubtract(trainerExtraBalance[_trainer], _amount);
return trainerExtraBalance[_trainer];
}
function setExtraBalance(address _trainer, uint256 _amount) onlyModerators public {
trainerExtraBalance[_trainer] = _amount;
}
// public
function () payable public {
addExtraBalance(msg.sender, msg.value);
}
// read access
function getSizeArrayType(ArrayType _type, uint64 _id) constant public returns(uint) {
uint8[] storage array = monsterWorld[_id].statBases;
if (_type == ArrayType.CLASS_TYPE) {
array = monsterClass[uint32(_id)].types;
} else if (_type == ArrayType.STAT_STEP) {
array = monsterClass[uint32(_id)].statSteps;
} else if (_type == ArrayType.STAT_START) {
array = monsterClass[uint32(_id)].statStarts;
} else if (_type == ArrayType.OBJ_SKILL) {
array = monsterWorld[_id].skills;
}
return array.length;
}
function getElementInArrayType(ArrayType _type, uint64 _id, uint _index) constant public returns(uint8) {
uint8[] storage array = monsterWorld[_id].statBases;
if (_type == ArrayType.CLASS_TYPE) {
array = monsterClass[uint32(_id)].types;
} else if (_type == ArrayType.STAT_STEP) {
array = monsterClass[uint32(_id)].statSteps;
} else if (_type == ArrayType.STAT_START) {
array = monsterClass[uint32(_id)].statStarts;
} else if (_type == ArrayType.OBJ_SKILL) {
array = monsterWorld[_id].skills;
}
if (_index >= array.length)
return 0;
return array[_index];
}
function getMonsterClass(uint32 _classId) constant public returns(uint32 classId, uint256 price, uint256 returnPrice, uint32 total, bool catchable) {
MonsterClass storage class = monsterClass[_classId];
classId = class.classId;
price = class.price;
returnPrice = class.returnPrice;
total = class.total;
catchable = class.catchable;
}
function getMonsterObj(uint64 _objId) constant public returns(uint64 objId, uint32 classId, address trainer, uint32 exp, uint32 createIndex, uint32 lastClaimIndex, uint createTime) {
MonsterObj storage monster = monsterWorld[_objId];
objId = monster.monsterId;
classId = monster.classId;
trainer = monster.trainer;
exp = monster.exp;
createIndex = monster.createIndex;
lastClaimIndex = monster.lastClaimIndex;
createTime = monster.createTime;
}
function getMonsterName(uint64 _objId) constant public returns(string name) {
return monsterWorld[_objId].name;
}
function getExtraBalance(address _trainer) constant public returns(uint256) {
return trainerExtraBalance[_trainer];
}
function getMonsterDexSize(address _trainer) constant public returns(uint) {
return trainerDex[_trainer].length;
}
function getMonsterObjId(address _trainer, uint index) constant public returns(uint64) {
if (index >= trainerDex[_trainer].length)
return 0;
return trainerDex[_trainer][index];
}
function getExpectedBalance(address _trainer) constant public returns(uint256) {
uint64[] storage objIdList = trainerDex[_trainer];
uint256 monsterBalance = 0;
for (uint i = 0; i < objIdList.length; i++) {
MonsterObj memory monster = monsterWorld[objIdList[i]];
MonsterClass storage class = monsterClass[monster.classId];
uint32 gap = uint32(safeSubtract(class.total, monster.lastClaimIndex));
monsterBalance += safeMult(gap, class.returnPrice);
}
return monsterBalance;
}
function getMonsterReturn(uint64 _objId) constant public returns(uint256 current, uint256 total) {
MonsterObj memory monster = monsterWorld[_objId];
MonsterClass storage class = monsterClass[monster.classId];
uint32 totalGap = uint32(safeSubtract(class.total, monster.createIndex));
uint32 currentGap = uint32(safeSubtract(class.total, monster.lastClaimIndex));
return (safeMult(currentGap, class.returnPrice), safeMult(totalGap, class.returnPrice));
}
}File 3 of 3: EtheremonWorld
pragma solidity ^0.4.16;
// copyright contact@Etheremon.com
contract SafeMath {
/* function assert(bool assertion) internal { */
/* if (!assertion) { */
/* throw; */
/* } */
/* } // assert no longer needed once solidity is on 0.4.10 */
function safeAdd(uint256 x, uint256 y) pure internal returns(uint256) {
uint256 z = x + y;
assert((z >= x) && (z >= y));
return z;
}
function safeSubtract(uint256 x, uint256 y) pure internal returns(uint256) {
assert(x >= y);
uint256 z = x - y;
return z;
}
function safeMult(uint256 x, uint256 y) pure internal returns(uint256) {
uint256 z = x * y;
assert((x == 0)||(z/x == y));
return z;
}
}
contract BasicAccessControl {
address public owner;
// address[] public moderators;
uint16 public totalModerators = 0;
mapping (address => bool) public moderators;
bool public isMaintaining = true;
function BasicAccessControl() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
modifier onlyModerators() {
require(moderators[msg.sender] == true);
_;
}
modifier isActive {
require(!isMaintaining);
_;
}
function ChangeOwner(address _newOwner) onlyOwner public {
if (_newOwner != address(0)) {
owner = _newOwner;
}
}
function AddModerator(address _newModerator) onlyOwner public {
if (moderators[_newModerator] == false) {
moderators[_newModerator] = true;
totalModerators += 1;
}
}
function RemoveModerator(address _oldModerator) onlyOwner public {
if (moderators[_oldModerator] == true) {
moderators[_oldModerator] = false;
totalModerators -= 1;
}
}
function UpdateMaintaining(bool _isMaintaining) onlyOwner public {
isMaintaining = _isMaintaining;
}
}
contract EtheremonEnum {
enum ResultCode {
SUCCESS,
ERROR_CLASS_NOT_FOUND,
ERROR_LOW_BALANCE,
ERROR_SEND_FAIL,
ERROR_NOT_TRAINER,
ERROR_NOT_ENOUGH_MONEY,
ERROR_INVALID_AMOUNT
}
enum ArrayType {
CLASS_TYPE,
STAT_STEP,
STAT_START,
STAT_BASE,
OBJ_SKILL
}
enum PropertyType {
ANCESTOR,
XFACTOR
}
}
contract EtheremonDataBase is EtheremonEnum, BasicAccessControl, SafeMath {
uint64 public totalMonster;
uint32 public totalClass;
// write
function withdrawEther(address _sendTo, uint _amount) onlyOwner public returns(ResultCode);
function addElementToArrayType(ArrayType _type, uint64 _id, uint8 _value) onlyModerators public returns(uint);
function updateIndexOfArrayType(ArrayType _type, uint64 _id, uint _index, uint8 _value) onlyModerators public returns(uint);
function setMonsterClass(uint32 _classId, uint256 _price, uint256 _returnPrice, bool _catchable) onlyModerators public returns(uint32);
function addMonsterObj(uint32 _classId, address _trainer, string _name) onlyModerators public returns(uint64);
function setMonsterObj(uint64 _objId, string _name, uint32 _exp, uint32 _createIndex, uint32 _lastClaimIndex) onlyModerators public;
function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
function removeMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public;
function addMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public;
function clearMonsterReturnBalance(uint64 _monsterId) onlyModerators public returns(uint256 amount);
function collectAllReturnBalance(address _trainer) onlyModerators public returns(uint256 amount);
function transferMonster(address _from, address _to, uint64 _monsterId) onlyModerators public returns(ResultCode);
function addExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256);
function deductExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256);
function setExtraBalance(address _trainer, uint256 _amount) onlyModerators public;
// read
function getSizeArrayType(ArrayType _type, uint64 _id) constant public returns(uint);
function getElementInArrayType(ArrayType _type, uint64 _id, uint _index) constant public returns(uint8);
function getMonsterClass(uint32 _classId) constant public returns(uint32 classId, uint256 price, uint256 returnPrice, uint32 total, bool catchable);
function getMonsterObj(uint64 _objId) constant public returns(uint64 objId, uint32 classId, address trainer, uint32 exp, uint32 createIndex, uint32 lastClaimIndex, uint createTime);
function getMonsterName(uint64 _objId) constant public returns(string name);
function getExtraBalance(address _trainer) constant public returns(uint256);
function getMonsterDexSize(address _trainer) constant public returns(uint);
function getMonsterObjId(address _trainer, uint index) constant public returns(uint64);
function getExpectedBalance(address _trainer) constant public returns(uint256);
function getMonsterReturn(uint64 _objId) constant public returns(uint256 current, uint256 total);
}
contract EtheremonGateway is EtheremonEnum, BasicAccessControl {
// using for battle contract later
function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
// read
function isGason(uint64 _objId) constant external returns(bool);
function getObjBattleInfo(uint64 _objId) constant external returns(uint32 classId, uint32 exp, bool isGason,
uint ancestorLength, uint xfactorsLength);
function getClassPropertySize(uint32 _classId, PropertyType _type) constant external returns(uint);
function getClassPropertyValue(uint32 _classId, PropertyType _type, uint index) constant external returns(uint32);
}
contract EtheremonWorld is EtheremonGateway, SafeMath {
// old processor
address constant public ETHEREMON_PROCESSOR = address(0x8a60806F05876f4d6dB00c877B0558DbCAD30682);
uint8 constant public STAT_COUNT = 6;
uint8 constant public STAT_MAX = 32;
uint8 constant public GEN0_NO = 24;
struct MonsterClassAcc {
uint32 classId;
uint256 price;
uint256 returnPrice;
uint32 total;
bool catchable;
}
struct MonsterObjAcc {
uint64 monsterId;
uint32 classId;
address trainer;
string name;
uint32 exp;
uint32 createIndex;
uint32 lastClaimIndex;
uint createTime;
}
// Gen0 has return price & no longer can be caught when this contract is deployed
struct Gen0Config {
uint32 classId;
uint256 originalPrice;
uint256 returnPrice;
uint32 total; // total caught (not count those from eggs)
}
struct GenXProperty {
uint32 classId;
bool isGason;
uint32[] ancestors;
uint32[] xfactors;
}
mapping(uint32 => Gen0Config) public gen0Config;
mapping(uint32 => GenXProperty) public genxProperty;
uint256 public totalCashout = 0; // for admin
uint256 public totalEarn = 0; // exclude gen 0
uint16 public priceIncreasingRatio = 1000;
uint public maxDexSize = 500;
address private lastHunter = address(0x0);
// data contract
address public dataContract;
// event
event EventCatchMonster(address indexed trainer, uint64 objId);
event EventCashOut(address indexed trainer, ResultCode result, uint256 amount);
event EventWithdrawEther(address indexed sendTo, ResultCode result, uint256 amount);
function EtheremonWorld(address _dataContract) public {
dataContract = _dataContract;
}
// admin & moderators
function setMaxDexSize(uint _value) onlyModerators external {
maxDexSize = _value;
}
function setOriginalPriceGen0() onlyModerators external {
gen0Config[1] = Gen0Config(1, 0.3 ether, 0.003 ether, 374);
gen0Config[2] = Gen0Config(2, 0.3 ether, 0.003 ether, 408);
gen0Config[3] = Gen0Config(3, 0.3 ether, 0.003 ether, 373);
gen0Config[4] = Gen0Config(4, 0.2 ether, 0.002 ether, 437);
gen0Config[5] = Gen0Config(5, 0.1 ether, 0.001 ether, 497);
gen0Config[6] = Gen0Config(6, 0.3 ether, 0.003 ether, 380);
gen0Config[7] = Gen0Config(7, 0.2 ether, 0.002 ether, 345);
gen0Config[8] = Gen0Config(8, 0.1 ether, 0.001 ether, 518);
gen0Config[9] = Gen0Config(9, 0.1 ether, 0.001 ether, 447);
gen0Config[10] = Gen0Config(10, 0.2 ether, 0.002 ether, 380);
gen0Config[11] = Gen0Config(11, 0.2 ether, 0.002 ether, 354);
gen0Config[12] = Gen0Config(12, 0.2 ether, 0.002 ether, 346);
gen0Config[13] = Gen0Config(13, 0.2 ether, 0.002 ether, 351);
gen0Config[14] = Gen0Config(14, 0.2 ether, 0.002 ether, 338);
gen0Config[15] = Gen0Config(15, 0.2 ether, 0.002 ether, 341);
gen0Config[16] = Gen0Config(16, 0.35 ether, 0.0035 ether, 384);
gen0Config[17] = Gen0Config(17, 0.1 ether, 0.001 ether, 305);
gen0Config[18] = Gen0Config(18, 0.1 ether, 0.001 ether, 427);
gen0Config[19] = Gen0Config(19, 0.1 ether, 0.001 ether, 304);
gen0Config[20] = Gen0Config(20, 0.4 ether, 0.005 ether, 82);
gen0Config[21] = Gen0Config(21, 1, 1, 123);
gen0Config[22] = Gen0Config(22, 0.2 ether, 0.001 ether, 468);
gen0Config[23] = Gen0Config(23, 0.5 ether, 0.0025 ether, 302);
gen0Config[24] = Gen0Config(24, 1 ether, 0.005 ether, 195);
}
function getEarningAmount() constant public returns(uint256) {
// calculate value for gen0
uint256 totalValidAmount = 0;
for (uint32 classId=1; classId <= GEN0_NO; classId++) {
// make sure there is a class
Gen0Config storage gen0 = gen0Config[classId];
if (gen0.total >0 && gen0.classId == classId && gen0.originalPrice > 0 && gen0.returnPrice > 0) {
uint256 rate = gen0.originalPrice/gen0.returnPrice;
if (rate < gen0.total) {
totalValidAmount += (gen0.originalPrice + gen0.returnPrice) * rate / 2;
totalValidAmount += (gen0.total - rate) * gen0.returnPrice;
} else {
totalValidAmount += (gen0.originalPrice + gen0.returnPrice * (rate - gen0.total + 1)) / 2 * gen0.total;
}
}
}
// add in earn from genx
totalValidAmount = safeAdd(totalValidAmount, totalEarn);
// deduct amount of cashing out
totalValidAmount = safeSubtract(totalValidAmount, totalCashout);
return totalValidAmount;
}
function withdrawEther(address _sendTo, uint _amount) onlyModerators external returns(ResultCode) {
if (_amount > this.balance) {
EventWithdrawEther(_sendTo, ResultCode.ERROR_INVALID_AMOUNT, 0);
return ResultCode.ERROR_INVALID_AMOUNT;
}
uint256 totalValidAmount = getEarningAmount();
if (_amount > totalValidAmount) {
EventWithdrawEther(_sendTo, ResultCode.ERROR_INVALID_AMOUNT, 0);
return ResultCode.ERROR_INVALID_AMOUNT;
}
_sendTo.transfer(_amount);
totalCashout += _amount;
EventWithdrawEther(_sendTo, ResultCode.SUCCESS, _amount);
return ResultCode.SUCCESS;
}
// convenient tool to add monster
function addMonsterClassBasic(uint32 _classId, uint8 _type, uint256 _price, uint256 _returnPrice,
uint8 _ss1, uint8 _ss2, uint8 _ss3, uint8 _ss4, uint8 _ss5, uint8 _ss6) onlyModerators external {
EtheremonDataBase data = EtheremonDataBase(dataContract);
MonsterClassAcc memory class;
(class.classId, class.price, class.returnPrice, class.total, class.catchable) = data.getMonsterClass(_classId);
// can add only one time
if (_classId == 0 || class.classId == _classId)
revert();
data.setMonsterClass(_classId, _price, _returnPrice, true);
data.addElementToArrayType(ArrayType.CLASS_TYPE, uint64(_classId), _type);
// add stat step
data.addElementToArrayType(ArrayType.STAT_START, uint64(_classId), _ss1);
data.addElementToArrayType(ArrayType.STAT_START, uint64(_classId), _ss2);
data.addElementToArrayType(ArrayType.STAT_START, uint64(_classId), _ss3);
data.addElementToArrayType(ArrayType.STAT_START, uint64(_classId), _ss4);
data.addElementToArrayType(ArrayType.STAT_START, uint64(_classId), _ss5);
data.addElementToArrayType(ArrayType.STAT_START, uint64(_classId), _ss6);
}
function addMonsterClassExtend(uint32 _classId, uint8 _type2, uint8 _type3,
uint8 _st1, uint8 _st2, uint8 _st3, uint8 _st4, uint8 _st5, uint8 _st6 ) onlyModerators external {
EtheremonDataBase data = EtheremonDataBase(dataContract);
if (_classId == 0 || data.getSizeArrayType(ArrayType.STAT_STEP, uint64(_classId)) > 0)
revert();
if (_type2 > 0) {
data.addElementToArrayType(ArrayType.CLASS_TYPE, uint64(_classId), _type2);
}
if (_type3 > 0) {
data.addElementToArrayType(ArrayType.CLASS_TYPE, uint64(_classId), _type3);
}
// add stat base
data.addElementToArrayType(ArrayType.STAT_STEP, uint64(_classId), _st1);
data.addElementToArrayType(ArrayType.STAT_STEP, uint64(_classId), _st2);
data.addElementToArrayType(ArrayType.STAT_STEP, uint64(_classId), _st3);
data.addElementToArrayType(ArrayType.STAT_STEP, uint64(_classId), _st4);
data.addElementToArrayType(ArrayType.STAT_STEP, uint64(_classId), _st5);
data.addElementToArrayType(ArrayType.STAT_STEP, uint64(_classId), _st6);
}
function setCatchable(uint32 _classId, bool catchable) onlyModerators external {
// can not edit gen 0 - can not catch forever
Gen0Config storage gen0 = gen0Config[_classId];
if (gen0.classId == _classId)
revert();
EtheremonDataBase data = EtheremonDataBase(dataContract);
MonsterClassAcc memory class;
(class.classId, class.price, class.returnPrice, class.total, class.catchable) = data.getMonsterClass(_classId);
data.setMonsterClass(class.classId, class.price, class.returnPrice, catchable);
}
function setPriceIncreasingRatio(uint16 _ratio) onlyModerators external {
priceIncreasingRatio = _ratio;
}
function setGason(uint32 _classId, bool _isGason) onlyModerators external {
GenXProperty storage pro = genxProperty[_classId];
pro.isGason = _isGason;
}
function addClassProperty(uint32 _classId, PropertyType _type, uint32 value) onlyModerators external {
GenXProperty storage pro = genxProperty[_classId];
pro.classId = _classId;
if (_type == PropertyType.ANCESTOR) {
pro.ancestors.push(value);
} else {
pro.xfactors.push(value);
}
}
// gate way
function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public {
EtheremonDataBase data = EtheremonDataBase(dataContract);
data.increaseMonsterExp(_objId, amount);
}
function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public {
EtheremonDataBase data = EtheremonDataBase(dataContract);
data.decreaseMonsterExp(_objId, amount);
}
// helper
function getRandom(uint8 maxRan, uint8 index, address priAddress) constant public returns(uint8) {
uint256 genNum = uint256(block.blockhash(block.number-1)) + uint256(priAddress);
for (uint8 i = 0; i < index && i < 6; i ++) {
genNum /= 256;
}
return uint8(genNum % maxRan);
}
function () payable public {
if (msg.sender != ETHEREMON_PROCESSOR)
revert();
}
// public
function isGason(uint64 _objId) constant external returns(bool) {
EtheremonDataBase data = EtheremonDataBase(dataContract);
MonsterObjAcc memory obj;
(obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
GenXProperty storage pro = genxProperty[obj.classId];
return pro.isGason;
}
function getObjIndex(uint64 _objId) constant public returns(uint32 classId, uint32 createIndex, uint32 lastClaimIndex) {
EtheremonDataBase data = EtheremonDataBase(dataContract);
MonsterObjAcc memory obj;
(obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
return (obj.classId, obj.createIndex, obj.lastClaimIndex);
}
function getObjBattleInfo(uint64 _objId) constant external returns(uint32 classId, uint32 exp, bool isGason,
uint ancestorLength, uint xfactorsLength) {
EtheremonDataBase data = EtheremonDataBase(dataContract);
MonsterObjAcc memory obj;
(obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
GenXProperty storage pro = genxProperty[obj.classId];
return (obj.classId, obj.exp, pro.isGason, pro.ancestors.length, pro.xfactors.length);
}
function getClassPropertySize(uint32 _classId, PropertyType _type) constant external returns(uint) {
if (_type == PropertyType.ANCESTOR)
return genxProperty[_classId].ancestors.length;
else
return genxProperty[_classId].xfactors.length;
}
function getClassPropertyValue(uint32 _classId, PropertyType _type, uint index) constant external returns(uint32) {
if (_type == PropertyType.ANCESTOR)
return genxProperty[_classId].ancestors[index];
else
return genxProperty[_classId].xfactors[index];
}
// only gen 0
function getGen0COnfig(uint32 _classId) constant public returns(uint32, uint256, uint32) {
Gen0Config storage gen0 = gen0Config[_classId];
return (gen0.classId, gen0.originalPrice, gen0.total);
}
// only gen 0
function getReturnFromMonster(uint64 _objId) constant public returns(uint256 current, uint256 total) {
/*
1. Gen 0 can not be caught anymore.
2. Egg will not give return.
*/
uint32 classId = 0;
uint32 createIndex = 0;
uint32 lastClaimIndex = 0;
(classId, createIndex, lastClaimIndex) = getObjIndex(_objId);
Gen0Config storage gen0 = gen0Config[classId];
if (gen0.classId != classId) {
return (0, 0);
}
uint32 currentGap = 0;
uint32 totalGap = 0;
if (lastClaimIndex < gen0.total)
currentGap = gen0.total - lastClaimIndex;
if (createIndex < gen0.total)
totalGap = gen0.total - createIndex;
return (safeMult(currentGap, gen0.returnPrice), safeMult(totalGap, gen0.returnPrice));
}
// write access
function moveDataContractBalanceToWorld() external {
EtheremonDataBase data = EtheremonDataBase(dataContract);
data.withdrawEther(address(this), data.balance);
}
function renameMonster(uint64 _objId, string name) isActive external {
EtheremonDataBase data = EtheremonDataBase(dataContract);
MonsterObjAcc memory obj;
(obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
if (obj.monsterId != _objId || obj.trainer != msg.sender) {
revert();
}
data.setMonsterObj(_objId, name, obj.exp, obj.createIndex, obj.lastClaimIndex);
}
function catchMonster(uint32 _classId, string _name) isActive external payable {
EtheremonDataBase data = EtheremonDataBase(dataContract);
MonsterClassAcc memory class;
(class.classId, class.price, class.returnPrice, class.total, class.catchable) = data.getMonsterClass(_classId);
if (class.classId == 0 || class.catchable == false) {
revert();
}
// can not keep too much etheremon
if (data.getMonsterDexSize(msg.sender) > maxDexSize)
revert();
uint256 totalBalance = safeAdd(msg.value, data.getExtraBalance(msg.sender));
uint256 payPrice = class.price;
// increase price for each etheremon created
if (class.total > 0)
payPrice += class.price*(class.total-1)/priceIncreasingRatio;
if (payPrice > totalBalance) {
revert();
}
totalEarn += payPrice;
// deduct the balance
data.setExtraBalance(msg.sender, safeSubtract(totalBalance, payPrice));
// add monster
uint64 objId = data.addMonsterObj(_classId, msg.sender, _name);
// generate base stat for the previous one
for (uint i=0; i < STAT_COUNT; i+= 1) {
uint8 value = getRandom(STAT_MAX, uint8(i), lastHunter) + data.getElementInArrayType(ArrayType.STAT_START, uint64(_classId), i);
data.addElementToArrayType(ArrayType.STAT_BASE, objId, value);
}
lastHunter = msg.sender;
EventCatchMonster(msg.sender, objId);
}
function cashOut(uint256 _amount) public returns(ResultCode) {
EtheremonDataBase data = EtheremonDataBase(dataContract);
uint256 totalAmount = data.getExtraBalance(msg.sender);
uint64 objId = 0;
// collect gen 0 return price
uint dexSize = data.getMonsterDexSize(msg.sender);
for (uint i = 0; i < dexSize; i++) {
objId = data.getMonsterObjId(msg.sender, i);
if (objId > 0) {
MonsterObjAcc memory obj;
(obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(objId);
Gen0Config storage gen0 = gen0Config[obj.classId];
if (gen0.classId == obj.classId) {
if (obj.lastClaimIndex < gen0.total) {
uint32 gap = uint32(safeSubtract(gen0.total, obj.lastClaimIndex));
if (gap > 0) {
totalAmount += safeMult(gap, gen0.returnPrice);
// reset total (except name is cleared :( )
data.setMonsterObj(obj.monsterId, " name me ", obj.exp, obj.createIndex, gen0.total);
}
}
}
}
}
// default to cash out all
if (_amount == 0) {
_amount = totalAmount;
}
if (_amount > totalAmount) {
revert();
}
// check contract has enough money
if (this.balance + data.balance < _amount){
revert();
} else if (this.balance < _amount) {
data.withdrawEther(address(this), data.balance);
}
if (_amount > 0) {
data.setExtraBalance(msg.sender, totalAmount - _amount);
if (!msg.sender.send(_amount)) {
data.setExtraBalance(msg.sender, totalAmount);
EventCashOut(msg.sender, ResultCode.ERROR_SEND_FAIL, 0);
return ResultCode.ERROR_SEND_FAIL;
}
}
EventCashOut(msg.sender, ResultCode.SUCCESS, _amount);
return ResultCode.SUCCESS;
}
// read access
function getTrainerEarn(address _trainer) constant public returns(uint256) {
EtheremonDataBase data = EtheremonDataBase(dataContract);
uint256 returnFromMonster = 0;
// collect gen 0 return price
uint256 gen0current = 0;
uint256 gen0total = 0;
uint64 objId = 0;
uint dexSize = data.getMonsterDexSize(_trainer);
for (uint i = 0; i < dexSize; i++) {
objId = data.getMonsterObjId(_trainer, i);
if (objId > 0) {
(gen0current, gen0total) = getReturnFromMonster(objId);
returnFromMonster += gen0current;
}
}
return returnFromMonster;
}
function getTrainerBalance(address _trainer) constant external returns(uint256) {
EtheremonDataBase data = EtheremonDataBase(dataContract);
uint256 userExtraBalance = data.getExtraBalance(_trainer);
uint256 returnFromMonster = getTrainerEarn(_trainer);
return (userExtraBalance + returnFromMonster);
}
function getMonsterClassBasic(uint32 _classId) constant external returns(uint256, uint256, uint256, bool) {
EtheremonDataBase data = EtheremonDataBase(dataContract);
MonsterClassAcc memory class;
(class.classId, class.price, class.returnPrice, class.total, class.catchable) = data.getMonsterClass(_classId);
return (class.price, class.returnPrice, class.total, class.catchable);
}
}