9
0
mirror of https://github.com/Xiao-MoMi/Custom-Fishing.git synced 2025-12-19 15:09:24 +00:00
This commit is contained in:
XiaoMoMi
2025-01-03 20:54:37 +08:00
parent 611f2f711c
commit dd36074add
10 changed files with 201 additions and 111 deletions

View File

@@ -473,7 +473,7 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
public abstract List<Pair<String, WeightOperation>> parseWeightOperation(List<String> ops, Function<String, Boolean> validator, Function<String, List<String>> groupProvider);
public abstract List<Pair<String, WeightOperation>> parseGroupWeightOperation(List<String> gops);
public abstract List<Pair<String, WeightOperation>> parseGroupWeightOperation(List<String> gops, boolean forAvailable, Function<String, List<String>> groupProvider);
@Deprecated
public Map<String, Node<ConfigParserFunction>> getDefaultFormatFunctions() {

View File

@@ -27,14 +27,19 @@ public class AddWeightOperation implements WeightOperation {
private final MathValue<Player> arg;
private final int sharedMembers;
private final boolean forAvailable;
public AddWeightOperation(MathValue<Player> arg, int sharedMembers) {
public AddWeightOperation(MathValue<Player> arg, int sharedMembers, boolean forAvailable) {
this.arg = arg;
this.sharedMembers = sharedMembers;
this.forAvailable = forAvailable;
}
@Override
public Double apply(Context<Player> context, Double weight, Map<String, Double> weights) {
if (this.forAvailable && weight <= 0) {
return weight;
}
return weight + arg.evaluate(context) / sharedMembers;
}
}

View File

@@ -34,17 +34,22 @@ public class CustomWeightOperation implements WeightOperation {
private final List<String> otherEntries;
private final List<Pair<String, String[]>> otherGroups;
private final int sharedMembers;
private final boolean forAvailable;
public CustomWeightOperation(MathValue<Player> arg, boolean hasTotalWeight, List<String> otherEntries, List<Pair<String, String[]>> otherGroups, int sharedMembers) {
public CustomWeightOperation(MathValue<Player> arg, boolean hasTotalWeight, List<String> otherEntries, List<Pair<String, String[]>> otherGroups, int sharedMembers, boolean forAvailable) {
this.arg = arg;
this.hasTotalWeight = hasTotalWeight;
this.otherEntries = otherEntries;
this.otherGroups = otherGroups;
this.sharedMembers = sharedMembers;
this.forAvailable = forAvailable;
}
@Override
public Double apply(Context<Player> context, Double weight, Map<String, Double> weights) {
if (this.forAvailable && weight <= 0) {
return weight;
}
context.arg(ContextKeys.WEIGHT, weight);
if (hasTotalWeight) {
context.arg(ContextKeys.TOTAL_WEIGHT, getValidTotalWeight(weights.values()));

View File

@@ -26,13 +26,18 @@ import java.util.Map;
public class DivideWeightOperation implements WeightOperation {
private final MathValue<Player> arg;
private final boolean forAvailable;
public DivideWeightOperation(MathValue<Player> arg) {
public DivideWeightOperation(MathValue<Player> arg, boolean forAvailable) {
this.arg = arg;
this.forAvailable = forAvailable;
}
@Override
public Double apply(Context<Player> context, Double weight, Map<String, Double> weights) {
if (this.forAvailable && weight <= 0) {
return weight;
}
return weight / arg.evaluate(context);
}
}

View File

@@ -26,13 +26,18 @@ import java.util.Map;
public class ModuloWeightOperation implements WeightOperation {
private final MathValue<Player> arg;
private final boolean forAvailable;
public ModuloWeightOperation(MathValue<Player> arg) {
public ModuloWeightOperation(MathValue<Player> arg, boolean forAvailable) {
this.arg = arg;
this.forAvailable = forAvailable;
}
@Override
public Double apply(Context<Player> context, Double weight, Map<String, Double> weights) {
if (this.forAvailable && weight <= 0) {
return weight;
}
return weight % arg.evaluate(context);
}
}

View File

@@ -26,13 +26,18 @@ import java.util.Map;
public class MultiplyWeightOperation implements WeightOperation {
private final MathValue<Player> arg;
private final boolean forAvailable;
public MultiplyWeightOperation(MathValue<Player> arg) {
public MultiplyWeightOperation(MathValue<Player> arg, boolean forAvailable) {
this.arg = arg;
this.forAvailable = forAvailable;
}
@Override
public Double apply(Context<Player> context, Double weight, Map<String, Double> weights) {
if (this.forAvailable && weight <= 0) {
return weight;
}
return weight * arg.evaluate(context);
}
}

View File

@@ -27,14 +27,19 @@ public class ReduceWeightOperation implements WeightOperation {
private final MathValue<Player> arg;
private final int sharedMembers;
private final boolean forAvailable;
public ReduceWeightOperation(MathValue<Player> arg, int sharedMembers) {
public ReduceWeightOperation(MathValue<Player> arg, int sharedMembers, boolean forAvailable) {
this.arg = arg;
this.sharedMembers = sharedMembers;
this.forAvailable = forAvailable;
}
@Override
public Double apply(Context<Player> context, Double weight, Map<String, Double> weights) {
if (this.forAvailable && weight < 0) {
return weight;
}
return weight - arg.evaluate(context) / sharedMembers;
}
}

View File

@@ -30,6 +30,8 @@ import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import dev.dejvokep.boostedyaml.utils.format.NodeRole;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.event.FishingEffectApplyEvent;
import net.momirealms.customfishing.api.event.FishingResultEvent;
import net.momirealms.customfishing.api.mechanic.MechanicType;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.action.ActionManager;
@@ -649,7 +651,7 @@ public class BukkitConfigManager extends ConfigManager {
}));
}
case "group-mod", "group_mod" -> {
var op = parseGroupWeightOperation(section.getStringList("value"));
var op = parseGroupWeightOperation(section.getStringList("value"), true, groupProvider);
return (((effect, context, phase) -> {
if (phase == 1) {
effect.weightOperations(op);
@@ -658,7 +660,7 @@ public class BukkitConfigManager extends ConfigManager {
}));
}
case "group-mod-ignore-conditions", "group_mod_ignore_conditions" -> {
var op = parseGroupWeightOperation(section.getStringList("value"));
var op = parseGroupWeightOperation(section.getStringList("value"), false, groupProvider);
return (((effect, context, phase) -> {
if (phase == 1) {
effect.weightOperationsIgnored(op);
@@ -786,15 +788,15 @@ public class BukkitConfigManager extends ConfigManager {
}
}
private WeightOperation parseSharedGroupWeight(String op, int memberCount) {
private WeightOperation parseSharedGroupWeight(String op, int memberCount, boolean forAvailable, Function<String, List<String>> groupProvider) {
switch (op.charAt(0)) {
case '-' -> {
MathValue<Player> arg = MathValue.auto(op.substring(1));
return new ReduceWeightOperation(arg, memberCount);
return new ReduceWeightOperation(arg, memberCount, forAvailable);
}
case '+' -> {
MathValue<Player> arg = MathValue.auto(op.substring(1));
return new AddWeightOperation(arg, memberCount);
return new AddWeightOperation(arg, memberCount, forAvailable);
}
case '=' -> {
String expression = op.substring(1);
@@ -806,43 +808,42 @@ public class BukkitConfigManager extends ConfigManager {
if (placeholder.startsWith("{entry_")) {
otherEntries.add(placeholder.substring("{entry_".length(), placeholder.length() - 1));
} else if (placeholder.startsWith("{group")) {
// only for loots
String groupName = placeholder.substring("{group_".length(), placeholder.length() - 1);
List<String> members = plugin.getLootManager().getGroupMembers(groupName);
String groupExpression = placeholder.substring("{group_".length(), placeholder.length() - 1);
List<String> members = getGroupMembers(groupExpression, groupProvider);
if (members.isEmpty()) {
plugin.getPluginLogger().warn("Failed to load expression: " + expression + ". Invalid group: " + groupName);
plugin.getPluginLogger().warn("Failed to load expression: " + expression + ". Invalid group: " + groupExpression);
continue;
}
otherGroups.add(Pair.of(groupName, members.toArray(new String[0])));
otherGroups.add(Pair.of(groupExpression, members.toArray(new String[0])));
}
}
return new CustomWeightOperation(arg, expression.contains("{1}"), otherEntries, otherGroups, memberCount);
return new CustomWeightOperation(arg, expression.contains("{1}"), otherEntries, otherGroups, memberCount, forAvailable);
}
default -> throw new IllegalArgumentException("Invalid shared weight operation: " + op);
}
}
private WeightOperation parseWeightOperation(String op) {
private WeightOperation parseWeightOperation(String op, boolean forAvailable, Function<String, List<String>> groupProvider) {
switch (op.charAt(0)) {
case '/' -> {
MathValue<Player> arg = MathValue.auto(op.substring(1));
return new DivideWeightOperation(arg);
return new DivideWeightOperation(arg, forAvailable);
}
case '*' -> {
MathValue<Player> arg = MathValue.auto(op.substring(1));
return new MultiplyWeightOperation(arg);
return new MultiplyWeightOperation(arg, forAvailable);
}
case '-' -> {
MathValue<Player> arg = MathValue.auto(op.substring(1));
return new ReduceWeightOperation(arg, 1);
return new ReduceWeightOperation(arg, 1, forAvailable);
}
case '%' -> {
MathValue<Player> arg = MathValue.auto(op.substring(1));
return new ModuloWeightOperation(arg);
return new ModuloWeightOperation(arg, forAvailable);
}
case '+' -> {
MathValue<Player> arg = MathValue.auto(op.substring(1));
return new AddWeightOperation(arg, 1);
return new AddWeightOperation(arg, 1, forAvailable);
}
case '=' -> {
String expression = op.substring(1);
@@ -854,17 +855,16 @@ public class BukkitConfigManager extends ConfigManager {
if (placeholder.startsWith("{entry_")) {
otherEntries.add(placeholder.substring("{entry_".length(), placeholder.length() - 1));
} else if (placeholder.startsWith("{group")) {
// only for loots
String groupName = placeholder.substring("{group_".length(), placeholder.length() - 1);
List<String> members = plugin.getLootManager().getGroupMembers(groupName);
String groupExpression = placeholder.substring("{group_".length(), placeholder.length() - 1);
List<String> members = getGroupMembers(groupExpression, groupProvider);
if (members.isEmpty()) {
plugin.getPluginLogger().warn("Failed to load expression: " + expression + ". Invalid group: " + groupName);
plugin.getPluginLogger().warn("Failed to load expression: " + expression + ". Invalid group: " + groupExpression);
continue;
}
otherGroups.add(Pair.of(groupName, members.toArray(new String[0])));
otherGroups.add(Pair.of(groupExpression, members.toArray(new String[0])));
}
}
return new CustomWeightOperation(arg, expression.contains("{1}"), otherEntries, otherGroups, 1);
return new CustomWeightOperation(arg, expression.contains("{1}"), otherEntries, otherGroups, 1, forAvailable);
}
default -> throw new IllegalArgumentException("Invalid weight operation: " + op);
}
@@ -885,39 +885,68 @@ public class BukkitConfigManager extends ConfigManager {
plugin.getPluginLogger().warn("Illegal weight operation: " + op + ". Id " + id + " is not valid");
continue;
}
result.add(Pair.of(id, parseWeightOperation(split[1])));
result.add(Pair.of(id, parseWeightOperation(split[1], false, groupProvider)));
} else {
String type = split[0];
String id = split[1];
switch (type) {
case "group_for_each" -> {
List<String> members = groupProvider.apply(id);
List<String> members = getGroupMembers(id, groupProvider);
if (members.isEmpty()) {
plugin.getPluginLogger().warn("Failed to load expression: " + op + ". Invalid group: " + id);
continue;
}
WeightOperation operation = parseWeightOperation(split[2]);
WeightOperation operation = parseWeightOperation(split[2], false, groupProvider);
for (String member : members) {
result.add(Pair.of(member, operation));
}
}
case "group_available_for_each" -> {
List<String> members = getGroupMembers(id, groupProvider);
if (members.isEmpty()) {
plugin.getPluginLogger().warn("Failed to load expression: " + op + ". Invalid group: " + id);
continue;
}
WeightOperation operation = parseWeightOperation(split[2], true, groupProvider);
for (String member : members) {
result.add(Pair.of(member, operation));
}
}
case "group_total" -> {
List<String> members = groupProvider.apply(id);
List<String> members = getGroupMembers(id, groupProvider);
if (members.isEmpty()) {
plugin.getPluginLogger().warn("Failed to load expression: " + op + ". Invalid group: " + id);
continue;
}
WeightOperation operation = parseSharedGroupWeight(split[2], members.size());
WeightOperation operation = parseSharedGroupWeight(split[2], members.size(), false, groupProvider);
for (String member : members) {
result.add(Pair.of(member, operation));
}
}
case "group_available_total" -> {
List<String> members = getGroupMembers(id, groupProvider);
if (members.isEmpty()) {
plugin.getPluginLogger().warn("Failed to load expression: " + op + ". Invalid group: " + id);
continue;
}
WeightOperation operation = parseSharedGroupWeight(split[2], members.size(), true, groupProvider);
for (String member : members) {
result.add(Pair.of(member, operation));
}
}
case "loot_available" -> {
if (!validator.apply(id)) {
plugin.getPluginLogger().warn("Illegal weight operation: " + op + ". Id " + id + " is not valid");
continue;
}
result.add(Pair.of(id, parseWeightOperation(split[2], true, groupProvider)));
}
default -> {
if (!validator.apply(id)) {
plugin.getPluginLogger().warn("Illegal weight operation: " + op + ". Id " + id + " is not valid");
continue;
}
result.add(Pair.of(id, parseWeightOperation(split[2])));
result.add(Pair.of(id, parseWeightOperation(split[2], false, groupProvider)));
}
}
}
@@ -926,7 +955,7 @@ public class BukkitConfigManager extends ConfigManager {
}
@Override
public List<Pair<String, WeightOperation>> parseGroupWeightOperation(List<String> gops) {
public List<Pair<String, WeightOperation>> parseGroupWeightOperation(List<String> gops, boolean forAvailable, Function<String, List<String>> groupProvider) {
List<Pair<String, WeightOperation>> result = new ArrayList<>();
for (String gop : gops) {
String[] split = gop.split(":", 2);
@@ -934,14 +963,43 @@ public class BukkitConfigManager extends ConfigManager {
plugin.getPluginLogger().warn("Illegal weight operation: " + gop);
continue;
}
WeightOperation operation = parseWeightOperation(split[1]);
for (String member : plugin.getLootManager().getGroupMembers(split[0])) {
WeightOperation operation = parseWeightOperation(split[1], forAvailable, groupProvider);
String groupExpression = split[0];
for (String member : getGroupMembers(groupExpression, groupProvider)) {
result.add(Pair.of(member, operation));
}
}
FishingEffectApplyEvent event;
if (event.getStage() == FishingEffectApplyEvent.Stage.FISHING) {
event.getEffect().difficultyAdder(event.getEffect().difficultyAdder() + 10d);
}
return result;
}
private List<String> getGroupMembers(String groupExpression, Function<String, List<String>> groupProvider) {
if (groupExpression.contains("&")) {
String[] groups = groupExpression.split("&");
List<Set<String>> groupSets = new ArrayList<>();
for (String group : groups) {
groupSets.add(new HashSet<>(groupProvider.apply(group)));
}
Set<String> intersection = groupSets.get(0);
for (int i = 1; i < groupSets.size(); i++) {
intersection.retainAll(groupSets.get(i));
}
return new ArrayList<>(intersection);
} else if (groupExpression.contains("|")) {
Set<String> members = new HashSet<>();
String[] groups = groupExpression.split("&");
for (String group : groups) {
members.addAll(groupProvider.apply(group));
}
return new ArrayList<>(members);
} else {
return groupProvider.apply(groupExpression);
}
}
private void registerBuiltInHookParser() {
this.registerHookParser(object -> {
List<String> lore = ListUtils.toList(object);

View File

@@ -231,7 +231,9 @@ tuna_fish:
- <gray>Tuna is a kind of healthy food.
- '<white>size: {size_formatted}cm'
custom-model-data: 50001
group: ocean
group:
- ocean
- no_star
events:
success:
action_mending:
@@ -299,7 +301,9 @@ pike_fish:
- <gray>water and inland freshwater lakes
- '<white>size: {size_formatted}cm'
custom-model-data: 50004
group: ocean
group:
- ocean
- no_star
events:
success:
action_mending:
@@ -376,7 +380,9 @@ gold_fish:
bonus: 2.6
size: 2~3
custom-model-data: 50007
group: river
group:
- no_star
- river
events:
success:
action_mending:
@@ -442,7 +448,9 @@ perch_fish:
- <gray>foraging at dusk and early morning
- '<white>size: {size_formatted}cm'
custom-model-data: 50010
group: river
group:
- no_star
- river
events:
success:
action_mending:
@@ -510,7 +518,9 @@ mullet_fish:
- <gray>to treat spleen and stomach weakness
- '<white>size: {size_formatted}cm'
custom-model-data: 50013
group: river
group:
- no_star
- river
events:
success:
action_mending:
@@ -578,7 +588,9 @@ sardine_fish:
- <gray>Therefore, sardine are also called "smart food"
- '<white>size: {size_formatted}cm'
custom-model-data: 50016
group: ocean
group:
- ocean
- no_star
events:
success:
action_mending:
@@ -643,7 +655,9 @@ carp_fish:
- <gray>One of the most common edible fish
- '<white>size: {size_formatted}cm'
custom-model-data: 50019
group: river
group:
- no_star
- river
events:
success:
action_mending:
@@ -707,7 +721,9 @@ cat_fish:
- <gray>sharp jaw teeth, short intestine and stomach
- '<white>size: {size_formatted}cm'
custom-model-data: 50022
group: river
group:
- no_star
- river
events:
success:
action_mending:
@@ -773,7 +789,9 @@ octopus:
- <gray>People often use pots to catch octopus
- '<white>size: {size_formatted}cm'
custom-model-data: 50025
group: ocean
group:
- ocean
- no_star
events:
success:
action_mending:
@@ -838,7 +856,9 @@ sunfish:
- <gray>It only has one huge head
- '<white>size: {size_formatted}cm'
custom-model-data: 50028
group: ocean
group:
- ocean
- no_star
events:
success:
action_mending:
@@ -902,7 +922,9 @@ red_snapper_fish:
- <gray>with a male as the "head of the family"
- '<white>size: {size_formatted}cm'
custom-model-data: 50031
group: ocean
group:
- ocean
- no_star
events:
success:
action_mending:
@@ -971,7 +993,9 @@ salmon_void_fish:
- <gray>It's looking at you...
- '<white>size: {size_formatted}cm'
custom-model-data: 50034
group: lava
group:
- lava
- no_star
events:
success:
action_mending:
@@ -1037,7 +1061,9 @@ woodskip_fish:
- <gray>live in pools deep in the forest
- '<white>size: {size_formatted}cm'
custom-model-data: 50037
group: river
group:
- no_star
- swamp
events:
success:
action_mending:
@@ -1060,7 +1086,7 @@ woodskip_fish_silver_star:
custom-model-data: 50038
group:
- silver_star
- river
- swamp
events:
success:
action_mending:
@@ -1083,7 +1109,7 @@ woodskip_fish_golden_star:
custom-model-data: 50039
group:
- golden_star
- river
- swamp
events:
success:
action_mending:
@@ -1103,7 +1129,9 @@ sturgeon_fish:
- <gray>population. Females can live up to 150 years
- '<white>size: {size_formatted}cm'
custom-model-data: 50040
group: river
group:
- no_star
- cave
events:
success:
action_mending:
@@ -1126,7 +1154,7 @@ sturgeon_fish_silver_star:
custom-model-data: 50041
group:
- silver_star
- river
- cave
events:
success:
action_mending:
@@ -1149,7 +1177,7 @@ sturgeon_fish_golden_star:
custom-model-data: 50042
group:
- golden_star
- river
- cave
events:
success:
action_mending:
@@ -1168,7 +1196,9 @@ blue_jellyfish:
- <gray>Looks like a blue umbrella
- '<white>size: {size_formatted}cm'
custom-model-data: 50043
group: ocean
group:
- warm_ocean
- no_star
events:
success:
action_mending:
@@ -1190,7 +1220,7 @@ blue_jellyfish_silver_star:
custom-model-data: 50044
group:
- silver_star
- ocean
- warm_ocean
events:
success:
action_mending:
@@ -1212,7 +1242,7 @@ blue_jellyfish_golden_star:
custom-model-data: 50045
group:
- golden_star
- ocean
- warm_ocean
events:
success:
action_mending:
@@ -1231,7 +1261,9 @@ pink_jellyfish:
- <gray>Seems to be sweet
- '<white>size: {size_formatted}cm'
custom-model-data: 50046
group: ocean
group:
- warm_ocean
- no_star
events:
success:
action_mending:
@@ -1253,7 +1285,7 @@ pink_jellyfish_silver_star:
custom-model-data: 50047
group:
- silver_star
- ocean
- warm_ocean
events:
success:
action_mending:
@@ -1275,7 +1307,7 @@ pink_jellyfish_golden_star:
custom-model-data: 50048
group:
- golden_star
- ocean
- warm_ocean
events:
success:
action_mending:

View File

@@ -33,24 +33,9 @@ global-group:
- apple_crate:+2
- carrot_crate:+2
- kelp:+25
- tuna_fish:+15
- tuna_fish_silver_star:+3
- tuna_fish_golden_star:+1
- pike_fish:+15
- pike_fish_silver_star:+3
- pike_fish_golden_star:+1
- sardine_fish:+15
- sardine_fish_silver_star:+3
- sardine_fish_golden_star:+1
- octopus:+10
- octopus_silver_star:+2
- octopus_golden_star:+1
- sunfish:+15
- sunfish_silver_star:+3
- sunfish_golden_star:+1
- red_snapper_fish:+20
- red_snapper_fish_silver_star:+5
- red_snapper_fish_golden_star:+2
- group_for_each:ocean&no_star:+15
- group_for_each:ocean&silver_star:+3
- group_for_each:ocean&golden_star:+1
sub-groups:
warm_ocean_fish:
conditions:
@@ -59,12 +44,9 @@ global-group:
- minecraft:deep_lukewarm_ocean
- minecraft:warm_ocean
list:
- blue_jellyfish:+15
- blue_jellyfish_silver_star:+3
- blue_jellyfish_golden_star:+1
- pink_jellyfish:+15
- pink_jellyfish_silver_star:+3
- pink_jellyfish_golden_star:+1
- group_for_each:warm_ocean&no_star:+15
- group_for_each:warm_ocean&silver_star:+3
- group_for_each:warm_ocean&golden_star:+1
river_fish:
conditions:
'!biome':
@@ -80,27 +62,15 @@ global-group:
list:
- rainbow_fish:+5
- stick:+15
- gold_fish:+15
- gold_fish_silver_star:+3
- gold_fish_golden_star:+1
- perch_fish:+15
- perch_fish_silver_star:+3
- perch_fish_golden_star:+1
- mullet_fish:+15
- mullet_fish_silver_star:+3
- mullet_fish_golden_star:+1
- carp_fish:+25
- carp_fish_silver_star:+5
- carp_fish_golden_star:+2
- cat_fish:+15
- cat_fish_silver_star:+3
- cat_fish_golden_star:+1
- group_for_each:river&no_star:+15
- group_for_each:river&silver_star:+3
- group_for_each:river&golden_star:+1
sub-groups:
swamp_fish:
list:
- 'woodskip_fish:+30'
- 'woodskip_fish_silver_star:+5'
- 'woodskip_fish_golden_star:+2'
- group_for_each:swamp&no_star:+15
- group_for_each:swamp&silver_star:+3
- group_for_each:swamp&golden_star:+1
conditions:
biome:
- minecraft:swamp
@@ -110,9 +80,9 @@ global-group:
ypos:
- -64~0
list:
- 'sturgeon_fish:+9'
- 'sturgeon_fish_silver_star:+3'
- 'sturgeon_fish_golden_star:+1'
- group_for_each:cave&no_star:+9
- group_for_each:cave&silver_star:+3
- group_for_each:cave&golden_star:+1
# Skeletons might appear if player holds the bone_rod and fish at night
skeleton_group:
conditions:
@@ -148,9 +118,9 @@ global-group:
- 'gold_ingot:+3'
- 'gold_nugget:+8'
- 'magma_cube:+3'
- 'salmon_void_fish:+15'
- 'salmon_void_fish_silver_star:+3'
- 'salmon_void_fish_golden_star:+1'
- group_for_each:lava&no_star:+15
- group_for_each:lava&silver_star:+3
- group_for_each:lava&golden_star:+1
sub-groups:
skeleton_group:
conditions: