// Contract: NPCs
// Target function: function delegate(address delegatee) external
 Staff",
        "Thimble",
        "Needle",
        "Baby shoes, never worn",
        "Kava",
        "Kombucha",
        "Blocks",
        "Chains",
        "Immutable Ledger",
        "Instrument of Mining",
        "Instrument of Staking",
        "Grass sword",
        "Miniature Cloth Blanket of Sneezing",
        "Paper Scroll of Wiping",
        "Scabbard",
        "Broken hilt",
        "Celestial map",
        "Castle map",
        "Town map",
        "Healing salve",
        "Icefire Cream of Ache-reduction",
        "Pufferfish frozen in time",
        "Oats",
        "Hand puppets",
        "Marionettes",
        "Cinnabar ore",
        "Ink",
        "Pen",
        "False thumb",
        "Nightshade",
        "Colored puzzle cube",
        "Frankincense",
        "Pyrite",
        "Myrrh",
        "Hardtack",
        "Duff",
        "Salt pork",
        "Salt beef",
        "Pound of flesh"
    ];
    
    string[] pockets = [
        "Dirt",
        "Sand",
        "Cotton ball",
        "Pebble",
        "Fork",
        "Butter knife",
        "Spoon",
        "Fingernail",
        "Toenail",
        "Lint",
        "Wood shavings",
        "Apple core",
        "Nuts",
        "Moonshine",
        "Ale",
        "Waterskin",
        "Wineskin",
        "Pottery shards",
        "Barley",
        "Coal",
        "Bento box (empty)",
        "Condom (used)",
        "Condom (new)",
        "Birdseed",
        "Packet of salt",
        "Handknitted scarf",
        "Triangle (musical instrument)",
        "Checkers set",
        "Blank scroll",
        "Quill and ink",
        "Sea glass",
        "Seaweed",
        "Good Luck Rock (magical item)",
        "Pearl of Power",
        "Vial (truth serum - 1 use)",
        "Vial (viper poison - 1 use)",
        "Vial (pufferfish poison - 1 use)", 
        "Vial (good luck draught - 1 use)",
        "Vial (healing potion - 1 use)",
        "1 strand unicorn hair",
        "Vial (chutney - 1 use)",
        "Bezoar",
        "Magical parchment",
        "Vial (empty)",
        "Egg (rotten)",
        "Egg (raw)",
        "Rusted coin",
        "Jar ('dragonbreath')",
        "Magic perfume",
        "Magic cologne",
        "Wizard's honor badge",
        "Hypnosis necklace",
        "Pamphlet ('Guide to the Cataclysm')",
        "Pamphlet ('Guide to the Apocalypse')",
        "Pamphlet ('Guide to Damnation')",
        "Pamphlet ('Guide to Pandemonium')",
        "Pamphlet ('Guide to the Divine')",
        "Pamphlet ('Guide to the Twins')",
        "Crumpled note",
        "Abacus",
        "Magical abacus (calculates on voice command)",
        "Manual ('Destoning golems')",
        "Manual ('Disassembling chimeras')",
        "Manual ('Antidotes to viper poison')",
        "Manual ('Standing on giants' shoulders')",
        "Manual ('Disarming krakens')",
        "Manual ('Dowsing phoenix flame')",
        "Manual ('Beautifying ogres')",
        "Manual ('Socializing trolls')",
        "Manual ('Friend-zoning ghosts')",
        "Pamphlet ('Guide to the Holy')",
        "Manual ('Tinder for soulmating')",
        "Manual ('How to wear bees')",
        "Manual ('How to breathe fire')",
        "Pamphlet ('Guide to the Titans')",
        "Manual ('How to breathe underwater')",
        "Pamphlet ('Guide to the Plague')",
        "Manual ('How to speak to plague deniers')",
        "Manual ('So your son is into necromancy...')",
        "Nail",
        "Lumber",
        "Bricks",
        "Slab of stone",
        "Miniature bridge of hair",
        "Orb of Firestarting (5 uses)",
        "Orb of Black Light (5 uses)",
        "Orb of White Light (5 uses)",
        "Ground flaxseed",
        "Ground coffee beans",
        "Loose tea leaves",
        "Gordian knot",
        "Miniature bored ape",
        "Miniature moon cat",
        "Miniature pengu",
        "Miniature cryptic punk",
        "Pamphlet ('Guide to Loot (for Adventurers)')",
        "Miniature cryptic kitten",
        "Sixty-sided die",
        "Mutation serum",
        "Miniature degenerate ape",
        "Manual ('So your life is runed...')",
        "More pockets",
        "Drawing of first love",
        "Drawing of mortal enemy"
    ];
    
    string[] love = [
        "Little sister",
        "Big brother",
        "Spouse",
        "Self",
        "Sunsets",
        "Sunrise",
        "Sleeping",
        "Fire",
        "Dogs",
        "Cats",
        "Conversation",
        "Starry nights",
        "Trees",
        "Clean clothes",
        "Baths",
        "Dancing",
        "Candles",
        "Whittling",
        "Sheep",
        "Walks on the beach",
        "Karaoke",
        "Watermelon",
        "Strawberries",
        "Lying",
        "Daughter",
        "Son", 
        "Roast chicken",
        "Hiking",
        "Beer",
        "Watering plants",
        "Talking to plants",
        "Talking to animals", 
        "Hugging trees",
        "Handstands",
        "Magic tricks",
        "Quiet nights with a loved one",
        "Days that end in 'y'",
        "Purposefully acting stupid",
        "Numbers associated with evil",
        "Numbers associated with good",
        "Making sleepy grass go to sleep",
        "Roaring good yawns",
        "Sneezing spells",
        "Micturating spells",
        "Expectorating spells",
        "Evacuation spells",
        "To be walked like a dog",
        "Solving riddles",
        "Prostheses",
        "Word play",
        "Creating riddles",
        "Improv",
        "Puppeteering",
        "Competitive arm wrestling",
        "Inspirational speeches",
        "Playing the lute",
        "Praying"
    ];
    
    string[] secret = [
        "Plays with fire",
        "Manifests daily",
        "Too many regrets",
        "Faking it",
        "Feels misunderstood a lot",
        "Tummy ache",
        "False teeth",
        "Hard of hearing",
        "Left handed",
        "Incest",
        "Disappointed in self",
        "Doesn't feel enough",
        "Unlovable",
        "Blister",
        "Hates new haircut",
        "Had an affair",
        "Retired armwrestler",
        "Chokes on water",
        "Is never quite hydrated",
        "Hungry but just ate",
        "Alcoholic",
        "Hates the king",
        "In love with the queen",
        "Face is a very well-made mask",
        "Forgot to put skeletons back in closet",
        "Has nonfunctional gills",
        "Believes in romance",
        "Hated by dogs",
        "Hated by children",
        "Can't swim",
        "Body odor",
        "Was a bully",
        "Was bullied",
        "Teacher's pet",
        "Achingly normal",
        "Not actually principled",
        "Dies hard",
        "Dies harder",
        "Dies hard with a vengeance",
        "Lives free",
        "Does simply walk into volcanic realms",
        "Directed a wayward carriage to kill five people instead of one",
        "Is contemplating whether the cat in the box over there exists or not or both",
        "Obsessed with black magic",
        "Has engaged in light cannibalism",
        "Has undergone medical experimentation",
        "Possessed by the spirit of best friend's uncle",
        "Possesses psychic abilities",
        "Comes from an alternate dimension",
        "Comes from another time period"
    ];

    string[] habitus = [
        "Upright",
        "Hunched",
        "Flexible",
        "Confident",
        "Stiff",
        "Weak",
        "Strong",
        "Quiet",
        "Battle hardened",
        "Duty bound",
        "Pained",
        "Slow",
        "Fumbling",
        "Knavish",
        "Bent",
        "Gawky",
        "Fat",
        "Clumsy",
        "Elegant",
        "Striving",
        "Dangerous",
        "Intimidating",
        "Huge",
        "Tiny",
        "Miniature",
        "Miniscule",
        "Low class",
        "Muscular",
        "Fit",
        "Slender",
        "Approachable",
        "Unapproachable",
        "Leering",
        "Peering",
        "Cryptic",
        "Flailing",
        "Wild",
        "Uncontrollable",
        "Feral",
        "Untamed",
        "Tame",
        "Civilized",
        "Mourning",
        "Enraged",
        "Frenetic",
        "Fanatic",
        "Lonely",
        "Asleep",
        "Hyped",
        "Gaunt",
        "Thin",
        "Thick",
        "Thick-headed",
        "Thin-skinned",
        "Manipulable",
        "Malleable",
        "Easily convinced",
        "Unintelligent",
        "Of the South",
        "Of the North",
        "Inhuman",
        "Unnatural",
        "Diseased",
        "Standing against tree",
        "Sitting on grass",
        "Micturating",
        "Scanning the horizon",
        "Arms crossed",
        "Hands on hips",
        "Right hip cocked",
        "Tilted head",
        "Proper",
        "Bonebroken",
        "Squinting",
        "Leprotic",
        "Morbidly obese",
        "Big demigod energy",
        "Covered in massive scars",
        "Charming smile",
        "Albino",
        "Covered in tattoos",
        "Rosy cheeked",
        "Frumpy",
        "Lanky",
        "Wiry",
        "Cross-eyed",
        "Shaved eyebrows", 
        "Extremely clean",
        "Innocent eyes",
        "Smells musty",
        "Extremely pale"
    ];

    function random(string memory input) private pure returns (uint256 rand) {
        rand = uint256(keccak256(abi.encodePacked(input)));
    }
    
    function getLocation(uint256 tokenId) public view returns (string memory location) {
        location = pluck(tokenId, "LOCATION", defaultLocation);
    }
    
    function getOutfit(uint256 tokenId) public view returns (string memory fit) {
        fit = pluck(tokenId, "OUTFIT", outfit);
    }
    
    function getAttitude(uint256 tokenId) public view returns (string memory att) {
        att = pluck(tokenId, "ATTITUDE", attitude);
    }
    
    function getHands(uint256 tokenId) public view returns (string memory hand) {
        hand = pluck(tokenId, "HANDS", hands);
    }
    
    function getPockets(uint256 tokenId) public view returns (string memory pocket) {
        pocket = pluck(tokenId, "POCKETS", pockets);
    }

    function getLove(uint256 tokenId) public view returns (string memory lov) {
        lov = pluck(tokenId, "LOVE", love);
    }
    
    function getSecret(uint256 tokenId) public view returns (string memory secr) {
        secr = pluck(tokenId, "SECRET", secret);
    }
    
    function getHabitus(uint256 tokenId) public view returns (string memory habit) {
        habit = pluck(tokenId, "HABITUS", habitus);
    }
    
    function pluck(uint256 tokenId, string memory keyPrefix, string[] memory sourceArray) private pure returns (string memory output) {
        uint256 rand = random(string(abi.encodePacked(keyPrefix, toString(tokenId))));
        output = sourceArray[rand % sourceArray.length];
    }

    function tokenURI(uint256 tokenId) override public view returns (string memory output) {
        string[17] memory parts;
        parts[0] = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { fill: white; font-family: serif; font-size: 14px; }</style><rect width="100%" height="100%" fill="black" /><text x="10" y="20" class="base">';
        
        parts[1] = getLocation(tokenId);

        parts[2] = '</text><text x="10" y="40" class="base">';
        
        parts[3] = getOutfit(tokenId);

        parts[4] = '</text><text x="10" y="60" class="base">';
        
        parts[5] = getAttitude(tokenId);

        parts[6] = '</text><text x="10" y="80" class="base">';
        
        parts[7] = getHands(tokenId);

        parts[8] = '</text><text x="10" y="100" class="base">';
        
        parts[9] = getPockets(tokenId);

        parts[10] = '</text><text x="10" y="120" class="base">';
        
        parts[11] = getLove(tokenId);

        parts[12] = '</text><text x="10" y="140" class="base">';
        
        parts[13] = getSecret(tokenId);

        parts[14] = '</text><text x="10" y="160" class="base">';
        
        parts[15] = getHabitus(tokenId);

        parts[16] = '</text></svg>';
        
        output = string(abi.encodePacked(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7], parts[8]));
        output = string(abi.encodePacked(output, parts[9], parts[10], parts[11], parts[12], parts[13], parts[14], parts[15], parts[16]));
        
        string memory json = 
        Base64.encode(bytes(string(abi.encodePacked(
        '{"name": "NPC #', toString(tokenId), '", "description": "NPCs are random onchain characters with stories to tell for Loot & xLoot Adventurers", "attributes": [{"trait_type": "Location","value": "', getLocation(tokenId), '"}, {"trait_type": "Outfit","value": "', getOutfit(tokenId), '"}, {"trait_type": "Attitude","value": "', getAttitude(tokenId), '"}, {"trait_type": "Secret","value": "', getSecret(tokenId), '"}], "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
        output = string(abi.encodePacked('data:application/json;base64,', json));
    }
    
    /**
     * @dev Creates a new NPC token for `_msgSender()`. The token
     * URI is autogenerated based on 'METADATA MAGICK' and ID matches Loot/xLoot holdings, after ETH fee.
     *
     * See {ERC721-_mint}.
     */
    function claim(uint256 tokenId) external nonReentrant payable {
        // @dev Check caller owns Loot/xLoot to mint NPC.
        if (tokenId < 8001) { // @dev confirm Loot ownership / discount
            require(
                _msgSender() == loot.ownerOf(tokenId),
                "MUST_OWN_LOOT_ID");
            require(msg.value == 0.025 ether, "INSUFF_ETH");
        } else if (tokenId > 8000 && tokenId < 16001) { // @dev confirm xLoot ownership / discount
            require(
                _msgSender() == xLoot.ownerOf(tokenId),
                "MUST_OWN_XLOOT_ID");
            require(msg.value == 0.025 ether, "INSUFF_ETH");
        } else { // @dev public mint remainder before reserve
            require(tokenId < 19501, "OUT_OF_BOUNDS");
            require(msg.value == 0.05 ether, "INSUFF_ETH");
        }
        _safeMint(_msgSender(), tokenId);
    }
    
    function ownerClaim(uint256 tokenId) external nonReentrant onlyOwner {
        require(tokenId > 19500 && tokenId < 20001, "OUT_OF_BOUNDS");
        _safeMint(_msgSender(), tokenId);
    }
    
    /**
     * @dev Allows `owner` to collect NFT minting fees in ETH.
     */
    function collectETH() external nonReentrant onlyOwner {
        (bool success, ) = _msgSender().call{value: address(this).balance}("");
        require(success, "NOT_PAYABLE");
    }
    
    function toString(uint256 value) private pure returns (string memory output) {
        // @dev Inspired by OraclizeAPI's implementation - MIT license -
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol.
        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }