Files
BattleBane/src/engine/objects/CharacterSkill.java
T
MagicBotandClaude Opus 5.5 b0460ed2d0 Derive mob attributes from every rune the client is sent
Client ArcCharacter_RecalcMaxAttributes (0x470440) and RecalcAttributes
(0x470610) treat the stats in the mob message as spent points and add
rune_attr_adj from all of the character's runes, clamped to
[1, max(1, sum of rune_max_attr_adj)]. The server summed only the race
rune and sent the result, so the client counted it twice.

Mob.getClientRuneIDs lists the runes serializeForClientMsgOtherPlayer
sends (race, plus Pet 2522 for pets, Warrior 2518 + Guard 252621 for
contract and guard mobs); getRuneAttribute applies the client rule.
calculateModifiedStats and the mob branch of CharacterSkill use it,
and the message now sends 0 (no spent points), as NPCs already do.
MobBaseStats (race rune only) has no users left and is removed.

Value changes (server side):
- Contract mobs and player guards: +100 to all five attributes
  (Guard rune 252621; its max adj is also +100, so no new clamp).
- Pets: + Pet rune 2522 (Str 76, Dex 76, Con 120, Int 40, Spi 40;
  max Str 500, Dex 500, Con 2000, Int 40, Spi 40).
- The new max clamp changes no loaded mobbase on its own (checked all
  2375 against their race runes).
These feed mob skill values (CharacterSkill) and statXxxCurrent.
Guard ATR/defense/damage/health still come from the NPCManager guard
formulas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 11:42:48 -04:00

1241 lines
49 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.objects;
import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.mbEnums.ModType;
import engine.mbEnums.SourceType;
import engine.net.ByteBufferWriter;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class CharacterSkill extends AbstractGameObject {
private static final int[] maxTrains = {
29, 29, 29, 29, 29, //0 to 4
29, 32, 34, 36, 38, //5 to 9
40, 42, 43, 45, 47, //10 to 14
48, 49, 51, 52, 53, //15 to 19
55, 56, 57, 58, 59, //20 to 24
60, 62, 63, 64, 65, //25 to 29
66, 67, 68, 68, 69, //30 to 34
70, 71, 72, 73, 74, //35 to 39
75, 76, 76, 77, 78, //40 to 44
79, 80, 80, 81, 82, //45 to 49
83, 83, 84, 85, 85, //50 to 54
86, 87, 88, 88, 89, //55 to 59
90, 90, 91, 92, 92, //60 to 64
93, 94, 94, 95, 95, //65 to 69
96, 97, 97, 98, 99, //70 to 74
99, 100, 100, 101, 101, //75 to 79
102, 103, 103, 104, 104, //80 to 84
105, 105, 106, 106, 107, //85 to 89
108, 109, 109, 110, 110, //90 to 94
111, 112, 112, 113, 113, //95 to 99
114, 115, 115, 116, 116, //100 to 104
117, 118, 118, 119, 119, //105 to 109
120, 121, 121, 122, 122, //110 to 114
123, 124, 124, 125, 125, //115 to 119
126, 127, 127, 128, 128, //120 to 124
129, 130, 130, 131, 131, //125 to 129
132, 133, 133, 134, 134, //130 to 134
135, 136, 136, 137, 137, //135 to 139
138, 139, 139, 140, 140, //140 to 144
141, 142, 142, 143, 143, //145 to 149
144, 145, 145, 146, 146, //150 to 154
147, 148, 148, 149, 149, //155 to 159
150, 151, 151, 152, 152, //160 to 164
153, 154, 154, 155, 155, //165 to 169
156, 157, 157, 158, 158, //170 to 174
159, 160, 160, 161, 161, //175 to 179
162, 163, 163, 164, 164, //180 to 184
165, 166, 166, 167, 167, //185 to 189
168}; //190
private static final float[] baseSkillValues = {
0.0f, 0.0f, 0.2f, 0.4f, 0.6f, //0 to 4
0.8f, 1.0f, 1.1666666f, 1.3333334f, 1.5f, //5 to 9
1.6666667f, 1.8333334f, 2.0f, 2.2f, 2.4f, //10 to 14
2.6f, 2.8f, 3.0f, 3.2f, 3.4f, //15 to 19
3.6f, 3.8f, 4.0f, 4.2f, 4.4f, //20 to 24
4.6f, 4.8f, 5.0f, 5.25f, 5.5f, //25 to 29
5.75f, 6.0f, 6.2f, 6.4f, 6.6f, //30 to 34
6.8f, 7.0f, 7.25f, 7.5f, 7.75f, //35 to 39
8.0f, 8.2f, 8.4f, 8.6f, 8.8f, //40 to 44
9.0f, 9.25f, 9.5f, 9.75f, 10.0f, //45 to 49
10.25f, 10.5f, 10.75f, 11.0f, 11.2f, //50 to 54
11.4f, 11.6f, 11.8f, 12.0f, 12.25f, //55 to 59
12.5f, 12.75f, 13.0f, 13.25f, 13.5f, //60 to 64
13.75f, 14.0f, 14.25f, 14.5f, 14.75f, //65 to 69
15.0f, 15.333333f, 15.666667f, 16.0f, 16.25f, //70 to 74
16.5f, 16.75f, 17.0f, 17.25f, 17.5f, //75 to 79
17.75f, 18.0f, 18.25f, 18.5f, 18.75f, //80 to 84
19.0f, 19.333334f, 19.666666f, 20.0f, 20.25f, //85 to 89
20.5f, 20.75f, 21.0f, 21.25f, 21.5f, //90 to 94
21.75f, 22.0f, 22.333334f, 22.666666f, 23.0f, //95 to 99
23.25f, 23.5f, 23.75f, 24.0f, 24.333334f, //100 to 104
24.666666f, 25.0f, 25.25f, 25.5f, 25.75f, //105 to 109
26.0f, 26.333334f, 26.666666f, 27.0f, 27.333334f, //110 to 114
27.666666f, 28.0f, 28.25f, 28.5f, 28.75f, //115 to 119
29.0f, 29.333334f, 29.666666f, 30.0f, 30.333334f, //120 to 124
30.666666f, 31.0f, 31.25f, 31.5f, 31.75f, //125 to 129
32.0f, 32.333332f, 32.666668f, 33.0f, 33.333332f, //130 to 134
33.666668f, 34.0f, 34.333332f, 34.666668f, 35.0f, //135 to 139
35.333332f, 35.666668f, 36.0f, 36.333332f, 36.666668f, //140 to 144
37.0f, 37.25f, 37.5f, 37.75f, 38.0f, //145 to 149
38.333332f, 38.666668f, 39.0f, 39.333332f, 39.666668f, //150 to 154
40.0f, 40.333332f, 40.666668f, 41.0f, 41.333332f, //155 to 159
41.666668f, 42.0f, 42.333332f, 42.666668f, 43.0f, //160 to 164
43.333332f, 43.666668f, 44.0f, 44.333332f, 44.666668f, //165 to 169
45.0f, 45.5f, 46.0f, 46.333332f, 46.666668f, //170 to 174
47.0f, 47.333332f, 47.666668f, 48.0f, 48.333332f, //175 to 179
48.666668f, 49.0f, 49.333332f, 49.666668f, 50.0f, //180 to 184
50.333332f, 50.666668f, 51.0f, 51.333332f, 51.666668f, //185 to 189
52.0f, 52.5f, 53.0f, 53.333332f, 53.666668f, //190 to 194
54.0f, 54.333332f, 54.666668f, 55.0f, 55.333332f, //195 to 199
55.666668f, 56.0f, 56.333332f, 56.666668f, 57.0f, //200 to 204
57.5f, 58.0f, 58.333332f, 58.666668f, 59.0f, //205 to 209
59.333332f, 59.666668f, 60.0f, 60.5f, 61.0f, //210 to 214
61.333332f, 61.666668f, 62.0f, 62.333332f, 62.666668f, //215 to 219
63.0f, 63.5f, 64.0f, 64.333336f, 64.666664f, //220 to 224
65.0f, 65.333336f, 65.666664f, 66.0f, 66.5f, //225 to 229
67.0f, 67.333336f, 67.666664f, 68.0f, 68.5f, //230 to 234
69.0f, 69.333336f, 69.666664f, 70.0f, 70.333336f, //235 to 239
70.666664f, 71.0f, 71.5f, 72.0f, 72.5f, //240 to 244
73.0f, 73.333336f, 73.666664f, 74.0f, 74.333336f, //245 to 249
74.666664f, 75.0f, 75.5f, 76.0f, 76.333336f, //250 to 254
76.666664f, 77.0f, 77.5f, 78.0f, 78.333336f, //255 to 259
78.666664f, 79.0f, 79.5f, 80.0f, 80.333336f, //260 to 264
80.666664f, 81.0f, 81.5f, 82.0f, 82.333336f, //265 to 269
82.666664f, 83.0f, 83.5f, 84.0f, 84.333336f, //270 to 274
84.666664f, 85.0f, 85.5f, 86.0f, 86.5f, //275 to 279
87.0f, 87.333336f, 87.666664f, 88.0f, 88.5f, //280 to 284
89.0f, 89.333336f, 89.666664f, 90.0f, 90.5f, //285 to 289
91.0f, 91.5f, 92.0f, 92.333336f, 92.666664f, //290 to 294
93.0f, 93.5f, 94.0f, 94.5f, 95.0f, //295 to 299
95.333336f, 95.666664f, 96.0f, 96.5f, 97.0f, //300 to 304
97.5f, 98.0f, 98.333336f, 98.666664f, 99.0f, //305 to 309
99.5f, 100.0f, 100.5f, 101.0f, 101.5f, //310 to 314
102.0f, 102.5f, 103.0f, 103.333336f, 103.666664f, //315 to 319
104.0f, 104.333336f, 104.666664f, 105.0f, 105.5f, //320 to 324
106.0f, 106.5f, 107.0f, 108.0f, 108.333336f, //325 to 329
108.666664f, 109.0f, 109.333336f, 109.666664f, 110.0f, //330 to 334
110.5f, 111.0f, 111.5f, 112.0f, 112.5f, //335 to 339
113.0f, 113.333336f, 113.666664f, 114.0f, 114.5f, //340 to 344
115.0f, 115.5f, 116.0f, 116.5f, 117.0f, //345 to 349
117.5f, 118.0f, 118.333336f, 118.666664f, 119.0f, //350 to 354
119.5f, 120.0f, 120.5f, 121.0f, 121.5f, //355 to 359
122.0f, 122.5f, 123.0f, 123.333336f, 123.666664f, //360 to 364
124.0f, 124.5f, 125.0f, 125.5f, 126.0f, //365 to 369
126.5f, 127.0f, 127.5f, 128.0f, 128.5f, //370 to 374
129.0f, 129.5f, 130.0f, 130.33333f, 130.66667f, //375 to 379
131.0f, 131.5f, 132.0f, 132.5f, 133.0f, //380 to 384
133.5f, 134.0f, 134.5f, 135.0f, 135.5f, //385 to 389
136.0f, 136.5f, 137.0f, 137.5f, 138.0f, //390 to 394
138.5f, 139.0f, 139.5f, 140.0f, 140.33333f, //395 to 399
140.66667f, 141.0f, 141.5f, 142.0f, 142.5f, //400 to 404
143.0f, 143.5f, 144.0f, 144.5f, 145.0f, //405 to 409
145.5f, 146.0f, 146.5f, 147.0f, 147.5f, //410 to 414
148.0f, 148.5f, 149.0f, 149.5f, 150.0f, //415 to 419
150.5f, 151.0f, 151.5f, 152.0f, 152.5f, //420 to 424
153.0f, 153.5f, 154.0f, 154.5f, 155.0f, //425 to 429
155.5f, 156.0f, 156.5f, 157.0f, 157.5f, //430 to 434
158.0f, 158.5f, 159.0f, 159.5f, 160.0f, //435 to 439
160.5f, 161.0f, 161.5f, 162.0f, 162.5f, //440 to 444
163.0f, 163.5f, 164.0f, 164.5f, 165.0f, //445 to 449
165.5f, 166.0f, 166.5f, 167.0f, 167.5f, //450 to 454
168.0f, 168.5f, 169.0f, 169.5f, 170.0f, //455 to 459
170.5f, 171.0f, 171.5f, 172.0f, 172.5f, //460 to 464
173.0f, 173.5f, 174.0f, 174.5f, 175.0f, //465 to 469
176.0f, 176.5f, 177.0f, 177.5f, 178.0f, //470 to 474
178.5f, 179.0f, 179.5f, 180.0f, 180.5f, //475 to 479
181.0f, 181.5f, 182.0f, 182.5f, 183.0f, //480 to 484
183.5f, 184.0f, 184.5f, 185.0f, 185.5f, //485 to 489
186.0f, 187.0f, 187.5f, 188.0f, 188.5f, //490 to 494
189.0f, 189.5f, 190.0f, 190.5f, 191.0f, //495 to 499
191.5f, 192.0f, 192.5f, 193.0f, 193.5f, //500 to 504
194.0f, 194.5f, 195.0f, 196.0f, 196.5f, //505 to 509
197.0f, 197.5f, 198.0f, 198.5f, 199.0f, //510 to 514
199.5f, 200.0f, 200.5f, 201.0f, 201.5f, //515 to 519
202.0f, 203.0f, 203.5f, 204.0f, 204.5f, //520 to 524
205.0f, 205.5f, 206.0f, 206.5f, 207.0f, //525 to 529
207.5f, 208.0f, 209.0f, 209.5f, 210.0f, //530 to 534
210.5f, 211.0f, 211.5f, 212.0f, 212.5f, //535 to 539
213.0f, 214.0f, 214.5f, 215.0f, 215.5f, //540 to 544
216.0f, 216.5f, 217.0f, 217.5f, 218.0f, //545 to 549
218.5f, 219.0f, 220.0f, 220.5f, 221.0f, //550 to 554
221.5f, 222.0f, 222.5f, 223.0f, 224.0f, //555 to 559
224.5f, 225.0f, 225.5f, 226.0f, 226.5f, //560 to 564
227.0f, 227.5f, 228.0f, 229.0f, 229.5f, //565 to 569
230.0f, 230.5f, 231.0f, 231.5f, 232.0f, //570 to 574
233.0f, 233.5f, 234.0f, 234.5f, 235.0f, //575 to 579
235.5f, 236.0f, 237.0f, 237.5f, 238.0f, //580 to 584
238.5f, 239.0f, 239.5f, 240.0f, 241.0f, //585 to 589
241.5f, 242.0f, 242.5f, 243.0f, 243.5f, //590 to 594
244.0f, 245.0f, 245.5f, 246.0f, 246.5f, //595 to 599
247.0f}; //600
private SkillsBase skillsBase;
private AtomicInteger numTrains = new AtomicInteger();
//Skill% before trains and before any effects or item bonuses
private float baseAmountBeforeMods;
//Skill% after trains but before any effects or item bonuses
private float modifiedAmountBeforeMods;
private boolean isMobOwner = false;
//Skill% before trains but after any effects or item bonuses
private float baseAmount;
//Skill% after trains and after any effects or item bonuses
private float modifiedAmount;
private int ownerUID;
private boolean trained = false;
private int requiredLevel = 0;
/**
* No Table ID Constructor
*/
public CharacterSkill(SkillsBase skillsBase, PlayerCharacter pc) {
super();
this.skillsBase = skillsBase;
this.numTrains.set(0);
this.ownerUID = pc.getObjectUUID();
calculateBaseAmount();
calculateModifiedAmount(false);
}
/**
* Normal Constructor
*/
public CharacterSkill(SkillsBase skillsBase, PlayerCharacter pc, int newUUID) {
super(newUUID);
this.skillsBase = skillsBase;
this.numTrains.set(0);
this.ownerUID = pc.getObjectUUID();
this.trained = true;
calculateBaseAmount();
calculateModifiedAmount(false);
}
/**
* ResultSet Constructor
*/
public CharacterSkill(ResultSet rs, PlayerCharacter pc) throws SQLException {
super(rs);
int skillsBaseID = rs.getInt("SkillsBaseID");
this.skillsBase = DbManager.SkillsBaseQueries.GET_BASE(skillsBaseID);
this.numTrains.set(rs.getShort("trains"));
this.ownerUID = pc.getObjectUUID();
calculateBaseAmount();
calculateModifiedAmount(false);
}
public CharacterSkill(SkillsBase sb, Mob mob, int trains) {
super();
this.skillsBase = sb;
this.numTrains.set(trains);
this.ownerUID = mob.getObjectUUID();
this.isMobOwner = true;
boolean isGuard = mob.agentType.equals(mbEnums.AIAgentType.GUARDCAPTAIN) || mob.agentType.equals(mbEnums.AIAgentType.GUARDMINION);
calculateMobBaseAmount(isGuard);
calculateModifiedAmount(isGuard);
}
public CharacterSkill(ResultSet rs) throws SQLException {
super(rs);
int skillsBaseID = rs.getInt("SkillsBaseID");
this.skillsBase = DbManager.SkillsBaseQueries.GET_BASE(skillsBaseID);
this.numTrains.set(rs.getShort("trains"));
this.ownerUID = rs.getInt("CharacterID");
// this.owner = DbManager.PlayerCharacterQueries.GET_PLAYER_CHARACTER(rs.getInt("CharacterID"));
calculateBaseAmount();
calculateModifiedAmount(false);
}
public static AbstractCharacter GetOwner(CharacterSkill cs) {
if (cs.ownerUID == 0)
return null;
if (cs.isMobOwner)
return Mob.getFromCache(cs.ownerUID);
else
return PlayerCharacter.getFromCache(cs.ownerUID);
}
/*
* Getters
*/
public static float getATR(AbstractCharacter ac, String name) {
if (ac == null)
return 0f;
float atr;
ConcurrentHashMap<String, CharacterSkill> skills = ac.getSkills();
CharacterSkill skill = skills.get(name);
if (skill != null)
atr = skill.getATR(ac);
else {
float mast = CharacterSkill.getQuickMastery(ac, name);
atr = (((int) mast * 7) + (ac.getStatDexCurrent() / 2));
}
//apply effect mods
PlayerBonuses bonus = ac.getBonuses();
if (bonus == null)
return atr;
atr += bonus.getFloat(ModType.OCV, SourceType.None);
float pos_Bonus = bonus.getFloatPercentPositive(ModType.OCV, SourceType.None);
atr *= (1 + pos_Bonus);
//rUNES will already be applied
// atr *= (1 + ((float)bonus.getShort("rune.Attack") / 100)); //precise
float neg_Bonus = bonus.getFloatPercentNegative(ModType.OCV, SourceType.None);
atr *= (1 + neg_Bonus);
return atr;
}
public static void serializeForClientMsg(CharacterSkill characterSkill, ByteBufferWriter writer) {
if (characterSkill.skillsBase == null) {
Logger.error("SkillsBase not found for skill " + characterSkill.getObjectUUID());
writer.putInt(0);
writer.putInt(0);
} else {
writer.putInt(characterSkill.skillsBase.getToken());
writer.putInt(characterSkill.numTrains.get());
}
}
/**
* @ This updates all Base Skill Amouts for a player
* Convienence method
*/
public static void updateAllBaseAmounts(PlayerCharacter pc) {
if (pc == null)
return;
ConcurrentHashMap<String, CharacterSkill> skills = pc.getSkills();
Iterator<String> it = skills.keySet().iterator();
while (it.hasNext()) {
String name = it.next();
CharacterSkill cs = skills.get(name);
if (cs != null)
cs.calculateBaseAmount();
// Logger.info("CharacterSkill", pc.getName() + ", skill: " +
// cs.getSkillsBase().getName() + ", trains: " + cs.numTrains +
// ", base: " + cs.baseAmount + ", mod: " + cs.modifiedAmount);
}
//Recalculate ATR, damage and defense
//pc.calculateAtrDefenseDamage();
//recalculate movement bonus
//pc.calculateSpeedMod();
}
/**
* @ This updates all Modified skill Amounts for a player
* Convienence method
*/
public static void updateAllModifiedAmounts(PlayerCharacter pc) {
if (pc == null)
return;
ConcurrentHashMap<String, CharacterSkill> skills = pc.getSkills();
Iterator<String> it = skills.keySet().iterator();
while (it.hasNext()) {
String name = it.next();
CharacterSkill cs = skills.get(name);
if (cs != null)
cs.calculateModifiedAmount(false);
}
//Recalculate ATR, damage and defense
//pc.calculateAtrDefenseDamage();
}
public static int getTrainsAvailable(PlayerCharacter pc) {
if (pc == null)
return 0;
if (pc.race == null || pc.baseClass == null) {
Logger.error("Race or BaseClass not found for player " + pc.getObjectUUID());
return 0;
}
int raceBonus = 0;
int baseMod = 0;
int promoMod = 6;
int available = 0;
//get racial bonus;
if (pc.race.getRaceType().equals(mbEnums.RaceType.HUMANMALE) ||
pc.race.getRaceType().equals(mbEnums.RaceType.HUMANFEMALE))
raceBonus = 1; //Human racial bonus;
//get base class trains
if (pc.baseClass.getObjectUUID() == 2500 || pc.baseClass.getObjectUUID() == 2502) {
baseMod = 4; //Fighter or Rogue
} else {
baseMod = 5; //Healer or Mage
}
int level = pc.getLevel();
if (level > 74)
available = 62 + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus));
else if (level > 69)
available = ((level - 69) * 3) + 45 + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus));
else if (level > 64)
available = ((level - 64) * 4) + 25 + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus));
else if (level > 59) //Between 60 and 65
available = ((level - 59) * 5) + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus));
else if (level > 10) //Between 11 and 59
available = ((level - 10) * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus));
// available = (level - 59) + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus));
else if (level == 10 && (pc.getPromotionClass() != null)) //10 but promoted
available = (promoMod + baseMod + raceBonus) + (9 * (baseMod + raceBonus));
else //not promoted
available = (level - 1) * (baseMod + raceBonus);
//next subtract trains in any skills
ConcurrentHashMap<String, CharacterSkill> skills = pc.getSkills();
Iterator<String> it = skills.keySet().iterator();
while (it.hasNext()) {
String name = it.next();
CharacterSkill cs = skills.get(name);
if (cs != null)
available -= cs.numTrains.get();
}
//TODO subtract any trains from powers
ConcurrentHashMap<Integer, CharacterPower> powers = pc.getPowers();
for (CharacterPower power : powers.values()) {
if (power != null)
available -= power.getTrains();
}
if (MBServerStatics.BONUS_TRAINS_ENABLED) {
available += 1000;
}
// if (available < 0) {
//TODO readd this error log after test
// Logger.error("CharacterSkill.getTrainsAvailable", "Number of trains available less then 0 for Player " + pc.getUUID());
// available = 0;
// }
//return what's left
return available;
}
/**
* @ Returns mastery base when mastery not granted
* to player. For calculating damage correctly
*/
public static float getQuickMastery(AbstractCharacter pc, String mastery) {
SkillsBase sb = SkillsBase.getFromCache(mastery);
if (sb == null) {
sb = DbManager.SkillsBaseQueries.GET_BASE_BY_NAME(mastery);
if (sb == null) {
//Logger.error("CharacterSkill.getQuickMastery", "Unable to find skillsbase of name " + mastery);
return 0f;
}
}
float bonus = 0f;
SourceType sourceType = SourceType.GetSourceType(sb.getNameNoSpace());
if (pc.getBonuses() != null) {
//Get bonuses from runes
bonus = pc.getBonuses().getSkillBonus(sb.sourceType);
}
float base = 4.75f;
base += (0.0025f * sb.getStrMod() * pc.getStatStrCurrent());
base += (0.0025f * sb.getDexMod() * pc.getStatDexCurrent());
base += (0.0025f * sb.getConMod() * pc.getStatConCurrent());
base += (0.0025f * sb.getIntMod() * pc.getStatIntCurrent());
base += (0.0025f * sb.getSpiMod() * pc.getStatSpiCurrent());
return base + bonus;
}
/*
* This iterates through players runes and adds and removes skills as needed
* Don't Call this directly. Instead call pc.calculateSkills().
*/
public static void calculateSkills(AbstractCharacter absChar) {
if (absChar == null && absChar.getObjectType().equals(mbEnums.GameObjectType.PlayerCharacter) == false)
return;
PlayerCharacter pc = (PlayerCharacter) absChar;
ConcurrentHashMap<String, CharacterSkill> skills = pc.getSkills();
//First add skills that don't exist
Race race = pc.race;
if (race != null) {
CharacterSkill.grantSkills(race.getSkillsGranted(), pc);
} else
Logger.error("Failed to find Race for player " + pc.getObjectUUID());
BaseClass bc = pc.baseClass;
if (bc != null) {
CharacterSkill.grantSkills(bc.getSkillsGranted(), pc);
} else
Logger.error("Failed to find BaseClass for player " + pc.getObjectUUID());
PromotionClass promo = pc.promotionClass;
if (promo != null)
CharacterSkill.grantSkills(promo.getSkillsGranted(), pc);
ArrayList<CharacterRune> runes = pc.getRunes();
if (runes != null) {
for (CharacterRune rune : runes) {
CharacterSkill.grantSkills(rune.getSkillsGranted(), pc);
}
} else
Logger.error("Failed to find Runes list for player " + pc.getObjectUUID());
//next remove any skills that no longer belong
Iterator<CharacterSkill> it = skills.values().iterator();
while (it.hasNext()) {
CharacterSkill cs = it.next();
if (cs == null)
continue;
SkillsBase sb = cs.skillsBase;
if (sb == null) {
DbManager.CharacterSkillQueries.DELETE_SKILL(cs.getObjectUUID());
it.remove();
continue;
}
boolean valid = false;
if (CharacterSkill.skillAllowed(sb.getObjectUUID(), race.getSkillsGranted(), pc))
continue;
if (CharacterSkill.skillAllowed(sb.getObjectUUID(), bc.getSkillsGranted(), pc))
continue;
if (promo != null)
if (CharacterSkill.skillAllowed(sb.getObjectUUID(), promo.getSkillsGranted(), pc))
continue;
for (CharacterRune rune : runes) {
if (CharacterSkill.skillAllowed(sb.getObjectUUID(), rune.getSkillsGranted(), pc)) {
valid = true;
continue;
}
}
//if skill doesn't belong to any runes, then remove it
if (!valid) {
DbManager.CharacterSkillQueries.DELETE_SKILL(cs.getObjectUUID());
it.remove();
}
}
CharacterSkill.updateAllBaseAmounts(pc);
CharacterSkill.updateAllModifiedAmounts(pc);
CharacterPower.calculatePowers(pc);
}
/*
*This grants skills for specific runes
*/
private static void grantSkills(ArrayList<SkillReq> skillsGranted, PlayerCharacter pc) {
ConcurrentHashMap<String, CharacterSkill> skills = pc.getSkills();
if (skills == null)
return;
for (SkillReq skillreq : skillsGranted) {
SkillsBase skillsBase = skillreq.getSkillsBase();
//If player not high enough level for skill, then skip
if (pc.getLevel() < skillreq.getLevel())
continue;
//If player doesn't have prereq skills high enough then skip
boolean valid = true;
for (byte prereqSkill : skillreq.getSkillReqs()) {
SkillsBase sb = null;
sb = DbManager.SkillsBaseQueries.GET_BASE(prereqSkill);
if (sb != null) {
if (skills.containsKey(sb.getName())) {
if (validForWarrior(pc, skills.get(sb.getName()), skillreq)) {
valid = true;
break; //add if any prereq skills met
} else if (skills.get(sb.getName()).modifiedAmountBeforeMods >= 80) {
valid = true;
break; //add if any prereq skills met
// allow blade masters to use blade master without training sword above 80..
} else if (skillsBase.getObjectUUID() == 9) {
valid = true;
break;
}
}
} else {
Logger.error("Failed to find SkillsBase of ID " + prereqSkill);
}
valid = false;
}
// Throwing does not need axe,dagger, or hammer at 80%
if (skillreq.getSkillID() == 43)
valid = true;
if (!valid)
continue;
//Skill valid for player. Add if don't already have
if (skillsBase != null) {
if (!skills.containsKey(skillsBase.getName())) {
CharacterSkill newSkill = new CharacterSkill(skillsBase, pc);
CharacterSkill cs = null;
try {
cs = DbManager.CharacterSkillQueries.ADD_SKILL(newSkill);
} catch (Exception e) {
cs = null;
}
if (cs != null) {
cs.requiredLevel = (int) skillreq.getLevel();
skills.put(skillsBase.getName(), cs);
} else
Logger.error("Failed to add CharacterSkill to player " + pc.getObjectUUID());
} else {
CharacterSkill cs = skills.get(skillsBase.getName());
if (cs != null && cs.requiredLevel == 0) {
cs.requiredLevel = (int) skillreq.getLevel();
}
}
} else
Logger.error("Failed to find SkillsBase for SkillReq " + skillreq.getObjectUUID());
}
}
private static boolean validForWarrior(PlayerCharacter pc, CharacterSkill skill, SkillReq skillreq) {
if (pc.getPromotionClass() == null || pc.getPromotionClass().getObjectUUID() != 2518 || skill == null || skillreq == null)
return false; //not a warrior
int sID = (skill.skillsBase != null) ? skill.skillsBase.getObjectUUID() : 0;
switch (skillreq.getSkillID()) {
case 3: //Axe mastery
case 19: //Great axe mastery
return (sID == 4) ? (skill.modifiedAmountBeforeMods >= 50) : false;
case 15: //Dagger mastery
return (sID == 16) ? (skill.modifiedAmountBeforeMods >= 50) : false;
case 20: //Great hammer mastery
case 22: //Hammer mastery
return (sID == 23) ? (skill.modifiedAmountBeforeMods >= 50) : false;
case 28: //Polearm mastery
return (sID == 29) ? (skill.modifiedAmountBeforeMods >= 50) : false;
case 21: //Great sword mastery
case 39: //Sword mastery
return (sID == 40) ? (skill.modifiedAmountBeforeMods >= 50) : false;
case 34: //Spear mastery
return (sID == 35) ? (skill.modifiedAmountBeforeMods >= 50) : false;
case 36: //Staff mastery
return (sID == 37) ? (skill.modifiedAmountBeforeMods >= 50) : false;
case 45: //Unarmed combat mastery
return (sID == 46) ? (skill.modifiedAmountBeforeMods >= 50) : false;
case 40:
return true;
case 9:
return true;
default:
}
return false;
}
/*
* This verifies if a skill is valid for a players rune
*/
private static boolean skillAllowed(int UUID, ArrayList<SkillReq> skillsGranted, PlayerCharacter pc) {
ConcurrentHashMap<String, CharacterSkill> skills = pc.getSkills();
for (SkillReq skillreq : skillsGranted) {
SkillsBase sb = skillreq.getSkillsBase();
if (sb != null) {
if (sb.getObjectUUID() == UUID) {
if (skillreq.getLevel() <= pc.getLevel()) {
SkillsBase sbp = null;
if (skillreq.getSkillReqs().size() == 0)
return true;
for (byte prereqSkill : skillreq.getSkillReqs()) {
sbp = DbManager.SkillsBaseQueries.GET_BASE(prereqSkill);
if (sbp != null && skills.containsKey(sbp.getName())) {
if (validForWarrior(pc, skills.get(sbp.getName()), skillreq)) {
return true;
} else if (skills.get(sbp.getName()).modifiedAmountBeforeMods >= 80)
return true;
}
}
if (skillreq.getSkillID() == 43)
return true;
if (skillreq.getSkillID() == 9)
return true;
}
}
} else
Logger.error("Failed to find SkillsBase for SkillReq " + skillreq.getObjectUUID());
}
return false;
}
//Print skills for player for debugging
public static void printSkills(PlayerCharacter pc) {
if (pc == null)
return;
ConcurrentHashMap<String, CharacterSkill> skills = pc.getSkills();
String out = "Player: " + pc.getObjectUUID() + ", SkillCount: " + skills.size();
Iterator<String> it = skills.keySet().iterator();
while (it.hasNext()) {
String name = it.next();
out += ", " + name;
}
Logger.info(out);
}
public static int getMaxTrains(int intt) {
if (intt > 0 && intt < 191)
return CharacterSkill.maxTrains[intt];
else
return (int) (33 + 1.25 * intt - 0.005 * Math.pow(intt, 2));
}
private float getATR(AbstractCharacter ac) {
return (((int) this.modifiedAmount * 7) + (ac.getStatDexCurrent() / 2));
}
public synchronized boolean train(PlayerCharacter pc) {
if (pc == null || this.skillsBase == null)
return false;
boolean running = false;
//trying out a table lookup
int intt = (int) pc.statIntBase;
int maxTrains = 0;
if (intt > 0 && intt < 191)
maxTrains = CharacterSkill.maxTrains[intt];
else
maxTrains = (int) (33 + 1.25 * (int) pc.statIntBase - 0.005 * Math.pow((int) pc.statIntBase, 2));
int oldTrains = this.numTrains.get();
boolean succeeded = true;
if (pc.getTrainsAvailable() <= 0)
return false;
if (oldTrains == maxTrains) //at gold, stop here
return false;
else if (oldTrains > maxTrains) //catch incase we somehow go over
this.numTrains.set(maxTrains);
else //add the train
succeeded = this.numTrains.compareAndSet(oldTrains, oldTrains + 1);
if (this.numTrains.get() > maxTrains) { //double check not over max trains
this.numTrains.set(maxTrains);
succeeded = false;
}
if (succeeded) {
this.trained = true;
//subtract from trains available
pc.modifyTrainsAvailable(-1);
//update database
pc.addDatabaseJob("Skills", MBServerStatics.THIRTY_SECONDS);
//recalculate this skill
calculateBaseAmount();
calculateModifiedAmount(false);
//see if any new skills or powers granted
pc.calculateSkills();
//reapply all bonuses
pc.applyBonuses();
//update cache if running trains change
if (running)
pc.incVer();
return true;
} else
return false;
}
public float getTrainingCost(PlayerCharacter pc, NPC trainer) {
int charLevel = pc.getLevel();
int skillRank = this.getNumTrains() - 1 + this.requiredLevel;
float baseCost = 15 * this.requiredLevel; //TODO GET BASE COSTS OF SKILLS.
float sellPercent = -.20f;
float cost;
float const5;
int const2 = 1;
float const3 = 50;
float const4 = const3 + const2;
if (charLevel > 50)
const5 = 50 / const4;
else
const5 = charLevel / const4;
const5 = 1 - const5;
const5 = (float) (Math.log(const5) / Math.log(2) * .75f);
float rounded5 = Math.round(const5);
const5 = rounded5 - const5;
const5 *= -1;
const5 = (float) (Math.pow(2, const5) - 1);
const5 += 1;
const5 = Math.scalb(const5, (int) rounded5);
const5 *= (charLevel - skillRank);
const5 *= sellPercent;
const5 = (float) (Math.log(const5) / Math.log(2) * 3);
rounded5 = Math.round(const5);
const5 = rounded5 - const5;
const5 *= -1;
const5 = (float) (Math.pow(2, const5) - 1);
const5 += 1;
const5 = Math.scalb(const5, (int) rounded5);
const5 += 1;
cost = const5 * (baseCost);
if (Float.isNaN(cost))
cost = baseCost;
return cost;
}
//Call this to refine skills and recalculate everything for pc.
public boolean refine(PlayerCharacter pc) {
return refine(pc, true);
}
public boolean refine(PlayerCharacter pc, boolean recalculate) {
if (pc == null || this.skillsBase == null)
return false;
int oldTrains = this.numTrains.get();
boolean succeeded = true;
if (this.getNumTrains() < 1)
return false;
succeeded = this.numTrains.compareAndSet(oldTrains, oldTrains - 1);
if (succeeded) {
this.trained = true;
//add to trains available
pc.modifyTrainsAvailable(1);
//update database
pc.addDatabaseJob("Skills", MBServerStatics.THIRTY_SECONDS);
if (recalculate) {
//recalculate this skill
calculateBaseAmount();
calculateModifiedAmount(false);
//see if any skills or powers removed
pc.calculateSkills();
//reapply all bonuses
pc.applyBonuses();
}
return true;
} else
return false;
}
/*
* Serializing
*/
public boolean reset(PlayerCharacter pc, boolean recalculate) {
if (pc == null || this.skillsBase == null)
return false;
int oldTrains = this.numTrains.get();
boolean succeeded = true;
if (this.getNumTrains() < 1)
return false;
succeeded = this.numTrains.compareAndSet(oldTrains, 0);
if (succeeded) {
this.trained = true;
//add to trains available
pc.modifyTrainsAvailable(oldTrains);
//update database
pc.addDatabaseJob("Skills", MBServerStatics.THIRTY_SECONDS);
if (recalculate) {
//recalculate this skill
calculateBaseAmount();
calculateModifiedAmount(false);
//see if any skills or powers removed
pc.calculateSkills();
//reapply all bonuses
pc.applyBonuses();
}
return true;
} else
return false;
}
/*
* Returns Skill Base for skill
*/
public SkillsBase getSkillsBase() {
return this.skillsBase;
}
/*
* Returns number of trains in skill
*/
public int getNumTrains() {
return this.numTrains.get();
}
/*
* Returns Skill% before trains added
*/
public float getBaseAmount() {
return this.baseAmount;
}
/*
* Returns Skill% after trains added
*/
public float getModifiedAmount() {
return this.modifiedAmount;
}
/*
* Returns Skill% before trains added, minus bonus from equip and effects
*/
public float getBaseAmountBeforeMods() {
return this.baseAmountBeforeMods;
}
/*
* Returns Skill% after trains added, minus bonus from equip and effects
*/
public float getModifiedAmountBeforeMods() {
return this.modifiedAmountBeforeMods;
}
public String getName() {
if (this.skillsBase != null)
return this.skillsBase.getName();
return "";
}
public int getToken() {
if (this.skillsBase != null)
return this.skillsBase.getToken();
return 0;
}
public boolean isTrained() {
return trained;
}
public void syncTrains() {
this.trained = false;
}
/**
* @ Calculates Base Skill Percentage
* Call this when stats change for a player
*/
public void calculateBaseAmount() {
if (CharacterSkill.GetOwner(this) == null) {
Logger.error("owner not found for owner uuid : " + this.ownerUID);
this.baseAmount = 1;
this.modifiedAmount = 1;
return;
}
if (this.skillsBase == null) {
Logger.error("SkillsBase not found for skill " + this.getObjectUUID());
this.baseAmount = 1;
this.modifiedAmount = 1;
return;
}
//Get any rune bonus
float bonus = 0f;
//runes will already be calculated
if (CharacterSkill.GetOwner(this).getBonuses() != null) {
//Get bonuses from runes
bonus = CharacterSkill.GetOwner(this).getBonuses().getSkillBonus(this.skillsBase.sourceType);
}
//Get Base skill for unmodified stats
float base = 7f;
float statMod = 0.5f;
if (this.skillsBase.getStrMod() > 0)
statMod += (float) this.skillsBase.getStrMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statStrBase / 100f;
if (this.skillsBase.getDexMod() > 0)
statMod += (float) this.skillsBase.getDexMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statDexBase / 100f;
if (this.skillsBase.getConMod() > 0)
statMod += (float) this.skillsBase.getConMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statConBase / 100f;
if (this.skillsBase.getIntMod() > 0)
statMod += (float) this.skillsBase.getIntMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statIntBase / 100f;
if (this.skillsBase.getSpiMod() > 0)
statMod += (float) this.skillsBase.getSpiMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statSpiBase / 100f;
if (statMod < 1)
statMod = 1f;
else if (statMod > 600)
statMod = 600f;
base += CharacterSkill.baseSkillValues[(int) statMod];
if (base + bonus < 1f)
this.baseAmountBeforeMods = 1f;
else
this.baseAmountBeforeMods = base + bonus;
this.modifiedAmountBeforeMods = Math.round(this.baseAmountBeforeMods + calculateAmountAfterTrains());
}
public void calculateMobBaseAmount(boolean isGuard) {
if (!isGuard) {
if (CharacterSkill.GetOwner(this) == null) {
Logger.error("owner not found for owner uuid : " + this.ownerUID);
this.baseAmount = 1;
this.modifiedAmount = 1;
return;
}
if (this.skillsBase == null) {
Logger.error("SkillsBase not found for skill " + this.getObjectUUID());
this.baseAmount = 1;
this.modifiedAmount = 1;
return;
}
}
//Get any rune bonus
float bonus = 0f;
//TODO SKILLS RUNES
float base = 7f;
float statMod = 0.5f;
if (CharacterSkill.GetOwner(this) != null && CharacterSkill.GetOwner(this).getBonuses() != null) {
//Get bonuses from runes
bonus = CharacterSkill.GetOwner(this).getBonuses().getSkillBonus(this.skillsBase.sourceType);
//Get Base skill for unmodified stats
if (this.skillsBase.getStrMod() > 0)
statMod += (float) this.skillsBase.getStrMod() * (float) ((Mob) CharacterSkill.GetOwner(this)).getRuneAttribute(mbEnums.AttributeType.Strength) / 100f;
if (this.skillsBase.getDexMod() > 0)
statMod += (float) this.skillsBase.getDexMod() * (float) ((Mob) CharacterSkill.GetOwner(this)).getRuneAttribute(mbEnums.AttributeType.Dexterity) / 100f;
if (this.skillsBase.getConMod() > 0)
statMod += (float) this.skillsBase.getConMod() * (float) ((Mob) CharacterSkill.GetOwner(this)).getRuneAttribute(mbEnums.AttributeType.Constitution) / 100f;
if (this.skillsBase.getIntMod() > 0)
statMod += (float) this.skillsBase.getIntMod() * (float) ((Mob) CharacterSkill.GetOwner(this)).getRuneAttribute(mbEnums.AttributeType.Intelligence) / 100f;
if (this.skillsBase.getSpiMod() > 0)
statMod += (float) this.skillsBase.getSpiMod() * (float) ((Mob) CharacterSkill.GetOwner(this)).getRuneAttribute(mbEnums.AttributeType.Spirit) / 100f;
if (statMod < 1)
statMod = 1f;
else if (statMod > 600)
statMod = 600f;
}
base += CharacterSkill.baseSkillValues[(int) statMod];
if (base + bonus < 1f)
this.baseAmountBeforeMods = 1f;
else
this.baseAmountBeforeMods = base + bonus;
this.modifiedAmountBeforeMods = (int) (this.baseAmountBeforeMods + calculateAmountAfterTrains());
}
public void calculateModifiedAmount(boolean isGuard) {
if (!isGuard) {
if (CharacterSkill.GetOwner(this) == null || this.skillsBase == null) {
Logger.error("owner or SkillsBase not found for skill " + this.getObjectUUID());
this.baseAmount = 1;
this.modifiedAmount = 1;
return;
}
}
//Get any rune bonus
float bonus = 0f;
float base = 7f;
float statMod = 0.5f;
if (CharacterSkill.GetOwner(this) != null && CharacterSkill.GetOwner(this).getBonuses() != null) {
//Get bonuses from runes
bonus = CharacterSkill.GetOwner(this).getBonuses().getSkillBonus(this.skillsBase.sourceType);
//Get Base skill for modified stats
//TODO this fomula needs verified
if (this.skillsBase.getStrMod() > 0)
statMod += (float) this.skillsBase.getStrMod() * (float) CharacterSkill.GetOwner(this).getStatStrCurrent() / 100f;
if (this.skillsBase.getDexMod() > 0)
statMod += (float) this.skillsBase.getDexMod() * (float) CharacterSkill.GetOwner(this).getStatDexCurrent() / 100f;
if (this.skillsBase.getConMod() > 0)
statMod += (float) this.skillsBase.getConMod() * (float) CharacterSkill.GetOwner(this).getStatConCurrent() / 100f;
if (this.skillsBase.getIntMod() > 0)
statMod += (float) this.skillsBase.getIntMod() * (float) CharacterSkill.GetOwner(this).getStatIntCurrent() / 100f;
if (this.skillsBase.getSpiMod() > 0)
statMod += (float) this.skillsBase.getSpiMod() * (float) CharacterSkill.GetOwner(this).getStatSpiCurrent() / 100f;
if (statMod < 1)
statMod = 1f;
else if (statMod > 600)
statMod = 600f;
}
base += CharacterSkill.baseSkillValues[(int) statMod];
SourceType sourceType = SourceType.GetSourceType(this.skillsBase.getNameNoSpace());
//Get any rune, effect and item bonus
if (CharacterSkill.GetOwner(this) != null && CharacterSkill.GetOwner(this).getBonuses() != null) {
//add bonuses from effects/items and runes
base += bonus + CharacterSkill.GetOwner(this).getBonuses().getFloat(ModType.Skill, sourceType);
}
if (base < 1f)
this.baseAmount = 1f;
else
this.baseAmount = base;
float modAmount = this.baseAmount + calculateAmountAfterTrains();
if (CharacterSkill.GetOwner(this) != null && CharacterSkill.GetOwner(this).getBonuses() != null) {
//Multiply any percent bonuses
modAmount *= (1 + CharacterSkill.GetOwner(this).getBonuses().getFloatPercentAll(ModType.Skill, sourceType));
}
this.modifiedAmount = (int) (modAmount);
}
/**
* @ Calculates Modified Skill Percentage
* Call this when number of trains change for skill
*/
public float calculateAmountAfterTrains() {
if (this.skillsBase == null) {
Logger.error("SkillsBase not found for skill " + this.getObjectUUID());
this.modifiedAmount = this.baseAmount;
}
int amount;
int trains = this.numTrains.get();
if (trains < 10)
amount = (trains * 2);
else if (trains < 90)
amount = 10 + trains;
else if (trains < 134)
amount = 100 + ((trains - 90) / 2);
else
amount = 122 + ((trains - 134) / 3);
return amount;
}
public int getSkillPercentFromAttributes() {
AbstractCharacter ac = CharacterSkill.GetOwner(this);
if (ac == null)
return 0;
float statMod = 0;
if (this.skillsBase.getStrMod() > 0) {
float strengthModPercent = (float) this.skillsBase.getStrMod() * .01f;
strengthModPercent *= ac.getStatStrCurrent() * .01f + .6f;
statMod += strengthModPercent;
}
if (this.skillsBase.getDexMod() > 0) {
float dexModPercent = (float) this.skillsBase.getDexMod() * .01f;
dexModPercent *= ac.getStatDexCurrent() * .01f + .6f;
statMod += dexModPercent;
}
if (this.skillsBase.getConMod() > 0) {
float conModPercent = (float) this.skillsBase.getConMod() * .01f;
conModPercent *= ac.getStatConCurrent() * .01f + .6f;
statMod += conModPercent;
}
if (this.skillsBase.getIntMod() > 0) {
float intModPercent = (float) this.skillsBase.getIntMod() * .01f;
intModPercent *= ac.getStatIntCurrent() * .01f + .6f;
statMod += intModPercent;
}
if (this.skillsBase.getSpiMod() > 0) {
float spiModPercent = (float) this.skillsBase.getSpiMod() * .01f;
spiModPercent *= ac.getStatSpiCurrent() * .01f + .6f;
statMod += spiModPercent;
}
statMod = (float) (Math.pow(statMod, 1.5f) * 15f);
if (statMod < 1)
statMod = 1f;
else if (statMod > 600)
statMod = 600f;
return (int) statMod;
}
public int getSkillPercentFromTrains() {
int trains = this.numTrains.get();
if (trains <= 10)
return 2 * trains;
if (trains <= 90)
return trains + 10;
if (trains > 130)
return (int) (120 - ((trains - 130) * -0.33000001));
return (int) (100 - ((trains - 90) * -0.5));
}
public int getTotalSkillPercet() {
AbstractCharacter ac = CharacterSkill.GetOwner(this);
if (ac == null)
return 0;
float bonus = 0f;
SourceType sourceType = SourceType.GetSourceType(this.skillsBase.getNameNoSpace());
if (CharacterSkill.GetOwner(this).getBonuses() != null) {
//Get bonuses from runes
bonus = CharacterSkill.GetOwner(this).getBonuses().getSkillBonus(this.skillsBase.sourceType);
}
return this.getSkillPercentFromTrains() + this.getSkillPercentFromAttributes();
}
@Override
public void updateDatabase() {
DbManager.CharacterSkillQueries.updateDatabase(this);
}
public int getRequiredLevel() {
return requiredLevel;
}
public void setRequiredLevel(int requiredLevel) {
this.requiredLevel = requiredLevel;
}
}