{
  "name": "dailyRewardsItems",
  "author": "limon",
  "supportedGames": [
    "all"
  ],
  "takaroVersion": "v0.0.21",
  "versions": [
    {
      "tag": "2.0.0",
      "description": "Provides a comprehensive daily reward system for players, including streak tracking, milestone bonuses, and the chance to earn random items.\n\n  -   **Daily Rewards:** Players can claim a base reward that increases with their daily login streak.\n  -   **Streak Tracking:** The module tracks players' consecutive daily logins, encouraging consistent engagement.\n  -   **Milestone Rewards:** Configurable bonus rewards are granted to players for reaching specific streak milestones, providing long-term goals.\n  -   **Random Item Rewards:** Players have a chance to receive random in-game items as part of their daily reward, with configurable item chances and a streak-based multiplier.\n  -   **Customizable Rewards:** The base reward amount, maximum streak, milestone rewards, possible items, and item chances are all configurable.\n  -   **Permission System:** Supports permissions to control who can claim rewards and to allow for reward multipliers (e.g., for donors).\n  -   **Player Notifications:** Provides in-game messages to inform players about their streak, next claim time, milestones, and rewards.",
      "configSchema": "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"baseReward\":{\"type\":\"number\",\"title\":\"Base Reward\",\"description\":\"Base amount of currency given for daily rewards. This is multiplied by streak level.\",\"default\":100,\"minimum\":1},\"maxStreak\":{\"type\":\"number\",\"title\":\"Maximum Streak\",\"description\":\"Maximum streak level a player can reach\",\"default\":365,\"minimum\":1},\"milestoneRewards\":{\"type\":\"array\",\"title\":\"Milestone Rewards\",\"description\":\"Additional rewards for reaching certain streak milestones\",\"items\":{\"type\":\"object\",\"properties\":{\"days\":{\"type\":\"number\",\"description\":\"Days needed to reach milestone\",\"minimum\":1},\"reward\":{\"type\":\"number\",\"description\":\"Bonus reward amount\"},\"message\":{\"type\":\"string\",\"description\":\"Message to show when milestone is reached\"}}},\"default\":[{\"days\":7,\"reward\":1000,\"message\":\"You did it! 7 days in a row!\"},{\"days\":30,\"reward\":5000,\"message\":\"A whole month! You're on fire!\"},{\"days\":90,\"reward\":20000,\"message\":\"90 days! You're unstoppable!\"},{\"days\":180,\"reward\":50000,\"message\":\"Half a year! You're a legend!\"},{\"days\":365,\"reward\":150000,\"message\":\"365 days! You're a true champion!\"}]},\"possibleItems\":{\"type\":\"array\",\"title\":\"Possible Items\",\"description\":\"Items that can be randomly awarded as daily rewards\",\"x-component\":\"item\",\"uniqueItems\":true,\"items\":{\"type\":\"object\",\"title\":\"Item\",\"properties\":{\"item\":{\"type\":\"string\",\"title\":\"Item\"},\"amount\":{\"type\":\"number\",\"title\":\"Amount\",\"default\":1},\"quality\":{\"type\":\"string\",\"title\":\"Quality\"}}},\"default\":[]},\"itemChance\":{\"title\":\"Base Item Chance\",\"description\":\"Base chance to receive an item (1-100)\",\"type\":\"string\",\"minLength\":1,\"maxLength\":100,\"default\":\"25\"},\"useStreakMultiplier\":{\"title\":\"Use Streak Multiplier\",\"description\":\"Increase the item chance based on player streak\",\"type\":\"boolean\",\"default\":false},\"streakMultiplierValue\":{\"title\":\"Streak Multiplier Value\",\"description\":\"How much to increase item chance per streak day (e.g. 0.5 = +0.5% per day)\",\"type\":\"number\",\"default\":0.5,\"minimum\":0},\"maxItemChance\":{\"title\":\"Maximum Item Chance\",\"description\":\"Cap for total item chance after streak multiplier (1-100)\",\"type\":\"number\",\"default\":95,\"minimum\":1,\"maximum\":100}},\"required\":[\"baseReward\",\"maxStreak\",\"milestoneRewards\"],\"additionalProperties\":false}",
      "uiSchema": "{\"possibleItems\":{\"items\":{\"item\":{\"ui:widget\":\"item\"}}}}",
      "commands": [
        {
          "name": "streak",
          "trigger": "streak",
          "helpText": "Check your current daily reward streak and next claim time",
          "function": "import { data, takaro } from '@takaro/helpers';\nimport { getPlayerStreak, getLastClaim, getMultiplier } from './utils.js';\nasync function main() {\n    const { pog, gameServerId, module: mod } = data;\n    const streak = await getPlayerStreak(gameServerId, pog.playerId, mod.moduleId);\n    const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId);\n    const multiplier = await getMultiplier(pog);\n    const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;\n    if (!streak || !lastClaim) {\n        await pog.pm(`You haven't claimed any daily rewards yet! Use ${prefix}daily to get started.`);\n        return;\n    }\n    const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);\n    const now = new Date();\n    const canClaim = now >= nextClaimTime;\n    // Find next milestone\n    let nextMilestone = null;\n    for (const milestone of mod.userConfig.milestoneRewards) {\n        if (milestone.days > streak) {\n            nextMilestone = milestone;\n            break;\n        }\n    }\n    let message = `Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x donor bonus!)` : ''}\\n`;\n    message += canClaim\n        ? `Your daily reward is available! Use ${prefix}daily to claim it!\\n`\n        : `Next reward available at: ${nextClaimTime.toLocaleString()}\\n`;\n    if (nextMilestone) {\n        message += `\\n🎯 Next milestone: ${nextMilestone.days} days (${nextMilestone.days - streak} days to go!)`;\n    }\n    await pog.pm(message);\n}\nawait main();\n//# sourceMappingURL=streak.js.map",
          "arguments": []
        },
        {
          "name": "daily",
          "trigger": "daily",
          "helpText": "Claim your daily reward",
          "function": "import { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\nimport { DAILY_KEY, STREAK_KEY, getMultiplier } from './utils.js';\n\nasync function main() {\n    const { pog, gameServerId, module: mod } = data;\n\n    if (!checkPermission(pog, 'DAILY_CLAIM')) {\n        throw new TakaroUserError('You do not have permission to claim daily rewards.');\n    }\n\n    // Get last claim time\n    const lastClaimRes = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [DAILY_KEY],\n            gameServerId: [gameServerId],\n            playerId: [pog.playerId],\n            moduleId: [mod.moduleId],\n        },\n    });\n\n    const now = new Date();\n    let streak = 1;\n\n    if (lastClaimRes.data.data.length > 0) {\n        const lastClaim = new Date(JSON.parse(lastClaimRes.data.data[0].value));\n        const hoursSinceLastClaim = (now - lastClaim) / (1000 * 60 * 60);\n\n        // Check if 24 hours have passed\n        if (hoursSinceLastClaim < 24) {\n            const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);\n            throw new TakaroUserError(`You can claim your next reward at ${nextClaimTime.toLocaleString()}`);\n        }\n\n        // Get current streak\n        const streakRes = await takaro.variable.variableControllerSearch({\n            filters: {\n                key: [STREAK_KEY],\n                gameServerId: [gameServerId],\n                playerId: [pog.playerId],\n                moduleId: [mod.moduleId],\n            },\n        });\n\n        if (streakRes.data.data.length > 0) {\n            // If claimed within 48 hours, increment streak\n            if (hoursSinceLastClaim < 48) {\n                streak = Math.min(JSON.parse(streakRes.data.data[0].value) + 1, mod.userConfig.maxStreak);\n                await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, {\n                    value: JSON.stringify(streak),\n                });\n            } else {\n                // Reset streak if more than 48 hours\n                streak = 1;\n                await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, {\n                    value: JSON.stringify(1),\n                });\n            }\n        } else {\n            // Create new streak record\n            await takaro.variable.variableControllerCreate({\n                key: STREAK_KEY,\n                value: JSON.stringify(1),\n                gameServerId,\n                playerId: pog.playerId,\n                moduleId: mod.moduleId,\n            });\n        }\n\n        // Update last claim time\n        await takaro.variable.variableControllerUpdate(lastClaimRes.data.data[0].id, {\n            value: JSON.stringify(now),\n        });\n    } else {\n        // First time claim\n        await takaro.variable.variableControllerCreate({\n            key: DAILY_KEY,\n            value: JSON.stringify(now),\n            gameServerId,\n            playerId: pog.playerId,\n            moduleId: mod.moduleId,\n        });\n\n        await takaro.variable.variableControllerCreate({\n            key: STREAK_KEY,\n            value: JSON.stringify(1),\n            gameServerId,\n            playerId: pog.playerId,\n            moduleId: mod.moduleId,\n        });\n    }\n\n    const multiplier = await getMultiplier(pog);\n    const baseReward = mod.userConfig.baseReward * streak * multiplier;\n    let bonusReward = 0;\n    let milestoneMessage = '';\n\n    // Check for milestones\n    for (const milestone of mod.userConfig.milestoneRewards) {\n        if (streak === milestone.days) {\n            bonusReward = milestone.reward;\n            milestoneMessage = `\\n${milestone.message}`;\n            break;\n        }\n    }\n\n    // Award total currency rewards\n    const totalReward = baseReward + bonusReward;\n    await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, pog.playerId, {\n        currency: totalReward,\n    });\n\n    // Get currency name for display\n    let currencyName = \"coins\";\n    try {\n        const currencySettings = await takaro.settings.settingsControllerGetOne('currencyName', gameServerId);\n        if (currencySettings.data.data) {\n            currencyName = currencySettings.data.data.value;\n        }\n    } catch (error) {\n        // Use default if setting not found\n    }\n\n    // Check for item rewards\n    let itemMessage = \"\";\n    const possibleItems = mod.userConfig.possibleItems || [];\n    let baseItemChance = parseInt(mod.userConfig.itemChance || '0');\n\n    // Apply streak multiplier if enabled\n    let finalItemChance = baseItemChance;\n    let streakBonusMessage = \"\";\n\n    if (mod.userConfig.useStreakMultiplier && streak > 1) {\n        const streakBonus = (streak - 1) * mod.userConfig.streakMultiplierValue;\n        finalItemChance = Math.min(baseItemChance + streakBonus, mod.userConfig.maxItemChance || 100);\n        streakBonusMessage = ` (includes +${streakBonus.toFixed(1)}% streak bonus)`;\n    }\n\n    // Check for permission override\n    const itemChancePermission = checkPermission(pog, 'DAILY_ITEM_CHANCE_OVERRIDE');\n    const actualItemChance = itemChancePermission && itemChancePermission.count != null\n        ? itemChancePermission.count\n        : finalItemChance;\n\n    // Determine if player gets an item\n    if (possibleItems.length > 0 && Math.random() * 100 < actualItemChance) {\n        // Select random item from the list\n        const randomItem = possibleItems[Math.floor(Math.random() * possibleItems.length)];\n\n        if (randomItem && randomItem.item) {\n            try {\n                // Get item details\n                const itemDetails = await takaro.item.itemControllerFindOne(randomItem.item);\n                if (itemDetails && itemDetails.data.data) {\n                    // Give item to player\n                    await takaro.gameserver.gameServerControllerGiveItem(gameServerId, pog.playerId, {\n                        name: itemDetails.data.data.code,\n                        amount: randomItem.amount || 1,\n                        quality: randomItem.quality || ''\n                    });\n                    itemMessage = `\\nYou also received: ${randomItem.amount || 1}x ${itemDetails.data.data.name}`;\n                }\n            } catch (error) {\n                // Failed to give item, continue without item message\n            }\n        }\n    }\n\n    let message = `Daily reward claimed! You received ${totalReward} ${currencyName}\\n`;\n    message += `Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x currency bonus!)` : ''}`;\n\n    if (possibleItems.length > 0) {\n        message += `\\nItem chance: ${actualItemChance.toFixed(1)}%${streakBonusMessage}`;\n    }\n\n    message += itemMessage;\n    message += milestoneMessage;\n\n    await pog.pm(message);\n}\n\nawait main();",
          "arguments": []
        },
        {
          "name": "topstreak",
          "trigger": "topstreak",
          "helpText": "Shows the players with highest daily reward streaks",
          "function": "import { takaro, data } from '@takaro/helpers';\nimport { STREAK_KEY } from './utils.js';\nasync function main() {\n    const { pog, gameServerId, module: mod, arguments: args } = data;\n    // Limit count to reasonable number\n    const count = Math.min(Math.max(1, args.count), 50);\n    // Get all streaks\n    const streaksRes = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [STREAK_KEY],\n            gameServerId: [gameServerId],\n            moduleId: [mod.moduleId],\n        },\n        limit: 1000, // Get all possible streaks\n    });\n    if (streaksRes.data.data.length === 0) {\n        await pog.pm('No players have started their daily streak yet!');\n        return;\n    }\n    // Sort by streak value\n    const sortedStreaks = streaksRes.data.data\n        .map((record) => ({\n        playerId: record.playerId,\n        streak: JSON.parse(record.value),\n    }))\n        .sort((a, b) => b.streak - a.streak)\n        .slice(0, count);\n    // Get player names\n    const playerDetails = await Promise.all(sortedStreaks.map(async (record) => {\n        const player = (await takaro.player.playerControllerGetOne(record.playerId)).data.data;\n        return {\n            name: player.name,\n            streak: record.streak,\n        };\n    }));\n    // Build message\n    let message = `Top ${count} Daily Streaks:\\n\\n`;\n    playerDetails.forEach((player, index) => {\n        message += `${index + 1}. ${player.name}: ${player.streak} days\\n`;\n    });\n    await pog.pm(message);\n}\nawait main();\n//# sourceMappingURL=topstreak.js.map",
          "arguments": [
            {
              "name": "count",
              "type": "number",
              "helpText": "Number of players to show (max 25)",
              "defaultValue": "5",
              "position": 0
            }
          ]
        }
      ],
      "hooks": [
        {
          "name": "dailyLoginCheck",
          "eventType": "player-connected",
          "description": "Hook for player-connected events",
          "regex": null,
          "function": "import { data, takaro } from '@takaro/helpers';\nimport { getLastClaim } from './utils.js';\nasync function main() {\n    const { pog, gameServerId, module: mod } = data;\n    const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;\n    const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId);\n    // First time player\n    if (!lastClaim) {\n        await pog.pm(`Welcome! Use ${prefix}daily to claim your first daily reward and start your streak!`);\n        return;\n    }\n    const now = new Date();\n    const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);\n    if (now >= nextClaimTime) {\n        await pog.pm(`Your daily reward is ready! Use ${prefix}daily to claim it!`);\n    }\n}\nawait main();\n//# sourceMappingURL=dailyLoginCheck.js.map"
        }
      ],
      "cronJobs": [],
      "functions": [
        {
          "name": "utils",
          "description": "",
          "function": "import { takaro, checkPermission } from '@takaro/helpers';\nexport const DAILY_KEY = 'daily_timestamp';\nexport const STREAK_KEY = 'daily_streak';\nexport async function getMultiplier(pog) {\n    const perm = checkPermission(pog, 'DAILY_REWARD_MULTIPLIER');\n    if (perm)\n        return perm.count;\n    return 1;\n}\nexport async function getPlayerStreak(gameServerId, playerId, moduleId) {\n    const streakRes = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [STREAK_KEY],\n            gameServerId: [gameServerId],\n            playerId: [playerId],\n            moduleId: [moduleId],\n        },\n    });\n    return streakRes.data.data.length ? parseInt(JSON.parse(streakRes.data.data[0].value)) : 0;\n}\nexport async function getLastClaim(gameServerId, playerId, moduleId) {\n    const lastClaimRes = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [DAILY_KEY],\n            gameServerId: [gameServerId],\n            playerId: [playerId],\n            moduleId: [moduleId],\n        },\n    });\n    return lastClaimRes.data.data.length ? new Date(JSON.parse(lastClaimRes.data.data[0].value)) : null;\n}\n//# sourceMappingURL=utils.js.map"
        }
      ],
      "permissions": [
        {
          "permission": "DAILY_CLAIM",
          "friendlyName": "Claim Daily Rewards",
          "description": "Allows the player to claim daily rewards",
          "canHaveCount": false
        },
        {
          "permission": "DAILY_REWARD_MULTIPLIER",
          "friendlyName": "Multiplier",
          "description": "Control the multiplier per role. This is useful to give your donors a little extra. Count is an integer multiplier.",
          "canHaveCount": true
        }
      ]
    }
  ]
}