forked from MagicBane/Server
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>
1241 lines
49 KiB
Java
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;
|
|
}
|
|
}
|