EXP system & Mob buffs/diseases optimization

Solved a problem within EXP distribution system that would hand out less overall EXP than the expected when the amount to be earned is low.
Optimized mob buffs and diseases, now using a dedicated thread to process all status expirations on a batch.
Refactored MonitoredLockTypes names to something more easily identificable.
Added a delay on mob effect applications, to be registered in after the cast animation time.
Fixed Flame Thrower acting passively when a attacking skill is used by the player.
This commit is contained in:
ronancpl
2018-06-30 22:48:02 -03:00
parent dac5c43635
commit 94425ba616
57 changed files with 926 additions and 608 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
/logs/**
/logs/**
.idea/
# build files
+1
View File
@@ -13,6 +13,7 @@ Known issues:
- If there are multiple bosses that shows HPBar on the map, if a player hits more than one the HPBar may start flickering on the screen.
- Sometimes battleship may behave oddly with the enhanced buff system, making the character d/c in certain scenarios.
- Dragon Roar doesn't show the stun effect to players.
- Some monster status such as freeze and weapon/magic reflect doesn't behave properly in certain scenarios. Freeze seems to not work on mobs with low OID or are starters from server boot time.
---------------------------
---------------------------
+11 -1
View File
@@ -1083,4 +1083,14 @@ Adicionado efeito de dispel de weapondef e magicdef up em mobs ao atacar com Sha
Corrigido Shadow Meso consumindo projetil (deveria usar meso somente).
Adicionado recompensa de buff ao completar o HolidayPQ.
Adicionado drops de itens que dão buffs característicos nos reatores da Zakum Prequest.
Scroll shop custom mudou para a NPC Asia em Neo City (anteriormente estava no Spindle).
Scroll shop custom mudou para a NPC Asia em Neo City (anteriormente estava no Spindle).
27 Junho 2018,
Resolvido um problema no sistema de ganho de EXP que daria ao jogador um valor abaixo do que seria o normal nos casos onde o EXP ganho é baixo.
Otimizado mob buffs e diseases agora utilizando thread dedicada para lidar com a duração, anteriormente eram criadas novas schedules pra cada instância.
Refatorado MonitoredLockTypes para agora dar um label específico a cada lock do sistema.
29 - 30 Junho 2018,
Corrigido quest Milk Jug com NPCs trocados.
Adicionado um delay na aplicação de efeitos dos skills de mobs, buffs e demais efeitos agora são registrados após o tempo da animação.
Corrigido Flame Thrower atuando passivamente quando o jogador usa uma skill de ataque.
+1 -1
View File
@@ -241,7 +241,7 @@
4000172 - Three-Tailed Foxtail - A soft foxtail that Three-Tailed Fox removed from itself.
4000173 - Broom - A small broom made from bushes that Blins dropped.
4000174 - Money Envelope - You can sell this envelope for 10,000 meso in the store.
4000175 - Minature Pianus - A miniature version of Pianus
4000175 - Miniature Pianus - A miniature version of Pianus
4000176 - Poisonous Mushroom - A poisonous mushroom that lives on the humongous Zombie Mushroom.
4000177 - Mixed Block - A block from Mix Golem that consists of its chest area.
4000178 - Iron Boar Armor - A small piece of the solid armor from Iron Boar.
+1 -1
View File
@@ -49,7 +49,7 @@ function start() {
if (cm.getLevel() >= 10 && cm.canGetFirstJob(jobType))
cm.sendNext("So you decided to become a #rBowman#k?");
else {
cm.sendOk("Train a bit more and I can show you the way of the #rBowman#k.");
cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rBowman#k.");
cm.dispose();
}
} else if (cm.getLevel() >= 30 && cm.getJobId() == 300) {
+1 -1
View File
@@ -50,7 +50,7 @@ function start() {
if (cm.getLevel() >= 10 && cm.canGetFirstJob(jobType))
cm.sendNext("Do you want to become a Warrior? You need to meet some criteria in order to do so.#b You should be at least in level 10, with at least 35 in STR#k. Let's see...");
else {
cm.sendOk("Train a bit more and I can show you the way of the #rWarrior#k.");
cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rWarrior#k.");
cm.dispose();
}
} else if (cm.getLevel() >= 30 && cm.getJobId() == 100) {
+1 -1
View File
@@ -50,7 +50,7 @@ function start() {
if (cm.getLevel() >= 8 && cm.canGetFirstJob(jobType))
cm.sendNext("Want to be a magician? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 8#k, with getting INT as your top priority. Let's see.");
else {
cm.sendOk("Train a bit more and I can show you the way of the #rMagician#k.");
cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rMagician#k.");
cm.dispose();
}
} else if (cm.getLevel() >= 30 && cm.getJobId() == 200) {
+1 -1
View File
@@ -49,7 +49,7 @@ function start() {
if (cm.getLevel() >= 10 && cm.canGetFirstJob(jobType))
cm.sendNext("Want to be a thief? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 10, with your DEX over 25#k. Let's see.");
else {
cm.sendOk("Train a bit more and I can show you the way of the #rThief#k.");
cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rThief#k.");
cm.dispose();
}
} else if (cm.getLevel() >= 30 && cm.getJobId() == 400) {
+1 -1
View File
@@ -49,7 +49,7 @@ function start() {
if (cm.getLevel() >= 10 && cm.canGetFirstJob(jobType))
cm.sendNext("Want to be a pirate? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 10#k. Let's see.");
else {
cm.sendOk("Train a bit more and I can show you the way of the #rPirate#k.");
cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rPirate#k.");
cm.dispose();
}
} else if (cm.getLevel() >= 30 && cm.getJobId() == 500) {
+26 -26
View File
@@ -19,34 +19,34 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
status = -1;
function start() {
if(cm.haveItem(4031847))
cm.sendNext("The hungry calf is drinking all the milk! The bottle remains empty...");
else if(cm.haveItem(4031848) || cm.haveItem(4031849) || cm.haveItem(4031850)){
cm.sendNext("The hungry calf is drinking all the milk! The bottle is now empty.");
if(cm.haveItem(4031848))
cm.gainItem(4031848,-1);
else if(cm.haveItem(4031849))
cm.gainItem(4031849, -1);
else
cm.gainItem(4031850, -1);
cm.gainItem(4031847, 1);
if(cm.getQuestProgress(2180, 0) == 1) {
cm.sendNext("You have taken milk from this cow recently, check another cow.");
cm.dispose();
return;
}
}
function action(mode, type, selection){
if(mode == -1)
cm.dispose();
else if(mode == 0){
status--;
start();
}else
status++;
if(status == 0)
cm.sendPrev("The hungry calf isn't interested in the empty bottle.");
else if(status == 1)
cm.dispose();
if (cm.canHold(4031848) && cm.haveItem(4031847)) {
cm.sendNext("Now filling up the bottle with milk. The bottle is now 1/3 full of milk.");
cm.gainItem(4031847, -1);
cm.gainItem(4031848, 1);
cm.setQuestProgress(2180, 0, 1);
} else if (cm.canHold(4031849, 1) && cm.haveItem(4031848)) {
cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk.");
cm.gainItem(4031848, -1);
cm.gainItem(4031849, 1);
cm.setQuestProgress(2180, 0, 1);
} else if (cm.canHold(4031850) && cm.haveItem(4031849)) {
cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk.");
cm.gainItem(4031849, -1);
cm.gainItem(4031850, 1);
cm.setQuestProgress(2180, 0, 1);
} else {
cm.sendNext("Your inventory is full, and there's no room for a milk bottle.");
}
cm.dispose();
}
+26 -27
View File
@@ -20,34 +20,33 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
status = -1;
function start() {
if (cm.haveItem(4031847))
cm.sendNext("The hungry calf is drinking all the milk! The bottle remains empty...");
else if (cm.haveItem(4031848) || cm.haveItem(4031849) || cm.haveItem(4031850)) {
cm.sendNext("The hungry calf is drinking all the milk! The bottle is now empty.");
if (cm.haveItem(4031848))
cm.gainItem(4031848,-1);
else if (cm.haveItem(4031849))
cm.gainItem(4031849, -1);
else
cm.gainItem(4031850, -1);
cm.gainItem(4031847, 1);
if(cm.getQuestProgress(2180, 0) == 2) {
cm.sendNext("You have taken milk from this cow recently, check another cow.");
cm.dispose();
return;
}
}
function action(mode, type, selection){
if (mode == -1)
cm.dispose();
else if (mode == 0) {
status--;
start();
} else
status++;
if (status == 0)
cm.sendPrev("The hungry calf isn't interested in the empty bottle.");
else if (status == 1)
cm.dispose();
if (cm.canHold(4031848) && cm.haveItem(4031847)){
cm.sendNext("Now filling up the bottle with milk. The bottle is now 1/3 full of milk.");
cm.gainItem(4031847, -1);
cm.gainItem(4031848, 1);
cm.setQuestProgress(2180, 0, 2);
} else if(cm.canHold(4031849) && cm.haveItem(4031848)){
cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk.");
cm.gainItem(4031848, -1);
cm.gainItem(4031849, 1);
cm.setQuestProgress(2180, 0, 2);
} else if(cm.canHold(4031850) && cm.haveItem(4031849)){
cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk.");
cm.gainItem(4031849, -1);
cm.gainItem(4031850, 1);
cm.setQuestProgress(2180, 0, 2);
} else {
cm.sendNext("Your inventory is full, and there's no room for a milk bottle.");
}
cm.dispose();
}
+28 -15
View File
@@ -19,21 +19,34 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
status = -1;
function start() {
if (cm.canHold(4031848) && cm.haveItem(4031847)) {
cm.sendNext("Now filling up the bottle with milk. The bottle is now 1/3 full of milk.");
cm.gainItem(4031847, -1);
cm.gainItem(4031848, 1);
} else if (cm.canHold(4031849, 1) && cm.haveItem(4031848)) {
cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk.");
cm.gainItem(4031848, -1);
cm.gainItem(4031849, 1);
} else if (cm.canHold(4031850) && cm.haveItem(4031849)) {
cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk.");
cm.gainItem(4031849, -1);
cm.gainItem(4031850, 1);
} else
cm.sendNext("Your inventory is full, and there's no room for a milk bottle.");
cm.dispose();
if(cm.haveItem(4031847))
cm.sendNext("The hungry calf is drinking all the milk! The bottle remains empty...");
else if(cm.haveItem(4031848) || cm.haveItem(4031849) || cm.haveItem(4031850)){
cm.sendNext("The hungry calf is drinking all the milk! The bottle is now empty.");
if(cm.haveItem(4031848))
cm.gainItem(4031848,-1);
else if(cm.haveItem(4031849))
cm.gainItem(4031849, -1);
else
cm.gainItem(4031850, -1);
cm.gainItem(4031847, 1);
cm.dispose();
}
}
function action(mode, type, selection){
if(mode == -1)
cm.dispose();
else if(mode == 0){
status--;
start();
}else
status++;
if(status == 0)
cm.sendPrev("The hungry calf isn't interested in the empty bottle.");
else if(status == 1)
cm.dispose();
}
+28 -14
View File
@@ -20,20 +20,34 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
status = -1;
function start() {
if (cm.canHold(4031848) && cm.haveItem(4031847)){
cm.sendNext("Now filling up the bottle with milk. The bottle is now 1/3 full of milk.");
cm.gainItem(4031847, -1);
cm.gainItem(4031848, 1);
} else if(cm.canHold(4031849) && cm.haveItem(4031848)){
cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk.");
cm.gainItem(4031848, -1);
cm.gainItem(4031849, 1);
} else if(cm.canHold(4031850) && cm.haveItem(4031849)){
cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk.");
cm.gainItem(4031849, -1);
cm.gainItem(4031850, 1);
if (cm.haveItem(4031847))
cm.sendNext("The hungry calf is drinking all the milk! The bottle remains empty...");
else if (cm.haveItem(4031848) || cm.haveItem(4031849) || cm.haveItem(4031850)) {
cm.sendNext("The hungry calf is drinking all the milk! The bottle is now empty.");
if (cm.haveItem(4031848))
cm.gainItem(4031848,-1);
else if (cm.haveItem(4031849))
cm.gainItem(4031849, -1);
else
cm.gainItem(4031850, -1);
cm.gainItem(4031847, 1);
cm.dispose();
}
}
function action(mode, type, selection){
if (mode == -1)
cm.dispose();
else if (mode == 0) {
status--;
start();
} else
cm.sendNext("Your inventory is full, and there's no room for a milk bottle.");
cm.dispose();
status++;
if (status == 0)
cm.sendPrev("The hungry calf isn't interested in the empty bottle.");
else if (status == 1)
cm.dispose();
}
+3
View File
@@ -144,6 +144,7 @@ function writeFeatureTab_Serverpotentials() {
addFeature("Enhanced auto-pot system: smart pet potion handle.");
addFeature("Enhanced buff system: best buffs effects takes place.");
addFeature("Enhanced AP auto-assigner: focus on eqp demands.");
addFeature("Consistent experience gain system.");
addFeature("NPC crafters won't take items freely anymore.");
addFeature("Duey: pkg rcvd popup and many delivery mechanics.");
addFeature("Pet pickup gives preference to player attacks.");
@@ -169,6 +170,7 @@ function writeFeatureTab_AdminGMcommands() {
function writeFeatureTab_CustomNPCs() {
addFeature("Spiegelmann: automatized rock-refiner.");
addFeature("Asia: scroll & rarities shop NPC.");
addFeature("Abdula: lists droppers of needed skill/mastery books.");
addFeature("Agent E: accessory crafter.");
addFeature("Donation Box: automatized item-buyer.");
@@ -197,6 +199,7 @@ function writeFeatureTab_Project() {
addFeature("Reviewed many Java aspects that needed attention.");
addFeature("Reviewed SQL data, eliminating duplicated entries.");
addFeature("Protected many flaws with login management system.");
addFeature("Developed many survey tools for content management.");
addFeature("ThreadTracker: runtime tool for deadlock detection.");
addFeature("Heavily reviewed future task management, spawning much less threads and relieving task overload on the TimerManager.");
}
+2 -2
View File
@@ -22,8 +22,8 @@ function end(mode, type, selection) {
qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Dawn Warrior?");
} else if (status == 1) {
if(!qm.canGetFirstJob(jobType)) {
cm.sendOk("Train a bit more and I can show you the way of the #rDawn Warrior#k.");
cm.dispose();
qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rDawn Warrior#k.");
qm.dispose();
return;
}
+2 -2
View File
@@ -22,8 +22,8 @@ function end(mode, type, selection) {
qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Blaze Wizard?");
} else if (status == 1) {
if(!qm.canGetFirstJob(jobType)) {
cm.sendOk("Train a bit more and I can show you the way of the #rBlaze Wizard#k.");
cm.dispose();
qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rBlaze Wizard#k.");
qm.dispose();
return;
}
+2 -2
View File
@@ -22,8 +22,8 @@ function end(mode, type, selection) {
qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Wind Archer?");
} else if (status == 1) {
if(!qm.canGetFirstJob(jobType)) {
cm.sendOk("Train a bit more and I can show you the way of the #rWind Archer#k.");
cm.dispose();
qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rWind Archer#k.");
qm.dispose();
return;
}
+2 -2
View File
@@ -22,8 +22,8 @@ function end(mode, type, selection) {
qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Night Walker?");
} else if (status == 1) {
if(!qm.canGetFirstJob(jobType)) {
cm.sendOk("Train a bit more and I can show you the way of the #rNight Walker#k.");
cm.dispose();
qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rNight Walker#k.");
qm.dispose();
return;
}
+2 -2
View File
@@ -22,8 +22,8 @@ function end(mode, type, selection) {
qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Thunder Breaker?");
} else if (status == 1) {
if(!qm.canGetFirstJob(jobType)) {
cm.sendOk("Train a bit more and I can show you the way of the #rThunder Breaker#k.");
cm.dispose();
qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rThunder Breaker#k.");
qm.dispose();
return;
}
+1 -1
View File
@@ -41,7 +41,7 @@ function end(mode, type, selection) {
if (status == 0) {
if(qm.haveItem(5460000)) {
qm.sendOk("You got the Pet Snack! Thanks! You can use these to feed multiple pets at once!");
qm.teachSkill(0008, 1, 1, -1);
qm.teachSkill(8, 1, 1, -1);
qm.gainItem(5460000, -1, false);
qm.completeQuest();
qm.dispose();
+1 -1
View File
@@ -20,5 +20,5 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
function act(){
rm.spawnMonster(9400112);
rm.spawnMonster(9400112, 1, 420, 160);
}
+7 -7
View File
@@ -3794,7 +3794,7 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`,
(3575, 4250001, 1442005, 1, 1, 0, 700),
(3576, 4250001, 4000438, 1, 1, 0, 600000),
(3577, 4250001, 4130014, 1, 1, 0, 6000),
(3578, 5090000, 4000413, 1, 1, 0, 600000),
(3578, 5090000, 4000413, 1, 1, 0, 200000),
(3579, 5090001, 4000412, 1, 1, 0, 600000),
(3580, 5100000, 4000048, 1, 1, 0, 600000),
(3581, 5100000, 4003005, 1, 1, 0, 200000),
@@ -7995,7 +7995,7 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`,
(7772, 8180000, 4004001, 1, 1, 0, 100000),
(7773, 8180000, 4004002, 1, 1, 0, 100000),
(7774, 8180000, 4004003, 1, 1, 0, 100000),
(7775, 8180000, 4000235, 1, 1, 0, 600000),
(7775, 8180000, 4000235, 1, 1, 0, 200000),
(7776, 8180000, 4000244, 1, 1, 0, 20000),
(7777, 8180000, 4000245, 1, 1, 0, 20000),
(7778, 8180000, 2290003, 1, 1, 0, 5000),
@@ -8052,7 +8052,7 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`,
(7829, 8180001, 4004001, 1, 1, 0, 100000),
(7830, 8180001, 4004002, 1, 1, 0, 100000),
(7831, 8180001, 4004003, 1, 1, 0, 100000),
(7832, 8180001, 4000243, 1, 1, 0, 600000),
(7832, 8180001, 4000243, 1, 1, 0, 200000),
(7833, 8180001, 4000244, 1, 1, 0, 20000),
(7834, 8180001, 4000245, 1, 1, 0, 20000),
(7835, 8180001, 2290018, 1, 1, 0, 5000),
@@ -10413,10 +10413,10 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`,
(10190, 9400546, 4031681, 1, 1, 4915, 1000000),
(10191, 9600005, 4000191, 1, 1, 0, 1000000),
(10192, 9600006, 4000192, 1, 1, 0, 1000000),
(10193, 8500002, 4031196, 1, 1, 0, 1000000),
(10194, 8500002, 4031196, 1, 1, 0, 1000000),
(10195, 8500002, 4031196, 1, 1, 0, 187500),
(10196, 8500002, 4031196, 1, 1, 0, 125000),
(10193, 8500002, 4031196, 1, 1, 0, 80000),
(10194, 8500002, 4031196, 1, 1, 0, 80000),
(10195, 8500002, 4031196, 1, 1, 0, 80000),
(10196, 8500002, 4031196, 1, 1, 0, 80000),
(10197, 3000006, 4031209, 1, 1, 3072, 500000),
(10198, 2230109, 4031209, 1, 1, 3072, 500000),
(10199, 2230200, 4031209, 1, 1, 3072, 500000),
+23 -23
View File
@@ -15794,15 +15794,15 @@ USE `heavenms`;
(6400006, 4030012, 1, 1, 0, 125000),
(6400009, 4030012, 1, 1, 0, 125000),
(9303013, 4030012, 1, 1, 0, 125000),
(8150000, 4031906, 1, 1, 0, 7000),
(9300210, 4031906, 1, 1, 0, 7000),
(9500140, 4031906, 1, 1, 0, 7000),
(9500171, 4031906, 1, 1, 0, 7000),
(9500328, 4031906, 1, 1, 0, 7000),
(9500358, 4031906, 1, 1, 0, 7000),
(6400006, 4031906, 1, 1, 0, 7000),
(6400009, 4031906, 1, 1, 0, 7000),
(9303013, 4031906, 1, 1, 0, 7000),
(8150000, 4031906, 1, 1, 0, 400000),
(9300210, 4031906, 1, 1, 0, 400000),
(9500140, 4031906, 1, 1, 0, 400000),
(9500171, 4031906, 1, 1, 0, 400000),
(9500328, 4031906, 1, 1, 0, 400000),
(9500358, 4031906, 1, 1, 0, 400000),
(6400006, 4031906, 1, 1, 0, 400000),
(6400009, 4031906, 1, 1, 0, 400000),
(9303013, 4031906, 1, 1, 0, 400000),
(8150000, 2043002, 1, 4, 0, 10000),
(9300210, 2043002, 1, 4, 0, 10000),
(9500140, 2043002, 1, 4, 0, 10000),
@@ -17473,18 +17473,18 @@ USE `heavenms`;
(8200010, 1072225, 1, 1, 0, 700),
(8200010, 1082158, 1, 1, 0, 700),
(8200010, 1002646, 1, 1, 0, 700),
(8500002, 4031196, 1, 1, 0, 7000),
(9300214, 4031196, 1, 1, 0, 7000),
(9500180, 4031196, 1, 1, 0, 7000),
(9500181, 4031196, 1, 1, 0, 7000),
(9500331, 4031196, 1, 1, 0, 7000),
(9500362, 4031196, 1, 1, 0, 7000),
(8500002, 4031901, 1, 1, 0, 7000),
(9300214, 4031901, 1, 1, 0, 7000),
(9500180, 4031901, 1, 1, 0, 7000),
(9500181, 4031901, 1, 1, 0, 7000),
(9500331, 4031901, 1, 1, 0, 7000),
(9500362, 4031901, 1, 1, 0, 7000),
(8500002, 4031196, 1, 1, 0, 80000),
(9300214, 4031196, 1, 1, 0, 80000),
(9500180, 4031196, 1, 1, 0, 80000),
(9500181, 4031196, 1, 1, 0, 80000),
(9500331, 4031196, 1, 1, 0, 80000),
(9500362, 4031196, 1, 1, 0, 80000),
(8500002, 4031901, 1, 1, 0, 400000),
(9300214, 4031901, 1, 1, 0, 400000),
(9500180, 4031901, 1, 1, 0, 400000),
(9500181, 4031901, 1, 1, 0, 400000),
(9500331, 4031901, 1, 1, 0, 400000),
(9500362, 4031901, 1, 1, 0, 400000),
(8500002, 4001084, 1, 1, 0, 7000),
(9300214, 4001084, 1, 1, 0, 7000),
(9500180, 4001084, 1, 1, 0, 7000),
@@ -20775,7 +20775,7 @@ USE `heavenms`;
UPDATE drop_data SET questid=28248 WHERE itemid=4001358;
UPDATE drop_data SET questid=28248 WHERE itemid=4001359;
UPDATE drop_data SET questid=28175 WHERE itemid=4001342;
UPDATE drop_data SET questid=7777 WHERE itemid=4031906; #id 7777 for ALL "quest items" with no v83 quest.
UPDATE drop_data SET questid=7777 WHERE itemid=4031905; #id 7777 for ALL "quest items" with no v83 quest.
UPDATE drop_data SET chance=0 WHERE itemid=2050099;
UPDATE drop_data SET questid=6191 WHERE itemid=4031477;
UPDATE drop_data SET questid=6190 WHERE itemid=4001111;
@@ -21364,7 +21364,7 @@ USE `heavenms`;
(9300270, 2022431, 1, 1, 0, 200000),
(9300270, 2022432, 1, 1, 0, 200000),
(9300270, 2022433, 1, 1, 0, 200000),
(6090002, 4000414, 1, 1, 0, 400000),
(6090002, 4000414, 1, 1, 0, 200000),
(9300147, 4001132, 1, 1, 0, 400000),
(9300148, 4001133, 1, 1, 0, 100000),
(4300012, 4000537, 1, 1, 0, 200000),
+4 -4
View File
@@ -273,10 +273,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private ScheduledFuture<?> extraRecoveryTask = null;
private ScheduledFuture<?> chairRecoveryTask = null;
private ScheduledFuture<?> pendantOfSpirit = null; //1122017
private Lock chrLock = new MonitoredReentrantLock(MonitoredLockType.CHR, true);
private Lock effLock = new MonitoredReentrantLock(MonitoredLockType.EFF, true);
private Lock petLock = new MonitoredReentrantLock(MonitoredLockType.PET, true); // for quest tasks as well
private Lock prtLock = new MonitoredReentrantLock(MonitoredLockType.PRT);
private Lock chrLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_CHR, true);
private Lock effLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_EFF, true);
private Lock petLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_PET, true); // for quest tasks as well
private Lock prtLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_PRT);
private Map<Integer, Set<Integer>> excluded = new LinkedHashMap<>();
private Set<Integer> excludedItems = new LinkedHashSet<>();
private static String[] ariantroomleader = new String[3];
+2 -2
View File
@@ -107,8 +107,8 @@ public class MapleClient {
private byte gender = -1;
private boolean disconnecting = false;
private final Lock lock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true);
private final Lock encoderLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true);
private static final Lock loginLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true);
private final Lock encoderLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT_ENCODER, true);
private static final Lock loginLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT_LOGIN, true);
private int votePoints;
private int voteTime = -1;
private long lastNpcClick;
+1 -29
View File
@@ -23,7 +23,6 @@ package client.status;
import client.Skill;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import server.life.MobSkill;
import tools.ArrayMap;
@@ -33,9 +32,7 @@ public class MonsterStatusEffect {
private Skill skill;
private MobSkill mobskill;
private boolean monsterSkill;
private ScheduledFuture<?> cancelTask;
private ScheduledFuture<?> damageSchedule;
public MonsterStatusEffect(Map<MonsterStatus, Integer> stati, Skill skillId, MobSkill mobskill, boolean monsterSkill) {
this.stati = new ArrayMap<>(stati);
this.skill = skillId;
@@ -59,35 +56,10 @@ public class MonsterStatusEffect {
return monsterSkill;
}
public final void cancelTask() {
if (cancelTask != null) {
cancelTask.cancel(false);
}
cancelTask = null;
}
public ScheduledFuture<?> getCancelTask() {
return cancelTask;
}
public void setCancelTask(ScheduledFuture<?> cancelTask) {
this.cancelTask = cancelTask;
}
public void removeActiveStatus(MonsterStatus stat) {
stati.remove(stat);
}
public void setDamageSchedule(ScheduledFuture<?> damageSchedule) {
this.damageSchedule = damageSchedule;
}
public void cancelDamageSchedule() {
if (damageSchedule != null) {
damageSchedule.cancel(false);
}
}
public MobSkill getMobSkill() {
return mobskill;
}
+4
View File
@@ -123,6 +123,10 @@ public class ServerConstants {
public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map.
public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions.
//Channel Mob Disease Monitor Configuration
public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule.
public static final int MOB_STATUS_MONITOR_LIFE = 84; //Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity.
//Some Gameplay Enhancing Configurations
//Scroll Configuration
public static final boolean USE_PERFECT_GM_SCROLL = true; //Scrolls from GMs never uses up slots nor fails.
+2 -2
View File
@@ -96,8 +96,8 @@ public class Server {
private final Map<Integer, MapleGuild> guilds = new HashMap<>(100);
private final Map<MapleClient, Long> inLoginState = new HashMap<>(100);
private final Lock srvLock = new MonitoredReentrantLock(MonitoredLockType.SERVER);
private final Lock lgnLock = new MonitoredReentrantLock(MonitoredLockType.SERVER);
private final Lock disLock = new MonitoredReentrantLock(MonitoredLockType.SERVER);
private final Lock lgnLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_LOGIN);
private final Lock disLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_DISEASES);
private final PlayerBuffStorage buffStorage = new PlayerBuffStorage();
private final Map<Integer, MapleAlliance> alliances = new HashMap<>(100);
private final Map<Integer, NewYearCardRecord> newyears = new HashMap<>();
+1 -1
View File
@@ -233,7 +233,7 @@ public class ThreadTracker {
List<MonitoredLockType> list = threadTracker.get(tid);
for(int i = list.size() - 1; i >= 0; i--) {
if(lockId.getValue() == list.get(i).getValue()) {
if(lockId.equals(list.get(i))) {
list.remove(i);
break;
}
+35
View File
@@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.channel;
import net.server.channel.worker.MobStatusScheduler;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.ArrayList;
@@ -70,6 +71,7 @@ import server.maps.MapleMiniDungeon;
import tools.MaplePacketCreator;
import tools.Pair;
import client.MapleCharacter;
import client.status.MonsterStatusEffect;
import constants.ServerConstants;
import server.maps.MapleMiniDungeonInfo;
import tools.locks.MonitoredLockType;
@@ -83,6 +85,7 @@ public final class Channel {
private String ip, serverMessage;
private MapleMapFactory mapFactory;
private EventScriptManager eventSM;
private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[4];
private Map<Integer, MapleHiredMerchant> hiredMerchants = new HashMap<>();
private final Map<Integer, Integer> storedVars = new HashMap<>();
private List<MapleExpedition> expeditions = new ArrayList<>();
@@ -149,6 +152,10 @@ public final class Channel {
dojoTask[i] = null;
}
for(int i = 0; i < 4; i++) {
mobStatusSchedulers[i] = new MobStatusScheduler();
}
System.out.println(" Channel " + getId() + ": Listening on port " + port);
} catch (Exception e) {
e.printStackTrace();
@@ -817,6 +824,34 @@ public final class Channel {
}
}
private static int getMobStatusSchedulerIndex(int mapid) {
if(mapid >= 250000000) {
if(mapid >= 900000000) {
return 3;
} else {
return 2;
}
} else {
if(mapid >= 200000000) {
return 1;
} else {
return 0;
}
}
}
public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration) {
registerMobStatus(mapid, mse, cancelAction, duration, null, -1);
}
public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration, Runnable overtimeAction, int overtimeDelay) {
mobStatusSchedulers[getMobStatusSchedulerIndex(mapid)].registerMobStatus(mse, cancelAction, duration, overtimeAction, overtimeDelay);
}
public void interruptMobStatus(int mapid, MonsterStatusEffect mse) {
mobStatusSchedulers[getMobStatusSchedulerIndex(mapid)].interruptMobStatus(mse);
}
public void debugMarriageStatus() {
System.out.println(" ----- WORLD DATA -----");
Server.getInstance().getWorld(world).debugMarriageStatus();
@@ -326,6 +326,15 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
} else if (attack.skill == Outlaw.HOMING_BEACON || attack.skill == Corsair.BULLSEYE) {
player.setMarkedMonster(monster.getObjectId());
player.announce(MaplePacketCreator.giveBuff(1, attack.skill, Collections.singletonList(new Pair<>(MapleBuffStat.HOMING_BEACON, monster.getObjectId()))));
} else if (attack.skill == Outlaw.FLAME_THROWER) {
if (!monster.isBoss()) {
Skill type = SkillFactory.getSkill(Outlaw.FLAME_THROWER);
if (player.getSkillLevel(type) > 0) {
MapleStatEffect DoT = type.getEffect(player.getSkillLevel(type));
MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.POISON, 1), type, null, false);
monster.applyStatus(player, monsterStatusEffect, true, DoT.getDuration(), false);
}
}
}
if (job == 2111 || job == 2112) {
@@ -406,15 +415,6 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
}
}
}
} else if (job == 521 || job == 522) { // from what I can gather this is how it should work
if (!monster.isBoss()) {
Skill type = SkillFactory.getSkill(Outlaw.FLAME_THROWER);
if (player.getSkillLevel(type) > 0) {
MapleStatEffect DoT = type.getEffect(player.getSkillLevel(type));
MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.POISON, 1), type, null, false);
monster.applyStatus(player, monsterStatusEffect, true, DoT.getDuration(), false);
}
}
} else if (job >= 311 && job <= 322) {
if (!monster.isBoss()) {
Skill mortalBlow;
@@ -27,6 +27,7 @@ import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import server.life.MapleMonster;
import server.life.MapleMonsterInformationProvider;
//import server.life.MobAttackInfo;
//import server.life.MobAttackInfoFactory;
import server.life.MobSkill;
@@ -94,20 +95,27 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
} else if (toUse.getHP() < percHpLeft) {
toUse = null;
} else if (monster.canUseSkill(toUse)) {
toUse.applyEffect(c.getPlayer(), monster, true, banishPlayers);
int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(monster.getId(), Random);
if(animationTime > 0) {
toUse.applyDelayedEffect(c.getPlayer(), monster, true, banishPlayers, animationTime);
} else {
toUse.applyEffect(c.getPlayer(), monster, true, banishPlayers);
}
} else {
toUse = null;
}
} else {
toUse = null; // paliative measure for suspicious mob movement
/*
long curtime = System.currentTimeMillis();
if(curtime >= monster.getNextBasicSkillTime()) { // dont use the special attack too often, chase the player f3
//MobAttackInfo mobAttack = MobAttackInfoFactory.getMobAttackInfo(monster, attackId);
//monster.setNextBasicSkillTime(curtime);
toUse = null; // paliative measure for suspicious mob movement
MobAttackInfo mobAttack = MobAttackInfoFactory.getMobAttackInfo(monster, attackId);
monster.setNextBasicSkillTime(curtime);
} else {
toUse = null;
}
*/
}
}
@@ -0,0 +1,148 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.channel.worker;
import client.status.MonsterStatusEffect;
import constants.ServerConstants;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import server.TimerManager;
import tools.Pair;
import tools.locks.MonitoredLockType;
import tools.locks.MonitoredReentrantLock;
/**
*
* @author Ronan
*/
public class MobStatusScheduler {
private int idleProcs = 0;
private Map<MonsterStatusEffect, Pair<Runnable, Long>> registeredMobStatus = new HashMap<>();
private Map<MonsterStatusEffect, MobStatusOvertimeEntry> registeredMobStatusOvertime = new HashMap<>();
private class MobStatusOvertimeEntry {
private int procCount;
private int procLimit;
private Runnable r;
protected MobStatusOvertimeEntry(int delay, Runnable run) {
procCount = 0;
procLimit = (int)Math.ceil((float) delay / ServerConstants.MOB_STATUS_MONITOR_PROC);
r = run;
}
protected void update() {
procCount++;
if(procCount >= procLimit) {
procCount = 0;
r.run();
}
}
}
private ScheduledFuture<?> mobStatusSchedule = null;
private Lock mobStatusLock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_MOBSTATUS, true);
private Runnable monitorTask = new Runnable() {
@Override
public void run() {
runMobStatusSchedule();
}
};
private void runMobStatusSchedule() {
mobStatusLock.lock();
try {
if(registeredMobStatus.isEmpty()) {
idleProcs++;
if(idleProcs >= ServerConstants.MOB_STATUS_MONITOR_LIFE) {
if(mobStatusSchedule != null) {
mobStatusSchedule.cancel(false);
mobStatusSchedule = null;
}
}
return;
}
idleProcs = 0;
long timeNow = System.currentTimeMillis();
List<MonsterStatusEffect> toRemove = new LinkedList<>();
for(Entry<MonsterStatusEffect, Pair<Runnable, Long>> rmd : registeredMobStatus.entrySet()) {
Pair<Runnable, Long> r = rmd.getValue();
if(r.getRight() < timeNow) {
r.getLeft().run(); // runs the cancel action
toRemove.add(rmd.getKey());
}
}
for(MonsterStatusEffect mse : toRemove) {
registeredMobStatus.remove(mse);
registeredMobStatusOvertime.remove(mse);
}
// it's probably ok to use one thread for both management & overtime actions
List<MobStatusOvertimeEntry> mdoeList = new ArrayList<>(registeredMobStatusOvertime.values());
for(MobStatusOvertimeEntry mdoe : mdoeList) {
mdoe.update();
}
} finally {
mobStatusLock.unlock();
}
}
public void registerMobStatus(MonsterStatusEffect mse, Runnable cancelStatus, long duration, Runnable overtimeStatus, int overtimeDelay) {
mobStatusLock.lock();
try {
idleProcs = 0;
if(mobStatusSchedule == null) {
mobStatusSchedule = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC);
}
registeredMobStatus.put(mse, new Pair<>(cancelStatus, System.currentTimeMillis() + duration));
if(overtimeStatus != null) {
MobStatusOvertimeEntry mdoe = new MobStatusOvertimeEntry(overtimeDelay, overtimeStatus);
registeredMobStatusOvertime.put(mse, mdoe);
}
} finally {
mobStatusLock.unlock();
}
}
public void interruptMobStatus(MonsterStatusEffect mse) {
mobStatusLock.lock();
try {
Pair<Runnable, Long> rmd = registeredMobStatus.remove(mse);
if(rmd != null) rmd.getLeft().run();
registeredMobStatusOvertime.remove(mse);
} finally {
mobStatusLock.unlock();
}
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ import net.server.world.World;
/**
* @author Ronan
*/
public class BaseWorker implements Runnable {
public abstract class BaseWorker implements Runnable {
protected World wserv;
@Override
+1 -1
View File
@@ -124,7 +124,7 @@ public class World {
private Map<Runnable, Long> registeredTimedMapObjects = new LinkedHashMap<>();
private ScheduledFuture<?> timedMapObjectsSchedule;
private Lock timedMapObjectLock = new MonitoredReentrantLock(MonitoredLockType.MAP_OBJS, true);
private Lock timedMapObjectLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MAPOBJS, true);
private ScheduledFuture<?> charactersSchedule;
private ScheduledFuture<?> marriagesSchedule;
@@ -978,4 +978,23 @@ public class AbstractPlayerInteraction {
return true;
}
}
public static String getFirstJobStatRequirement(int jobType) {
switch(jobType) {
case 1:
return "STR " + 35;
case 2:
return "INT " + 20;
case 3:
case 4:
return "DEX " + 25;
case 5:
return "DEX " + 20;
}
return null;
}
}
+1 -1
View File
@@ -69,7 +69,7 @@ public abstract class AbstractScriptManager {
}
try (FileReader fr = new FileReader(scriptFile)) {
if (ServerConstants.JAVA_8){
engine.eval("load('nashorn:mozilla_compat.js');");
engine.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
}
engine.eval(fr);
} catch (final ScriptException | IOException t) {
+8 -3
View File
@@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package scripting.item;
import client.MapleClient;
import constants.ServerConstants;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
@@ -29,7 +30,6 @@ import java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.Map;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
@@ -76,8 +76,13 @@ public class ItemScriptManager {
ScriptEngine portal = sef.getScriptEngine();
try {
fr = new FileReader(scriptFile);
CompiledScript compiled = ((Compilable) portal).compile(fr);
compiled.eval();
// java 8 support here thanks to Arufonsu
if (ServerConstants.JAVA_8){
portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
}
((Compilable) portal).compile(fr).eval();
final Invocable script = ((Invocable) portal);
scripts.put(scriptName, script);
+9 -3
View File
@@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package scripting.map;
import client.MapleClient;
import constants.ServerConstants;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
@@ -29,7 +30,6 @@ import java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.Map;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
@@ -80,8 +80,14 @@ public class MapScriptManager {
ScriptEngine portal = sef.getScriptEngine();
try {
fr = new FileReader(scriptFile);
CompiledScript compiled = ((Compilable) portal).compile(fr);
compiled.eval();
// java 8 support here thanks to Arufonsu
if (ServerConstants.JAVA_8){
portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
}
((Compilable) portal).compile(fr).eval();
final Invocable script = ((Invocable) portal);
scripts.put(scriptName, script);
script.invokeFunction("start", new MapScriptMethods(c));
@@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package scripting.portal;
import client.MapleClient;
import constants.ServerConstants;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
@@ -65,6 +66,12 @@ public class PortalScriptManager {
ScriptEngine portal = sef.getScriptEngine();
try {
fr = new FileReader(scriptFile);
// java 8 support here thanks to Arufonsu
if (ServerConstants.JAVA_8){
portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
}
((Compilable) portal).compile(fr).eval();
} catch (ScriptException | IOException | UndeclaredThrowableException e) {
FilePrinter.printError(FilePrinter.PORTAL + scriptName + ".txt", e);
+15 -4
View File
@@ -132,12 +132,23 @@ public class MapleLifeFactory {
stats.setRevives(revives);
}
decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, ""));
MapleData monsterSkillData = monsterInfoData.getChildByPath("skill");
if (monsterSkillData != null) {
MapleData monsterSkillInfoData = monsterInfoData.getChildByPath("skill");
if (monsterSkillInfoData != null) {
int i = 0;
List<Pair<Integer, Integer>> skills = new ArrayList<>();
while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) {
skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillData, 0))));
while (monsterSkillInfoData.getChildByPath(Integer.toString(i)) != null) {
skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillInfoData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillInfoData, 0))));
MapleData monsterSkillData = monsterData.getChildByPath("skill" + i);
if(monsterSkillData != null) {
int animationTime = 0;
for(MapleData effectEntry : monsterSkillData.getChildren()) {
animationTime += MapleDataTool.getIntConvert("delay", effectEntry, 0);
}
MapleMonsterInformationProvider.getInstance().setMobSkillAnimationTime(mid, i, animationTime);
}
i++;
}
stats.setSkills(skills);
+54 -35
View File
@@ -56,6 +56,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredReentrantLock;
import net.server.channel.Channel;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import server.TimerManager;
@@ -320,7 +321,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
return takenDamage.containsKey(chr.getId());
}
private void distributeExperienceToParty(int pid, int exp, int killer, Set<MapleCharacter> underleveled, int minThresholdLevel) {
private void distributeExperienceToParty(int pid, float exp, int killer, Set<MapleCharacter> underleveled, int minThresholdLevel) {
List<MapleCharacter> members = new LinkedList<>();
MapleCharacter pchar = getMap().getAnyCharacterFromParty(pid);
if(pchar != null) {
@@ -353,7 +354,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if (level >= minThresholdLevel) {
boolean isKiller = killer == id;
boolean mostDamage = mostDamageCid == id;
int xp = (int) ((0.80f * exp * level) / partyLevel);
float xp = ((0.80f * exp * level) / partyLevel);
if (mostDamage) {
xp += (0.20f * exp);
}
@@ -378,14 +379,14 @@ public class MapleMonster extends AbstractLoadedMapleLife {
int minThresholdLevel = calcThresholdLevel(this.getMap().getEventInstance() != null);
int exp = getExp();
long totalHealth = maxHpPlusHeal.get();
Map<Integer, Integer> expDist = new HashMap<>();
Map<Integer, Integer> partyExp = new HashMap<>();
Map<Integer, Float> expDist = new HashMap<>();
Map<Integer, Float> partyExp = new HashMap<>();
float exp8perHp = (0.8f * exp) / totalHealth; // 80% of pool is split amongst all the damagers
float exp2 = (0.2f * exp); // 20% of pool goes to the killer or his/her party
for (Entry<Integer, AtomicInteger> damage : takenDamage.entrySet()) {
expDist.put(damage.getKey(), (int) (Math.min((exp8perHp * damage.getValue().get()), Integer.MAX_VALUE)));
expDist.put(damage.getKey(), exp8perHp * damage.getValue().get());
}
Collection<MapleCharacter> chrs = map.getCharacters();
@@ -393,15 +394,16 @@ public class MapleMonster extends AbstractLoadedMapleLife {
for (MapleCharacter mc : chrs) {
if (expDist.containsKey(mc.getId())) {
boolean isKiller = (mc.getId() == killerId);
int xp = expDist.get(mc.getId());
float xp = expDist.get(mc.getId());
if (isKiller) {
xp = (int)Math.min(exp2 + xp, Integer.MAX_VALUE);
xp += exp2;
}
MapleParty p = mc.getParty();
if (p != null) {
int pID = p.getId();
long pXP = (long) xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0);
partyExp.put(pID, (int)Math.min(pXP, Integer.MAX_VALUE));
float pXP = xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0);
partyExp.put(pID, pXP);
} else {
if(mc.getLevel() >= minThresholdLevel) {
//NO EXP WILL BE GIVEN for those who are underleveled!
@@ -413,7 +415,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
}
for (Entry<Integer, Integer> party : partyExp.entrySet()) {
for (Entry<Integer, Float> party : partyExp.entrySet()) {
distributeExperienceToParty(party.getKey(), party.getValue(), killerId, underleveled, minThresholdLevel);
}
@@ -422,7 +424,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
}
public void giveExpToCharacter(MapleCharacter attacker, int exp, boolean isKiller, int numExpSharers) {
public void giveExpToCharacter(MapleCharacter attacker, float exp, boolean isKiller, int numExpSharers) {
if (isKiller) {
if (getMap().getEventInstance() != null) {
getMap().getEventInstance().monsterKilled(attacker, this);
@@ -434,9 +436,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
int partyExp = 0;
if (attacker.getHp() > 0) {
int personalExp = exp * attacker.getExpRate();
exp *= attacker.getExpRate();
int personalExp = (int) exp;
if (exp > 0) {
if (exp <= Integer.MAX_VALUE) { // assuming no negative xp here
if (partyModifier > 0.0f) {
partyExp = (int) (personalExp * partyModifier * ServerConstants.PARTY_BONUS_EXP_RATE);
}
@@ -444,7 +447,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if (holySymbol != null) {
personalExp *= 1.0 + (holySymbol.doubleValue() / 100.0);
}
statiLock.lock();
try {
if (stati.containsKey(MonsterStatus.SHOWDOWN)) {
@@ -453,6 +456,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
} finally {
statiLock.unlock();
}
} else {
personalExp = Integer.MAX_VALUE;
}
attacker.gainExp(personalExp, partyExp, true, false, isKiller);
@@ -777,9 +782,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
byte[] packet = MaplePacketCreator.applyMonsterStatus(getObjectId(), status, null);
map.broadcastMessage(packet, getPosition());
MapleCharacter controller = getController();
if (controller != null && !controller.isMapObjectVisible(this)) {
controller.getClient().announce(packet);
MapleCharacter chrController = getController();
if (chrController != null && !chrController.isMapObjectVisible(this)) {
chrController.getClient().announce(packet);
}
return animationTime;
@@ -832,6 +837,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
}
final Channel ch = map.getChannelServer();
final int mapid = map.getId();
if(statis.size() > 0) {
statiLock.lock();
try {
@@ -840,8 +847,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if (oldEffect != null) {
oldEffect.removeActiveStatus(stat);
if (oldEffect.getStati().isEmpty()) {
oldEffect.cancelTask();
oldEffect.cancelDamageSchedule();
ch.interruptMobStatus(mapid, oldEffect);
}
}
}
@@ -850,7 +856,6 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
}
TimerManager timerManager = TimerManager.getInstance();
final Runnable cancelTask = new Runnable() {
@Override
@@ -875,17 +880,21 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
setVenomMulti(0);
status.cancelDamageSchedule();
}
};
Runnable overtimeAction = null;
int overtimeDelay = -1;
int animationTime;
if (poison) {
int poisonLevel = from.getSkillLevel(status.getSkill());
int poisonDamage = Math.min(Short.MAX_VALUE, (int) (getMaxHp() / (70.0 - poisonLevel) + 0.999));
status.setValue(MonsterStatus.POISON, Integer.valueOf(poisonDamage));
animationTime = broadcastStatusEffect(status);
status.setDamageSchedule(timerManager.register(new DamageTask(poisonDamage, from, status, cancelTask, 0), 1000, 1000));
overtimeAction = new DamageTask(poisonDamage, from, status, 0);
overtimeDelay = 1000;
} else if (venom) {
if (from.getJob() == MapleJob.NIGHTLORD || from.getJob() == MapleJob.SHADOWER || from.getJob().isA(MapleJob.NIGHTWALKER3)) {
int poisonLevel, matk, jobid = from.getJob().getId();
@@ -910,7 +919,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
status.setValue(MonsterStatus.VENOMOUS_WEAPON, Integer.valueOf(poisonDamage));
status.setValue(MonsterStatus.POISON, Integer.valueOf(poisonDamage));
animationTime = broadcastStatusEffect(status);
status.setDamageSchedule(timerManager.register(new DamageTask(poisonDamage, from, status, cancelTask, 0), 1000, 1000));
overtimeAction = new DamageTask(poisonDamage, from, status, 0);
overtimeDelay = 1000;
} else {
return false;
}
@@ -919,7 +930,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
int webDamage = (int) (getMaxHp() / 50.0 + 0.999);
status.setValue(MonsterStatus.SHADOW_WEB, Integer.valueOf(webDamage));
animationTime = broadcastStatusEffect(status);
status.setDamageSchedule(timerManager.schedule(new DamageTask(webDamage, from, status, cancelTask, 1), 3500));
overtimeAction = new DamageTask(webDamage, from, status, 1);
overtimeDelay = 3500;
*/
} else if (status.getSkill().getId() == 4121004 || status.getSkill().getId() == 4221004) { // Ninja Ambush
final Skill skill = SkillFactory.getSkill(status.getSkill().getId());
@@ -928,7 +941,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
status.setValue(MonsterStatus.NINJA_AMBUSH, Integer.valueOf(damage));
animationTime = broadcastStatusEffect(status);
status.setDamageSchedule(timerManager.register(new DamageTask(damage, from, status, cancelTask, 2), 1000, 1000));
overtimeAction = new DamageTask(damage, from, status, 2);
overtimeDelay = 1000;
} else {
animationTime = broadcastStatusEffect(status);
}
@@ -943,12 +958,11 @@ public class MapleMonster extends AbstractLoadedMapleLife {
statiLock.unlock();
}
status.setCancelTask(timerManager.schedule(cancelTask, duration + animationTime - 100));
ch.registerMobStatus(mapid, status, cancelTask, duration + animationTime - 100, overtimeAction, overtimeDelay);
return true;
}
public void applyMonsterBuff(final Map<MonsterStatus, Integer> stats, final int x, int skillId, long duration, MobSkill skill, final List<Integer> reflection) {
TimerManager timerManager = TimerManager.getInstance();
final Runnable cancelTask = new Runnable() {
@Override
@@ -991,7 +1005,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if (controller != null && !controller.isMapObjectVisible(this)) {
controller.getClient().announce(packet);
}
effect.setCancelTask(timerManager.schedule(cancelTask, duration));
map.getChannelServer().registerMobStatus(map.getId(), effect, cancelTask, duration);
}
private void debuffMobStat(MonsterStatus stat) {
@@ -1191,15 +1206,13 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private final int dealDamage;
private final MapleCharacter chr;
private final MonsterStatusEffect status;
private final Runnable cancelTask;
private final int type;
private final MapleMap map;
private DamageTask(int dealDamage, MapleCharacter chr, MonsterStatusEffect status, Runnable cancelTask, int type) {
private DamageTask(int dealDamage, MapleCharacter chr, MonsterStatusEffect status, int type) {
this.dealDamage = dealDamage;
this.chr = chr;
this.status = status;
this.cancelTask = cancelTask;
this.type = type;
this.map = chr.getMap();
}
@@ -1207,20 +1220,26 @@ public class MapleMonster extends AbstractLoadedMapleLife {
@Override
public void run() {
int curHp = hp.get();
if(curHp <= 1) return;
if(curHp <= 1) {
map.getChannelServer().interruptMobStatus(map.getId(), status);
return;
}
int damage = dealDamage;
if (damage >= curHp) {
damage = curHp - 1;
if (type == 1 || type == 2) {
cancelTask.run();
status.getCancelTask().cancel(false);
map.getChannelServer().interruptMobStatus(map.getId(), status);
}
}
if (damage > 0) {
damage(chr, damage, true);
if (type == 1) { // ninja ambush (type 2) is already displaying DOT
if (type == 1) {
map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), damage), getPosition());
} else if (type == 2) {
if(damage < dealDamage) { // ninja ambush (type 2) is already displaying DOT to the caster
map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), damage), getPosition());
}
}
}
}
@@ -53,6 +53,8 @@ public class MapleMonsterInformationProvider {
private final Map<Integer, List<Integer>> dropsChancePool = new HashMap<>(); // thanks to ronan
private final Set<Integer> hasNoMultiEquipDrops = new HashSet<>();
private final Map<Integer, List<MonsterDropEntry>> extraMultiEquipDrops = new HashMap<>();
private final Map<Pair<Integer, Integer>, Integer> mobSkillAnimationTime = new HashMap<>();
protected MapleMonsterInformationProvider() {
retrieveGlobal();
@@ -223,9 +225,17 @@ public class MapleMonsterInformationProvider {
dropsChancePool.put(monsterId, ret);
return ret;
}
public final void setMobSkillAnimationTime(int monsterId, int skillPos, int animationTime) {
mobSkillAnimationTime.put(new Pair<>(monsterId, skillPos), animationTime);
}
public final Integer getMobSkillAnimationTime(int monsterId, int skillPos) {
Integer time = mobSkillAnimationTime.get(new Pair<>(monsterId, skillPos));
return time == null ? 0 : time;
}
public static ArrayList<Pair<Integer, String>> getMobsIDsFromName(String search)
{
public static ArrayList<Pair<Integer, String>> getMobsIDsFromName(String search) {
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
ArrayList<Pair<Integer, String>> retMobs = new ArrayList<Pair<Integer, String>>();
MapleData data = dataProvider.getData("Mob.img");
@@ -243,8 +253,7 @@ public class MapleMonsterInformationProvider {
return retMobs;
}
public static String getMobNameFromId(int id)
{
public static String getMobNameFromId(int id) {
try
{
return MapleLifeFactory.getMonster(id).getName();
@@ -261,8 +270,7 @@ public class MapleMonsterInformationProvider {
}
}
public static String getMobNameFromID(int id)
{
public static String getMobNameFromID(int id) {
try
{
return MapleLifeFactory.getMonster(id).getName();
+21 -9
View File
@@ -32,6 +32,7 @@ import client.status.MonsterStatus;
import constants.GameConstants;
import java.util.LinkedList;
import java.util.Map;
import server.TimerManager;
import tools.Randomizer;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
@@ -103,6 +104,17 @@ public class MobSkill {
this.limit = limit;
}
public void applyDelayedEffect(final MapleCharacter player, final MapleMonster monster, final boolean skill, final List<MapleCharacter> banishPlayers, int animationTime) {
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
if(monster.isAlive()) {
applyEffect(player, monster, skill, banishPlayers);
}
}
}, animationTime);
}
public void applyEffect(MapleCharacter player, MapleMonster monster, boolean skill, List<MapleCharacter> banishPlayers) {
MapleDisease disease = null;
Map<MonsterStatus, Integer> stats = new ArrayMap<MonsterStatus, Integer>();
@@ -282,15 +294,15 @@ public class MobSkill {
System.out.println("Unhandled Mob skill: " + skillId);
break;
}
if (stats.size() > 0) {
if (lt != null && rb != null && skill) {
for (MapleMapObject mons : getObjectsInRange(monster, MapleMapObjectType.MONSTER)) {
((MapleMonster) mons).applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
}
} else {
monster.applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
}
}
if (stats.size() > 0) {
if (lt != null && rb != null && skill) {
for (MapleMapObject mons : getObjectsInRange(monster, MapleMapObjectType.MONSTER)) {
((MapleMonster) mons).applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
}
} else {
monster.applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
}
}
if (disease != null) {
if (lt != null && rb != null && skill) {
int i = 0;
+29 -26
View File
@@ -59,7 +59,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.lang.ref.WeakReference;
import java.util.Comparator;
import net.server.Server;
import net.server.channel.Channel;
import scripting.map.MapScriptManager;
@@ -255,15 +254,43 @@ public class MapleMap {
public int getId() {
return mapid;
}
public Channel getChannelServer() {
return Server.getInstance().getWorld(world).getChannel(channel);
}
public MapleMap getReturnMap() {
if(returnMapId == 999999999) return this;
return Server.getInstance().getWorld(world).getChannel(channel).getMapFactory().getMap(returnMapId);
return getChannelServer().getMapFactory().getMap(returnMapId);
}
public int getReturnMapId() {
return returnMapId;
}
public MapleMap getForcedReturnMap() {
return getChannelServer().getMapFactory().getMap(forcedReturnMap);
}
public int getForcedReturnId() {
return forcedReturnMap;
}
public void setForcedReturnMap(int map) {
this.forcedReturnMap = map;
}
public long getTimeLimit() {
return timeLimit;
}
public void setTimeLimit(int timeLimit) {
this.timeLimit = timeLimit;
}
public int getTimeLeft() {
return (int) ((timeLimit - System.currentTimeMillis()) / 1000);
}
public void setReactorState() {
chrRLock.lock();
@@ -322,30 +349,6 @@ public class MapleMap {
return true;
}
public int getForcedReturnId() {
return forcedReturnMap;
}
public MapleMap getForcedReturnMap() {
return Server.getInstance().getWorld(world).getChannel(channel).getMapFactory().getMap(forcedReturnMap);
}
public void setForcedReturnMap(int map) {
this.forcedReturnMap = map;
}
public long getTimeLimit() {
return timeLimit;
}
public void setTimeLimit(int timeLimit) {
this.timeLimit = timeLimit;
}
public int getTimeLeft() {
return (int) ((timeLimit - System.currentTimeMillis()) / 1000);
}
public int getCurrentPartyId() {
for (MapleCharacter chr : this.getCharacters()) {
if (chr.getPartyId() != -1) {
+1 -1
View File
@@ -55,7 +55,7 @@ public class MapleReactor extends AbstractMapleMapObject {
private boolean attackHit;
private ScheduledFuture<?> timeoutTask = null;
private Lock reactorLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR, true);
private Lock hitLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR, true);
private Lock hitLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR_HIT, true);
public MapleReactor(MapleReactorStats stats, int rid) {
this.evstate = (byte)0;
+4 -3
View File
@@ -3877,12 +3877,13 @@ public class MaplePacketCreator {
}
public static byte[] applyMonsterStatus(final int oid, final MonsterStatusEffect mse, final List<Integer> reflection) {
Map<MonsterStatus, Integer> stati = mse.getStati();
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.APPLY_MONSTER_STATUS.getValue());
mplew.writeInt(oid);
mplew.writeLong(0);
writeIntMask(mplew, mse.getStati());
for (Map.Entry<MonsterStatus, Integer> stat : mse.getStati().entrySet()) {
writeIntMask(mplew, stati);
for (Map.Entry<MonsterStatus, Integer> stat : stati.entrySet()) {
mplew.writeShort(stat.getValue());
if (mse.isMonsterSkill()) {
mplew.writeShort(mse.getMobSkill().getSkillId());
@@ -3892,7 +3893,7 @@ public class MaplePacketCreator {
}
mplew.writeShort(-1); // might actually be the buffTime but it's not displayed anywhere
}
int size = mse.getStati().size(); // size
int size = stati.size(); // size
if (reflection != null) {
for (Integer ref : reflection) {
mplew.writeInt(ref);
+53 -56
View File
@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
Copyleft 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -25,59 +25,56 @@ package tools.locks;
*/
public enum MonitoredLockType {
UNDEFINED(-1),
CHR(0),
EFF(1),
PET(2),
PRT(3),
CLIENT(4),
BOOK(5),
ITEM(6),
INVENTORY(7),
SRVHANDLER_IDLE(8),
SRVHANDLER_TEMP(9),
BUFF_STORAGE(10),
PLAYER_STORAGE(11),
SERVER(12),
MERCHANT(13),
CHANNEL(14),
GUILD(15),
PARTY(16),
WORLD_PARTY(17),
WORLD_OWL(18),
WORLD_PETS(19),
WORLD_MOUNTS(20),
WORLD_PSHOPS(21),
WORLD_MERCHS(21),
EIM(22),
EIM_PARTY(23),
EIM_SCRIPT(24),
EM_LOBBY(25),
EM_QUEUE(26),
CASHSHOP(27),
VISITOR_PSHOP(28),
STORAGE(29),
MOB_EXT(30),
MOB(31),
MOB_STATI(32),
MOBSKILL_FACTORY(33),
PORTAL(34),
VISITOR_MERCH(35),
MAP_CHRS(36),
MAP_OBJS(37),
MAP_FACTORY(38),
MAP_ITEM(39),
MAP_BOUNDS(40),
MINIDUNGEON(41),
REACTOR(42);
private final int i;
private MonitoredLockType(int val) {
this.i = val;
}
public int getValue() {
return i;
}
UNDEFINED,
CHARACTER_CHR,
CHARACTER_EFF,
CHARACTER_PET,
CHARACTER_PRT,
CLIENT,
CLIENT_ENCODER,
CLIENT_LOGIN,
BOOK,
ITEM,
INVENTORY,
SRVHANDLER_IDLE,
SRVHANDLER_TEMP,
BUFF_STORAGE,
PLAYER_STORAGE,
SERVER,
SERVER_LOGIN,
SERVER_DISEASES,
MERCHANT,
CHANNEL,
CHANNEL_MOBSTATUS,
GUILD,
PARTY,
WORLD_PARTY,
WORLD_OWL,
WORLD_PETS,
WORLD_MOUNTS,
WORLD_PSHOPS,
WORLD_MERCHS,
WORLD_MAPOBJS,
EIM,
EIM_PARTY,
EIM_SCRIPT,
EM_LOBBY,
EM_QUEUE,
CASHSHOP,
VISITOR_PSHOP,
STORAGE,
MOB_EXT,
MOB,
MOB_STATI,
MOBSKILL_FACTORY,
PORTAL,
VISITOR_MERCH,
MAP_CHRS,
MAP_OBJS,
MAP_FACTORY,
MAP_ITEM,
MAP_BOUNDS,
MINIDUNGEON,
REACTOR,
REACTOR_HIT;
}
Binary file not shown.
@@ -1,5 +1,5 @@
# Report File autogenerated from the MapleQuestItemFetcher feature by Ronan Lana.
# Generated data takes into account several data info from the underlying DB and the server-side WZ.xmls.
# Generated data takes into account several data info from the underlying DB, server source files and the server-side WZ.xmls.
INCORRECT QUESTIDS ON DB
2022055 : -1 -> 9330 EXPIRED
@@ -403,36 +403,29 @@ public class MapleQuestItemFetcher {
}
}
private static boolean foundMatchingDataOnFile(Scanner scan, String searchStr) {
while(scan.hasNext()){
String line = scan.nextLine().toLowerCase();
if(line.contains(searchStr)){
return true;
}
}
return false;
private static boolean foundMatchingDataOnFile(String fileContent, String searchStr) {
return fileContent.contains(searchStr);
}
private static void fileSearchMatchingData(File file, List<Pair<Integer, Integer>> itemsWithQuest) {
try {
Scanner scanner = new Scanner(file);
String fileContent = FileUtils.readFileToString(file, "UTF-8");
List<Pair<Integer, Integer>> copyItemsWithQuest = new ArrayList<>(itemsWithQuest);
for(Pair<Integer, Integer> iq : copyItemsWithQuest) {
scanner.reset();
if(foundMatchingDataOnFile(scanner, String.valueOf(iq.getLeft()))) {
if(foundMatchingDataOnFile(fileContent, String.valueOf(iq.getLeft()))) {
itemsWithQuest.remove(iq);
}
}
} catch(FileNotFoundException e) {}
} catch(IOException ioe) {
System.out.println("Failed to read file: " + file.getAbsolutePath());
ioe.printStackTrace();
}
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleQuestItemFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the underlying DB and the server-side WZ.xmls.");
printWriter.println(" # Generated data takes into account several data info from the underlying DB, server source files and the server-side WZ.xmls.");
printWriter.println();
}
+4 -6
View File
@@ -9725,10 +9725,8 @@
<vector name="origin" x="-1" y="31"/>
<int name="z" value="0"/>
</canvas>
<int name="price" value="1"/>
<int name="tradeBlock" value="1"/>
<int name="slotMax" value="1"/>
<int name="notSale" value="1"/>
<int name="price" value="1700"/>
<int name="slotMax" value="100"/>
</imgdir>
</imgdir>
<imgdir name="04031902">
@@ -9805,8 +9803,8 @@
<vector name="origin" x="-3" y="30"/>
<int name="z" value="0"/>
</canvas>
<int name="price" value="1"/>
<int name="slotMax" value="1"/>
<int name="price" value="1500"/>
<int name="slotMax" value="100"/>
</imgdir>
</imgdir>
<imgdir name="04031907">
+1 -1
View File
@@ -10,7 +10,7 @@
<string name="bgm" value="Bgm01/MoonlightShadow"/>
<string name="mapMark" value="Ellinia"/>
<int name="hideMinimap" value="0"/>
<int name="fieldLimit" value="393340"/>
<int name="fieldLimit" value="393342"/>
<int name="VRTop" value="-900"/>
<int name="VRLeft" value="1230"/>
<int name="VRBottom" value="550"/>
+142 -126
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="Act.img">
<imgdir name="1000">
<imgdir name="1">
@@ -39231,7 +39231,7 @@
<int name="count" value="-50"/>
</imgdir>
<imgdir name="3">
<int name="id" value="4000150"/>
<int name="id" value="4000135"/>
<int name="count" value="-50"/>
</imgdir>
<imgdir name="4">
@@ -39258,130 +39258,6 @@
<int name="id" value="4000176"/>
<int name="count" value="-2"/>
</imgdir>
<imgdir name="12">
<int name="id" value="4000175"/>
<int name="count" value="-2"/>
</imgdir>
<imgdir name="13">
<int name="id" value="4000138"/>
<int name="count" value="-2"/>
</imgdir>
<imgdir name="14">
<int name="id" value="4011007"/>
<int name="count" value="-2"/>
</imgdir>
<imgdir name="15">
<int name="id" value="4021009"/>
<int name="count" value="-2"/>
</imgdir>
<imgdir name="16">
<int name="id" value="4005000"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="17">
<int name="id" value="4005001"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="18">
<int name="id" value="4005002"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="19">
<int name="id" value="4005003"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="20">
<int name="id" value="4005004"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="21">
<int name="id" value="4011000"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="22">
<int name="id" value="4011001"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="23">
<int name="id" value="4011002"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="24">
<int name="id" value="4011003"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="25">
<int name="id" value="4011004"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="26">
<int name="id" value="4011005"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="27">
<int name="id" value="4011006"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="28">
<int name="id" value="4021000"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="29">
<int name="id" value="4021001"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="30">
<int name="id" value="4021002"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="31">
<int name="id" value="4021003"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="32">
<int name="id" value="4021004"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="33">
<int name="id" value="4021005"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="34">
<int name="id" value="4021006"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="35">
<int name="id" value="4021007"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="36">
<int name="id" value="4021008"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="37">
<int name="id" value="1442030"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="38">
<int name="id" value="1002033"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="39">
<int name="id" value="1050132"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="40">
<int name="id" value="1082002"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="41">
<int name="id" value="1072169"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="42">
<int name="id" value="2049100"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="10">
<int name="count" value="-2"/>
<int name="id" value="4000413"/>
@@ -39390,6 +39266,146 @@
<int name="count" value="-2"/>
<int name="id" value="4000414"/>
</imgdir>
<imgdir name="12">
<int name="count" value="-2"/>
<int name="id" value="4031901"/>
</imgdir>
<imgdir name="13">
<int name="count" value="-2"/>
<int name="id" value="4000224"/>
</imgdir>
<imgdir name="14">
<int name="count" value="-2"/>
<int name="id" value="4000235"/>
</imgdir>
<imgdir name="15">
<int name="count" value="-2"/>
<int name="id" value="4000243"/>
</imgdir>
<imgdir name="16">
<int name="id" value="4000175"/>
<int name="count" value="-2"/>
</imgdir>
<imgdir name="17">
<int name="id" value="4000138"/>
<int name="count" value="-2"/>
</imgdir>
<imgdir name="18">
<int name="id" value="4011007"/>
<int name="count" value="-2"/>
</imgdir>
<imgdir name="19">
<int name="id" value="4021009"/>
<int name="count" value="-2"/>
</imgdir>
<imgdir name="20">
<int name="id" value="4005000"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="21">
<int name="id" value="4005001"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="22">
<int name="id" value="4005002"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="23">
<int name="id" value="4005003"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="24">
<int name="id" value="4005004"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="25">
<int name="id" value="4011000"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="26">
<int name="id" value="4011001"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="27">
<int name="id" value="4011002"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="28">
<int name="id" value="4011003"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="29">
<int name="id" value="4011004"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="30">
<int name="id" value="4011005"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="31">
<int name="id" value="4011006"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="32">
<int name="id" value="4021000"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="33">
<int name="id" value="4021001"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="34">
<int name="id" value="4021002"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="35">
<int name="id" value="4021003"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="36">
<int name="id" value="4021004"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="37">
<int name="id" value="4021005"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="38">
<int name="id" value="4021006"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="39">
<int name="id" value="4021007"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="40">
<int name="id" value="4021008"/>
<int name="count" value="-5"/>
</imgdir>
<imgdir name="41">
<int name="id" value="1442030"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="42">
<int name="id" value="1002033"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="43">
<int name="id" value="1050132"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="44">
<int name="id" value="1082002"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="45">
<int name="id" value="1072169"/>
<int name="count" value="-1"/>
</imgdir>
<imgdir name="46">
<int name="id" value="2049100"/>
<int name="count" value="5"/>
</imgdir>
</imgdir>
<int name="exp" value="80000"/>
<int name="pop" value="5"/>
+137 -121
View File
@@ -62651,7 +62651,7 @@
<int name="count" value="50"/>
</imgdir>
<imgdir name="3">
<int name="id" value="4000150"/>
<int name="id" value="4000135"/>
<int name="count" value="50"/>
</imgdir>
<imgdir name="4">
@@ -62678,126 +62678,6 @@
<int name="id" value="4000176"/>
<int name="count" value="2"/>
</imgdir>
<imgdir name="12">
<int name="id" value="4000175"/>
<int name="count" value="2"/>
</imgdir>
<imgdir name="13">
<int name="id" value="4000138"/>
<int name="count" value="2"/>
</imgdir>
<imgdir name="14">
<int name="id" value="4011007"/>
<int name="count" value="2"/>
</imgdir>
<imgdir name="15">
<int name="id" value="4021009"/>
<int name="count" value="2"/>
</imgdir>
<imgdir name="16">
<int name="id" value="4005000"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="17">
<int name="id" value="4005001"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="18">
<int name="id" value="4005002"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="19">
<int name="id" value="4005003"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="20">
<int name="id" value="4005004"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="21">
<int name="id" value="4011000"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="22">
<int name="id" value="4011001"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="23">
<int name="id" value="4011002"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="24">
<int name="id" value="4011003"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="25">
<int name="id" value="4011004"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="26">
<int name="id" value="4011005"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="27">
<int name="id" value="4011006"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="28">
<int name="id" value="4021000"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="29">
<int name="id" value="4021001"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="30">
<int name="id" value="4021002"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="31">
<int name="id" value="4021003"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="32">
<int name="id" value="4021004"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="33">
<int name="id" value="4021005"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="34">
<int name="id" value="4021006"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="35">
<int name="id" value="4021007"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="36">
<int name="id" value="4021008"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="37">
<int name="id" value="1442030"/>
<int name="count" value="1"/>
</imgdir>
<imgdir name="38">
<int name="id" value="1002033"/>
<int name="count" value="1"/>
</imgdir>
<imgdir name="39">
<int name="id" value="1050132"/>
<int name="count" value="1"/>
</imgdir>
<imgdir name="40">
<int name="id" value="1082002"/>
<int name="count" value="1"/>
</imgdir>
<imgdir name="41">
<int name="id" value="1072169"/>
<int name="count" value="1"/>
</imgdir>
<imgdir name="10">
<int name="count" value="2"/>
<int name="id" value="4000413"/>
@@ -62806,6 +62686,142 @@
<int name="count" value="2"/>
<int name="id" value="4000414"/>
</imgdir>
<imgdir name="12">
<int name="count" value="2"/>
<int name="id" value="4031901"/>
</imgdir>
<imgdir name="13">
<int name="count" value="2"/>
<int name="id" value="4000224"/>
</imgdir>
<imgdir name="14">
<int name="count" value="2"/>
<int name="id" value="4000235"/>
</imgdir>
<imgdir name="15">
<int name="count" value="2"/>
<int name="id" value="4000243"/>
</imgdir>
<imgdir name="16">
<int name="id" value="4000175"/>
<int name="count" value="2"/>
</imgdir>
<imgdir name="17">
<int name="id" value="4000138"/>
<int name="count" value="2"/>
</imgdir>
<imgdir name="18">
<int name="id" value="4011007"/>
<int name="count" value="2"/>
</imgdir>
<imgdir name="19">
<int name="id" value="4021009"/>
<int name="count" value="2"/>
</imgdir>
<imgdir name="20">
<int name="id" value="4005000"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="21">
<int name="id" value="4005001"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="22">
<int name="id" value="4005002"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="23">
<int name="id" value="4005003"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="24">
<int name="id" value="4005004"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="25">
<int name="id" value="4011000"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="26">
<int name="id" value="4011001"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="27">
<int name="id" value="4011002"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="28">
<int name="id" value="4011003"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="29">
<int name="id" value="4011004"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="30">
<int name="id" value="4011005"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="31">
<int name="id" value="4011006"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="32">
<int name="id" value="4021000"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="33">
<int name="id" value="4021001"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="34">
<int name="id" value="4021002"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="35">
<int name="id" value="4021003"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="36">
<int name="id" value="4021004"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="37">
<int name="id" value="4021005"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="38">
<int name="id" value="4021006"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="39">
<int name="id" value="4021007"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="40">
<int name="id" value="4021008"/>
<int name="count" value="5"/>
</imgdir>
<imgdir name="41">
<int name="id" value="1442030"/>
<int name="count" value="1"/>
</imgdir>
<imgdir name="42">
<int name="id" value="1002033"/>
<int name="count" value="1"/>
</imgdir>
<imgdir name="43">
<int name="id" value="1050132"/>
<int name="count" value="1"/>
</imgdir>
<imgdir name="44">
<int name="id" value="1082002"/>
<int name="count" value="1"/>
</imgdir>
<imgdir name="45">
<int name="id" value="1072169"/>
<int name="count" value="1"/>
</imgdir>
</imgdir>
</imgdir>
<imgdir name="0">
+1 -1
View File
@@ -19525,7 +19525,7 @@ Able to proceed to &apos;Merry-go-round in Kampung&apos; as next quest.
<imgdir name="8206">
<string name="name" value="Ace of Heart&apos;s Secret Ability"/>
<string name="0" value="#b#p2041017##k and his partner seems pretty busy recently. Should I check with them on what kind of business they are on now?"/>
<string name="1" value="#b#p2041017##k said he can make a nice package for a chocolate... ...... Nope, not this time. Actually, he and his partner are providing a &quot;synthetizing on demand&quot; kind of service. Of #b#t2049100##k! By completing his quest, I am granted a fair amount of these scrolls. But LOOK AT THIS LIST:\n\n#i4032474# #t4032474# #c4032474# / #r5#k\n#i4000046# #t4000046# #c4000046# / #r50#k\n#i4000054# #t4000054# #c4000054# / #r50#k\n#i4000150# #t4000150# #c4000150# / #r50#k\n#i4000080# #t4000080# #c4000080# / #r50#k\n#i4000151# #t4000151# #c4000151# / #r5#k\n#i4000152# #t4000152# #c4000152# / #r5#k\n#i4031906# #t4031906# #c4031906# / #r5#k\n#i4000040# #t4000040# #c4000040# / #r2#k\n#i4000176# #t4000176# #c4000176# / #r2#k\n#i4000413# #t4000413# #c4000413# / #r2#k\n#i4000414# #t4000414# #c4000414# / #r2#k\n#i4000175# #t4000175# #c4000175# / #r2#k\n#i4000138# #t4000138# #c4000138# / #r2#k\n#i4011007# #t4011007# #c4011007# / #r2#k\n#i4021009# #t4021009# #c4021009# / #r2#k\n#i4005000# #t4005000# #c4005000# / #r5#k\n#i4005001# #t4005001# #c4005001# / #r5#k\n#i4005002# #t4005002# #c4005002# / #r5#k\n#i4005003# #t4005003# #c4005003# / #r5#k\n#i4005004# #t4005004# #c4005004# / #r5#k\n#i4011000# #t4011000# #c4011000# / #r5#k\n#i4011001# #t4011001# #c4011001# / #r5#k\n#i4011002# #t4011002# #c4011002# / #r5#k\n#i4011003# #t4011003# #c4011003# / #r5#k\n#i4011004# #t4011004# #c4011004# / #r5#k\n#i4011005# #t4011005# #c4011005# / #r5#k\n#i4011006# #t4011006# #c4011006# / #r5#k\n#i4021000# #t4021000# #c4021000# / #r5#k\n#i4021001# #t4021001# #c4021001# / #r5#k\n#i4021002# #t4021002# #c4021002# / #r5#k\n#i4021003# #t4021003# #c4021003# / #r5#k\n#i4021004# #t4021004# #c4021004# / #r5#k\n#i4021005# #t4021005# #c4021005# / #r5#k\n#i4021006# #t4021006# #c4021006# / #r5#k\n#i4021007# #t4021007# #c4021007# / #r5#k\n#i4021008# #t4021008# #c4021008# / #r5#k\n#i1442030# #t1442030# #c1442030# / #r1#k\n#i1002033# #t1002033# #c1002033# / #r1#k\n#i1050132# #t1050132# #c1050132# / #r1#k\n#i1082002# #t1082002# #c1082002# / #r1#k\n#i1072169# #t1072169# #c1072169# / #r1#k"/>
<string name="1" value="#b#p2041017##k said he can make a nice package for a chocolate... ...... Nope, not this time. Actually, he and his partner are providing a &quot;synthetizing on demand&quot; kind of service. Of #b#t2049100##k! By completing his quest, I am granted a fair amount of these scrolls. But LOOK AT THIS LIST:\n\n#i4032474# #t4032474# #c4032474# / #r5#k\n#i4000046# #t4000046# #c4000046# / #r50#k\n#i4000054# #t4000054# #c4000054# / #r50#k\n#i4000135# #t4000135# #c4000135# / #r50#k\n#i4000080# #t4000080# #c4000080# / #r50#k\n#i4000151# #t4000151# #c4000151# / #r5#k\n#i4000152# #t4000152# #c4000152# / #r5#k\n#i4031906# #t4031906# #c4031906# / #r5#k\n#i4000040# #t4000040# #c4000040# / #r2#k\n#i4000176# #t4000176# #c4000176# / #r2#k\n#i4000413# #t4000413# #c4000413# / #r2#k\n#i4000414# #t4000414# #c4000414# / #r2#k\n#i4031901# #t4031901# #c4031901# / #r2#k\n#i4000224# #t4000224# #c4000224# / #r2#k\n#i4000235# #t4000235# #c4000235# / #r2#k\n#i4000243# #t4000243# #c4000243# / #r2#k\n#i4000175# #t4000175# #c4000175# / #r2#k\n#i4000138# #t4000138# #c4000138# / #r2#k\n#i4011007# #t4011007# #c4011007# / #r2#k\n#i4021009# #t4021009# #c4021009# / #r2#k\n#i4005000# #t4005000# #c4005000# / #r5#k\n#i4005001# #t4005001# #c4005001# / #r5#k\n#i4005002# #t4005002# #c4005002# / #r5#k\n#i4005003# #t4005003# #c4005003# / #r5#k\n#i4005004# #t4005004# #c4005004# / #r5#k\n#i4011000# #t4011000# #c4011000# / #r5#k\n#i4011001# #t4011001# #c4011001# / #r5#k\n#i4011002# #t4011002# #c4011002# / #r5#k\n#i4011003# #t4011003# #c4011003# / #r5#k\n#i4011004# #t4011004# #c4011004# / #r5#k\n#i4011005# #t4011005# #c4011005# / #r5#k\n#i4011006# #t4011006# #c4011006# / #r5#k\n#i4021000# #t4021000# #c4021000# / #r5#k\n#i4021001# #t4021001# #c4021001# / #r5#k\n#i4021002# #t4021002# #c4021002# / #r5#k\n#i4021003# #t4021003# #c4021003# / #r5#k\n#i4021004# #t4021004# #c4021004# / #r5#k\n#i4021005# #t4021005# #c4021005# / #r5#k\n#i4021006# #t4021006# #c4021006# / #r5#k\n#i4021007# #t4021007# #c4021007# / #r5#k\n#i4021008# #t4021008# #c4021008# / #r5#k\n#i1442030# #t1442030# #c1442030# / #r1#k\n#i1002033# #t1002033# #c1002033# / #r1#k\n#i1050132# #t1050132# #c1050132# / #r1#k\n#i1082002# #t1082002# #c1082002# / #r1#k\n#i1072169# #t1072169# #c1072169# / #r1#k"/>
<string name="2" value="Ace of Hearts helped me make these scrolls... Geez, that was hard. It is repeatable within 3 days, huh?"/>
<int name="area" value="50"/>
</imgdir>
+2 -2
View File
@@ -973,8 +973,8 @@
<string name="desc" value="You can sell this envelope for 10,000 meso in the store."/>
</imgdir>
<imgdir name="4000175">
<string name="name" value="Minature Pianus"/>
<string name="desc" value="A miniature version of Pianus"/>
<string name="name" value="Miniature Pianus"/>
<string name="desc" value="A miniature version of Pianus."/>
</imgdir>
<imgdir name="4000176">
<string name="name" value="Poisonous Mushroom"/>