diff --git a/Server/src/main/content/global/skill/runecrafting/Rune.java b/Server/src/main/content/global/skill/runecrafting/Rune.java index 79762bf96..7736c579b 100644 --- a/Server/src/main/content/global/skill/runecrafting/Rune.java +++ b/Server/src/main/content/global/skill/runecrafting/Rune.java @@ -9,19 +9,19 @@ import core.game.node.item.Item; * @date 01/11/2013 */ public enum Rune { - AIR(Runes.AIR_RUNE.transform(), 1, 5, new int[] { 1, 11, 22, 33, 44, 55, 66, 77, 88, 99 }), - MIND(Runes.MIND_RUNE.transform(), 2, 5.5, new int[] { 1, 14, 28, 42, 56, 70, 84, 98 }), - WATER(Runes.WATER_RUNE.transform(), 5, 6, new int[] { 1, 19, 38, 57, 76, 95 }), - EARTH(Runes.EARTH_RUNE.transform(), 9, 6.5, new int[] { 1, 26, 52, 78 }), - FIRE(Runes.FIRE_RUNE.transform(), 14, 7, new int[] { 1, 35, 70 }), - BODY(Runes.BODY_RUNE.transform(), 20, 7.5, new int[] { 1, 46, 92 }), - COSMIC(Runes.COSMIC_RUNE.transform(), 27, 8, new int[] { 1, 59 }), - CHAOS(Runes.CHAOS_RUNE.transform(), 35, 8.5, new int[] { 1, 74 }), - ASTRAL(Runes.ASTRAL_RUNE.transform(), 40, 8.7, new int[] { 1, 82 }), - NATURE(Runes.NATURE_RUNE.transform(), 44, 9, new int[] { 1, 91 }), - LAW(Runes.LAW_RUNE.transform(), 54, 9.5), - DEATH(Runes.DEATH_RUNE.transform(), 65, 10), - BLOOD(Runes.BLOOD_RUNE.transform(), 77, 10.5), + AIR(Runes.AIR_RUNE.transform(), 1, 5, new int[] { 1, 11, 22, 33, 44, 55, 66, 77, 88, 99, 110 }), + MIND(Runes.MIND_RUNE.transform(), 2, 5.5, new int[] { 1, 14, 28, 42, 56, 70, 84, 98, 112 }), + WATER(Runes.WATER_RUNE.transform(), 5, 6, new int[] { 1, 19, 38, 57, 76, 95, 114 }), + EARTH(Runes.EARTH_RUNE.transform(), 9, 6.5, new int[] { 1, 26, 52, 78, 104 }), + FIRE(Runes.FIRE_RUNE.transform(), 14, 7, new int[] { 1, 35, 70, 105 }), + BODY(Runes.BODY_RUNE.transform(), 20, 7.5, new int[] { 1, 46, 92, 138 }), + COSMIC(Runes.COSMIC_RUNE.transform(), 27, 8, new int[] { 1, 59, 118 }), + CHAOS(Runes.CHAOS_RUNE.transform(), 35, 8.5, new int[] { 1, 74, 148 }), + ASTRAL(Runes.ASTRAL_RUNE.transform(), 40, 8.7, new int[] { 1, 82, 164 }), + NATURE(Runes.NATURE_RUNE.transform(), 44, 9, new int[] { 1, 91, 182 }), + LAW(Runes.LAW_RUNE.transform(), 54, 9.5, new int[] { 1, 110, }), + DEATH(Runes.DEATH_RUNE.transform(), 65, 10, new int[] { 1, 131 }), + BLOOD(Runes.BLOOD_RUNE.transform(), 77, 10.5, new int[] { 1, 154 }), SOUL(Runes.SOUL_RUNE.transform(), 90, 11); /** diff --git a/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java b/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java index fe97eb3a1..dd48fe6b1 100644 --- a/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java +++ b/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java @@ -1,6 +1,7 @@ package content.global.skill.runecrafting; import content.global.handlers.item.equipment.fistofguthixgloves.FOGGlovesManager; +import core.ServerConstants; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; @@ -333,6 +334,12 @@ public final class RuneCraftPulse extends SkillPulse { return 1; } int rcLevel = player.getSkills().getLevel(Skills.RUNECRAFTING); + int runecraftingFormulaRevision = ServerConstants.RUNECRAFTING_FORMULA_REVISION; + boolean lumbridgeDiary = player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE).isComplete(1); + return RuneCraftPulse.getMultiplier(rcLevel, rune, runecraftingFormulaRevision, lumbridgeDiary); + } + + public static int getMultiplier(int rcLevel, Rune rune, int runecraftingFormulaRevision, boolean lumbridgeDiary) { int[] multipleLevels = rune.getMultiple(); int i = 0; for (int level : multipleLevels) { @@ -341,17 +348,18 @@ public final class RuneCraftPulse extends SkillPulse { } } - if(multipleLevels.length > i) { + if(multipleLevels.length > i && runecraftingFormulaRevision >= 573) { int a = Math.max(multipleLevels[i-1], rune.getLevel()); int b = multipleLevels[i]; - double chance = ((double)rcLevel - (double)a) / ((double)b - (double)a); - player.debug(String.format("%d %d %d, %f", a, b, rcLevel, chance)); - if(RandomFunction.random(0.0, 1.0) < chance) { - i += 1; + if(b <= 99 || runecraftingFormulaRevision >= 581) { + double chance = ((double)rcLevel - (double)a) / ((double)b - (double)a); + if(RandomFunction.random(0.0, 1.0) < chance) { + i += 1; + } } } - if (player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE).isComplete(1) + if (lumbridgeDiary && new ArrayList<>(Arrays.asList(Rune.AIR, Rune.WATER, Rune.FIRE, Rune.EARTH)).contains(rune) && RandomFunction.getRandom(10) == 0) { //approximately 10% chance i += 1; diff --git a/Server/src/main/core/ServerConstants.kt b/Server/src/main/core/ServerConstants.kt index 287350a4f..8cb7c1333 100644 --- a/Server/src/main/core/ServerConstants.kt +++ b/Server/src/main/core/ServerConstants.kt @@ -317,5 +317,8 @@ class ServerConstants { @JvmField var FORCE_HALLOWEEN_RANDOMS = false + + @JvmField + var RUNECRAFTING_FORMULA_REVISION = 581 } } diff --git a/Server/src/main/core/game/system/config/ServerConfigParser.kt b/Server/src/main/core/game/system/config/ServerConfigParser.kt index 0ba6c9892..ccdaa71e3 100644 --- a/Server/src/main/core/game/system/config/ServerConfigParser.kt +++ b/Server/src/main/core/game/system/config/ServerConfigParser.kt @@ -163,6 +163,7 @@ object ServerConfigParser { ServerConstants.NEW_PLAYER_ANNOUNCEMENT = data.getBoolean("world.new_player_announcement", true) ServerConstants.HOLIDAY_EVENT_RANDOMS = data.getBoolean("world.holiday_event_randoms", true) ServerConstants.FORCE_HALLOWEEN_RANDOMS = data.getBoolean("world.force_halloween_randoms", false) + ServerConstants.RUNECRAFTING_FORMULA_REVISION = data.getLong("world.runecrafting_formula_revision", 581).toInt() val logLevel = data.getString("server.log_level", "VERBOSE").uppercase() ServerConstants.LOG_LEVEL = parseEnumEntry(logLevel) ?: LogLevel.VERBOSE diff --git a/Server/src/test/kotlin/RunecraftingTests.kt b/Server/src/test/kotlin/RunecraftingTests.kt new file mode 100644 index 000000000..c7e0db664 --- /dev/null +++ b/Server/src/test/kotlin/RunecraftingTests.kt @@ -0,0 +1,51 @@ +import content.global.skill.runecrafting.RuneCraftPulse; +import content.global.skill.runecrafting.Rune; + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class RunecraftTests { + fun rollRc(rcLevel: Int, rune: Rune, revision: Int, lo: Double, hi: Double) { + var total = 0.0 + for(i in 0 until 1000) { + total += RuneCraftPulse.getMultiplier(rcLevel, rune, revision, false) + } + val average = total / 1000.0 + Assertions.assertTrue(lo <= average && average <= hi, "rollRc: ${rcLevel} ${rune.name} ${revision}: ${average}") + } + + @Test fun testNatureRcMultipliers() { + rollRc(44, Rune.NATURE, 530, 1.0, 1.0); + rollRc(44, Rune.NATURE, 573, 1.0, 1.0); + rollRc(44, Rune.NATURE, 581, 1.0, 1.0); + + rollRc(68, Rune.NATURE, 530, 1.0, 1.0); + rollRc(68, Rune.NATURE, 573, 1.45, 1.55); + rollRc(68, Rune.NATURE, 581, 1.45, 1.55); + + rollRc(91, Rune.NATURE, 530, 2.0, 2.0); + rollRc(91, Rune.NATURE, 573, 2.0, 2.0); + rollRc(91, Rune.NATURE, 581, 2.0, 2.0); + + rollRc(90, Rune.NATURE, 530, 1.0, 1.0); + rollRc(90, Rune.NATURE, 573, 1.95, 2.0); + rollRc(90, Rune.NATURE, 581, 1.95, 2.0); + + rollRc(99, Rune.NATURE, 530, 2.0, 2.0); + rollRc(99, Rune.NATURE, 573, 2.0, 2.0); + rollRc(99, Rune.NATURE, 581, 2.0, 2.1); + } + @Test fun testLawRcMultipliers() { + rollRc(54, Rune.LAW, 530, 1.0, 1.0); + rollRc(54, Rune.LAW, 573, 1.0, 1.0); + rollRc(54, Rune.LAW, 581, 1.0, 1.0); + + rollRc(77, Rune.LAW, 530, 1.0, 1.0); + rollRc(77, Rune.LAW, 573, 1.0, 1.0); + rollRc(77, Rune.LAW, 581, 1.35, 1.45); + + rollRc(99, Rune.LAW, 530, 1.0, 1.0); + rollRc(99, Rune.LAW, 573, 1.0, 1.0); + rollRc(99, Rune.LAW, 581, 1.75, 1.85); + } +} diff --git a/Server/worldprops/default.conf b/Server/worldprops/default.conf index 0f5271bd8..eebd334a8 100644 --- a/Server/worldprops/default.conf +++ b/Server/worldprops/default.conf @@ -101,6 +101,8 @@ new_player_announcement = true holiday_event_randoms = true #force holiday randoms (can only force one at a time) force_halloween_randoms = false +# runecrafting formula revision (573 introduced probabilistic multiple runes, 581 extrapolated probabilistic runes past 99) +runecrafting_formula_revision = 581 [paths] #path to the data folder, which contains the cache subfolder and such