# Custom Games

There are two methods to register a custom game type: using an expansion jar or a plugin. Using the **expansion jar** method requires placing the built jar file into the "expansion" folder, similar to how it works with PlaceholderAPI. If you're using the **plugin** method to register a custom game type, after adding the custom game factory, you will need to call the **CustomFishing's reload** function to apply the changes.

### Using a plugin

```java
BukkitCustomFishingPlugin.getInstance().getGameManager().registerGameType("accurate_click_v2", ((id, section) -> {
    GameBasics basics = getGameBasics(section);
    return new AbstractGame(id, basics) {

        private final String barWidth = section.getString("title.total-width", "15~20");
        private final String barSuccess = section.getString("title.success-width","3~4");
        private final String barBody = section.getString("title.body","");
        private final String left = section.getString("title.left","");
        private final String right = section.getString("title.right","");
        private final String barPointer = section.getString("title.pointer", "");
        private final String barTarget = section.getString("title.target","");
        private final String subtitle = section.getString("subtitle", "<gray>Reel in at the most critical moment</gray>");

        @Override
        public BiFunction<CustomFishingHook, GameSetting, AbstractGamingPlayer> gamingPlayerProvider() {

            int minWidth = Integer.parseInt(barWidth.split("~")[0]);
            int maxWidth = Integer.parseInt(barWidth.split("~")[1]);
            int minSuccess = Integer.parseInt(barSuccess.split("~")[0]);
            int maxSuccess = Integer.parseInt(barSuccess.split("~")[1]);

            return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) {

                private final int totalWidth = RandomUtils.generateRandomInt(minWidth, maxWidth);
                private final int successWidth = RandomUtils.generateRandomInt(minSuccess, maxSuccess);
                private final int successPosition = ThreadLocalRandom.current().nextInt((totalWidth - successWidth + 1)) + 1;
                private int currentIndex = 0;
                private int timer = 0;
                private boolean face = true;

                @Override
                protected void tick() {
                    timer++;
                    if (timer % ((106 - (int) settings.difficulty()) / 5) == 0) {
                        movePointer();
                    }
                    showUI();
                }

                private void movePointer() {
                    if (face) {
                        currentIndex++;
                        if (currentIndex >= totalWidth - 1) {
                            face = false;
                        }
                    } else {
                        currentIndex--;
                        if (currentIndex <= 0) {
                            face = true;
                        }
                    }
                }

                private void showUI() {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (int i = 1; i <= totalWidth; i++) {
                        if (i == currentIndex + 1) {
                            stringBuilder.append(barPointer);
                            continue;
                        }
                        if (i >= successPosition && i <= successPosition + successWidth - 1) {
                            stringBuilder.append(barTarget);
                            continue;
                        }
                        stringBuilder.append(barBody);
                    }

                    SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(left + stringBuilder + right), AdventureHelper.miniMessageToJson(subtitle), 0, 20, 0);
                }

                @Override
                public boolean isSuccessful() {
                    if (isTimeOut) return false;
                    return currentIndex + 1 <= successPosition + successWidth - 1 && currentIndex + 1 >= successPosition;
                }
            };
        }
    };
}));
```

### Using an expansion jar

```java
package net.momirealms.customfishing.api;

import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook;
import net.momirealms.customfishing.api.mechanic.game.*;
import net.momirealms.customfishing.api.mechanic.misc.value.MathValue;
import net.momirealms.customfishing.common.helper.AdventureHelper;
import net.momirealms.customfishing.common.util.RandomUtils;
import net.momirealms.sparrow.heart.SparrowHeart;

import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;

public class CustomGameFactory extends GameExpansion {
    @Override
    public String getVersion() {
        return "1.0";
    }

    @Override
    public String getAuthor() {
        return "XiaoMoMi";
    }

    @Override
    public String getGameType() {
        return "accurate_click_v2";
    }

    @Override
    public GameFactory getGameFactory() {
        return ((id, section) -> {
            GameBasics basics = getGameBasics(section);
            return new AbstractGame(id, basics) {

                private final String barWidth = section.getString("title.total-width", "15~20");
                private final String barSuccess = section.getString("title.success-width","3~4");
                private final String barBody = section.getString("title.body","");
                private final String left = section.getString("title.left","");
                private final String right = section.getString("title.right","");
                private final String barPointer = section.getString("title.pointer", "");
                private final String barTarget = section.getString("title.target","");
                private final String subtitle = section.getString("subtitle", "<gray>Reel in at the most critical moment</gray>");

                @Override
                public BiFunction<CustomFishingHook, GameSetting, AbstractGamingPlayer> gamingPlayerProvider() {

                    int minWidth = Integer.parseInt(barWidth.split("~")[0]);
                    int maxWidth = Integer.parseInt(barWidth.split("~")[1]);
                    int minSuccess = Integer.parseInt(barSuccess.split("~")[0]);
                    int maxSuccess = Integer.parseInt(barSuccess.split("~")[1]);

                    return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) {

                        private final int totalWidth = RandomUtils.generateRandomInt(minWidth, maxWidth);
                        private final int successWidth = RandomUtils.generateRandomInt(minSuccess, maxSuccess);
                        private final int successPosition = ThreadLocalRandom.current().nextInt((totalWidth - successWidth + 1)) + 1;
                        private int currentIndex = 0;
                        private int timer = 0;
                        private boolean face = true;

                        @Override
                        protected void tick() {
                            timer++;
                            if (timer % ((106 - (int) settings.difficulty()) / 5) == 0) {
                                movePointer();
                            }
                            showUI();
                        }

                        private void movePointer() {
                            if (face) {
                                currentIndex++;
                                if (currentIndex >= totalWidth - 1) {
                                    face = false;
                                }
                            } else {
                                currentIndex--;
                                if (currentIndex <= 0) {
                                    face = true;
                                }
                            }
                        }

                        private void showUI() {
                            StringBuilder stringBuilder = new StringBuilder();
                            for (int i = 1; i <= totalWidth; i++) {
                                if (i == currentIndex + 1) {
                                    stringBuilder.append(barPointer);
                                    continue;
                                }
                                if (i >= successPosition && i <= successPosition + successWidth - 1) {
                                    stringBuilder.append(barTarget);
                                    continue;
                                }
                                stringBuilder.append(barBody);
                            }

                            SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(left + stringBuilder + right), AdventureHelper.miniMessageToJson(subtitle), 0, 20, 0);
                        }

                        @Override
                        public boolean isSuccessful() {
                            if (isTimeOut) return false;
                            return currentIndex + 1 <= successPosition + successWidth - 1 && currentIndex + 1 >= successPosition;
                        }
                    };
                }
            };
        });
    }

    private GameBasics getGameBasics(Section section) {
        return GameBasics.builder()
                .difficulty(MathValue.auto(section.get("difficulty", "20~80"), false))
                .time(MathValue.auto(section.get("time", 15), false))
                .build();
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mo-mi.gitbook.io/xiaomomi-plugins/customfishing/plugin-wiki/customfishing/api/custom-games.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
