diff --git a/src/lib/sidebar/tabs/Guides.svelte b/src/lib/sidebar/tabs/Guides.svelte index 3a5c059..ce9149b 100644 --- a/src/lib/sidebar/tabs/Guides.svelte +++ b/src/lib/sidebar/tabs/Guides.svelte @@ -42,6 +42,9 @@ import IconRuler2 from "~icons/tabler/ruler-2"; import IconMathMaxMin from "~icons/tabler/math-max-min"; import IconCat from "~icons/tabler/cat"; + import IconClock from "~icons/tabler/clock"; + import IconStar from "~icons/tabler/star"; + import IconHourglass from "~icons/tabler/hourglass-empty"; import SidebarPlaceholder from "../navigation/SidebarPlaceholder.svelte"; @@ -71,6 +74,15 @@ page="/guide/adding-new-features/custom-items/models" /> + + + + + + + + + diff --git a/src/routes/guide/cooldown/+page.svx b/src/routes/guide/cooldown/+page.svx new file mode 100644 index 0000000..f4b863f --- /dev/null +++ b/src/routes/guide/cooldown/+page.svx @@ -0,0 +1,48 @@ +--- +title: Cooldown checks +description: Learn how to create and check cooldown timers using several common methods. +version: 26.1 +--- + +# Cooldown checks + +A cooldown restricts how frequently a player triggers a custom action. In most +datapacks, these actions involve running functions or commands. + +For example, consider a custom wand that shoots fireballs when a player +right clicks. Without a cooldown, players could spam the wand, resulting +in lag or unbalanced gameplay. Adding a cooldown ensures the player waits a few +seconds before shooting another fireball. + +## Core concepts +- **Cooldown**: A timer that prevents an action from running again until a specified +amount of time has elapsed since the last execution. +- **Action**: The command or function the cooldown restricts. +- **Namespace**: A unique identifier for your datapack that prevents conflicts with other +datapacks. Use it as a prefix for all functions and resources (for example, `mypack:my_function`). + + +## Implementation methods + +This guide covers three common methods for implementing cooldowns in Minecraft datapacks: +scoreboard-based, advancement-based, and worldclock-based. + +### [Scoreboard-based cooldown](/guide/cooldown/scoreboard) + +Use a scoreboard objective to store how much time remaining until the cooldown expires. + +This method works well for small datapacks, or when you only need a few different cooldowns. + +### [Advancement-based cooldown](/guide/cooldown/advancement) + +Use an advancement to tick the cooldown. + +This method only ticks the cooldown when the player is using the action, making it ideal +for performance-sensitive projects. + +### [Worldclock-based cooldown](/guide/cooldown/worldclock) + +Use a timespan on a worldclock to measure elapsed time. + +This is a non-ticking approach that works well for large datapacks or any setup +that needs many cooldown timers. diff --git a/src/routes/guide/cooldown/advancement/+page.svx b/src/routes/guide/cooldown/advancement/+page.svx new file mode 100644 index 0000000..75afb82 --- /dev/null +++ b/src/routes/guide/cooldown/advancement/+page.svx @@ -0,0 +1,102 @@ +--- +title: Advancement-based cooldown +description: Learn how to create and check a cooldown timer using an advancement and a +scoreboard objective. +version: 26.1 +--- + +# Advancement-based cooldown + +An advancement-based cooldown uses a `minecraft:tick` advancement to run a function while +a player is on cooldown. + +Compared to a fully ticking scoreboard approach, this method is semi-ticking: + +- If a player is not on cooldown, nothing runs for them. +- If a player is on cooldown, the tick advancement runs a function each tick until the +cooldown ends. + +This setup takes a little more work because you need an extra advancement, but it can reduce +the amount of work your datapack does when most players are not on cooldown. + +## What you will build + +- A `my_cd` scoreboard objective to store the cooldown value (in ticks). +- A tick advancement (`:my_cd_adv`) that runs a tick function. +- A `:reduce_my_cd` function that counts the cooldown down. +- A `:cooldown` function that checks the cooldown before running your action. + +:::tip + +To learn about advancements, see the [Advancement guide](/wiki/files/advancements). + +::: + +### 1) Create the tick advancement (`my_cd_adv.json`) + +Create an advancement that triggers every tick and runs `:reduce_my_cd` +as a reward. + +```json:my_cd_adv.json +{ + "criteria": { + "tick": { + "trigger": "minecraft:tick" + } + }, + "rewards": { + "function": ":reduce_my_cd" + } +} +``` +The function `:reduce_my_cd` only runs when the advancement +is not granted. This happens because advancements only grant their +rewards to players who don't already have them. + +### 2) Create the scoreboard objective. + +Create a dummy objective named `my_cd` to store the cooldown value. + + ```mcfunction:load.mcfunction + scoreboard objectives add my_cd dummy + ``` +### 3) Create the cooldown countdown function (`reduce_my_cd.mcfunction`) + +This function runs once per tick for players who do not have the advancement granted. + +- While the cooldown is active (`my_cd` ≥ 1), revoke the advancement + to keep it un-granted so the tick function continues to run. +- Reduce the score by 1 each tick. + +```mcfunction:reduce_my_cd.mcfunction +# Reduce the cooldown by 1 tick +scoreboard players remove @s my_cd 1 +# Keep the advancement un-granted while the cooldown is active +execute if score @s my_cd matches 1.. run advancement revoke @s only :my_cd_adv +``` + +### 4) Create the `cooldown` function + +This function checks the cooldown as follows: + +- If the player's `my_cd` score ≥ 1, the cooldown has not ended, return fail. +- Set the player's cooldown to 5 seconds (100 ticks). +- Make `:my_cd_adv` advancement un-granted. + +```mcfunction:cooldown.mcfunction +# If the player is on cooldown, return fail +execute if score @s my_cd matches 1.. run return fail +# Reset the cooldown (change "100" to your cooldown length in ticks) +scoreboard players set @s my_cd 100 +# Make the advancement un-granted +advancement revoke @s only :my_cd_adv +# Return success +return 1 +``` + +## Testing + +Check the cooldown in your function: +```mcfunction:my_function +execute if function :cooldown run tellraw @a {"text": "Cooldown Ready!"} +``` diff --git a/src/routes/guide/cooldown/scoreboard/+page.svx b/src/routes/guide/cooldown/scoreboard/+page.svx new file mode 100644 index 0000000..6d0821a --- /dev/null +++ b/src/routes/guide/cooldown/scoreboard/+page.svx @@ -0,0 +1,69 @@ +--- +title: Scoreboard-based cooldown +description: Learn how to create and check cooldown timers using a scoreboard objective. +version: 26.1 +--- + +# Scoreboard-based cooldown + +A scoreboard-based cooldown stores a number on each player (or entity) and +changes that number over time. + +This guide uses a ticking approach, which means the datapack updates the +cooldown every game tick (20 ticks per second). Ticking cooldowns work well +for small projects with only a few cooldowns and short durations. If you track many cooldowns or +many entities, ticking affects performance. + +## What you will build + +- A `my_cd` scoreboard objective +- A `tick` function that reduces each player's cooldown over time +- A `cooldown` function that checks the cooldown before doing anything + +:::tip + +To learn how to trigger a function when a player uses an item, see the +[Right-click menu](/guide/right-click) guide. + +::: + +### 1) Create the scoreboard objective + +Create a dummy objective named `my_cd`. + +A dummy objective stores a value that only changes when commands modify it. + +```mcfunction:load.mcfunction +scoreboard objectives add my_cd dummy +``` + +### 2) Create the `tick` function + +In your `tick` function which runs every tick, reduce the `my_cd` +score by 1 for any player with `my_cd` ≥ 1 + +```mcfunction:tick.mcfunction +execute as @a[scores={my_cd=1..}] run scoreboard players remove @s my_cd 1 +``` + +### 3) Create the `cooldown` function + +In this function, check the executor’s `my_cd` score. +- If the score is 1 or higher, stop the function and tell the player. +- If the score is 0 or doesn't exist yet, set a new cooldown value and continue. + +```mcfunction:cooldown.mcfunction +# If the player is still on cooldown, stop here +execute if score @s my_cd matches 1.. run return fail +# Reset the cooldown (change "100" to your cooldown length in ticks) +scoreboard players set @s my_cd 100 +# Return success +return 1 +``` + +## Testing + +Check the cooldown in your function: +```mcfunction:my_function +execute if function :cooldown run tellraw @a {"text": "Cooldown Ready!"} +``` diff --git a/src/routes/guide/cooldown/worldclock/+page.svx b/src/routes/guide/cooldown/worldclock/+page.svx new file mode 100644 index 0000000..20c952c --- /dev/null +++ b/src/routes/guide/cooldown/worldclock/+page.svx @@ -0,0 +1,74 @@ +--- +title: Worldclock-based cooldown +description: Learn how to create and check cooldown timers using the world clock. +version: 26.1 +--- + +# Worldclock-based cooldown + +A worldclock cooldown stores a timestamp of when an action was last used in a scoreboard objective. +Your datapack compares this stored time with the current time to determine if the cooldown has ended. + +This is a non-ticking approach: your datapack only does work when the player +tries to use the action. This makes it a good fit for larger datapacks with many +different cooldowns to check. + +## What you will build +- A `cooldown.json` clock in the world clock folder. +- A `timespan` scoreboard objective that stores when the action was last used. +- A `:cooldown` function that checks whether enough time has passed. + +### 1) Setup the world clock +In your datapack namespace, create a folder named `world_clock`. Then, create a file named +`cooldown.json` inside the folder. +```json:cooldown.json +{} +``` +Restart your world/server, then run `/time of :cooldown query time` to verify. +(`/reload` won't work). + +### 2) Create the scoreboard objectives + +The `timespan` scoreboard objective stores the last time the action was used in the `cooldown` world clock. + +```mcfunction +scoreboard objectives add timespan dummy +``` + +### 3) Create the action function (`do_things.mcfunction`) + +When you trigger this function, this is what happens: + +- Store the current time in the `#current` score. +- Subtract `timespan` from `#current` to get ticks since last use. +- Subtract the cooldown length from the result. +- If the result is negative, the player is still on cooldown, return fail. +- Otherwise, store the current time into `timespan` and return 1 (success). + +```mcfunction:cooldown.mcfunction +# Store the current timespan +execute store result score #current timespan run time of :cooldown query time +# ticks since last use = #current - timespan +scoreboard players operation #current timespan -= @s timespan +# cooldown = ticks since last use - cooldown duration (change 100 to your cooldown length in ticks) +scoreboard players remove #current timespan 100 +# If the result is negative, the cooldown is still active, return fail. +execute if score #current timespan matches ..-1 run return fail +# Cooldown finished: save the current gametime as the new last_used timestamp +execute store result score @s timespan run time of :cooldown query time +# Return success +return 1 +``` + +:::warning + +Do not modify the `cooldown` world clock; it will break the cooldown calculations. + +::: + +## Testing + +Check the cooldown in your function: +```mcfunction:my_function +execute if function :cooldown run tellraw @a {"text": "Cooldown Ready!"} +```