WyvernMods Configurable Phase 2

This commit is contained in:
Sindusk
2019-05-06 09:25:48 -04:00
parent 9eb8d1a2b5
commit acf5d74a9c
11 changed files with 854 additions and 1409 deletions

View File

@@ -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<Creature> 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<CombatChanges> 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");

View File

@@ -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<Mastercraft> 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();
}
}
}

View File

@@ -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<MeditationPerks> 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);
}

View File

@@ -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);*/
}
}

View File

@@ -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<SkillChanges> 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);

View File

@@ -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<Integer,String> skillName = new HashMap<>();
public static HashMap<Integer,Float> skillDifficulty = new HashMap<>();
public static HashMap<Integer,Long> 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;
}

View File

@@ -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<length;i++){
c[i]=(int)obj[i];
}
String[] a = new String[length];
for (int i=0;i<length;i++){
a[i]=String.format("%02X", c[i] & 0xff);
}
logger.log(Level.INFO,name + " : " + Arrays.toString(a));
}
}

View File

@@ -1,223 +0,0 @@
package mod.sin.wyvern.mastercraft;
import com.wurmonline.server.skills.SkillList;
import javassist.*;
import javassist.bytecode.*;
import org.gotti.wurmunlimited.modloader.classhooks.CodeReplacer;
import org.gotti.wurmunlimited.modloader.classhooks.HookManager;
import java.util.ArrayList;
class ExtendTitleEnum {
private final String className;
private final int valuesSizerIndex; // the bytecode index which puts a size specifying value on the stack for anewarray.
private final int populateVALUESIndex; // the bytecode index where references to various enum instances are put in the $VALUES array.
private final ConstPool constPool;
private static ArrayList<EnumFields> 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, "<init>",
"(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());
}
}

View File

@@ -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<Mastercraft> 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();
}
}
}