Prechádzať zdrojové kódy

Adjust EXP storage to uint64 (#4377)

* Updates EXP table to 200/70
* Always store exp as uint64 no matter the client date.
* Adjusts guild exp to uint64.
Co-authored-by: Aleos <aleos89@users.noreply.github.com>
Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>
Cydh Ramdh 4 rokov pred
rodič
commit
1658f273de

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
db/import-tmpl/job_exp.txt


+ 28 - 26
db/re/job_exp.txt

@@ -2,26 +2,28 @@
 // Only official levels included, check db/import-tmpl/job_exp.txt for an expanded list
 //
 // Structure of Database:
-// Max Level,Class list,Type,Exp for Lv 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175
+// Max Level,Class list,Type,Exp for Lv 1,2,3,...,200
 //
 // Type:
 //	0 = Base Exp, 1 = Job Exp
 
 //Base - Normal and Baby Jobs
-99,0:1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19:20:21:23:24:25:4023:4024:4025:4026:4027:4028:4029:4030:4031:4032:4033:4034:4035:4036:4037:4038:4039:4040:4041:4042:4043:4044:4045:4046:4047:4048:4049:4050:4051:4052:4222:4225:4226:4227:4228:4238,0,550,900,1500,2200,3200,3800,4200,4550,5000,5500,6000,6100,6350,6700,7350,8000,8400,8800,9200,9700,10300,11000,11800,13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23200,24000,26000,27500,29000,30000,31500,33000,34000,36000,37500,38000,40000,42000,44500,47000,49000,51000,53000,55000,57000,59000,61500,63000,65000,67000,69000,70000,73000,77000,80000,84000,88000,91000,95000,110000,128000,140000,155000,163000,170000,180000,188000,195000,200000,230000,260000,300000,350000,400000,480000,550000,600000,680000,750000,900000,1000000,1200000,1500000,1800000,2100000,2400000,2800000,3300000,4000000,99999999
+99,0:1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19:20:21:23:24:25:4023:4024:4025:4026:4027:4028:4029:4030:4031:4032:4033:4034:4035:4036:4037:4038:4039:4040:4041:4042:4043:4044:4045:4046:4047:4048:4049:4050:4051:4052:4222:4225:4226:4227:4228:4238,0,550,900,1500,2200,3200,3800,4200,4550,5000,5500,6000,6100,6350,6700,7350,8000,8400,8800,9200,9700,10300,11000,11800,13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23200,24000,26000,27500,29000,30000,31500,33000,34000,36000,37500,38000,40000,42000,44500,47000,49000,51000,53000,55000,59000,61500,61500,63000,65000,67000,69000,70000,73000,77000,80000,84000,88000,91000,95000,110000,128000,140000,155000,163000,170000,180000,188000,195000,200000,230000,260000,300000,350000,400000,480000,550000,600000,680000,750000,900000,1000000,1200000,1500000,1800000,2100000,2400000,2800000,3300000,4000000,99999999
 
-//Base - Adv Jobs
-99,4001:4002:4003:4004:4005:4006:4007:4008:4009:4010:4011:4012:4013:4014:4015:4016:4017:4018:4019:4020:4021:4022,0,660,1080,1800,2640,3840,4560,5040,5460,6000,6600,7200,7320,7620,8040,8820,9600,10080,10560,11040,12610,13390,14300,15340,16900,18460,19500,20800,22100,23400,24700,26000,27300,28600,30160,31200,33800,35750,37700,39000,44100,46200,47600,50400,52500,53200,56000,58800,62300,65800,68600,71400,74200,77000,79800,82600,86100,88200,91000,93800,103500,105000,109500,115500,120000,126000,132000,136500,142500,165000,192000,210000,232500,244500,255000,270000,282000,292500,300000,345000,416000,480000,560000,640000,768000,880000,960000,1088000,1200000,1440000,1700000,2040000,2550000,3060000,3570000,4080000,4760000,5610000,6800000,99999999
+//Base - Trans classes
+99,4001:4002:4003:4004:4005:4006:4007:4008:4009:4010:4011:4012:4013:4014:4015:4016:4017:4018:4019:4020:4021:4022,0,600,1080,1800,2640,3840,4560,5040,5460,6000,6600,7200,7320,7620,8040,8820,9600,10080,10560,11040,12610,13390,14300,15340,16900,18460,19500,20800,22100,23400,24700,26000,27300,28600,30160,31200,33800,35750,37700,39000,44100,46200,47600,50400,52500,53200,56000,58800,62300,65800,68600,71400,74200,77000,79800,82600,86100,88200,91000,93800,103500,105000,109500,115500,120000,126000,132000,136500,142500,165000,192000,210000,232500,244500,255000,270000,282000,292500,300000,345000,416000,480000,560000,640000,768000,880000,960000,1088000,1200000,1440000,1700000,2040000,2550000,3060000,3570000,4080000,4760000,5610000,6800000,99999999
 
-//Base - 3rd Jobs, Baby 3rds, Oboro/Kagerou, Rebellion, Summoner, Star Emperor, Soul Reaper
-//Note: (First 98 values [Level 1 - 98] are only used by Summoner Class, because 3rd classes start at level 99.)
-175,4054:4055:4056:4057:4058:4059:4060:4061:4062:4063:4064:4065:4066:4067:4068:4069:4070:4071:4072:4073:4074:4075:4076:4077:4078:4079:4080:4081:4082:4083:4084:4085:4086:4087:4096:4097:4098:4099:4100:4101:4102:4103:4104:4105:4106:4107:4108:4109:4110:4111:4112:4211:4212:4215:4218:4220:4223:4224:4229:4239:4240:4241:4242:4243:4244,0,55,90,150,220,320,380,420,455,500,600,700,800,900,1000,1100,1200,1300,1400,1500,1800,2100,2400,2700,3000,3300,3600,3900,4200,4500,5400,6300,7200,8100,9000,9900,10800,11700,12600,13500,16200,18900,21600,24300,27000,29700,32400,35100,37800,40500,43200,45900,48600,51300,54000,56700,59400,62100,64800,67500,75600,83700,91800,99900,108000,116100,124200,132300,140400,165000,192000,210000,232500,244500,255000,270000,282000,292500,300000,345000,416000,480000,560000,640000,768000,880000,960000,1088000,1200000,1440000,1700000,2040000,2550000,3060000,3570000,4080000,4760000,5610000,6800000,7070000,7400000,7770000,8150000,8550000,9100000,9610000,10150000,10570000,11180000,12000000,12200000,12930000,13150000,14030000,14420000,15420000,15670000,16870000,17140000,18720000,19020000,20590000,20930000,22690000,23310000,25290000,26020000,27860000,28535000,30990000,31680000,33560000,34942000,36372000,38350000,39890000,41545000,43330000,45400000,48100000,50410000,53370000,56250000,59230000,62590000,66120000,70200000,75330000,81100000,95000000,98000000,103000000,107000000,112000000,116000000,121000000,125000000,130000000,134000000,139000000,145000000,152200000,160840000,171200000,191930000,202290000,214720000,229640000,247550000,283370000,301280000,322770000,348560000,379500000,441390000,99999999
+//Base - Extended classes - Values for level 1 - 99 are unused and are simply copy-pasted from Normal classes values.
+200,4190:4191:4211:4212:4215:4239:4240:4223:4224:4229:4239:4240:4241:4242:4243:4244,0,550,900,1500,2200,3200,3800,4200,4550,5000,5500,6000,6100,6350,6700,7350,8000,8400,8800,9200,9700,10300,11000,11800,13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23200,24000,26000,27500,29000,30000,31500,33000,34000,36000,37500,38000,40000,42000,44500,47000,49000,51000,53000,55000,59000,61500,61500,63000,65000,67000,69000,70000,73000,77000,80000,84000,88000,91000,95000,110000,128000,140000,155000,163000,170000,180000,188000,195000,200000,230000,260000,300000,350000,400000,480000,550000,600000,680000,750000,900000,1000000,1200000,1500000,1800000,2100000,2400000,2800000,3300000,4000000,1272747,1354202,1440870,1533085,1631202,1735598,1846676,1964863,2090614,2224413,2366775,2518248,2679415,2850897,3033354,3227488,3434047,3653826,3887670,4136480,4401214,4755467,5138234,5551810,5998675,6481508,7003204,7566891,8175950,8834032,9545083,10313366,11143488,12040427,13009560,14056699,15188122,16410613,17731503,19158711,20700795,22367001,24167320,26112547,28214345,30485317,32939080,35590346,38455012,41550255,44894635,48508204,52412629,56631321,61189576,66114724,71436298,77186205,83398922,90111701,97364791,105201683,113669366,122818613,132704283,143385650,154926760,167396814,180870583,195428856,211158924,229155105,246519309,266361648,287801097,310966207,352013746,398479560,451078861,510621270,578023277,654322349,740692899,838464361,949141656,1074428354,1216252896,1376798278,1558535650,1764262355,1997144985,2260768123,2559189515,2897002530,3279406863,3712288568,4202310658,4757015664,5384941731,6095754039,6900363572,99999999
 
-//Base - Expanded Super Novice & Expanded Super Baby
-160,4190:4191,0,660,1080,1800,2640,3840,4560,5040,5460,6000,6600,7200,7320,7620,8040,8820,9600,10080,10560,11040,12610,13390,14300,15340,16900,18460,19500,20800,22100,23400,24700,26000,27300,28600,30160,31200,33800,35750,37700,39000,44100,46200,47600,50400,52500,53200,56000,58800,62300,65800,68600,71400,74200,77000,79800,82600,86100,88200,91000,93800,103500,105000,109500,115500,120000,126000,132000,136500,142500,165000,192000,210000,232500,244500,255000,270000,282000,292500,300000,345000,416000,480000,560000,640000,768000,880000,960000,1088000,1200000,1440000,1700000,2040000,2550000,3060000,3570000,4080000,4760000,5610000,6800000,7070000,7400000,7770000,8150000,8550000,9100000,9610000,10150000,10570000,11180000,12000000,12200000,12930000,13150000,14030000,14420000,15420000,15670000,16870000,17140000,18720000,19020000,20590000,20930000,22690000,23310000,25290000,26020000,27860000,28535000,30990000,31680000,33560000,34942000,36372000,38350000,39890000,41545000,43330000,45400000,48100000,50410000,53370000,56250000,59230000,62590000,66120000,70200000,75330000,81100000,95000000,98000000,103000000,107000000,112000000,116000000,121000000,125000000,130000000,134000000,139000000,145000000,152200000,160840000,171200000,191930000,202290000,214720000,229640000,247550000,283370000,301280000,322770000,348560000,379500000,441390000,99999999
+//Base - Third classes - Values for level 1 - 99 are unused and are simply copy-pasted from Trans classes values.
+200,4054:4055:4056:4057:4058:4059:4066:4067:4068:4069:4070:4071:4072:4060:4061:4062:4063:4064:4065:4073:4074:4075:4076:4077:4078:4079:4080:4081:4082:4083:4084:4085:4086:4087:4096:4097:4098:4099:4100:4101:4102:4103:4104:4105:4106:4107:4108:4109:4110:4111:4112,0,600,1080,1800,2640,3840,4560,5040,5460,6000,6600,7200,7320,7620,8040,8820,9600,10080,10560,11040,12610,13390,14300,15340,16900,18460,19500,20800,22100,23400,24700,26000,27300,28600,30160,31200,33800,35750,37700,39000,44100,46200,47600,50400,52500,53200,56000,58800,62300,65800,68600,71400,74200,77000,79800,82600,86100,88200,91000,93800,103500,105000,109500,115500,120000,126000,132000,136500,142500,165000,192000,210000,232500,244500,255000,270000,282000,292500,300000,345000,416000,480000,560000,640000,768000,880000,960000,1088000,1200000,1440000,1700000,2040000,2550000,3060000,3570000,4080000,4760000,5610000,6800000,1272747,1354202,1440870,1533085,1631202,1735598,1846676,1964863,2090614,2224413,2366775,2518248,2679415,2850897,3033354,3227488,3434047,3653826,3887670,4136480,4401214,4755467,5138234,5551810,5998675,6481508,7003204,7566891,8175950,8834032,9545083,10313366,11143488,12040427,13009560,14056699,15188122,16410613,17731503,19158711,20700795,22367001,24167320,26112547,28214345,30485317,32939080,35590346,38455012,41550255,44894635,48508204,52412629,56631321,61189576,66114724,71436298,77186205,83398922,90111701,97364791,105201683,113669366,122818613,132704283,143385650,154926760,167396814,180870583,195428856,211158924,229155105,246519309,266361648,287801097,310966207,352013746,398479560,451078861,510621270,578023277,654322349,740692899,838464361,949141656,1074428354,1216252896,1376798278,1558535650,1764262355,1997144985,2260768123,2559189515,2897002530,3279406863,3712288568,4202310658,4757015664,5384941731,6095754039,6900363572,99999999
+
+//Base- Summoner
+200,4218:4220,0,55,90,150,220,320,380,420,455,500,550,600,700,800,900,1000,1100,1200,1400,1500,1800,2100,2400,2700,3000,3300,3600,3900,4200,4500,5400,6300,7200,8100,9000,9900,10800,11700,12600,13500,16200,18900,21600,24300,27000,29700,32400,35100,37800,40500,43200,45900,48600,51300,54000,56700,59400,62100,64800,67500,70200,72900,75600,78300,81000,83700,86400,89100,91800,94500,103950,114345,125779,138356,152201,167421,184163,202579,222836,240662,259914,280707,303163,327416,353609,381897,412448,445443,481078,519564,561129,606019,654500,706860,763408,824480,890438,961673,1038606,1121694,1211429,1308343,1413010,1526050,1648134,1779984,1922382,2076172,2242265,2421646,2615377,2824607,3050575,3294621,3558190,3842845,4150272,4482293,4840876,5228146,5646397,6098108,6585956,7112832,7681858,8296406,8960118,9676927,10451081,11287167,12190140,13165351,14218579,15356065,16584550,17911314,19344219,20891756,22563096,24368143,26317594,28423001,30696841,33152588,35804795,38669178,41762712,45103728,48712026,52608988,56817707,61363123,66272172,71573945,77299860,83483848,90162555,97375559,105165603,113578851,122665159,132478371,143076640,154522771,166884592,177548517,188893867,200964185,222383346,234614430,247518223,261131725,275493969,290646137,306631674,347137718,392994610,444909197,503681701,570218053,645543857,730820200,827361548,936656008,1060388266,1200465555,1359047054,1538577169,1741823213,1971918059,2232408434,2527309588,2861167184,3239127369,3667016094,4151428920,4699832680,5320680577,6023542481,6819252442,99999999
 
 //Job - Novice & Baby Novice
-10,0:4023,1,10,18,28,40,91,151,205,268,340,999999999
+10,0:4023,1,55,90,150,150,320,380,420,455,500,999999999
 
 //Job - 1st Classes & Baby 1st Classes, Taekwon
 50,1:2:3:4:5:6:4024:4025:4026:4027:4028:4029:4046:4050:4225,1,60,130,260,460,780,1060,1300,1560,1910,2290,2680,2990,3340,3740,4360,4970,5530,6120,6700,8090,8920,9970,11080,12690,14440,15850,17400,19220,21060,22870,24910,26840,29080,31320,33300,37110,40500,43570,46180,53510,57200,60310,65690,70090,72130,77540,83320,90120,97180,999999999
@@ -29,29 +31,29 @@
 //Job - 2nd Classes & Baby 2nd Classes, Soul Linker
 50,7:8:9:10:11:12:13:14:15:16:17:18:19:20:21:4030:4031:4032:4033:4034:4035:4036:4037:4038:4039:4040:4041:4042:4043:4044:4049:4051:4052:4227,1,2500,4200,7000,10300,15900,18900,20900,22600,24900,28800,31500,32000,33300,35100,40500,44100,46300,48500,50700,56000,59400,63500,68100,75000,85700,90500,96600,102600,108600,119700,126000,132300,138600,146100,157500,170600,180400,190300,196800,214900,225200,232000,245700,255900,279300,294000,308700,327000,345400,999999999
 
-//Job - Novice High
-10,4001,1,11,20,31,44,100,166,226,295,374,999999999
+//Job - Ninja, Gunslinger
+70,24:25:4222:4228,1,200,300,400,600,700,1000,1200,1400,1700,1900,2400,2700,3200,3600,4200,4900,5500,6100,6900,7700,8400,9300,10100,11100,12100,13000,14600,16100,17500,18600,21500,23300,24700,27000,29000,30000,32400,35000,38100,41100,44000,46700,49600,52500,55600,58900,62700,65500,69200,72300,81200,84100,89300,95500,100900,107800,114900,120700,128600,150500,176900,196100,219600,234200,247900,266400,281300,296600,308000,999999999
 
-//Job - Adv First Classes
-50,4002:4003:4004:4005:4006:4007,1,100,200,350,550,800,1100,1450,1850,2300,2800,3350,3950,4600,5300,6050,6850,7700,8600,9550,10550,11600,12700,13850,15050,16300,17600,18950,20350,21800,23300,24850,26450,28100,29800,31550,33350,35200,37100,39050,41050,43100,45200,47350,49550,51800,54100,56450,58850,61300,999999999
+//Job - Star Gladiator
+50,4047:4048:4226:4238,1,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,101400,112000,118800,127000,136200,150000,171400,181000,193200,205200,217200,239400,252000,264600,277200,292200,315000,341200,360800,380600,393600,429800,450400,464000,491400,511800,558600,588000,617400,654000,690800,999999999
 
-//Job - Adv Second Classes
-70,4008:4009:4010:4011:4012:4013:4014:4015:4016:4017:4018:4019:4020:4021:4022,1,3800,6200,10400,15200,22900,27100,30000,32500,35700,41300,45000,45800,47600,50300,58700,63900,67100,70300,73500,90600,96200,102700,110200,121400,144700,152900,163100,173300,183500,213500,224700,236000,247200,260700,299800,324800,343600,362300,374800,474400,497000,512100,542200,564800,644300,678200,712100,754500,796900,873100,911900,950600,989400,1028100,1143300,1199900,1233800,1279100,1324300,1486900,1515900,1603000,1719200,1806300,2040300,2244300,2415900,2746000,3326000,999999999
+//Job - High Novice
+10,4001,1,12,22,35,50,113,188,256,335,425,999999999
 
-//Job - 3rd Jobs & Baby 3rds, Oboro/Kagerou, Rebellion, Star Emperor, Soul Reaper
-60,4054:4055:4056:4057:4058:4059:4060:4061:4062:4063:4064:4065:4066:4067:4068:4069:4070:4071:4072:4073:4074:4075:4076:4077:4078:4079:4080:4081:4082:4083:4084:4085:4086:4087:4096:4097:4098:4099:4100:4101:4102:4103:4104:4105:4106:4107:4108:4109:4110:4111:4112:4211:4212:4215:4223:4224:4229:4239:4240:4241:4242:4243:4244,1,112000,355000,615000,917000,1253000,1595000,2007000,2430000,2868000,3420000,3863000,4504000,4998000,5769000,6321000,7254000,7870000,9015000,9530000,11072000,11848000,13467000,14337000,16243000,17216000,19446000,20781000,23070000,24453000,27568000,29118000,31820000,34125000,37048000,40204000,42972000,45937000,49110000,52696000,57158000,61305000,67167000,72285000,77647000,83624000,91113000,98548000,107630000,119077000,131668000,145518000,160753000,177511000,195944000,216220000,238523000,263056000,290042000,319726000,999999999
+//Job - Trans 1st Classes
+50,4002:4003:4004:4005:4006:4007,1,340,550,760,990,1250,1600,1980,2340,2740,3140,3950,4510,5210,5950,7000,8150,9130,10220,11480,12780,14090,15560,16980,18620,20280,21780,24510,27000,29000,31000,36000,39000,41000,45000,49000,51900,55000,59450,64630,70030,74940,79800,84630,89610,95170,100420,107250,112070,118120,999999999
 
-//Job - Expanded Super Novice & Expanded Super Baby
-50,4190:4191,1,112000,355000,615000,917000,1253000,1595000,2007000,2430000,2868000,3420000,3863000,4504000,4998000,5769000,6321000,7254000,7870000,9015000,9530000,11072000,11848000,13467000,14337000,16243000,17216000,19446000,20781000,23070000,24453000,27568000,29118000,31820000,34125000,37048000,40204000,42972000,45937000,49110000,52696000,57158000,61305000,67167000,72285000,77647000,83624000,91113000,98548000,107630000,119077000,999999999
+//Job - Trans 2nd Classes
+70,4008:4009:4010:4011:4012:4013:4014:4015:4016:4017:4018:4019:4020:4021:4022,1,1803,2972,5036,7434,11312,13520,15117,16540,18350,21441,23596,24256,25461,27174,32029,35216,37349,39521,41734,51958,55721,60081,65113,72448,87216,93081,100283,107620,115094,135249,143768,152508,161343,171856,199607,218415,233368,248529,259675,331968,351261,365552,390909,411275,473857,503777,534248,571719,609886,674886,711926,749561,787957,826965,928824,984554,1022494,1070636,1119554,1269585,1307290,1396228,1512414,1604928,1830969,2034180,2211611,2538936,3105953,999999
 
-//Job - Ninja/Gunslinger
-70,24:25:4222:4228,1,200,300,400,600,700,1000,1200,1400,1700,1900,2400,2700,3200,3600,4200,4900,5500,6100,6900,7700,8400,9300,10100,11100,12100,13000,14600,16100,17500,18600,21500,23300,24700,27000,29000,30000,32400,35000,38100,41100,44000,46700,49600,52500,55600,58900,62700,65500,69200,72300,81200,84100,89300,95500,100900,107800,114900,120700,128600,150500,176900,196100,219600,234200,247900,266400,281300,296600,308000,999999999
+//Job - 3rd Jobs & Baby 3rds, Oboro, Kagerou, Rebellion, Star Emperor, Soul Reaper
+70,4054:4055:4056:4057:4058:4059:4060:4061:4062:4063:4064:4065:4066:4067:4068:4069:4070:4071:4072:4073:4074:4075:4076:4077:4078:4079:4080:4081:4082:4083:4084:4085:4086:4087:4096:4097:4098:4099:4100:4101:4102:4103:4104:4105:4106:4107:4108:4109:4110:4111:4112:4211:4212:4215:4223:4224:4229:4239:4240:4241:4242:4243:4244,1,12800,16384,20971,26843,34359,43980,56294,72057,92233,118059,151115,193428,247588,316912,405648,519229,664613,850705,1088903,1393796,1784059,2283596,2923003,3741444,4231573,4785909,5412863,6121948,6923924,7830958,8856813,10017056,11329290,12813427,14491986,16390436,18537584,20966007,23712554,26818899,30332175,34305690,38799735,43882500,49631108,56132783,63486178,71802867,81209043,91847428,103879441,117487647,132878529,150285617,169973033,192239500,217422874,245905271,278118862,319836691,367812195,422984024,486431628,559396372,632361116,705325860,778290604,851255348,924220092,999999999
 
-//Job - Star Gladiator
-50,4047:4048:4226:4238,1,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,50700,101400,112000,118800,127000,136200,150000,171400,181000,193200,205200,217200,239400,252000,264600,277200,292200,315000,341200,360800,380600,393600,429800,450400,464000,491400,511800,558600,588000,617400,654000,690800,999999999
+//Job - Expanded Super Novice & Expanded Super Baby
+50,4190:4191,1,112000,355000,615000,917000,1253000,1595000,2007000,2430000,2868000,3420000,3863000,4504000,4998000,5769000,6321000,7254000,7870000,9015000,9530000,11072000,11848000,13467000,14337000,16243000,17216000,19446000,20781000,23070000,24453000,27568000,29118000,31820000,34125000,37048000,40204000,42972000,45937000,49110000,52696000,57158000,61305000,67167000,72285000,77647000,83624000,91113000,98548000,107630000,119077000,999999999
 
 //Job - Super Novice & Baby Super Novice
-99,23:4045,1,60,130,260,460,780,1060,1300,1560,1910,2290,2680,2990,3340,3740,4360,4970,5530,6120,6700,8090,8920,9970,11080,12690,14440,15850,17400,19220,21060,22870,24910,26840,29080,31320,33300,37110,40500,43570,46180,53510,57200,60310,65690,70090,72130,77540,83320,90120,97180,590120,600120,610120,620120,630120,640120,650120,660120,670120,680120,690120,700120,710120,720120,730120,740120,750120,760120,770120,780120,790120,800120,810120,820120,830120,840120,850120,860120,870120,880120,890120,900120,910120,920120,930120,940120,950120,960120,970120,980120,990120,1000120,1010120,1020120,1030120,1040120,1050120,1060120,1070120,999999999
+99,23:4045,1,60,130,260,460,780,1060,1300,1560,1910,2290,2680,2990,3340,3740,4360,4970,5530,6120,6700,8090,8920,9970,11080,12690,14440,15850,17400,19220,21060,22870,24910,26840,29080,31320,33300,37110,40500,43570,46180,53510,57200,60310,65690,70090,72130,77540,83320,90120,590120,600120,610120,620120,630120,640120,650120,660120,670120,680120,690120,700120,710120,720120,730120,740120,750120,760120,770120,780120,790120,800120,810120,820120,830120,840120,850120,860120,870120,880120,890120,900120,910120,920120,930120,940120,950120,960120,970120,980120,990120,1000120,1010120,1020120,1030120,1040120,1050120,1060120,1070120,1080120,999999999
 
 //Job - Summoner
-50,4218:4220,1,60,130,260,460,780,1060,1300,1560,1910,2500,4200,7000,10300,15900,18900,20900,22600,24900,28800,31500,34900,38300,41700,45100,48500,51900,55000,72000,89000,106000,112000,355000,615000,917000,1253000,1595000,2007000,2430000,2868000,3420000,3863000,4504000,4998000,5769000,6321000,7254000,7870000,9015000,9530000,999999999
+70,4218:4220,1,60,130,260,460,780,1060,1300,1560,1910,2500,4200,7000,10300,15900,18900,20900,22600,24900,28800,33100,35100,40500,44100,46300,48500,50700,56000,59000,63500,68100,75000,85700,90500,106000,112000,355000,615000,917000,1253000,1595000,2007000,2430000,2868000,3420000,3863000,4504000,4998000,5769000,6321000,7585200,9860760,13805064,20707596,33132154,53011447,72890740,92770033,112649326,132528619,319836691,367812195,422984024,486431628,559396372,632361116,705325860,778290604,851255348,924220092,999999999

+ 1 - 1
sql-files/main.sql

@@ -439,7 +439,7 @@ CREATE TABLE IF NOT EXISTS `guild` (
   `max_member` tinyint(6) unsigned NOT NULL default '0',
   `average_lv` smallint(6) unsigned NOT NULL default '1',
   `exp` bigint(20) unsigned NOT NULL default '0',
-  `next_exp` int(11) unsigned NOT NULL default '0',
+  `next_exp` bigint(20) unsigned NOT NULL default '0',
   `skill_point` tinyint(11) unsigned NOT NULL default '0',
   `mes1` varchar(60) NOT NULL default '',
   `mes2` varchar(120) NOT NULL default '',

+ 2 - 0
sql-files/upgrades/upgrade_20200728.sql

@@ -0,0 +1,2 @@
+ALTER TABLE `guild`
+	CHANGE COLUMN `next_exp` `next_exp` bigint(20) unsigned NOT NULL default '0';

+ 9 - 9
src/char/char.cpp

@@ -303,7 +303,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){
 	)
 	{	//Save status
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d',"
-			"`base_exp`='%u', `job_exp`='%u', `zeny`='%d',"
+			"`base_exp`='%" PRIu64 "', `job_exp`='%" PRIu64 "', `zeny`='%d',"
 			"`max_hp`='%u',`hp`='%u',`max_sp`='%u',`sp`='%u',`status_point`='%d',`skill_point`='%d',"
 			"`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
 			"`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',`elemental_id`='%d',"
@@ -916,8 +916,8 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf, uint8* coun
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 3,  SQLDT_SHORT,  &p.class_, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 4,  SQLDT_UINT,   &p.base_level, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 5,  SQLDT_UINT,   &p.job_level, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 6,  SQLDT_UINT,   &p.base_exp, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 7,  SQLDT_UINT,   &p.job_exp, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 6,  SQLDT_UINT64, &p.base_exp, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 7,  SQLDT_UINT64, &p.job_exp, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 8,  SQLDT_INT,    &p.zeny, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 9,  SQLDT_SHORT,  &p.str, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 10, SQLDT_SHORT,  &p.agi, 0, NULL, NULL)
@@ -1034,8 +1034,8 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 4,  SQLDT_SHORT,  &p->class_, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 5,  SQLDT_UINT,   &p->base_level, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 6,  SQLDT_UINT,   &p->job_level, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 7,  SQLDT_UINT,   &p->base_exp, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 8,  SQLDT_UINT,   &p->job_exp, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 7,  SQLDT_UINT64, &p->base_exp, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 8,  SQLDT_UINT64, &p->job_exp, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 9,  SQLDT_INT,    &p->zeny, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 10, SQLDT_SHORT,  &p->str, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 11, SQLDT_SHORT,  &p->agi, 0, NULL, NULL)
@@ -1756,19 +1756,19 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p)
 	buf = WBUFP(buffer,0);
 	WBUFL(buf,0) = p->char_id;
 #if PACKETVER >= 20170830
-	WBUFQ(buf,4) = u64min((uint64)p->base_exp, INT64_MAX);
+	WBUFQ(buf,4) = u64min(p->base_exp, MAX_EXP);
 	offset += 4;
 	buf = WBUFP(buffer, offset);
 #else
-	WBUFL(buf,4) = umin(p->base_exp, INT32_MAX);
+	WBUFL(buf,4) = (int32)u64min(p->base_exp, MAX_EXP);
 #endif
 	WBUFL(buf,8) = p->zeny;
 #if PACKETVER >= 20170830
-	WBUFQ(buf,12) = u64min((uint64)p->job_exp, INT64_MAX);
+	WBUFQ(buf,12) = u64min(p->job_exp, MAX_EXP);
 	offset += 4;
 	buf = WBUFP(buffer, offset);
 #else
-	WBUFL(buf,12) = umin(p->job_exp, INT32_MAX);
+	WBUFL(buf,12) = (int32)u64min(p->job_exp, MAX_EXP);
 #endif
 	WBUFL(buf,16) = p->job_level;
 	WBUFL(buf,20) = 0; // probably opt1

+ 14 - 15
src/char/int_guild.cpp

@@ -19,6 +19,8 @@
 #include "char_mapif.hpp"
 #include "inter.hpp"
 
+using namespace rathena;
+
 #define GS_MEMBER_UNMODIFIED 0x00
 #define GS_MEMBER_MODIFIED 0x01
 #define GS_MEMBER_NEW 0x02
@@ -36,7 +38,7 @@ static const char dataToHex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9
 static DBMap* guild_db_; // int guild_id -> struct guild*
 static DBMap *castle_db;
 
-static unsigned int guild_exp[100];
+static t_exp guild_exp[MAX_GUILDLEVEL];
 
 int mapif_parse_GuildLeave(int fd,int guild_id,uint32 account_id,uint32 char_id,int flag,const char *mes);
 int mapif_guild_broken(int guild_id,int flag);
@@ -221,7 +223,7 @@ int inter_guild_tosql(struct guild *g,int flag)
 				StringBuf_AppendStr(&buf, ", ");
 			//else	//last condition using add_coma setting
 			//	add_comma = true;
-			StringBuf_Printf(&buf, "`guild_lv`=%d, `skill_point`=%d, `exp`=%" PRIu64 ", `next_exp`=%u, `max_member`=%d", g->guild_lv, g->skill_point, g->exp, g->next_exp, g->max_member);
+			StringBuf_Printf(&buf, "`guild_lv`=%d, `skill_point`=%d, `exp`=%" PRIu64 ", `next_exp`=%" PRIu64 ", `max_member`=%d", g->guild_lv, g->skill_point, g->exp, g->next_exp, g->max_member);
 		}
 		StringBuf_Printf(&buf, " WHERE `guild_id`=%d", g->guild_id);
 		if( SQL_ERROR == Sql_Query(sql_handle, "%s", StringBuf_Value(&buf)) )
@@ -377,7 +379,7 @@ struct guild * inter_guild_fromsql(int guild_id)
 	}
 	Sql_GetData(sql_handle,  5, &data, NULL); g->average_lv = atoi(data);
 	Sql_GetData(sql_handle,  6, &data, NULL); g->exp = strtoull(data, NULL, 10);
-	Sql_GetData(sql_handle,  7, &data, NULL); g->next_exp = (unsigned int)strtoul(data, NULL, 10);
+	Sql_GetData(sql_handle,  7, &data, NULL); g->next_exp = strtoull(data, nullptr, 10);
 	Sql_GetData(sql_handle,  8, &data, NULL); g->skill_point = atoi(data);
 	Sql_GetData(sql_handle,  9, &data, &len); memcpy(g->mes1, data, zmin(len, sizeof(g->mes1)));
 	Sql_GetData(sql_handle, 10, &data, &len); memcpy(g->mes2, data, zmin(len, sizeof(g->mes2)));
@@ -638,10 +640,10 @@ struct guild_castle* inter_guildcastle_fromsql(int castle_id)
 // Read exp_guild.txt
 bool exp_guild_parse_row(char* split[], int column, int current)
 {
-	unsigned int exp = (unsigned int)atol(split[0]);
+	t_exp exp = strtoull(split[0], nullptr, 10);
 
-	if (exp >= UINT_MAX) {
-		ShowError("exp_guild: Invalid exp %d at line %d\n", exp, current);
+	if (exp > MAX_GUILD_EXP) {
+		ShowError("exp_guild: Invalid exp %" PRIu64 " at line %d, exceeds max of %" PRIu64 "\n", exp, current, MAX_GUILD_EXP);
 		return false;
 	}
 
@@ -833,7 +835,7 @@ bool guild_check_empty(struct guild *g)
 	return i < g->max_member ? false : true; // not empty
 }
 
-unsigned int guild_nextexp(int level)
+t_exp guild_nextexp(int level)
 {
 	if (level == 0)
 		return 1;
@@ -852,7 +854,7 @@ int guild_checkskill(struct guild *g,int id)
 int guild_calcinfo(struct guild *g)
 {
 	int i,c;
-	unsigned int nextexp;
+	t_exp nextexp;
 	struct guild before = *g; // Save guild current values
 
 	if(g->guild_lv<=0)
@@ -1546,22 +1548,19 @@ int mapif_parse_GuildMemberInfoChange(int fd,int guild_id,uint32 account_id,uint
 		  }
 		case GMI_EXP:
 		{	// EXP
-			uint64 old_exp=g->member[i].exp;
-			g->member[i].exp=*((uint64 *)data);
+			t_exp old_exp=g->member[i].exp;
+			g->member[i].exp=*((t_exp *)data);
 			g->member[i].modified = GS_MEMBER_MODIFIED;
 			if (g->member[i].exp > old_exp)
 			{
-				uint64 exp = g->member[i].exp - old_exp;
+				t_exp exp = g->member[i].exp - old_exp;
 
 				// Compute gained exp
 				if (charserv_config.guild_exp_rate != 100)
 					exp = exp*(charserv_config.guild_exp_rate)/100;
 
 				// Update guild exp
-				if (exp > UINT64_MAX - g->exp)
-					g->exp = UINT64_MAX;
-				else
-					g->exp+=exp;
+				g->exp = util::safe_addition_cap(g->exp, exp, MAX_GUILD_EXP);
 
 				guild_calcinfo(g);
 				mapif_guild_basicinfochanged(guild_id,GBI_EXP,&g->exp,sizeof(g->exp));

+ 4 - 4
src/common/mmo.hpp

@@ -488,7 +488,7 @@ struct mmo_charstatus {
 	uint32 mother;
 	uint32 child;
 
-	unsigned int base_exp,job_exp;
+	t_exp base_exp,job_exp;
 	int zeny;
 
 	short class_; ///< Player's JobID
@@ -632,7 +632,7 @@ struct map_session_data;
 struct guild_member {
 	uint32 account_id, char_id;
 	short hair,hair_color,gender,class_,lv;
-	uint64 exp;
+	t_exp exp;
 	short online,position;
 	char name[NAME_LENGTH];
 	struct map_session_data *sd;
@@ -667,8 +667,8 @@ struct Channel;
 struct guild {
 	int guild_id;
 	short guild_lv, connect_member, max_member, average_lv;
-	uint64 exp;
-	unsigned int next_exp;
+	t_exp exp;
+	t_exp next_exp;
 	int skill_point;
 	char name[NAME_LENGTH],master[NAME_LENGTH];
 	struct guild_member member[MAX_GUILD];

+ 0 - 22
src/common/utilities.cpp

@@ -69,28 +69,6 @@ int levenshtein(const std::string &s1, const std::string &s2)
 	return result;
 }
 
-bool rathena::util::safe_addition( int64 a, int64 b, int64& result ){
-#if __has_builtin( __builtin_add_overflow ) || ( defined( __GNUC__ ) && !defined( __clang__ ) && defined( GCC_VERSION  ) && GCC_VERSION >= 50100 )
-	return __builtin_add_overflow( a, b, &result );
-#else
-	bool overflow = false;
-
-	if( b < 0 ){
-		if( a < ( INT64_MIN - b ) ){
-			overflow = true;
-		}
-	}else{
-		if( a > ( INT64_MAX - b ) ){
-			overflow = true;
-		}
-	}
-
-	result = a + b;
-
-	return overflow;
-#endif
-}
-
 bool rathena::util::safe_substraction( int64 a, int64 b, int64& result ){
 #if __has_builtin( __builtin_sub_overflow ) || ( defined( __GNUC__ ) && !defined( __clang__ ) && defined( GCC_VERSION  ) && GCC_VERSION >= 50100 )
 	return __builtin_sub_overflow( a, b, &result );

+ 51 - 1
src/common/utilities.hpp

@@ -14,6 +14,10 @@
 #include "cbasetypes.hpp"
 #include "random.hpp"
 
+#ifndef __has_builtin
+	#define __has_builtin(x) 0
+#endif
+
 // Class used to perform time measurement
 class cScopeTimer {
 	struct sPimpl; //this is to avoid long compilation time
@@ -192,9 +196,55 @@ namespace rathena {
 			}
 		}
 
-		bool safe_addition( int64 a, int64 b, int64& result );
+#if __has_builtin( __builtin_add_overflow ) || ( defined( __GNUC__ ) && !defined( __clang__ ) && defined( GCC_VERSION  ) && GCC_VERSION >= 50100 )
+		template <typename T> bool safe_addition(T a, T b, T &result) {
+			return __builtin_add_overflow(a, b, &result);
+		}
+#else
+		template <typename T> bool safe_addition( T a, T b, T& result ){
+			bool overflow = false;
+
+			if( std::numeric_limits<T>::is_signed ){
+				if( b < 0 ){
+					if( a < ( (std::numeric_limits<T>::min)() - b ) ){
+						overflow = true;
+					}
+				}else{
+					if( a > ( (std::numeric_limits<T>::max)() - b ) ){
+						overflow = true;
+					}
+				}
+			}else{
+				if( a > ( (std::numeric_limits<T>::max)() - b ) ){
+					overflow = true;
+				}
+			}
+
+			result = a + b;
+
+			return overflow;
+		}
+#endif
+
 		bool safe_substraction( int64 a, int64 b, int64& result );
 		bool safe_multiplication( int64 a, int64 b, int64& result );
+
+		/**
+		 * Safely add values without overflowing.
+		 * @param a: Holder of value to increment
+		 * @param b: Increment by
+		 * @param cap: Cap value
+		 * @return Result of a + b
+		 */
+		template <typename T> T safe_addition_cap( T a, T b, T cap ){
+			T result;
+
+			if( rathena::util::safe_addition( a, b, result ) ){
+				return cap;
+			}else{
+				return result;
+			}
+		}
 	}
 }
 

+ 23 - 2
src/common/utils.cpp

@@ -365,7 +365,7 @@ unsigned int get_percentage(const unsigned int A, const unsigned int B)
 
 	if( B == 0 )
 	{
-		ShowError("get_percentage(): divison by zero! (A=%u,B=%u)\n", A, B);
+		ShowError("get_percentage: divison by zero! (A=%u,B=%u)\n", A, B);
 		return ~0U;
 	}
 
@@ -373,9 +373,30 @@ unsigned int get_percentage(const unsigned int A, const unsigned int B)
 
 	if( result > UINT_MAX )
 	{
-		ShowError("get_percentage(): result percentage too high! (A=%u,B=%u,result=%g)\n", A, B, result);
+		ShowError("get_percentage: result percentage too high! (A=%u,B=%u,result=%g)\n", A, B, result);
 		return UINT_MAX;
 	}
 
 	return (unsigned int)floor(result);
 }
+
+uint32 get_percentage_exp(const uint64 a, const uint64 b)
+{
+	double result;
+
+	if (b == 0)
+	{
+		ShowError("get_percentage_exp: divison by zero! (a=%" PRIu64 ",b=%" PRIu64 ")\n", a, b);
+		return ~0U;
+	}
+
+	result = 100.0 * ((double)a / (double)b);
+
+	if (result > UINT32_MAX)
+	{
+		ShowError("get_percentage_exp: result percentage too high! (a=%" PRIu64 ",b=%" PRIu64 ",result=%g)\n", a, b, result);
+		return UINT32_MAX;
+	}
+
+	return (uint32)floor(result);
+}

+ 1 - 0
src/common/utils.hpp

@@ -27,6 +27,7 @@ bool exists(const char* filename);
 
 /// calculates the value of A / B, in percent (rounded down)
 unsigned int get_percentage(const unsigned int A, const unsigned int B);
+uint32 get_percentage_exp(const uint64 a, const uint64 b);
 
 //////////////////////////////////////////////////////////////////////////
 // byte word dword access [Shinomori]

+ 21 - 0
src/config/const.hpp

@@ -4,6 +4,8 @@
 #ifndef CONFIG_CONST_H
 #define CONFIG_CONST_H
 
+#include "../common/cbasetypes.hpp"
+
 /**
  * rAthena configuration file (http://rathena.org)
  * For detailed guidance on these check http://rathena.org/wiki/SRC/config/
@@ -53,6 +55,25 @@
 	#define DEFTYPE_MAX CHAR_MAX
 #endif
 
+/**
+ * EXP definition type
+ */
+typedef uint64 t_exp;
+
+/// Max Base and Job EXP for players
+#if PACKETVER >= 20170830
+	const t_exp MAX_EXP = INT64_MAX;
+#else
+	const t_exp MAX_EXP = INT32_MAX;
+#endif
+
+/// Max EXP for guilds
+const t_exp MAX_GUILD_EXP = INT32_MAX;
+/// Max Base EXP for player on Max Base Level
+const t_exp MAX_LEVEL_BASE_EXP = 99999999;
+/// Max Job EXP for player on Max Job Level
+const t_exp MAX_LEVEL_JOB_EXP = 999999999;
+
 /* pointer size fix which fixes several gcc warnings */
 #ifdef __64BIT__
 	#define __64BPRTSIZE(y) (intptr)y

+ 2 - 2
src/map/atcommand.cpp

@@ -5313,11 +5313,11 @@ ACMD_FUNC(exp)
 	nullpo_retr(-1, sd);
 	memset(output, '\0', sizeof(output));
 
-	nextb = pc_nextbaseexp(sd);
+	nextb = (double)pc_nextbaseexp(sd);
 	if (nextb)
 		nextb = sd->status.base_exp*100.0/nextb;
 
-	nextj = pc_nextjobexp(sd);
+	nextj = (double)pc_nextjobexp(sd);
 	if (nextj)
 		nextj = sd->status.job_exp*100.0/nextj;
 

+ 26 - 16
src/map/clif.cpp

@@ -65,6 +65,16 @@ static inline uint32 client_tick( t_tick tick ){
 	return (uint32)tick;
 }
 
+#if PACKETVER >= 20170830
+static inline int64 client_exp(t_exp exp) {
+	return (int64)u64min(exp, MAX_EXP);
+}
+#else
+static inline int32 client_exp(t_exp exp) {
+	return (int32)u64min(exp, MAX_EXP);
+}
+#endif
+
 /* for clif_clearunit_delayed */
 static struct eri *delay_clearunit_ers;
 
@@ -3357,40 +3367,40 @@ void clif_updatestatus(struct map_session_data *sd,int type)
 #if PACKETVER >= 20170830
 	case SP_BASEEXP:
 		WFIFOW(fd,0)=0xacb;
-		WFIFOQ(fd,4)=sd->status.base_exp;
+		WFIFOQ(fd,4)=client_exp(sd->status.base_exp);
 		len = packet_len(0xacb);
 		break;
 	case SP_JOBEXP:
 		WFIFOW(fd,0)=0xacb;
-		WFIFOQ(fd,4)=sd->status.job_exp;
+		WFIFOQ(fd,4)=client_exp(sd->status.job_exp);
 		len = packet_len(0xacb);
 		break;
 	case SP_NEXTBASEEXP:
 		WFIFOW(fd,0)=0xacb;
-		WFIFOQ(fd,4)=pc_nextbaseexp(sd);
+		WFIFOQ(fd,4)=client_exp(pc_nextbaseexp(sd));
 		len = packet_len(0xacb);
 		break;
 	case SP_NEXTJOBEXP:
 		WFIFOW(fd,0)=0xacb;
-		WFIFOQ(fd,4)=pc_nextjobexp(sd);
+		WFIFOQ(fd,4)=client_exp(pc_nextjobexp(sd));
 		len = packet_len(0xacb);
 		break;
 #else
 	case SP_BASEEXP:
 		WFIFOW(fd,0)=0xb1;
-		WFIFOL(fd,4)=sd->status.base_exp;
+		WFIFOL(fd,4)=client_exp(sd->status.base_exp);
 		break;
 	case SP_JOBEXP:
 		WFIFOW(fd,0)=0xb1;
-		WFIFOL(fd,4)=sd->status.job_exp;
+		WFIFOL(fd,4)=client_exp(sd->status.job_exp);
 		break;
 	case SP_NEXTBASEEXP:
 		WFIFOW(fd,0)=0xb1;
-		WFIFOL(fd,4)=pc_nextbaseexp(sd);
+		WFIFOL(fd,4)= client_exp(pc_nextbaseexp(sd));
 		break;
 	case SP_NEXTJOBEXP:
 		WFIFOW(fd,0)=0xb1;
-		WFIFOL(fd,4)=pc_nextjobexp(sd);
+		WFIFOL(fd,4)= client_exp(pc_nextjobexp(sd));
 		break;
 #endif
 
@@ -8525,8 +8535,8 @@ void clif_guild_basicinfo(struct map_session_data *sd) {
 	WFIFOL(fd,10)=g->connect_member;
 	WFIFOL(fd,14)=g->max_member;
 	WFIFOL(fd,18)=g->average_lv;
-	WFIFOL(fd,22)=(uint32)cap_value(g->exp,0,INT32_MAX);
-	WFIFOL(fd,26)=g->next_exp;
+	WFIFOL(fd,22)=(uint32)cap_value(g->exp, 0, MAX_GUILD_EXP);
+	WFIFOL(fd,26)=(uint32)cap_value(g->next_exp, 0, MAX_GUILD_EXP);
 	WFIFOL(fd,30)=0;	// Tax Points
 	WFIFOL(fd,34)=0;	// Honor: (left) Vulgar [-100,100] Famed (right)
 	WFIFOL(fd,38)=0;	// Virtue: (down) Wicked [-100,100] Righteous (up)
@@ -14707,10 +14717,10 @@ void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd)
 void clif_parse_NoviceExplosionSpirits(int fd, struct map_session_data *sd)
 {
 	if( (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE ) {
-		unsigned int next = pc_nextbaseexp(sd);
+		t_exp next = pc_nextbaseexp(sd);
 
 		if( next ) {
-			int percent = (int)( ( (float)sd->status.base_exp/(float)next )*1000. );
+			int percent = (int)( ( (double)sd->status.base_exp/(double)next )*1000. );
 
 			if( percent && ( percent%100 ) == 0 ) {// 10.0%, 20.0%, ..., 90.0%
 				sc_start(&sd->bl,&sd->bl, status_skill2sc(MO_EXPLOSIONSPIRITS), 100, 17, skill_get_time(MO_EXPLOSIONSPIRITS, 5)); //Lv17-> +50 critical (noted by Poki) [Skotlex]
@@ -18168,9 +18178,9 @@ void clif_party_show_picker( struct map_session_data* sd, struct item* item_data
  * @param exp EXP value gained/loss
  * @param type SP_BASEEXP, SP_JOBEXP
  * @param quest False:Normal EXP; True:Quest EXP (displayed in purple color)
- * @param lost True:if lossing EXP
+ * @param lost True:if losing EXP
  */
-void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, bool quest, bool lost)
+void clif_displayexp(struct map_session_data *sd, t_exp exp, char type, bool quest, bool lost)
 {
 	int fd;
 	int offset;
@@ -18188,10 +18198,10 @@ void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, b
 	WFIFOW(fd,0) = cmd;
 	WFIFOL(fd,2) = sd->bl.id;
 #if PACKETVER >= 20170830
-	WFIFOQ(fd,6) = (int64)u64min((uint64)exp, INT_MAX) * (lost ? -1 : 1);
+	WFIFOQ(fd,6) = client_exp(exp) * (lost ? -1 : 1);
 	offset = 4;
 #else
-	WFIFOL(fd,6) = (int)umin(exp, INT_MAX) * (lost ? -1 : 1);
+	WFIFOL(fd,6) = client_exp(exp) * (lost ? -1 : 1);
 	offset = 0;
 #endif
 	WFIFOW(fd,10+offset) = type;

+ 1 - 1
src/map/clif.hpp

@@ -932,7 +932,7 @@ void clif_quest_delete(struct map_session_data * sd, int quest_id);
 void clif_quest_update_status(struct map_session_data * sd, int quest_id, bool active);
 void clif_quest_update_objective(struct map_session_data * sd, struct quest * qd);
 void clif_quest_show_event(struct map_session_data *sd, struct block_list *bl, e_questinfo_types effect, e_questinfo_markcolor color);
-void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, bool quest, bool lost);
+void clif_displayexp(struct map_session_data *sd, t_exp exp, char type, bool quest, bool lost);
 
 int clif_send(const void* buf, int len, struct block_list* bl, enum send_target type);
 void do_init_clif(void);

+ 7 - 16
src/map/guild.cpp

@@ -51,7 +51,7 @@ struct eventlist {
 //Guild EXP cache
 struct guild_expcache {
 	int guild_id, account_id, char_id;
-	uint64 exp;
+	t_exp exp;
 };
 static struct eri *expcache_ers; //For handling of guild exp payment.
 
@@ -402,10 +402,7 @@ int guild_payexp_timer_sub(DBKey key, DBData *data, va_list ap) {
 		return 0;
 	}
 
-	if (g->member[i].exp > UINT64_MAX - c->exp)
-		g->member[i].exp = UINT64_MAX;
-	else
-		g->member[i].exp+= c->exp;
+	g->member[i].exp = util::safe_addition_cap(g->member[i].exp, c->exp, MAX_GUILD_EXP);
 
 	intif_guild_change_memberinfo(g->guild_id,c->account_id,c->char_id,
 		GMI_EXP,&g->member[i].exp,sizeof(g->member[i].exp));
@@ -1384,7 +1381,7 @@ static DBData create_expcache(DBKey key, va_list args) {
 /*====================================================
  * Return taxed experience from player sd to guild
  *---------------------------------------------------*/
-unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp) {
+t_exp guild_payexp(struct map_session_data *sd,t_exp exp) {
 	struct guild *g;
 	struct guild_expcache *c;
 	int per;
@@ -1405,11 +1402,7 @@ unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp) {
 	//Otherwise tax everything.
 
 	c = (struct guild_expcache *)db_data2ptr(guild_expcache_db->ensure(guild_expcache_db, db_i2key(sd->status.char_id), create_expcache, sd));
-
-	if (c->exp > UINT64_MAX - exp)
-		c->exp = UINT64_MAX;
-	else
-		c->exp += exp;
+	c->exp = util::safe_addition_cap(c->exp, exp, MAX_GUILD_EXP);
 
 	return exp;
 }
@@ -1419,7 +1412,7 @@ unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp) {
  * Add this experience to guild exp
  * [Celest]
  *---------------------------------------------------*/
-int guild_getexp(struct map_session_data *sd,int exp) {
+t_exp guild_getexp(struct map_session_data *sd,t_exp exp) {
 	struct guild_expcache *c;
 	nullpo_ret(sd);
 
@@ -1427,10 +1420,8 @@ int guild_getexp(struct map_session_data *sd,int exp) {
 		return 0;
 
 	c = (struct guild_expcache *)db_data2ptr(guild_expcache_db->ensure(guild_expcache_db, db_i2key(sd->status.char_id), create_expcache, sd));
-	if (c->exp > UINT64_MAX - exp)
-		c->exp = UINT64_MAX;
-	else
-		c->exp += exp;
+	c->exp = util::safe_addition_cap(c->exp, exp, MAX_GUILD_EXP);
+
 	return exp;
 }
 

+ 2 - 2
src/map/guild.hpp

@@ -44,8 +44,8 @@ struct guild_castle* guild_mapindex2gc(short mapindex);
 struct map_session_data *guild_getavailablesd(struct guild *g);
 int guild_getindex(struct guild *g,uint32 account_id,uint32 char_id);
 int guild_getposition(struct map_session_data *sd);
-unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp);
-int guild_getexp(struct map_session_data *sd,int exp); // [Celest]
+t_exp guild_payexp(struct map_session_data *sd,t_exp exp);
+t_exp guild_getexp(struct map_session_data *sd,t_exp exp); // [Celest]
 
 int guild_create(struct map_session_data *sd, const char *name);
 int guild_created(uint32 account_id,int guild_id);

+ 1 - 1
src/map/map.hpp

@@ -51,7 +51,7 @@ void map_msg_reload(void);
 #define NATURAL_HEAL_INTERVAL 500
 #define MIN_FLOORITEM 2
 #define MAX_FLOORITEM START_ACCOUNT_NUM
-#define MAX_LEVEL 175
+#define MAX_LEVEL 200
 #define MAX_DROP_PER_MAP 48
 #define MAX_IGNORE_LIST 20 	// official is 14
 #define MAX_VENDING 12

+ 58 - 59
src/map/pc.cpp

@@ -65,8 +65,6 @@ int pc_split_atoui(char* str, unsigned int* val, char sep, int max);
 static inline bool pc_attendance_rewarded_today( struct map_session_data* sd );
 
 #define PVP_CALCRANK_INTERVAL 1000	// PVP calculation interval
-#define MAX_LEVEL_BASE_EXP 99999999 ///< Max Base EXP for player on Max Base Level
-#define MAX_LEVEL_JOB_EXP 999999999 ///< Max Job EXP for player on Max Job Level
 
 static unsigned int statp[MAX_LEVEL+1];
 #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP)
@@ -1227,7 +1225,7 @@ enum adopt_responses pc_try_adopt(struct map_session_data *p1_sd, struct map_ses
 bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data *p2_sd, struct map_session_data *b_sd)
 {
 	int job, joblevel;
-	unsigned int jobexp;
+	t_exp jobexp;
 
 	if( pc_try_adopt(p1_sd, p2_sd, b_sd) != ADOPT_ALLOWED )
 		return false;
@@ -7041,7 +7039,7 @@ int pc_follow(struct map_session_data *sd,int target_id)
 }
 
 int pc_checkbaselevelup(struct map_session_data *sd) {
-	unsigned int next = pc_nextbaseexp(sd);
+	t_exp next = pc_nextbaseexp(sd);
 
 	if (!next || sd->status.base_exp < next || pc_is_maxbaselv(sd))
 		return 0;
@@ -7052,12 +7050,10 @@ int pc_checkbaselevelup(struct map_session_data *sd) {
 		if( ( !battle_config.multi_level_up || ( battle_config.multi_level_up_base > 0 && sd->status.base_level >= battle_config.multi_level_up_base ) ) && sd->status.base_exp > next-1 )
 			sd->status.base_exp = next-1;
 
-		next = pc_gets_status_point(sd->status.base_level);
-		sd->status.base_level++;
-		sd->status.status_point += next;
+		sd->status.status_point += pc_gets_status_point(sd->status.base_level++);
 
 		if( pc_is_maxbaselv(sd) ){
-			sd->status.base_exp = u32min(sd->status.base_exp,MAX_LEVEL_BASE_EXP);
+			sd->status.base_exp = u64min(sd->status.base_exp,MAX_LEVEL_BASE_EXP);
 			break;
 		}
 	} while ((next=pc_nextbaseexp(sd)) > 0 && sd->status.base_exp >= next);
@@ -7109,7 +7105,7 @@ void pc_baselevelchanged(struct map_session_data *sd) {
 
 int pc_checkjoblevelup(struct map_session_data *sd)
 {
-	unsigned int next = pc_nextjobexp(sd);
+	t_exp next = pc_nextjobexp(sd);
 
 	nullpo_ret(sd);
 	if(!next || sd->status.job_exp < next || pc_is_maxjoblv(sd))
@@ -7125,7 +7121,7 @@ int pc_checkjoblevelup(struct map_session_data *sd)
 		sd->status.skill_point ++;
 
 		if( pc_is_maxjoblv(sd) ){
-			sd->status.job_exp = u32min(sd->status.job_exp,MAX_LEVEL_JOB_EXP);
+			sd->status.job_exp = u64min(sd->status.job_exp,MAX_LEVEL_JOB_EXP);
 			break;
 		}
 	} while ((next=pc_nextjobexp(sd)) > 0 && sd->status.job_exp >= next);
@@ -7152,7 +7148,7 @@ int pc_checkjoblevelup(struct map_session_data *sd)
 * @param job_exp Job EXP before peronal bonuses
 * @param src Block list that affecting the exp calculation
 */
-static void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsigned int *job_exp, struct block_list *src)
+static void pc_calcexp(struct map_session_data *sd, t_exp *base_exp, t_exp *job_exp, struct block_list *src)
 {
 	int bonus = 0, vip_bonus_base = 0, vip_bonus_job = 0;
 
@@ -7186,8 +7182,8 @@ static void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsi
 	}
 
 	if (*base_exp) {
-		unsigned int exp = (unsigned int)(*base_exp + (double)*base_exp * (bonus + vip_bonus_base)/100.);
-		*base_exp =  cap_value(exp, 1, UINT_MAX);
+		t_exp exp = (t_exp)(*base_exp + ((double)*base_exp * ((bonus + vip_bonus_base) / 100.)));
+		*base_exp = cap_value(exp, 1, MAX_EXP);
 	}
 
 	// Give JEXPBOOST for quests even if src is NULL.
@@ -7195,8 +7191,8 @@ static void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsi
 		bonus += sd->sc.data[SC_JEXPBOOST]->val1;
 
 	if (*job_exp) {
-		unsigned int exp = (unsigned int)(*job_exp + (double)*job_exp * (bonus + vip_bonus_job)/100.);
-		*job_exp = cap_value(exp, 1, UINT_MAX);
+		t_exp exp = (t_exp)(*job_exp + ((double)*job_exp * ((bonus + vip_bonus_job) / 100.)));
+		*job_exp = cap_value(exp, 1, MAX_EXP);
 	}
 
 	return;
@@ -7211,7 +7207,7 @@ static void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsi
  * @param next_job_exp Job EXP needed for next job level
  * @param lost True:EXP penalty, lose EXP
  **/
-void pc_gainexp_disp(struct map_session_data *sd, unsigned int base_exp, unsigned int next_base_exp, unsigned int job_exp, unsigned int next_job_exp, bool lost) {
+void pc_gainexp_disp(struct map_session_data *sd, t_exp base_exp, t_exp next_base_exp, t_exp job_exp, t_exp next_job_exp, bool lost) {
 	char output[CHAT_SIZE_MAX];
 
 	nullpo_retv(sd);
@@ -7232,9 +7228,9 @@ void pc_gainexp_disp(struct map_session_data *sd, unsigned int base_exp, unsigne
  * @param exp_flag 1: Quest EXP; 2: Param Exp (Ignore Guild EXP tax, EXP adjustments)
  * @return
  **/
-void pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int base_exp, unsigned int job_exp, uint8 exp_flag)
+void pc_gainexp(struct map_session_data *sd, struct block_list *src, t_exp base_exp, t_exp job_exp, uint8 exp_flag)
 {
-	unsigned int nextb = 0, nextj = 0;
+	t_exp nextb = 0, nextj = 0;
 	uint8 flag = 0; ///< 1: Base EXP given, 2: Job EXP given, 4: Max Base level, 8: Max Job Level
 
 	nullpo_retv(sd);
@@ -7292,20 +7288,16 @@ void pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned in
 
 	// Give EXP for Base Level
 	if (base_exp) {
-		if ((uint64)sd->status.base_exp + base_exp > UINT32_MAX)
-			sd->status.base_exp = UINT32_MAX;
-		else
-			sd->status.base_exp += base_exp;
+		sd->status.base_exp = util::safe_addition_cap(sd->status.base_exp, base_exp, MAX_EXP);
+
 		if (!pc_checkbaselevelup(sd))
 			clif_updatestatus(sd,SP_BASEEXP);
 	}
 
 	// Give EXP for Job Level
 	if (job_exp) {
-		if ((uint64)sd->status.job_exp + job_exp > UINT32_MAX)
-			sd->status.job_exp = UINT32_MAX;
-		else
-			sd->status.job_exp += job_exp;
+		sd->status.job_exp = util::safe_addition_cap(sd->status.job_exp, job_exp, MAX_EXP);
+
 		if (!pc_checkjoblevelup(sd))
 			clif_updatestatus(sd,SP_JOBEXP);
 	}
@@ -7325,19 +7317,19 @@ void pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned in
  * @param base_exp Base EXP lost
  * @param job_exp Job EXP lost
  **/
-void pc_lostexp(struct map_session_data *sd, unsigned int base_exp, unsigned int job_exp) {
+void pc_lostexp(struct map_session_data *sd, t_exp base_exp, t_exp job_exp) {
 
 	nullpo_retv(sd);
 
 	if (base_exp) {
-		base_exp = u32min(sd->status.base_exp, base_exp);
+		base_exp = u64min(sd->status.base_exp, base_exp);
 		sd->status.base_exp -= base_exp;
 		clif_displayexp(sd, base_exp, SP_BASEEXP, false, true);
 		clif_updatestatus(sd, SP_BASEEXP);
 	}
 
 	if (job_exp) {
-		job_exp = u32min(sd->status.job_exp, job_exp);
+		job_exp = u64min(sd->status.job_exp, job_exp);
 		sd->status.job_exp -= job_exp;
 		clif_displayexp(sd, job_exp, SP_JOBEXP, false, true);
 		clif_updatestatus(sd, SP_JOBEXP);
@@ -7408,7 +7400,7 @@ bool pc_is_maxjoblv(struct map_session_data *sd) {
  * @param sd
  * @return Base EXP needed for next base level
  **/
-unsigned int pc_nextbaseexp(struct map_session_data *sd){
+t_exp pc_nextbaseexp(struct map_session_data *sd){
 	nullpo_ret(sd);
 	if (sd->status.base_level == 0) // Is this something that possible?
 		return 0;
@@ -7422,7 +7414,7 @@ unsigned int pc_nextbaseexp(struct map_session_data *sd){
  * @param sd
  * @return Job EXP needed for next job level
  **/
-unsigned int pc_nextjobexp(struct map_session_data *sd){
+t_exp pc_nextjobexp(struct map_session_data *sd){
 	nullpo_ret(sd);
 	if (sd->status.job_level == 0) // Is this something that possible?
 		return 0;
@@ -8244,9 +8236,9 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
 	// Activate Steel body if a super novice dies at 99+% exp [celest]
 	// Super Novices have no kill or die functions attached when saved by their angel
 	if (!sd->state.snovice_dead_flag && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) {
-		unsigned int exp = pc_nextbaseexp(sd);
+		t_exp exp = pc_nextbaseexp(sd);
 
-		if( exp && get_percentage(sd->status.base_exp,exp) >= 99 ) {
+		if( exp && get_percentage_exp(sd->status.base_exp, exp) >= 99 ) {
 			sd->state.snovice_dead_flag = 1;
 			pc_setrestartvalue(sd,1);
 			status_percent_heal(&sd->bl, 100, 100);
@@ -8447,8 +8439,8 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
 		&& !sd->sc.data[SC_BABY] && !sd->sc.data[SC_LIFEINSURANCE]
 		&& !mapdata->flag[MF_NOEXPPENALTY] && !mapdata_flag_gvg2(mapdata))
 	{
-		uint32 base_penalty = 0;
-		uint32 job_penalty = 0;
+		t_exp base_penalty = 0;
+		t_exp job_penalty = 0;
 		uint32 zeny_penalty = 0;
 
 		if (pc_isvip(sd)) { // EXP penalty for VIP
@@ -8463,13 +8455,13 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
 
 		if ((battle_config.death_penalty_maxlv&1 || !pc_is_maxbaselv(sd)) && base_penalty > 0) {
 			switch (battle_config.death_penalty_type) {
-				case 1: base_penalty = (uint32) ( pc_nextbaseexp(sd) * ( base_penalty / 10000. ) ); break;
-				case 2: base_penalty = (uint32) ( sd->status.base_exp * ( base_penalty / 10000. ) ); break;
+				case 1: base_penalty = (t_exp) ( pc_nextbaseexp(sd) * ( base_penalty / 10000. ) ); break;
+				case 2: base_penalty = (t_exp) ( sd->status.base_exp * ( base_penalty / 10000. ) ); break;
 			}
 			if (base_penalty){ //recheck after altering to speedup
 				if (battle_config.pk_mode && src && src->type==BL_PC)
 					base_penalty *= 2;
-				base_penalty = u32min(sd->status.base_exp, base_penalty);
+				base_penalty = u64min(sd->status.base_exp, base_penalty);
 			}
 		}
 		else 
@@ -8483,7 +8475,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
 			if (job_penalty) {
 				if (battle_config.pk_mode && src && src->type==BL_PC)
 					job_penalty *= 2;
-				job_penalty = u32min(sd->status.job_exp, job_penalty);
+				job_penalty = u64min(sd->status.job_exp, job_penalty);
 			}
 		}
 		else
@@ -8658,10 +8650,10 @@ int64 pc_readparam(struct map_session_data* sd,int64 type)
 		case SP_SEX:             val = sd->status.sex; break;
 		case SP_WEIGHT:          val = sd->weight; break;
 		case SP_MAXWEIGHT:       val = sd->max_weight; break;
-		case SP_BASEEXP:         val = sd->status.base_exp; break;
-		case SP_JOBEXP:          val = sd->status.job_exp; break;
-		case SP_NEXTBASEEXP:     val = pc_nextbaseexp(sd); break;
-		case SP_NEXTJOBEXP:      val = pc_nextjobexp(sd); break;
+		case SP_BASEEXP:         val = u64min(sd->status.base_exp, MAX_EXP); break;
+		case SP_JOBEXP:          val = u64min(sd->status.job_exp, MAX_EXP); break;
+		case SP_NEXTBASEEXP:     val = u64min(pc_nextbaseexp(sd), MAX_EXP); break;
+		case SP_NEXTJOBEXP:      val = u64min(pc_nextjobexp(sd), MAX_EXP); break;
 		case SP_HP:              val = sd->battle_status.hp; break;
 		case SP_MAXHP:           val = sd->battle_status.max_hp; break;
 		case SP_SP:              val = sd->battle_status.sp; break;
@@ -8866,22 +8858,18 @@ bool pc_setparam(struct map_session_data *sd,int64 type,int64 val_tmp)
 		sd->status.zeny = cap_value(val, 0, MAX_ZENY);
 		break;
 	case SP_BASEEXP:
-		{
-			val = cap_value(val, 0, INT_MAX);
-			if (val < sd->status.base_exp) // Lost
-				pc_lostexp(sd, sd->status.base_exp - val, 0);
-			else // Gained
-				pc_gainexp(sd, NULL, val - sd->status.base_exp, 0, 2);
-		}
+		val_tmp = cap_value(val_tmp, 0, pc_is_maxbaselv(sd) ? MAX_LEVEL_BASE_EXP : MAX_EXP);
+		if (val_tmp < sd->status.base_exp) // Lost
+			pc_lostexp(sd, sd->status.base_exp - val_tmp, 0);
+		else // Gained
+			pc_gainexp(sd, NULL, val_tmp - sd->status.base_exp, 0, 2);
 		return true;
 	case SP_JOBEXP:
-		{
-			val = cap_value(val, 0, INT_MAX);
-			if (val < sd->status.job_exp) // Lost
-				pc_lostexp(sd, 0, sd->status.job_exp - val);
-			else // Gained
-				pc_gainexp(sd, NULL, 0, val - sd->status.job_exp, 2);
-		}
+		val_tmp = cap_value(val_tmp, 0, pc_is_maxjoblv(sd) ? MAX_LEVEL_JOB_EXP : MAX_EXP);
+		if (val_tmp < sd->status.job_exp) // Lost
+			pc_lostexp(sd, 0, sd->status.job_exp - val_tmp);
+		else // Gained
+			pc_gainexp(sd, NULL, 0, val_tmp - sd->status.job_exp, 2);
 		return true;
 	case SP_SEX:
 		sd->status.sex = val ? SEX_MALE : SEX_FEMALE;
@@ -12035,8 +12023,19 @@ static bool pc_readdb_job_exp(char* fields[], int columns, int current)
 	idx = pc_class2idx(job_id);
 
 	job_info[idx].max_level[type] = maxlvl;
-	for(i=0; i<maxlvl; i++)
-		job_info[idx].exp_table[type][i] = ((uint32) atoi(fields[3+i]));
+	for(i=0; i<maxlvl; i++){
+		t_exp exp = strtoull( fields[3 + i], nullptr, 10 );
+
+		if( exp == 0 ){
+			ShowWarning( "pc_readdb_job_exp: No value defined for level %d in line %d. Defaulting to MAX_EXP...\n", i + 1, current );
+			exp = MAX_EXP;
+		}else if( exp > MAX_EXP ){
+			ShowWarning( "pc_readdb_job_exp: Value %" PRIu64 " is too high, capping to %" PRIu64 "...\n", exp, MAX_EXP );
+			exp = MAX_EXP;
+		}
+
+		job_info[idx].exp_table[type][i] = exp;
+	}
 	//Reverse check in case the array has a bunch of trailing zeros... [Skotlex]
 	//The reasoning behind the -2 is this... if the max level is 5, then the array
 	//should look like this:

+ 6 - 6
src/map/pc.hpp

@@ -894,7 +894,7 @@ struct s_job_info {
 #else
 	int aspd_base[MAX_WEAPON_TYPE];	//[blackhole89]
 #endif
-	uint32 exp_table[2][MAX_LEVEL];
+	t_exp exp_table[2][MAX_LEVEL];
 	uint32 max_level[2];
 	struct s_params {
 		uint16 str, agi, vit, int_, dex, luk;
@@ -1201,11 +1201,11 @@ bool pc_is_maxbaselv(struct map_session_data *sd);
 bool pc_is_maxjoblv(struct map_session_data *sd);
 int pc_checkbaselevelup(struct map_session_data *sd);
 int pc_checkjoblevelup(struct map_session_data *sd);
-void pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int base_exp, unsigned int job_exp, uint8 exp_flag);
-void pc_gainexp_disp(struct map_session_data *sd, unsigned int base_exp, unsigned int next_base_exp, unsigned int job_exp, unsigned int next_job_exp, bool lost);
-void pc_lostexp(struct map_session_data *sd, unsigned int base_exp, unsigned int job_exp);
-unsigned int pc_nextbaseexp(struct map_session_data *sd);
-unsigned int pc_nextjobexp(struct map_session_data *sd);
+void pc_gainexp(struct map_session_data *sd, struct block_list *src, t_exp base_exp, t_exp job_exp, uint8 exp_flag);
+void pc_gainexp_disp(struct map_session_data *sd, t_exp base_exp, t_exp next_base_exp, t_exp job_exp, t_exp next_job_exp, bool lost);
+void pc_lostexp(struct map_session_data *sd, t_exp base_exp, t_exp job_exp);
+t_exp pc_nextbaseexp(struct map_session_data *sd);
+t_exp pc_nextjobexp(struct map_session_data *sd);
 int pc_gets_status_point(int);
 int pc_need_status_point(struct map_session_data *,int,int);
 int pc_maxparameterincrease(struct map_session_data*,int);

+ 8 - 7
src/map/skill.cpp

@@ -6633,7 +6633,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			skill_area_temp[0] = battle_config.exp_cost_redemptio_limit - skill_area_temp[0]; // The actual penalty...
 			if (skill_area_temp[0] > 0 && !map_getmapflag(src->m, MF_NOEXPPENALTY) && battle_config.exp_cost_redemptio) { //Apply penalty
 				//If total penalty is 1% => reduced 0.2% penalty per each revived player
-				pc_lostexp(sd, u32min(sd->status.base_exp, (pc_nextbaseexp(sd) * skill_area_temp[0] * battle_config.exp_cost_redemptio / battle_config.exp_cost_redemptio_limit) / 100), 0);
+				pc_lostexp(sd, u64min(sd->status.base_exp, (pc_nextbaseexp(sd) * skill_area_temp[0] * battle_config.exp_cost_redemptio / battle_config.exp_cost_redemptio_limit) / 100), 0);
 			}
 			status_set_hp(src, 1, 0);
 			status_set_sp(src, 0, 0);
@@ -6675,14 +6675,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 				clif_skill_nodamage(src,bl,ALL_RESURRECTION,skill_lv,1); //Both Redemptio and Res show this skill-animation.
 				if(sd && dstsd && battle_config.resurrection_exp > 0)
 				{
-					int exp = 0,jexp = 0;
+					t_exp exp = 0,jexp = 0;
 					int lv = dstsd->status.base_level - sd->status.base_level, jlv = dstsd->status.job_level - sd->status.job_level;
 					if(lv > 0 && pc_nextbaseexp(dstsd)) {
-						exp = (int)((double)dstsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+						exp = (t_exp)(dstsd->status.base_exp * lv * battle_config.resurrection_exp / 1000000.);
 						if (exp < 1) exp = 1;
 					}
 					if(jlv > 0 && pc_nextjobexp(dstsd)) {
-						jexp = (int)((double)dstsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+						jexp = (t_exp)(dstsd->status.job_exp * lv * battle_config.resurrection_exp / 1000000.);
 						if (jexp < 1) jexp = 1;
 					}
 					if(exp > 0 || jexp > 0)
@@ -10525,7 +10525,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 
 	case LG_INSPIRATION:
 		if( sd && !map_getmapflag(sd->bl.m, MF_NOEXPPENALTY) && battle_config.exp_cost_inspiration )
-			pc_lostexp(sd, u32min(sd->status.base_exp, pc_nextbaseexp(sd) * battle_config.exp_cost_inspiration / 100), 0); // 1% penalty.
+			pc_lostexp(sd, u64min(sd->status.base_exp, pc_nextbaseexp(sd) * battle_config.exp_cost_inspiration / 100), 0); // 1% penalty.
 		clif_skill_nodamage(bl,src,skill_id,skill_lv, sc_start(src,bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
 		break;
 	case SR_CURSEDCIRCLE:
@@ -15809,7 +15809,8 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i
 		case PR_REDEMPTIO:
 		case LG_INSPIRATION:
 			{
-				unsigned int exp, exp_needp = 0;
+				t_exp exp;
+				uint32 exp_needp;
 				switch (skill_id) {
 					case PR_REDEMPTIO:
 						exp_needp = battle_config.exp_cost_redemptio;
@@ -15818,7 +15819,7 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i
 						exp_needp = battle_config.exp_cost_inspiration;
 						break;
 				}
-				if (exp_needp && ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < exp_needp)) {
+				if (exp_needp && ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage_exp(sd->status.base_exp, exp) < exp_needp)) {
 					clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); //Not enough exp.
 					return false;
 				}

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov