Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>local</groupId>
<artifactId>gson</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class Cache {
public static double alloyForgeBaseSuccess = 2.0;
public static double alloyForgeBonusPerSqrtValue = 4.0;
public static double alloyForgeMaxSuccess = 85.0;
public static double gemstoneStatBaseChance = 55.0;
public static double gemstoneStatBonusPerValue = 5.0;
/** Flat success% added when the ingredient (lowercase id) is the alloy base. Catalysts never get it. */
public static Map<String, Double> alloyForgeBaseBonus = new HashMap<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public void load(File configFile) {
Cache.alloyForgeBaseSuccess = clampPercent(config.getDouble("alloy-forge.base-success-percent", 2.0));
Cache.alloyForgeBonusPerSqrtValue = Math.max(0.0, config.getDouble("alloy-forge.success-bonus-per-sqrt-value", 4.0));
Cache.alloyForgeMaxSuccess = clampPercent(config.getDouble("alloy-forge.max-success-percent", 85.0));
Cache.gemstoneStatBaseChance = clampPercent(config.getDouble("alloy-forge.gem-stat-base-percent", 55.0));
Cache.gemstoneStatBonusPerValue = Math.max(0.0, config.getDouble("alloy-forge.gem-stat-bonus-per-value", 5.0));
if (Cache.alloyForgeMaxSuccess < Cache.alloyForgeBaseSuccess) {
Cache.alloyForgeMaxSuccess = Cache.alloyForgeBaseSuccess;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import net.tfminecraft.advancedcrafting.lifecycle.CraftLifecycle;

public class AlloyForger {
private static final String GEMSTONE_PATH_PREFIX = "m.gemstones.";
private int value;
private AlloyStation station;
private StatData stats;
Expand Down Expand Up @@ -181,11 +182,23 @@ public void mergeStats() {
}
List<StatModifier> merge = new ArrayList<>();
for(Ingredient i : station.getCatalysts()) {
// Gemstones span metal and crystal ingredient types; their item paths identify them consistently.
boolean gemstone = isGemstonePath(i.getPath());
for(StatModifier m : i.getIngredientData().getStatData().getModifiers()) {
if(baseItem.getIngredientData().statIsProtected(m)) continue;
if(base.containsKey(m.getType())) {
StatModifier n = new StatModifier(m.getType(), m.getAmount()-base.get(m.getType()).getAmount());
merge.add(n);
StatModifier baseModifier = base.get(m.getType());
if(gemstone) {
if(Math.random()*100 >= gemstoneInheritanceChance(i.getIngredientData().getValue())) continue;
merge.add(modifierForMerge(m, baseModifier, true));
if(baseModifier == null) {
StatModifier limit = m.copy();
limit.setAmount(limit.getAmount()*Cache.maxFactor);
// A later, weaker gem with the same stat must not lower an earlier gem's cap.
max.merge(m.getType(), limit, (current, candidate) ->
current.getAmount() >= candidate.getAmount() ? current : candidate);
}
} else if(baseModifier != null) {
merge.add(modifierForMerge(m, baseModifier, false));
} else {
if(Math.random()*100 < (20+i.getIngredientData().getValue()*3)) {
merge.add(m.copy());
Expand Down Expand Up @@ -213,6 +226,20 @@ public void mergeStats() {
}
}
}

static boolean isGemstonePath(String path) {
return path != null && path.regionMatches(true, 0, GEMSTONE_PATH_PREFIX, 0, GEMSTONE_PATH_PREFIX.length());
}

static double gemstoneInheritanceChance(int ingredientValue) {
return Math.max(0.0, Math.min(100.0,
Cache.gemstoneStatBaseChance + Cache.gemstoneStatBonusPerValue * ingredientValue));
}

static StatModifier modifierForMerge(StatModifier catalyst, StatModifier base, boolean gemstone) {
if(gemstone || base == null) return catalyst.copy();
return new StatModifier(catalyst.getType(), catalyst.getAmount() - base.getAmount());
}

private void merge(HashMap<String, StatModifier> base, HashMap<String, StatModifier> max, List<StatModifier> list) {
for(StatModifier m : list) {
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ alloy-forge:
base-success-percent: 2.0
success-bonus-per-sqrt-value: 4.0
max-success-percent: 85.0
# Gemstone catalyst stat roll, before the total-value merge roll. Defaults also apply to existing configs without these keys.
gem-stat-base-percent: 55.0
gem-stat-bonus-per-value: 5.0
# Flat success% added when this ingredient is the base. Catalysts never get it; unlisted ingredients get 0.
base-bonus-percent:
steel_ingot: 8.0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package net.tfminecraft.advancedcrafting.objects.alloys;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import org.junit.Test;

import net.tfminecraft.advancedcrafting.objects.stats.StatModifier;

public class AlloyForgerTest {
@Test
public void everyConfiguredGemstoneUsesTheGemstoneRule() throws IOException {
long gemstoneCount = 0;
for(String line : Files.readAllLines(Path.of("src/main/resources/ingredients.yml"))) {
String trimmed = line.trim();
if(!trimmed.startsWith("path:")) continue;
String path = trimmed.substring("path:".length()).trim();
if(path.startsWith("m.gemstones.")) {
assertTrue(AlloyForger.isGemstonePath(path));
gemstoneCount++;
} else {
assertFalse(AlloyForger.isGemstonePath(path));
}
}
assertEquals(40, gemstoneCount);
assertFalse(AlloyForger.isGemstonePath(null));
assertFalse(AlloyForger.isGemstonePath("m.gemstones_fake.ruby"));
}

@Test
public void gemstoneChanceUsesTheConfiguredIngredientValue() {
assertEquals(60.0, AlloyForger.gemstoneInheritanceChance(1), 0.0);
assertEquals(65.0, AlloyForger.gemstoneInheritanceChance(2), 0.0);
assertEquals(70.0, AlloyForger.gemstoneInheritanceChance(3), 0.0);
assertEquals(75.0, AlloyForger.gemstoneInheritanceChance(4), 0.0);
assertEquals(100.0, AlloyForger.gemstoneInheritanceChance(10), 0.0);
}

@Test
public void gemstoneWithMatchingStatAddsItsOwnPositiveValue() {
StatModifier steelArmor = new StatModifier("armor", 0.5);
StatModifier jasperArmor = new StatModifier("armor", 0.05);
StatModifier gemContribution = AlloyForger.modifierForMerge(jasperArmor, steelArmor, true);

assertEquals(0.05, gemContribution.getAmount(), 0.0);
assertNotSame(jasperArmor, gemContribution);
assertEquals(0.5, steelArmor.getAmount(), 0.0);
assertEquals(-0.45, AlloyForger.modifierForMerge(jasperArmor, steelArmor, false).getAmount(), 0.000001);
}

@Test
public void gemstoneWithAnyOtherMatchingStatAlsoAddsItsOwnValue() {
StatModifier baseHealth = new StatModifier("max_health", 2.0);
StatModifier gemHealth = new StatModifier("max_health", 0.5);
assertEquals(0.5, AlloyForger.modifierForMerge(gemHealth, baseHealth, true).getAmount(), 0.0);
assertEquals(0.5, AlloyForger.modifierForMerge(gemHealth, null, true).getAmount(), 0.0);
}
}