Skip to content

Commit

Permalink
Modified the way loot is generated
Browse files Browse the repository at this point in the history
  • Loading branch information
Levoment committed Apr 30, 2022
1 parent d949a04 commit 6d8f283
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 147 deletions.
63 changes: 39 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@

Minecraft mod for modifying the items that appear in chests in Minecraft

In this mod, the game needs to be restarted for the changes in the configuration to
take effect and the changes will apply only to chests that have not been generated yet
in the world.
Everytime a chest is generated in the world, it is not until the chest is opened for the
first time that loot for it is generated. Therefore, this mod does not require the game to be
restarted for the changes in the config file to take effect. However, once a chest
is opened for the first time, the loot generated for said chest will not change unless there is a mod
that regenerates the loot for the chest.

## Configuration

Expand All @@ -21,14 +23,26 @@ A sample file looks like this:

```json
{

"Names": [
"Common",
"Uncommon",
"Rare",
"SuperRare"
],


"Names": {
"Common": {
"MinRolls": 1,
"MaxRolls": 2
},
"Uncommon": {
"MinRolls": 1,
"MaxRolls": 2
},
"Rare": {
"MinRolls": 1,
"MaxRolls": 3
},
"SuperRare": {
"MinRolls": 2,
"MaxRolls": 4
}
},

"ChestDefinitions": {
"Common": ["minecraft:chests/spawn_bonus_chest", "minecraft:chests/village/village_mason", "minecraft:chests/simple_dungeon"],
"Uncommon": ["minecraft:chests/desert_pyramid", "minecraft:chests/pillager_outpost", "minecraft:chests/ruined_portal"],
Expand All @@ -37,17 +51,20 @@ A sample file looks like this:
},

"LootDefinitions": {
"Common": ["minecraft:golden_sword(100)(3)", "minecraft:golden_pickaxe(100)(12)", "minecraft:coal_ore(100)(16)"],
"Uncommon": ["minecraft:diamond(70)(4)", "minecraft:diamond(90)(2)", "minecraft:diamond_sword(85)(1)"],
"Rare": ["minecraft:netherite_axe(80)(1)", "minecraft:netherite_ingot(95)(8)"],
"SuperRare": ["minecraft:elytra(99)(1)", "minecraft:shulker_box(90)(2)"]
"Common": ["minecraft:golden_sword(1)(1)", "minecraft:golden_pickaxe(1)(1)", "minecraft:diamond(16)(3)"],
"Uncommon": ["minecraft:diamond(8)(4)", "minecraft:diamond_sword(1)(1)"],
"Rare": ["minecraft:netherite_axe(1)(3)", "minecraft:netherite_ingot(8)(1)"],
"SuperRare": ["minecraft:elytra(1)(2)", "minecraft:shulker_box(2)(5)"]
}

}
```

`Names` are the names used to identify the kind of loot. This can be anything, but whatever
they are, they must be the same on `ChestDefinitions` and `LootDefinitions`.
- `MinRolls` and `MaxRolls` are the number of rolls to do for that rarity pool. Choosing
a minimum of `0` will give it a chance that none of the items in that pool will appear.
These numbers are integers. Because it is a range between min and max, the rolls for the
pool will be a random number between the range.

`ChestDefinitions` these are the chests that will apply to a loot. For instance, which
chests will get Common, Uncommon, Rare, and SuperRare loot. These must be the fully
Expand All @@ -57,14 +74,12 @@ qualified Minecraft Identifiers for the chests.
which items will apply to Common loot, Uncommon loot, etc.

- **The number in the first
parenthesis** is the number that will indicate the percentage chance of the item appearing
in the chest. To make it always appear, set it to 100. Note that if the game fills the
chest with other items, even a 100 number could make the item not appear in the chest.

- **The number in the second parenthesis** is the number range of items that the game should
put on the chest. The game will not always put the specified number. It will put anything
within that range. The game could put 0 of the item, the number specified, or a number in
between.
parenthesis** is the number that will indicate the number of items to appear if the item
is selected to appear on the chest that the rarity applies to.

- **The number in the second parenthesis** is the weight for the item. The higher the
weight, the higher the chances that such item will appear should the pool be selected for
the chest.

## Issues
The mod has a lot of logic to print to the console when something fails. If the game crashes
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ org.gradle.jvmargs=-Xmx1G
loader_version=0.13.3

# Mod Properties
mod_version = 1.0.1
mod_version = 1.0.2
maven_group = com.github.levoment.chestlootmodifier
archives_base_name = [1.18.2] Chest Loot Modifier

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,123 +5,32 @@
import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback;
import net.minecraft.item.Item;
import net.minecraft.loot.LootPool;
import net.minecraft.loot.condition.RandomChanceLootCondition;
import net.minecraft.loot.entry.ItemEntry;
import net.minecraft.loot.function.SetCountLootFunction;
import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
import net.minecraft.loot.provider.number.UniformLootNumberProvider;
import net.minecraft.util.Identifier;
import net.minecraft.util.InvalidIdentifierException;
import net.minecraft.util.registry.Registry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ChestLootModifierMod implements ModInitializer {
// This logger is used to write text to the console and the log file.
// It is considered best practice to use your mod id as the logger's name.
// That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger("chestlootmodifier");

@Override
public void onInitialize() {
// Create the config file if it doesn't exist
ConfigManager.createConfigFile();
// Read the config file if it exists
ConfigManager.readConfigFile();
// Regex to match everything before a parenthesis
Pattern beforeParenthesisPattern = Pattern.compile("^.*?(?=\\()", Pattern.CASE_INSENSITIVE);
Pattern betweenParenthesisPattern = Pattern.compile("(?<=\\().*?(?=\\))", Pattern.CASE_INSENSITIVE);

LootTableLoadingCallback.EVENT.register((resourceManager, manager, id, supplier, setter) -> {
// If the configuration was loaded successfully
if (ConfigManager.SUCCESSFULLY_LOADED_CONFIG) {
String idString = id.toString();
ConfigManager.CURRENT_CONFIG.ChestDefinitions.forEach((key, chestIDs) -> {
// If the chest ID matches the current id being registered
if (chestIDs.contains(idString)) {
// Check if there is a loot definition for the chest
if (ConfigManager.CURRENT_CONFIG.LootDefinitions.containsKey(key)) {
List<String> lootList = ConfigManager.CURRENT_CONFIG.LootDefinitions.get(key);
// Check if the list is not empty
if (lootList.size() > 0) {
// Go through all the loot definitions for this chest in the config file
for(String itemID : lootList) {
// Try to match the item part
Matcher matcher = beforeParenthesisPattern.matcher(itemID);
if (matcher.find()) {
String wholeMatch = matcher.group();
// Try to get the item from the identifier
try {
// Try to get the given item
Item currentItem = Registry.ITEM.get(new Identifier(matcher.group()));
// Get the part of the percentage and amount conditions for the item
Matcher numberMatcher = betweenParenthesisPattern.matcher(itemID);
if (numberMatcher.find()) {
String firstMatch = numberMatcher.group(0);
String secondMatch = null;
if (numberMatcher.find()) {
secondMatch = numberMatcher.group(0);
}

if (firstMatch == null || firstMatch.isBlank() || secondMatch == null || secondMatch.isBlank()) {
ChestLootModifierMod.LOGGER.error("[Chest Loot Modifier Mod] Item: '" + itemID + "' doesn't have a properly formatted number " +
"for the percentage loot chance and or amount for the item between parenthesis. " +
"Both, the percentage and number, must be integers. An example would be: minecraft:stone_sword(45)(1). Where 45 " +
"would be 45% chance of appearing among other loot in the chest and 1 is the amount of stone swords to put on the chest " +
"should a stone word appear on it.");
} else {
// Try to get the percentage
try {
int percentageInt = Integer.parseInt(firstMatch);
if (percentageInt > 0 && percentageInt < 101) {
double percentageDouble = (double) percentageInt / 100.0;
float percentage = (float) (Math.round(percentageDouble * Math.pow(10, 1)) / Math.pow(10, 1));
// Try to get the number of items to put on the chest
try {
int itemNumberInt = Integer.parseInt(secondMatch);

// Create the pool builder
LootPool poolBuilder = FabricLootPoolBuilder.builder().
withEntry(ItemEntry.builder(currentItem).build())
.withCondition(RandomChanceLootCondition.builder(percentage).build())
.withFunction(SetCountLootFunction.builder(ConstantLootNumberProvider.create(itemNumberInt)).build())
.build();
// Add the item to the pool of items for the current chest
supplier.withPool(poolBuilder);

} catch (NumberFormatException numberFormatException) {
ChestLootModifierMod.LOGGER.error("[Chest Loot Modifier Mod] The provided number: '" + secondMatch + "' in " + itemID + " could not be converted to an integer.");
}
} else {
ChestLootModifierMod.LOGGER.error("[Chest Loot Modifier Mod] The provided percentage: '" + firstMatch + "' in " + itemID + "needs to be more than 0 and less than 101");
}
} catch (NumberFormatException numberFormatException) {
ChestLootModifierMod.LOGGER.error("[Chest Loot Modifier Mod] The provided number: '" + firstMatch + "' in " + itemID + " could not be converted to an integer.");
}
}
} else {
ChestLootModifierMod.LOGGER.error("[Chest Loot Modifier Mod] Item: '" + wholeMatch + "' is missing a percentage loot chance and or amount for the item between parenthesis.");
}
} catch (InvalidIdentifierException invalidIdentifierException) {
ChestLootModifierMod.LOGGER.error("[Chest Loot Modifier Mod] The game could not find the item with identifier: '" + wholeMatch + "'");
}
}
}
} else {
ChestLootModifierMod.LOGGER.error("[Chest Loot Modifier Mod] There is no loot defined for " + key + " in the config file LootDefinition object");
}
} else {
ChestLootModifierMod.LOGGER.error("[Chest Loot Modifier Mod] There is no loot defined for " + key + " in the config file LootDefinition object");
}


}
});
}
});

}
// This logger is used to write text to the console and the log file.
// It is considered best practice to use your mod id as the logger's name.
// That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger("chestlootmodifier");

@Override
public void onInitialize() {
// Create the config file if it doesn't exist
ConfigManager.createConfigFile();
// Read the config file if it exists
ConfigManager.readConfigFile();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,39 @@ public static void createConfigFile() {
try {
// Config file string
String configFileText = "{\n" +
"\n" +
" \"Names\": {\n" +
" \"Common\": {\n" +
" \"MinRolls\": 1,\n" +
" \"MaxRolls\": 2\n" +
" },\n" +
" \"Uncommon\": {\n" +
" \"MinRolls\": 1,\n" +
" \"MaxRolls\": 2\n" +
" },\n" +
" \"Rare\": {\n" +
" \"MinRolls\": 1,\n" +
" \"MaxRolls\": 3\n" +
" },\n" +
" \"SuperRare\": {\n" +
" \"MinRolls\": 2,\n" +
" \"MaxRolls\": 4\n" +
" }\n" +
" },\n" +
" \n" +
" \"Names\": [\n" +
" \"Common\",\n" +
" \"Uncommon\",\n" +
" \"Rare\",\n" +
" \"SuperRare\"\n" +
" ],\n" +
" \n" +
" \"Chest Definitions\": {\n" +
" \"ChestDefinitions\": {\n" +
" \"Common\": [],\n" +
" \"Uncommon\": [],\n" +
" \"Rare\": [],\n" +
" \"SuperRare\": []\n" +
" },\n" +
"\n" +
" \"Loot Definitions\": {\n" +
" \"LootDefinitions\": {\n" +
" \"Common\": [],\n" +
" \"Uncommon\": [],\n" +
" \"Rare\": [],\n" +
" \"SuperRare\": []\n" +
" }\n" +
" \n" +
"}";
// Create the file writer to write the file
FileWriter configFileWritter = new FileWriter(configFile);
Expand Down Expand Up @@ -95,6 +106,5 @@ public static void readConfigFile() {
System.exit(-1);
}
} else ChestLootModifierMod.LOGGER.error("No configuration file found for Chest Loot Modifier Mod in: " + configFile.getPath());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@

public class ConfigurationObject {

public List<String> Names;
public Map<String, RarityObject> Names;
public Map<String, List<String>> ChestDefinitions;
public Map<String, List<String>> LootDefinitions;

public ConfigurationObject(List<String> names, Map<String, List<String>> chestDefinitions, Map<String, List<String>> lootDefinitions) {
public ConfigurationObject(Map<String, RarityObject> names, Map<String, List<String>> chestDefinitions, Map<String, List<String>> lootDefinitions) {
this.Names = names;
this.ChestDefinitions = chestDefinitions;
this.LootDefinitions = lootDefinitions;
}

public List<String> getNames() {
public Map<String, RarityObject> getNames() {
return Names;
}

public void setNames(List<String> names) {
Names = names;
public void setNames(Map<String, RarityObject> names) {
this.Names = names;
}

public Map<String, List<String>> getChestDefinitions() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.github.levoment.chestlootmodifier;

public class RarityObject {
private int MinRolls;
private int MaxRolls;

public RarityObject(int minRolls, int maxRolls) {
MinRolls = minRolls;
MaxRolls = maxRolls;
}

public int getMinRolls() {
return MinRolls;
}

public void setMinRolls(int minRolls) {
MinRolls = minRolls;
}

public int getMaxRolls() {
return MaxRolls;
}

public void setMaxRolls(int maxRolls) {
MaxRolls = maxRolls;
}
}
Loading

0 comments on commit 6d8f283

Please sign in to comment.