pragma solidity ^0.4.19;
// File: contracts/ERC721Draft.sol
/// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens
contract ERC721 {
function implementsERC721() public pure returns (bool);
function totalSupply() public view returns (uint256 total);
function balanceOf(address _owner) public view returns (uint256 balance);
function ownerOf(uint256 _tokenId) public view returns (address owner);
function approve(address _to, uint256 _tokenId) public;
function transferFrom(address _from, address _to, uint256 _tokenId) public;
function transfer(address _to, uint256 _tokenId) public;
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
// Optional
// function name() public view returns (string name);
// function symbol() public view returns (string symbol);
// function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 tokenId);
// function tokenMetadata(uint256 _tokenId) public view returns (string infoUrl);
}
// File: contracts/FighterCoreInterface.sol
contract FighterCoreInterface is ERC721 {
function getFighter(uint256 _id)
public
view
returns (
uint256 prizeCooldownEndTime,
uint256 battleCooldownEndTime,
uint256 prizeCooldownIndex,
uint256 battlesFought,
uint256 battlesWon,
uint256 generation,
uint256 genes,
uint256 dexterity,
uint256 strength,
uint256 vitality,
uint256 luck,
uint256 experience
);
function createPrizeFighter(
uint16 _generation,
uint256 _genes,
uint8 _dexterity,
uint8 _strength,
uint8 _vitality,
uint8 _luck,
address _owner
) public;
function updateFighter(
uint256 _fighterId,
uint8 _dexterity,
uint8 _strength,
uint8 _vitality,
uint8 _luck,
uint32 _experience,
uint64 _prizeCooldownEndTime,
uint16 _prizeCooldownIndex,
uint64 _battleCooldownEndTime,
uint16 _battlesFought,
uint16 _battlesWon
) public;
function updateFighterBattleStats(
uint256 _fighterId,
uint64 _prizeCooldownEndTime,
uint16 _prizeCooldownIndex,
uint64 _battleCooldownEndTime,
uint16 _battlesFought,
uint16 _battlesWon
) public;
function updateDexterity(uint256 _fighterId, uint8 _dexterity) public;
function updateStrength(uint256 _fighterId, uint8 _strength) public;
function updateVitality(uint256 _fighterId, uint8 _vitality) public;
function updateLuck(uint256 _fighterId, uint8 _luck) public;
function updateExperience(uint256 _fighterId, uint32 _experience) public;
}
// File: contracts/Battle/BattleDeciderInterface.sol
contract BattleDeciderInterface {
function isBattleDecider() public pure returns (bool);
function determineWinner(uint256[7][] teamAttacker, uint256[7][] teamDefender) public returns (
bool attackerWon,
uint256 xpForAttacker,
uint256 xpForDefender
);
}
// File: contracts/Ownable.sol
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
// File: contracts/Pausable.sol
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
// File: contracts/Battle/GeneScienceInterface.sol
/// @title defined the interface that will be referenced in main Fighter contract
contract GeneScienceInterface {
/// @dev simply a boolean to indicate this is the contract we expect to be
function isGeneScience() public pure returns (bool);
/// @dev given genes of fighter 1 & 2, return a genetic combination - may have a random factor
/// @param genes1 genes of fighter1
/// @param genes2 genes of fighter1
/// @return the genes that are supposed to be passed down the new fighter
function mixGenes(uint256 genes1, uint256 genes2) public returns (uint256);
}
// File: contracts/Battle/BattleBase.sol
contract BattleBase is Ownable, Pausable {
event TeamCreated(uint256 indexed teamId, uint256[] fighterIds);
event TeamDeleted(uint256 indexed teamId, uint256[] fighterIds);
event BattleResult(address indexed winnerAddress, address indexed loserAddress, uint256[] attackerFighterIds, uint256[] defenderFighterIds, bool attackerWon, uint16 prizeFighterGeneration, uint256 prizeFighterGenes, uint32 attackerXpGained, uint32 defenderXpGained);
struct Team {
address owner;
uint256[] fighterIds;
}
struct RaceBaseStats {
uint8 strength;
uint8 dexterity;
uint8 vitality;
}
Team[] public teams;
// index => base stats (where index represents the race)
RaceBaseStats[] public raceBaseStats;
uint256 internal randomCounter = 0;
FighterCoreInterface public fighterCore;
GeneScienceInterface public geneScience;
BattleDeciderInterface public battleDecider;
mapping (uint256 => uint256) public fighterIndexToTeam;
mapping (uint256 => bool) public teamIndexToExist;
// an array of deleted teamIds owned by each address so that we can reuse these again
// mapping (address => uint256[]) public addressToDeletedTeams;
// an array of deleted teams we can reuse later
uint256[] public deletedTeamIds;
uint256 public maxPerTeam = 5;
uint8[] public genBaseStats = [
16, // gen 0
12, // gen 1
10, // gen 2
8, // gen 3
7, // gen 4
6, // gen 5
5, // gen 6
4, // gen 7
3, // gen 8
2, // gen 9
1 // gen 10+
];
// modifier ownsFighters(uint256[] _fighterIds) {
// uint len = _fighterIds.length;
// for (uint i = 0; i < len; i++) {
// require(fighterCore.ownerOf(_fighterIds[i]) == msg.sender);
// }
// _;
// }
modifier onlyTeamOwner(uint256 _teamId) {
require(teams[_teamId].owner == msg.sender);
_;
}
modifier onlyExistingTeam(uint256 _teamId) {
require(teamIndexToExist[_teamId] == true);
_;
}
function teamExists(uint256 _teamId) public view returns (bool) {
return teamIndexToExist[_teamId] == true;
}
/// @dev random number from 0 to (_modulus - 1)
function randMod(uint256 _randCounter, uint _modulus) internal view returns (uint256) {
return uint(keccak256(now, msg.sender, _randCounter)) % _modulus;
}
function getDeletedTeams() public view returns (uint256[]) {
// return addressToDeletedTeams[_address];
return deletedTeamIds;
}
function getRaceBaseStats(uint256 _id) public view returns (
uint256 strength,
uint256 dexterity,
uint256 vitality
) {
RaceBaseStats storage race = raceBaseStats[_id];
strength = race.strength;
dexterity = race.dexterity;
vitality = race.vitality;
}
}
// File: contracts/Battle/BattleAdmin.sol
contract BattleAdmin is BattleBase {
event ContractUpgrade(address newContract);
address public newContractAddress;
// An approximation of currently how many seconds are in between blocks.
uint256 public secondsPerBlock = 15;
uint32[7] public prizeCooldowns = [
uint32(1 minutes),
uint32(30 minutes),
uint32(2 hours),
uint32(6 hours),
uint32(12 hours),
uint32(1 days),
uint32(3 days)
];
function setFighterCoreAddress(address _address) public onlyOwner {
_setFighterCoreAddress(_address);
}
function _setFighterCoreAddress(address _address) internal {
FighterCoreInterface candidateContract = FighterCoreInterface(_address);
require(candidateContract.implementsERC721());
fighterCore = candidateContract;
}
function setGeneScienceAddress(address _address) public onlyOwner {
_setGeneScienceAddress(_address);
}
function _setGeneScienceAddress(address _address) internal {
GeneScienceInterface candidateContract = GeneScienceInterface(_address);
require(candidateContract.isGeneScience());
geneScience = candidateContract;
}
function setBattleDeciderAddress(address _address) public onlyOwner {
_setBattleDeciderAddress(_address);
}
function _setBattleDeciderAddress(address _address) internal {
BattleDeciderInterface deciderCandidateContract = BattleDeciderInterface(_address);
require(deciderCandidateContract.isBattleDecider());
battleDecider = deciderCandidateContract;
}
function addRace(uint8 _strength, uint8 _dexterity, uint8 _vitality) public onlyOwner {
raceBaseStats.push(RaceBaseStats({
strength: _strength,
dexterity: _dexterity,
vitality: _vitality
}));
}
// in case we ever add a bad race type
function removeLastRace() public onlyOwner {
// don't allow the first 4 races to be removed
require(raceBaseStats.length > 4);
delete raceBaseStats[raceBaseStats.length - 1];
}
/// @dev Used to mark the smart contract as upgraded, in case there is a serious
/// breaking bug. This method does nothing but keep track of the new contract and
/// emit a message indicating that the new address is set. It's up to clients of this
/// contract to update to the new contract address in that case.
/// @param _v2Address new address
function setNewAddress(address _v2Address) public onlyOwner whenPaused {
newContractAddress = _v2Address;
ContractUpgrade(_v2Address);
}
// Owner can fix how many seconds per blocks are currently observed.
function setSecondsPerBlock(uint256 _secs) external onlyOwner {
require(_secs < prizeCooldowns[0]);
secondsPerBlock = _secs;
}
}
// File: contracts/Battle/BattlePrize.sol
contract BattlePrize is BattleAdmin {
// array index is level, value is experience to reach that level
uint32[50] public stats = [
0,
100,
300,
600,
1000,
1500,
2100,
2800,
3600,
4500,
5500,
6600,
7800,
9100,
10500,
12000,
13600,
15300,
17100,
19000,
21000,
23100,
25300,
27600,
30000,
32500,
35100,
37800,
40600,
43500,
46500,
49600,
52800,
56100,
59500,
63000,
66600,
70300,
74100,
78000,
82000,
86100,
90300,
94600,
99000,
103500,
108100,
112800,
117600,
122500
];
uint8[11] public extraStatsForGen = [
16, // 0 - here for ease of use even though we never create gen0s
12, // 1
10, // 2
8, // 3
7, // 4
6, // 5
5, // 6
4, // 7
3, // 8
2, // 9
1 // 10+
];
// the number of battles before a delay to gain new exp kicks in
uint8 public battlesTillBattleCooldown = 5;
// the number of battles before a delay to gain new exp kicks in
uint32 public experienceDelay = uint32(6 hours);
// Luck is determined as follows:
// Rank 0 (5 stars) - random between 4~5
// Rank 1-2 (4 stars) - random between 2~4
// Rank 3-8 (3 stars) - random between 2~3
// Rank 9-15 (2 stars) - random between 1~3
// Rank 16+ (1 star) - random between 1~2
function genToLuck(uint256 _gen, uint256 _rand) public pure returns (uint8) {
if (_gen >= 1 || _gen <= 2) {
return 2 + uint8(_rand) % 3; // 2 to 4
} else if (_gen >= 3 || _gen <= 8) {
return 2 + uint8(_rand) % 2; // 2 to 3
} else if (_gen >= 9 || _gen <= 15) {
return 1 + uint8(_rand) % 3; // 1 to 3
} else { // 16+
return 1 + uint8(_rand) % 2; // 1 to 2
}
}
function raceToBaseStats(uint _race) public view returns (
uint8 strength,
uint8 dexterity,
uint8 vitality
) {
// in case we ever have an unknown race due to new races added
if (_race >= raceBaseStats.length) {
_race = 0;
}
RaceBaseStats memory raceStats = raceBaseStats[_race];
strength = raceStats.strength;
dexterity = raceStats.dexterity;
vitality = raceStats.vitality;
}
function genToExtraStats(uint256 _gen, uint256 _rand) public view returns (
uint8 extraStrength,
uint8 extraDexterity,
uint8 extraVitality
) {
// in case we ever have an unknown race due to new races added
if (_gen >= 10) {
_gen = 10;
}
uint8 extraStats = extraStatsForGen[_gen];
uint256 rand1 = _rand & 0xff;
uint256 rand2 = _rand >> 16 & 0xff;
uint256 rand3 = _rand >> 16 >> 16 & 0xff;
uint256 sum = rand1 + rand2 + rand3;
extraStrength = uint8((extraStats * rand1) / sum);
extraDexterity = uint8((extraStats * rand2) / sum);
extraVitality = uint8((extraStats * rand3) / sum);
uint8 remainder = extraStats - (extraStrength + extraDexterity + extraVitality);
if (rand1 > rand2 && rand1 > rand3) {
extraStrength += remainder;
} else if (rand2 > rand3) {
extraDexterity += remainder;
} else {
extraVitality += remainder;
}
}
function _getStrengthDexterityVitality(uint256 _race, uint256 _generation, uint256 _rand) public view returns (
uint256 strength,
uint256 dexterity,
uint256 vitality
) {
uint8 baseStrength;
uint8 baseDexterity;
uint8 baseVitality;
uint8 extraStrength;
uint8 extraDexterity;
uint8 extraVitality;
(baseStrength, baseDexterity, baseVitality) = raceToBaseStats(_race);
(extraStrength, extraDexterity, extraVitality) = genToExtraStats(_generation, _rand);
strength = baseStrength + extraStrength;
dexterity = baseDexterity + extraDexterity;
vitality = baseVitality + extraVitality;
}
// we return an array here, because we had an issue of too many local variables when returning a tuple
// function _generateFighterStats(uint256 _attackerLeaderId, uint256 _defenderLeaderId) internal returns (uint256[6]) {
function _generateFighterStats(uint256 generation1, uint256 genes1, uint256 generation2, uint256 genes2) internal returns (uint256[6]) {
// uint256 generation1;
// uint256 genes1;
// uint256 generation2;
// uint256 genes2;
uint256 generation256 = ((generation1 + generation2) / 2) + 1;
// making sure a gen 65536 doesn't turn out as a gen 0 :)
if (generation256 > 65535)
generation256 = 65535;
uint16 generation = uint16(generation256);
uint256 genes = geneScience.mixGenes(genes1, genes2);
uint256 strength;
uint256 dexterity;
uint256 vitality;
uint256 rand = uint(keccak256(now, msg.sender, randomCounter++));
(strength, dexterity, vitality) = _getStrengthDexterityVitality(_getRaceFromGenes(genes), generation, rand);
uint256 luck = genToLuck(genes, rand);
return [
generation,
genes,
strength,
dexterity,
vitality,
luck
];
}
// takes in genes and returns raceId
// race is first loci after version.
// [][]...[][race][version]
// each loci = 2B, race is also 2B. father's gene is determining the fighter's race
function _getRaceFromGenes(uint256 _genes) internal pure returns (uint256) {
return (_genes >> (16)) & 0xff;
}
function experienceToLevel(uint256 _experience) public view returns (uint256) {
for (uint256 i = 0; i < stats.length; i++) {
if (stats[i] > _experience) {
// current level is i
return i;
}
}
return 50;
}
// returns a number between 0 and 4 based on which stat to increase
// 0 - no stat increase
// 1 - dexterity
// 2 - strength
// 3 - vitality
// 4 - luck
function _calculateNewStat(uint32 _currentExperience, uint32 _newExperience) internal returns (uint256) {
// find current level
for (uint256 i = 0; i < stats.length; i++) {
if (stats[i] > _currentExperience) {
// current level is i
if (stats[i] <= _newExperience) {
// level up a random stat
return 1 + randMod(randomCounter++, 4);
} else {
return 0;
}
}
}
// at max level
return 0;
}
// function _getFighterGenAndGenes(uint256 _fighterId) internal view returns (
// uint256 generation,
// uint256 genes
// ) {
// (,,,,, generation, genes,,,,,) = fighterCore.getFighter(_fighterId);
// }
function _getFighterStatsData(uint256 _fighterId) internal view returns (uint256[6]) {
uint256 dexterity;
uint256 strength;
uint256 vitality;
uint256 luck;
uint256 experience;
uint256 battleCooldownEndTime;
(
,
battleCooldownEndTime,
,
,
,
,
,
dexterity,
strength,
vitality,
luck,
experience
) = fighterCore.getFighter(_fighterId);
return [
dexterity,
strength,
vitality,
luck,
experience,
battleCooldownEndTime
];
}
function _getFighterBattleData(uint256 _fighterId) internal view returns (uint256[7]) {
uint256 prizeCooldownEndTime;
uint256 prizeCooldownIndex;
uint256 battleCooldownEndTime;
uint256 battlesFought;
uint256 battlesWon;
uint256 generation;
uint256 genes;
(
prizeCooldownEndTime,
battleCooldownEndTime,
prizeCooldownIndex,
battlesFought,
battlesWon,
generation,
genes,
,
,
,
,
) = fighterCore.getFighter(_fighterId);
return [
prizeCooldownEndTime,
prizeCooldownIndex,
battleCooldownEndTime,
battlesFought,
battlesWon,
generation,
genes
];
}
function _increaseFighterStats(
uint256 _fighterId,
uint32 _experienceGained,
uint[6] memory data
) internal {
// dont update if on cooldown
if (data[5] >= block.number) {
return;
}
uint32 experience = uint32(data[4]);
uint32 newExperience = experience + _experienceGained;
uint256 _statIncrease = _calculateNewStat(experience, newExperience);
fighterCore.updateExperience(_fighterId, newExperience);
if (_statIncrease == 1) {
fighterCore.updateDexterity(_fighterId, uint8(++data[0]));
} else if (_statIncrease == 2) {
fighterCore.updateStrength(_fighterId, uint8(++data[1]));
} else if (_statIncrease == 3) {
fighterCore.updateVitality(_fighterId, uint8(++data[2]));
} else if (_statIncrease == 4) {
fighterCore.updateLuck(_fighterId, uint8(++data[3]));
}
}
function _increaseTeamFighterStats(uint256[] memory _fighterIds, uint32 _experienceGained) private {
for (uint i = 0; i < _fighterIds.length; i++) {
_increaseFighterStats(_fighterIds[i], _experienceGained, _getFighterStatsData(_fighterIds[i]));
}
}
function _updateFighterBattleStats(
uint256 _fighterId,
bool _winner,
bool _leader,
uint[7] memory data,
bool _skipAwardPrize
) internal {
uint64 prizeCooldownEndTime = uint64(data[0]);
uint16 prizeCooldownIndex = uint16(data[1]);
uint64 battleCooldownEndTime = uint64(data[2]);
uint16 updatedBattlesFought = uint16(data[3]) + 1;
// trigger prize cooldown
if (_winner && _leader && !_skipAwardPrize) {
prizeCooldownEndTime = uint64((prizeCooldowns[prizeCooldownIndex] / secondsPerBlock) + block.number);
if (prizeCooldownIndex < 6) {
prizeCooldownIndex += 1;
}
}
if (updatedBattlesFought % battlesTillBattleCooldown == 0) {
battleCooldownEndTime = uint64((experienceDelay / secondsPerBlock) + block.number);
}
fighterCore.updateFighterBattleStats(
_fighterId,
prizeCooldownEndTime,
prizeCooldownIndex,
battleCooldownEndTime,
updatedBattlesFought,
uint16(data[4]) + (_winner ? 1 : 0) // battlesWon
);
}
function _updateTeamBattleStats(uint256[] memory _fighterIds, bool _attackerWin, bool _skipAwardPrize) private {
for (uint i = 0; i < _fighterIds.length; i++) {
_updateFighterBattleStats(_fighterIds[i], _attackerWin, i == 0, _getFighterBattleData(_fighterIds[i]), _skipAwardPrize);
}
}
function _awardPrizeFighter(
address _winner, uint256[7] _attackerLeader, uint256[7] _defenderLeader
)
internal
returns (uint16 prizeGen, uint256 prizeGenes)
{
uint256[6] memory newFighterData = _generateFighterStats(_attackerLeader[5], _attackerLeader[6], _defenderLeader[5], _defenderLeader[6]);
prizeGen = uint16(newFighterData[0]);
prizeGenes = newFighterData[1];
fighterCore.createPrizeFighter(
prizeGen,
prizeGenes,
uint8(newFighterData[2]),
uint8(newFighterData[3]),
uint8(newFighterData[4]),
uint8(newFighterData[5]),
_winner
);
}
function _updateFightersAndAwardPrizes(
uint256[] _attackerFighterIds,
uint256[] _defenderFighterIds,
bool _attackerWin,
address _winnerAddress,
uint32 _attackerExperienceGained,
uint32 _defenderExperienceGained
)
internal
returns (uint16 prizeGen, uint256 prizeGenes)
{
// grab prize cooldown info before it gets updated
uint256[7] memory attackerLeader = _getFighterBattleData(_attackerFighterIds[0]);
uint256[7] memory defenderLeader = _getFighterBattleData(_defenderFighterIds[0]);
bool skipAwardPrize = (_attackerWin && attackerLeader[0] >= block.number) || (!_attackerWin && defenderLeader[0] >= block.number);
_increaseTeamFighterStats(_attackerFighterIds, _attackerExperienceGained);
_increaseTeamFighterStats(_defenderFighterIds, _defenderExperienceGained);
_updateTeamBattleStats(_attackerFighterIds, _attackerWin, skipAwardPrize);
_updateTeamBattleStats(_defenderFighterIds, !_attackerWin, skipAwardPrize);
// prizes
// dont award prize if on cooldown
if (skipAwardPrize) {
return;
}
return _awardPrizeFighter(_winnerAddress, attackerLeader, defenderLeader);
}
}
// File: contracts/Battle/BattleCore.sol
contract BattleCore is BattlePrize {
function BattleCore(address _coreAddress, address _geneScienceAddress, address _battleDeciderAddress) public {
addRace(4, 4, 4); // half elf
addRace(6, 2, 4); // orc
addRace(4, 5, 3); // succubbus
addRace(6, 4, 2); // mage
addRace(7, 1, 4);
_setFighterCoreAddress(_coreAddress);
_setGeneScienceAddress(_geneScienceAddress);
_setBattleDeciderAddress(_battleDeciderAddress);
// no team 0
uint256[] memory fighterIds = new uint256[](1);
fighterIds[0] = uint256(0);
_createTeam(address(0), fighterIds);
teamIndexToExist[0] = false;
}
/// @dev DON'T give me your money.
function() external {}
function totalTeams() public view returns (uint256) {
// team 0 doesn't exist
return teams.length - 1;
}
function isValidTeam(uint256[] _fighterIds) public view returns (bool) {
for (uint i = 0; i < _fighterIds.length; i++) {
uint256 fighterId = _fighterIds[i];
if (fighterCore.ownerOf(fighterId) != msg.sender)
return false;
if (fighterIndexToTeam[fighterId] > 0)
return false;
// check for duplicate fighters
for (uint j = i + 1; j < _fighterIds.length; j++) {
if (_fighterIds[i] == _fighterIds[j]) {
return false;
}
}
}
return true;
}
function createTeam(uint256[] _fighterIds)
public
whenNotPaused
returns(uint256)
{
require(_fighterIds.length > 0 && _fighterIds.length <= maxPerTeam);
require(isValidTeam(_fighterIds));
return _createTeam(msg.sender, _fighterIds);
}
function _createTeam(address _owner, uint256[] _fighterIds) internal returns(uint256) {
Team memory _team = Team({
owner: _owner,
fighterIds: _fighterIds
});
uint256 newTeamId;
// reuse teamId if address has deleted teams
if (deletedTeamIds.length > 0) {
newTeamId = deletedTeamIds[deletedTeamIds.length - 1];
delete deletedTeamIds[deletedTeamIds.length - 1];
deletedTeamIds.length--;
teams[newTeamId] = _team;
} else {
newTeamId = teams.push(_team) - 1;
}
require(newTeamId <= 4294967295);
for (uint i = 0; i < _fighterIds.length; i++) {
uint256 fighterId = _fighterIds[i];
fighterIndexToTeam[fighterId] = newTeamId;
}
teamIndexToExist[newTeamId] = true;
TeamCreated(newTeamId, _fighterIds);
return newTeamId;
}
function deleteTeam(uint256 _teamId)
public
whenNotPaused
onlyTeamOwner(_teamId)
onlyExistingTeam(_teamId)
{
_deleteTeam(_teamId);
}
function _deleteTeam(uint256 _teamId) private {
Team memory team = teams[_teamId];
for (uint256 i = 0; i < team.fighterIds.length; i++) {
fighterIndexToTeam[team.fighterIds[i]] = 0;
}
TeamDeleted(_teamId, team.fighterIds);
delete teams[_teamId];
deletedTeamIds.push(_teamId);
teamIndexToExist[_teamId] = false;
}
function battle(uint256[] _attackerFighterIds, uint256 _defenderTeamId)
public
whenNotPaused
onlyExistingTeam(_defenderTeamId)
returns (bool)
{
require(_attackerFighterIds.length > 0 && _attackerFighterIds.length <= maxPerTeam);
require(isValidTeam(_attackerFighterIds));
Team memory defenderTeam = teams[_defenderTeamId];
// check that a user isn't attacking himself
require(msg.sender != defenderTeam.owner);
uint256[] memory defenderFighterIds = defenderTeam.fighterIds;
bool attackerWon;
uint256 xpForAttacker;
uint256 xpForDefender;
_deleteTeam(_defenderTeamId);
(
attackerWon,
xpForAttacker,
xpForDefender
) = battleDecider.determineWinner(getFighterArray(_attackerFighterIds), getFighterArray(defenderFighterIds));
address winnerAddress;
address loserAddress;
if (attackerWon) {
winnerAddress = msg.sender;
loserAddress = defenderTeam.owner;
} else {
winnerAddress = defenderTeam.owner;
loserAddress = msg.sender;
}
uint16 prizeGen;
uint256 prizeGenes;
(prizeGen, prizeGenes) = _updateFightersAndAwardPrizes(_attackerFighterIds, defenderFighterIds, attackerWon, winnerAddress, uint32(xpForAttacker), uint32(xpForDefender));
BattleResult(winnerAddress, loserAddress, _attackerFighterIds, defenderFighterIds, attackerWon, prizeGen, prizeGenes, uint32(xpForAttacker), uint32(xpForDefender));
return attackerWon;
}
/// @param _id The ID of the team of interest.
function getTeam(uint256 _id)
public
view
returns (
address owner,
uint256[] fighterIds
) {
Team storage _team = teams[_id];
owner = _team.owner;
fighterIds = _team.fighterIds;
}
function getFighterArray(uint256[] _fighterIds) public view returns (uint256[7][]) {
uint256[7][] memory res = new uint256[7][](_fighterIds.length);
for (uint i = 0; i < _fighterIds.length; i++) {
uint256 generation;
uint256 genes;
uint256 dexterity;
uint256 strength;
uint256 vitality;
uint256 luck;
uint256 experience;
(
,
,
,
,
,
generation,
genes,
dexterity,
strength,
vitality,
luck,
experience
) = fighterCore.getFighter(_fighterIds[i]);
uint256 level = experienceToLevel(experience);
res[i] = [
level,
generation,
strength,
dexterity,
vitality,
luck,
genes
];
}
return res;
}
}
{
"compilationTarget": {
"BattleCore.sol": "BattleCore"
},
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getTeam","outputs":[{"name":"owner","type":"address"},{"name":"fighterIds","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_race","type":"uint256"},{"name":"_generation","type":"uint256"},{"name":"_rand","type":"uint256"}],"name":"_getStrengthDexterityVitality","outputs":[{"name":"strength","type":"uint256"},{"name":"dexterity","type":"uint256"},{"name":"vitality","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"fighterIndexToTeam","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setGeneScienceAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_gen","type":"uint256"},{"name":"_rand","type":"uint256"}],"name":"genToExtraStats","outputs":[{"name":"extraStrength","type":"uint8"},{"name":"extraDexterity","type":"uint8"},{"name":"extraVitality","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_fighterIds","type":"uint256[]"}],"name":"getFighterArray","outputs":[{"name":"","type":"uint256[7][]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"battlesTillBattleCooldown","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"prizeCooldowns","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_experience","type":"uint256"}],"name":"experienceToLevel","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"teams","outputs":[{"name":"owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"raceBaseStats","outputs":[{"name":"strength","type":"uint8"},{"name":"dexterity","type":"uint8"},{"name":"vitality","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_race","type":"uint256"}],"name":"raceToBaseStats","outputs":[{"name":"strength","type":"uint8"},{"name":"dexterity","type":"uint8"},{"name":"vitality","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"teamIndexToExist","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_secs","type":"uint256"}],"name":"setSecondsPerBlock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fighterCore","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalTeams","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"newContractAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_v2Address","type":"address"}],"name":"setNewAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"secondsPerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"removeLastRace","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_strength","type":"uint8"},{"name":"_dexterity","type":"uint8"},{"name":"_vitality","type":"uint8"}],"name":"addRace","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"extraStatsForGen","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"battleDecider","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxPerTeam","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_attackerFighterIds","type":"uint256[]"},{"name":"_defenderTeamId","type":"uint256"}],"name":"battle","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_teamId","type":"uint256"}],"name":"teamExists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"deletedTeamIds","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"stats","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"genBaseStats","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setBattleDeciderAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"experienceDelay","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getDeletedTeams","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setFighterCoreAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_gen","type":"uint256"},{"name":"_rand","type":"uint256"}],"name":"genToLuck","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_fighterIds","type":"uint256[]"}],"name":"createTeam","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getRaceBaseStats","outputs":[{"name":"strength","type":"uint256"},{"name":"dexterity","type":"uint256"},{"name":"vitality","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_teamId","type":"uint256"}],"name":"deleteTeam","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_fighterIds","type":"uint256[]"}],"name":"isValidTeam","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"geneScience","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_coreAddress","type":"address"},{"name":"_geneScienceAddress","type":"address"},{"name":"_battleDeciderAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":false,"stateMutability":"nonpayable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newContract","type":"address"}],"name":"ContractUpgrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"teamId","type":"uint256"},{"indexed":false,"name":"fighterIds","type":"uint256[]"}],"name":"TeamCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"teamId","type":"uint256"},{"indexed":false,"name":"fighterIds","type":"uint256[]"}],"name":"TeamDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"winnerAddress","type":"address"},{"indexed":true,"name":"loserAddress","type":"address"},{"indexed":false,"name":"attackerFighterIds","type":"uint256[]"},{"indexed":false,"name":"defenderFighterIds","type":"uint256[]"},{"indexed":false,"name":"attackerWon","type":"bool"},{"indexed":false,"name":"prizeFighterGeneration","type":"uint16"},{"indexed":false,"name":"prizeFighterGenes","type":"uint256"},{"indexed":false,"name":"attackerXpGained","type":"uint32"},{"indexed":false,"name":"defenderXpGained","type":"uint32"}],"name":"BattleResult","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]