Skip to content
3 changes: 3 additions & 0 deletions .vscode/settings.json
Comment thread
AzureDoom marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "interactive"
}
5 changes: 5 additions & 0 deletions src/main/java/com/azuredoom/levelingcore/LevelingCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.azuredoom.levelingcore.level.mobs.mapping.MobBiomeMapping;
import com.azuredoom.levelingcore.level.mobs.mapping.MobEnvironmentMapping;
import com.azuredoom.levelingcore.level.mobs.mapping.MobInstanceMapping;
import com.azuredoom.levelingcore.level.mobs.mapping.MobOverrideMapping;
import com.azuredoom.levelingcore.level.mobs.mapping.MobZoneMapping;
import com.azuredoom.levelingcore.level.rewards.LevelRewards;
import com.azuredoom.levelingcore.level.rewards.RewardEntry;
Expand Down Expand Up @@ -99,6 +100,10 @@ public class LevelingCore extends JavaPlugin {
LevelingCore.configPath
);

public static final Map<String, Integer> mobOverrideMapping = MobOverrideMapping.loadOrCreate(
LevelingCore.configPath
);

public static final MobLevelRegistry mobLevelRegistry = new MobLevelRegistry();

public static final MobLevelPersistence mobLevelPersistence = new MobLevelPersistence();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.azuredoom.levelingcore.level.mobs.mapping;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;

import com.azuredoom.levelingcore.LevelingCore;
import com.azuredoom.levelingcore.config.internal.ConfigManager;
import com.azuredoom.levelingcore.exceptions.LevelingCoreException;

public class MobOverrideMapping {

public static final String FILE_NAME = "moboverridemapping.csv";

public static final String RESOURCE_DEFAULT = "/defaultmoboverridemapping.csv";

private MobOverrideMapping() {}

public static Map<String, Integer> loadOrCreate(Path dataDir) {
try {
Files.createDirectories(dataDir);
var configPath = dataDir.resolve(FILE_NAME);

if (Files.notExists(configPath)) {
try (InputStream in = ConfigManager.class.getResourceAsStream(RESOURCE_DEFAULT)) {
if (in == null) {
throw new LevelingCoreException(
"defaultmoboverridemapping.csv not found in resources (expected at " + RESOURCE_DEFAULT
+ ")"
);
}
LevelingCore.LOGGER.at(Level.INFO)
.log("Creating default Mob Override Levels Mapping config at " + configPath);
Files.copy(in, configPath, StandardCopyOption.REPLACE_EXISTING);
}
}

var mapping = readXpCsv(configPath);

LevelingCore.LOGGER.at(Level.INFO)
.log(
"Loaded Mob Override Levels Mapping mapping from " + configPath + " " + mapping.size() + " entries)"
);
return mapping;

} catch (Exception e) {
throw new LevelingCoreException("Failed to load Mob Override Levels Mapping config", e);
}
}

private static Map<String, Integer> readXpCsv(Path csvPath) throws Exception {
Map<String, Integer> out = new LinkedHashMap<>();

try (var reader = Files.newBufferedReader(csvPath, StandardCharsets.UTF_8)) {
String line;
var firstNonEmptyLine = true;

while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.isEmpty())
continue;
if (line.startsWith("#"))
continue;

if (firstNonEmptyLine) {
firstNonEmptyLine = false;
if (line.equalsIgnoreCase("npc_id,lvl")) {
continue;
}
}

var parts = line.split(",", 2);
if (parts.length != 2) {
LevelingCore.LOGGER.at(Level.WARNING).log("Skipping invalid CSV line: " + line);
continue;
}

var npcId = parts[0].trim();
var lvlStr = parts[1].trim();

if (npcId.isEmpty()) {
LevelingCore.LOGGER.at(Level.WARNING).log("Skipping CSV line with empty NPC ID: " + line);
continue;
}

int lvl;
try {
lvl = Integer.parseInt(lvlStr);
} catch (NumberFormatException nfe) {
LevelingCore.LOGGER.at(Level.WARNING)
.log(
"Invalid NPC ID value for " + npcId + ": " + lvlStr + " (line: " + line + ")"
);
continue;
}

out.put(npcId, lvl);
}
}

return out;
}
}
38 changes: 24 additions & 14 deletions src/main/java/com/azuredoom/levelingcore/utils/MobLevelingUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,24 @@ public static int computeDynamicLevel(
Store<EntityStore> store
) {
var modeStr = config.get().getLevelMode();
var overrideLevel = computeNPCOverrideLevel(npc);

if (overrideLevel != 0) {
return overrideLevel;
}

if (modeStr == null) {
return computeNearbyPlayersMeanLevel(transform, store);
}

return CoreLevelMode.fromString(modeStr)
.map(mode -> switch (mode) {
case SPAWN_ONLY ->
computeSpawnLevel(npc);
case NEARBY_PLAYERS_MEAN ->
computeNearbyPlayersMeanLevel(transform, store);
case BIOME ->
computeBiomeLevel(store);
case ZONE ->
computeZoneLevel(store);
case ENVIRONMENT ->
computeEnvironmentLevel(transform, store);
case INSTANCE ->
computeInstanceLevel(store);
case SPAWN_ONLY -> computeSpawnLevel(npc);
case NEARBY_PLAYERS_MEAN -> computeNearbyPlayersMeanLevel(transform, store);
case BIOME -> computeBiomeLevel(store);
case ZONE -> computeZoneLevel(store);
case ENVIRONMENT -> computeEnvironmentLevel(transform, store, npc);
case INSTANCE -> computeInstanceLevel(store);
})
.orElseGet(() -> {
LevelingCore.LOGGER.at(Level.INFO)
Expand Down Expand Up @@ -134,7 +133,7 @@ public static int computeBiomeLevel(Store<EntityStore> store) {
return biomeMapping.getOrDefault(currentBiome.toLowerCase(), 1);
}

public static int computeEnvironmentLevel(TransformComponent transform, Store<EntityStore> store) {
public static int computeEnvironmentLevel(TransformComponent transform, Store<EntityStore> store, NPCEntity npc) {
var world = store.getExternalData().getWorld();
var mobPos = transform.getPosition();
var chunk = world.getChunk(ChunkUtil.indexChunkFromBlock((int) mobPos.x, (int) mobPos.z));
Expand All @@ -160,8 +159,12 @@ public static int computeEnvironmentLevel(TransformComponent transform, Store<En
}

var environmentMapping = LevelingCore.mobEnvironmentMapping;
var seed = npc.getUuid().getMostSignificantBits() ^ npc.getUuid().getLeastSignificantBits();
var rng = new Random(seed);
final var baseLevel = environmentMapping.getOrDefault(envName.toLowerCase(), 1);
final var range = baseLevel + 10;

return environmentMapping.getOrDefault(envName.toLowerCase(), 1);
return baseLevel + rng.nextInt((range - baseLevel) + 1);
}

public static int computeNearbyPlayersMeanLevel(TransformComponent transform, Store<EntityStore> store) {
Expand Down Expand Up @@ -193,4 +196,11 @@ public static int computeNearbyPlayersMeanLevel(TransformComponent transform, St
var mean = (double) sum / (double) count;
return (int) Math.round(mean);
}

public static int computeNPCOverrideLevel(NPCEntity npc) {
var npcTypeID = npc.getNPCTypeId();
var overrideMapping = LevelingCore.mobOverrideMapping;

return overrideMapping.getOrDefault(npcTypeID.toLowerCase(), 0);
}
}
2 changes: 2 additions & 0 deletions src/main/resources/defaultmoboverridemapping.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
npc_id,lvl
bunny,5
Loading