# Gradle Setup ## What is Shogi? Shogi is a rule-based effect evaluation system for Minecraft mods. It lets your mod expose named properties and then resolve them against runtime context instead of supporting only static configurations. That means values can depend on things like the player, dimension, distance, item state, or any custom context your integration provides. For most integrations, you only need the embeddable `shogi-api`: your mod exposes hooks, players configure rules, and your mod can still fall back to default behavior when Shogi is not installed. If you need advanced runtime features such as classes from `shogi-common` or `.networked()` values, you will also depend on Shogi itself as a regular mod dependency. ## Prerequisites In order to use Shogi, you should have - an understanding of Java and Minecraft Modding - an IDE like [IntelliJ IDEA](https://www.jetbrains.com/idea/){rel=""nofollow""} - the ability to clone a [Git](https://www.git-scm.com/){rel=""nofollow""} repository ## Adding Shogi to your Project Add Shogi's API as an included library so that it will be embedded inside of your mod's jar. That way, Shogi support will be optional and your mod will fall back to default value providers if players do not have Shogi installed. Note that the artifact is called `shogi-api` and works across all loaders. If you want to use advanced Shogi features, such as classes from `shogi-common` or `.networked()` values, you will need to also add a dependency on `shogi-common` and its loader-specific variants - these artifacts should not be included in your mod's jar and instead be declared as a regular mod dependency. ```groovy [common/build.gradle] repositories { maven { url = 'https://maven.twelveiterations.com/repository/maven-public/' content { includeGroup 'net.blay09.mods' } } } dependencies { implementation("net.blay09.mods:shogi-api:${shogi_version}") { changing = shogi_version.endsWith('SNAPSHOT') } } ``` ```groovy [fabric/build.gradle] repositories { maven { url = 'https://maven.twelveiterations.com/repository/maven-public/' content { includeGroup 'net.blay09.mods' } } } dependencies { include("net.blay09.mods:shogi-api:${shogi_version}") { changing = shogi_version.contains('SNAPSHOT') } } ``` ```groovy [neoforge/build.gradle] repositories { maven { url = 'https://maven.twelveiterations.com/repository/maven-public/' content { includeGroup 'net.blay09.mods' } } } dependencies { implementation("net.blay09.mods:shogi-api:${shogi_version}") { changing = shogi_version.contains('SNAPSHOT') } } ``` ```groovy [forge/build.gradle] repositories { maven { url = 'https://maven.twelveiterations.com/repository/maven-public/' content { includeGroup 'net.blay09.mods' } } } dependencies { implementation("net.blay09.mods:shogi-api:${shogi_version}") { changing = shogi_version.contains('SNAPSHOT') } } ``` ## I'm lost - what should I do? If you can't find the answer you're looking for in these pages, try looking at [Blay's various visible source mods](https://github.com/TwelveIterations){rel=""nofollow""}, or ask a question in the [Balm Developers Discord](https://discord.gg/36qHFMNgAh){rel=""nofollow""}. ## Next guides - [Defining Shogi Values](https://shogi.twelveiterations.com/developers/values) - [Custom Scopes](https://shogi.twelveiterations.com/developers/scopes) - [Releasing your Mod](https://shogi.twelveiterations.com/developers/release) # Defining Shogi Values ## Goal Define a `ShogiValue` that exposes one of your mod's properties to Shogi while keeping a safe default when no rule override applies. ## Minimal example Your mod could expose a property like `yourmod:your_property` like this: ```java public class YourModRules { public static final ShogiScope scope = Shogi.scope(Identifier.fromNamespaceAndPath("yourmod", "rules"), it -> { it.setDefaultNamespaces(List.of("yourmod", "shogi")); }); public static final ShogiValue yourProperty = scope.intValue(id("your_property"), entity -> entity.totalExperience); } ``` This creates an integer-backed value with: - the key `yourmod:your_property` - the scope `yourmod:rules` - an `Entity` resolution context - a fallback value provider that is used if Shogi is not installed ## Defining a ShogiValue Create a dedicated scope for your mod, then use the typed helper on that scope that matches the value you want to expose: ```java public class ExampleModRules { public static final ShogiScope scope = Shogi.scope(Identifier.fromNamespaceAndPath("yourmod", "rules"), it -> { it.setDefaultNamespaces(List.of("yourmod", "shogi")); }); public static final ShogiValue preventDeath = scope.booleanValue(id("prevent_death"), player -> ExampleModConfig.getActive().preventDeath); public static final ShogiValue fallingHeight = scope.intValue(id("falling_height"), entity -> entity.level().getHeight()); public static final ShogiValue titleText = scope.stringValue(id("title_text"), player -> "Hello World"); } ``` Available helpers: - `scope.intValue(...)` - `scope.floatValue(...)` - `scope.booleanValue(...)` - `scope.stringValue(...)` - `scope.componentValue(...)` A mod-owned scope keeps your rule files, default namespaces, and future custom effects under your control. ## Resolving a ShogiValue Once defined, resolve it wherever your mod needs the final value: ```java int targetHeight = ForgivingVoidRules.fallingHeight.getOrDefault(entity); ``` Common resolution methods: - `getOrDefault(context)` to use your default provider on failure - `getOrElse(context, fallback)` to supply a one-off fallback - `getOrThrow(context)` to fail hard if resolution does not produce a success value ## Networked Values Some rules can only be resolved on the server and must be synchronized to clients. ShogiValues that are marked as `.networked()` will resolve to the authoritative server value when resolved on the client. The server will automatically sync updates to this value whenever it is resolved to a new value. ```java public static final ShogiValue canUseOverlay = scope.booleanValue(id("can_use_overlay"), player -> true).networked(); ``` ::warning This is an advanced feature and requires your mod to depend on the full Shogi mod. The embedded `shogi-api` alone will not perform any special handling for networked values. :: ## Scopes When using helpers from your own scope, such as `scope.intValue(...)`, your values are registered on that scope and rules are parsed with that scope's effects and default namespaces. You can register specialized effects onto your custom scope that should only be available for rules on values defined on that scope. Learn more on the [Custom Scopes](https://shogi.twelveiterations.com/developers/scopes) page. ## Rule Overrides After defining a Shogi value, users can target it with rules such as: ```json { "forgivingvoid:falling_height": [ "is_dimension('minecraft:the_nether') -> 180", "220" ] } ``` Rules can be configured in the `config/..json` file, or individually in a datapack file via `data////.json`. For a `yourmod:example` property on a `yourmod:rules` scope, that would be in `config/yourmod.rules.json` and `data/yourmod/yourmod/rules/example.json`. ## Next guides - [Custom Scopes](https://shogi.twelveiterations.com/developers/scopes) - [Releasing your Mod](https://shogi.twelveiterations.com/developers/release) # Custom Scopes ## Goal Use a custom `ShogiScope` when your mod needs its own rule vocabulary instead of exposing everything on Shogi's global default scope. This is the pattern used by mods like Waystones: values are registered on a dedicated scope, and that scope exposes extra effects such as `is_inventory_button`, `is_owner`, or `is_warp_stone`. ## 1. Create a dedicated scope Create the scope once, usually in the same class that declares your Shogi values: ```java public class YourModRules { public static final ShogiScope scope = Shogi.scope(id("rules"), it -> { it.setDefaultNamespaces(List.of("yourmod", "shogi")); }); } ``` What this does: - creates a separate scope identified as `yourmod:rules` - keeps your custom effects out of Shogi's global default scope - allows unqualified names to resolve from `yourmod` first, before falling back to `shogi` If you do not call `setDefaultNamespaces(...)`, the scope will only default to its own namespace. ## 2. Register custom effects on that scope Simple no-argument effects can be added with `registerSimpleEffect(...)`: ```java public static final ShogiScope scope = Shogi.scope(id("rules"), it -> { it.setDefaultNamespaces(List.of("yourmod", "shogi")); it.registerSimpleEffect(id("is_special_mode"), context -> context.level().dimension().location().getPath().equals("the_end")); it.registerSimpleEffect(id("is_owner"), context -> context.entity() instanceof ServerPlayer player && isOwner(player, context)); }); ``` For parameterized effects, register a codec and optional positional argument names: ```java public static final ShogiScope scope = Shogi.scope(id("rules"), it -> { it.setDefaultNamespaces(List.of("yourmod", "shogi")); it.registerEffect(IsWithinCharge.IDENTIFIER, IsWithinCharge.MAP_CODEC, List.of("charge")); it.registerEffect(ApplyCost.IDENTIFIER, ApplyCost.mapCodec(it), List.of("amount")); }); ``` The positional names are what let users write concise expressions like: ```text is_within_charge(200) -> apply_cost(3) ``` That is the same shape Waystones uses for registrations such as: - `it.registerEffect(IsWithinDistance.IDENTIFIER, IsWithinDistance.MAP_CODEC, List.of("distance"))` - `it.registerSimpleEffect(id("is_inventory_button"), ...)` ## 3. Define values on the custom scope Once you have a scope, create values from the scope itself: ```java public class YourModRules { public static final ShogiScope scope = Shogi.scope(id("rules"), it -> { it.setDefaultNamespaces(List.of("yourmod", "shogi")); it.registerSimpleEffect(id("is_special_mode"), context -> isSpecialMode(context)); }); public static final ShogiValue actionCost = scope.intValue(id("action_cost"), context -> 5); public static final ShogiValue allowTeleport = scope.booleanValue(id("allow_teleport"), context -> true); } ``` Any override for `action_cost` will now be parsed against your scope, so your custom effects are available there. ## 4. Tell users where rules go Rule file locations are derived from the scope identifier. For a value `yourmod:action_cost` on scope `yourmod:rules`: - config file: `config/yourmod.rules.json` - datapack file: `data/yourmod/yourmod/rules/action_cost.json` If your scope path contains slashes, config files replace them with dots: - scope `yourmod:teleports/rules` - config file `config/yourmod.teleports.rules.json` - datapack prefix `data//yourmod/teleports/rules/` ## 5. What users can write Because the scope can default to both your namespace and `shogi` when you set them with `setDefaultNamespaces`, users can mix your effects with built-in Shogi ones without writing full namespaces every time. Example expressions for a Waystones-style scope: ```text is_inventory_button -> cooldown_cost('inventory_button', '300s') is_warp_stone -> damage_item(80) is_owner + is_global -> xp_points_cost(0) ``` These work because the scope recognizes mod-specific names like `is_inventory_button` and Shogi names like `cooldown_cost` in the same rule set. ## When to use a custom scope Use a custom scope when: - your mod needs effect names that only make sense for its own context - you want shorthand names like `is_owner` or `is_warp_plate` to resolve cleanly - values for one integration should not accidentally expose effects from another integration - you want to future-proof and avoid breaking changes should you need to expand in the future ## Troubleshooting - Unknown function/effect: the effect was not registered on the same scope as the value being resolved. - Effect only works with full namespace: your `defaultNamespaces` order does not include the namespace you expect. - Rules are not loading: check the scope id and make sure the file path matches `config/..json`. - Value resolves but custom rules never apply: make sure the value was created from the same scope that owns the rule file. ## Next guides - [Defining Shogi Values](https://shogi.twelveiterations.com/developers/values) - [Releasing your Mod](https://shogi.twelveiterations.com/developers/release) # Releasing your Mod Congratulations on your new mod! Before releasing it into the wild, you should make sure some things are set up correctly so players won't run into issues. If you are only using the embedded `shogi-api` to enable support for Shogi in your mod, you do not need to do anything more. Your mod will run just fine whether the user has Shogi installed or not. ### Mod Metadata If you are using advanced Shogi features, such as classes from `shogi-common` or `.networked()` values, make sure that your mod metadata files declare a dependency on Shogi. ```json [fabric/src/main/resources/fabric.mod.json] "depends": { "shogi": ">=${shogi_version}" } ``` ```toml [fabric/src/main/resources/META-INF/neoforge.mods.toml] [[dependencies.yourmodid]] modId="shogi" mandatory=true versionRange="[${shogi_version},)" ordering="NONE" side="BOTH" ``` ```toml [fabric/src/main/resources/META-INF/mods.toml] [[dependencies.yourmodid]] modId="shogi" mandatory=true versionRange="[${shogi_version},)" ordering="NONE" side="BOTH" ``` If you're adding these lines, make sure your Gradle setup correctly replaces the `${shogi_version}` placeholder as well. ### File Dependencies If you are using advanced Shogi features, such as classes from `shogi-common` or `.networked()` values, you should also declare Shogi as a `Required Dependency` on your CurseForge / Modrinth upload. That way players installing your mod with a Launcher will have Shogi installed automatically alongside your mod. ## Mod Hosting Platforms Shogi is available on [CurseForge](https://www.curseforge.com/minecraft/mc-mods/shogi){rel=""nofollow""}. Third Party Downloads are enabled, so Shogi can be easily installed even outside of the official CurseForge App. # Getting Started ## Why Shogi exists Minecraft mod configs go through a lot of churn: they often start simple, and then keep growing and changing as new feature requests come in. Customizability is what makes modding great, but it also makes it more complicated. A teleport cost may need to depend on distance, dimension, item used, or whether the player is on cooldown. Falling through the void may need different behavior per dimension. Reviving a downed player may need to check whether monsters are nearby before allowing another player to help. Without Shogi, each of those mods has to keep adding more purpose-built configuration options for each of those cases. That leads to config creep: larger files, more special-case implementations, and still not enough flexibility for the next player's wild imagination. Shogi takes that complexity and moves it into a shared rule layer. Mods expose selected values or actions to Shogi, and players or modpack authors can override them with contextual rules when they need more control. ## What Shogi does Shogi is a library mod used by Minecraft mods like Waystones to allow for rule-based contextual configuration of properties and events. A supported mod defines a named Shogi value, such as `waystones:warp_stone_use_time`, and instead of merely being a numeric option, it enables the full Shogi effect library to turn it into a dynamic rule. This opens a lot of new possibilities without clogging up the config file: - Having it take longer while in the Nether - Having it take longer while monsters are nearby - Having it take longer the more damaged the warp stone is, etc. But this can go beyond just numeric values too. Mods can use Shogi to define hooks and cost evaluations as well. Waystones uses this for its "Warp Requirements", which defines both whether a warp is allowed as all, as well as all the costs associated with it. Rules then describe how those contexts should be resolved. For example, in Hardcore Revival you can use a Shogi rule to block revival in the Nether or add an item cost, xp cost, cooldown, etc. - all without needing to think of every possible use case up front, through a single rule instead of a conglomerate of config options. ```text [hardcorerevival:can_revive] is_dimension('minecraft:the_nether') -> refuse('You cannot revive others in the Nether.') ``` ```text [hardcorerevival:can_revive] is_dimension('minecraft:the_nether') -> item_cost('golden_apple') ``` This guide is for players and modpack developers who want to customize mods that expose Shogi properties. It is recommended to also consult the mod-specific guides, such as the [Warp Rules](https://mods.twelveiterations.com/minecraft/waystones/guides/warp-rules){rel=""nofollow""} page in the Waystones documentation. Shogi can be used by any mod developer to provide easy extensibility for their configuration. For information on how to use Shogi when developing your own mods, see the [Developer Guide](https://shogi.twelveiterations.com/developers). ## Prerequisites You must install the Shogi mod to configure rules for supported mods. Some mods (like Waystones) already have Shogi marked as required dependency, while others only offer optional Shogi support. If not already installed, [download and install Shogi](https://www.curseforge.com/minecraft/mc-mods/shogi){rel=""nofollow""} like you would any other mod. ## Rule Configuration File The location where rules can be defined differs depending on the support mod. Consult the other mod's documentation to find out where rules should be configured. If not otherwise specified, the file for configuring rules is usually `.rules.json`. If that doesn't work, try `shogi.rules.json` or refer to the mod's documentation. ## Example: Waystones Waystones exposes Shogi values in a `config/waystones.rules.json` file (create it if it does not exist). ```json [config/waystones.rules.json] { "waystones:warp_stone_use_time": [ "32", "is_dimension('minecraft:the_nether') -> 16" ], "waystones:scroll_use_time": [ "32", "is_dimension('minecraft:the_end') -> 48", "is_portal_scroll -> '1s'" ] } ``` This sets a default at 32 ticks, with overrides for a shorter warp stone use time in the Nether, makes scrolls take longer in the End, and makes portal scrolls always take one second (20 ticks). ## Basic Rule Syntax Shogi rules follow a simple pattern: ```text condition -> effect ``` **Some conditions and effects take parameters:** ```text is_dimension('minecraft:the_end') -> refuse('You cannot use Waystones in the End.') ``` Note that effects are evaluated in context of the property they are configured for. For example, when the above rule is configured as part of Waystones' `warpRequirements` option, `is_dimension` in this case would refer to the player's dimension, and `refuse` would determine that a teleport is not allowed. **Multiple parameters are separated by commas, and conditions can be negated with an exclamation mark:** ```text !is_block_state_property('origin', 'player') -> refuse('You cannot break generated Waystones') ``` Again, rules are context-specific, and this example assumes it's defined as part of an [Unbreakables](https://www.curseforge.com/minecraft/mc-mods/unbreakables){rel=""nofollow""} rule. In that case, `is_block_state_property` refers to the block being broken, and `refuse` determines that the block will be unbreakable with the given error message. **Conditions can be composed using plus, comma or contextual modifiers** ```text is_biome('minecraft:forest') + can_see_sky -> refuse('You can't break forest blocks that can see the sky') has_mob_effect('minecraft:poison'), has_mob_effect('minecraft:wither') -> refuse('You can't do this while under the effects of Poison or Wither') offhand(is_item('minecraft:totem_of_undying')) -> refuse('You can't teleport while holding a Totem of Undying') ``` For a full list of all inbuilt conditions and effects, check the [Available Effects](https://shogi.twelveiterations.com/guides/effects) page. Mods like Waystones may also provide additional conditions and effects that can be used; refer to the mod-specific guides for a list of those. ## Next Guides - [Rule Expression Format](https://shogi.twelveiterations.com/guides/expressions) - [Available Effects](https://shogi.twelveiterations.com/guides/effects) - [Advanced: Rules as JSON](https://shogi.twelveiterations.com/advanced/json) # Rule Expression Format ## What this page covers This page documents the full expression format supported by Shogi's expression parser. For a first simple intro, read the [Getting Started](https://shogi.twelveiterations.com/guides) page instead. Use this guide when you want to understand: - the overall rule shape - operator precedence - variables and assignments - how function arguments work - what kinds of syntax are valid or invalid For a list of built-in Shogi conditions and effects, see [Available Effects](https://shogi.twelveiterations.com/guides/effects). ## Rule shapes Shogi accepts both effect calls as well as assignments as top-level forms, with optional conditions: ```text condition -> effect condition -> $variable = expression ``` Examples: ```text xp_points_cost(12) is_dimension('minecraft:the_end') -> refuse('You cannot use this here') $xp_cost = clamp($distance * 0.01, 0, 27) ``` The arrow form means "if the condition matches, run the effect". If the condition does not match, nothing happens. ## Literals Expressions can contain these literal values: - numbers, such as `42` or `0.01` - strings in single or double quotes, such as `'hello'` or `"hello"` - booleans: `true` and `false` Examples: ```text 42 'minecraft:the_nether' true ``` String literals support escaping by prefixing the next character with `\`. ## Variables Variables start with `$`: ```text $distance $player.level $foo.bar ``` Variable names can use dotted paths. These are commonly used when a rule calculates a value from the current evaluation context. Assignments use the same variable syntax on the left-hand side: ```text $result = 5 $xp_cost = $distance * 0.01 ``` ## Function and effect calls Most rule logic is written as effect calls: ```text is_player can_see_sky failure('Players only') clamp($value, 0, 27) ``` ### Zero-argument calls Zero-argument effects can usually be written without parentheses: ```text is_player can_see_sky dismount ``` The parenthesized form is also valid when the effect actually takes no arguments: ```text is_player() ``` ### Positional arguments Effects that register positional parameters can be called like this: ```text is_dimension('minecraft:the_end') clamp($distance * 0.01, 0, 27) if(can_see_sky, 180, 220) ``` Arguments are matched by their registered parameter order. For example, `if(...)` uses: ```text if(condition, then, else) ``` Some effects support a variadic positional list, such as: ```text and(is_player, can_see_sky, has_item('minecraft:ender_pearl', 2)) any(is_dimension('minecraft:the_nether'), is_dimension('minecraft:the_end')) aggregate(item_cost('minecraft:ender_pearl', 1), xp_points_cost(3)) ``` ### Named arguments Effects can also be called with named arguments: ```text if(condition = can_see_sky, then = 180, else = 220) clamp(value = $distance * 0.01, min = 0, max = 27) ``` Named arguments may be written in any order: ```text if(condition = can_see_sky, else = 27, then = $distance * 0.01) ``` ### Do not mix named and positional arguments One call must use exactly one style: ```text binary_op('+', 1, 2) binary_op(op = '+', left = 1, right = 2) ``` This is invalid: ```text binary_op(op = '+', 1, 2) ``` ## Conditions Conditions are the part before `->`: ```text condition -> effect ``` Examples: ```text is_player -> true can_see_sky -> xp_points_cost(3) is_dimension('minecraft:the_end') -> refuse('Disabled here') ``` ### Negation Use `!` to negate a condition: ```text !is_player -> failure('Players only') ``` You can also negate a grouped condition: ```text !(is_player, can_see_sky) -> failure('Condition failed') ``` ### AND with `+` Use `+` when all conditions must match: ```text is_player + can_see_sky -> true ``` ### OR with `,` Use `,` when any condition may match: ```text is_dimension('minecraft:the_nether'), is_dimension('minecraft:the_end') -> 256 ``` ### Grouping with parentheses Use parentheses to group condition logic explicitly: ```text is_player + (can_see_sky, is_dimension('minecraft:the_end')) -> true (is_player, has_item('minecraft:ender_pearl', 2)) + can_see_sky -> true ``` ### Condition precedence Condition operators are parsed in this order: 1. `!` 2. `+` 3. `,` So this: ```text noop + noop, noop -> noop ``` is parsed like this: ```text (noop + noop), noop -> noop ``` If you want a different grouping, add parentheses. ## Expressions and arithmetic The right-hand side of a rule, and function arguments inside calls, can be general expressions. Arithmetic operators: - `*` - `/` - `+` - `-` Examples: ```text $distance * 0.01 1 + 2 * 3 (1 + 2) * 3 $xp_cost = clamp($distance * 0.01, 0, 27) ``` Arithmetic precedence follows normal rules: 1. unary `!` 2. `*` and `/` 3. `+` and `-` Parentheses override precedence. `!` is also valid inside expressions, not only in top-level conditions: ```text $result = !true + 1 $result = !has_cooldown('inventory_button') ``` ## Identifier rules and namespaces Effect names use identifiers such as: ```text is_player shogi:is_player waystones:is_owner use('test:other_rule') ``` In most user-facing configs, you can omit the namespace and write the short name. The scope decides which namespaces are searched first. For example, a scope that defaults to `waystones` and `shogi` can resolve both: ```text is_owner + is_global -> xp_points_cost(0) cooldown_cost('inventory_button', '300s') ``` If multiple namespaces are configured, the first matching effect wins. ## Whitespace Whitespace is optional around operators and punctuation. These parse the same way: ```text noop + noop, noop -> noop noop+noop,noop->noop ``` Use spaces anyway when possible, because they are much easier to read. ## Common patterns ### Simple conditional rule ```text is_dimension('minecraft:the_end') -> refuse('You cannot use this here') ``` ### Combined conditions ```text is_player + can_see_sky -> xp_points_cost(3) ``` ### Any-of condition ```text is_dimension('minecraft:the_nether'), is_dimension('minecraft:the_end') -> 256 ``` ### Nested contextual call ```text offhand(is_item('minecraft:totem_of_undying')) -> refuse('Totems block this action') ``` ### Assignment with arithmetic ```text $xp_cost = clamp($distance * 0.01, 0, 27) ``` ### Conditional expression with named arguments ```text $xp_cost = if(condition = can_see_sky, else = 27, then = $distance * 0.01) ``` ## Common parse errors These are common problems when writing expressions: - mixed named and positional arguments in the same call - duplicate named parameters such as `op = '+', op = '-'` - too many positional arguments for the chosen effect - unknown or invalid effect identifiers - trailing tokens after a complete expression - missing closing parentheses - empty condition groups like `() -> noop` Examples of invalid syntax: ```text binary_op(op = '+', 1, 2) binary_op(op = '+', op = '-', left = 1, right = 2) 1 2 () -> noop noop + () -> noop ``` ## When to use JSON instead Expressions are the shortest and most convenient format for most rules. Switch to JSON when: - a rule becomes too deeply nested to read comfortably - you want the explicit object shape of each effect - you are generating rules programmatically See [Advanced: Rules as JSON](https://shogi.twelveiterations.com/advanced/json) for the JSON form. ## Next guides - [Available Effects](https://shogi.twelveiterations.com/guides/effects) - [Advanced: Rules as JSON](https://shogi.twelveiterations.com/advanced/json) # Available Effects ## What this page covers This is a reference for the built-in `shogi` effects available in the default scope. ## Reading this reference - zero-argument effects can usually be written without parentheses, such as `is_player` or `can_see_sky` - string-like ids should be quoted, such as `'minecraft:the_nether'` or `'inventory_button'` - some effects support either positional arguments like `clamp($value, 0, 27)` or named arguments like `if(condition = is_player, then = true, else = false)`, but do not mix the two styles in one call For operator syntax, grouping, variables, and general expression rules, see [Rule Expression Format](https://shogi.twelveiterations.com/guides/expressions). ## Control and logic - `if(condition, then, else)` chooses between two branches based on a condition. Example: `if(condition = is_dimension('minecraft:the_nether'), then = 180, else = 220)` - `not(condition)` negates a condition, but in expressions `!condition` is usually shorter. Example: `!is_player -> failure('Players only')` - `and(...)` requires every condition to match, but in expressions `+` is the usual syntax. Example: `is_player + can_see_sky -> true` - `any(...)` matches when any listed condition matches, but in expressions `,` is the usual syntax. Example: `is_dimension('minecraft:the_nether'), is_dimension('minecraft:the_end') -> 256` - `use(identifier)` imports another named Shogi rule by id. Example: `use('pack:shared_cost_rule')` ## Math helpers - `clamp(value, min, max)` keeps a computed value inside a range. Example: `clamp($distance * 0.01, 0, 27)` - `clamp_min(value, min)` raises a value up to a minimum floor. Example: `clamp_min($height, 64)` - `clamp_max(value, max)` lowers a value down to a maximum ceiling. Example: `clamp_max($height, 256)` ## Context helpers - `any_hand(condition)` checks the player's main hand and offhand against the same nested condition. Example: `any_hand(has_enchantment('minecraft:silk_touch')) -> true` - `offhand(effect)` evaluates a nested effect using the player's offhand item context. Example: `offhand(is_item('minecraft:totem_of_undying')) -> true` ## Entity, player, and item checks - `is_player` matches when the current entity context is a player. Example: `is_player -> true` - `has_entity_tag(tag)` checks whether the current entity has a given entity tag. Example: `has_entity_tag('example') -> true` - `has_mob_effect(effect)` checks whether the current entity has a matching status effect. Example: `has_mob_effect('minecraft:poison') -> true` - `is_on_any_vehicle` matches when the current entity is riding something. Example: `is_on_any_vehicle -> dismount` - `is_on_vehicle(vehicle)` matches when the current entity is riding a specific vehicle type. Example: `is_on_vehicle('minecraft:boat') -> true` - `has_item(item, count)` checks whether the player inventory contains enough matching items, defaulting `count` to `1`. Example: `has_item('minecraft:ender_pearl', 2) -> true` - `has_empty_inventory` matches when the player inventory is empty. Example: `has_empty_inventory -> true` - `is_wearing_any_armor` matches when the player has any armor equipped. Example: `is_wearing_any_armor -> true` - `is_item(item)` checks whether the current item context matches a specific item. Example: `is_item('minecraft:elytra') -> damage_item(1)` - `has_enchantment(enchantment, level)` checks whether the current item has an enchantment at or above the given level, defaulting `level` to `1`. Example: `has_enchantment('minecraft:unbreaking', 3) -> true` ## Position and world checks - `can_see_sky` matches when the current block position has direct sky access. Example: `can_see_sky -> true` - `is_above_y(y)` matches when the current position is above the given Y value. Example: `is_above_y(200) -> true` - `is_below_y(y)` matches when the current position is below the given Y value. Example: `is_below_y(0) -> failure('Too low')` - `is_at(pos)` matches one exact block position. Example: compare against a fixed teleport pad location. - `is_near(pos, distance)` matches when the current position is within range of a target block position. Example: compare against a nearby anchor point with a small radius. - `is_within(bounds)` matches when the current position is inside a bounding box. Example: restrict a rule to a defined safe region. - `is_dimension(dimension)` matches the current dimension id. Example: `is_dimension('minecraft:the_end') -> 256` - `is_biome(biome)` matches the biome at the current position. Example: `is_biome('minecraft:desert') -> true` - `is_block(block)` matches the block at the current position. Example: `is_block('minecraft:respawn_anchor') -> true` - `is_block_state_property(property, value)` checks a block-state property at the current position. Example: `is_block_state_property('powered', 'true') -> true` - `is_entity_nearby(entity, distance, min)` checks whether enough matching entities are within range, defaulting `min` to `1`. Example: `is_entity_nearby('minecraft:villager', 24, 1) -> true` - `is_animal_nearby(distance, min)` checks for nearby animals, defaulting `min` to `1`. Example: `is_animal_nearby(16, 3) -> true` - `is_mob_nearby(distance, min)` checks for nearby mobs, defaulting `min` to `1`. Example: `is_mob_nearby(12, 1) -> failure('Unsafe area')` - `is_player_nearby(distance, min)` checks for nearby players, defaulting `min` to `1`. Example: `is_player_nearby(12, 1) -> true` - `is_near_poi(poi, distance)` checks for a nearby point of interest. Example: match when the player is close to a configured village-style POI. ## Outcomes, costs, and actions - `failure(message)` returns a failure result with a message. Example: `failure('You need more XP')` - `refuse(message)` returns a refusal result with a message. Example: `refuse('This teleport source is disabled')` - `dismount` makes the player stop riding. Example: `is_on_any_vehicle -> dismount` - `damage_item(amount)` damages the current item stack by a non-negative amount. Example: `damage_item(80)` - `item_cost(item, count)` consumes matching items from the player inventory, defaulting `count` to `1`. Example: `item_cost('minecraft:ender_pearl', 1)` - `xp_points_cost(xp)` charges raw experience points from the player. Example: `xp_points_cost(12)` - `xp_level_cost(level)` charges whole experience levels from the player. Example: `xp_level_cost(3)` - `has_advancement(advancement)` checks whether the player has completed an advancement. Example: `has_advancement('minecraft:story/mine_diamond') -> true` - `has_cooldown(identifier)` checks whether a named Shogi cooldown currently exists. Example: `has_cooldown('inventory_button') -> failure('Still cooling down')` - `is_cooldown_above(cooldown, duration)` checks whether a cooldown has at least the given remaining duration. Example: `is_cooldown_above('inventory_button', '30s') -> true` - `add_cooldown(identifier, duration)` always adds or refreshes a named cooldown. Example: `add_cooldown('inventory_button', '300s')` - `cooldown_cost(identifier, duration)` only succeeds when the named cooldown is not already active, unlike `add_cooldown`, and it only resolves on the server side. Example: `cooldown_cost('inventory_button', '300s')` ## Next guides - [Rule Expression Format](https://shogi.twelveiterations.com/guides/expressions) - [Advanced: Rules as JSON](https://shogi.twelveiterations.com/advanced/json) # Using JSON ## Expression vs JSON Shogi supports both: - expression syntax (short, config-friendly) - JSON syntax (explicit, structured) Expressions are easier to read and more concise. Use JSON only when you need complex structure that would get messy within expressions. ## Minimal JSON anatomy A Shogi JSON rule usually includes: - `type`: the effect/operation identifier - effect-specific fields: parameters consumed by that type Example concepts: - constants use `shogi:constant` with a value field - conditions and branches use types such as `shogi:if` - custom mod effects use their own namespaced `type` ## Translation example Expression: ```text is_dimension('minecraft:the_nether') -> failure('Disabled in the Nether') ``` Equivalent JSON shape: ```json { "type": "shogi:if", "condition": { "type": "shogi:is_dimension", "dimension": "minecraft:the_nether" }, "then": { "type": "shogi:failure", "message": "Disabled in the Nether" } } ```