Challenges Inside Inventory

Adds a button to Dead Frontier Inventory to open challenges inside the game inventory. So you don't need to come back to outpost to check if you completed the challenges.

// ==UserScript==
// @name         Challenges Inside Inventory
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Adds a button to Dead Frontier Inventory to open challenges inside the game inventory. So you don't need to come back to outpost to check if you completed the challenges.
// @author       Lucky11
// @match        https://fairview.deadfrontier.com/onlinezombiemmo/DF3D/DF3D_InventoryPage.php?page=31*
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Inject challenges CSS
    const style = document.createElement('style');
    style.type = 'text/css';
    style.textContent = `
        body {
            background-color: #1a1a1a; /* Dark background */
            color: #e6e6e6; /* Light text color */
        }
        #pageLogo {
            color: red;
            font-size: 14pt;
        }
        #invController {
            padding-top: 20px;
            overflow-y: auto;
        }
        .challengeContainer {
            border: 1px solid #555;
            margin: 10px;
            background-color: rgba(0, 0, 0, 0.6);
            padding-bottom: 8px;
            text-align: left;
            cursor: pointer;
        }
        .challengeContainer .challengeNamefield {
            position: relative;
            border-bottom: 1px solid darkgrey;
            background-image: linear-gradient(black, black, rgba(50, 50, 50, 0.5));
            padding: 3px 5px;
            font-weight: 900;
            color: #E6CC4D;
        }
        .challengeContainer .challengeDescription {
            padding: 3px 5px;
            border-bottom: 1px solid darkgrey;
        }
        .challengeContainer .challengeHidden {
            max-height: 0;
            overflow-y: hidden;
            transition: max-height 0.1s;
        }
        .challengeContainer .challengeComplete {
            position: absolute;
            text-align: center;
            top: 0;
            left: 0;
            right: 0;
            color: #00ff00;
            font-size: 12pt;
        }
        .challengeContainer.challengeOpened .challengeHidden {
            max-height: 500px;
        }
        .challengeContainer .challengeNamefield::before {
            content: "\\25b2  ";
        }
        .challengeContainer.challengeOpened .challengeNamefield::before {
            content: "\\25bc  ";
        }
        .challengeContainer .challengeRewards {
            float: right;
            border-left: 1px solid red;
            width: 160px;
        }
        .challengeContainer .challengeRewards .item_rewards {
            border-top: 1px solid #660000;
        }
        .challengeContainer .challengeRewards .autoPad {
            padding: 5px 10px;
        }
        .challengeContainer .challengeRewards .fakeItem {
            border-bottom: 1px solid #660000;
            padding: 5px 10px;
        }
        .challengeContainer .challengeObjectives {
            margin-right: 160px;
        }
        .challengeContainer.clanChallenge .challengeRewards {
            width: 200px;
        }
        .challengeContainer.clanChallenge .challengeRewards {
            text-align: center;
        }
        .challengeContainer.clanChallenge .challengeObjectives {
            margin-right: 200px;
        }
        .challengeContainer .challengeObjectives .challengeObjective {
            border: 1px solid #660000;
            border-right: 1px solid red;
            background-color: black;
        }
        .challengeContainer .challengeObjectives .objectiveProgress {
            background-color: #128800;
            white-space: nowrap;
        }
        .challengeContainer .challengeObjectives .objectiveProgress .pads {
            padding: 5px 10px;
        }
        .challengeContainer .challengeProgress {
            background-color: black;
            text-align: center;
        }
        .challengeContainer .challengeBar {
            background-color: #128800;
            white-space: nowrap;
        }
    `;
    document.head.appendChild(style);

    // Create overlay elements
    const overlay = document.createElement('div');
    overlay.style.position = 'fixed';
    overlay.style.top = '0';
    overlay.style.left = '0';
    overlay.style.width = '100%';
    overlay.style.height = '100%';
    overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
    overlay.style.zIndex = '9999';
    overlay.style.display = 'none';
    overlay.style.justifyContent = 'center';
    overlay.style.alignItems = 'center';

    const overlayContent = document.createElement('div');
    overlayContent.style.backgroundColor = 'rgba(50, 50, 50, 0.9)'; // Dark background
    overlayContent.style.padding = '20px';
    overlayContent.style.borderRadius = '8px';
    overlayContent.style.maxWidth = '600px';
    overlayContent.style.width = '90%';
    overlayContent.style.overflowY = 'auto';
    overlayContent.style.maxHeight = '80%';

    const closeButton = document.createElement('button');
    closeButton.innerText = '✖'; // Change text to match the style
    closeButton.style.marginLeft = '550px';
    closeButton.style.fontFamily = 'Downcome'; // Match font family
    //closeButton.style.color = 'rgb(153, 0, 0)'; // Match text color
    closeButton.style.fontSize = '20pt'; // Match font size
    closeButton.style.backgroundColor = 'transparent'; // Make background transparent
    closeButton.style.border = 'none'; // Remove border
    closeButton.style.cursor = 'pointer'; // Pointer cursor
    closeButton.style.marginBottom = '5px'; // Maintain margin

    closeButton.addEventListener('click', () => {
        overlay.style.display = 'none';
    });

    overlayContent.appendChild(closeButton);
    overlay.appendChild(overlayContent);
    document.body.appendChild(overlay);
    // Event listener to close the overlay when clicking outside of it
    overlay.addEventListener('click', (event) => {
        if (event.target === overlay) {
            overlay.style.display = 'none'; // Hide the overlay
        }
    });
    // Function to serialize the request parameters
    function serializeObject(obj) {
        return Object.keys(obj).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])).join('&');
    }

    // Function to make the POST request
    function makeRequest() {
        const requestUrl = "https://fairview.deadfrontier.com/onlinezombiemmo/hotrods/load_challenge.php";
        const playerLevel = unsafeWindow.userVars["DFSTATS_df_level"]; // Get player's level
        const requestParams = {
            userID: unsafeWindow.userVars["userID"],
            password: unsafeWindow.userVars["password"],
            sc: unsafeWindow.userVars["sc"],
            action: "get",
            minLevel: playerLevel, // Set minLevel to player's level
            maxLevel: playerLevel  // Set maxLevel to player's level
        };

        // Calculate the hash
        const payload = serializeObject(requestParams);
        const hash = unsafeWindow.hash(payload);
        const fullPayload = "hash=" + hash + "&" + payload;

        // Send the POST request
        GM_xmlhttpRequest({
            method: "POST",
            url: requestUrl,
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            data: fullPayload,
            onload: function(response) {
                //console.log('Response:', response.responseText);
                displayChallenges(response.responseText);
            },
            onerror: function(error) {
                console.error('Error:', error);
                alert('Error sending POST request.');
            }
        });
    }

    // Function to display challenges in the overlay
    function displayChallenges(data) {
        const challenges = parseChallengeData(data);
        overlayContent.innerHTML = ''; // Clear existing content
        overlayContent.appendChild(closeButton); // Re-add close button

        if (challenges.length === 0) {
            overlayContent.innerHTML = "<p style='color: white;'>No challenges available.</p>";
            overlay.style.display = 'flex'; // Show the overlay
            return;
        }

        challenges.forEach(challenge => {
            const challengeContainer = document.createElement("div");
            challengeContainer.classList.add("challengeContainer");

            const nameField = document.createElement("div");
            nameField.classList.add("challengeNamefield");
            nameField.innerHTML = `${challenge.name}<span style="float: right;">Ends: ${challenge.endTime}</span>`;

            challengeContainer.appendChild(nameField);

            const description = document.createElement("div");
            description.classList.add("challengeDescription");
            description.textContent = challenge.description;
            challengeContainer.appendChild(description);

            const rewards = document.createElement("div");
            rewards.classList.add("challengeRewards");

            let rewardsHTML = `<div class='autoPad'>
    <div class='cashhack redElements' style='position: relative;' data-cash='Rewards'>Rewards</div>`;

            if (challenge.rewardCash > 0) {
                rewardsHTML += `<div class='cashhack' style='position: relative;' data-cash='Cash: ${parseInt(challenge.rewardCash).toLocaleString()}'>Cash: ${parseInt(challenge.rewardCash).toLocaleString()}</div>`;
            }

            if (challenge.rewardExp > 0) {
                rewardsHTML += `<div class='credits cashhack' style='position: relative;' data-cash='Exp: ${parseInt(challenge.rewardExp).toLocaleString()}'>Exp: ${parseInt(challenge.rewardExp).toLocaleString()}</div>`;
            }
            rewardsHTML += `</div>`;
            rewards.innerHTML = rewardsHTML;

            challengeContainer.appendChild(rewards);


            const objectives = document.createElement("div");
            objectives.classList.add("challengeObjectives");
            challenge.objectives.forEach(obj => {
                const objective = document.createElement("div");
                objective.classList.add("challengeObjective");
                objective.innerHTML = `<div class='objectiveProgress' style='width: ${obj.progress}%;'>
                    <div class='pads'>${obj.name}: ${obj.current}/${obj.target}</div>
                </div>`;
                objectives.appendChild(objective);
            });
            challengeContainer.appendChild(objectives);

            const progress = document.createElement("div");
            progress.classList.add("challengeProgress");
            //progress.innerHTML = `<div class='challengeBar' style='width: ${challenge.progress}%;'>${challenge.progress}%</div>`;
            // Determine margin-top based on conditions
            let marginTopStyle = "3px"; // Default margin
            const hasCash = challenge.rewardCash > 0;
            const hasExp = challenge.rewardExp > 0;
            const totalObjectives = challenge.objectives.length;

            if (hasCash && hasExp && totalObjectives === 1) {
                marginTopStyle = "34px";
            } else if (!hasCash && hasExp && totalObjectives === 1) {
                marginTopStyle = "16px";
            }

            progress.innerHTML = `
    <div class='challengeProgress' style='margin-top: ${marginTopStyle};'>
        <div class='challengeBar' style='width: ${challenge.progress}%;'>${challenge.progress}%</div>
    </div>
`;
            challengeContainer.appendChild(progress);

            overlayContent.appendChild(challengeContainer);
        });

        overlay.style.display = 'flex'; // Show the overlay
    }

    // Function to parse challenge data from the response
    function parseChallengeData(data) {
        const challenges = [];
        const parsedData = new URLSearchParams(data);

        const maxChallenges = parseInt(parsedData.get('max_challenges')) || 0;
        const playerLevel = unsafeWindow.userVars["DFSTATS_df_level"]; // Get player's level

        for (let i = 0; i < maxChallenges; i++) {
            const minLevel = parseInt(parsedData.get(`challenge_${i}_min_level`)) || 0;
            const maxLevel = parseInt(parsedData.get(`challenge_${i}_max_level`)) || 0;

            // Check if the player's level is within the challenge's level range
            if (playerLevel < minLevel || playerLevel > maxLevel) {
                continue; // Skip this challenge if the player's level is not appropriate
            }

            let rewardExp = parseInt(parsedData.get(`challenge_${i}_reward_exp`)) || 0; // Get rewardExp and ensure it's a number

            // Check if the user is a gold member and double the rewardExp if true
            if (unsafeWindow.userVars['DFSTATS_df_goldmember'] === '1') {
                rewardExp *= 2; // Double the experience reward for gold members
            }
            rewardExp *= playerLevel; // Now multiply by player level

            const challenge = {
                name: parsedData.get(`challenge_${i}_name`),
                description: parsedData.get(`challenge_${i}_description`),
                endTime: new Date(new Date(0).setUTCSeconds(parseInt(parsedData.get(`challenge_${i}_end_time`)) + 1200000000)).toLocaleString(),
                rewardCash: parsedData.get(`challenge_${i}_reward_cash`),
                rewardExp: rewardExp,
                objectives: [],
                progress: 0
            };

            const totalObjectives = parseInt(parsedData.get(`challenge_${i}_objectives`)) || 0;
            let totalProgress = 0; // To accumulate total progress for the challenge
            let totalTargetScore = 0; // To accumulate total target score for the challenge
            let totalPlayerScore = 0; // To accumulate player's score for the challenge

            for (let j = 1; j <= totalObjectives; j++) {
                const targetKey = `challenge_${i}_objectives_${j}_target`;
                const playerScoreKey = `challenge_${i}_objective_${j}_player_score`;
                const objectiveNameKey = `challenge_${i}_objectives_${j}_name`;

                const target = parseInt(parsedData.get(targetKey)) || 0; // Correctly retrieve target
                const playerScore = parseInt(parsedData.get(playerScoreKey)) || 0;
                const objectiveName = parsedData.get(objectiveNameKey);

                // Calculate progress for the objective
                const progress = (target > 0) ? Math.min((playerScore / target) * 100, 100) : 0;

                challenge.objectives.push({
                    name: objectiveName,
                    target: target, // Ensure target is set correctly
                    current: playerScore,
                    progress: progress // Store the calculated progress
                });

                // Accumulate total target and player scores
                totalTargetScore += target;
                totalPlayerScore += playerScore;
                totalProgress += progress; // Accumulate individual progress
            }

            // Calculate overall progress as the average of individual objective progress
            const overallProgress = (totalObjectives > 0) ? (totalProgress / totalObjectives) : 0;

            // Set the challenge progress based on overall progress
            challenge.progress = Math.min(100, parseFloat(overallProgress.toFixed(2)));

            // Uncomment for debugging
            // console.log("Specific Objective Progress: ", specificObjectiveProgress);
            // console.log("Overall Progress: ", overallProgress);
            // console.log("Challenge Progress: ", challenge.progress);

            challenges.push(challenge);
        }

        return challenges;
    }

    // Create the button
    const button = document.createElement('button');
    button.innerText = 'Show Current Challenges';
    button.style.position = 'absolute';

//<button class="opElem" style="left: 20px; bottom: 86px;">SlotLock?</button>

    //button.style.backgroundColor = '#4CAF50'; // Green button
    button.style.left = '20px';
    button.style.bottom = '110px';
    button.style.cursor = 'pointer';

    // Append the button to the body
    //document.body.appendChild(button);
    var invController = document.getElementById('invController'); // Select the element with ID 'invController'
    invController.appendChild(button); // Append the button to the selected element

    // Add click event listener to the button
    button.addEventListener('click', makeRequest);
})();