contract Snip3D is  Owned {
    using SafeMath for uint;
    uint public _totalSupply;

    mapping(address => uint256)public  balances;// soldiers on field
    mapping(address => uint256)public  bullets;// amount of bullets Owned
    mapping(uint256 => address)public  formation;// the playing field
    uint256 public nextFormation;// next spot in formation
    mapping(address => uint256)public lastMove;//blocknumber lastMove
    mapping(uint256 => address) public RefundWaitingLine;
    uint256 public  NextInLine;//next person to be refunded
    uint256 public  NextAtLineEnd;//next spot to add loser
    uint256 public Refundpot;
    uint256 public blocksBeforeSemiRandomShoot = 200;
    uint256 public blocksBeforeTargetShoot = 800;
    
    // events
    event death(address indexed player);
    event semiShot(address indexed player);
    event targetShot(address indexed player);
    
    //constructor
    constructor()
        public
    {
        
        
    }
    //mods
    modifier isAlive()
    {
        require(balances[msg.sender] > 0);
        _;
    }
    // divfunctions
HourglassInterface constant P3Dcontract_ = HourglassInterface(0xB3775fB83F7D12A36E0475aBdD1FCA35c091efBe);
SPASMInterface constant SPASM_ = SPASMInterface(0xfaAe60F2CE6491886C9f7C9356bd92F688cA66a1);
// view functions
function harvestabledivs()
        view
        public
        returns(uint256)
    {
        return ( P3Dcontract_.dividendsOf(address(this)))  ;
    }
    function nextonetogetpaid()
        public
        view
        returns(address)
    {
        
        return (RefundWaitingLine[NextInLine]);
    }
    function playervanity(address theplayer)
        public
        view
        returns( string )
    {
        return (Vanity[theplayer]);
    }
    function blocksTillSemiShoot(address theplayer)
        public
        view
        returns( uint256 )
    {
        uint256 number;
        if(block.number - lastMove[theplayer] < blocksBeforeSemiRandomShoot)
        {number = blocksBeforeSemiRandomShoot -(block.number - lastMove[theplayer]);}
        return (number);
    }
    function blocksTillTargetShoot(address theplayer)
        public
        view
        returns( uint256 )
    {
        uint256 number;
        if(block.number - lastMove[theplayer] < blocksBeforeTargetShoot)
        {number = blocksBeforeTargetShoot -(block.number - lastMove[theplayer]);}
        return (number);
    }
function amountofp3d() external view returns(uint256){
    return ( P3Dcontract_.balanceOf(address(this)))  ;
}
    //divsection
uint256 public pointMultiplier = 10e18;
struct Account {
  uint balance;
  uint lastDividendPoints;
}
mapping(address=>Account) accounts;
mapping(address => string) public Vanity;
uint public ethtotalSupply;
uint public totalDividendPoints;
uint public unclaimedDividends;

function dividendsOwing(address account) public view returns(uint256) {
  uint256 newDividendPoints = totalDividendPoints.sub(accounts[account].lastDividendPoints);
  return (balances[account] * newDividendPoints) / pointMultiplier;
}
modifier updateAccount(address account) {
  uint256 owing = dividendsOwing(account);
  if(owing > 0) {
    unclaimedDividends = unclaimedDividends.sub(owing);
    
    account.transfer(owing);
  }
  accounts[account].lastDividendPoints = totalDividendPoints;
  _;
}
function () external payable{}
function fetchdivs(address toupdate) public updateAccount(toupdate){}
// Gamefunctions
function sendInSoldier(address masternode) public updateAccount(msg.sender)  payable{
    uint256 value = msg.value;
    require(value >= 100 finney);// sending in sol costs 0.1 eth
    address sender = msg.sender;
    // add life
    balances[sender]++;
    // update totalSupply
    _totalSupply++;
    // add bullet 
    bullets[sender]++;
    // add to playing field
    formation[nextFormation] = sender;
    nextFormation++;
    // reset lastMove to prevent people from adding bullets and start shooting
    lastMove[sender] = block.number;
    // buy P3D
    P3Dcontract_.buy.value(5 finney)(masternode);
    // check excess of payed 
     if(value > 100 finney){Refundpot += value - 100 finney;}
    // progress refundline
    Refundpot += 5 finney;
    // send SPASM cut
    SPASM_.disburse.value(2 finney)();

}
function sendInSoldierReferal(address masternode, address referal) public updateAccount(msg.sender)  payable{
    uint256 value = msg.value;
    require(value >= 100 finney);// sending in sol costs 0.1 eth
    address sender = msg.sender;
    // add life
    balances[sender]++;
    // update totalSupply
    _totalSupply++;
    // add bullet 
    bullets[sender]++;
    // add to playing field
    formation[nextFormation] = sender;
    nextFormation++;
    // reset lastMove to prevent people from adding bullets and start shooting
    lastMove[sender] = block.number;
    // buy P3D
    P3Dcontract_.buy.value(5 finney)(masternode);
    // check excess of payed 
    if(value > 100 finney){Refundpot += value - 100 finney;}
    // progress refundline
    Refundpot += 5 finney;
    // send SPASM cut
    SPASM_.disburse.value(1 finney)();
    // send referal cut
    referal.transfer(1 finney);

}
function shootSemiRandom() public isAlive() {
    address sender = msg.sender;
    require(block.number > lastMove[sender] + blocksBeforeSemiRandomShoot);
    require(bullets[sender] > 0);
    uint256 semiRNG = (block.number.sub(lastMove[sender])) % 200;
    
    uint256 shot = uint256 (blockhash(block.number.sub(semiRNG))) % nextFormation;
    address killed = formation[shot];
    // solo soldiers self kill prevention - shoots next in line instead
    if(sender == killed)
    {
        shot = uint256 (blockhash(block.number.sub(semiRNG).add(1))) % nextFormation;
        killed = formation[shot];
    }
    // update divs loser
    fetchdivs(killed);
    // remove life
    balances[killed]--;
    // update totalSupply
    _totalSupply--;
    // remove bullet 
    bullets[sender]--;
    // remove from playing field
    uint256 lastEntry = nextFormation.sub(1);
    formation[shot] = formation[lastEntry];
    nextFormation--;
    // reset lastMove to prevent people from adding bullets and start shooting
    lastMove[sender] = block.number;
    
    
    // add loser to refundline
    RefundWaitingLine[NextAtLineEnd] = killed;
    NextAtLineEnd++;
    // disburse eth to survivors
    uint256 amount = 88 finney;
    totalDividendPoints = totalDividendPoints.add(amount.mul(pointMultiplier).div(_totalSupply));
    unclaimedDividends = unclaimedDividends.add(amount);
    emit semiShot(sender);
    emit death(killed);

}
function shootTarget(uint256 target) public isAlive() {
    address sender = msg.sender;
    require(target <= nextFormation && target > 0);
    require(block.number > lastMove[sender] + blocksBeforeTargetShoot);
    require(bullets[sender] > 0);
    if(target == nextFormation){target = 0;}
    address killed = formation[target];
    
    // update divs loser
    fetchdivs(killed);
    
    // remove life
    balances[killed]--;
    // update totalSupply
    _totalSupply--;
    // remove bullet 
    bullets[sender]--;
    // remove from playing field
    uint256 lastEntry = nextFormation.sub(1);
    formation[target] = formation[lastEntry];
    nextFormation--;
    // reset lastMove to prevent people from adding bullets and start shooting
    lastMove[sender] = block.number;
    
    // add loser to refundline
    RefundWaitingLine[NextAtLineEnd] = killed;
    NextAtLineEnd++;
    // fetch contracts divs
    //allocate p3d dividends to contract 
            uint256 dividends =  harvestabledivs();
            require(dividends > 0);
            uint256 base = dividends.div(100);
            P3Dcontract_.withdraw();
            SPASM_.disburse.value(base)();// to dev fee sharing contract SPASM
    // disburse eth to survivors
    uint256 amount = 88 finney;
    uint256 amt2 = dividends.sub(base);
    Refundpot = Refundpot.add(amt2);
    totalDividendPoints = totalDividendPoints.add(amount.mul(pointMultiplier).div(_totalSupply));
    unclaimedDividends = unclaimedDividends.add(amount);
    emit targetShot(sender);
    emit death(killed);
}

function Payoutnextrefund ()public
    {
        //allocate p3d dividends to sacrifice if existing
            uint256 Pot = Refundpot;
            require(Pot > 0.1 ether);
            Refundpot -= 0.1 ether;
            RefundWaitingLine[NextInLine].transfer(0.1 ether);
            NextInLine++;
            //
    }

function disburse() public  payable {
    uint256 amount = msg.value;
    uint256 base = amount.div(100);
    uint256 amt2 = amount.sub(base);
  totalDividendPoints = totalDividendPoints.add(amt2.mul(pointMultiplier).div(_totalSupply));
 unclaimedDividends = unclaimedDividends.add(amt2);
 
}
function changevanity(string van) public payable{
    require(msg.value >= 1  finney);
    Vanity[msg.sender] = van;
    Refundpot += msg.value;
}
function P3DDivstocontract() public{
    uint256 divs = harvestabledivs();
    require(divs > 0);
 
P3Dcontract_.withdraw();
    //1% to owner
    uint256 base = divs.div(100);
    uint256 amt2 = divs.sub(base);
    SPASM_.disburse.value(base)();// to dev fee sharing contract
   Refundpot = Refundpot.add(amt2);// add divs to refund line
}

// bugtest selfdestruct function - deactivate on live
//  function die () public onlyOwner {
//      selfdestruct(msg.sender);
//  }

    
}
// Snip3d contract
function harvestabledivs()
        view
        public
        returns(uint256)
    {
        return ( P3Dcontract_.dividendsOf(address(this)))  ;
    }
// view functions