Skip to content

Commit

Permalink
feat: Add configuration options and /check_config command
Browse files Browse the repository at this point in the history
- Implemented customizable healing values based on difficulty (Easy, Normal, Hard).
- Added configurable bonus healing multiplier based on damage dealt.
- Added an option to enable/disable particle effects when healing.
- Configurable cooldown for lifesteal activation.
- Configuration values saved in `lifedrain.json` and automatically updated for missing fields.
- Introduced `/check_config` command to automatically check and update the config file.
- Improved mod customization through external configuration.

Configurable lifesteal mechanics now give players more control over mod behavior.
  • Loading branch information
vorlie committed Jan 12, 2025
1 parent 04a6b04 commit 63c4998
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 37 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,22 @@
- Bonus healing based on the damage dealt: 20% of the damage dealt.
- Applies only to hostile mobs, ensuring lifesteal is balanced and works as intended in combat.

⚙️ **Configuration:**
- Customize healing values for different difficulty levels:
- Base healing for Easy, Normal, and Hard modes.
- Bonus healing multiplier based on the damage dealt.
- Enable or disable particle effects when healing is triggered.
- Adjust the cooldown for lifesteal activation (time between consecutive lifesteal uses).
- Configuration is saved to `PATH-TO-MINECRAFT-INSTANCE/config/lifedrain.json` and can be modified directly.
- The config file is automatically updated to add missing values if they are not found.
- Use the `/check_config` command to automatically check and update the config file if necessary.

🔧 **Technical Details:**
- Fully implemented lifesteal logic triggered upon attacking hostile mobs.
- Debugging logs added to track healing values and lifesteal performance.
- Added config file support to customize mod behavior through the configuration file.
- Config can be updated to add missing fields if necessary.
- Added a command to check and update the config file (`/check_config`), ensuring it always has the correct values.

This mod is lightweight and perfect for anyone who enjoys combat-focused gameplay or just wants a little extra survivability.

Expand Down
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ dependencies {

// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

}

processResources {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ yarn_mappings=1.21+build.9
loader_version=0.16.10

# Mod Properties
mod_version=1.0.1
mod_version=1.0.2
maven_group=vorlie.lifedrain
archives_base_name=lifedrain

Expand Down
94 changes: 61 additions & 33 deletions src/main/java/vorlie/lifedrain/LifeDrain.java
Original file line number Diff line number Diff line change
@@ -1,71 +1,99 @@
package vorlie.lifedrain;

import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;

import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.random.Random;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import vorlie.lifedrain.commands.ConfigCheckCommand;
import vorlie.lifedrain.config.ConfigManager;

public class LifeDrain implements ModInitializer {
public static final String MOD_ID = "lifedrain";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);

public static Identifier id(String path) {
return Identifier.of(MOD_ID, path);
}
private long lastLifestealTime = 0; // Time of the last lifesteal
private long COOLDOWN_TIME; // Cooldown time in milliseconds (1 second)

@Override
public void onInitialize() {
LOGGER.info("LifeDrain mod initialized!");
ConfigManager.load(); // Load configuration

COOLDOWN_TIME = ConfigManager.CONFIG.lifestealCooldown;

// Register the attack event
AttackEntityCallback.EVENT.register((
player,
world,
hand,
entity,
hitResult) -> {
// Ensure the code runs on the server and the target is a valid hostile mob
AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
if (!world.isClient && entity instanceof HostileEntity) {
handleLifesteal(player, (HostileEntity) entity);
if (canActivateLifesteal()) {
handleLifesteal(player, (HostileEntity) entity);
}
}
return ActionResult.PASS;
});
}

// Register the config reload event
ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, resourceManager, success) -> {
ConfigManager.load();
LOGGER.info("LifeDrain config reloaded.");
COOLDOWN_TIME = ConfigManager.CONFIG.lifestealCooldown;
});

CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
ConfigCheckCommand.register(dispatcher); // Only two parameters are expected here
});
}
private void handleLifesteal(PlayerEntity player, HostileEntity mob) {
if (player != null && mob != null && mob.isAlive()) {
// Base healing amount based on difficulty
// Base healing amount from configuration
float baseHeal = switch (mob.getWorld().getDifficulty()) {
case EASY -> 2.0F;
case NORMAL -> 1.0F;
case HARD -> 0.5F;
case EASY -> ConfigManager.CONFIG.baseHealEasy;
case NORMAL -> ConfigManager.CONFIG.baseHealNormal;
case HARD -> ConfigManager.CONFIG.baseHealHard;
default -> 0.0F;
};

// Bonus healing based on damage dealt
float damageDealt = mob.getMaxHealth() - mob.getHealth(); // Approximation of damage dealt
float bonusHeal = damageDealt * 0.2F; // 20% of the damage dealt
float damageDealt = mob.getMaxHealth() - mob.getHealth();
float bonusHeal = damageDealt * ConfigManager.CONFIG.bonusHealMultiplier;

// Total healing (base + bonus)
float totalHeal = baseHeal + bonusHeal;

// Heal the player
player.heal(totalHeal);

// Log for debugging
LOGGER.info("Lifesteal activated: {} healed {} health (Base: {}, Bonus: {}, Mob Health: {} -> {})!",
player.getName().getString(),
totalHeal,
baseHeal,
bonusHeal,
mob.getMaxHealth(),
mob.getHealth());
// Show particles if enabled
if (ConfigManager.CONFIG.enableParticles && player.getWorld() instanceof ServerWorld serverWorld) {
Random random = player.getRandom();
for (int i = 0; i < 10; i++) {
serverWorld.spawnParticles(
ParticleTypes.HEART,
player.getX() + random.nextDouble() - 0.5,
player.getY() + random.nextDouble(),
player.getZ() + random.nextDouble() - 0.5,
1, 0, 0, 0, 0);
}
}

LOGGER.info("Lifesteal: {} healed {} (Base: {}, Bonus: {}).",
player.getName().getString(), totalHeal, baseHeal, bonusHeal);

// Update last lifesteal activation time
lastLifestealTime = System.currentTimeMillis();
}
}

// Check if enough time has passed since the last lifesteal to activate it again
private boolean canActivateLifesteal() {
long currentTime = System.currentTimeMillis();
return (currentTime - lastLifestealTime) >= COOLDOWN_TIME;
}
}
43 changes: 43 additions & 0 deletions src/main/java/vorlie/lifedrain/commands/ConfigCheckCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package vorlie.lifedrain.commands;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import vorlie.lifedrain.config.ConfigManager;
import static vorlie.lifedrain.LifeDrain.MOD_ID;

public class ConfigCheckCommand {
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);

// Command registration
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
dispatcher.register(
CommandManager.literal("check_config")
.requires(source -> source.hasPermissionLevel(1)) // Permission level check
.executes(ConfigCheckCommand::executeCheckConfigCommand) // Execute the method directly
);
}

// Execute the command
public static int executeCheckConfigCommand(CommandContext<ServerCommandSource> context) {
LOGGER.info("Checking config for missing values...");

// Check and add missing values in the config
ConfigManager.load(); // Reload the config file
ConfigManager.addMissingFields(ConfigManager.getConfigJson()); // Add missing fields

// Send feedback to the player (command source)
context.getSource().sendFeedback(() -> Text.literal(
"LifeDrain: Config file has been checked and updated. " + "To apply changes, run /reload."
), false);

// Save the updated config back to the file
ConfigManager.save();

return 1; // Return value to indicate command execution success
}
}
96 changes: 96 additions & 0 deletions src/main/java/vorlie/lifedrain/config/ConfigManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package vorlie.lifedrain.config;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;

import net.fabricmc.loader.api.FabricLoader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import static vorlie.lifedrain.LifeDrain.MOD_ID;

public class ConfigManager {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final File CONFIG_FILE = new File(FabricLoader.getInstance().getConfigDir().toFile(), "lifedrain.json");
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static LifeDrainConfig CONFIG = new LifeDrainConfig();

public static void load() {
if (CONFIG_FILE.exists()) {
try (FileReader reader = new FileReader(CONFIG_FILE)) {
JsonObject configJson = GSON.fromJson(reader, JsonObject.class);

// Check for missing fields and add default values
addMissingFields(configJson);

// Now convert the JsonObject to our config class
CONFIG = GSON.fromJson(configJson, LifeDrainConfig.class);
} catch (IOException e) {
LOGGER.error("Error reading config: ", e);
}
} else {
save(); // Save defaults if the config file doesn't exist
}
}

public static JsonObject getConfigJson() {
JsonObject configJson = new JsonObject();
// Add current configuration values to a JsonObject
configJson.addProperty("lifestealCooldown", CONFIG.lifestealCooldown);
configJson.addProperty("enableParticles", CONFIG.enableParticles);
configJson.addProperty("baseHealEasy", CONFIG.baseHealEasy);
configJson.addProperty("baseHealNormal", CONFIG.baseHealNormal);
configJson.addProperty("baseHealHard", CONFIG.baseHealHard);
configJson.addProperty("bonusHealMultiplier", CONFIG.bonusHealMultiplier);

return configJson;
}

public static void addMissingFields(JsonObject configJson) {
// Add new fields if they don't exist
if (!configJson.has("lifestealCooldown")) {
LOGGER.info("Missing 'lifestealCooldown', adding default value.");
configJson.addProperty("lifestealCooldown", LifeDrainConfig.DEFAULT_LIFESTEAL_COOLDOWN);
}

if (!configJson.has("enableParticles")) {
LOGGER.info("Missing 'enableParticles', adding default value.");
configJson.addProperty("enableParticles", LifeDrainConfig.DEFAULT_ENABLE_PARTICLES);
}

if (!configJson.has("baseHealEasy")) {
LOGGER.info("Missing 'baseHealEasy', adding default value.");
configJson.addProperty("baseHealEasy", LifeDrainConfig.DEFAULT_BASE_HEAL_EASY);
}

if (!configJson.has("baseHealNormal")) {
LOGGER.info("Missing 'baseHealNormal', adding default value.");
configJson.addProperty("baseHealNormal", LifeDrainConfig.DEFAULT_BASE_HEAL_NORMAL);
}

if (!configJson.has("baseHealHard")) {
LOGGER.info("Missing 'baseHealHard', adding default value.");
configJson.addProperty("baseHealHard", LifeDrainConfig.DEFAULT_BASE_HEAL_HARD);
}

if (!configJson.has("bonusHealMultiplier")) {
LOGGER.info("Missing 'bonusHealMultiplier', adding default value.");
configJson.addProperty("bonusHealMultiplier", LifeDrainConfig.DEFAULT_BONUS_HEAL_MULTIPLIER);
}
}

public static void save() {
try (FileWriter writer = new FileWriter(CONFIG_FILE)) {
GSON.toJson(CONFIG, writer);
} catch (IOException e) {
LOGGER.error("Error saving config: ", e);
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/vorlie/lifedrain/config/LifeDrainConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package vorlie.lifedrain.config;

public class LifeDrainConfig {
public static final int DEFAULT_LIFESTEAL_COOLDOWN = 1000; // Default cooldown time in milliseconds
public static final boolean DEFAULT_ENABLE_PARTICLES = true; // Default for enabling particles
public static final float DEFAULT_BASE_HEAL_EASY = 2.0F;
public static final float DEFAULT_BASE_HEAL_NORMAL = 1.0F;
public static final float DEFAULT_BASE_HEAL_HARD = 0.5F;
public static final float DEFAULT_BONUS_HEAL_MULTIPLIER = 0.2F;

public boolean enableParticles = DEFAULT_ENABLE_PARTICLES;
public float baseHealEasy = DEFAULT_BASE_HEAL_EASY;
public float baseHealNormal = DEFAULT_BASE_HEAL_NORMAL;
public float baseHealHard = DEFAULT_BASE_HEAL_HARD;
public float bonusHealMultiplier = DEFAULT_BONUS_HEAL_MULTIPLIER;
public int lifestealCooldown = DEFAULT_LIFESTEAL_COOLDOWN;
}
3 changes: 1 addition & 2 deletions src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@
"fabricloader": ">=0.16.10",
"minecraft": "~1.21",
"java": ">=21",
"fabric-api": "*"
"fabric-api": ">=0.102.0+1.21"
},
"suggests": {
"another-mod": "*"
}
}

0 comments on commit 63c4998

Please sign in to comment.