From acf5d74a9c94faad53b0ab1a81578d84930b346d Mon Sep 17 00:00:00 2001 From: Sindusk Date: Mon, 6 May 2019 09:25:48 -0400 Subject: [PATCH] WyvernMods Configurable Phase 2 --- build.gradle | 2 +- mods/WyvernMods.properties | 248 +++++++++ .../java/mod/sin/wyvern/CombatChanges.java | 308 +++-------- src/main/java/mod/sin/wyvern/Mastercraft.java | 126 +++++ .../java/mod/sin/wyvern/MeditationPerks.java | 166 +++--- .../java/mod/sin/wyvern/PlayerTitles.java | 95 +--- .../java/mod/sin/wyvern/SkillChanges.java | 111 ++-- src/main/java/mod/sin/wyvern/WyvernMods.java | 287 +++++++++- .../sin/wyvern/mastercraft/BytecodeTools.java | 494 ------------------ .../wyvern/mastercraft/ExtendTitleEnum.java | 223 -------- .../sin/wyvern/mastercraft/Mastercraft.java | 203 ------- 11 files changed, 854 insertions(+), 1409 deletions(-) create mode 100644 src/main/java/mod/sin/wyvern/Mastercraft.java delete mode 100644 src/main/java/mod/sin/wyvern/mastercraft/BytecodeTools.java delete mode 100644 src/main/java/mod/sin/wyvern/mastercraft/ExtendTitleEnum.java delete mode 100644 src/main/java/mod/sin/wyvern/mastercraft/Mastercraft.java diff --git a/build.gradle b/build.gradle index c5e79dc..5032b80 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ repositories { dependencies { compile 'org.gotti.wurmunlimited:server-modlauncher:0.43' - compile 'com.github.Sindusk:sindusklibrary:v2.2' + compile 'com.github.Sindusk:sindusklibrary:v2.3' compile 'com.github.bdew-wurm:bdew_server_mod_tools:v1.0.0' compile 'com.github.Sindusk:DiscordRelay:v1.2' compile 'com.github.Sindusk:DUSKombat:v1.0' diff --git a/mods/WyvernMods.properties b/mods/WyvernMods.properties index 5aa6675..b5cea90 100644 --- a/mods/WyvernMods.properties +++ b/mods/WyvernMods.properties @@ -515,6 +515,254 @@ regenerateStaminaOnVehicleAnySlope=true ## >> END QUALITY OF LIFE MODULE << ## +## >> COMBAT MODULE << ## +## The Combat Module offers options that affect the combat aspects of the game. +## This isn't restricted to simply the actual combat itself. It also includes some options to change combat-oriented spells and other mechanics. +enableCombatModule=true + +# > COMBAT RATING ADJUSTMENTS < # +# Combat Rating Adjustments involve some tweaks to how combat rating is calculated. +# There are two components: additive and multiplicative combat rating adjustments. +# Additive modifiers are always applied first. This creates a "base" combat rating that is then modified. +# Multiplicative modifiers apply after all additive combat rating adjustments, including vanilla ones. +enableCombatRatingAdjustments=true +#royalExecutionerBonus: Additive - Adds +2 CR against PvE targets while holding Royal Executioner kingdom title. +# The vanilla royal executioner bonus only applies to PvP combat. This option is meant to give it a PvE bonus as well. +royalExecutionerBonus=true +#TODO: Make this option a value instead and allow configuration of how much CR to grant. +#petSoulDepthScaling: Multiplicative - Scales the CR of pets with the Soul Depth of the owner/dominator. +# Applies a multiplier of Soul Depth * 0.02 to the pet's CR. This grants 40% CR at 20 Soul Depth and 200% CR at 100 Soul Depth. +# At 50 Soul Depth this modifier equates to 100%, resulting in no change to the pet's CR. +petSoulDepthScaling=true +#TODO: Make this option a value instead and allow configuration of the CR multiplier per soul depth. +#vehicleCombatRatingPenalty: Multiplicative - Reduces the CR of players on a vehicle by 25%. +# There is no significant offensive penalty for being the commander of a vehicle while in combat. +# This option is meant to encourage players to avoid fighting while on a vehicle. +vehicleCombatRatingPenalty=true +#TODO: Make this option a value instead and allow configuration of the CR multiplier while on a vehicle. +# > END COMBAT RATING ADJUSTMENTS < # + +#fixMagranonDamageStacking: In vanilla combat, Magranon/warrior damage bonus (+15% damage from deity) does not stack correctly. +# This applies only in aggressive stance, where the aggressive fighting skill calculations will adjust if the player has warrior bonus. +# When this occurs, it results in a loss of damage from the bonus, making the warrior passive effectively ~10-12% instead of the full 15%. +# This option adjusts the fight skill calculations and forces it to accurately apply the 15% damage bonus regardless of other factors. +fixMagranonDamageStacking=true +#adjustCombatRatingSpellPower: Adjusts the combat rating bonus from Truehit and Excel to 50% of their vanilla values. +adjustCombatRatingSpellPower=true +#TODO: Make this option a multiplier instead of a boolean. +#disableLegendaryRegeneration: Disables the natural healing/regeneration of legendary creatures (uniques). +disableLegendaryRegeneration=true +#useStaticLegendaryRegeneration: Applies a static healing over time to legendary creatures (uniques). +# This option is meant to be used in conjunction with the previous one to make legendaries able to be taken slowly. +# Instead of requiring their defeat in 10 minutes (where they would outheal their attackers), it now regenerates at a set rate. +# This allows players to take their time with uniques and use strategy, so long as the damage inflicted outpaces the natural static regeneration. +# The regeneration is set to 75 health per second (~0.1% maximim health per second). Requires ~874 seconds (14.5 minutes) to heal a full health bar. +useStaticLegendaryRegeneration=true +#TODO: Allow configuration of health regeneration per second. + +## >> END COMBAT MODULE << ## + +## >> MASTERCRAFT MODULE << ## +## The Mastercraft Module is a new series of systems designed to encourage players to explore end-game crafting further. +## Vanilla settings generally don't encourage players to skill past 90 or improve items past 90. +## The goals of this system, and the settings involved here, are to encourage players to strive for perfection instead of settling at "good enough." +enableMastercraftModule=true + +# > DIFFICULTY ADJUSTMENTS < # +# Difficulty Adjustments modify the difficulty when a skill check occurs based on the factors set here. +# This affects almost all systems in the game, from combat to improvement to creation chance to spell power. +# These configurations are aimed at increasing the ways a player can improve their results for any given action. +# By putting time and effort into one's craft, they can more easily improve items to higher levels or be more successful in their activities. +enableDifficultyAdjustments=true +#affinityDifficultyBonus: Reduces the difficulty of actions by 1 per affinity in the skill. +affinityDifficultyBonus=true +#TODO: Make this configuration able to be set to any amount of difficulty reduction. +#legendDifficultyBonus: Grants a reduction to difficulty for all skill over 99. +# Begins at 99.00 skill with no difficulty change, then maxes out at 100.00 skill granting -2 difficulty. +legendDifficultyBonus=true +#masterDifficultyBonus: Grants a reduction to difficulty for all skill over 90. +# Begins at 90.00 skill with no difficulty change, then maxes out at 100.00 skill granting -2 difficulty. +masterDifficultyBonus=true +#TODO: Make the skill scaling a multi-configuration where multiple thresholds can be set for a variety of scaling difficulty changes. +#itemRarityDifficultyBonus: Reduces the difficulty of an action depending on the rarity of the tool being used. +# Normal: No bonus, Rare: -1 difficulty, Supreme: -2 difficulty, Fantastic: -3 difficulty +itemRarityDifficultyBonus=true +#legendItemDifficultyBonus: Reduces the difficulty of an action for all quality over 99 of the tool being used. +# Begins at 99.00 QL with no difficulty change, then maxes out at 100.00 QL granting -1 difficulty. +legendItemDifficultyBonus=true +#masterItemDifficultyBonus: Reduces the difficulty of an action for all quality over 90 of the tool being used. +# Begins at 90.00 QL with no difficulty change, then maxes out at 100.00 QL granting -1 difficulty. +masterItemDifficultyBonus=true +#TODO: Allow further customization of the configurations for difficulty adjustments regarding items. + +# > END DIFFICULTY ADJUSTMENTS < # + +#empoweredChannelers: Empowers players with up to double the power on their casts based on their channeling skill and affinities. +# This grants +2 power to casts per affinity in channeling. +# Furthermore, it also rolls 4 times up to the value of their channeling skill. It then takes the lowest value and adds it to their cast power. +# !!! Be warned that enabling this option will allow players to perform natural casts of enchants up to roughly 200 power !!! +empoweredChannelers=true +#TODO: Expand on this option and enable more configuration of how the enchant power addition is calculated. +#channelSkillFavorReduction: Reduces the favor costs of spells based on channeling skill. +# Reduces favor cost by 2% per channeling affinity. +# Reduces favor cost by 10% per skill over 99. No bonus at 99.00, scaling to 10% at 100.00 channeling. +# Reduces favor cost by 1% per skill over 90. No bonus at 90.00, scaling to 10% at 100.00 channeling. +channelSkillFavorReduction=true +#TODO: Split up the favor cost reductions, then expand into a multi-configuration style to allow further customization. +#TODO: Also, the code that executes these configurations is old and should be updated with new priest rework code to make it cleaner. + +## >> END MASTERCRAFT MODULE << ## + +## >> SKILL MODULE << ## +## The Skill Module allows control over the components of skills, including skill gain adjustments. +enableSkillModule=true + +# > HYBRID SKILL GAIN < # +# Hybrid Skill Gain is the system which combines the vanilla Wurm Unlimited skill gain system with the classic Wurm Online model. +# In Wurm Unlimited, players gain skill for any successful action whether the result is 0.1 or 100. The skill tick size is the same every action. +# In Wurm Online, players gain skill for any successful action within the range 0 - 40. The skill tick size is based on the duration of the action. +# This new hybrid system makes it so players gain skill for any action, including mild failures (about -20), up to 100. +# However, the system also rewards more skill gain for actions with results closer to zero (if configured that way). +# This was done in order to discourage the "spam creation" skilling method. It is still effective, but improvement is now competitive. +# Visual graph that represents what this skill gain system looks like: https://i.imgur.com/9ykw5dJ.png +# Here is the formula which handles the multiplier for the new skill gain: +# valueAtOneHundred*Math.pow(valueAtZero/valueAtOneHundred, (2-Math.pow(100/(100+Math.max(-99,power)), negativeDecayRate))*Math.pow((100-power)*0.01, positiveDecayRate)); +# If you can't read/don't understand that, don't worry! Not many people can! However, if you can read or understand the function, +# you should be able to plug in a formula into Google Sheets, Wolfram Alpha, or Excel and create your own graph where you +# can adjust the following options and give the proper result that you're looking for. +# The default values listed here result in the visual graph represented above. +enableHybridSkillGain=true +#hybridNegativeDecayRate: [Limit: > 0] How fast the function decays past zero going negative. +# Higher values will cause the "midpoint" to move in a positive direction. It will also increase the sharpness of decline after the midpoint. +# Lower values will cause the "midpoint" to move in a negative direction. It will also decrease the sharpness of the decline after the midpoint, resulting a smoother curve. +hybridNegativeDecayRate=5 +#hybridPositiveDecayRate: [Limit: > 0] How quickly the function will decay past zero going positive. +# Higher values will result in a sharper curve to the 1.0 multiplier in values greater than zero. +# Lower values will result in a less sharp curve towards the 1.0 multiplier in values greater than zero. +hybridPositiveDecayRate=3 +#hybridValueAtZero: [Limit: > 0, must be greater than hybridValueAtOneHundred] What the multiplier should be be when an action results in zero. +hybridValueAtZero=3.74 +#hybridValueAtOneHundred: [Limit: > 0, must be less than hybridValueAtZero] What the multiplier should be when an action results in one hundred. +hybridValueAtOneHundred=0.9 + +# > END HYBRID SKILL GAIN < # + +#skillName-#: Changes the name of a skill. Format: skillName-#:skill,newName +# skill = Either the internal ID of a skill or the name of a skill (case insensitive). Example: 1008, mining, MINING, Mining, MiNiNg +# newName = The new name for the skill. Should be allowed to have spaces and other characters, but needs additional testing. +# - Case will carry to the in-game display, so keep that in mind. Making the new name fully lower case might make it look out of place among other skills. +# Set name of Preaching to Gem Augmentation +skillName-1:Preaching,Gem augmentation +# Set name of Stealing to Soulstealing +skillName-2:Stealing,Soulstealing + +#skillDifficulty-#: Changes the internal difficulty of a skill. Format: skillDifficulty-#:skill,difficulty +# skill = Either the internal ID of a skill or the name of a skill (case insensitive). Example: 1008, mining, MINING, Mining, MiNiNg +# difficulty = The new difficulty to set for the skill. Most skills default to 4000. This difficulty will affect the skill gain for that skill. +# - In the example of a 4000 difficulty skill: 2000 would double skill gain. 8000 would halve skill gain. 6000 would make it 1.5x slower. +# - For a full list of difficulty for each skill... I don't have one. Nevermind. +# Set mining to 3000 difficulty. Default is 8000. Makes it 2.66x higher skill gain. +skillDifficulty-1:Mining,3000 +# Set lockpicking to 700 difficulty. Default is 2000. Makes it 2.85x higher skill gain. +skillDifficulty-2:Lock Picking,700 +# Set meditating difficulty to 300. Default is 2000. Makes it 6.66x higher skill gain. Meditating is evil just like the increase. +skillDifficulty-3:Meditating,300 + +#skillTickTime-#: Changes the tick timer for skill gain on a skill. Format: skillTickTime-#:skill,tickTime +# skill = Either the internal ID of a skill or the name of a skill (case insensitive). Example: 1008, mining, MINING, Mining, MiNiNg +# tickTime = The interval (in milliseconds) before players can gain a skill tick for a skill. +# - Example: Lock picking has a default of 600000, or 600 seconds, for it's tick time. +# - This prevents players from being able to gain another skill tick of Lock picking until 600 seconds have passed (10 minutes). +# Set tick time of stealing to zero to remove the 10 minute interval. This is useful for Soulstealing. +skillTickTime-1:Stealing,0 +# Set tick time of meditating to 3600 seconds (1 hour). +skillTickTime-2:Meditating,3600000 + +#changePreachingLocation: Changes the dependencies of Preaching so that it moves under Masonry. +# Recommended to set to false. Option is here temporarily until further work can be done to customize dependencies. +changePreachingLocation=true +#TODO: Add multiple configuration settings to change the dependencies of skills and remove this hold-over option. + +## >> END SKILL MODULE << ## + +## >> MEDITATION MODULE << ## +## The Meditation Module enables adjustments to the meditation system of Wurm. +## It includes additions that make meditation perks more reasonable at lower levels and scale better as it increases. +enableMeditationModule=true + +#simplifyMeditationTerrain: Simplifies the terrain where players can meditate for a question. +# Instead of having to find specific tiles, any tile of the proper type will grant a question: +# - Insanity: Any cave tile +# - Love: Any grass, bush, or tree +# - Hate: Any mycelium, mycelium bush, or infected tree +# - Knowledge: Any sand +# - Power: Any rock or cliff tile (above ground). +simplifyMeditationTerrain=true +#removeInsanitySotG: Removes the Shield of the Gone bonus from Insanity. The buff will still show, but the effect will be gone. +removeInsanitySotG=true +#removeHateWarBonus: Removes the active effect to deal increased damage in combat from Path of Hate. Does not remove the structure damage version. +removeHateWarBonus=true +#insanitySpeedBonus: Adds new effect to Path of Insanity which grants 2% increased action speed per level starting at 7. +# At Insanity level 13, this bonus equates to 12% increased action speed. Does not affect weapon swing speed. +insanitySpeedBonus=true +#TODO: Allow configuration of the speed bonus and level it starts at. +#hateMovementBonus: Adds a new effect to Path of Hate which grants 1% increased movement speed (walking only) per level starting at 3. +# At Hate level 13, this bonus equates to 10% increased movement speed. Does not affect mounted or vehicle movement speed. +hateMovementBonus=true +#TODO: Allow configuration of the movement bonus and level it starts at. +#scalingPowerStaminaBonus: Scales the Path of Power stamina bonus from 30 at level 11 to start at 7 and increase with each level. +# Grants 5 bonus stamina at level 7, and adds 5 additional stamina per level afterwards. +# At Power level 13, this bonus equates to 30 additional Body Stamina. +scalingPowerStaminaBonus=true +#TODO: Allow configuration of the stamina bonus and level it starts at. +#scalingKnowledgeSkillGain: Scales the Path of Knowledge skill gain bonus from 25% at level 11 to start at 7 and increase with each level. +# Grants 5% increased skill gain at level 7, and adds 5% additional skill gain per level afterwards. +# At Knowledge level 13, this bonus grants 30% increased skill gain. +scalingKnowledgeSkillGain=true +#TODO: Allow configuration of the skill gain bonus and level it starts at. +#removeMeditationTickTimer: Removes the vanilla meditation tick timer which is not attached to the skill. +# This tick timer is coded differently from the skill system. It has to be removed like this in order to make the skill system version work. +# Enabling this option will allow you to configure the meditation tick timer using the Skills module. +# Without this enabled, this will override any settings from the Skills module and prevent skill gain unless the meditation system allows it. +removeMeditationTickTimer=true +#newMeditationBuffs: This option is meant to adjust the buff icons for meditation to coincide with all the options above. +# It's recommended to only allow this if all (or at least most) of the options above are enabled. +# Otherwise it probably wont display buffs correctly for some paths. +newMeditationBuffs=true +#TODO: Recode this to make it adjust properly to the configurations set throughout the rest of the module. + +# > MEDITATION ABILITY COOLDOWNS < # +# Meditation Ability Cooldowns allow you to set all of the cooldowns for meditation abilities. +# All cooldowns are set using milliseconds. A setting of 1000 means 1 second in this configuration. +enableMeditationAbilityCooldowns=true +#loveRefreshCooldown: Cooldown for Path of Love's Refresh ability. (Default: 64800000 [18 hours]) +loveRefreshCooldown=28800000 +#loveEnchantNatureCooldown: Cooldown for Path of Love's Enchant Nature ability. (Default: 64800000 [18 hours]) +loveEnchantNatureCooldown=28800000 +#loveLoveEffectCooldown: Cooldown for Path of Love's Love Effect ability. (Default: 64800000 [18 hours]) +loveLoveEffectCooldown=14400000 +#hateWarDamageCooldown: Cooldown for Path of Hate's War Damage ability. (Default: 64800000 [18 hours]) +hateWarDamageCooldown=21600000 +#hateStructureDamageCooldown: Cooldown for Path of Hate's Structure Damage ability. (Default: 64800000 [18 hours]) +hateStructureDamageCooldown=14400000 +#hateFearCooldown: Cooldown for Path of Hate's Fear ability. (Default: 64800000 [18 hours]) +hateFearCooldown=21600000 +#powerElementalImmunityCooldown: Cooldown of Path of Power's Elemental Immunity ability. (Default: 64800000 [18 hours]) +powerElementalImmunityCooldown=21600000 +#powerEruptFreezeCooldown: Cooldown of Path of Power's Erupt/Freeze abilities. (Default: 64800000 [18 hours]) +powerEruptFreezeCooldown=28800000 +#powerIgnoreTrapsCooldown: Cooldown of Path of Power's Ignore Traps ability. (Default: 64800000 [18 hours]) +powerIgnoreTrapsCooldown=14400000 +#knowledgeInfoCreatureCooldown: Cooldown of Path of Knowledge's Get Info (Creature) ability. (Default: 64800000 [18 hours]) +knowledgeInfoCreatureCooldown=14400000 +#knowledgeInfoTileCooldown: Cooldown of Path of Knowledge's Get Info (Tile) ability. (Default: 64800000 [18 hours]) +knowledgeInfoTileCooldown=14400000 +#TODO: Add the rest of the meditation abilites as configurable options. + +# > END MEDITATION ABILITY COOLDOWNS < # + +## >> END MEDITATION MODULE << ## + ## >> TREASURE CHEST LOOT MODULE << ## ## The Treasure Chest Loot Module affects the vanilla treasure chests. ## By default, treasure chests can spawn randomly in the wild. This module reconfigures the loot that spawns in them. diff --git a/src/main/java/mod/sin/wyvern/CombatChanges.java b/src/main/java/mod/sin/wyvern/CombatChanges.java index ad2478b..39a6ab2 100644 --- a/src/main/java/mod/sin/wyvern/CombatChanges.java +++ b/src/main/java/mod/sin/wyvern/CombatChanges.java @@ -24,23 +24,12 @@ import java.util.logging.Logger; public class CombatChanges { public static Logger logger = Logger.getLogger(CombatChanges.class.getName()); - // Added to CombatHandled - public static int getWeaponType(Item weapon){ - if(weapon.enchantment == Enchants.ACID_DAM){ - return Wound.TYPE_ACID; - }else if(weapon.enchantment == Enchants.FROST_DAM){ - return Wound.TYPE_COLD; - }else if(weapon.enchantment == Enchants.FIRE_DAM){ - return Wound.TYPE_BURN; - } - return -1; - } - public static float combatRatingAdditive(float combatRating, Creature cret, Creature opponent){ //logger.info("Checking additive ("+cret.getName()+" vs "+opponent.getName()+"), combatRating = "+combatRating); float add = 0.0f; if(cret != null && cret.isPlayer() && (opponent != null && !opponent.isPlayer())){ - if(cret.isRoyalExecutioner()){ + // 2 CR against non-players for Royal Executioner kingdom title. + if(WyvernMods.royalExecutionerBonus && cret.isRoyalExecutioner()){ add += 2.0f; } } @@ -51,7 +40,7 @@ public class CombatChanges { float mult = 1.0f; if(cret != null){ //logger.info("Cret is a pet."); - if(cret.isDominated() && cret.getDominator() != null) { + if(WyvernMods.petSoulDepthScaling && cret.isDominated() && cret.getDominator() != null) { if (cret.getDominator() instanceof Player) { Player owner = (Player) cret.getDominator(); double depth = owner.getSoulDepth().getKnowledge(); @@ -61,7 +50,7 @@ public class CombatChanges { logger.info("Somehow a pet is dominated by a non-player? (" + cret.getDominator().getName() + ")"); } } - if(QualityOfLife.getVehicleSafe(cret) != null){ + if(WyvernMods.vehicleCombatRatingPenalty && QualityOfLife.getVehicleSafe(cret) != null){ mult *= 0.75f; } } @@ -98,17 +87,6 @@ public class CombatChanges { } } - // Added (kind of) to CombatHandled - public static float getAdjustedOakshell(Creature defender, Item armour, float armourMod){ - if(defender != null && armour == null){ - float oakshellPower = defender.getBonusForSpellEffect(Enchants.CRET_OAKSHELL); - if(oakshellPower > 0f){ - return (float) (1-(0.8f*Math.pow((oakshellPower/(oakshellPower+80)), 0.5d))); - } - } - return armourMod; // Returns previous armourMod if the target has armour. - } - protected static ArrayList uniques = new ArrayList<>(); public static void pollUniqueCollection(){ for(Creature cret : Creatures.getInstance().getCreatures()){ @@ -151,114 +129,6 @@ public class CombatChanges { return damage > 1D; } - /* Disabled as of WU 1.9 - No longer necessary and no longer functions. - - static void patchCombatDamageCheckCombatEngine(ClassPool classPool) throws NotFoundException, BadBytecode { - CtClass cls = classPool.getCtClass("com.wurmonline.server.combat.CombatEngine"); - CtClass ctCreature = classPool.get("com.wurmonline.server.creatures.Creature"); - CtClass ctString = classPool.get("java.lang.String"); - CtClass ctBattle = classPool.get("com.wurmonline.server.combat.Battle"); - CtClass ctCombatEngine = classPool.get("com.wurmonline.server.combat.CombatEngine"); - // @Nullable Creature performer, Creature defender, byte type, int pos, double damage, float armourMod, - // String attString, @Nullable Battle battle, float infection, float poison, boolean archery, boolean alreadyCalculatedResist - CtClass[] params1 = { - ctCreature, - ctCreature, - CtClass.byteType, - CtClass.intType, - CtClass.doubleType, - CtClass.floatType, - ctString, - ctBattle, - CtClass.floatType, - CtClass.floatType, - CtClass.booleanType, - CtClass.booleanType - }; - String desc1 = Descriptor.ofMethod(CtClass.booleanType, params1); - CtMethod method = cls.getMethod("addWound", desc1); - //CtMethod method = cls.getMethod("setDamage", "(Lcom/wurmonline/server/creatures/Creature;Lcom/wurmonline/server/items/Item;DBB)Z"); - MethodInfo methodInfo = method.getMethodInfo(); - CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); - ConstPool constPool = codeAttribute.getConstPool(); - CodeIterator codeIterator = codeAttribute.iterator(); - - // Scan through all the bytecode - look for a multiplication followed by comparing - while (codeIterator.hasNext()) { - int pos = codeIterator.next(); - int op = codeIterator.byteAt(pos); - if (op != CodeIterator.DMUL) continue; // not multiplication - continue - op = codeIterator.byteAt(++pos); - if (op == CodeIterator.LDC2_W && codeIterator.byteAt(pos + 3) == CodeIterator.DCMPL) { - // found the pattern, check the value it's comparing to - int ref = codeIterator.u16bitAt(pos + 1); - double val = constPool.getDoubleInfo(ref); - if (val == 500.0) { - // here it is, generate new code to insert - // We'll be calling canDoDamage, the first parameter (damage) is already on the stack, prepare the rest - Bytecode newCode = new Bytecode(constPool); - - newCode.add(Bytecode.ALOAD_0); // performer - first parameter of addWound - newCode.add(Bytecode.ALOAD_1); // defender - first parameter of addWound - - // call our methor, result is left on the stack - newCode.addInvokestatic( - CombatChanges.class.getName(), "canDoDamage", - "(DLcom/wurmonline/server/creatures/Creature;Lcom/wurmonline/server/creatures/Creature;)Z"); - - // The code we're replacing is 4 bytes - LDC2_W, 2byte reference and DCMPL - // Insert a gap for to match the size of the new code - codeIterator.insertGap(pos, newCode.getSize() - 4); - - // And put the new code - codeIterator.write(newCode.get(), pos); - } - } - } - }*/ - - /*static void patchCombatDamageCheckCombatHandler(ClassPool classPool) throws NotFoundException, BadBytecode { - CtClass cls = classPool.getCtClass("com.wurmonline.server.creatures.CombatHandler"); - CtMethod method = cls.getMethod("setDamage", "(Lcom/wurmonline/server/creatures/Creature;Lcom/wurmonline/server/items/Item;DBB)Z"); - MethodInfo methodInfo = method.getMethodInfo(); - CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); - ConstPool constPool = codeAttribute.getConstPool(); - CodeIterator codeIterator = codeAttribute.iterator(); - - // Scan through all the bytecode - look for a multiplication followed by comparing - while (codeIterator.hasNext()) { - int pos = codeIterator.next(); - int op = codeIterator.byteAt(pos); - if (op != CodeIterator.DMUL) continue; // not multiplication - continue - op = codeIterator.byteAt(++pos); - if (op == CodeIterator.LDC2_W && codeIterator.byteAt(pos + 3) == CodeIterator.DCMPL) { - // found the pattern, check the value it's comparing to - int ref = codeIterator.u16bitAt(pos + 1); - double val = constPool.getDoubleInfo(ref); - if (val == 500.0) { - // here it is, generate new code to insert - // We'll be calling canDoDamage, the first parameter (damage) is already on the stack, prepare the rest - Bytecode newCode = new Bytecode(constPool); - newCode.add(Bytecode.ALOAD_0); // this - newCode.addGetfield(cls, "creature", "Lcom/wurmonline/server/creatures/Creature;"); // this.creature - newCode.add(Bytecode.ALOAD_1); // defender - first parameter of setDamage - - // call our methor, result is left on the stack - newCode.addInvokestatic( - CombatChanges.class.getName(), "canDoDamage", - "(DLcom/wurmonline/server/creatures/Creature;Lcom/wurmonline/server/creatures/Creature;)Z"); - - // The code we're replacing is 4 bytes - LDC2_W, 2byte reference and DCMPL - // Insert a gap for to match the size of the new code - codeIterator.insertGap(pos, newCode.getSize() - 4); - - // And put the new code - codeIterator.write(newCode.get(), pos); - } - } - } - }*/ - // Added to CombatHandled public static void pollCreatureActionStacks(){ for(Creature c : Creatures.getInstance().getCreatures()){ @@ -278,119 +148,73 @@ public class CombatChanges { final Class thisClass = CombatChanges.class; String replace; - Util.setReason("Make custom combat rating changes."); CtClass ctCombatHandler = classPool.get("com.wurmonline.server.creatures.CombatHandler"); - replace = "combatRating += "+CombatChanges.class.getName()+".combatRatingAdditive(combatRating, this.creature, $1);" + - "crmod *= "+CombatChanges.class.getName()+".combatRatingMultiplicative(combatRating, this.creature, $1);" + - "$_ = $proceed($$);"; - Util.instrumentDeclared(thisClass, ctCombatHandler, "getCombatRating", "getFlankingModifier", replace); - CtClass ctCreature = classPool.get("com.wurmonline.server.creatures.Creature"); - - /* Disabled in Wurm Unlimited 1.9 - No longer necessary while using DUSKombat. - - Util.setReason("Increase unique damage to pets."); - CtClass ctString = classPool.get("java.lang.String"); - CtClass ctBattle = classPool.get("com.wurmonline.server.combat.Battle"); - CtClass ctCombatEngine = classPool.get("com.wurmonline.server.combat.CombatEngine"); - // @Nullable Creature performer, Creature defender, byte type, int pos, double damage, float armourMod, - // String attString, @Nullable Battle battle, float infection, float poison, boolean archery, boolean alreadyCalculatedResist - CtClass[] params1 = { - ctCreature, - ctCreature, - CtClass.byteType, - CtClass.intType, - CtClass.doubleType, - CtClass.floatType, - ctString, - ctBattle, - CtClass.floatType, - CtClass.floatType, - CtClass.booleanType, - CtClass.booleanType - }; - String desc1 = Descriptor.ofMethod(CtClass.booleanType, params1); - replace = "if($2.isDominated() && $1 != null && $1.isUnique()){" + - //" logger.info(\"Detected unique hit on a pet. Adding damage.\");" + - " $5 = $5 * 2d;" + - "}" + - "if($2.isUnique() && $1 != null && $1.isDominated()){" + - " logger.info(\"Detected pet hit on a unique. Reducing damage.\");" + - " $5 = $5 * 0.5d;" + - "}"; - Util.insertBeforeDescribed(thisClass, ctCombatEngine, "addWound", desc1, replace);*/ - - Util.setReason("Adjust weapon damage type based on the potion/salve applied."); - replace = "int wt = "+CombatChanges.class.getName()+".getWeaponType($1);" - + "if(wt != -1){" - + " type = wt;" - + " return wt;" - + "}"; - Util.insertBeforeDeclared(thisClass, ctCombatHandler, "getType", replace); - CtClass ctItem = classPool.get("com.wurmonline.server.items.Item"); - CtClass[] params2 = { - ctCreature, - ctItem, - ctCreature - }; - String desc2 = Descriptor.ofMethod(CtClass.doubleType, params2); - - /* Disabled in Wurm Unlimited 1.9 - Priest Rework adjusted Bloodthirst in an identical way. - - Util.setReason("Adjust bloodthirst to epic settings."); - replace = "$_ = true;"; - Util.instrumentDescribed(thisClass, ctCombatHandler, "getDamage", desc2, "isThisAnEpicOrChallengeServer", replace);*/ - - Util.setReason("Fix magranon damage bonus stacking."); - replace = "if(mildStack){" + - " $_ = $proceed($$) * 8 / 5;" + - "}else{" + - " $_ = $proceed($$);" + - "}"; - Util.instrumentDescribed(thisClass, ctCombatHandler, "getDamage", desc2, "getModifiedFloatEffect", replace); - CtClass ctAttackAction = classPool.get("com.wurmonline.server.creatures.AttackAction"); - CtClass[] params3 = { - ctCreature, - ctAttackAction, - ctCreature - }; - String desc3 = Descriptor.ofMethod(CtClass.doubleType, params3); - /* Disabled in Wurm Unlimited 1.9 - Priest Rework adjusted Bloodthirst in an identical way. + if (WyvernMods.enableCombatRatingAdjustments) { + Util.setReason("Make custom combat rating changes."); + replace = "combatRating += " + CombatChanges.class.getName() + ".combatRatingAdditive(combatRating, this.creature, $1);" + + "crmod *= " + CombatChanges.class.getName() + ".combatRatingMultiplicative(combatRating, this.creature, $1);" + + "$_ = $proceed($$);"; + Util.instrumentDeclared(thisClass, ctCombatHandler, "getCombatRating", "getFlankingModifier", replace); + } - Util.setReason("Adjust bloodthirst to epic settings."); - replace = "$_ = true;"; - Util.instrumentDescribed(thisClass, ctCombatHandler, "getDamage", desc3, "isThisAnEpicOrChallengeServer", replace);*/ + if (WyvernMods.fixMagranonDamageStacking) { + // Normal combat version + CtClass[] params2 = { + ctCreature, + ctItem, + ctCreature + }; + String desc2 = Descriptor.ofMethod(CtClass.doubleType, params2); - Util.setReason("Fix magranon damage bonus stacking."); - replace = "if(mildStack){" + - " $_ = $proceed($$) * 8 / 5;" + - "}else{" + - " $_ = $proceed($$);" + - "}"; - Util.instrumentDescribed(thisClass, ctCombatHandler, "getDamage", desc3, "getModifiedFloatEffect", replace); + Util.setReason("Fix magranon damage bonus stacking."); + replace = "if(mildStack){" + + " $_ = $proceed($$) * 8 / 5;" + + "}else{" + + " $_ = $proceed($$);" + + "}"; + Util.instrumentDescribed(thisClass, ctCombatHandler, "getDamage", desc2, "getModifiedFloatEffect", replace); - Util.setReason("Nerf truehit/excel."); - replace = "$_ = $proceed($$) * 0.5f;"; - Util.instrumentDeclared(thisClass, ctCombatHandler, "getCombatRating", "getBonusForSpellEffect", replace); + // AttackAction version + CtClass[] params3 = { + ctCreature, + ctAttackAction, + ctCreature + }; + String desc3 = Descriptor.ofMethod(CtClass.doubleType, params3); - Util.setReason("Nerf oakshell."); - replace = "if(defender.isPlayer() && defender.getBonusForSpellEffect((byte)22) > 0f){" + - " armourMod = "+CombatChanges.class.getName()+".getAdjustedOakshell(defender, armour, armourMod);" + - "}" + - "$_ = $proceed($$);"; - Util.instrumentDeclared(thisClass, ctCombatHandler, "setDamage", "getDamReductionBonusFor", replace); + Util.setReason("Fix magranon damage bonus stacking."); + replace = "if(mildStack){" + + " $_ = $proceed($$) * 8 / 5;" + + "}else{" + + " $_ = $proceed($$);" + + "}"; + Util.instrumentDescribed(thisClass, ctCombatHandler, "getDamage", desc3, "getModifiedFloatEffect", replace); + } - Util.setReason("Disable natural regeneration on uniques."); - CtClass ctWound = classPool.get("com.wurmonline.server.bodys.Wound"); - replace = "if(!this.creature.isUnique()){" - + " $_ = $proceed($$);" - + "}"; - Util.instrumentDeclared(thisClass, ctWound, "poll", "modifySeverity", replace); - Util.instrumentDeclared(thisClass, ctWound, "poll", "checkInfection", replace); - Util.instrumentDeclared(thisClass, ctWound, "poll", "checkPoison", replace); + if (WyvernMods.adjustCombatRatingSpellPower) { + Util.setReason("Nerf truehit/excel."); + replace = "$_ = $proceed($$) * 0.5f;"; + Util.instrumentDeclared(thisClass, ctCombatHandler, "getCombatRating", "getBonusForSpellEffect", replace); + } + + if (WyvernMods.disableLegendaryRegeneration) { + Util.setReason("Disable natural regeneration on legendary creatures."); + CtClass ctWound = classPool.get("com.wurmonline.server.bodys.Wound"); + replace = "if(!this.creature.isUnique()){" + + " $_ = $proceed($$);" + + "}"; + Util.instrumentDeclared(thisClass, ctWound, "poll", "modifySeverity", replace); + Util.setReason("Disable natural regeneration on legendary creatures."); + Util.instrumentDeclared(thisClass, ctWound, "poll", "checkInfection", replace); + Util.setReason("Disable natural regeneration on legendary creatures."); + Util.instrumentDeclared(thisClass, ctWound, "poll", "checkPoison", replace); + } + + /* Disabled for now until it can be fixed for new Priest Update adjustments. Util.setReason("Allow Life Transfer to stack with Rotting Touch (Mechanics-Wise)."); replace = CombatChanges.class.getName()+".doLifeTransfer(this.creature, defender, attWeapon, defdamage, armourMod);" @@ -403,15 +227,7 @@ public class CombatChanges { Util.setReason("Reduce Life Transfer healing amount for certain wounds."); replace = "$_ = $proceed("+CombatChanges.class.getName()+".getLifeTransferAmountModifier($0, $1));"; - Util.instrumentDeclared(thisClass, ctCombatHandler, "setDamage", "modifySeverity", replace); - - Util.setReason("Make AOSP not disgustingly broken."); - replace = "if(!this.creature.isPlayer()){" + - " $_ = $proceed($$)*this.creature.addSpellResistance((short) 278);" + - "}else{" + - " $_ = $proceed($$);" + - "}"; - Util.instrumentDeclared(thisClass, ctCombatHandler, "setDamage", "getSpellPainShare", replace); + Util.instrumentDeclared(thisClass, ctCombatHandler, "setDamage", "modifySeverity", replace);*/ /*Util.setReason("Debug attack method"); CtClass ctAction = classPool.get("com.wurmonline.server.behaviours.Action"); diff --git a/src/main/java/mod/sin/wyvern/Mastercraft.java b/src/main/java/mod/sin/wyvern/Mastercraft.java new file mode 100644 index 0000000..77b2486 --- /dev/null +++ b/src/main/java/mod/sin/wyvern/Mastercraft.java @@ -0,0 +1,126 @@ +package mod.sin.wyvern; + +import com.wurmonline.server.Server; +import com.wurmonline.server.items.Item; +import com.wurmonline.server.players.Titles; +import com.wurmonline.server.skills.Skill; +import javassist.*; +import javassist.expr.ExprEditor; +import javassist.expr.MethodCall; +import mod.sin.lib.Util; +import org.gotti.wurmunlimited.modloader.ReflectionUtil; +import org.gotti.wurmunlimited.modloader.classhooks.HookManager; + +import java.util.Objects; +import java.util.logging.Logger; + +public class Mastercraft { + private static Logger logger = Logger.getLogger(Mastercraft.class.getName()); + public static double getNewDifficulty(Skill skill, double diff, Item item){ + if(WyvernMods.affinityDifficultyBonus && skill.affinity > 0){ + diff -= skill.affinity; + } + if(WyvernMods.legendDifficultyBonus && skill.getKnowledge() > 99.0d){ + diff -= 2d-((100d-skill.getKnowledge())*2d); + } + if(WyvernMods.masterDifficultyBonus && skill.getKnowledge() > 90.0d){ + diff -= 2d-((100d-skill.getKnowledge())*0.2d); + } + if(item != null){ + if(WyvernMods.itemRarityDifficultyBonus && item.getRarity() > 0){ + diff -= item.getRarity(); + } + if(WyvernMods.legendItemDifficultyBonus && item.getCurrentQualityLevel() > 99.0f){ + diff -= 1d-((100d-item.getCurrentQualityLevel())*1d); + } + if(WyvernMods.masterItemDifficultyBonus && item.getCurrentQualityLevel() > 90.0f){ + diff -= 1d-((100d-item.getCurrentQualityLevel())*0.1d); + } + } + return diff; + } + public static float getCastPowerIncrease(Skill skill){ + double addedPower = 0; + if(skill.affinity > 0){ + addedPower += 2*skill.affinity; + } + if(skill.getKnowledge() > 0){ + float lowFloat1 = Math.min(Server.rand.nextFloat(), Server.rand.nextFloat()); + float lowFloat2 = Math.min(Server.rand.nextFloat(), Server.rand.nextFloat()); + addedPower += Math.min(skill.getKnowledge()*lowFloat1, skill.getKnowledge()*lowFloat2); + }else{ + logger.warning("Error: Some player just tried casting with no channeling skill!"); + } + return (float) addedPower; + } + public static float getFavorCostMultiplier(Skill skill){ + float mult = 1f; + if(skill.affinity > 0){ + mult -= skill.affinity*0.02f; //2% reduction per affinity + } + if(skill.getKnowledge() > 90d){ + mult -= 0.1d-((100d-skill.getKnowledge())*0.01d); + } + if(skill.getKnowledge() > 99d){ + mult -= 0.1d-((100-skill.getKnowledge())*0.1d); + } + return mult; + } + + public static void changeExistingTitles(){ + for (Titles.Title title : Titles.Title.values()) { + if (Objects.equals("Pumpkin King", title.getFemaleName())){ + try { + ReflectionUtil.setPrivateField(title, ReflectionUtil.getField(title.getClass(), "femaleName"), "Pumpkin Queen"); + } catch (IllegalArgumentException | IllegalAccessException | ClassCastException | NoSuchFieldException e) { + e.printStackTrace(); + } + } + } + } + public static void preInit(){ + try { + ClassPool classPool = HookManager.getInstance().getClassPool(); + Class thisClass = Mastercraft.class; + + // - Reduce skill check difficulty with high skills or tools - // + CtClass ctSkill = classPool.get("com.wurmonline.server.skills.Skill"); + + if (WyvernMods.enableDifficultyAdjustments) { + Util.setReason("Modify difficulty for skill checks in MasterCraft."); + String replace = "$1 = " + Mastercraft.class.getName() + ".getNewDifficulty(this, $1, $2);"; + Util.insertBeforeDeclared(thisClass, ctSkill, "checkAdvance", replace); + } + + // - Increase spellcasting power for skilled channelers - // + CtClass ctSpell = classPool.get("com.wurmonline.server.spells.Spell"); + CtMethod[] ctRuns = ctSpell.getDeclaredMethods("run"); + for(CtMethod method : ctRuns){ + if (WyvernMods.empoweredChannelers) { + method.instrument(new ExprEditor() { + public void edit(MethodCall m) throws CannotCompileException { + if (m.getMethodName().equals("doEffect")) { + m.replace("$2 += " + Mastercraft.class.getName() + ".getCastPowerIncrease(castSkill);" + + "$_ = $proceed($$);"); + logger.info("Instrumented doEffect in run()"); + } + } + }); + } + if (WyvernMods.channelSkillFavorReduction) { + method.instrument(new ExprEditor() { + public void edit(MethodCall m) throws CannotCompileException { + if (m.getMethodName().equals("depleteFavor")) { + m.replace("$1 *= " + Mastercraft.class.getName() + ".getFavorCostMultiplier(castSkill);" + + "$_ = $proceed($$);"); + logger.info("Instrumented depleteFavor in run()"); + } + } + }); + } + } + } catch (CannotCompileException | NotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/mod/sin/wyvern/MeditationPerks.java b/src/main/java/mod/sin/wyvern/MeditationPerks.java index 9c3c3f8..b12d8ca 100644 --- a/src/main/java/mod/sin/wyvern/MeditationPerks.java +++ b/src/main/java/mod/sin/wyvern/MeditationPerks.java @@ -60,7 +60,7 @@ public class MeditationPerks { if(path.getPath() == Cults.PATH_HATE){ byte level = path.getLevel(); if(level >= 3){ - float levelDiff = level-3f; + float levelDiff = level-2f; return 1.0f+(levelDiff*0.01f); } } @@ -156,36 +156,54 @@ public class MeditationPerks { final Class thisClass = MeditationPerks.class; String replace; - Util.setReason("Enable buff icons for meditation."); CtClass ctCultist = classPool.get("com.wurmonline.server.players.Cultist"); - replace = MeditationPerks.class.getName()+".sendPassiveBuffs($0);"; - Util.insertBeforeDeclared(thisClass, ctCultist, "sendPassiveBuffs", replace); - - Util.setReason("Make meditating paths more straightforward."); CtClass ctCults = classPool.get("com.wurmonline.server.players.Cults"); - replace = "{ return "+MeditationPerks.class.getName()+".getNewPathFor($1, $2, $3); }"; - Util.setBodyDeclared(thisClass, ctCults, "getPathFor", replace); - - Util.setReason("Replace stamina modifier for new Insanity ability."); CtClass ctActions = classPool.get("com.wurmonline.server.behaviours.Actions"); - replace = "{ return "+MeditationPerks.class.getName()+".newStaminaModifierFor($1, $2); }"; - Util.setBodyDeclared(thisClass, ctActions, "getStaminaModiferFor", replace); - - Util.setReason("Increase movement speed for Path of Hate users."); CtClass ctMovementScheme = classPool.get("com.wurmonline.server.creatures.MovementScheme"); - replace = "if($_ > 0){" + - " $_ = $_ * "+MeditationPerks.class.getName()+".getCultistSpeedMultiplier(this);" + - "}"; - Util.insertAfterDeclared(thisClass, ctMovementScheme, "getSpeedModifier", replace); - - Util.setReason("Scale path of power stamina bonus from level 7 onwards."); CtClass ctCreatureStatus = classPool.get("com.wurmonline.server.creatures.CreatureStatus"); - replace = "staminaMod += "+MeditationPerks.class.getName()+".getPowerStaminaBonus(this.statusHolder);" + - "$_ = false;"; - Util.instrumentDeclared(thisClass, ctCreatureStatus, "modifyStamina", "usesNoStamina", replace); - - Util.setReason("Scale path of knowledge skill gain from level 7 onwards."); CtClass ctSkill = classPool.get("com.wurmonline.server.skills.Skill"); + + if (WyvernMods.simplifyMeditationTerrain) { + Util.setReason("Make meditating paths more straightforward."); + replace = "{ return " + MeditationPerks.class.getName() + ".getNewPathFor($1, $2, $3); }"; + Util.setBodyDeclared(thisClass, ctCults, "getPathFor", replace); + } + + if (WyvernMods.removeInsanitySotG) { + Util.setReason("Remove shield of the gone effect."); + replace = "{ return 0.0f; }"; + Util.setBodyDeclared(thisClass, ctCultist, "getHalfDamagePercentage", replace); + } + + if (WyvernMods.removeHateWarBonus) { + Util.setReason("Remove hate war damage effect."); + replace = "{ return false; }"; + Util.setBodyDeclared(thisClass, ctCultist, "mayStartDoubleWarDamage", replace); + Util.setReason("Remove hate war damage effect."); + Util.setBodyDeclared(thisClass, ctCultist, "doubleWarDamage", replace); + } + + if (WyvernMods.insanitySpeedBonus) { + Util.setReason("Replace stamina modifier for new Insanity ability."); + replace = "{ return " + MeditationPerks.class.getName() + ".newStaminaModifierFor($1, $2); }"; + Util.setBodyDeclared(thisClass, ctActions, "getStaminaModiferFor", replace); + } + + if (WyvernMods.hateMovementBonus) { + Util.setReason("Increase movement speed for Path of Hate users."); + replace = "if($_ > 0){" + + " $_ = $_ * " + MeditationPerks.class.getName() + ".getCultistSpeedMultiplier(this);" + + "}"; + Util.insertAfterDeclared(thisClass, ctMovementScheme, "getSpeedModifier", replace); + } + + if (WyvernMods.scalingPowerStaminaBonus) { + Util.setReason("Scale path of power stamina bonus from level 7 onwards."); + replace = "staminaMod += " + MeditationPerks.class.getName() + ".getPowerStaminaBonus(this.statusHolder);" + + "$_ = false;"; + Util.instrumentDeclared(thisClass, ctCreatureStatus, "modifyStamina", "usesNoStamina", replace); + } + CtClass[] params1 = { CtClass.doubleType, CtClass.booleanType, @@ -194,57 +212,61 @@ public class MeditationPerks { CtClass.doubleType }; String desc1 = Descriptor.ofMethod(CtClass.voidType, params1); - replace = "staminaMod *= "+MeditationPerks.class.getName()+".getKnowledgeSkillGain(player);" + - "$_ = false;"; - Util.instrumentDescribed(thisClass, ctSkill, "alterSkill", desc1, "levelElevenSkillgain", replace); - Util.setReason("Remove shield of the gone effect."); - replace = "{ return 0.0f; }"; - Util.setBodyDeclared(thisClass, ctCultist, "getHalfDamagePercentage", replace); - Util.setReason("Remove hate war damage effect."); - replace = "{ return false; }"; - Util.setBodyDeclared(thisClass, ctCultist, "mayStartDoubleWarDamage", replace); - Util.setBodyDeclared(thisClass, ctCultist, "doubleWarDamage", replace); + if (WyvernMods.scalingKnowledgeSkillGain) { + Util.setReason("Scale path of knowledge skill gain from level 7 onwards."); + replace = "staminaMod *= " + MeditationPerks.class.getName() + ".getKnowledgeSkillGain(player);" + + "$_ = false;"; + Util.instrumentDescribed(thisClass, ctSkill, "alterSkill", desc1, "levelElevenSkillgain", replace); + } - Util.setReason("Remove artifical tick timer for meditation."); - replace = "$_ = 0;"; - Util.instrumentDeclared(thisClass, ctCults, "meditate", "getLastMeditated", replace); + if (WyvernMods.removeMeditationTickTimer) { + Util.setReason("Remove artifical tick timer for meditation."); + replace = "$_ = 0;"; + Util.instrumentDeclared(thisClass, ctCults, "meditate", "getLastMeditated", replace); + } - // - Reduce meditation cooldowns - // - replace = "return this.path == 1 && this.level > 3 && System.currentTimeMillis() - this.cooldown1 > "+(TimeConstants.HOUR_MILLIS*8)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayRefresh", replace); - //ctCultist.getDeclaredMethod("mayRefresh").setBody("return this.path == 1 && this.level > 3 && System.currentTimeMillis() - this.cooldown1 > 28800000;"); - replace = "return this.path == 1 && this.level > 6 && System.currentTimeMillis() - this.cooldown2 > "+(TimeConstants.HOUR_MILLIS*8)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayEnchantNature", replace); - //ctCultist.getDeclaredMethod("mayEnchantNature").setBody("return this.path == 1 && this.level > 6 && System.currentTimeMillis() - this.cooldown2 > 28800000;"); - replace = "return this.path == 1 && this.level > 8 && System.currentTimeMillis() - this.cooldown3 > "+(TimeConstants.HOUR_MILLIS*4)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayStartLoveEffect", replace); - //ctCultist.getDeclaredMethod("mayStartLoveEffect").setBody("return this.path == 1 && this.level > 8 && System.currentTimeMillis() - this.cooldown3 > 14400000;"); - replace = "return this.path == 2 && this.level > 6 && System.currentTimeMillis() - this.cooldown1 > "+(TimeConstants.HOUR_MILLIS*6)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayStartDoubleWarDamage", replace); - //ctCultist.getDeclaredMethod("mayStartDoubleWarDamage").setBody("return this.path == 2 && this.level > 6 && System.currentTimeMillis() - this.cooldown1 > 21600000;"); - replace = "return this.path == 2 && this.level > 3 && System.currentTimeMillis() - this.cooldown2 > "+(TimeConstants.HOUR_MILLIS*4)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayStartDoubleStructDamage", replace); - //ctCultist.getDeclaredMethod("mayStartDoubleStructDamage").setBody("return this.path == 2 && this.level > 3 && System.currentTimeMillis() - this.cooldown2 > 14400000;"); - replace = "return this.path == 2 && this.level > 8 && System.currentTimeMillis() - this.cooldown3 > "+(TimeConstants.HOUR_MILLIS*6)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayStartFearEffect", replace); - //ctCultist.getDeclaredMethod("mayStartFearEffect").setBody("return this.path == 2 && this.level > 8 && System.currentTimeMillis() - this.cooldown3 > 21600000;"); - replace = "return this.path == 5 && this.level > 8 && System.currentTimeMillis() - this.cooldown1 > "+(TimeConstants.HOUR_MILLIS*6)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayStartNoElementalDamage", replace); - //ctCultist.getDeclaredMethod("mayStartNoElementalDamage").setBody("return this.path == 5 && this.level > 8 && System.currentTimeMillis() - this.cooldown1 > 21600000;"); - replace = "return this.path == 5 && this.level > 6 && System.currentTimeMillis() - this.cooldown2 > "+(TimeConstants.HOUR_MILLIS*8)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "maySpawnVolcano", replace); - //ctCultist.getDeclaredMethod("maySpawnVolcano").setBody("return this.path == 5 && this.level > 6 && System.currentTimeMillis() - this.cooldown2 > 28800000;"); - replace = "return this.path == 5 && this.level > 3 && System.currentTimeMillis() - this.cooldown3 > "+(TimeConstants.HOUR_MILLIS*4)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayStartIgnoreTraps", replace); - //ctCultist.getDeclaredMethod("mayStartIgnoreTraps").setBody("return this.path == 5 && this.level > 3 && System.currentTimeMillis() - this.cooldown3 > 14400000;"); - replace = "return this.path == 3 && this.level > 3 && System.currentTimeMillis() - this.cooldown1 > "+(TimeConstants.HOUR_MILLIS*4)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayCreatureInfo", replace); - //ctCultist.getDeclaredMethod("mayCreatureInfo").setBody("return this.path == 3 && this.level > 3 && System.currentTimeMillis() - this.cooldown1 > 14400000;"); - replace = "return this.path == 3 && this.level > 6 && System.currentTimeMillis() - this.cooldown2 > "+(TimeConstants.HOUR_MILLIS*4)+";"; - Util.setBodyDeclared(thisClass, ctCultist, "mayInfoLocal", replace); - //ctCultist.getDeclaredMethod("mayInfoLocal").setBody("return this.path == 3 && this.level > 6 && System.currentTimeMillis() - this.cooldown2 > 14400000;"); + if (WyvernMods.newMeditationBuffs) { + Util.setReason("Enable buff icons for meditation."); + replace = MeditationPerks.class.getName() + ".sendPassiveBuffs($0);"; + Util.insertBeforeDeclared(thisClass, ctCultist, "sendPassiveBuffs", replace); + } + if (WyvernMods.enableMeditationAbilityCooldowns) { + // - Adjust meditation ability cooldowns - // + replace = "return this.path == 1 && this.level > 3 && System.currentTimeMillis() - this.cooldown1 > " + String.valueOf(WyvernMods.loveRefreshCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayRefresh", replace); + + replace = "return this.path == 1 && this.level > 6 && System.currentTimeMillis() - this.cooldown2 > " + String.valueOf(WyvernMods.loveEnchantNatureCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayEnchantNature", replace); + + replace = "return this.path == 1 && this.level > 8 && System.currentTimeMillis() - this.cooldown3 > " + String.valueOf(WyvernMods.loveLoveEffectCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayStartLoveEffect", replace); + + replace = "return this.path == 2 && this.level > 6 && System.currentTimeMillis() - this.cooldown1 > " + String.valueOf(WyvernMods.hateWarDamageCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayStartDoubleWarDamage", replace); + + replace = "return this.path == 2 && this.level > 3 && System.currentTimeMillis() - this.cooldown2 > " + String.valueOf(WyvernMods.hateStructureDamageCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayStartDoubleStructDamage", replace); + + replace = "return this.path == 2 && this.level > 8 && System.currentTimeMillis() - this.cooldown3 > " + String.valueOf(WyvernMods.hateFearCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayStartFearEffect", replace); + + replace = "return this.path == 5 && this.level > 8 && System.currentTimeMillis() - this.cooldown1 > " + String.valueOf(WyvernMods.powerElementalImmunityCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayStartNoElementalDamage", replace); + + replace = "return this.path == 5 && this.level > 6 && System.currentTimeMillis() - this.cooldown2 > " + String.valueOf(WyvernMods.powerEruptFreezeCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "maySpawnVolcano", replace); + + replace = "return this.path == 5 && this.level > 3 && System.currentTimeMillis() - this.cooldown3 > " + String.valueOf(WyvernMods.powerIgnoreTrapsCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayStartIgnoreTraps", replace); + + replace = "return this.path == 3 && this.level > 3 && System.currentTimeMillis() - this.cooldown1 > " + String.valueOf(WyvernMods.knowledgeInfoCreatureCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayCreatureInfo", replace); + + replace = "return this.path == 3 && this.level > 6 && System.currentTimeMillis() - this.cooldown2 > " + String.valueOf(WyvernMods.knowledgeInfoTileCooldown) + ";"; + Util.setBodyDeclared(thisClass, ctCultist, "mayInfoLocal", replace); + } } catch ( NotFoundException | IllegalArgumentException | ClassCastException e) { throw new HookException(e); } diff --git a/src/main/java/mod/sin/wyvern/PlayerTitles.java b/src/main/java/mod/sin/wyvern/PlayerTitles.java index 7bff2bb..da7e742 100644 --- a/src/main/java/mod/sin/wyvern/PlayerTitles.java +++ b/src/main/java/mod/sin/wyvern/PlayerTitles.java @@ -23,23 +23,6 @@ public class PlayerTitles { // Event Title ID's public static int TITAN_SLAYER = 10000; public static int SPECTRAL = 10001; - //public static int PASTAMANCER = 10002; - - // Player Donation Title ID's - /*public static int PATRON = 19999; - public static int DONATOR = 20000; - public static int PAZZA_FAVORITE_GM = 20001; - public static int WARRIORGEN_THAT_GUY = 20002; - public static int ETERNALLOVE_WARRIORGENS_WIFE = 20003; - public static int BAMBAM_THORN_ONE = 20004; - public static int SVENJA_CARE_DEPENDANT = 20005; - public static int ALEXIA_THE_TREASURING = 20006; - public static int REEVI_SCIENCE_GUY = 20007; - public static int GENOCIDE_GRAND_DESIGNER = 20008; - public static int SELEAS_CRAZY_CAT_LORD = 20009; - public static int PIRATEMAX_SLAVE = 20010; - public static int ELTACOLAD_TRUE_TACO = 20011; - public static int ATTICUS_THE_GREAT_ILLUMINATY = 20012;*/ public static void init(){ for (WyvernMods.CustomTitle title : WyvernMods.customTitles){ @@ -48,28 +31,8 @@ public class PlayerTitles { // Event Titles createTitle(TITAN_SLAYER, "Titanslayer", "Titanslayer", -1, "NORMAL"); createTitle(SPECTRAL, "Spectral", "Spectral", -1, "NORMAL"); - //PlayerTitles.createTitle(buster, "Holdstrong_Architect", 702, "Holdstrong Architect", "Holdstrong Architect", -1, Titles.TitleType.NORMAL); - //PlayerTitles.createTitle(buster, "Stronghold_Architect", 703, "Stronghold Architect", "Stronghold Architect", -1, Titles.TitleType.NORMAL); - //createTitle(PASTAMANCER, "Pastamancer", "Pastamancer", -1, "NORMAL"); - // Donation Titles - /* Moved to configuration - createTitle(PATRON, "Patron", "Patron", -1, "NORMAL"); - createTitle(DONATOR, "Donator", "Donator", -1, "NORMAL"); - createTitle(PAZZA_FAVORITE_GM, "Sindusks Favourite GM", "Sindusks Favourite GM", -1, "NORMAL"); - createTitle(WARRIORGEN_THAT_GUY, "That Guy", "That Guy", -1, "NORMAL"); - createTitle(ETERNALLOVE_WARRIORGENS_WIFE, "Warriorgens Wife", "Warriorgens Wife", -1, "NORMAL"); - createTitle(BAMBAM_THORN_ONE, "Thorn One", "Thorn One", -1, "NORMAL"); - createTitle(SVENJA_CARE_DEPENDANT, "The care-dependent", "The care-dependent", -1, "NORMAL"); - createTitle(ALEXIA_THE_TREASURING, "The Treasuring", "The Treasuring", -1, "NORMAL"); - createTitle(REEVI_SCIENCE_GUY, "Science Guy", "Science Guy", -1, "NORMAL"); - createTitle(GENOCIDE_GRAND_DESIGNER, "Grand Designer", "Grand Designer", -1, "NORMAL"); - createTitle(SELEAS_CRAZY_CAT_LORD, "The Crazy Cat Lord", "The Crazy Cat Lord", -1, "NORMAL"); - createTitle(PIRATEMAX_SLAVE, "Slave", "Slave", -1, "NORMAL"); - createTitle(ELTACOLAD_TRUE_TACO, "The One True Taco", "The One True Taco", -1, "NORMAL"); - createTitle(ATTICUS_THE_GREAT_ILLUMINATY, "The Great Illuminaty", "The Great Illuminaty", -1, "NORMAL");*/ - - // Supporter titles + // Display all existing titles logger.info(Arrays.toString(Titles.Title.values())); } @@ -105,61 +68,5 @@ public class PlayerTitles { logger.warning("Failed to get title with ID "+titleId); } } - /*if(donatorTitles.contains(name)){ - Titles.Title donator = Titles.Title.getTitle(DONATOR); - p.addTitle(donator); - } - if(patronTitles.contains(name)){ - Titles.Title patron = Titles.Title.getTitle(PATRON); - p.addTitle(patron); - } - if(customTitles.containsKey(name)){ - Titles.Title customTitle = Titles.Title.getTitle(customTitles.get(name)); - p.addTitle(customTitle); - }*/ - } - public static void preInit(){ - // Donations - /*donatorTitles.add("Pazza"); - customTitles.put("Pazza", PAZZA_FAVORITE_GM); // Sindusks Favorite GM - - donatorTitles.add("Warriorgen"); - customTitles.put("Warriorgen", WARRIORGEN_THAT_GUY); // That Guy - - donatorTitles.add("Eternallove"); - customTitles.put("Eternallove", ETERNALLOVE_WARRIORGENS_WIFE); // Warriorgens Wife - - donatorTitles.add("Bambam"); - customTitles.put("Bambam", BAMBAM_THORN_ONE); // Thorn One - - donatorTitles.add("Svenja"); - customTitles.put("Svenja", SVENJA_CARE_DEPENDANT); // The care-dependent - playerTitles.put("Svenja", "Akuma"); - - donatorTitles.add("Alexiaselena"); - customTitles.put("Alexiaselena", ALEXIA_THE_TREASURING); // The Treasuring - playerTitles.put("Alexiaselena", "Kami"); - - donatorTitles.add("Reevi"); - customTitles.put("Reevi", REEVI_SCIENCE_GUY); // Science Guy - - customTitles.put("Genocide", GENOCIDE_GRAND_DESIGNER); // Grand Designer - - donatorTitles.add("Seleas"); - customTitles.put("Seleas", SELEAS_CRAZY_CAT_LORD); // The Crazy Cat Lord - playerTitles.put("Seleas", "No, Really"); - - donatorTitles.add("Piratemax"); - customTitles.put("Piratemax", PIRATEMAX_SLAVE); // Slave - playerTitles.put("Piratemax", "Boy Next Door"); - - donatorTitles.add("Eltacolad"); - customTitles.put("Eltacolad", ELTACOLAD_TRUE_TACO); // The One True Taco - - patronTitles.add("Atticus"); - customTitles.put("Atticus", ATTICUS_THE_GREAT_ILLUMINATY); // The Great Illuminaty - - // Other rewards - customTitles.put("Critias", PASTAMANCER);*/ } } diff --git a/src/main/java/mod/sin/wyvern/SkillChanges.java b/src/main/java/mod/sin/wyvern/SkillChanges.java index ea286b1..b2bd2f9 100644 --- a/src/main/java/mod/sin/wyvern/SkillChanges.java +++ b/src/main/java/mod/sin/wyvern/SkillChanges.java @@ -37,10 +37,10 @@ public class SkillChanges { try { Skills parent = ReflectionUtil.getPrivateField(skill, ReflectionUtil.getField(skill.getClass(), "parent")); double advanceMultiplicator = (100.0 - skill.getKnowledge()) / (skill.getDifficulty(parent.priest) * skill.getKnowledge() * skill.getKnowledge()) * learnMod * bonus; - double negativeDecayRate = 5; - double positiveDecayRate = 3; - double valueAtZero = 3.74d; - double valueAtOneHundred = 0.9d; + double negativeDecayRate = (double) WyvernMods.hybridNegativeDecayRate; + double positiveDecayRate = (double) WyvernMods.hybridPositiveDecayRate; + double valueAtZero = (double) WyvernMods.hybridValueAtZero; + double valueAtOneHundred = (double) WyvernMods.hybridValueAtOneHundred; //advanceMultiplicator *= Math.pow(2, ((2-Math.pow((100/(100+power)), p))*(100-power)/100)); //double mult = Math.pow(2, (2-Math.pow(100/(100+power), negativeDecayRate))*Math.pow((100-power)*0.01, positiveDecayRate)); double mult = valueAtOneHundred*Math.pow(valueAtZero/valueAtOneHundred, (2-Math.pow(100/(100+Math.max(-99,power)), negativeDecayRate))*Math.pow((100-power)*0.01, positiveDecayRate)); @@ -65,65 +65,52 @@ public class SkillChanges { return 0; } - public static void onServerStarted(){ - /*SkillTemplate exorcism = SkillSystem.templates.get(SkillList.EXORCISM); - int[] deps = {SkillList.GROUP_ALCHEMY}; + public static void setSkillName(int id, String newName){ + SkillTemplate skillTemplate = SkillSystem.templates.get(id); try { - String newName = "Crystal handling"; - ReflectionUtil.setPrivateField(exorcism, ReflectionUtil.getField(exorcism.getClass(), "name"), newName); - SkillSystem.skillNames.put(exorcism.getNumber(), newName); - SkillSystem.namesToSkill.put(newName, exorcism.getNumber()); - ReflectionUtil.setPrivateField(exorcism, ReflectionUtil.getField(exorcism.getClass(), "dependencies"), deps); + ReflectionUtil.setPrivateField(skillTemplate, ReflectionUtil.getField(skillTemplate.getClass(), "name"), newName); + SkillSystem.skillNames.put(skillTemplate.getNumber(), newName); + SkillSystem.namesToSkill.put(newName, skillTemplate.getNumber()); } catch (IllegalAccessException | NoSuchFieldException e) { - logger.info("Failed to rename exorcism!"); + logger.info("Failed to rename skill with ID "+id+"!"); e.printStackTrace(); } - SkillTemplate ballistae = SkillSystem.templates.get(SkillList.BALLISTA); - int[] deps2 = {SkillList.GROUP_ALCHEMY}; + } + public static void setSkillDifficulty(int id, float difficulty){ + SkillTemplate skillTemplate = SkillSystem.templates.get(id); + skillTemplate.setDifficulty(difficulty); + } + public static void setSkillTickTime(int id, long tickTime){ + SkillTemplate skillTemplate = SkillSystem.templates.get(id); try { - String newName = "Mystic components"; - ReflectionUtil.setPrivateField(ballistae, ReflectionUtil.getField(ballistae.getClass(), "name"), newName); - SkillSystem.skillNames.put(ballistae.getNumber(), newName); - SkillSystem.namesToSkill.put(newName, ballistae.getNumber()); - ReflectionUtil.setPrivateField(ballistae, ReflectionUtil.getField(ballistae.getClass(), "dependencies"), deps2); + ReflectionUtil.setPrivateField(skillTemplate, ReflectionUtil.getField(skillTemplate.getClass(), "tickTime"), tickTime); } catch (IllegalAccessException | NoSuchFieldException e) { - logger.info("Failed to rename ballistae!"); - e.printStackTrace(); - }*/ - SkillTemplate preaching = SkillSystem.templates.get(SkillList.PREACHING); - int[] deps3 = {SkillList.MASONRY}; - try { - String newName = "Gem augmentation"; - ReflectionUtil.setPrivateField(preaching, ReflectionUtil.getField(preaching.getClass(), "name"), newName); - SkillSystem.skillNames.put(preaching.getNumber(), newName); - SkillSystem.namesToSkill.put(newName, preaching.getNumber()); - ReflectionUtil.setPrivateField(preaching, ReflectionUtil.getField(preaching.getClass(), "dependencies"), deps3); - } catch (IllegalAccessException | NoSuchFieldException e) { - logger.info("Failed to rename preaching!"); + logger.info("Failed to set tickTime for skill with ID "+id+"!"); e.printStackTrace(); } - SkillTemplate stealing = SkillSystem.templates.get(SkillList.STEALING); - try { - ReflectionUtil.setPrivateField(stealing, ReflectionUtil.getField(stealing.getClass(), "tickTime"), 0); - } catch (IllegalAccessException | NoSuchFieldException e) { - logger.info("Failed to set tickTime for stealing!"); - e.printStackTrace(); - } - SkillTemplate meditating = SkillSystem.templates.get(SkillList.MEDITATING); - try { - ReflectionUtil.setPrivateField(meditating, ReflectionUtil.getField(meditating.getClass(), "tickTime"), TimeConstants.HOUR_MILLIS); - } catch (IllegalAccessException | NoSuchFieldException e) { - logger.info("Failed to set tickTime for meditating!"); - e.printStackTrace(); - } - meditating.setDifficulty(300f); + } - // Set mining difficulty down to be equivalent to digging. - SkillTemplate mining = SkillSystem.templates.get(SkillList.MINING); - mining.setDifficulty(3000f); - // Triple lockpicking skill - SkillTemplate lockpicking = SkillSystem.templates.get(SkillList.LOCKPICKING); - lockpicking.setDifficulty(700f); + public static void onServerStarted(){ + for (int skillId : WyvernMods.skillName.keySet()){ + setSkillName(skillId, WyvernMods.skillName.get(skillId)); + } + for (int skillId : WyvernMods.skillDifficulty.keySet()){ + setSkillDifficulty(skillId, WyvernMods.skillDifficulty.get(skillId)); + } + for (int skillId : WyvernMods.skillTickTime.keySet()){ + setSkillTickTime(skillId, WyvernMods.skillTickTime.get(skillId)); + } + + if (WyvernMods.changePreachingLocation) { + SkillTemplate preaching = SkillSystem.templates.get(SkillList.PREACHING); + int[] deps3 = {SkillList.MASONRY}; + try { + ReflectionUtil.setPrivateField(preaching, ReflectionUtil.getField(preaching.getClass(), "dependencies"), deps3); + } catch (IllegalAccessException | NoSuchFieldException e) { + logger.info("Failed to rename preaching!"); + e.printStackTrace(); + } + } } public static void preInit(){ @@ -132,13 +119,15 @@ public class SkillChanges { final Class thisClass = SkillChanges.class; String replace; - Util.setReason("Allow skill check failures to add skill gain."); - CtClass ctSkill = classPool.get("com.wurmonline.server.skills.Skill"); - replace = "{" + - " double advanceMultiplicator = "+SkillChanges.class.getName()+".newDoSkillGainNew($0, $1, $2, $3, $4, $5);" + - " $0.alterSkill(advanceMultiplicator, false, $4, true, $5);" + - "}"; - Util.setBodyDeclared(thisClass, ctSkill, "doSkillGainNew", replace); + if (WyvernMods.enableHybridSkillGain) { + Util.setReason("Add hybrid skill gain system hook."); + CtClass ctSkill = classPool.get("com.wurmonline.server.skills.Skill"); + replace = "{" + + " double advanceMultiplicator = " + SkillChanges.class.getName() + ".newDoSkillGainNew($0, $1, $2, $3, $4, $5);" + + " $0.alterSkill(advanceMultiplicator, false, $4, true, $5);" + + "}"; + Util.setBodyDeclared(thisClass, ctSkill, "doSkillGainNew", replace); + } } catch ( NotFoundException | IllegalArgumentException | ClassCastException e) { throw new HookException(e); diff --git a/src/main/java/mod/sin/wyvern/WyvernMods.java b/src/main/java/mod/sin/wyvern/WyvernMods.java index 8910dad..375756c 100644 --- a/src/main/java/mod/sin/wyvern/WyvernMods.java +++ b/src/main/java/mod/sin/wyvern/WyvernMods.java @@ -16,9 +16,9 @@ import mod.sin.actions.items.SorcerySplitAction; import mod.sin.creatures.*; import mod.sin.creatures.titans.*; import mod.sin.lib.Prop; +import mod.sin.lib.SkillAssist; import mod.sin.lib.Util; import mod.sin.wyvern.bestiary.MethodsBestiary; -import mod.sin.wyvern.mastercraft.Mastercraft; import org.gotti.wurmunlimited.modloader.ReflectionUtil; import org.gotti.wurmunlimited.modloader.classhooks.HookException; import org.gotti.wurmunlimited.modloader.classhooks.HookManager; @@ -161,6 +161,65 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea public static boolean mineGemsToVehicle = true; public static boolean regenerateStaminaOnVehicleAnySlope = true; + // Combat Module Configuration + public static boolean enableCombatModule = true; + public static boolean enableCombatRatingAdjustments = true; + public static boolean royalExecutionerBonus = true; + public static boolean petSoulDepthScaling = true; + public static boolean vehicleCombatRatingPenalty = true; + public static boolean fixMagranonDamageStacking = true; + public static boolean adjustCombatRatingSpellPower = true; + public static boolean disableLegendaryRegeneration = true; + public static boolean useStaticLegendaryRegeneration = true; + + // Mastercraft Module Configuration + public static boolean enableMastercraftModule = true; + public static boolean enableDifficultyAdjustments = true; + public static boolean affinityDifficultyBonus = true; + public static boolean legendDifficultyBonus = true; + public static boolean masterDifficultyBonus = true; + public static boolean itemRarityDifficultyBonus = true; + public static boolean legendItemDifficultyBonus = true; + public static boolean masterItemDifficultyBonus = true; + public static boolean empoweredChannelers = true; + public static boolean channelSkillFavorReduction = true; + + // Skill Module Configuration + public static boolean enableSkillModule = true; + public static boolean enableHybridSkillGain = true; + public static float hybridNegativeDecayRate = 5f; + public static float hybridPositiveDecayRate = 3f; + public static float hybridValueAtZero = 3.74f; + public static float hybridValueAtOneHundred = 0.9f; + public static HashMap skillName = new HashMap<>(); + public static HashMap skillDifficulty = new HashMap<>(); + public static HashMap skillTickTime = new HashMap<>(); + public static boolean changePreachingLocation = true; + + // Meditation Module Configuration + public static boolean enableMeditationModule = true; + public static boolean simplifyMeditationTerrain = true; + public static boolean removeInsanitySotG = true; + public static boolean removeHateWarBonus = true; + public static boolean insanitySpeedBonus = true; + public static boolean hateMovementBonus = true; + public static boolean scalingPowerStaminaBonus = true; + public static boolean scalingKnowledgeSkillGain = true; + public static boolean removeMeditationTickTimer = true; + public static boolean newMeditationBuffs = true; + public static boolean enableMeditationAbilityCooldowns = true; + public static long loveRefreshCooldown = 64800000L; // 18 hours default + public static long loveEnchantNatureCooldown = 64800000L; // 18 hours default + public static long loveLoveEffectCooldown = 64800000L; // 18 hours default + public static long hateWarDamageCooldown = 64800000L; // 18 hours default + public static long hateStructureDamageCooldown = 64800000L; // 18 hours default + public static long hateFearCooldown = 64800000L; // 18 hours default + public static long powerElementalImmunityCooldown = 64800000L; // 18 hours default + public static long powerEruptFreezeCooldown = 64800000L; // 18 hours default + public static long powerIgnoreTrapsCooldown = 64800000L; // 18 hours default + public static long knowledgeInfoCreatureCooldown = 64800000L; // 18 hours default + public static long knowledgeInfoTileCooldown = 64800000L; // 18 hours default + // Treasure Chest Loot Module Configuration public static boolean enableTreasureChestLootModule = true; @@ -345,6 +404,62 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea mineGemsToVehicle = Prop.getBooleanProperty("mineGemsToVehicle", mineGemsToVehicle); regenerateStaminaOnVehicleAnySlope = Prop.getBooleanProperty("regenerateStaminaOnVehicleAnySlope", regenerateStaminaOnVehicleAnySlope); + // Combat Module + enableCombatModule = Prop.getBooleanProperty("enableCombatModule", enableCombatModule); + enableCombatRatingAdjustments = Prop.getBooleanProperty("enableCombatRatingAdjustments", enableCombatRatingAdjustments); + royalExecutionerBonus = Prop.getBooleanProperty("royalExecutionerBonus", royalExecutionerBonus); + petSoulDepthScaling = Prop.getBooleanProperty("petSoulDepthScaling", petSoulDepthScaling); + vehicleCombatRatingPenalty = Prop.getBooleanProperty("vehicleCombatRatingPenalty", vehicleCombatRatingPenalty); + fixMagranonDamageStacking = Prop.getBooleanProperty("fixMagranonDamageStacking", fixMagranonDamageStacking); + adjustCombatRatingSpellPower = Prop.getBooleanProperty("adjustCombatRatingSpellPower", adjustCombatRatingSpellPower); + disableLegendaryRegeneration = Prop.getBooleanProperty("disableLegendaryRegeneration", disableLegendaryRegeneration); + useStaticLegendaryRegeneration = Prop.getBooleanProperty("useStaticLegendaryRegeneration", useStaticLegendaryRegeneration); + + // Mastercraft Module + enableMastercraftModule = Prop.getBooleanProperty("enableMastercraftModule", enableMastercraftModule); + enableDifficultyAdjustments = Prop.getBooleanProperty("enableDifficultyAdjustments", enableDifficultyAdjustments); + affinityDifficultyBonus = Prop.getBooleanProperty("affinityDifficultyBonus", affinityDifficultyBonus); + legendDifficultyBonus = Prop.getBooleanProperty("legendDifficultyBonus", legendDifficultyBonus); + masterDifficultyBonus = Prop.getBooleanProperty("masterDifficultyBonus", masterDifficultyBonus); + itemRarityDifficultyBonus = Prop.getBooleanProperty("itemRarityDifficultyBonus", itemRarityDifficultyBonus); + legendItemDifficultyBonus = Prop.getBooleanProperty("legendItemDifficultyBonus", legendItemDifficultyBonus); + masterItemDifficultyBonus = Prop.getBooleanProperty("masterItemDifficultyBonus", masterItemDifficultyBonus); + empoweredChannelers = Prop.getBooleanProperty("empoweredChannelers", empoweredChannelers); + channelSkillFavorReduction = Prop.getBooleanProperty("channelSkillFavorReduction", channelSkillFavorReduction); + + // Skill Module + enableSkillModule = Prop.getBooleanProperty("enableSkillModule", enableSkillModule); + enableHybridSkillGain = Prop.getBooleanProperty("enableHybridSkillGain", enableHybridSkillGain); + hybridNegativeDecayRate = Prop.getFloatProperty("hybridNegativeDecayRate", hybridNegativeDecayRate); + hybridPositiveDecayRate = Prop.getFloatProperty("hybridPositiveDecayRate", hybridPositiveDecayRate); + hybridValueAtZero = Prop.getFloatProperty("hybridValueAtZero", hybridValueAtZero); + hybridValueAtOneHundred = Prop.getFloatProperty("hybridValueAtOneHundred", hybridValueAtOneHundred); + changePreachingLocation = Prop.getBooleanProperty("changePreachingLocation", changePreachingLocation); + + // Meditation Module + enableMeditationModule = Prop.getBooleanProperty("enableMeditationModule", enableMeditationModule); + simplifyMeditationTerrain = Prop.getBooleanProperty("simplifyMeditationTerrain", simplifyMeditationTerrain); + removeInsanitySotG = Prop.getBooleanProperty("removeInsanitySotG", removeInsanitySotG); + removeHateWarBonus = Prop.getBooleanProperty("removeHateWarBonus", removeHateWarBonus); + insanitySpeedBonus = Prop.getBooleanProperty("insanitySpeedBonus", insanitySpeedBonus); + hateMovementBonus = Prop.getBooleanProperty("hateMovementBonus", hateMovementBonus); + scalingPowerStaminaBonus = Prop.getBooleanProperty("scalingPowerStaminaBonus", scalingPowerStaminaBonus); + scalingKnowledgeSkillGain = Prop.getBooleanProperty("scalingKnowledgeSkillGain", scalingKnowledgeSkillGain); + removeMeditationTickTimer = Prop.getBooleanProperty("removeMeditationTickTimer", removeMeditationTickTimer); + newMeditationBuffs = Prop.getBooleanProperty("newMeditationBuffs", newMeditationBuffs); + enableMeditationAbilityCooldowns = Prop.getBooleanProperty("enableMeditationAbilityCooldowns", enableMeditationAbilityCooldowns); + loveRefreshCooldown = Prop.getLongProperty("loveRefreshCooldown", loveRefreshCooldown); + loveEnchantNatureCooldown = Prop.getLongProperty("loveEnchantNatureCooldown", loveEnchantNatureCooldown); + loveLoveEffectCooldown = Prop.getLongProperty("loveLoveEffectCooldown", loveLoveEffectCooldown); + hateWarDamageCooldown = Prop.getLongProperty("hateWarDamageCooldown", hateWarDamageCooldown); + hateStructureDamageCooldown = Prop.getLongProperty("hateStructureDamageCooldown", hateStructureDamageCooldown); + hateFearCooldown = Prop.getLongProperty("hateFearCooldown", hateFearCooldown); + powerElementalImmunityCooldown = Prop.getLongProperty("powerElementalImmunityCooldown", powerElementalImmunityCooldown); + powerEruptFreezeCooldown = Prop.getLongProperty("powerEruptFreezeCooldown", powerEruptFreezeCooldown); + powerIgnoreTrapsCooldown = Prop.getLongProperty("powerIgnoreTrapsCooldown", powerIgnoreTrapsCooldown); + knowledgeInfoCreatureCooldown = Prop.getLongProperty("knowledgeInfoCreatureCooldown", knowledgeInfoCreatureCooldown); + knowledgeInfoTileCooldown = Prop.getLongProperty("knowledgeInfoTileCooldown", knowledgeInfoTileCooldown); + // Treasure Chest Loot Module enableTreasureChestLootModule = Prop.getBooleanProperty("enableTreasureChestLootModule", enableTreasureChestLootModule); @@ -398,6 +513,51 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea } } awardTitles.put(titleId, playerList); + }else if (name.startsWith("skillName")) { + String[] values = value.split(","); + if(values.length < 2 || values.length > 2){ + logger.warning("Error parsing Skill Name: Invalid amount of arguments for following property: "+value); + } + int skillId = SkillAssist.getSkill(values[0]); + if (skillId < 0){ + skillId = Integer.parseInt(values[0]); + } + if (skillName.containsKey(skillId)){ + logger.warning("Duplicate skill name configurations for skill id "+skillId); + }else{ + String newName = values[1]; + skillName.put(skillId, newName); + } + }else if (name.startsWith("skillDifficulty")) { + String[] values = value.split(","); + if(values.length < 2 || values.length > 2){ + logger.warning("Error parsing Skill Difficulty: Invalid amount of arguments for following property: "+value); + } + int skillId = SkillAssist.getSkill(values[0]); + if (skillId < 0){ + skillId = Integer.parseInt(values[0]); + } + if (skillDifficulty.containsKey(skillId)){ + logger.warning("Duplicate difficulty configurations for skill id "+skillId); + }else{ + float difficulty = Float.parseFloat(values[1]); + skillDifficulty.put(skillId, difficulty); + } + }else if (name.startsWith("skillTickTime")) { + String[] values = value.split(","); + if(values.length < 2 || values.length > 2){ + logger.warning("Error parsing Skill Tick Time: Invalid amount of arguments for following property: "+value); + } + int skillId = SkillAssist.getSkill(values[0]); + if (skillId < 0){ + skillId = Integer.parseInt(values[0]); + } + if (skillTickTime.containsKey(skillId)){ + logger.warning("Duplicate tick time configurations for skill id "+skillId); + }else{ + long difficulty = Long.parseLong(values[1]); + skillTickTime.put(skillId, difficulty); + } } } } catch (Exception e) { @@ -545,6 +705,86 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea logger.info("Regenerate Stamina On Vehicle Any Slope: "+regenerateStaminaOnVehicleAnySlope); } + logger.info("Combat Module: "+enableCombatModule); + if (enableCombatModule){ + logger.info("Combat Rating Adjustments: "+enableCombatRatingAdjustments); + if (enableCombatRatingAdjustments){ + logger.info("Royal Executioner Bonus: "+royalExecutionerBonus); + logger.info("Pet Soul Depth Scaling: "+petSoulDepthScaling); + logger.info("Vehicle Combat Rating Penalty: "+vehicleCombatRatingPenalty); + } + logger.info("Fix Magranon Damage Stacking: "+fixMagranonDamageStacking); + logger.info("Adjust Combat Rating Spell Power: "+adjustCombatRatingSpellPower); + logger.info("Disable Legendary Regeneration: "+disableLegendaryRegeneration); + logger.info("Use Static Legendary Regeneration: "+useStaticLegendaryRegeneration); + } + + logger.info("Mastercraft Module: "+enableMastercraftModule); + if (enableMastercraftModule){ + logger.info("Difficulty Adjustments: "+enableDifficultyAdjustments); + if (enableDifficultyAdjustments){ + logger.info("Affinity Difficulty Bonus: "+affinityDifficultyBonus); + logger.info("Legend Difficulty Bonus: "+legendDifficultyBonus); + logger.info("Master Difficulty Bonus: "+masterDifficultyBonus); + logger.info("Item Rarity Difficulty Bonus: "+itemRarityDifficultyBonus); + logger.info("Legend Item Difficulty Bonus: "+legendItemDifficultyBonus); + logger.info("Master Item Difficulty Bonus: "+masterItemDifficultyBonus); + } + logger.info("Empowered Channelers: "+empoweredChannelers); + logger.info("Channel Skill Favor Reduction: "+channelSkillFavorReduction); + } + + logger.info("Skill Module: "+enableSkillModule); + if (enableSkillModule){ + logger.info("Hybrid Skill Gain: "+enableHybridSkillGain); + if (enableHybridSkillGain){ + logger.info("Hybrid Negative Decay Rate: "+hybridNegativeDecayRate); + logger.info("Hybrid Positive Decay Rate: "+hybridPositiveDecayRate); + logger.info("Hybrid Value At Zero: "+hybridValueAtZero); + logger.info("Hybrid Value At One Hundred: "+hybridValueAtOneHundred); + } + for (int skillId : skillName.keySet()){ + logger.info(String.format("Changing name of skill %s to %s.", + SkillAssist.getSkill(skillId), skillName.get(skillId))); + } + for (int skillId : skillDifficulty.keySet()){ + logger.info(String.format("Setting difficulty of skill %s to %.2f.", + SkillAssist.getSkill(skillId), skillDifficulty.get(skillId))); + } + for (int skillId : skillTickTime.keySet()){ + logger.info(String.format("Setting tick time of skill %s to %d.", + SkillAssist.getSkill(skillId), skillTickTime.get(skillId))); + } + logger.info("Change Preaching Location: "+changePreachingLocation); + } + + logger.info("Meditation Module: "+enableMeditationModule); + if (enableMeditationModule){ + logger.info("Simplify Meditation Terrain: "+simplifyMeditationTerrain); + logger.info("Remove Insanity Shield of the Gone: "+removeInsanitySotG); + logger.info("Remove Hate War Bonus: "+removeHateWarBonus); + logger.info("Insanity Speed Bonus: "+insanitySpeedBonus); + logger.info("Hate Movement Bonus: "+hateMovementBonus); + logger.info("Scaling Power Stamina Bonus: "+scalingPowerStaminaBonus); + logger.info("Scaling Knowledge Skill Gain: "+scalingKnowledgeSkillGain); + logger.info("Remove Meditation Tick Timer: "+removeMeditationTickTimer); + logger.info("New Meditation Buffs: "+newMeditationBuffs); + logger.info("Meditation Ability Cooldowns: "+enableMeditationAbilityCooldowns); + if (enableMeditationAbilityCooldowns){ + logger.info("Love Refresh Cooldown: "+loveRefreshCooldown); + logger.info("Love Enchant Nature Cooldown: "+loveEnchantNatureCooldown); + logger.info("Love Love Effect Cooldown: "+loveLoveEffectCooldown); + logger.info("Hate War Bonus Cooldown: "+hateWarDamageCooldown); + logger.info("Hate Structure Damage Cooldown: "+hateStructureDamageCooldown); + logger.info("Hate Fear Cooldown: "+hateFearCooldown); + logger.info("Power Elemental Immunity Cooldown: "+powerElementalImmunityCooldown); + logger.info("Power Erupt/Freeze Cooldown: "+powerEruptFreezeCooldown); + logger.info("Power Ignore Traps Cooldown: "+powerIgnoreTrapsCooldown); + logger.info("Knowledge Info Creature Cooldown: "+knowledgeInfoCreatureCooldown); + logger.info("Knowledge Info Tile Cooldown: "+knowledgeInfoTileCooldown); + } + } + logger.info("Treasure Chest Loot Module: "+enableTreasureChestLootModule); //this.logger.log(Level.INFO, "Property: " + this.somevalue); @@ -582,11 +822,6 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea Arena.preInit(); } - // Custom Titles Module Pre-Init - if (enableCustomTitlesModule) { - PlayerTitles.preInit(); - } - // Anti-Cheat Module Pre-Init if (enableAntiCheatModule) { AntiCheat.preInit(); @@ -597,6 +832,24 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea QualityOfLife.preInit(); } + // Combat Module Pre-Init + if (enableCombatModule) { + CombatChanges.preInit(); + } + + // Mastercraft Module Pre-Init + if (enableMastercraftModule) { + Mastercraft.preInit(); + } + + if (enableSkillModule) { + SkillChanges.preInit(); + } + + if (enableMeditationModule) { + MeditationPerks.preInit(); + } + // Treasure Chest Loot Module Pre-Init if (enableTreasureChestLootModule) { TreasureChests.preInit(); @@ -607,15 +860,13 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea TeleportHandler.preInit(); MethodsBestiary.preInit(); MissionCreator.preInit(); - SkillChanges.preInit(); - MeditationPerks.preInit(); MountedChanges.preInit(); EconomicChanges.preInit(); - Bloodlust.preInit(); - Mastercraft.preInit(); SupplyDepots.preInit(); KeyEvent.preInit(); - CombatChanges.preInit(); + + // Bloodlust might no longer be necessary. Code remains for reference. + //Bloodlust.preInit(); // Gem Augmentation is not complete. //GemAugmentation.preInit(); @@ -737,7 +988,11 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea @Override public void onPlayerLogin(Player p) { DatabaseHelper.onPlayerLogin(p); - PlayerTitles.awardCustomTitles(p); + + // Award Custom Titles on player login + if(enableCustomTitlesModule) { + PlayerTitles.awardCustomTitles(p); + } } @Override @@ -778,7 +1033,9 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea //espCounter = Servers.localServer.PVPSERVER; // Enables on PvP server by default. //espCounter = false; - SkillChanges.onServerStarted(); + if (enableSkillModule) { + SkillChanges.onServerStarted(); + } CreationEntry lockpicks = CreationMatrix.getInstance().getCreationEntry(ItemList.lockpick); try { @@ -847,11 +1104,11 @@ implements WurmServerMod, Configurable, PreInitable, Initable, ItemTemplatesCrea Bloodlust.pollLusts(); lastPolledBloodlust += pollBloodlustTime; } - if(lastPolledUniqueRegeneration + pollUniqueRegenerationTime < System.currentTimeMillis()){ + if(WyvernMods.useStaticLegendaryRegeneration && lastPolledUniqueRegeneration + pollUniqueRegenerationTime < System.currentTimeMillis()){ CombatChanges.pollUniqueRegeneration(); lastPolledUniqueRegeneration += pollUniqueRegenerationTime; } - if(lastPolledUniqueCollection + pollUniqueCollectionTime < System.currentTimeMillis()){ + if(WyvernMods.enableCombatModule && lastPolledUniqueCollection + pollUniqueCollectionTime < System.currentTimeMillis()){ CombatChanges.pollUniqueCollection(); lastPolledUniqueCollection += pollUniqueCollectionTime; } diff --git a/src/main/java/mod/sin/wyvern/mastercraft/BytecodeTools.java b/src/main/java/mod/sin/wyvern/mastercraft/BytecodeTools.java deleted file mode 100644 index ab7eaa7..0000000 --- a/src/main/java/mod/sin/wyvern/mastercraft/BytecodeTools.java +++ /dev/null @@ -1,494 +0,0 @@ -package mod.sin.wyvern.mastercraft; - -import javassist.bytecode.*; - -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.IntStream; - - -public class BytecodeTools extends Bytecode { - - //private static final int EMPTY_INT = Integer.MAX_VALUE; - - BytecodeTools(ConstPool constPool){ - super(constPool); - } - - /** - * Look for a class in the constant pool. - * Throws NoMatchingConstPoolIndexException if the references isn't found. - * - * @param className String Object type. Full class name using periods. - * @return int primitive, index in constant pool table. - */ - int findClassIndex(String className){ - int classReferenceIndex = getClassReferenceIndex(className); - if (classReferenceIndex == -1) - throw new RuntimeException("No matching class found."); - this.add((classReferenceIndex >>> 8) & 0xFF, classReferenceIndex & 0xFF); - return classReferenceIndex; - } - - int addClassIndex(String className){ - int classReferenceIndex = getClassReferenceIndex(className); - if (classReferenceIndex == -1) - classReferenceIndex = this.getConstPool().addClassInfo(className); - this.add((classReferenceIndex >>> 8) & 0xFF, classReferenceIndex & 0xFF); - return classReferenceIndex; - } - - int findMethodIndex(int opcode, String name, String descriptor, String className){ - int methodReferenceIndex; - int classReferenceIndex = getClassReferenceIndex(className); - if (classReferenceIndex == -1) - throw new RuntimeException("No matching class found."); - - methodReferenceIndex = IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_Methodref) - .filter(value -> this.getConstPool().eqMember(name, descriptor, value) != null) - .filter(value -> Objects.equals(this.getConstPool().getMethodrefClass(value), classReferenceIndex)) - .findFirst() - .orElseThrow(() -> new RuntimeException("No matching method found.")); - this.addOpcode(opcode); - this.add((methodReferenceIndex >>> 8) & 0xFF, methodReferenceIndex & 0xFF); - - return methodReferenceIndex; - } - - int addMethodIndex(int opcode, String name, String methodDescriptor, String className){ - int classReferenceIndex = getClassReferenceIndex(className); - if (classReferenceIndex == -1) - classReferenceIndex = this.getConstPool().addClassInfo(className); - int indexReference = this.getConstPool().addMethodrefInfo(classReferenceIndex, name, methodDescriptor); - this.addOpcode(opcode); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - return indexReference; - } - - int findFieldIndex(int opcode, String name, String descriptor, String className){ - int fieldReferenceIndex; - int classReferenceIndex = getClassReferenceIndex(className); - if (classReferenceIndex == -1) - throw new RuntimeException("No matching class found."); - fieldReferenceIndex = IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_Fieldref) - .filter(value -> this.getConstPool().eqMember(name, descriptor, value) != null) - .filter(value -> this.getConstPool().getFieldrefClass(value) == classReferenceIndex) - .findFirst() - .orElseThrow(() -> new RuntimeException("No matching field found.")); - this.addOpcode(opcode); - this.add((fieldReferenceIndex >>> 8) & 0xFF, fieldReferenceIndex & 0xFF); - return fieldReferenceIndex; - } - - int addFieldIndex(int opcode, String name, String descriptor, String className){ - int classReferenceIndex = getClassReferenceIndex(className); - if (classReferenceIndex == -1) - classReferenceIndex = this.getConstPool().addClassInfo(className); - int fieldReferenceIndex = this.getConstPool().addFieldrefInfo(classReferenceIndex, name, descriptor); - this.addOpcode(opcode); - this.add((fieldReferenceIndex >>> 8) & 0xFF, fieldReferenceIndex & 0xFF); - return fieldReferenceIndex; - } - - void findStringIndex(String string){ - int indexReference = IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_String) - .filter(value -> Objects.equals(this.getConstPool().getStringInfo(value), string)) - .findFirst() - .orElseThrow(() -> new RuntimeException("No matching string found.")); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - - void addStringIndex(String string){ - int indexReference = this.getConstPool().addStringInfo(string); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - - void findLongIndex(long longValue){ - int indexReference = IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_Long) - .filter(value -> this.getConstPool().getLongInfo(value) == longValue) - .findFirst() - .orElseThrow(() -> new RuntimeException("No matching long found.")); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - - void addLongIndex(long longValue){ - int indexReference = this.getConstPool().addLongInfo(longValue); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - - void findFloatIndex(float floatValue){ - int indexReference = IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_Float) - .filter(value -> this.getConstPool().getFloatInfo(value) == floatValue) - .findFirst() - .orElseThrow(() -> new RuntimeException("No matching float found.")); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - - void addFloatIndex(float floatValue){ - int indexReference = this.getConstPool().addFloatInfo(floatValue); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - - /** - * a double value stored in the constant pool is always a LDC2_W. This is different then a local variable holding a double. - * - * @param doubleValue primitive double. - */ - void findDoubleIndex(double doubleValue){ - int indexReference = IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_Double) - .filter(value -> this.getConstPool().getDoubleInfo(value) == doubleValue) - .findFirst() - .orElseThrow(() -> new RuntimeException("No matching double found.")); - this.addOpcode(Opcode.LDC2_W); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - - /** - * a double value stored in the constant pool is always a LDC2_W. This is different then a local variable holding a double. - * - * @param doubleValue primitive double. - */ - void addDoubleIndex(double doubleValue){ - int indexReference = this.getConstPool().addDoubleInfo(doubleValue); - this.addOpcode(Opcode.LDC2_W); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - - private boolean hasAClassDeclaringTag(int tag){ - return tag == ConstPool.CONST_Methodref || tag == ConstPool.CONST_Fieldref || tag == ConstPool.CONST_InterfaceMethodref; - } - - /** - * Often a class's name will appear twice in the constant pool. One of the occurrence is not used as a declaring class for anything. - * I have no idea why it's present but it can break looking up constant pool references if the unassociated one is picked. JA has a - * built in way of finding existent references but a underlying mechanic is that a hash map uses a string class name as a key - * in a hashMap. Two equal strings will overwrite each other in this case. This is part the the tools library to look for matches - * instead of relying on JA. - * - * 1. scan the constant pool and get the class references that match className. - * 2. scan again through the constant pool looking for class associations that use the references found in #1. One of the options - * will have no references and illuminate that one to return the one that should be used. - * - * @param className String type object, uses full class name and periods. - * @return int primitive, the address in constant pool for the class matching className. - */ - private int getClassReferenceIndex(String className){ - return IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_Class) - .filter(value -> Objects.equals(Descriptor.toClassName(this.getConstPool().getClassInfoByDescriptor(value)), className)) - .filter( verifyIndex -> - IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> hasAClassDeclaringTag(this.getConstPool().getTag(value))) - .filter(value -> { - boolean result = false; - switch (this.getConstPool().getTag(value)) { - case ConstPool.CONST_Methodref: - result = this.getConstPool().getMethodrefClass(value) == verifyIndex; - break; - case ConstPool.CONST_Fieldref: - result = this.getConstPool().getFieldrefClass(value) == verifyIndex; - break; - case ConstPool.CONST_InterfaceMethodref: - result = this.getConstPool().getInterfaceMethodrefClass(value) == verifyIndex; - break; - } - return result;}) - .count() > 0 - ) - .findFirst() - .orElse(-1); - } - - void findInterfaceMethodIndex(String name, String descriptor){ - if(IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_InterfaceMethodref) - .filter(value -> this.getConstPool().eqMember(name, descriptor, value) != null) - .count() != 1){ - throw new RuntimeException("No matching interface found."); - } - else { - int indexReference = IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_InterfaceMethodref) - .filter(value -> this.getConstPool().eqMember(name, descriptor, value) != null) - .findFirst() - .orElseThrow(() -> new RuntimeException("No matching interface found.")); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - } - - void addInterfaceMethodIndex(String name, String descriptor){ - int classIndexReference = IntStream.range(1, this.getConstPool().getSize()) - .filter(value -> this.getConstPool().getTag(value) == ConstPool.CONST_Class) - .filter(value -> Objects.equals(this.getConstPool().getClassInfoByDescriptor(value), Descriptor.toClassName(descriptor))) - .findFirst() - .orElseThrow(() -> new RuntimeException("No matching class found.")); - int indexReference = this.getConstPool().addInterfaceMethodrefInfo(classIndexReference, name, descriptor); - this.add((indexReference >>> 8) & 0xFF, indexReference & 0xFF); - } - - void codeBranching(int opcode, int branchCount){ - this.addOpcode(opcode); - this.add((branchCount >>> 8) & 0xFF, branchCount & 0xFF); - } - - void localVariableIndex(int opcode, int slot){ - this.addOpcode(opcode); - this.add(slot); - } - - void integerIndex(int opcode, int value){ - switch (opcode) { - case Opcode.BIPUSH : - this.addOpcode(Opcode.BIPUSH); - this.add((byte)value); - break; - case Opcode.SIPUSH : - this.addOpcode(Opcode.SIPUSH); - this.add((value >>> 8) & 0xFF, value & 0xFF); - } - } - - /** - * Encode the value for arg "integer" into the appropriate byteCode opCode + operand for the java-int. Add the - * encoded information to the byte code object "bytecode". - * - * @param integer int value. - */ - void addInteger(int integer) { - switch (integer) { - case -1: - this.add(Opcode.ICONST_M1); - break; - case 0: - this.add(Opcode.ICONST_0); - break; - case 1: - this.add(Opcode.ICONST_1); - break; - case 2: - this.add(Opcode.ICONST_2); - break; - case 3: - this.add(Opcode.ICONST_3); - break; - case 4: - this.add(Opcode.ICONST_4); - break; - case 5: - this.add(Opcode.ICONST_5); - break; - default: - if (integer >= Byte.MIN_VALUE && integer <= Byte.MAX_VALUE) { - this.add(Opcode.BIPUSH); - // integer bound to byte size. - this.add(integer); - } else if (integer >= Short.MIN_VALUE && integer <= Short.MAX_VALUE) { - this.add(Opcode.SIPUSH); - // Since byte code requires byte sized blocks, break up integer with bitmask and shift. - this.add((integer & 0xff00) >>> 8, integer & 0x00ff); - } else { - // Appends LDC or LDC_W depending on constant pool size. - this.addLdc(this.getConstPool().addIntegerInfo(integer)); - } - } - } - - /** - * Decode the byte code represented by a opCode + operand(s) at the position in arg "instructionIndex". Return - * decoded data as java-int. - * - * @param codeIterator JA CodeIterator object. - * @param instructionIndex int value, it is the codeIterator index of an opCode. - * @return int value. - */ - int getInteger(CodeIterator codeIterator, int instructionIndex) { - int opCode = codeIterator.byteAt(instructionIndex); - switch (opCode) { - case Opcode.ICONST_M1: - return -1; - case Opcode.ICONST_0: - return 0; - case Opcode.ICONST_1: - return 1; - case Opcode.ICONST_2: - return 2; - case Opcode.ICONST_3: - return 3; - case Opcode.ICONST_4: - return 4; - case Opcode.ICONST_5: - return 5; - case Opcode.BIPUSH: - return codeIterator.byteAt(instructionIndex + 1); - case Opcode.SIPUSH: - return codeIterator.s16bitAt(instructionIndex + 1); - case Opcode.LDC: - return this.getConstPool().getIntegerInfo(codeIterator.byteAt(instructionIndex + 1)); - case Opcode.LDC_W: - return this.getConstPool().getIntegerInfo(codeIterator.u16bitAt(instructionIndex + 1)); - default: - throw new RuntimeException(String.format("Failed to decode integer. Pos = %d, Bytecode = %d", instructionIndex, opCode)); - } - } - - static int findSlotInLocalVariableTable(CodeAttribute codeAttribute, String variableName){ - LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); - int tableOrdinal; - tableOrdinal = IntStream.range(0,table.tableLength()).filter(value -> Objects.equals(table.variableName(value), variableName )).findFirst().orElse(-1); - if (tableOrdinal == -1){ - return -1; - } - return table.index(tableOrdinal); - } - - static int findLineNumberInLineNumberTable(CodeAttribute codeAttribute, String variableName){ - LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LineNumberAttribute.tag); - int tableOrdinal; - tableOrdinal = IntStream.range(0,table.tableLength()).filter(value -> Objects.equals(table.variableName(value), variableName )).findFirst().orElse(-1); - if (tableOrdinal == -1){ - return -1; - } - return table.index(tableOrdinal); - } - - private static int[] getInstruction(int size, int index, CodeIterator codeIterator) { - int[] toReturn = null; - int bitLine; - int bitLine2; - switch (size) { - case 1: - bitLine = codeIterator.byteAt(index); - toReturn = new int[]{ - bitLine - }; - break; - case 2: - bitLine = codeIterator.s16bitAt(index); - toReturn = new int[]{ - (bitLine & 0xff00) >>> 8, - bitLine & 0x00ff - }; - break; - case 3: - bitLine = codeIterator.s32bitAt(index); - toReturn = new int[]{ - (bitLine & 0xff000000) >>> 24, - (bitLine & 0x00ff0000) >>> 16, - (bitLine & 0x0000ff00) >>> 8 - // not using the last byte - }; - break; - case 4: - bitLine = codeIterator.s32bitAt(index); - toReturn = new int[]{ - (bitLine & 0xff000000) >>> 24, - (bitLine & 0x00ff0000) >>> 16, - (bitLine & 0x0000ff00) >>> 8, - (bitLine & 0x000000ff) - }; - break; - case 5: - bitLine = codeIterator.s32bitAt(index); - bitLine2 = codeIterator.byteAt(index + 4); - toReturn = new int[]{ - (bitLine & 0xff000000) >>> 24, - (bitLine & 0x00ff0000) >>> 16, - (bitLine & 0x0000ff00) >>> 8, - (bitLine & 0x000000ff), - bitLine2 - }; - break; - case 6: - bitLine = codeIterator.s32bitAt(index); - bitLine2 = codeIterator.s16bitAt(index + 4); - toReturn = new int[]{ - (bitLine & 0xff000000) >>> 24, - (bitLine & 0x00ff0000) >>> 16, - (bitLine & 0x0000ff00) >>> 8, - (bitLine & 0x000000ff), - (bitLine2 & 0xff00) >>> 8, - (bitLine2 & 0x00ff) - }; - break; - case 7: - bitLine = codeIterator.s32bitAt(index); - bitLine2 = codeIterator.s32bitAt(index + 4); - toReturn = new int[]{ - (bitLine & 0xff000000) >>> 24, - (bitLine & 0x00ff0000) >>> 16, - (bitLine & 0x0000ff00) >>> 8, - (bitLine & 0x000000ff), - (bitLine2 & 0xff000000) >>> 24, - (bitLine2 & 0x00ff0000) >>> 16, - (bitLine2 & 0x0000ff00) >>> 8 - // not using the last byte - }; - break; - case 8: - bitLine = codeIterator.s32bitAt(index); - bitLine2 = codeIterator.s32bitAt(index + 4); - toReturn = new int[]{ - (bitLine & 0xff000000) >>> 24, - (bitLine & 0x00ff0000) >>> 16, - (bitLine & 0x0000ff00) >>> 8, - (bitLine & 0x000000ff), - (bitLine2 & 0xff000000) >>> 24, - (bitLine2 & 0x00ff0000) >>> 16, - (bitLine2 & 0x0000ff00) >>> 8, - (bitLine2 & 0x000000ff) - }; - break; - } - return toReturn; - } - - public static void byteCodePrint(String destinationPath, CodeIterator codeIterator) throws FileNotFoundException, BadBytecode { - Path printPath = Paths.get(destinationPath); - PrintWriter out = new PrintWriter(printPath.toFile()); - final String[] instructionOut = {""}; - codeIterator.begin(); - while (codeIterator.hasNext()) { - int index = codeIterator.next(); - int[] instruction = getInstruction(codeIterator.lookAhead() - index, index, codeIterator); - instructionOut[0] += Integer.toString(index); - instructionOut[0] += " "; - instructionOut[0] += Mnemonic.OPCODE[instruction[0]]; - if (instruction.length > 1) { - instructionOut[0] += " "; - IntStream.range(1, instruction.length) - .forEach(value -> { - instructionOut[0] += Integer.toString(instruction[value]); - instructionOut[0] += " "; - }); - } - out.println(instructionOut[0]); - instructionOut[0] = ""; - } - out.close(); - } - - static void printArrayToHex(Object[] obj, String name, Logger logger){ - int length = obj.length; - int[] c = new int[length]; - for (int i=0;i toExtendEntries = new ArrayList<>(); - private static ExtendTitleEnum singletonInstance; - - private ExtendTitleEnum(String className, int valuesSizerIndex, int populateVALUESIndex, ConstPool constPool) { - this.className = className; - this.valuesSizerIndex = valuesSizerIndex; - this.populateVALUESIndex = populateVALUESIndex; - this.constPool = constPool; - } - - /** - * Goes through the enum class's initiator to find bytecode index positions. - * - * @throws BadBytecode forwarded, Javassist stuff. - */ - static void builder(String className) throws BadBytecode, NotFoundException { - int valuesSizerIndex = -1; - //int indexANEWARRAY = -1; - int populateVALUESIndex = -1; - CtClass ctClassEnum = HookManager.getInstance().getClassPool().get(className); - ConstPool constPool = ctClassEnum.getClassFile().getConstPool(); - CodeIterator codeIterator = ctClassEnum.getClassInitializer().getMethodInfo().getCodeAttribute().iterator(); - // Get the byte code instruction index for - // 1) size value for ANEWARRAY, - // 2) the VALUES array assignment or population. - BytecodeTools b = new BytecodeTools(constPool); - String valuesDescriptor = className.replace(".", "/"); - valuesDescriptor = "[L" + valuesDescriptor + ";"; - int constPoolValuesIndex = b.findFieldIndex(Opcode.PUTSTATIC, "$VALUES", - valuesDescriptor, className); - codeIterator.begin(); - int lastIndex = 0; - while (codeIterator.hasNext()){ - int instructionIndex = codeIterator.next(); - int opCode = codeIterator.byteAt(instructionIndex); - switch (opCode){ - case Opcode.ANEWARRAY : - valuesSizerIndex = lastIndex; - //indexANEWARRAY = instructionIndex; - break; - case Opcode.PUTSTATIC : - int cpAddress = codeIterator.u16bitAt(instructionIndex+1); - if (cpAddress == constPoolValuesIndex){ - populateVALUESIndex = instructionIndex; - } - break; - default: - break; - } - lastIndex = instructionIndex; - } - - synchronized (ExtendTitleEnum.class) { - singletonInstance = new ExtendTitleEnum(className, valuesSizerIndex, populateVALUESIndex, constPool); - } - } - - - static ExtendTitleEnum getSingletonInstance() { - return singletonInstance; - } - - /** - * A method to create data structures and add record a reference for that object. - * - * @param fieldName the name for the enum entry. - * @param titleId an ordinal for the Titles.Title enum. - * @param maleName in-game title name for male toons. - * @param femaleName in-game title name for femaleName toons. - * @param skillId A id number for the skill associated with the title, see {@link SkillList} - * @param titleTypes A string representation of entries in {@link com.wurmonline.server.players.Titles.TitleType}. In - * order to avoid premature class initialization for Javassist's bytecode stages we use a string - * instead of the WU object. The string must match one of the enum field names. - */ - synchronized void addExtendEntry(String fieldName, int titleId, String maleName, String femaleName, int skillId, String titleTypes) { - if (singletonInstance == null) { - throw new RuntimeException("ExtendTitleEnum instance is null, build it before addExtendEntry"); - } - EnumFields enumFields = new EnumFields(fieldName, titleId, maleName, femaleName, skillId, titleTypes); - toExtendEntries.add(enumFields); - } - - class EnumFields { - final String fieldName; - final int titleId; - final String maleName; - final String femaleName; - final int skillId; - final String titleTypes; - - /** - * @param fieldName the name for the enum entry. - * @param titleId an ordinal for the Titles.Title enum. - * @param maleName in-game title name for male toons. - * @param femaleName in-game title name for femaleName toons. - * @param skillId A id number for the skill associated with the title, see {@link SkillList} - * @param titleTypes A string representation of entries in {@link com.wurmonline.server.players.Titles.TitleType}. In - * order to avoid premature class initialization for Javassist's bytecode stages we use a string - * instead of the WU object. - **/ - EnumFields(String fieldName, int titleId, String maleName, String femaleName, - int skillId, String titleTypes){ - this.fieldName = fieldName; - this.titleId = titleId; - this.maleName = maleName; - this.femaleName = femaleName; - this.skillId = skillId; - this.titleTypes = titleTypes; - } - } - - /** - * Intended to be used in WurmServerMod-initiate section and it's for bytecode changes. This adds field objects to the enum class. - * - * @throws CannotCompileException forwarded, Javassist stuff. - */ - private synchronized void createFieldsInEnum() throws CannotCompileException, NotFoundException { - if (toExtendEntries.size() == 0){ - throw new RuntimeException("Can not extend an enum without values in toExtendEntries arrayList."); - } - - CtClass enumCtClass = HookManager.getInstance().getClassPool().get(this.className); - for (EnumFields enumData : toExtendEntries) { - CtField field = new CtField(enumCtClass, enumData.fieldName, enumCtClass); - field.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL | Modifier.ENUM); - enumCtClass.addField(field); - } - } - - /** - * This method uses JA bytecode to inject into the Enum's class initiator in order to expand the enum's $VALUES field. - * - * @throws BadBytecode forwarded, Javassist stuff. - */ - private void resizeEnumVALUES() throws BadBytecode, ClassNotFoundException, NotFoundException { - int expansion = toExtendEntries.size(); - CtClass ctClassEnum = HookManager.getInstance().getClassPool().get(this.className); - CodeIterator codeIterator = ctClassEnum.getClassInitializer().getMethodInfo().getCodeAttribute().iterator(); - - BytecodeTools findBytecode = new BytecodeTools(this.constPool); - int currentSize = findBytecode.getInteger(codeIterator, this.valuesSizerIndex); - findBytecode.addInteger(currentSize); - findBytecode.addOpcode(Opcode.ANEWARRAY); - findBytecode.addClassIndex(this.className); - - BytecodeTools replaceBytecode = new BytecodeTools(this.constPool); - replaceBytecode.addInteger(currentSize + expansion); - replaceBytecode.addOpcode(Opcode.ANEWARRAY); - replaceBytecode.addClassIndex(this.className); - - CodeReplacer codeReplacer = new CodeReplacer(ctClassEnum.getClassInitializer().getMethodInfo().getCodeAttribute()); - codeReplacer.replaceCode(findBytecode.get(), replaceBytecode.get()); - } - - /** - * This method builds bytecode to inject into the enum's initiator. The injected code initializes new enum entries and adds - * a reference of that new object to the $VALUES array. - * - * @throws BadBytecode forwarded, JA stuff. - * @throws ClassNotFoundException forwarded, JA stuff. - * @throws NotFoundException forwarded, JA stuff. - */ - synchronized void ExtendEnumEntries() throws BadBytecode, ClassNotFoundException, NotFoundException, CannotCompileException { - createFieldsInEnum(); - CtClass ctClassEnum = HookManager.getInstance().getClassPool().get(this.className); - CodeIterator initiatorCodeIterator = ctClassEnum.getClassInitializer().getMethodInfo().getCodeAttribute().iterator(); - - BytecodeTools enumInitiator = new BytecodeTools(ctClassEnum.getClassFile().getConstPool()); - BytecodeTools populateVALUES = new BytecodeTools(ctClassEnum.getClassFile().getConstPool()); - int extensionCounter = 0; - int valuesSize = enumInitiator.getInteger(initiatorCodeIterator, this.valuesSizerIndex); - // Construct the two bytecode objects to be inserted. The multiple enumData in toExtendEntries are combined into one - // long bytecode sequence and inserted at the proper point. - for (EnumFields enumData : toExtendEntries) { - enumInitiator.addOpcode(Opcode.NEW); - enumInitiator.findClassIndex(this.className); - enumInitiator.addOpcode(Opcode.DUP); - enumInitiator.addLdc(enumData.fieldName); - enumInitiator.addInteger(valuesSize + extensionCounter); - enumInitiator.addInteger(enumData.titleId); - enumInitiator.addLdc(enumData.maleName); - enumInitiator.addLdc(enumData.femaleName); - enumInitiator.addInteger(enumData.skillId); - enumInitiator.addFieldIndex(Opcode.GETSTATIC, enumData.titleTypes, - "Lcom/wurmonline/server/players/Titles$TitleType;", - "com/wurmonline/server/players/Titles$TitleType"); - enumInitiator.addMethodIndex(Opcode.INVOKESPECIAL, "", - "(Ljava/lang/String;IILjava/lang/String;Ljava/lang/String;ILcom/wurmonline/server/players/Titles$TitleType;)V", - this.className); - enumInitiator.addFieldIndex(Opcode.PUTSTATIC, enumData.fieldName, "Lcom/wurmonline/server/players/Titles$Title;", - this.className); - - populateVALUES.addOpcode(Opcode.DUP); - populateVALUES.addInteger(valuesSize + extensionCounter); - extensionCounter++; - populateVALUES.findFieldIndex(Opcode.GETSTATIC, enumData.fieldName, "Lcom/wurmonline/server/players/Titles$Title;", - this.className); - populateVALUES.addOpcode(Opcode.AASTORE); - } - // Do bytecode changes from the bottom up so bytecode indexes don't change after every insert. - initiatorCodeIterator.insert(populateVALUESIndex, populateVALUES.get()); - resizeEnumVALUES(); - initiatorCodeIterator.insert(valuesSizerIndex, enumInitiator.get()); - } -} \ No newline at end of file diff --git a/src/main/java/mod/sin/wyvern/mastercraft/Mastercraft.java b/src/main/java/mod/sin/wyvern/mastercraft/Mastercraft.java deleted file mode 100644 index 512152c..0000000 --- a/src/main/java/mod/sin/wyvern/mastercraft/Mastercraft.java +++ /dev/null @@ -1,203 +0,0 @@ -package mod.sin.wyvern.mastercraft; - -import com.wurmonline.server.Server; -import com.wurmonline.server.items.Item; -import com.wurmonline.server.players.Titles; -import com.wurmonline.server.skills.Skill; -import com.wurmonline.server.skills.SkillList; -import javassist.*; -import javassist.bytecode.BadBytecode; -import javassist.expr.ExprEditor; -import javassist.expr.MethodCall; -import mod.sin.lib.Util; -import org.gotti.wurmunlimited.modloader.ReflectionUtil; -import org.gotti.wurmunlimited.modloader.classhooks.HookManager; - -import java.util.Objects; -import java.util.logging.Logger; - -public class Mastercraft { - private static Logger logger = Logger.getLogger(Mastercraft.class.getName()); - public static double getNewDifficulty(Skill skill, double diff, Item item){ - if(skill.affinity > 0){ - diff -= skill.affinity; - } - if(skill.getKnowledge() > 99.0d){ - diff -= 2d-((100d-skill.getKnowledge())*2d); - } - if(skill.getKnowledge() > 90.0d){ - diff -= 2d-((100d-skill.getKnowledge())*0.2d); - } - if(item != null){ - if(item.getRarity() > 0){ - diff -= item.getRarity(); - } - if(item.getCurrentQualityLevel() > 99.0f){ - diff -= 1d-((100d-item.getCurrentQualityLevel())*1d); - } - if(item.getCurrentQualityLevel() > 90.0f){ - diff -= 1d-((100d-item.getCurrentQualityLevel())*0.1d); - } - } - return diff; - } - public static float getCastPowerIncrease(Skill skill){ - double addedPower = 0; - if(skill.affinity > 0){ - addedPower += 2*skill.affinity; - } - if(skill.getKnowledge() > 0){ - float lowFloat1 = Math.min(Server.rand.nextFloat(), Server.rand.nextFloat()); - float lowFloat2 = Math.min(Server.rand.nextFloat(), Server.rand.nextFloat()); - addedPower += Math.min(skill.getKnowledge()*lowFloat1, skill.getKnowledge()*lowFloat2); - }else{ - logger.warning("Error: Some player just tried casting with no channeling skill!"); - } - return (float) addedPower; - } - public static float getFavorCostMultiplier(Skill skill){ - float mult = 1f; - if(skill.affinity > 0){ - mult -= skill.affinity*0.02f; //2% reduction per affinity - } - if(skill.getKnowledge() > 90d){ - mult -= 0.1d-((100d-skill.getKnowledge())*0.01d); - } - if(skill.getKnowledge() > 99d){ - mult -= 0.1d-((100-skill.getKnowledge())*0.1d); - } - return mult; - } - /*public static void addNewTitles(){ - try { - ExtendTitleEnum.builder("com.wurmonline.server.players.Titles$Title"); - // GM/Developer Titles - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Game_Master", 2500, "Game Master", "Game Master", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Developer", 2501, "Developer", "Developer", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Pet_Me", 2502, "Pet Me", "Pet Me", -1, "NORMAL"); - - // Troll Titles - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Macro_King", 550, "Macro King", "Macro King", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Drama_Queen", 551, "Drama Queen", "Drama Queen", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Zergling", 552, "Zergling", "Zergling", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Special_Title", 553, "Special Guy", "Special Girl", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Prophet_Ear", 554, "Prophet Ear", "Prophet Ear", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Koza", 555, "Koza", "Koza", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Wyvern_Hunter", 556, "Wyvern Hunter", "Wyvern Hunter", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Overlord", 557, "Overlord", "Overlord", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Troll", 558, "Troll", "Troll", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Beggar", 559, "Beggar", "Beggar", -1, "NORMAL"); - - // Contest Titles - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Home_Decorator", 600, "Home Decorator", "Home Decorator", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Arena_Champion", 601, "Champion of the Arena", "Champion of the Arena", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Pastamancer", 602, "Pastamancer", "Pastamancer", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Pizzamancer", 603, "Pizzamancer", "Pizzamancer", -1, "NORMAL"); - - // Special Event Titles - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Titan_Slayer", 700, "Titanslayer", "Titanslayer", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Spectral_Slayer", 701, "Spectral Warrior", "Spectral Warrior", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Holdstrong_Architect", 702, "Holdstrong Architect", "Holdstrong Architect", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Stronghold_Architect", 703, "Stronghold Architect", "Stronghold Architect", -1, "NORMAL"); - - // Donation titles - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Donator", 800, "Donator", "Donator", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Pazza_FavoriteGM", 801, "Sindusks Favourite GM", "Sindusks Favourite GM", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Warriorgen_ThatGuy", 802, "That Guy", "That Guy", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Eternallove_WarriorgensWife", 803, "Warriorgens Wife", "Warriorgens Wife", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Bambam_ThornOne", 804, "Thorn One", "Thorn One", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Svenja_CareDependant", 805, "The care-dependent", "The care-dependent", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Alexia_TheTreasuring", 806, "The Treasuring", "The Treasuring", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Reevi_ScienceGuy", 807, "Science Guy", "Science Guy", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Genocide_GrandDesigner", 808, "Grand Designer", "Grand Designer", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Seleas_CrazyCatLord", 809, "The Crazy Cat Lord", "The Crazy Cat Lord", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Piratemax_Slave", 810, "Slave", "Slave", -1, "NORMAL"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Eltacolad_TrueTaco", 811, "The One True Taco", "The One True Taco", -1, "NORMAL"); - - // Skill Titles (100) - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Archery_Legendary", 1500, "Legendary Marksman", "Legendary Marksman", 1030, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Body_Legendary", 1501, "Hercules", "Hercules", 1, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Axes_Legendary", 1502, "Viking", "Viking", 1003, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Baking_Legendary", 1503, "Patissier", "Patissier", 10039, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Archaeology_Legendary", 1504, "Curator", "Curator", 10069, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("CarvingKnife_Legendary", 1505, "Woodsculptor", "Woodsculptor", 10007, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Taming_Legendary", 1506, "King of the Jungle", "Queen of the Jungle", 10078, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Climbing_Legendary", 1507, "Moonwalker", "Moonwalker", 10073, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Tracking_Legendary", 1508, "Bloodhound", "Bloodhound", 10018, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Clubs_Legendary", 1509, "Bam Bam", "Bam Bam", 1025, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Catapults_Legendary", 1510, "Castle Crasher", "Castle Crasher", 10077, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Firemaking_Legendary", 1511, "Incendiary", "Incendiary", 1010, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Gardening_Legendary", 1512, "Earthbound", "Earthbound", 10045, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Hammers_Legendary", 1513, "Doomhammer", "Doomhammer", 1027, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Locksmithing_Legendary", 1514, "Vault Smith", "Vault Smith", 10034, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Religion_Legendary", 1515, "Chosen", "Chosen", 1026, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Yoyo_Legendary", 1516, "String Theorist", "String Theorist", 10050, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Nature_Legendary", 1517, "Naturalist", "Naturalist", 1019, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Mind_Legendary", 1518, "Enlightened", "Enlightened", 2, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Mauls_Legendary", 1519, "Breaker", "Breaker", 1004, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Shipbuilding_Legendary", 1520, "Naval Engineer", "Naval Engineer", 10082, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("NaturalSubstances_Legendary", 1521, "Biochemist", "Biochemist", 10042, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("WarMachines_Legendary", 1522, "Eradicator", "Eradicator", 1029, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Thievery_Legendary", 1523, "Shadow", "Shadow", 1028, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Swords_Legendary", 1524, "Samurai", "Samurai", 1000, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().addExtendEntry("Forestry_Legendary", 1525, "Silvanus", "Mother Nature", 10048, "LEGENDARY"); - ExtendTitleEnum.getSingletonInstance().ExtendEnumEntries(); - - } catch (BadBytecode | ClassNotFoundException | NotFoundException | CannotCompileException e) { - logger.warning(e.getMessage()); - } - }*/ - - public static void changeExistingTitles(){ - for (Titles.Title title : Titles.Title.values()) { - if (Objects.equals("Pumpkin King", title.getFemaleName())){ - try { - ReflectionUtil.setPrivateField(title, ReflectionUtil.getField(title.getClass(), "femaleName"), "Pumpkin Queen"); - } catch (IllegalArgumentException | IllegalAccessException | ClassCastException | NoSuchFieldException e) { - e.printStackTrace(); - } - } - } - } - public static void preInit(){ - try { - ClassPool classPool = HookManager.getInstance().getClassPool(); - Class thisClass = Mastercraft.class; - - // - Reduce skill check difficulty with high skills or tools - // - CtClass ctSkill = classPool.get("com.wurmonline.server.skills.Skill"); - - /*ctSkill.getDeclaredMethod("checkAdvance").insertBefore("" - + "$1 = "+Mastercraft.class.getName()+".getNewDifficulty(this, $1, $2);");*/ - Util.setReason("Modify difficulty for skill checks in MasterCraft."); - String replace = "$1 = "+Mastercraft.class.getName()+".getNewDifficulty(this, $1, $2);"; - Util.insertBeforeDeclared(thisClass, ctSkill, "checkAdvance", replace); - - // - Increase spellcasting power for skilled channelers - // - CtClass ctSpell = classPool.get("com.wurmonline.server.spells.Spell"); - CtMethod[] ctRuns = ctSpell.getDeclaredMethods("run"); - for(CtMethod method : ctRuns){ - method.instrument(new ExprEditor(){ - public void edit(MethodCall m) throws CannotCompileException { - if (m.getMethodName().equals("doEffect")) { - m.replace("$2 += "+Mastercraft.class.getName()+".getCastPowerIncrease(castSkill);" - + "$_ = $proceed($$);"); - logger.info("Instrumented doEffect in run()"); - } - } - }); - method.instrument(new ExprEditor(){ - public void edit(MethodCall m) throws CannotCompileException { - if (m.getMethodName().equals("depleteFavor")) { - m.replace("$1 *= "+Mastercraft.class.getName()+".getFavorCostMultiplier(castSkill);" - + "$_ = $proceed($$);"); - logger.info("Instrumented depleteFavor in run()"); - } - } - }); - } - } catch (CannotCompileException | NotFoundException e) { - e.printStackTrace(); - } - } -}