ETH Price: $2,184.88 (-5.55%)

Transaction Decoder

Block:
6558969 at Oct-21-2018 10:40:54 PM +UTC
Transaction Fee:
0.00024736272 ETH $0.54
Gas Used:
204,432 Gas / 1.21 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x4F2d119b...88758BDEF
0x59De2a5C...0FC5fF7b5
(Spark Pool)
6,166.112485636569853697 Eth6,166.112732999289853697 Eth0.00024736272
0xed1b3F41...e1b7Eefbf
0.004964860008888888 Eth
Nonce: 169
0.004717497288888888 Eth
Nonce: 170
0.00024736272

Execution Trace

MEAManager.launchShipOnMEA( _assetId=202, starId=42 ) => ( 1540228186 )
  • CSCPreSaleManager.getCollectibleDetails( _assetId=202 ) => ( assetId=202, sequenceId=33, collectibleType=1, collectibleClass=2, isRedeemed=False, owner=0xed1b3F41d412d41411c8eb311de0083e1b7Eefbf )
  • 0x4f2d119b1063f75bd2088b80ce288a588758bdef.8ede1817( )
  • 0x4f2d119b1063f75bd2088b80ce288a588758bdef.7f9c8974( )
  • 0x4f2d119b1063f75bd2088b80ce288a588758bdef.85dc6721( )
  • 0x4f2d119b1063f75bd2088b80ce288a588758bdef.4d15642a( )
    launchShipOnMEA[MEAManager (ln:530)]
    File 1 of 2: MEAManager
    pragma solidity ^0.4.19;
    
    
    
    /// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens
    /// @author Dieter Shirley <dete@axiomzen.co> (https://github.com/dete)
    contract CSCERC721 {
      // Required methods
      function balanceOf(address _owner) public view returns (uint256 balance) { 
          return 0;
          
      }
      function ownerOf(uint256 _tokenId) public view returns (address owner) { return;}
    
      function getCollectibleDetails(uint256 _assetId) external view returns(uint256 assetId, uint256 sequenceId, uint256 collectibleType, uint256 collectibleClass, bool isRedeemed, address owner) {
            assetId = 0;
            sequenceId = 0;
            collectibleType = 0;
            collectibleClass = 0;
            owner = 0;
            isRedeemed = false;
      }
    
       function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
            return;
       }
    
    }
    
    contract CSCFactoryERC721 {
        
        function ownerOf(uint256 _tokenId) public view returns (address owner) { return;}
    
        function getCollectibleDetails(uint256 _tokenId) external view returns(uint256 assetId, uint256 sequenceId, uint256 collectibleType, uint256 collectibleClass, bytes32 collectibleName, bool isRedeemed, address owner) {
    
            assetId = 0;
            sequenceId = 0;
            collectibleType = 0;
            collectibleClass = 0;
            owner = 0;
            collectibleName = 0x0;
            isRedeemed = false;
        }
    
        function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
            return;
       }
    }
    
    contract ERC20 {
      function balanceOf(address who) public view returns (uint256);
      function transfer(address to, uint256 value) public returns (bool);
    }
    
    contract CSCResourceFactory {
        mapping(uint16 => address) public resourceIdToAddress; 
    }
    
    
    contract MEAHiddenLogic {
    
    
        function getTotalTonsClaimed() external view returns(uint32) {
            return;
        }
    
        function getTotalSupply() external view returns(uint32) {
            return;
        }
    
         function getStarTotalSupply(uint8 _starId) external view returns(uint32) {
            return;
        }
    
        function getReturnTime(uint256 _assetId) external view returns(uint256 time) {
            return;
        }
    
        //uint256 iron, uint256 quartz, uint256 nickel, uint256 cobalt, uint256 silver, uint256 titanium, uint256 lucinite, uint256 gold, uint256 cosmethyst, uint256 allurum,  uint256 platinum,  uint256 trilite 
        function setResourceForStar(uint8[5] _resourceTypes, uint16[5] _resourcePer, uint32[5] _resourceAmounts) public returns(uint8 starId) {
        }
    
        
        /// @dev Method to fetch collected ore details
        function getAssetCollectedOreBallances(uint256 _assetID) external view returns(uint256 iron, uint256 quartz, uint256 nickel, uint256 cobalt, uint256 silver, uint256 titanium, uint256 lucinite, uint256 gold, uint256 cosmethyst, uint256 allurum,  uint256 platinum,  uint256 trilite);
    
        function getAssetCollectedOreBallancesArray(uint256 _assetID) external view returns(uint256[12] ores);
    
        function emptyShipCargo(uint32 _assetId) external;
    
         /// @dev For creating CSC Collectible
        function startMEAMission(uint256 _assetId, uint256 oreMax, uint8 starId, uint256 _travelTime) public returns(uint256);
    
        
    }
    
    /* Controls state and access rights for contract functions
     * @title Operational Control
     * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
     * Inspired and adapted from contract created by OpenZeppelin
     * Ref: https://github.com/OpenZeppelin/zeppelin-solidity/
     */
    contract OperationalControl {
        // Facilitates access & control for the game.
        // Roles:
        //  -The Managers (Primary/Secondary): Has universal control of all elements (No ability to withdraw)
        //  -The Banker: The Bank can withdraw funds and adjust fees / prices.
        //  -otherManagers: Contracts that need access to functions for gameplay
    
        /// @dev Emited when contract is upgraded
        event ContractUpgrade(address newContract);
    
        /// @dev Emited when other manager is set
        event OtherManagerUpdated(address otherManager, uint256 state);
    
        // The addresses of the accounts (or contracts) that can execute actions within each roles.
        address public managerPrimary;
        address public managerSecondary;
        address public bankManager;
    
        // Contracts that require access for gameplay
        mapping(address => uint8) public otherManagers;
    
        // @dev Keeps track whether the contract is paused. When that is true, most actions are blocked
        bool public paused = false;
    
        // @dev Keeps track whether the contract erroredOut. When that is true, most actions are blocked & refund can be claimed
        bool public error = false;
    
        /// @dev Operation modifiers for limiting access
        modifier onlyManager() {
            require(msg.sender == managerPrimary || msg.sender == managerSecondary);
            _;
        }
    
        modifier onlyBanker() {
            require(msg.sender == bankManager);
            _;
        }
    
        modifier onlyOtherManagers() {
            require(otherManagers[msg.sender] == 1);
            _;
        }
    
    
        modifier anyOperator() {
            require(
                msg.sender == managerPrimary ||
                msg.sender == managerSecondary ||
                msg.sender == bankManager ||
                otherManagers[msg.sender] == 1
            );
            _;
        }
    
        /// @dev Assigns a new address to act as the Other Manager. (State = 1 is active, 0 is disabled)
        function setOtherManager(address _newOp, uint8 _state) external onlyManager {
            require(_newOp != address(0));
    
            otherManagers[_newOp] = _state;
    
            OtherManagerUpdated(_newOp,_state);
        }
    
        /// @dev Assigns a new address to act as the Primary Manager.
        function setPrimaryManager(address _newGM) external onlyManager {
            require(_newGM != address(0));
    
            managerPrimary = _newGM;
        }
    
        /// @dev Assigns a new address to act as the Secondary Manager.
        function setSecondaryManager(address _newGM) external onlyManager {
            require(_newGM != address(0));
    
            managerSecondary = _newGM;
        }
    
        /// @dev Assigns a new address to act as the Banker.
        function setBanker(address _newBK) external onlyManager {
            require(_newBK != address(0));
    
            bankManager = _newBK;
        }
    
        /*** Pausable functionality adapted from OpenZeppelin ***/
    
        /// @dev Modifier to allow actions only when the contract IS NOT paused
        modifier whenNotPaused() {
            require(!paused);
            _;
        }
    
        /// @dev Modifier to allow actions only when the contract IS paused
        modifier whenPaused {
            require(paused);
            _;
        }
    
        /// @dev Modifier to allow actions only when the contract has Error
        modifier whenError {
            require(error);
            _;
        }
    
        /// @dev Called by any Operator role to pause the contract.
        /// Used only if a bug or exploit is discovered (Here to limit losses / damage)
        function pause() external onlyManager whenNotPaused {
            paused = true;
        }
    
        /// @dev Unpauses the smart contract. Can only be called by the Game Master
        /// @notice This is public rather than external so it can be called by derived contracts. 
        function unpause() public onlyManager whenPaused {
            // can't unpause if contract was upgraded
            paused = false;
        }
    
        /// @dev Unpauses the smart contract. Can only be called by the Game Master
        /// @notice This is public rather than external so it can be called by derived contracts. 
        function hasError() public onlyManager whenPaused {
            error = true;
        }
    
        /// @dev Unpauses the smart contract. Can only be called by the Game Master
        /// @notice This is public rather than external so it can be called by derived contracts. 
        function noError() public onlyManager whenPaused {
            error = false;
        }
    }
    
    contract MEAManager is OperationalControl {
    
        /*** EVENTS ***/
    
        /*** CONSTANTS ***/
        uint256 public constant REAPER_INTREPID = 3; 
        uint256 public constant REAPER_INTREPID_EXTRACTION_BASE = 10; // tons per hour of mining
        uint256 public constant REAPER_INTREPID_FTL_SPEED = 900; // Seconds to travel 1 light year
        uint256 public constant REAPER_INTREPID_MAX_CARGO = 320;
    
        uint256 public constant PHOENIX_CORSAIR = 2;
        uint256 public constant PHOENIX_CORSAIR_EXTRACTION_BASE = 40; // tons per hour of mining
        uint256 public constant PHOENIX_CORSAIR_FTL_SPEED = 1440; // Seconds to travel 1 light year
        uint256 public constant PHOENIX_CORSAIR_MAX_CARGO = 1500;
    
        uint256 public constant VULCAN_PROMETHEUS = 1;
        uint256 public constant VULCAN_PROMETHEUS_EXTRACTION_BASE = 300; // tons per hour of mining
        uint256 public constant VULCAN_PROMETHEUS_FTL_SPEED = 2057; // Seconds to travel 1 light year
        uint256 public constant VULCAN_PROMETHEUS_MAX_CARGO = 6000; 
    
        uint256 public constant SIGMA = 4;
        uint256 public constant SIGMA_EXTRACTION_BASE = 150; // tons per hour of mining
        uint256 public constant SIGMA_FTL_SPEED = 4235; // Seconds to travel 1 light year
        uint256 public constant SIGMA_MAX_CARGO = 15000; 
    
        uint256 public constant HAYATO = 5;
        uint256 public constant HAYATO_EXTRACTION_BASE = 150; // tons per hour of mining
        uint256 public constant HAYATO_FTL_SPEED = 360; // Seconds to travel 1 light year
        uint256 public constant HAYATO_MAX_CARGO = 1500; 
    
        uint256 public constant CPGPEREGRINE = 6;
        uint256 public constant CPGPEREGRINE_EXTRACTION_BASE = 150; // tons per hour of mining
        uint256 public constant CPGPEREGRINE_FTL_SPEED = 720; // Seconds to travel 1 light year
        uint256 public constant CPGPEREGRINE_MAX_CARGO = 4000; 
    
        uint256 public constant TACTICALCRUISER = 7;
        uint256 public constant TACTICALCRUISER_EXTRACTION_BASE = 150; // tons per hour of mining
        uint256 public constant TACTICALCRUISER_FTL_SPEED = 720; // Seconds to travel 1 light year
        uint256 public constant TACTICALCRUISER_MAX_CARGO = 1000;
    
        uint256 public constant OTHERCRUISER = 8;
        uint256 public constant OTHERCRUISER_EXTRACTION_BASE = 100; // tons per hour of mining
        uint256 public constant OTHERCRUISER_FTL_SPEED = 720; // Seconds to travel 1 light year
        uint256 public constant OTHERCRUISER_MAX_CARGO = 1500;  
    
        uint256 public constant VULCAN_POD = 9;
        uint256 public constant VULCAN_POD_EXTRACTION_BASE = 1; // tons per hour of mining
        uint256 public constant VULCAN_POD_FTL_SPEED = 2000; // Seconds to travel 1 light year
        uint256 public constant VULCAN_POD_MAX_CARGO = 75;  
    
        //For Devs to Travel Around
        uint256 public constant DEVCLASS = 99;
        uint256 public constant DEVCLASS_EXTRACTION_BASE = 50; // tons per hour of mining
        uint256 public constant DEVCLASS_FTL_SPEED = 10; // Seconds to travel 1 light year
        uint256 public constant DEVCLASS_MAX_CARGO = 500; 
        
        /// @notice Name and symbol of the non fungible token, as defined in ERC721.
        string public constant NAME = "MEAGameManager";
    
        /*** Mappings ***/
    
         /// @dev assetID to ore type to qty collected
        mapping(uint32 => mapping(uint8 => uint32)) public collectedOreAssetMapping;
    
        /// @dev owner address to ore type to qty collected
        mapping(address => mapping(uint8 => uint32)) public collectedOreBalanceMapping;
    
        /// @dev owner address to ore type to qty collected
        mapping(address => mapping(uint8 => uint32)) public distributedOreBalanceMapping;
    
        /// @dev assetID to number of MEA trips it has completed
        mapping(uint32 => uint32) public assetIdNumberOfTripsMapping;
    
        /// @dev assetID to ore type to qty collected
        mapping(uint8 => uint16) public starLightyearDistanceMapping;
    
        /// @dev assetID to last star visited
        mapping(uint32 => uint8) public assetIdToStarVisitedMapping;
    
        /// @dev assetID to last star visited
        mapping(uint16 => address) public resourceERC20Address;
    
        /// @dev assetID to Start Time of Current Trip
        mapping(uint32 => uint32) public assetIdCurrentTripStartTimeMapping;
    
    
        /*** Variables ***/
        uint256 public miningTimePerTrip = 3600; // 3600 for 1 hour 10
        uint256 public aimeIncreasePerTrip = 2500; // 25.00
    
        address cscERC721Address;
        address cscFactoryERC721Address;
        address hiddenLogicAddress;
     
    
        function MEAManager() public {
            require(msg.sender != address(0));
            paused = true; 
            managerPrimary = msg.sender;
            managerSecondary = msg.sender;
            bankManager = msg.sender;
            cscERC721Address = address(0xe4f5e0d5c033f517a943602df942e794a06bc123);
            cscFactoryERC721Address = address(0xcc9a66acf8574141b0e025202dd57649765a4be7);
        }
    
        /*** Management Functions ***/
    
        /// @dev Set HiddenLogic
        function setHiddenLogic(address _hiddenLogicAddress) public onlyManager {
            hiddenLogicAddress = _hiddenLogicAddress;
        }
    
        /// @dev Set HiddenLogic
        function setResourceERC20Address(uint16 _resId, address _reourceAddress) public onlyManager {
            resourceERC20Address[_resId] = _reourceAddress;
        }
    
        /// @dev Set HiddenLogic
        function setAllResourceERC20Addresses(address _master) public onlyManager {
            CSCResourceFactory factory = CSCResourceFactory(_master);
            for(uint8 i = 0; i < 12; i++) {
                resourceERC20Address[i] = factory.resourceIdToAddress(i);
            }
        }
    
        /// @dev Set CSCErc721 Contract
        function setCSCERC721(address _cscERC721Address) public onlyManager {
            cscERC721Address = _cscERC721Address;
        }
    
         /// @dev Set CSCFactoryErc721 Contract
        function setCSCFactoryERC721(address _cscFactoryERC721Address) public onlyManager {
            cscFactoryERC721Address = _cscFactoryERC721Address;
        }
    
        /// @dev Set / Modify Lightyear Distance 3.456 ly = 3456
        function setStarDistance(uint8 _starId, uint16 _lightyearsInThousands) public anyOperator {
            starLightyearDistanceMapping[_starId] = _lightyearsInThousands;
        }
    
        /// @dev Set / Modify MEA Game Attributes
        function setMEAAttributes(uint256 _aime, uint256 _miningTime) public onlyManager {
            aimeIncreasePerTrip = _aime;
            miningTimePerTrip = _miningTime;
        }
    
        /// @dev Withdraw Remaining Resource Tokens
        function reclaimResourceDeposits(address _withdrawAddress) public onlyManager {
            require(_withdrawAddress != address(0));
            for(uint8 ii = 0; ii < 12; ii++) {
                if(resourceERC20Address[ii] != 0) {
                    ERC20 resCont = ERC20(resourceERC20Address[ii]);
                    uint256 bal = resCont.balanceOf(this);
                    resCont.transfer(_withdrawAddress, bal);
                }
            }
        }
    
        /*** Public Functions ***/
    
         /// @dev Get Current Cargo Hold of AssetId (item names)
        function getAssetIdCargo(uint32 _assetId) public view returns(uint256 iron, uint256 quartz, uint256 nickel, uint256 cobalt, uint256 silver, uint256 titanium, uint256 lucinite, uint256 gold, uint256 cosmethyst, uint256 allurum,  uint256 platinum,  uint256 trilite) {
            uint256[12] memory _ores = getAssetIdCargoArray(_assetId);
            iron = _ores[0];
            quartz = _ores[1];
            nickel = _ores[2];
            cobalt = _ores[3];
            silver = _ores[4];
            titanium = _ores[5];
            lucinite = _ores[6];
            gold = _ores[7];
            cosmethyst = _ores[8];
            allurum = _ores[9];
            platinum = _ores[10];
            trilite = _ores[11];
        }
    
        // function getAllShipStats(uint32[] _shipIds) public view returns(uint32[] results) {
        //     //loop all results
        //     for(uint i = 0; i < _shipIds.length; i++) {
        //         results[]];
        //     }
    
        // }
    
        /// @dev Get Current Cargo Hold of AssetId (array)
        function getAssetIdCargoArray (uint32 _assetId) public view returns(uint256[12])  {
            MEAHiddenLogic logic = MEAHiddenLogic(hiddenLogicAddress);
            return logic.getAssetCollectedOreBallancesArray(_assetId);
        }
    
        /// @dev Get AssetId Trip Completed Time
        function getAssetIdTripCompletedTime(uint256 _assetId) external view returns(uint256 time) {
            MEAHiddenLogic logic = MEAHiddenLogic(hiddenLogicAddress);
            return logic.getReturnTime(uint32(_assetId));
        }
    
        /// @dev Get AssetId Trip Completed Time
        function getAssetIdTripStartTime(uint256 _assetId) external view returns(uint256 time) {
    
            return assetIdCurrentTripStartTimeMapping[uint32(_assetId)];
        }
    
        function getLastStarOfAssetId(uint32 _assetId) public view returns(uint8 starId){
            return assetIdToStarVisitedMapping[_assetId];
        }
    
        /// @dev Get Resource Address
        function getResourceERC20Address(uint16 _resId) public view returns(address resourceContract) {
            return resourceERC20Address[_resId];
        }
    
        /// @dev Get Time
        function getMEATime() external view returns(uint256 time) {
            return now;
        }
    
        /// @dev Method to fetch processed ore details
        function getCollectedOreBalances(address _owner) external view returns(uint256 iron, uint256 quartz, uint256 nickel, uint256 cobalt, uint256 silver, uint256 titanium, uint256 lucinite, uint256 gold, uint256 cosmethyst, uint256 allurum,  uint256 platinum,  uint256 trilite) {
    
            iron = collectedOreBalanceMapping[_owner][0];
            quartz = collectedOreBalanceMapping[_owner][1];
            nickel = collectedOreBalanceMapping[_owner][2];
            cobalt = collectedOreBalanceMapping[_owner][3];
            silver = collectedOreBalanceMapping[_owner][4];
            titanium = collectedOreBalanceMapping[_owner][5];
            lucinite = collectedOreBalanceMapping[_owner][6];
            gold = collectedOreBalanceMapping[_owner][7];
            cosmethyst = collectedOreBalanceMapping[_owner][8];
            allurum = collectedOreBalanceMapping[_owner][9];
            platinum = collectedOreBalanceMapping[_owner][10];
            trilite = collectedOreBalanceMapping[_owner][11];
        }
    
        /// @dev Method to fetch processed ore details
        function getDistributedOreBalances(address _owner) external view returns(uint256 iron, uint256 quartz, uint256 nickel, uint256 cobalt, uint256 silver, uint256 titanium, uint256 lucinite, uint256 gold, uint256 cosmethyst, uint256 allurum,  uint256 platinum,  uint256 trilite) {
    
            iron = distributedOreBalanceMapping[_owner][0];
            quartz = distributedOreBalanceMapping[_owner][1];
            nickel = distributedOreBalanceMapping[_owner][2];
            cobalt = distributedOreBalanceMapping[_owner][3];
            silver = distributedOreBalanceMapping[_owner][4];
            titanium = distributedOreBalanceMapping[_owner][5];
            lucinite = distributedOreBalanceMapping[_owner][6];
            gold = distributedOreBalanceMapping[_owner][7];
            cosmethyst = distributedOreBalanceMapping[_owner][8];
            allurum = distributedOreBalanceMapping[_owner][9];
            platinum = distributedOreBalanceMapping[_owner][10];
            trilite = distributedOreBalanceMapping[_owner][11];
        }
    
        function withdrawCollectedResources() public {
    
            for(uint8 ii = 0; ii < 12; ii++) {
                require(resourceERC20Address[ii] != address(0));
                uint32 oreOutstanding = collectedOreBalanceMapping[msg.sender][ii] - distributedOreBalanceMapping[msg.sender][ii];
                if(oreOutstanding > 0) {
                    ERC20 resCont = ERC20(resourceERC20Address[ii]);
                    distributedOreBalanceMapping[msg.sender][ii] += oreOutstanding;
                    resCont.transfer(msg.sender, oreOutstanding);
                }
            }
    
        }
    
        //Gets star distance in thousandths of ly
        function getStarDistanceInLyThousandths(uint8 _starId) public view returns (uint32 total) {
            return starLightyearDistanceMapping[_starId];
        }
        
        //Gets total resources already claimed by commanders
        function totalMEATonsClaimed() public view returns (uint32 total) {
            MEAHiddenLogic logic = MEAHiddenLogic(hiddenLogicAddress);
            return logic.getTotalTonsClaimed();
        }
    
        //Gets total seeded supply commanders
        function totalMEATonsSupply() public view returns (uint32 total) {
            MEAHiddenLogic logic = MEAHiddenLogic(hiddenLogicAddress);
            return logic.getTotalSupply();
        }
    
         function totalStarSupplyRemaining(uint8 _starId) external view returns(uint32) {
            MEAHiddenLogic logic = MEAHiddenLogic(hiddenLogicAddress);
            return logic.getStarTotalSupply(_starId);
        }
    
        function claimOreOnlyFromAssetId(uint256 _assetId) {
            uint256 collectibleClass = 0;
            address shipOwner;
            (collectibleClass, shipOwner) = _getShipInfo(_assetId);
    
             require(shipOwner == msg.sender);
    
            _claimOreAndClear(uint32(_assetId), 0);
        }
        /// @dev For creating CSC Collectible
        function launchShipOnMEA(uint256 _assetId, uint8 starId) public whenNotPaused returns(uint256) {
            
            MEAHiddenLogic logic = MEAHiddenLogic(hiddenLogicAddress);
    
            uint256 collectibleClass = 0;
            address shipOwner;
    
            (collectibleClass, shipOwner) = _getShipInfo(_assetId);
    
            //Check if the ship owner is sender
            require(shipOwner == msg.sender);
    
            //Check if ship is back at earth
            require(now > logic.getReturnTime(_assetId));
            
            //Claims ore and clears
            _claimOreAndClear(uint32(_assetId), starId);
    
            //Get Asset Stats
            uint tripCount = assetIdNumberOfTripsMapping[uint32(_assetId)];
            uint starTripDist = starLightyearDistanceMapping[starId];
            uint256 oreMax = 5;
            uint256 tripSeconds = 10;
    
            if(collectibleClass == REAPER_INTREPID) {
                oreMax = REAPER_INTREPID_EXTRACTION_BASE + (REAPER_INTREPID_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                tripSeconds = REAPER_INTREPID_FTL_SPEED * starTripDist / 1000; // 4LPH - 900 seconds per light year
                if(oreMax > REAPER_INTREPID_MAX_CARGO)
                    oreMax = REAPER_INTREPID_MAX_CARGO;
            }
            else if(collectibleClass == PHOENIX_CORSAIR) {
                oreMax = PHOENIX_CORSAIR_EXTRACTION_BASE + (PHOENIX_CORSAIR_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                tripSeconds = PHOENIX_CORSAIR_FTL_SPEED * starTripDist / 1000; // 2.5LPH - 1440 seconds per light year
                if(oreMax > PHOENIX_CORSAIR_MAX_CARGO)
                    oreMax = PHOENIX_CORSAIR_MAX_CARGO;
            }
            else if(collectibleClass == VULCAN_PROMETHEUS) {
                oreMax = VULCAN_PROMETHEUS_EXTRACTION_BASE + (VULCAN_PROMETHEUS_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                tripSeconds = VULCAN_PROMETHEUS_FTL_SPEED * starTripDist / 1000; // 1.75LPH - 2057 seconds per light year
                if(oreMax > VULCAN_PROMETHEUS_MAX_CARGO)
                    oreMax = VULCAN_PROMETHEUS_MAX_CARGO;
            }
            else if(collectibleClass == SIGMA) {
                oreMax = SIGMA_EXTRACTION_BASE + (SIGMA_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                tripSeconds = SIGMA_FTL_SPEED * starTripDist / 1000; // 0.85LPH - 4235 seconds per light year
                if(oreMax > SIGMA_MAX_CARGO)
                    oreMax = SIGMA_MAX_CARGO;
            }
            else if(collectibleClass == HAYATO) { //Hayato
                oreMax = HAYATO_EXTRACTION_BASE + (HAYATO_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                tripSeconds = HAYATO_FTL_SPEED * starTripDist / 1000; // 10LPH - 360 seconds per light year
                if(oreMax > HAYATO_MAX_CARGO)
                    oreMax = HAYATO_MAX_CARGO;
            }
            else if(collectibleClass == CPGPEREGRINE) { //CPG Peregrine
                oreMax = CPGPEREGRINE_EXTRACTION_BASE + (CPGPEREGRINE_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                tripSeconds = CPGPEREGRINE_FTL_SPEED * starTripDist / 1000; // 5LPH -720 seconds per light year
                if(oreMax > CPGPEREGRINE_MAX_CARGO)
                    oreMax = CPGPEREGRINE_MAX_CARGO;
            }
            else if(collectibleClass == TACTICALCRUISER) { //TACTICA CRUISER Ships
                oreMax = TACTICALCRUISER_EXTRACTION_BASE + (TACTICALCRUISER_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                tripSeconds = TACTICALCRUISER_FTL_SPEED * starTripDist / 1000; 
                if(oreMax > TACTICALCRUISER_MAX_CARGO)
                    oreMax = TACTICALCRUISER_MAX_CARGO;
            }
            else if(collectibleClass == VULCAN_POD) { //TACTICA CRUISER Ships
                oreMax = VULCAN_POD_EXTRACTION_BASE + (VULCAN_POD_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                tripSeconds = VULCAN_POD_FTL_SPEED * starTripDist / 1000; 
                if(oreMax > VULCAN_POD_MAX_CARGO)
                    oreMax = VULCAN_POD_MAX_CARGO;
            }
            else if(collectibleClass >= DEVCLASS) { //Dev Ships
                oreMax = DEVCLASS_EXTRACTION_BASE + (DEVCLASS_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                tripSeconds = DEVCLASS_FTL_SPEED * starTripDist / 1000;
                if(oreMax > DEVCLASS_MAX_CARGO)
                    oreMax = DEVCLASS_MAX_CARGO;
            } else {
                if(collectibleClass >= OTHERCRUISER) { //Support Other Promo Ships
                    oreMax = OTHERCRUISER_EXTRACTION_BASE + (OTHERCRUISER_EXTRACTION_BASE * tripCount * aimeIncreasePerTrip / 10000);
                    tripSeconds = OTHERCRUISER_FTL_SPEED * starTripDist / 1000; 
                    if(oreMax > OTHERCRUISER_MAX_CARGO)
                        oreMax = OTHERCRUISER_MAX_CARGO;
                }
            }
    
            //Make Round Trip + Mining
            tripSeconds = ((tripSeconds * 2) + miningTimePerTrip); //3600 for an hour - 0 for testing ***************************
    
            //calculate travel time
            uint256 returnTime = logic.startMEAMission(_assetId, oreMax, starId, tripSeconds);
    
            //Confirm trip
            if(returnTime > 0) {
                assetIdNumberOfTripsMapping[uint32(_assetId)] += 1;
                assetIdToStarVisitedMapping[uint32(_assetId)] = starId;
                assetIdCurrentTripStartTimeMapping[uint32(_assetId)] = uint32(now);
            }
            
            return returnTime;
        }
    
    
        /*** PRIVATE FUNCTIONS ***/
    
        /// @dev  Safety check on _to address to prevent against an unexpected 0x0 default.
        function _addressNotNull(address _to) internal pure returns (bool) {
            return _to != address(0);
        }
    
        /// @dev  Claims and clears cargo -- ONLY INTERNAL
        function _claimOreAndClear (uint32 _assetId, uint8 _starId) internal {
            MEAHiddenLogic logic = MEAHiddenLogic(hiddenLogicAddress);
            uint256[12] memory _ores = logic.getAssetCollectedOreBallancesArray(_assetId);
            bool hasItems = false;
    
            for(uint8 i = 0; i < 12; i++) {
                if(_ores[i] > 0) {
                    collectedOreBalanceMapping[msg.sender][i] += uint32(_ores[i]);
                    hasItems = true;
                }
            }
    
            //Doesn't Let you Travel to empty stars but lets you collect
            if(hasItems == false && _starId > 0) {
                require(logic.getStarTotalSupply(_starId) > 0);
            }
    
            logic.emptyShipCargo(_assetId);
        }
    
        function _getShipInfo(uint256 _assetId) internal view returns (uint256 collectibleClass, address owner) {
            
            uint256 nulldata;
            bool nullbool;
            uint256 collectibleType;
    
            if(_assetId <= 3000) {
                CSCERC721 shipData = CSCERC721(cscERC721Address);
                (nulldata, nulldata, collectibleType, collectibleClass, nullbool, owner) = shipData.getCollectibleDetails(_assetId);
            } else {
                bytes32 nullstring;
                CSCFactoryERC721 shipFData = CSCFactoryERC721(cscFactoryERC721Address);
                (nulldata, nulldata, collectibleType, collectibleClass, nullstring, nullbool, owner) = shipFData.getCollectibleDetails(_assetId);
            }
    
        }
    
        
        
        
        
    }

    File 2 of 2: CSCPreSaleManager
    pragma solidity ^0.4.19;
    
    /* Adapted from strings.sol created by Nick Johnson <arachnid@notdot.net>
     * Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol
     * @title String & slice utility library for Solidity contracts.
     * @author Nick Johnson <arachnid@notdot.net>
     */
    library strings {
        
        struct slice {
            uint _len;
            uint _ptr;
        }
    
        /*
         * @dev Returns a slice containing the entire string.
         * @param self The string to make a slice from.
         * @return A newly allocated slice containing the entire string.
         */
        function toSlice(string self) internal pure returns (slice) {
            uint ptr;
            assembly {
                ptr := add(self, 0x20)
            }
            return slice(bytes(self).length, ptr);
        }
    
        function memcpy(uint dest, uint src, uint len) private pure {
            // Copy word-length chunks while possible
            for(; len >= 32; len -= 32) {
                assembly {
                    mstore(dest, mload(src))
                }
                dest += 32;
                src += 32;
            }
    
            // Copy remaining bytes
            uint mask = 256 ** (32 - len) - 1;
            assembly {
                let srcpart := and(mload(src), not(mask))
                let destpart := and(mload(dest), mask)
                mstore(dest, or(destpart, srcpart))
            }
        }
    
        
        function concat(slice self, slice other) internal returns (string) {
            var ret = new string(self._len + other._len);
            uint retptr;
            assembly { retptr := add(ret, 32) }
            memcpy(retptr, self._ptr, self._len);
            memcpy(retptr + self._len, other._ptr, other._len);
            return ret;
        }
    
        /*
         * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`.
         * @param self The slice to search.
         * @param needle The text to search for in `self`.
         * @return The number of occurrences of `needle` found in `self`.
         */
        function count(slice self, slice needle) internal returns (uint cnt) {
            uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len;
            while (ptr <= self._ptr + self._len) {
                cnt++;
                ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len;
            }
        }
    
        // Returns the memory address of the first byte of the first occurrence of
        // `needle` in `self`, or the first byte after `self` if not found.
        function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private returns (uint) {
            uint ptr;
            uint idx;
    
            if (needlelen <= selflen) {
                if (needlelen <= 32) {
                    // Optimized assembly for 68 gas per byte on short strings
                    assembly {
                        let mask := not(sub(exp(2, mul(8, sub(32, needlelen))), 1))
                        let needledata := and(mload(needleptr), mask)
                        let end := add(selfptr, sub(selflen, needlelen))
                        ptr := selfptr
                        loop:
                        jumpi(exit, eq(and(mload(ptr), mask), needledata))
                        ptr := add(ptr, 1)
                        jumpi(loop, lt(sub(ptr, 1), end))
                        ptr := add(selfptr, selflen)
                        exit:
                    }
                    return ptr;
                } else {
                    // For long needles, use hashing
                    bytes32 hash;
                    assembly { hash := sha3(needleptr, needlelen) }
                    ptr = selfptr;
                    for (idx = 0; idx <= selflen - needlelen; idx++) {
                        bytes32 testHash;
                        assembly { testHash := sha3(ptr, needlelen) }
                        if (hash == testHash)
                            return ptr;
                        ptr += 1;
                    }
                }
            }
            return selfptr + selflen;
        }
    
        /*
         * @dev Splits the slice, setting `self` to everything after the first
         *      occurrence of `needle`, and `token` to everything before it. If
         *      `needle` does not occur in `self`, `self` is set to the empty slice,
         *      and `token` is set to the entirety of `self`.
         * @param self The slice to split.
         * @param needle The text to search for in `self`.
         * @param token An output parameter to which the first token is written.
         * @return `token`.
         */
        function split(slice self, slice needle, slice token) internal returns (slice) {
            uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr);
            token._ptr = self._ptr;
            token._len = ptr - self._ptr;
            if (ptr == self._ptr + self._len) {
                // Not found
                self._len = 0;
            } else {
                self._len -= token._len + needle._len;
                self._ptr = ptr + needle._len;
            }
            return token;
        }
    
         /*
         * @dev Splits the slice, setting `self` to everything after the first
         *      occurrence of `needle`, and returning everything before it. If
         *      `needle` does not occur in `self`, `self` is set to the empty slice,
         *      and the entirety of `self` is returned.
         * @param self The slice to split.
         * @param needle The text to search for in `self`.
         * @return The part of `self` up to the first occurrence of `delim`.
         */
        function split(slice self, slice needle) internal returns (slice token) {
            split(self, needle, token);
        }
    
        /*
         * @dev Copies a slice to a new string.
         * @param self The slice to copy.
         * @return A newly allocated string containing the slice's text.
         */
        function toString(slice self) internal pure returns (string) {
            var ret = new string(self._len);
            uint retptr;
            assembly { retptr := add(ret, 32) }
    
            memcpy(retptr, self._ptr, self._len);
            return ret;
        }
    
    }
    
    /* Helper String Functions for Game Manager Contract
     * @title String Healpers
     * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
     */
    contract StringHelpers {
        using strings for *;
        
        function stringToBytes32(string memory source) internal returns (bytes32 result) {
            bytes memory tempEmptyStringTest = bytes(source);
            if (tempEmptyStringTest.length == 0) {
                return 0x0;
            }
        
            assembly {
                result := mload(add(source, 32))
            }
        }
    
        function bytes32ToString(bytes32 x) constant internal returns (string) {
            bytes memory bytesString = new bytes(32);
            uint charCount = 0;
            for (uint j = 0; j < 32; j++) {
                byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
                if (char != 0) {
                    bytesString[charCount] = char;
                    charCount++;
                }
            }
            bytes memory bytesStringTrimmed = new bytes(charCount);
            for (j = 0; j < charCount; j++) {
                bytesStringTrimmed[j] = bytesString[j];
            }
            return string(bytesStringTrimmed);
        }
    }
    
    /// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens
    /// @author Dieter Shirley <dete@axiomzen.co> (https://github.com/dete)
    contract ERC721 {
      // Required methods
      function balanceOf(address _owner) public view returns (uint256 balance);
      function ownerOf(uint256 _assetId) public view returns (address owner);
      function approve(address _to, uint256 _assetId) public;
      function transfer(address _to, uint256 _assetId) public;
      function transferFrom(address _from, address _to, uint256 _assetId) public;
      function implementsERC721() public pure returns (bool);
      function takeOwnership(uint256 _assetId) public;
      function totalSupply() public view returns (uint256 total);
    
      event Transfer(address indexed from, address indexed to, uint256 tokenId);
      event Approval(address indexed owner, address indexed approved, uint256 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);
    
      // ERC-165 Compatibility (https://github.com/ethereum/EIPs/issues/165)
      function supportsInterface(bytes4 _interfaceID) external view returns (bool);
    }
    
    /* Controls game play state and access rights for game functions
     * @title Operational Control
     * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
     * Inspired and adapted from contract created by OpenZeppelin
     * Ref: https://github.com/OpenZeppelin/zeppelin-solidity/
     */
    contract OperationalControl {
        // Facilitates access & control for the game.
        // Roles:
        //  -The Game Managers (Primary/Secondary): Has universal control of all game elements (No ability to withdraw)
        //  -The Banker: The Bank can withdraw funds and adjust fees / prices.
    
        /// @dev Emited when contract is upgraded
        event ContractUpgrade(address newContract);
    
        // The addresses of the accounts (or contracts) that can execute actions within each roles.
        address public gameManagerPrimary;
        address public gameManagerSecondary;
        address public bankManager;
    
        // @dev Keeps track whether the contract is paused. When that is true, most actions are blocked
        bool public paused = false;
    
        // @dev Keeps track whether the contract erroredOut. When that is true, most actions are blocked & refund can be claimed
        bool public error = false;
    
        /// @dev Operation modifiers for limiting access
        modifier onlyGameManager() {
            require(msg.sender == gameManagerPrimary || msg.sender == gameManagerSecondary);
            _;
        }
    
        modifier onlyBanker() {
            require(msg.sender == bankManager);
            _;
        }
    
        modifier anyOperator() {
            require(
                msg.sender == gameManagerPrimary ||
                msg.sender == gameManagerSecondary ||
                msg.sender == bankManager
            );
            _;
        }
    
        /// @dev Assigns a new address to act as the GM.
        function setPrimaryGameManager(address _newGM) external onlyGameManager {
            require(_newGM != address(0));
    
            gameManagerPrimary = _newGM;
        }
    
        /// @dev Assigns a new address to act as the GM.
        function setSecondaryGameManager(address _newGM) external onlyGameManager {
            require(_newGM != address(0));
    
            gameManagerSecondary = _newGM;
        }
    
        /// @dev Assigns a new address to act as the Banker.
        function setBanker(address _newBK) external onlyGameManager {
            require(_newBK != address(0));
    
            bankManager = _newBK;
        }
    
        /*** Pausable functionality adapted from OpenZeppelin ***/
    
        /// @dev Modifier to allow actions only when the contract IS NOT paused
        modifier whenNotPaused() {
            require(!paused);
            _;
        }
    
        /// @dev Modifier to allow actions only when the contract IS paused
        modifier whenPaused {
            require(paused);
            _;
        }
    
        /// @dev Modifier to allow actions only when the contract has Error
        modifier whenError {
            require(error);
            _;
        }
    
        /// @dev Called by any Operator role to pause the contract.
        /// Used only if a bug or exploit is discovered (Here to limit losses / damage)
        function pause() external onlyGameManager whenNotPaused {
            paused = true;
        }
    
        /// @dev Unpauses the smart contract. Can only be called by the Game Master
        /// @notice This is public rather than external so it can be called by derived contracts. 
        function unpause() public onlyGameManager whenPaused {
            // can't unpause if contract was upgraded
            paused = false;
        }
    
        /// @dev Unpauses the smart contract. Can only be called by the Game Master
        /// @notice This is public rather than external so it can be called by derived contracts. 
        function hasError() public onlyGameManager whenPaused {
            error = true;
        }
    
        /// @dev Unpauses the smart contract. Can only be called by the Game Master
        /// @notice This is public rather than external so it can be called by derived contracts. 
        function noError() public onlyGameManager whenPaused {
            error = false;
        }
    }
    
    contract CSCCollectibleBase is ERC721, OperationalControl, StringHelpers {
    
      /*** EVENTS ***/
      /// @dev The Created event is fired whenever a new collectible comes into existence.
      event CollectibleCreated(address owner, uint256 globalId, uint256 collectibleType, uint256 collectibleClass, uint256 sequenceId, bytes32 collectibleName, bool isRedeemed);
      event Transfer(address from, address to, uint256 shipId);
    
      /*** CONSTANTS ***/
    
      /// @notice Name and symbol of the non fungible token, as defined in ERC721.
      string public constant NAME = "CSCPreSaleShip";
      string public constant SYMBOL = "CSC";
      bytes4 constant InterfaceSignature_ERC165 = bytes4(keccak256('supportsInterface(bytes4)'));
      bytes4 constant InterfaceSignature_ERC721 =
            bytes4(keccak256('name()')) ^
            bytes4(keccak256('symbol()')) ^
            bytes4(keccak256('totalSupply()')) ^
            bytes4(keccak256('balanceOf(address)')) ^
            bytes4(keccak256('ownerOf(uint256)')) ^
            bytes4(keccak256('approve(address,uint256)')) ^
            bytes4(keccak256('transfer(address,uint256)')) ^
            bytes4(keccak256('transferFrom(address,address,uint256)')) ^
            bytes4(keccak256('tokensOfOwner(address)')) ^
            bytes4(keccak256('tokenMetadata(uint256,string)'));
    
      /// @dev CSC Pre Sale Struct, having details of the ship
      struct CSCPreSaleItem {
    
        /// @dev asset ID i..e Local Index
        uint256 assetId;
    
        /// @dev name of the collectible stored in bytes
        bytes32 collectibleName;
    
        /// @dev Timestamp when bought
        uint256 boughtTimestamp;
    
        /// @dev Collectible Types (Voucher/Ship)
        /// can be 0 - Voucher, 1 - Ship
        uint256 collectibleType;
    
        /// @dev Collectible Class (1 - Prometheus, 2 - Crosair, 3 - Intrepid)
        uint256 collectibleClass;
    
        // @dev owner address
        address owner;
    
        // @dev redeeme flag (to help whether it got redeemed or not)
        bool isRedeemed;
      }
      
      // @dev Mapping containing the reference to all CSC PreSaleItem
      //mapping (uint256 => CSCPreSaleItem[]) public indexToPreSaleItem;
    
      // @dev array of CSCPreSaleItem type holding information on the Ships
      CSCPreSaleItem[] allPreSaleItems;
    
      // Max Count for Voucher(s), Prometheus, Crosair & Intrepid Ships
      uint256 public constant PROMETHEUS_SHIP_LIMIT = 300;
      uint256 public constant INTREPID_SHIP_LIMIT = 1500;
      uint256 public constant CROSAIR_SHIP_LIMIT = 600;
      uint256 public constant PROMETHEUS_VOUCHER_LIMIT = 100;
      uint256 public constant INTREPID_VOUCHER_LIMIT = 300;
      uint256 public constant CROSAIR_VOUCHER_LIMIT = 200;
    
      // Variable to keep a count of Prometheus/Intrepid/Crosair Minted
      uint256 public prometheusShipMinted;
      uint256 public intrepidShipMinted;
      uint256 public crosairShipMinted;
      uint256 public prometheusVouchersMinted;
      uint256 public intrepidVouchersMinted;
      uint256 public crosairVouchersMinted;
    
      // @dev mapping which holds all the possible addresses which are allowed to interact with the contract
      mapping (address => bool) approvedAddressList;
    
      // @dev mapping holds the preSaleItem -> owner details
      mapping (uint256 => address) public preSaleItemIndexToOwner;
    
      // @dev A mapping from owner address to count of tokens that address owns.
      //  Used internally inside balanceOf() to resolve ownership count.
      mapping (address => uint256) private ownershipTokenCount;
    
      /// @dev A mapping from preSaleItem to an address that has been approved to call
      ///  transferFrom(). Each Ship can only have one approved address for transfer
      ///  at any time. A zero value means no approval is outstanding.
      mapping (uint256 => address) public preSaleItemIndexToApproved;
    
      /// @dev A mapping of preSaleItem Type to Type Sequence Number to Collectible
      /// 0 - Voucher
      /// 1 - Prometheus
      /// 2 - Crosair
      /// 3 - Intrepid
      mapping (uint256 => mapping (uint256 => mapping ( uint256 => uint256 ) ) ) public preSaleItemTypeToSequenceIdToCollectible;
    
      /// @dev A mapping from Pre Sale Item Type IDs to the Sequqence Number .
      /// 0 - Voucher
      /// 1 - Prometheus
      /// 2 - Crosair
      /// 3 - Intrepid
      mapping (uint256 => mapping ( uint256 => uint256 ) ) public preSaleItemTypeToCollectibleCount;
    
      /// @notice Introspection interface as per ERC-165 (https://github.com/ethereum/EIPs/issues/165).
      ///  Returns true for any standardized interfaces implemented by this contract. We implement
      ///  ERC-165 (obviously!) and ERC-721.
      function supportsInterface(bytes4 _interfaceID) external view returns (bool)
      {
          // DEBUG ONLY
          //require((InterfaceSignature_ERC165 == 0x01ffc9a7) && (InterfaceSignature_ERC721 == 0x9a20483d));
          return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721));
      }
    
      function getCollectibleDetails(uint256 _assetId) external view returns(uint256 assetId, uint256 sequenceId, uint256 collectibleType, uint256 collectibleClass, bool isRedeemed, address owner) {
        CSCPreSaleItem memory _Obj = allPreSaleItems[_assetId];
        assetId = _assetId;
        sequenceId = _Obj.assetId;
        collectibleType = _Obj.collectibleType;
        collectibleClass = _Obj.collectibleClass;
        owner = _Obj.owner;
        isRedeemed = _Obj.isRedeemed;
      }
      
      /*** PUBLIC FUNCTIONS ***/
      /// @notice Grant another address the right to transfer token via takeOwnership() and transferFrom().
      /// @param _to The address to be granted transfer approval. Pass address(0) to
      ///  clear all approvals.
      /// @param _assetId The ID of the Token that can be transferred if this call succeeds.
      /// @dev Required for ERC-721 compliance.
      function approve(address _to, uint256 _assetId) public {
        // Caller must own token.
        require(_owns(msg.sender, _assetId));
        preSaleItemIndexToApproved[_assetId] = _to;
    
        Approval(msg.sender, _to, _assetId);
      }
    
      /// For querying balance of a particular account
      /// @param _owner The address for balance query
      /// @dev Required for ERC-721 compliance.
      function balanceOf(address _owner) public view returns (uint256 balance) {
        return ownershipTokenCount[_owner];
      }
    
      function implementsERC721() public pure returns (bool) {
        return true;
      }
    
      /// For querying owner of token
      /// @param _assetId The tokenID for owner inquiry
      /// @dev Required for ERC-721 compliance.
      function ownerOf(uint256 _assetId) public view returns (address owner) {
        owner = preSaleItemIndexToOwner[_assetId];
        require(owner != address(0));
      }
    
      /// @dev Required for ERC-721 compliance.
      function symbol() public pure returns (string) {
        return SYMBOL;
      }
    
      /// @notice Allow pre-approved user to take ownership of a token
      /// @param _assetId The ID of the Token that can be transferred if this call succeeds.
      /// @dev Required for ERC-721 compliance.
      function takeOwnership(uint256 _assetId) public {
        address newOwner = msg.sender;
        address oldOwner = preSaleItemIndexToOwner[_assetId];
    
        // Safety check to prevent against an unexpected 0x0 default.
        require(_addressNotNull(newOwner));
    
        // Making sure transfer is approved
        require(_approved(newOwner, _assetId));
    
        _transfer(oldOwner, newOwner, _assetId);
      }
    
      /// @param _owner The owner whose ships tokens we are interested in.
      /// @dev This method MUST NEVER be called by smart contract code. First, it's fairly
      ///  expensive (it walks the entire CSCShips array looking for emojis belonging to owner),
      ///  but it also returns a dynamic array, which is only supported for web3 calls, and
      ///  not contract-to-contract calls.
      function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
        uint256 tokenCount = balanceOf(_owner);
    
        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 totalShips = totalSupply() + 1;
            uint256 resultIndex = 0;
    
            // We count on the fact that all CSC Ship Collectible have IDs starting at 0 and increasing
            // sequentially up to the total count.
            uint256 _assetId;
    
            for (_assetId = 0; _assetId < totalShips; _assetId++) {
                if (preSaleItemIndexToOwner[_assetId] == _owner) {
                    result[resultIndex] = _assetId;
                    resultIndex++;
                }
            }
    
            return result;
        }
      }
    
      /// For querying totalSupply of token
      /// @dev Required for ERC-721 compliance.
      function totalSupply() public view returns (uint256 total) {
        return allPreSaleItems.length - 1; //Removed 0 index
      }
    
      /// Owner initates the transfer of the token to another account
      /// @param _to The address for the token to be transferred to.
      /// @param _assetId The ID of the Token that can be transferred if this call succeeds.
      /// @dev Required for ERC-721 compliance.
      function transfer(address _to, uint256 _assetId) public {
        require(_addressNotNull(_to));
        require(_owns(msg.sender, _assetId));
    
        _transfer(msg.sender, _to, _assetId);
      }
    
      /// Third-party initiates transfer of token from address _from to address _to
      /// @param _from The address for the token to be transferred from.
      /// @param _to The address for the token to be transferred to.
      /// @param _assetId The ID of the Token that can be transferred if this call succeeds.
      /// @dev Required for ERC-721 compliance.
      function transferFrom(address _from, address _to, uint256 _assetId) public {
        require(_owns(_from, _assetId));
        require(_approved(_to, _assetId));
        require(_addressNotNull(_to));
    
        _transfer(_from, _to, _assetId);
      }
    
      /*** PRIVATE FUNCTIONS ***/
      /// @dev  Safety check on _to address to prevent against an unexpected 0x0 default.
      function _addressNotNull(address _to) internal pure returns (bool) {
        return _to != address(0);
      }
    
      /// @dev  For checking approval of transfer for address _to
      function _approved(address _to, uint256 _assetId) internal view returns (bool) {
        return preSaleItemIndexToApproved[_assetId] == _to;
      }
    
      /// @dev For creating CSC Collectible
      function _createCollectible(bytes32 _collectibleName, uint256 _collectibleType, uint256 _collectibleClass) internal returns(uint256) {
        uint256 _sequenceId = uint256(preSaleItemTypeToCollectibleCount[_collectibleType][_collectibleClass]) + 1;
    
        // These requires are not strictly necessary, our calling code should make
        // sure that these conditions are never broken.
        require(_sequenceId == uint256(uint32(_sequenceId)));
        
        CSCPreSaleItem memory _collectibleObj = CSCPreSaleItem(
          _sequenceId,
          _collectibleName,
          0,
          _collectibleType,
          _collectibleClass,
          address(0),
          false
        );
    
        uint256 newCollectibleId = allPreSaleItems.push(_collectibleObj) - 1;
        
        preSaleItemTypeToSequenceIdToCollectible[_collectibleType][_collectibleClass][_sequenceId] = newCollectibleId;
        preSaleItemTypeToCollectibleCount[_collectibleType][_collectibleClass] = _sequenceId;
    
        // emit Created event
        // CollectibleCreated(address owner, uint256 globalId, uint256 collectibleType, uint256 collectibleClass, uint256 sequenceId, bytes32[6] attributes, bool isRedeemed);
        CollectibleCreated(address(this), newCollectibleId, _collectibleType, _collectibleClass, _sequenceId, _collectibleObj.collectibleName, false);
        
        // This will assign ownership, and also emit the Transfer event as
        // per ERC721 draft
        _transfer(address(0), address(this), newCollectibleId);
        
        return newCollectibleId;
      }
    
      /// Check for token ownership
      function _owns(address claimant, uint256 _assetId) internal view returns (bool) {
        return claimant == preSaleItemIndexToOwner[_assetId];
      }
    
      /// @dev Assigns ownership of a specific Emoji to an address.
      function _transfer(address _from, address _to, uint256 _assetId) internal {
        // Updating the owner details of the ship
        CSCPreSaleItem memory _shipObj = allPreSaleItems[_assetId];
        _shipObj.owner = _to;
        allPreSaleItems[_assetId] = _shipObj;
    
        // Since the number of emojis is capped to 2^32 we can't overflow this
        ownershipTokenCount[_to]++;
    
        //transfer ownership
        preSaleItemIndexToOwner[_assetId] = _to;
    
        // When creating new emojis _from is 0x0, but we can't account that address.
        if (_from != address(0)) {
          ownershipTokenCount[_from]--;
          // clear any previously approved ownership exchange
          delete preSaleItemIndexToApproved[_assetId];
        }
    
        // Emit the transfer event.
        Transfer(_from, _to, _assetId);
      }
    
      /// @dev Checks if a given address currently has transferApproval for a particular CSCPreSaleItem.
      /// 0 is a valid value as it will be the starter
      function _approvedFor(address _claimant, uint256 _assetId) internal view returns (bool) {
          return preSaleItemIndexToApproved[_assetId] == _claimant;
      }
    
      function _getCollectibleDetails (uint256 _assetId) internal view returns(CSCPreSaleItem) {
        CSCPreSaleItem storage _Obj = allPreSaleItems[_assetId];
        return _Obj;
      }
    
      /// @dev Helps in fetching the attributes of the ship depending on the ship
      /// assetId : The actual ERC721 Asset ID
      /// sequenceId : Index w.r.t Ship type
      function getShipDetails(uint256 _sequenceId, uint256 _shipClass) external view returns (
        uint256 assetId,
        uint256 sequenceId,
        string shipName,
        uint256 collectibleClass,
        uint256 boughtTimestamp,
        address owner
        ) {  
        uint256 _assetId = preSaleItemTypeToSequenceIdToCollectible[1][_shipClass][_sequenceId];
    
        CSCPreSaleItem storage _collectibleObj = allPreSaleItems[_assetId];
        require(_collectibleObj.collectibleType == 1);
    
        assetId = _assetId;
        sequenceId = _sequenceId;
        shipName = bytes32ToString(_collectibleObj.collectibleName);
        collectibleClass = _collectibleObj.collectibleClass;
        boughtTimestamp = _collectibleObj.boughtTimestamp;
        owner = _collectibleObj.owner;
      }
    
      /// @dev Helps in fetching information regarding a Voucher
      /// assetId : The actual ERC721 Asset ID
      /// sequenceId : Index w.r.t Voucher Type
      function getVoucherDetails(uint256 _sequenceId, uint256 _voucherClass) external view returns (
        uint256 assetId,
        uint256 sequenceId,
        uint256 boughtTimestamp,
        uint256 voucherClass,
        address owner
        ) {
        uint256 _assetId = preSaleItemTypeToSequenceIdToCollectible[0][_voucherClass][_sequenceId];
    
        CSCPreSaleItem storage _collectibleObj = allPreSaleItems[_assetId];
        require(_collectibleObj.collectibleType == 0);
    
        assetId = _assetId;
        sequenceId = _sequenceId;
        boughtTimestamp = _collectibleObj.boughtTimestamp;
        voucherClass = _collectibleObj.collectibleClass;
        owner = _collectibleObj.owner;
      }
    
      function _isActive(uint256 _assetId) internal returns(bool) {
        CSCPreSaleItem memory _Obj = allPreSaleItems[_assetId];
        return (_Obj.boughtTimestamp == 0);
      }
    }
    
    /* Lucid Sight, Inc. ERC-721 CSC Collectilbe Sale Contract. 
     * @title CSCCollectibleSale
     * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
     */
    contract CSCCollectibleSale is CSCCollectibleBase {
      event CollectibleBought (uint256 _assetId, address owner);
      event PriceUpdated (uint256 collectibleClass, uint256 newPrice, uint256 oldPrice);
    
      //  SHIP DATATYPES & CONSTANTS
      // @dev ship Prices & price cap
      uint256 public PROMETHEUS_SHIP_PRICE = 0.25 ether;
      uint256 public INTREPID_SHIP_PRICE = 0.005 ether;
      uint256 public CROSAIR_SHIP_PRICE = 0.1 ether;
    
      uint256 public constant PROMETHEUS_MAX_PRICE = 0.85 ether;
      uint256 public constant INTREPID_MAX_PRICE = 0.25 ether;
      uint256 public constant CROSAIR_MAX_PRICE = 0.5 ether;
    
      uint256 public constant PROMETHEUS_PRICE_INCREMENT = 0.05 ether;
      uint256 public constant INTREPID_PRICE_INCREMENT = 0.002 ether;
      uint256 public constant CROSAIR_PRICE_INCREMENT = 0.01 ether;
    
      uint256 public constant PROMETHEUS_PRICE_THRESHOLD = 0.85 ether;
      uint256 public constant INTREPID_PRICE_THRESHOLD = 0.25 ether;
      uint256 public constant CROSAIR_PRICE_THRESHOLD = 0.5 ether;
    
      uint256 public prometheusSoldCount;
      uint256 public intrepidSoldCount;
      uint256 public crosairSoldCount;
    
      //  VOUCHER DATATYPES & CONSTANTS
      uint256 public PROMETHEUS_VOUCHER_PRICE = 0.75 ether;
      uint256 public INTREPID_VOUCHER_PRICE = 0.2 ether;
      uint256 public CROSAIR_VOUCHER_PRICE = 0.35 ether;
    
      uint256 public prometheusVoucherSoldCount;
      uint256 public crosairVoucherSoldCount;
      uint256 public intrepidVoucherSoldCount;
      
      /// @dev Mapping created store the amount of value a wallet address used to buy assets
      mapping(address => uint256) addressToValue;
    
      /// @dev Mapping to holde the balance of each address, i.e. addrs -> collectibleType -> collectibleClass -> balance
      mapping(address => mapping(uint256 => mapping (uint256 => uint256))) addressToCollectibleTypeBalance;
    
      function _bid(uint256 _assetId, uint256 _price,uint256 _collectibleType,uint256 _collectibleClass, address _buyer) internal {
        CSCPreSaleItem memory _Obj = allPreSaleItems[_assetId];
    
        if(_collectibleType == 1 && _collectibleClass == 1) {
          require(_price == PROMETHEUS_SHIP_PRICE);
          _Obj.owner = _buyer;
          _Obj.boughtTimestamp = now;
    
          addressToValue[_buyer] += _price;
    
          prometheusSoldCount++;
          if(prometheusSoldCount % 10 == 0){
            if(PROMETHEUS_SHIP_PRICE < PROMETHEUS_PRICE_THRESHOLD){
              PROMETHEUS_SHIP_PRICE +=  PROMETHEUS_PRICE_INCREMENT;
            }
          }
        }
    
        if(_collectibleType == 1 && _collectibleClass == 2) {
          require(_price == CROSAIR_SHIP_PRICE);
          _Obj.owner = _buyer;
          _Obj.boughtTimestamp = now;
    
          addressToValue[_buyer] += _price;
    
          crosairSoldCount++;
          if(crosairSoldCount % 10 == 0){
            if(CROSAIR_SHIP_PRICE < CROSAIR_PRICE_THRESHOLD){
              CROSAIR_SHIP_PRICE += CROSAIR_PRICE_INCREMENT;
            }
          }
        }
    
        if(_collectibleType == 1 && _collectibleClass == 3) {
          require(_price == INTREPID_SHIP_PRICE);
          _Obj.owner = _buyer;
          _Obj.boughtTimestamp = now;
    
          addressToValue[_buyer] += _price;
    
          intrepidSoldCount++;
          if(intrepidSoldCount % 10 == 0){
            if(INTREPID_SHIP_PRICE < INTREPID_PRICE_THRESHOLD){
              INTREPID_SHIP_PRICE += INTREPID_PRICE_INCREMENT;
            }
          }
        }
    
        if(_collectibleType == 0 &&_collectibleClass == 1) {
            require(_price == PROMETHEUS_VOUCHER_PRICE);
            _Obj.owner = _buyer;
            _Obj.boughtTimestamp = now;
    
            addressToValue[_buyer] += _price;
    
            prometheusVoucherSoldCount++;
          }
    
          if(_collectibleType == 0 && _collectibleClass == 2) {
            require(_price == CROSAIR_VOUCHER_PRICE);
            _Obj.owner = _buyer;
            _Obj.boughtTimestamp = now;
    
            addressToValue[_buyer] += _price;
    
            crosairVoucherSoldCount++;
          }
          
          if(_collectibleType == 0 && _collectibleClass == 3) {
            require(_price == INTREPID_VOUCHER_PRICE);
            _Obj.owner = _buyer;
            _Obj.boughtTimestamp = now;
    
            addressToValue[_buyer] += _price;
    
            intrepidVoucherSoldCount++;
          }
    
        addressToCollectibleTypeBalance[_buyer][_collectibleType][_collectibleClass]++;
    
        CollectibleBought(_assetId, _buyer);
      }
    
      function getCollectibleTypeBalance(address _owner, uint256 _collectibleType, uint256 _collectibleClass) external view returns(uint256) {
        require(_owner != address(0));
        return addressToCollectibleTypeBalance[_owner][_collectibleType][_collectibleClass];
      }
    
      function getCollectiblePrice(uint256 _collectibleType, uint256 _collectibleClass) external view returns(uint256 _price){
    
        // For Ships
        if(_collectibleType == 1 && _collectibleClass == 1) {
          return PROMETHEUS_SHIP_PRICE;
        }
    
        if(_collectibleType == 1 && _collectibleClass == 2) {
          return CROSAIR_SHIP_PRICE;
        }
    
        if(_collectibleType == 1 && _collectibleClass == 3) {
          return INTREPID_SHIP_PRICE;
        }
    
        // For Vouchers
        if(_collectibleType == 0 && _collectibleClass == 1) {
          return PROMETHEUS_VOUCHER_PRICE;
        }
    
        if(_collectibleType == 0 && _collectibleClass == 2) {
          return CROSAIR_VOUCHER_PRICE;
        }
    
        if(_collectibleType == 0 && _collectibleClass == 3) {
          return INTREPID_VOUCHER_PRICE;
        }
      }
    }
    
    /* Lucid Sight, Inc. ERC-721 Collectibles. 
     * @title LSNFT - Lucid Sight, Inc. Non-Fungible Token
     * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
     */
    contract CSCPreSaleManager is CSCCollectibleSale {
      event RefundClaimed(address owner, uint256 refundValue);
    
      // Ship Names
      string private constant prometheusShipName = "Vulcan Harvester";
      string private constant crosairShipName = "Phoenix Cruiser";
      string private constant intrepidShipName = "Reaper Interceptor";
    
      bool CSCPreSaleInit = false;
    
      /// @dev Constructor creates a reference to the NFT (ERC721) ownership contract
      function CSCPreSaleManager() public {
          require(msg.sender != address(0));
          paused = true;
          error = false;
          gameManagerPrimary = msg.sender;
      }
    
      function addToApprovedAddress (address _newAddr) onlyGameManager {
        require(_newAddr != address(0));
        require(!approvedAddressList[_newAddr]);
        approvedAddressList[_newAddr] = true;
      }
    
      function removeFromApprovedAddress (address _newAddr) onlyGameManager {
        require(_newAddr != address(0));
        require(approvedAddressList[_newAddr]);
        approvedAddressList[_newAddr] = false;
      }
    
      function() external payable {
      }
    
      /// @dev Bid Function which call the interncal bid function
      /// after doing all the pre-checks required to initiate a bid
      function bid(uint256 _collectibleType, uint256 _collectibleClass) external payable {
        require(msg.sender != address(0));
        require(msg.sender != address(this));
    
        require(_collectibleType >= 0 && _collectibleType <= 1);
    
        require(_isActive(_assetId));
    
        bytes32 collectibleName;
    
        if(_collectibleType == 0){
          collectibleName = bytes32("NoNameForVoucher");
          if(_collectibleClass == 1){
            require(prometheusVouchersMinted < PROMETHEUS_VOUCHER_LIMIT);
            collectibleName = stringToBytes32(prometheusShipName);
            prometheusVouchersMinted++;
          }
          
          if(_collectibleClass == 2){
            require(crosairVouchersMinted < CROSAIR_VOUCHER_LIMIT);
            crosairVouchersMinted++;
          }
    
          if(_collectibleClass == 3){
            require(intrepidVoucherSoldCount < INTREPID_VOUCHER_LIMIT);
            intrepidVouchersMinted++;
          }
        }
    
        if(_collectibleType == 1){
          if(_collectibleClass == 1){
            require(prometheusShipMinted < PROMETHEUS_SHIP_LIMIT);
            collectibleName = stringToBytes32(prometheusShipName);
            prometheusShipMinted++;
          }
          
          if(_collectibleClass == 2){
            require(crosairShipMinted < CROSAIR_VOUCHER_LIMIT);
            collectibleName = stringToBytes32(crosairShipName);
            crosairShipMinted++;
          }
    
          if(_collectibleClass == 3){
            require(intrepidShipMinted < INTREPID_SHIP_LIMIT);
            collectibleName = stringToBytes32(intrepidShipName);
            intrepidShipMinted++;
          }
        }
    
        uint256 _assetId = _createCollectible(collectibleName, _collectibleType, _collectibleClass); 
    
        CSCPreSaleItem memory _Obj = allPreSaleItems[_assetId];
    
        _bid(_assetId, msg.value, _Obj.collectibleType, _Obj.collectibleClass, msg.sender);
        
        _transfer(address(this), msg.sender, _assetId);
      }
    
      /// @dev Bid Function which call the interncal bid function
      /// after doing all the pre-checks required to initiate a bid
      function createReferralGiveAways(uint256 _collectibleType, uint256 _collectibleClass, address _toAddress) onlyGameManager external {
        require(msg.sender != address(0));
        require(msg.sender != address(this));
    
        require(_collectibleType >= 0 && _collectibleType <= 1);
    
        bytes32 collectibleName;
    
        if(_collectibleType == 0){
          collectibleName = bytes32("ReferralGiveAwayVoucher");
          if(_collectibleClass == 1){
            collectibleName = stringToBytes32(prometheusShipName);
          }
          
          if(_collectibleClass == 2){
            crosairVouchersMinted++;
          }
    
          if(_collectibleClass == 3){
            intrepidVouchersMinted++;
          }
        }
    
        if(_collectibleType == 1){
          if(_collectibleClass == 1){
            collectibleName = stringToBytes32(prometheusShipName);
          }
          
          if(_collectibleClass == 2){
            collectibleName = stringToBytes32(crosairShipName);
          }
    
          if(_collectibleClass == 3){
            collectibleName = stringToBytes32(intrepidShipName);
          }
        }
    
        uint256 _assetId = _createCollectible(collectibleName, _collectibleType, _collectibleClass); 
    
        CSCPreSaleItem memory _Obj = allPreSaleItems[_assetId];
        
        _transfer(address(this), _toAddress, _assetId);
      }
    
      /// @dev Override unpause so it requires all external contract addresses
      ///  to be set before contract can be unpaused. Also, we can't have
      ///  newContractAddress set either, because then the contract was upgraded.
      /// @notice This is public rather than external so we can call super.unpause
      ///  without using an expensive CALL.
      function unpause() public onlyGameManager whenPaused {
          // Actually unpause the contract.
          super.unpause();
      }
    
      /// @dev Remove all Ether from the contract, which is the owner's cuts
      ///  as well as any Ether sent directly to the contract address.
      ///  Always transfers to the NFT (ERC721) contract, but can be called either by
      ///  the owner or the NFT (ERC721) contract.
      function withdrawBalance() onlyBanker {
          // We are using this boolean method to make sure that even if one fails it will still work
          bankManager.transfer(this.balance);
      }
    
      function claimRefund(address _ownerAddress) whenError {
        uint256 refundValue = addressToValue[_ownerAddress];
        addressToValue[_ownerAddress] = 0;
    
        _ownerAddress.transfer(refundValue);
        RefundClaimed(_ownerAddress, refundValue);
      }
      
      function preSaleInit() onlyGameManager {
        require(!CSCPreSaleInit);
        require(allPreSaleItems.length == 0);
          
        CSCPreSaleInit = true;
    
        //Fill in index 0 to null requests
        CSCPreSaleItem memory _Obj = CSCPreSaleItem(0, stringToBytes32("DummyAsset"), 0, 0, 0, address(this), true);
        allPreSaleItems.push(_Obj);
      }
    
      function isRedeemed(uint256 _assetId) {
        require(approvedAddressList[msg.sender]);
    
        CSCPreSaleItem memory _Obj = allPreSaleItems[_assetId];
        _Obj.isRedeemed = true;
    
        allPreSaleItems[_assetId] = _Obj;
      }
    }