diff --git a/.gitignore b/.gitignore index db23594d4c..b810f781b8 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,10 @@ /tools/MapleReactorDropFetcher/dist/ /tools/MapleReactorDropFetcher/nbproject/ +/tools/MapleSkillbookChanceFetcher/build/ +/tools/MapleSkillbookChanceFetcher/dist/ +/tools/MapleSkillbookChanceFetcher/nbproject/ + /tools/MapleSkillbookStackUpdate/build/ /tools/MapleSkillbookStackUpdate/dist/ /tools/MapleSkillbookStackUpdate/nbproject/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..70178f3236 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +# Docker support, thanks to xinyifly + +FROM openjdk:7-alpine +RUN apk -U add tini +WORKDIR /mnt +COPY ./ ./ +RUN sh ./posix-compile.sh +EXPOSE 8484 7575 7576 7577 +CMD exec tini -- sh ./docker-launch.sh \ No newline at end of file diff --git a/docker-launch.sh b/docker-launch.sh new file mode 100644 index 0000000000..654a2c088c --- /dev/null +++ b/docker-launch.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +sed -i "s/HOST=.*/HOST=${HOST}/" configuration.ini +sed -i "s|URL=.*|URL=${URL}|" configuration.ini +sed -i "s/DB_USER=.*/DB_USER=${DB_USER}/" configuration.ini +sed -i "s/DB_PASS=.*/DB_PASS=${DB_PASS}/" configuration.ini + +exec sh ./posix-launch.sh \ No newline at end of file diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 7166a46c81..5a1d1fa2ce 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -1840,6 +1840,7 @@ Revisado refatoração recente em acesso a valores de stats de jogadores levando 24 - 26 Abril 2019, Ajustado levemente taxa de respawn de mobs quando não se está usando a flag de respawn completo. Ajustado ganhos de stats de ataque ao passar de nível weapons, ganha-se menos do stat que não é a afinidade (watk/matk). +Implementado sistema de Party Search no fonte. Líderes de party são servidos em FIFO, buscando melhores jogadores na faixa escolhida sempre que possível. Thread separada lida com organização e convites automatizados para jogadores. Corrigido drops de mesos sendo bloqueados para drops rápidos de mesos, indevidamente dificultando jogabilidade para certas classes. Corrigido caso de XML parser em MapleSkillBookInformationParser não lidando com terminadores "/>" corretamente. @@ -1851,5 +1852,23 @@ Adicionado server flag para checagem de IP's ao logar jogadores. Corrigido mobskills não sendo devidamente aplicados devido a um deslize anterior que tentaria aplicar indevidamente 2x "uso de skill" (uma das vezes deveria ser somente checagem de rotina). Corrigido puppets de arqueiros interrompendo mob statuses para o dono ao serem lançados em campo. +03 Maio 2019, +Revisado chance de drops de skillbooks. Chance de drops agora está relacionado ao nível do mob em conjunto com disponibilidade do mesmo (em bosses chances são melhores de sair o item). +Ajustado ganhos de party bonus EXP. Penalidade por diferença de nível agora somente leva em conta membros de equipe que efetivamente participaram da ação. +Ajustado as mecânicas da Party Search recentemente implementadas, de forma que se comporte bem em jogo. +Ajustado inicialização do sistema de comandos, agora atuando na inicialização do server, evitando bottleneck de primeira instanciação. + 04 Maio 2019, -Corrigido visão de mob statuses sendo interrompido para o novo controlador ao realizar troca de controladores. \ No newline at end of file +Corrigido visão de mob statuses sendo interrompido para o novo controlador ao realizar troca de controladores. + +07 - 08 Maio 2019, +Adicionado ao sistema de buffs suporte para condicionais de atuação (e.g. buff tendo efeito ativo dependendo do mapa de onde o jogador se encontra). +Implementado ordenação topológica na atualização de buffs. Sistema aponta um "melhor caminho" na atualização dos buffs, desta forma mostrando visualmente ao jogador certas combinações de buffs aplicados que anteriormente falhava em mostrar (e.g. Congrats from GM & Chocolate Wafer). +Corrigido Arans não recebendo corretamente buff de velocidade de ataque do Dojo. +Implementado convite de Party Search sensível ao mapa onde os jogadores líder e convidado se encontram. O sistema somente detecta para convites jogadores em mapas "próximos". + +12 Maio 2019, +Corrigido pets sendo indevidamente removido da DB ao realizar operações de retirada do inventário. + +15 Maio 2019, +Revisado, via testes unitários, robustez dos registros/buscas de jogadores e fluidez do sistema de Party Search. \ No newline at end of file diff --git a/scripts/npc/1012112.js b/scripts/npc/1012112.js index ca0f753f7e..a8be1e38be 100644 --- a/scripts/npc/1012112.js +++ b/scripts/npc/1012112.js @@ -58,7 +58,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nI'm Tory. Inside here is a beautiful hill where the primrose blooms. There's a tiger that lives in the hill, Growlie, and he seems to be looking for something to eat. Would you like to head over to the hill of primrose and join forces with your party members to help Growlie out?#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details.\r\n#L3#I would like to redeem an instance hat."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nI'm Tory. Inside here is a beautiful hill where the primrose blooms. There's a tiger that lives in the hill, Growlie, and he seems to be looking for something to eat. Would you like to head over to the hill of primrose and join forces with your party members to help Growlie out?#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details.\r\n#L3#I would like to redeem an instance hat."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -81,7 +81,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else if (selection == 2) { cm.sendOk("#e#b#k#n\r\nCollect primrose seeds from the flowers at the bottom part of the map and drop them by the platforms above the stage. Primrose seed color must match to grow the seeds, so test until you find the correct combination. When all the seeds have been planted, that is, starting second part of the mission, scout the Moon Bunny while it prepares Rice Cakes for the hungry Growlie. Once Growlie becomes satisfied, your mission is complete."); diff --git a/scripts/npc/1052013.js b/scripts/npc/1052013.js index c6a83ff96b..e521c4ac44 100644 --- a/scripts/npc/1052013.js +++ b/scripts/npc/1052013.js @@ -102,7 +102,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nThe #p1052014# operates differently than the common ones. They do not use mesos or gachapon tickets, rather #rERASERS#k, that can be obtained by completing the missions held on the Premium Road. To go there, you must find partners and attend to a Party Quest. When teamed up and ready, have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nThe #p1052014# operates differently than the common ones. They do not use mesos or gachapon tickets, rather #rERASERS#k, that can be obtained by completing the missions held on the Premium Road. To go there, you must find partners and attend to a Party Quest. When teamed up and ready, have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 2) { if (selection == 0) { if (cm.getParty() == null) { @@ -125,7 +125,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nOn the maps ahead, you will face many common-leveled mobs to face on. Grind all the required coupons from them and give it to me. All members will then receive a eraser, corresponding with the level faced. Insert on the machine #bmany of the same eraser or multiple different ones#k to have a better chance on greater prizes."); diff --git a/scripts/npc/2013000.js b/scripts/npc/2013000.js index 1d58bd6777..415f0f5137 100644 --- a/scripts/npc/2013000.js +++ b/scripts/npc/2013000.js @@ -58,7 +58,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble or join a team to solve the puzzles of the #bTower of Goddess#k? Have your #bparty leader#k talk to me or make yourself a party.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details.\r\n#L3#I would like to reclaim a prize."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble or join a team to solve the puzzles of the #bTower of Goddess#k? Have your #bparty leader#k talk to me or make yourself a party.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details.\r\n#L3#I would like to reclaim a prize."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -81,7 +81,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else if (selection == 2) { cm.sendOk("#e#b#k#n\r\nOur goddess has been missing since some time ago, rumor has it She has been seen last time inside the Tower of Goddess. Furthermore, our sanctuary has been seized by the overwhelming forces of the pixies, those beings that are recently wandering at the outskirts of Orbis. Their leader, Papa Pixie, currently holds the throne and may know Her whereabouts, so we urge to find a composition of brave heroes to charge into and claim back our sanctuary and rescue Her. If your team is able to be a composite of every job niche available (Warrior, Magician, Bowman, Thief and Pirate), you guys will receive my blessings to aid you in battle. Will you aid us?\r\n"); diff --git a/scripts/npc/2040034.js b/scripts/npc/2040034.js index 09e8a01d06..5e7f0b5def 100644 --- a/scripts/npc/2040034.js +++ b/scripts/npc/2040034.js @@ -38,7 +38,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nYou can't go any higher because of the extremely dangerous creatures above. Would you like to collaborate with party members to complete the quest? If so, please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nYou can't go any higher because of the extremely dangerous creatures above. Would you like to collaborate with party members to complete the quest? If so, please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -61,7 +61,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nA Dimensional Schism has appeared in #b#m220000000#!#k We desperately need brave adventurers who can defeat the intruding monsters. Please, party with some dependable allies to save #m220000000#! You must pass through various stages by defeating monsters and solving quizzes, and ultimately defeat #r#o9300012##k."); diff --git a/scripts/npc/2041023.js b/scripts/npc/2041023.js index 3a90542a8c..4c664b85ae 100644 --- a/scripts/npc/2041023.js +++ b/scripts/npc/2041023.js @@ -66,7 +66,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nYou are looking for Elemental Thanatos, right? If you team up with another mage, with the opposite elemental affinity as yours, you guys will be able to overcome them. As a leader, talk to me when you feel ready to go.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nYou are looking for Elemental Thanatos, right? If you team up with another mage, with the opposite elemental affinity as yours, you guys will be able to overcome them. As a leader, talk to me when you feel ready to go.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -89,7 +89,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\n Team up with another mage with #rdifferent elemental affinity#k before entering the stage. This team aspect is crucial to overcome the elementals inside."); diff --git a/scripts/npc/2083001.js b/scripts/npc/2083001.js index f7d3e7e96c..0aa28a81d2 100644 --- a/scripts/npc/2083001.js +++ b/scripts/npc/2083001.js @@ -64,7 +64,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nThis is the path to Horntail's lair. If you want to face him, you and your team shall be tested on the trial grounds ahead.#b\r\n#L0#Let us pass to the trial grounds.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nThis is the path to Horntail's lair. If you want to face him, you and your team shall be tested on the trial grounds ahead.#b\r\n#L0#Let us pass to the trial grounds.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -87,7 +87,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if(selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nAs the gatekeeper of Horntail's lair, I will grant access #bjust to those worthy#k of his presence. Even for those people, the path inside is that of a maze, full of branches and trials. However, those #radept at fighting squad bosses#k have a better chance to stand to our leader, although those #rof our kind#k have a shabby chance as well."); diff --git a/scripts/npc/2094000.js b/scripts/npc/2094000.js index 7dcd4ffaba..0a35405ade 100644 --- a/scripts/npc/2094000.js +++ b/scripts/npc/2094000.js @@ -57,7 +57,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nHelp! My son has been kidnapped and is bound on the hands of the fearful #rLord Pirate#k. I need your help... Would you please assemble or join a team to save him? Have your #bparty leader#k talk to me or make yourself a party.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nHelp! My son has been kidnapped and is bound on the hands of the fearful #rLord Pirate#k. I need your help... Would you please assemble or join a team to save him? Have your #bparty leader#k talk to me or make yourself a party.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -80,7 +80,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nIn this PQ, your mission is to progressively make your way through the ship, taking on all pirates and baddies in your path. Reaching the #rLord Pirate#k, depending on how many great chests you opened on the stages before, the boss will reveal himself even more powerful, so stay alert. Said chests, if opened, gives many extra rewards to your crew, it's worth a shot! Good luck."); diff --git a/scripts/npc/2095000.js b/scripts/npc/2095000.js index 8d0366bd4b..faafa64a3c 100644 --- a/scripts/npc/2095000.js +++ b/scripts/npc/2095000.js @@ -54,7 +54,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nAh, #r#p1095000##k sent you here? Is she worried about me? ... I'm terribly sorry to hear that, but I can't really go back just yet, some monsters are under the Black Mage's influence, and it's up to me to liberate them! ... It seems you're not going to accept that either, huh? Would you like to collaborate with party members to help me? If so, please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nAh, #r#p1095000##k sent you here? Is she worried about me? ... I'm terribly sorry to hear that, but I can't really go back just yet, some monsters are under the Black Mage's influence, and it's up to me to liberate them! ... It seems you're not going to accept that either, huh? Would you like to collaborate with party members to help me? If so, please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else { cm.sendYesNo("The mission succeeded, thanks for escorting me! I can lead you to #b#m120000104##k, are you ready?"); } @@ -81,7 +81,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\n A ambush is under way! I must stand on the field for around 6 minutes to complete the liberation, please protect me during that time so that my mission is completed."); diff --git a/scripts/npc/2112003.js b/scripts/npc/2112003.js index 6c2872fc07..bbf907d813 100644 --- a/scripts/npc/2112003.js +++ b/scripts/npc/2112003.js @@ -65,7 +65,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nMy beloved Romeo has been kidnapped! Although he is Zenumist's, I can't stand by and just see him suffer just because of this foolish clash. I need you and your colleagues help to save him! Please, help us!! Please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nMy beloved Romeo has been kidnapped! Although he is Zenumist's, I can't stand by and just see him suffer just because of this foolish clash. I need you and your colleagues help to save him! Please, help us!! Please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -88,7 +88,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nNot long ago, a scientist named Yulete has been banished from this town because of his researches of combined alchemies of Alcadno's and Zenumist's. Because of the immensurable amount of power coming from this combination, it is forbidden by law to study both. Yet, he ignored this law and got hands in both researches. As a result, he has been exiled.\r\nHe is now retaliating, already took my beloved one and his next target is me, as we are big pictures of Magatia, successors of both societies. But I'm not afraid. We must recover him at all costs!\r\n"); diff --git a/scripts/npc/2112004.js b/scripts/npc/2112004.js index ef66127ef4..02b02a895a 100644 --- a/scripts/npc/2112004.js +++ b/scripts/npc/2112004.js @@ -65,7 +65,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nMy beloved Juliet has been kidnapped! Although she is Alcadno's, I can't stand by and just see her suffer just because of this foolish clash. I need you and your colleagues help to save her! Please, help us!! Please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nMy beloved Juliet has been kidnapped! Although she is Alcadno's, I can't stand by and just see her suffer just because of this foolish clash. I need you and your colleagues help to save her! Please, help us!! Please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -88,7 +88,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nNot long ago, a scientist named Yulete has been banished from this town because of his researches of combined alchemies of Alcadno's and Zenumist's. Because of the immensurable amount of power coming from this combination, it is forbidden by law to study both. Yet, he ignored this law and got hands in both researches. As a result, he has been exiled.\r\nHe is now retaliating, already took my beloved one and his next target is me, as we are big pictures of Magatia, successors of both societies. But I'm not afraid. We must recover her at all costs!\r\n"); diff --git a/scripts/npc/2133000.js b/scripts/npc/2133000.js index 918d61cd07..68ec447716 100644 --- a/scripts/npc/2133000.js +++ b/scripts/npc/2133000.js @@ -57,7 +57,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble or join a team to solve the puzzles of the #bForest of Poison Haze#k? Have your #bparty leader#k talk to me or make yourself a party.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details.\r\n#L3#I would like to reclaim a prize."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble or join a team to solve the puzzles of the #bForest of Poison Haze#k? Have your #bparty leader#k talk to me or make yourself a party.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details.\r\n#L3#I would like to reclaim a prize."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -80,7 +80,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else if (selection == 2) { cm.sendOk("#e#b#k#n\r\nIn this PQ, your mission is to progressively make your way through the woods, taking on all baddies in your path, solving many puzzles you encounter and rallying yourselves to take the best of teamwork to overcome time limits and powerful creatures. Clearing the final boss, your team have a chance to obtain a marble that, #bwhen dropped by the fountain at the exit map#k, will guarantee the team extra prizes. Good luck."); diff --git a/scripts/npc/9000037.js b/scripts/npc/9000037.js index 880fef9c34..7fa0cbab9c 100644 --- a/scripts/npc/9000037.js +++ b/scripts/npc/9000037.js @@ -122,7 +122,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to collaborate with party members to complete the expedition, or are you brave enough to take it on all by yourself? Have your #bparty leader#k talk to me or make yourself a party.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to collaborate with party members to complete the expedition, or are you brave enough to take it on all by yourself? Have your #bparty leader#k talk to me or make yourself a party.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } } else if (status == 1) { if(state == 3) { @@ -171,7 +171,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nBrave adventurers from all over the places travels here to test their skills and abilities in combat, as they face even more powerful bosses from MapleStory. Join forces with fellow adventurers or face all the burden by yourself and receive all the glory, it is up to you. REWARDS are given accordingly to how far the adventurers reach and extra prizes may are given to a random member of the party, all attributed at the end of an expedition.\r\n\r\nThis instance also supports #bmultiple lobbies for matchmaking several ranges of team levels#k at once: team up with players with lower level if you want better chances to swiftly set up a boss rush for your team."); diff --git a/scripts/npc/9020000.js b/scripts/npc/9020000.js index d29c90da81..a5e8aa3707 100644 --- a/scripts/npc/9020000.js +++ b/scripts/npc/9020000.js @@ -62,7 +62,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nHow about you and your party members collectively beating a quest? Here you'll find obstacles and problems where you won't be able to beat it without great teamwork. If you want to try it, please tell the #bleader of your party#k to talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nHow about you and your party members collectively beating a quest? Here you'll find obstacles and problems where you won't be able to beat it without great teamwork. If you want to try it, please tell the #bleader of your party#k to talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } } else if (status == 1) { if(state == 1) { @@ -91,7 +91,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nYour party must pass through many obstacles and puzzles while traversing the sub-objectives of this Party Quest. Coordinate with your team in order to further advance and defeat the final boss and collect the dropped item in order to access the rewards and bonus stage."); diff --git a/scripts/npc/9103001.js b/scripts/npc/9103001.js index f89c260448..33a156f98b 100644 --- a/scripts/npc/9103001.js +++ b/scripts/npc/9103001.js @@ -61,7 +61,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nThis is the entrance to the Ludibrium Maze. Enjoy!\r\n#b#L0#Enter the Lubidrium Maze#l\r\n#L1#I want to find party members.\r\n#L2#What is the Ludibrium Maze?"); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nThis is the entrance to the Ludibrium Maze. Enjoy!\r\n#b#L0#Enter the Lubidrium Maze#l\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#What is the Ludibrium Maze?"); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -84,7 +84,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nThis maze is available to all parties of 3 or more members, and all participants must be between Level 51~70. You will be given 15 minutes to escape the maze. At the center of the room, there will be a Warp Portal set up to transport you to a different room. These portals will transport you to other rooms where you'll (hopefully) find the exit. Pietri will be waiting at the exit, so all you need to do is talk to him, and he'll let you out. Break all the boxes located in the room, and a monster inside the box will drop a coupon. After escaping the maze, you will be awarded with EXP based on the coupons collected. Additionally, if the leader possesses at least 30 coupons, then a special gift will be presented to the party. If you cannot escape the maze within the allotted 15 minutes, you will receive 0 EXP for your time in the maze. If you decide to log off while you're in the maze, you will be automatically kicked out of the maze. Even if the members of the party leave in the middle of the quest, the remaining members will be able to continue on with the quest, except if they run out of the minimum amount of party members in the maze. If you are in critical condition and unable to hunt down the monsters, you may avoid them to save yourself. Your fighting spirit and wits will be tested! Good luck!"); diff --git a/scripts/npc/9105004.js b/scripts/npc/9105004.js index 8c899cdb06..eb128a35c7 100644 --- a/scripts/npc/9105004.js +++ b/scripts/npc/9105004.js @@ -72,7 +72,7 @@ function recruitPqAction(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nHow about you and your party members collectively beating a quest? Here you'll find obstacles and problems where you won't be able to beat it without great teamwork. If you want to try it, please tell the #bleader of your party#k to talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nHow about you and your party members collectively beating a quest? Here you'll find obstacles and problems where you won't be able to beat it without great teamwork. If you want to try it, please tell the #bleader of your party#k to talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -95,7 +95,8 @@ function recruitPqAction(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\n\r\nJoin in with your team to build up the Snowman that will protect Happyville from the misdoings of Scrooge. While inside, work out with your team to protect it at any means necessary while collecting Snow Vigor that will help on the build up of the Snowman."); diff --git a/scripts/npc/9201048.js b/scripts/npc/9201048.js index d1b674291e..15ba31bb15 100644 --- a/scripts/npc/9201048.js +++ b/scripts/npc/9201048.js @@ -57,7 +57,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nIf you're brave enough to attempt the Amorian Challenge, join with others like you and let your #bparty leader#k talk to me. If a party filled with whole married couples register to the challenge, better prizes awaits there.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nIf you're brave enough to attempt the Amorian Challenge, join with others like you and let your #bparty leader#k talk to me. If a party filled with whole married couples register to the challenge, better prizes awaits there.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -80,7 +80,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nI am Amos, hoster of the well-round famed Amorian Challenge. The instance consist of many team puzzles, where cooperation is the fundamental key for progress. Team up with other players to attempt for the bonus stage, where many goodies can be obtained at the end of the instance. If an all-couple party is formed, they can get even better prizes on the extra bonus stage."); diff --git a/scripts/npc/9220018.js b/scripts/npc/9220018.js index b9fa2c2d3b..fb9b631601 100644 --- a/scripts/npc/9220018.js +++ b/scripts/npc/9220018.js @@ -56,7 +56,7 @@ function action(mode, type, selection) { return; } - cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nYou can't go any further because of the extremely dangerous creatures lying ahead. Would you like to collaborate with party members to complete the quest? If so, please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details."); + cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nYou can't go any further because of the extremely dangerous creatures lying ahead. Would you like to collaborate with party members to complete the quest? If so, please have your #bparty leader#k talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I would like to " + (cm.getPlayer().isRecvPartySearchInviteEnabled() ? "disable" : "enable") + " Party Search.\r\n#L2#I would like to hear more details."); } else if (status == 1) { if (selection == 0) { if (cm.getParty() == null) { @@ -79,7 +79,8 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 1) { - cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!"); + var psState = cm.getPlayer().toggleRecvPartySearchInvite(); + cm.sendOk("Your Party Search status is now: #b" + (psState ? "enabled" : "disabled") + "#k. Talk to me whenever you want to change it back."); cm.dispose(); } else { cm.sendOk("#e#b#k#n\r\nMV appeared once more, disrupting the welfare of the people of New Leaf City. Join forces with other maplers to fend off this sudden attack. After defeating MV and his minions, fetch your prizes at MV's treasure room."); diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js index ab64b59dad..35cd83b79b 100644 --- a/scripts/npc/9977777.js +++ b/scripts/npc/9977777.js @@ -75,6 +75,7 @@ function writeFeatureTab_Quests() { } function writeFeatureTab_PlayerSocialNetwork() { + addFeature("Party Search fully functional."); addFeature("Guild and Alliance system fully functional."); addFeature("Guild contract system held in Guild Headquarters."); addFeature("Party for novices-only."); @@ -180,6 +181,7 @@ function writeFeatureTab_Playerpotentials() { addFeature("Character slots per world/server-wide."); addFeature("Optional cash shop inventory separated by classes."); addFeature("Players manage 'same-typed' invites exclusively."); + addFeature("Player buffs with conditional active effects."); } function writeFeatureTab_Serverpotentials() { diff --git a/sql/db_database.sql b/sql/db_database.sql index 888edc4d4b..05b2fdae1d 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -184,7 +184,7 @@ CREATE TABLE IF NOT EXISTS `characters` ( `dataString` varchar(64) NOT NULL DEFAULT '', `lastLogoutTime` timestamp NOT NULL DEFAULT '2015-01-01 05:00:00', `lastExpGainTime` timestamp NOT NULL DEFAULT '2015-01-01 05:00:00', - `pendantExp` tinyint(1) NOT NULL DEFAULT '0', + `partySearch` tinyint(1) NOT NULL DEFAULT '0', `jailexpire` bigint(20) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `accountid` (`accountid`), @@ -12750,7 +12750,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi (8150200, 2290135, 1, 1, 0, 1500), (8800002, 2280013, 1, 1, 0, 45000), (8140600, 2290132, 1, 1, 0, 1500), -(851000, 2290132, 1, 1, 0, 15000), +(8510000, 2290132, 1, 1, 0, 15000), (8520000, 2290132, 1, 1, 0, 15000), (8810018, 2290133, 1, 1, 0, 45000), (8200008, 2290133, 1, 1, 0, 1500), diff --git a/sql/db_drops.sql b/sql/db_drops.sql index 910413bf43..67d0abe1b9 100644 --- a/sql/db_drops.sql +++ b/sql/db_drops.sql @@ -17212,7 +17212,7 @@ USE `heavenms`; (9400014, 2040719, 1, 4, 0, 10000), (9400014, 1102030, 1, 1, 0, 40000), (9400014, 1032027, 1, 1, 0, 40000), -(9400014, 1402037, 1, 1, 0, 40000), +(9400014, 1402037, 1, 1, 0, 40000), # thanks Feras & Kitty for noticing issues with Stonetooth Sword layout (9400014, 1372011, 1, 1, 0, 40000), (8200008, 4000453, 1, 1, 0, 200000), (8200008, 4000459, 1, 1, 0, 200000), @@ -23923,4 +23923,622 @@ SET minimum_quantity = CASE ELSE maximum_quantity END ; - UPDATE drop_data SET `chance`=1287 WHERE `chance`=1500; \ No newline at end of file + UPDATE drop_data SET `chance`=1287 WHERE `chance`=1500; + + # MapleSkillbookChanceFetcher! Tuning up some skillbook drop chances in order to fit their dropper's availability (whether's a boss or not) and level. + REPLACE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES +(851000, 2290132, 1, 1, 0, 3861), +(7090000, 2290087, 1, 1, 0, 40000), +(8090000, 2290045, 1, 1, 0, 40000), +(8140103, 2290044, 1, 1, 0, 2000), +(8140511, 2290009, 1, 1, 0, 2000), +(8140511, 2290050, 1, 1, 0, 2000), +(8140511, 2290083, 1, 1, 0, 2000), +(8140511, 2290134, 1, 1, 0, 6000), +(8140512, 2290013, 1, 1, 0, 2000), +(8140512, 2290067, 1, 1, 0, 2000), +(8140512, 2290082, 1, 1, 0, 2000), +(8140512, 2290097, 1, 1, 0, 2000), +(8140512, 2290116, 1, 1, 0, 2000), +(8140512, 2290131, 1, 1, 0, 6000), +(8140600, 2290132, 1, 1, 0, 6000), +(8140700, 2290106, 1, 1, 0, 2000), +(8140700, 2290126, 1, 1, 0, 6000), +(8140701, 2290122, 1, 1, 0, 2000), +(8140702, 2290112, 1, 1, 0, 2000), +(8140703, 2290088, 1, 1, 0, 2000), +(8140703, 2290099, 1, 1, 0, 2000), +(8141000, 2290082, 1, 1, 0, 2000), +(8141000, 2290097, 1, 1, 0, 2000), +(8141100, 2280005, 1, 1, 0, 2000), +(8141300, 2290098, 1, 1, 0, 2000), +(8142100, 2290032, 1, 1, 0, 2000), +(8142100, 2290082, 1, 1, 0, 2000), +(8142100, 2290114, 1, 1, 0, 2000), +(8143000, 2280004, 1, 1, 0, 2000), +(8150000, 2280013, 1, 1, 0, 120000), +(8150000, 2290070, 1, 1, 0, 40000), +(8150000, 2290091, 1, 1, 0, 40000), +(8150100, 2290042, 1, 1, 0, 2000), +(8150100, 2290053, 1, 1, 0, 2000), +(8150100, 2290073, 1, 1, 0, 2000), +(8150100, 2290102, 1, 1, 0, 2000), +(8150100, 2290118, 1, 1, 0, 2000), +(8150101, 2290017, 1, 1, 0, 2000), +(8150101, 2290021, 1, 1, 0, 2000), +(8150101, 2290035, 1, 1, 0, 2000), +(8150101, 2290042, 1, 1, 0, 2000), +(8150101, 2290052, 1, 1, 0, 2000), +(8150101, 2290102, 1, 1, 0, 2000), +(8150200, 2290024, 1, 1, 0, 2000), +(8150200, 2290100, 1, 1, 0, 2000), +(8150200, 2290135, 1, 1, 0, 6000), +(8150201, 2290004, 1, 1, 0, 2000), +(8150201, 2290006, 1, 1, 0, 2000), +(8150201, 2290024, 1, 1, 0, 2000), +(8150201, 2290036, 1, 1, 0, 2000), +(8150201, 2290056, 1, 1, 0, 2000), +(8150201, 2290072, 1, 1, 0, 2000), +(8150201, 2290078, 1, 1, 0, 2000), +(8150201, 2290117, 1, 1, 0, 2000), +(8150300, 2290003, 1, 1, 0, 2000), +(8150300, 2290033, 1, 1, 0, 2000), +(8150300, 2290111, 1, 1, 0, 2000), +(8150300, 2290120, 1, 1, 0, 2000), +(8150300, 2290127, 1, 1, 0, 6000), +(8150301, 2290023, 1, 1, 0, 2000), +(8150301, 2290029, 1, 1, 0, 2000), +(8150301, 2290101, 1, 1, 0, 2000), +(8150301, 2290107, 1, 1, 0, 2000), +(8150302, 2290010, 1, 1, 0, 2000), +(8150302, 2290019, 1, 1, 0, 2000), +(8150302, 2290026, 1, 1, 0, 2000), +(8150302, 2290076, 1, 1, 0, 2000), +(8150302, 2290085, 1, 1, 0, 2000), +(8150302, 2290096, 1, 1, 0, 2000), +(8150302, 2290113, 1, 1, 0, 2000), +(8150302, 2290119, 1, 1, 0, 2000), +(8150302, 2290128, 1, 1, 0, 6000), +(8160000, 2290017, 1, 1, 0, 2000), +(8160000, 2290045, 1, 1, 0, 2000), +(8160000, 2290065, 1, 1, 0, 2000), +(8160000, 2290067, 1, 1, 0, 2000), +(8160000, 2290081, 1, 1, 0, 2000), +(8170000, 2290012, 1, 1, 0, 2000), +(8170000, 2290086, 1, 1, 0, 2000), +(8170000, 2290087, 1, 1, 0, 2000), +(8170000, 2290134, 1, 1, 0, 6000), +(8180000, 2290002, 1, 1, 0, 40000), +(8180000, 2290003, 1, 1, 0, 40000), +(8180000, 2290014, 1, 1, 0, 40000), +(8180000, 2290015, 1, 1, 0, 40000), +(8180000, 2290030, 1, 1, 0, 40000), +(8180000, 2290035, 1, 1, 0, 40000), +(8180000, 2290036, 1, 1, 0, 40000), +(8180000, 2290063, 1, 1, 0, 40000), +(8180000, 2290080, 1, 1, 0, 40000), +(8180000, 2290098, 1, 1, 0, 40000), +(8180000, 2290101, 1, 1, 0, 40000), +(8180000, 2290117, 1, 1, 0, 40000), +(8180000, 2290130, 1, 1, 0, 120000), +(8180001, 2290018, 1, 1, 0, 40000), +(8180001, 2290019, 1, 1, 0, 40000), +(8180001, 2290032, 1, 1, 0, 40000), +(8180001, 2290042, 1, 1, 0, 40000), +(8180001, 2290058, 1, 1, 0, 40000), +(8180001, 2290059, 1, 1, 0, 40000), +(8180001, 2290068, 1, 1, 0, 40000), +(8180001, 2290069, 1, 1, 0, 40000), +(8180001, 2290072, 1, 1, 0, 40000), +(8180001, 2290092, 1, 1, 0, 40000), +(8180001, 2290099, 1, 1, 0, 40000), +(8180001, 2290100, 1, 1, 0, 40000), +(8180001, 2290102, 1, 1, 0, 40000), +(8180001, 2290119, 1, 1, 0, 40000), +(8180001, 2290128, 1, 1, 0, 120000), +(8190000, 2280016, 1, 1, 0, 6000), +(8190000, 2290030, 1, 1, 0, 2000), +(8190000, 2290044, 1, 1, 0, 2000), +(8190000, 2290054, 1, 1, 0, 2000), +(8190000, 2290066, 1, 1, 0, 2000), +(8190000, 2290075, 1, 1, 0, 2000), +(8190000, 2290092, 1, 1, 0, 2000), +(8190000, 2290103, 1, 1, 0, 2000), +(8190002, 2290000, 1, 1, 0, 2000), +(8190002, 2290008, 1, 1, 0, 2000), +(8190002, 2290018, 1, 1, 0, 2000), +(8190002, 2290038, 1, 1, 0, 2000), +(8190002, 2290060, 1, 1, 0, 2000), +(8190002, 2290080, 1, 1, 0, 2000), +(8190002, 2290124, 1, 1, 0, 2000), +(8190003, 2280013, 1, 1, 0, 6000), +(8190003, 2290007, 1, 1, 0, 2000), +(8190003, 2290012, 1, 1, 0, 2000), +(8190003, 2290014, 1, 1, 0, 2000), +(8190003, 2290033, 1, 1, 0, 2000), +(8190003, 2290045, 1, 1, 0, 2000), +(8190003, 2290050, 1, 1, 0, 2000), +(8190003, 2290055, 1, 1, 0, 2000), +(8190003, 2290062, 1, 1, 0, 2000), +(8190003, 2290063, 1, 1, 0, 2000), +(8190003, 2290070, 1, 1, 0, 2000), +(8190003, 2290086, 1, 1, 0, 2000), +(8190003, 2290108, 1, 1, 0, 2000), +(8190003, 2290133, 1, 1, 0, 6000), +(8190004, 2290002, 1, 1, 0, 2000), +(8190004, 2290009, 1, 1, 0, 2000), +(8190004, 2290021, 1, 1, 0, 2000), +(8190004, 2290034, 1, 1, 0, 2000), +(8190004, 2290041, 1, 1, 0, 2000), +(8190004, 2290052, 1, 1, 0, 2000), +(8190004, 2290053, 1, 1, 0, 2000), +(8190004, 2290058, 1, 1, 0, 2000), +(8190004, 2290068, 1, 1, 0, 2000), +(8190004, 2290071, 1, 1, 0, 2000), +(8190004, 2290073, 1, 1, 0, 2000), +(8190004, 2290090, 1, 1, 0, 2000), +(8190004, 2290112, 1, 1, 0, 2000), +(8190004, 2290121, 1, 1, 0, 2000), +(8190004, 2290130, 1, 1, 0, 6000), +(8190005, 2290000, 1, 1, 0, 2000), +(8190005, 2290008, 1, 1, 0, 2000), +(8190005, 2290018, 1, 1, 0, 2000), +(8190005, 2290038, 1, 1, 0, 2000), +(8190005, 2290060, 1, 1, 0, 2000), +(8190005, 2290080, 1, 1, 0, 2000), +(8190005, 2290124, 1, 1, 0, 2000), +(8200000, 2290005, 1, 1, 0, 2000), +(8200000, 2290011, 1, 1, 0, 2000), +(8200000, 2290114, 1, 1, 0, 2000), +(8200001, 2280015, 1, 1, 0, 6000), +(8200001, 2290050, 1, 1, 0, 2000), +(8200001, 2290059, 1, 1, 0, 2000), +(8200001, 2290065, 1, 1, 0, 2000), +(8200001, 2290129, 1, 1, 0, 6000), +(8200002, 2290062, 1, 1, 0, 2000), +(8200002, 2290066, 1, 1, 0, 2000), +(8200002, 2290070, 1, 1, 0, 2000), +(8200002, 2290131, 1, 1, 0, 6000), +(8200002, 2290139, 1, 1, 0, 6000), +(8200003, 2290012, 1, 1, 0, 2000), +(8200003, 2290056, 1, 1, 0, 2000), +(8200003, 2290071, 1, 1, 0, 2000), +(8200003, 2290101, 1, 1, 0, 2000), +(8200003, 2290136, 1, 1, 0, 6000), +(8200004, 2280016, 1, 1, 0, 6000), +(8200004, 2290069, 1, 1, 0, 2000), +(8200004, 2290072, 1, 1, 0, 2000), +(8200004, 2290073, 1, 1, 0, 2000), +(8200004, 2290127, 1, 1, 0, 6000), +(8200004, 2290134, 1, 1, 0, 6000), +(8200005, 2280014, 1, 1, 0, 6000), +(8200005, 2290078, 1, 1, 0, 2000), +(8200005, 2290079, 1, 1, 0, 2000), +(8200005, 2290095, 1, 1, 0, 2000), +(8200006, 2290003, 1, 1, 0, 2000), +(8200006, 2290064, 1, 1, 0, 2000), +(8200006, 2290076, 1, 1, 0, 2000), +(8200006, 2290077, 1, 1, 0, 2000), +(8200006, 2290129, 1, 1, 0, 6000), +(8200006, 2290138, 1, 1, 0, 6000), +(8200007, 2290006, 1, 1, 0, 2000), +(8200007, 2290007, 1, 1, 0, 2000), +(8200007, 2290011, 1, 1, 0, 2000), +(8200007, 2290016, 1, 1, 0, 2000), +(8200007, 2290125, 1, 1, 0, 2000), +(8200007, 2290136, 1, 1, 0, 6000), +(8200008, 2290006, 1, 1, 0, 2000), +(8200008, 2290051, 1, 1, 0, 2000), +(8200008, 2290121, 1, 1, 0, 2000), +(8200008, 2290122, 1, 1, 0, 2000), +(8200008, 2290133, 1, 1, 0, 6000), +(8200009, 2290013, 1, 1, 0, 2000), +(8200009, 2290016, 1, 1, 0, 2000), +(8200009, 2290031, 1, 1, 0, 2000), +(8200009, 2290039, 1, 1, 0, 2000), +(8200010, 2290026, 1, 1, 0, 2000), +(8200010, 2290059, 1, 1, 0, 2000), +(8200010, 2290088, 1, 1, 0, 2000), +(8200010, 2290089, 1, 1, 0, 2000), +(8200010, 2290127, 1, 1, 0, 6000), +(8200011, 2290001, 1, 1, 0, 3000), +(8200011, 2290040, 1, 1, 0, 3000), +(8200011, 2290046, 1, 1, 0, 3000), +(8200011, 2290048, 1, 1, 0, 3000), +(8200011, 2290049, 1, 1, 0, 3000), +(8200011, 2290114, 1, 1, 0, 3000), +(8200011, 2290137, 1, 1, 0, 9000), +(8200012, 2290041, 1, 1, 0, 3000), +(8200012, 2290092, 1, 1, 0, 3000), +(8200012, 2290093, 1, 1, 0, 3000), +(8200012, 2290115, 1, 1, 0, 3000), +(8200012, 2290137, 1, 1, 0, 9000), +(8200012, 2290139, 1, 1, 0, 9000), +(8220002, 2290020, 1, 1, 0, 40000), +(8220002, 2290081, 1, 1, 0, 40000), +(8220002, 2290085, 1, 1, 0, 40000), +(8220002, 2290133, 1, 1, 0, 120000), +(8220003, 2290006, 1, 1, 0, 40000), +(8220003, 2290030, 1, 1, 0, 40000), +(8220003, 2290031, 1, 1, 0, 40000), +(8220003, 2290032, 1, 1, 0, 40000), +(8220003, 2290033, 1, 1, 0, 40000), +(8220003, 2290060, 1, 1, 0, 40000), +(8220003, 2290061, 1, 1, 0, 40000), +(8220003, 2290076, 1, 1, 0, 40000), +(8220003, 2290077, 1, 1, 0, 40000), +(8220003, 2290104, 1, 1, 0, 40000), +(8220003, 2290105, 1, 1, 0, 40000), +(8220003, 2290117, 1, 1, 0, 40000), +(8220003, 2290118, 1, 1, 0, 40000), +(8220004, 2290018, 1, 1, 0, 40000), +(8220004, 2290019, 1, 1, 0, 40000), +(8220004, 2290024, 1, 1, 0, 40000), +(8220004, 2290025, 1, 1, 0, 40000), +(8220004, 2290058, 1, 1, 0, 40000), +(8220004, 2290059, 1, 1, 0, 40000), +(8220004, 2290076, 1, 1, 0, 40000), +(8220004, 2290077, 1, 1, 0, 40000), +(8220004, 2290106, 1, 1, 0, 40000), +(8220004, 2290127, 1, 1, 0, 120000), +(8220004, 2290134, 1, 1, 0, 120000), +(8220005, 2290002, 1, 1, 0, 60000), +(8220005, 2290003, 1, 1, 0, 60000), +(8220005, 2290036, 1, 1, 0, 60000), +(8220005, 2290037, 1, 1, 0, 60000), +(8220005, 2290055, 1, 1, 0, 60000), +(8220005, 2290080, 1, 1, 0, 60000), +(8220005, 2290099, 1, 1, 0, 60000), +(8220005, 2290131, 1, 1, 0, 180000), +(8220005, 2290136, 1, 1, 0, 180000), +(8220006, 2290012, 1, 1, 0, 80000), +(8220006, 2290013, 1, 1, 0, 80000), +(8220006, 2290042, 1, 1, 0, 80000), +(8220006, 2290043, 1, 1, 0, 80000), +(8220006, 2290060, 1, 1, 0, 80000), +(8220006, 2290061, 1, 1, 0, 80000), +(8220006, 2290090, 1, 1, 0, 80000), +(8220006, 2290119, 1, 1, 0, 80000), +(8220006, 2290120, 1, 1, 0, 80000), +(8220006, 2290135, 1, 1, 0, 240000), +(8220006, 2290138, 1, 1, 0, 240000), +(8220007, 2290035, 1, 1, 0, 40000), +(8220007, 2290091, 1, 1, 0, 40000), +(8220007, 2290108, 1, 1, 0, 40000), +(8220009, 2290031, 1, 1, 0, 40000), +(8220009, 2290129, 1, 1, 0, 120000), +(8220015, 2280004, 1, 1, 0, 40000), +(8220015, 2280005, 1, 1, 0, 40000), +(8220015, 2280006, 1, 1, 0, 40000), +(8500002, 2280007, 1, 1, 0, 60000), +(8500002, 2280008, 1, 1, 0, 60000), +(8500002, 2280009, 1, 1, 0, 60000), +(8500002, 2280010, 1, 1, 0, 60000), +(8500002, 2290006, 1, 1, 0, 60000), +(8500002, 2290010, 1, 1, 0, 60000), +(8500002, 2290011, 1, 1, 0, 60000), +(8500002, 2290013, 1, 1, 0, 60000), +(8500002, 2290028, 1, 1, 0, 60000), +(8500002, 2290037, 1, 1, 0, 60000), +(8500002, 2290043, 1, 1, 0, 60000), +(8500002, 2290051, 1, 1, 0, 60000), +(8500002, 2290056, 1, 1, 0, 60000), +(8500002, 2290061, 1, 1, 0, 60000), +(8500002, 2290066, 1, 1, 0, 60000), +(8500002, 2290071, 1, 1, 0, 60000), +(8500002, 2290078, 1, 1, 0, 60000), +(8500002, 2290089, 1, 1, 0, 60000), +(8500002, 2290091, 1, 1, 0, 60000), +(8500002, 2290104, 1, 1, 0, 60000), +(8500002, 2290107, 1, 1, 0, 60000), +(8500002, 2290121, 1, 1, 0, 60000), +(8500002, 2290123, 1, 1, 0, 60000), +(8500002, 2290126, 1, 1, 0, 180000), +(8500002, 2290129, 1, 1, 0, 180000), +(8510000, 2280007, 1, 1, 0, 40000), +(8510000, 2280008, 1, 1, 0, 40000), +(8510000, 2280009, 1, 1, 0, 40000), +(8510000, 2280010, 1, 1, 0, 40000), +(8510000, 2290000, 1, 1, 0, 40000), +(8510000, 2290001, 1, 1, 0, 40000), +(8510000, 2290004, 1, 1, 0, 40000), +(8510000, 2290005, 1, 1, 0, 40000), +(8510000, 2290024, 1, 1, 0, 40000), +(8510000, 2290025, 1, 1, 0, 40000), +(8510000, 2290026, 1, 1, 0, 40000), +(8510000, 2290027, 1, 1, 0, 40000), +(8510000, 2290052, 1, 1, 0, 40000), +(8510000, 2290053, 1, 1, 0, 40000), +(8510000, 2290054, 1, 1, 0, 40000), +(8510000, 2290055, 1, 1, 0, 40000), +(8510000, 2290076, 1, 1, 0, 40000), +(8510000, 2290077, 1, 1, 0, 40000), +(8510000, 2290082, 1, 1, 0, 40000), +(8510000, 2290083, 1, 1, 0, 40000), +(8510000, 2290097, 1, 1, 0, 40000), +(8510000, 2290099, 1, 1, 0, 40000), +(8510000, 2290106, 1, 1, 0, 40000), +(8510000, 2290108, 1, 1, 0, 40000), +(8510000, 2290112, 1, 1, 0, 40000), +(8510000, 2290114, 1, 1, 0, 40000), +(8510000, 2290122, 1, 1, 0, 40000), +(8510000, 2290124, 1, 1, 0, 40000), +(8510000, 2290132, 1, 1, 0, 120000), +(8520000, 2280007, 1, 1, 0, 40000), +(8520000, 2280008, 1, 1, 0, 40000), +(8520000, 2280009, 1, 1, 0, 40000), +(8520000, 2280010, 1, 1, 0, 40000), +(8520000, 2290000, 1, 1, 0, 40000), +(8520000, 2290001, 1, 1, 0, 40000), +(8520000, 2290004, 1, 1, 0, 40000), +(8520000, 2290005, 1, 1, 0, 40000), +(8520000, 2290024, 1, 1, 0, 40000), +(8520000, 2290025, 1, 1, 0, 40000), +(8520000, 2290026, 1, 1, 0, 40000), +(8520000, 2290027, 1, 1, 0, 40000), +(8520000, 2290052, 1, 1, 0, 40000), +(8520000, 2290053, 1, 1, 0, 40000), +(8520000, 2290054, 1, 1, 0, 40000), +(8520000, 2290055, 1, 1, 0, 40000), +(8520000, 2290076, 1, 1, 0, 40000), +(8520000, 2290077, 1, 1, 0, 40000), +(8520000, 2290082, 1, 1, 0, 40000), +(8520000, 2290083, 1, 1, 0, 40000), +(8520000, 2290097, 1, 1, 0, 40000), +(8520000, 2290099, 1, 1, 0, 40000), +(8520000, 2290106, 1, 1, 0, 40000), +(8520000, 2290108, 1, 1, 0, 40000), +(8520000, 2290112, 1, 1, 0, 40000), +(8520000, 2290114, 1, 1, 0, 40000), +(8520000, 2290122, 1, 1, 0, 40000), +(8520000, 2290124, 1, 1, 0, 40000), +(8520000, 2290132, 1, 1, 0, 120000), +(8800002, 2280007, 1, 1, 0, 80000), +(8800002, 2280008, 1, 1, 0, 80000), +(8800002, 2280009, 1, 1, 0, 80000), +(8800002, 2280010, 1, 1, 0, 80000), +(8800002, 2280013, 1, 1, 0, 240000), +(8800002, 2280014, 1, 1, 0, 240000), +(8800002, 2280015, 1, 1, 0, 240000), +(8800002, 2280016, 1, 1, 0, 240000), +(8800002, 2290006, 1, 1, 0, 80000), +(8800002, 2290007, 1, 1, 0, 80000), +(8800002, 2290016, 1, 1, 0, 80000), +(8800002, 2290020, 1, 1, 0, 80000), +(8800002, 2290022, 1, 1, 0, 80000), +(8800002, 2290024, 1, 1, 0, 80000), +(8800002, 2290028, 1, 1, 0, 80000), +(8800002, 2290029, 1, 1, 0, 80000), +(8800002, 2290040, 1, 1, 0, 80000), +(8800002, 2290046, 1, 1, 0, 80000), +(8800002, 2290048, 1, 1, 0, 80000), +(8800002, 2290056, 1, 1, 0, 80000), +(8800002, 2290057, 1, 1, 0, 80000), +(8800002, 2290058, 1, 1, 0, 80000), +(8800002, 2290064, 1, 1, 0, 80000), +(8800002, 2290067, 1, 1, 0, 80000), +(8800002, 2290074, 1, 1, 0, 80000), +(8800002, 2290079, 1, 1, 0, 80000), +(8800002, 2290084, 1, 1, 0, 80000), +(8800002, 2290094, 1, 1, 0, 80000), +(8800002, 2290110, 1, 1, 0, 80000), +(8800002, 2290115, 1, 1, 0, 80000), +(8810018, 2290017, 1, 1, 0, 100000), +(8810018, 2290021, 1, 1, 0, 100000), +(8810018, 2290023, 1, 1, 0, 100000), +(8810018, 2290041, 1, 1, 0, 100000), +(8810018, 2290047, 1, 1, 0, 100000), +(8810018, 2290049, 1, 1, 0, 100000), +(8810018, 2290065, 1, 1, 0, 100000), +(8810018, 2290075, 1, 1, 0, 100000), +(8810018, 2290085, 1, 1, 0, 100000), +(8810018, 2290095, 1, 1, 0, 100000), +(8810018, 2290096, 1, 1, 0, 100000), +(8810018, 2290111, 1, 1, 0, 100000), +(8810018, 2290116, 1, 1, 0, 100000), +(8810018, 2290125, 1, 1, 0, 100000), +(8810018, 2290133, 1, 1, 0, 300000), +(8810018, 2290137, 1, 1, 0, 300000), +(8810018, 2290139, 1, 1, 0, 300000), +(8820000, 2290010, 1, 1, 0, 120000), +(8820000, 2290022, 1, 1, 0, 120000), +(8820000, 2290040, 1, 1, 0, 120000), +(8820000, 2290046, 1, 1, 0, 120000), +(8820000, 2290048, 1, 1, 0, 120000), +(8820000, 2290052, 1, 1, 0, 120000), +(8820000, 2290084, 1, 1, 0, 120000), +(8820000, 2290090, 1, 1, 0, 120000), +(8820000, 2290106, 1, 1, 0, 120000), +(8820000, 2290119, 1, 1, 0, 120000), +(8820001, 2290010, 1, 1, 0, 120000), +(8820001, 2290022, 1, 1, 0, 120000), +(8820001, 2290040, 1, 1, 0, 120000), +(8820001, 2290046, 1, 1, 0, 120000), +(8820001, 2290048, 1, 1, 0, 120000), +(8820001, 2290052, 1, 1, 0, 120000), +(8820001, 2290084, 1, 1, 0, 120000), +(8820001, 2290090, 1, 1, 0, 120000), +(8820001, 2290106, 1, 1, 0, 120000), +(8820001, 2290119, 1, 1, 0, 120000), +(9300028, 2280015, 1, 1, 0, 120000), +(9300028, 2290026, 1, 1, 0, 40000), +(9300028, 2290064, 1, 1, 0, 40000), +(9300028, 2290075, 1, 1, 0, 40000), +(9300028, 2290093, 1, 1, 0, 40000), +(9300028, 2290111, 1, 1, 0, 40000), +(9300094, 2280004, 1, 1, 0, 40000), +(9300094, 2280005, 1, 1, 0, 40000), +(9300094, 2280006, 1, 1, 0, 40000), +(9300095, 2280004, 1, 1, 0, 2000), +(9300095, 2280005, 1, 1, 0, 2000), +(9300095, 2280006, 1, 1, 0, 2000), +(9303016, 2290006, 1, 1, 0, 2000), +(9303016, 2290030, 1, 1, 0, 2000), +(9303016, 2290032, 1, 1, 0, 2000), +(9303016, 2290060, 1, 1, 0, 2000), +(9303016, 2290076, 1, 1, 0, 2000), +(9303016, 2290104, 1, 1, 0, 2000), +(9303016, 2290117, 1, 1, 0, 2000), +(9400014, 2290053, 1, 1, 0, 40000), +(9400014, 2290087, 1, 1, 0, 40000), +(9400014, 2290112, 1, 1, 0, 40000), +(9400014, 2290122, 1, 1, 0, 40000), +(9400120, 2290045, 1, 1, 0, 40000), +(9400121, 2280014, 1, 1, 0, 180000), +(9400121, 2290081, 1, 1, 0, 60000), +(9400121, 2290087, 1, 1, 0, 60000), +(9400121, 2290101, 1, 1, 0, 60000), +(9400121, 2290103, 1, 1, 0, 60000), +(9400122, 2290007, 1, 1, 0, 40000), +(9400122, 2290062, 1, 1, 0, 40000), +(9400122, 2290116, 1, 1, 0, 40000), +(9400300, 2290045, 1, 1, 0, 120000), +(9400300, 2290055, 1, 1, 0, 120000), +(9400300, 2290063, 1, 1, 0, 120000), +(9400300, 2290079, 1, 1, 0, 120000), +(9400300, 2290081, 1, 1, 0, 120000), +(9400300, 2290096, 1, 1, 0, 120000), +(9400514, 2290023, 1, 1, 0, 40000), +(9400514, 2290057, 1, 1, 0, 40000), +(9400514, 2290088, 1, 1, 0, 40000), +(9400514, 2290095, 1, 1, 0, 40000), +(9400514, 2290115, 1, 1, 0, 40000), +(9400514, 2290139, 1, 1, 0, 120000), +(9400549, 2290001, 1, 1, 0, 40000), +(9400549, 2290020, 1, 1, 0, 40000), +(9400549, 2290045, 1, 1, 0, 40000), +(9400549, 2290057, 1, 1, 0, 40000), +(9400549, 2290086, 1, 1, 0, 40000), +(9400575, 2290009, 1, 1, 0, 40000), +(9400575, 2290051, 1, 1, 0, 40000), +(9400575, 2290081, 1, 1, 0, 40000), +(9400575, 2290087, 1, 1, 0, 40000), +(9400575, 2290107, 1, 1, 0, 40000), +(9400575, 2290123, 1, 1, 0, 40000), +(9400580, 2290004, 1, 1, 0, 2000), +(9400580, 2290024, 1, 1, 0, 2000), +(9400580, 2290083, 1, 1, 0, 2000), +(9400580, 2290087, 1, 1, 0, 2000), +(9400580, 2290103, 1, 1, 0, 2000), +(9400580, 2290121, 1, 1, 0, 2000), +(9400582, 2290005, 1, 1, 0, 2000), +(9400582, 2290010, 1, 1, 0, 2000), +(9400582, 2290029, 1, 1, 0, 2000), +(9400582, 2290047, 1, 1, 0, 2000), +(9400582, 2290049, 1, 1, 0, 2000), +(9400582, 2290074, 1, 1, 0, 2000), +(9400582, 2290079, 1, 1, 0, 2000), +(9400582, 2290081, 1, 1, 0, 2000), +(9400582, 2290135, 1, 1, 0, 6000), +(9400590, 2290088, 1, 1, 0, 60000), +(9400590, 2290125, 1, 1, 0, 60000), +(9400590, 2290135, 1, 1, 0, 180000), +(9400591, 2290039, 1, 1, 0, 60000), +(9400591, 2290074, 1, 1, 0, 60000), +(9400591, 2290113, 1, 1, 0, 60000), +(9400592, 2290047, 1, 1, 0, 60000), +(9400592, 2290123, 1, 1, 0, 60000), +(9400592, 2290131, 1, 1, 0, 180000), +(9400593, 2290069, 1, 1, 0, 60000), +(9400593, 2290093, 1, 1, 0, 60000), +(9400593, 2290138, 1, 1, 0, 180000), +(9420513, 2290039, 1, 1, 0, 40000), +(9420513, 2290100, 1, 1, 0, 40000), +(9420513, 2290108, 1, 1, 0, 40000), +(9420513, 2290118, 1, 1, 0, 40000), +(9420513, 2290138, 1, 1, 0, 120000), +(9420514, 2290099, 1, 1, 0, 1287), +(9420517, 2290000, 1, 1, 0, 1287), +(9420517, 2290008, 1, 1, 0, 1287), +(9420517, 2290018, 1, 1, 0, 1287), +(9420517, 2290038, 1, 1, 0, 1287), +(9420517, 2290060, 1, 1, 0, 1287), +(9420517, 2290080, 1, 1, 0, 1287), +(9420517, 2290103, 1, 1, 0, 1287), +(9420518, 2290123, 1, 1, 0, 1287), +(9420519, 2290113, 1, 1, 0, 1287), +(9420522, 2290000, 1, 1, 0, 1287), +(9420522, 2290001, 1, 1, 0, 1287), +(9420522, 2290011, 1, 1, 0, 1287), +(9420522, 2290025, 1, 1, 0, 1287), +(9420522, 2290028, 1, 1, 0, 1287), +(9420522, 2290037, 1, 1, 0, 1287), +(9420522, 2290043, 1, 1, 0, 1287), +(9420522, 2290066, 1, 1, 0, 1287), +(9420522, 2290082, 1, 1, 0, 1287), +(9420522, 2290083, 1, 1, 0, 1287), +(9420522, 2290089, 1, 1, 0, 1287), +(9420522, 2290091, 1, 1, 0, 1287), +(9420522, 2290107, 1, 1, 0, 1287), +(9420540, 2280006, 1, 1, 0, 2000), +(9420540, 2290119, 1, 1, 0, 2000), +(9420540, 2290120, 1, 1, 0, 2000), +(9420544, 2280007, 1, 1, 0, 80000), +(9420544, 2280008, 1, 1, 0, 80000), +(9420544, 2280009, 1, 1, 0, 80000), +(9420544, 2280010, 1, 1, 0, 80000), +(9420544, 2290002, 1, 1, 0, 80000), +(9420544, 2290015, 1, 1, 0, 80000), +(9420544, 2290022, 1, 1, 0, 80000), +(9420544, 2290027, 1, 1, 0, 80000), +(9420544, 2290034, 1, 1, 0, 80000), +(9420544, 2290052, 1, 1, 0, 80000), +(9420544, 2290054, 1, 1, 0, 80000), +(9420544, 2290089, 1, 1, 0, 80000), +(9420544, 2290094, 1, 1, 0, 80000), +(9420544, 2290098, 1, 1, 0, 80000), +(9420544, 2290105, 1, 1, 0, 80000), +(9420544, 2290110, 1, 1, 0, 80000), +(9420544, 2290119, 1, 1, 0, 80000), +(9420549, 2280007, 1, 1, 0, 80000), +(9420549, 2280008, 1, 1, 0, 80000), +(9420549, 2280009, 1, 1, 0, 80000), +(9420549, 2280010, 1, 1, 0, 80000), +(9420549, 2290002, 1, 1, 0, 80000), +(9420549, 2290015, 1, 1, 0, 80000), +(9420549, 2290022, 1, 1, 0, 80000), +(9420549, 2290027, 1, 1, 0, 80000), +(9420549, 2290034, 1, 1, 0, 80000), +(9420549, 2290052, 1, 1, 0, 80000), +(9420549, 2290054, 1, 1, 0, 80000), +(9420549, 2290089, 1, 1, 0, 80000), +(9420549, 2290094, 1, 1, 0, 80000), +(9420549, 2290098, 1, 1, 0, 80000), +(9420549, 2290105, 1, 1, 0, 80000), +(9420549, 2290110, 1, 1, 0, 80000), +(9420549, 2290119, 1, 1, 0, 80000), +(9500166, 2290044, 1, 1, 0, 2000), +(9500173, 2290018, 1, 1, 0, 40000), +(9500173, 2290019, 1, 1, 0, 40000), +(9500173, 2290032, 1, 1, 0, 40000), +(9500173, 2290042, 1, 1, 0, 40000), +(9500173, 2290058, 1, 1, 0, 40000), +(9500173, 2290068, 1, 1, 0, 40000), +(9500173, 2290072, 1, 1, 0, 40000), +(9500173, 2290092, 1, 1, 0, 40000), +(9500173, 2290099, 1, 1, 0, 40000), +(9500173, 2290102, 1, 1, 0, 40000), +(9500173, 2290119, 1, 1, 0, 40000), +(9500173, 2290128, 1, 1, 0, 120000), +(9500174, 2290002, 1, 1, 0, 40000), +(9500174, 2290014, 1, 1, 0, 40000), +(9500174, 2290030, 1, 1, 0, 40000), +(9500174, 2290080, 1, 1, 0, 40000), +(9500174, 2290130, 1, 1, 0, 120000), +(9500180, 2290010, 1, 1, 0, 40000), +(9500180, 2290028, 1, 1, 0, 40000), +(9500180, 2290126, 1, 1, 0, 120000), +(9500181, 2290010, 1, 1, 0, 40000), +(9500181, 2290028, 1, 1, 0, 40000), +(9500181, 2290126, 1, 1, 0, 120000), +(9500331, 2290010, 1, 1, 0, 40000), +(9500331, 2290028, 1, 1, 0, 40000), +(9500331, 2290126, 1, 1, 0, 120000), +(9500332, 2290132, 1, 1, 0, 120000), +(9500333, 2290006, 1, 1, 0, 40000), +(9500333, 2290030, 1, 1, 0, 40000), +(9500333, 2290032, 1, 1, 0, 40000), +(9500333, 2290060, 1, 1, 0, 40000), +(9500333, 2290076, 1, 1, 0, 40000), +(9500333, 2290104, 1, 1, 0, 40000), +(9500333, 2290117, 1, 1, 0, 40000); \ No newline at end of file diff --git a/src/client/MapleBuffStat.java b/src/client/MapleBuffStat.java index 57d3689910..a70a7e2bfe 100644 --- a/src/client/MapleBuffStat.java +++ b/src/client/MapleBuffStat.java @@ -30,37 +30,35 @@ public enum MapleBuffStat { SHARP_EYES(0x20L), MANA_REFLECTION(0x40L), //ALWAYS_RIGHT(0X80L), - - //------ bgn EDITED SLOT (was unused before) -------- - MAP_PROTECTION(0x100000000000000L), - //------ end EDITED SLOT ---------------------------- - SHADOW_CLAW(0x100L), INFINITY(0x200L), HOLY_SHIELD(0x400L), HAMSTRING(0x800L), BLIND(0x1000L), CONCENTRATE(0x2000L), - HPREC(0x4000L), + PUPPET(0x4000L), ECHO_OF_HERO(0x8000L), - MPREC(0x10000L), + MESO_UP_BY_ITEM(0x10000L), GHOST_MORPH(0x20000L), AURA(0x40000L), CONFUSE(0x80000L), - ARIANT_PQ_SHIELD(0x40000L), // ------ COUPON feature ------ - COUPON_EXP1(0x100000L), COUPON_EXP2(0x200000L), - COUPON_EXP3(0x400000L), - COUPON_EXP4(0x800000L), - COUPON_DRP1(0x1000000L), - COUPON_DRP2(0x2000000L), - COUPON_DRP3(0x4000000L), + COUPON_EXP3(0x400000L), COUPON_EXP4(0x400000L), + COUPON_DRP1(0x800000L), + COUPON_DRP2(0x1000000L), COUPON_DRP3(0x1000000L), - // ---- end COUPON feature ---- + // ------ monster card buffs, thanks to Arnah (Vertisy) ------ + ITEM_UP_BY_ITEM(0x100000L), + RESPECT_PIMMUNE(0x200000L), + RESPECT_MIMMUNE(0x400000L), + DEFENSE_ATT(0x800000L), + DEFENSE_STATE(0x1000000L), + HPREC(0x2000000L), + MPREC(0x4000000L), BERSERK_FURY(0x8000000L), DIVINE_BODY(0x10000000L), SPARK(0x20000000L), @@ -73,7 +71,6 @@ public enum MapleBuffStat { ACC(0x1000000000L), AVOID(0x2000000000L), HANDS(0x4000000000L), - SHOWDASH(0x4000000000L), SPEED(0x8000000000L), JUMP(0x10000000000L), MAGIC_GUARD(0x20000000000L), @@ -96,11 +93,10 @@ public enum MapleBuffStat { MESOUP(0x200000000000000L), SHADOWPARTNER(0x400000000000000L), PICKPOCKET(0x800000000000000L), - PUPPET(0x800000000000000L), MESOGUARD(0x1000000000000000L), EXP_INCREASE(0x2000000000000000L), WEAKEN(0x4000000000000000L), - //THAT GAP + MAP_PROTECTION(0x8000000000000000L), //all incorrect buffstats SLOW(0x200000000L, true), @@ -144,4 +140,9 @@ public enum MapleBuffStat { public boolean isFirst() { return isFirst; } + + @Override + public String toString() { + return name(); + } } diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index 3774409a04..8e134781f5 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -44,6 +44,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.Stack; import java.util.Comparator; import java.util.concurrent.locks.Lock; import java.util.concurrent.ScheduledFuture; @@ -205,7 +206,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private transient int localstr, localdex, localluk, localint_, localmagic, localwatk; private transient int equipmaxhp, equipmaxmp, equipstr, equipdex, equipluk, equipint_, equipmagic, equipwatk, localchairhp, localchairmp; private int localchairrate; - private boolean hidden, equipchanged = true, canDoor = true, berserk, hasMerchant, hasSandboxItem = false, whiteChat = false; + private boolean hidden, equipchanged = true, canDoor = true, berserk, hasMerchant, hasSandboxItem = false, whiteChat = false, canRecvPartySearchInvite = true; private boolean equippedMesoMagnet = false, equippedItemPouch = false, equippedPetItemIgnore = false; private int linkedLevel = 0; private String linkedName = null; @@ -286,6 +287,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private Lock cpnLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_CPN); private Map> excluded = new LinkedHashMap<>(); private Set excludedItems = new LinkedHashSet<>(); + private Set disabledPartySearchInvites = new LinkedHashSet<>(); private static String[] ariantroomleader = new String[3]; private static int[] ariantroomslot = new int[3]; private long portaldelay = 0, lastcombo = 0; @@ -484,6 +486,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public void setEnteredChannelWorld() { awayFromWorld.set(false); client.getChannelServer().removePlayerAway(id); + + if (canRecvPartySearchInvite) { + this.getWorldServer().getPartySearchCoordinator().attachPlayer(this); + } } public void setAwayFromChannelWorld() { @@ -504,6 +510,46 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + public void updatePartySearchAvailability(boolean psearchAvailable) { + if (psearchAvailable) { + if (canRecvPartySearchInvite && getParty() == null) { + this.getWorldServer().getPartySearchCoordinator().attachPlayer(this); + } + } else { + if (canRecvPartySearchInvite) { + this.getWorldServer().getPartySearchCoordinator().detachPlayer(this); + } + } + } + + public boolean toggleRecvPartySearchInvite() { + canRecvPartySearchInvite = !canRecvPartySearchInvite; + + if (canRecvPartySearchInvite) { + updatePartySearchAvailability(getParty() == null); + } else { + this.getWorldServer().getPartySearchCoordinator().detachPlayer(this); + } + + return canRecvPartySearchInvite; + } + + public boolean isRecvPartySearchInviteEnabled() { + return canRecvPartySearchInvite; + } + + public void resetPartySearchInvite(int fromLeaderid) { + disabledPartySearchInvites.remove(fromLeaderid); + } + + public void disablePartySearchInvite(int fromLeaderid) { + disabledPartySearchInvites.add(fromLeaderid); + } + + public boolean hasDisabledPartySearchInvite(int fromLeaderid) { + return disabledPartySearchInvites.contains(fromLeaderid); + } + public void setSessionTransitionState() { client.getSession().setAttribute(MapleClient.CLIENT_TRANSITION); } @@ -1047,7 +1093,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return;//the fuck you doing idiot! } - this.job = newJob; + if (canRecvPartySearchInvite && getParty() == null) { + this.updatePartySearchAvailability(false); + this.job = newJob; + this.updatePartySearchAvailability(true); + } else { + this.job = newJob; + } int spGain = 1; if (GameConstants.hasSPTable(newJob)) { @@ -3448,6 +3500,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { for(Entry> bpl: buffEffects.entrySet()) { MapleBuffStatValueHolder mbsvhi = bpl.getValue().get(mbs); if(mbsvhi != null) { + if(!mbsvhi.effect.isActive(mapid)) { + continue; + } + if(mbsvhi.value > max.left) { max = new Pair<>(mbsvhi.value, mbsvhi.effect.getStatups().size()); mbsvh = mbsvhi; @@ -3477,6 +3533,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { effLock.lock(); chrLock.lock(); try { + System.out.println("-------------------"); + System.out.println("CACHED BUFF COUNT: "); + for(Entry bpl : buffEffectsCount.entrySet()) { + System.out.println(bpl.getKey() + ": " + bpl.getValue()); + } System.out.println("-------------------"); System.out.println("CACHED BUFFS: "); for(Entry> bpl : buffEffects.entrySet()) { @@ -3664,46 +3725,33 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + public void updateActiveEffects() { + chrLock.lock(); + try { + effects.clear(); + updateEffects(buffEffectsCount.keySet()); + } finally { + chrLock.unlock(); + } + } + private void updateEffects(Set removedStats) { chrLock.lock(); try { - Map> retrievedEffects = new LinkedHashMap<>(); - Map> maxStatups = new LinkedHashMap<>(); - - for(Entry> bel : buffEffects.entrySet()) { - for(Entry belv : bel.getValue().entrySet()) { - if(removedStats.contains(belv.getKey())) { - if(!retrievedEffects.containsKey(bel.getKey())) { - retrievedEffects.put(bel.getKey(), new Pair<>(belv.getValue().effect, belv.getValue().startTime)); - } - - Pair thisStat = maxStatups.get(belv.getKey()); - if(thisStat == null || belv.getValue().value > thisStat.getRight()) { - maxStatups.put(belv.getKey(), new Pair<>(bel.getKey(), belv.getValue().value)); - } - } - } - } - - Map> bestEffects = new LinkedHashMap<>(); Set retrievedStats = new LinkedHashSet<>(); - for(Entry> lmsee: maxStatups.entrySet()) { - if(isSingletonStatup(lmsee.getKey())) { - continue; - } + + for (MapleBuffStat mbs : removedStats) { + fetchBestEffectFromItemEffectHolder(mbs); - Integer srcid = lmsee.getValue().getLeft(); - if(!bestEffects.containsKey(srcid)) { - Pair msel = retrievedEffects.get(srcid); - - bestEffects.put(srcid, msel); - for(Pair mbsi : msel.getLeft().getStatups()) { - retrievedStats.add(mbsi.getLeft()); + MapleBuffStatValueHolder mbsvh = effects.get(mbs); + if (mbsvh != null) { + for (Pair statup : mbsvh.effect.getStatups()) { + retrievedStats.add(statup.getLeft()); } } } - propagateBuffEffectUpdates(bestEffects, retrievedStats); + propagateBuffEffectUpdates(new LinkedHashMap>(), retrievedStats, removedStats); } finally { chrLock.unlock(); } @@ -3771,21 +3819,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } if (!overwrite) { - List cancelStats = new LinkedList<>(); - - chrLock.lock(); - try { - for(Entry mbsl : buffstats.entrySet()) { - cancelStats.add(mbsl.getKey()); - } - } finally { - chrLock.unlock(); - } - - for(MapleBuffStat mbs : cancelStats) { - removedStats.add(mbs); - } - cancelPlayerBuffs(cancelStats); + removedStats.addAll(buffstats.keySet()); } return toCancel; @@ -3909,7 +3943,152 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return extractedStatBuffs; } - private void propagateBuffEffectUpdates(Map> retrievedEffects, Set retrievedStats) { + private void cancelInactiveBuffStats(Set retrievedStats, Set removedStats) { + List inactiveStats = new LinkedList<>(); + for (MapleBuffStat mbs : removedStats) { + if (!retrievedStats.contains(mbs)) { + inactiveStats.add(mbs); + } + } + + if (!inactiveStats.isEmpty()) { + client.announce(MaplePacketCreator.cancelBuff(inactiveStats)); + getMap().broadcastMessage(this, MaplePacketCreator.cancelForeignBuff(getId(), inactiveStats), false); + } + } + + private static Map topologicalSortLeafStatCount(Map> buffStack) { + Map leafBuffCount = new LinkedHashMap<>(); + + for (Entry> e : buffStack.entrySet()) { + Stack mseStack = e.getValue(); + if (mseStack.isEmpty()) { + continue; + } + + MapleStatEffect mse = mseStack.peek(); + Integer count = leafBuffCount.get(mse); + if (count == null) { + leafBuffCount.put(mse, 1); + } else { + leafBuffCount.put(mse, count + 1); + } + } + + return leafBuffCount; + } + + private static List topologicalSortRemoveLeafStats(Map> stackedBuffStats, Map> buffStack, Map leafStatCount) { + List clearedStatEffects = new LinkedList<>(); + Set clearedStats = new LinkedHashSet<>(); + + for (Entry e : leafStatCount.entrySet()) { + MapleStatEffect mse = e.getKey(); + + if (stackedBuffStats.get(mse).size() <= e.getValue()) { + clearedStatEffects.add(mse); + + for (MapleBuffStat mbs : stackedBuffStats.get(mse)) { + clearedStats.add(mbs); + } + } + } + + for (MapleBuffStat mbs : clearedStats) { + MapleStatEffect mse = buffStack.get(mbs).pop(); + stackedBuffStats.get(mse).remove(mbs); + } + + return clearedStatEffects; + } + + private static void topologicalSortRebaseLeafStats(Map> stackedBuffStats, Map> buffStack) { + for (Entry> e : buffStack.entrySet()) { + Stack mseStack = e.getValue(); + + if (!mseStack.isEmpty()) { + MapleStatEffect mse = mseStack.pop(); + stackedBuffStats.get(mse).remove(e.getKey()); + } + } + } + + private static List topologicalSortEffects(Map>> buffEffects) { + Map> stackedBuffStats = new LinkedHashMap<>(); + Map> buffStack = new LinkedHashMap<>(); + + for (Entry>> e : buffEffects.entrySet()) { + MapleBuffStat mbs = e.getKey(); + + Stack mbsStack = new Stack<>(); + buffStack.put(mbs, mbsStack); + + for (Pair emse : e.getValue()) { + MapleStatEffect mse = emse.getLeft(); + mbsStack.push(mse); + + Set mbsStats = stackedBuffStats.get(mse); + if (mbsStats == null) { + mbsStats = new LinkedHashSet<>(); + stackedBuffStats.put(mse, mbsStats); + } + + mbsStats.add(mbs); + } + } + + List buffList = new LinkedList<>(); + while (true) { + Map leafStatCount = topologicalSortLeafStatCount(buffStack); + if (leafStatCount.isEmpty()) break; + + List clearedNodes = topologicalSortRemoveLeafStats(stackedBuffStats, buffStack, leafStatCount); + if (clearedNodes.isEmpty()) { + topologicalSortRebaseLeafStats(stackedBuffStats, buffStack); + } else { + buffList.addAll(clearedNodes); + } + } + + return buffList; + } + + private static List sortEffectsList(Map updateEffectsList) { + Map>> buffEffects = new LinkedHashMap<>(); + + for (Entry p : updateEffectsList.entrySet()) { + MapleStatEffect mse = p.getKey(); + + for (Pair statup : mse.getStatups()) { + MapleBuffStat stat = statup.getLeft(); + + List> statBuffs = buffEffects.get(stat); + if (statBuffs == null) { + statBuffs = new ArrayList<>(); + buffEffects.put(stat, statBuffs); + } + + statBuffs.add(new Pair<>(mse, statup.getRight())); + } + } + + Comparator cmp = new Comparator>() { + @Override + public int compare(Pair o1, Pair o2) + { + return o2.getRight().compareTo(o1.getRight()); + } + }; + + for (Entry>> statBuffs : buffEffects.entrySet()) { + Collections.sort(statBuffs.getValue(), cmp); + } + + return topologicalSortEffects(buffEffects); + } + + private void propagateBuffEffectUpdates(Map> retrievedEffects, Set retrievedStats, Set removedStats) { + cancelInactiveBuffStats(retrievedStats, removedStats); if (retrievedStats.isEmpty()) { return; } @@ -3924,7 +4103,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { maxBuffValue.put(mbs, new Pair<>(Integer.MIN_VALUE, (MapleStatEffect) null)); } - Map> updateEffects = new LinkedHashMap<>(); + Map updateEffects = new LinkedHashMap<>(); List recalcMseList = new LinkedList<>(); for(Entry> re : retrievedEffects.entrySet()) { @@ -3937,7 +4116,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject { recalcMseList = new LinkedList<>(); for(MapleStatEffect mse : mseList) { - int mseAmount = 0; int maxEffectiveStatup = Integer.MIN_VALUE; for(Pair st : mse.getStatups()) { MapleBuffStat mbs = st.getLeft(); @@ -3972,36 +4150,16 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } } - - if(relevantStatup) { - mseAmount += st.getRight(); - } } - updateEffects.put(mse, new Pair<>(maxEffectiveStatup, mseAmount)); + updateEffects.put(mse, maxEffectiveStatup); } } while(!recalcMseList.isEmpty()); - List>> updateEffectsList = new ArrayList<>(); - for(Entry> ue : updateEffects.entrySet()) { - updateEffectsList.add(new Pair<>(ue.getKey(), ue.getValue())); - } - - Collections.sort(updateEffectsList, new Comparator>>() { - @Override - public int compare(Pair> o1, Pair> o2) - { - if(o1.getRight().getLeft().equals(o2.getRight().getLeft())) { - return o1.getRight().getRight().compareTo(o2.getRight().getRight()); - } else { - return o1.getRight().getLeft().compareTo(o2.getRight().getLeft()); - } - } - }); + List updateEffectsList = sortEffectsList(updateEffects); List>> toUpdateEffects = new LinkedList<>(); - for(Pair> msep : updateEffectsList) { - MapleStatEffect mse = msep.getLeft(); + for(MapleStatEffect mse : updateEffectsList) { toUpdateEffects.add(new Pair<>(mse.getBuffSourceId(), retrievedEffects.get(mse.getBuffSourceId()))); } @@ -4010,9 +4168,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { Pair msel = lmse.getRight(); for(Pair statup : getActiveStatupsFromSourceid(lmse.getLeft())) { - if(!isSingletonStatup(statup.getLeft())) { - activeStatups.add(statup); - } + activeStatups.add(statup); } msel.getLeft().updateBuffEffect(this, activeStatups, msel.getRight()); @@ -4061,6 +4217,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + private void addItemEffectHolderCount(MapleBuffStat stat) { + Byte val = buffEffectsCount.get(stat); + if (val != null) { + val = (byte) (val + 1); + } else { + val = (byte) 1; + } + + buffEffectsCount.put(stat, val); + } + public void registerEffect(MapleStatEffect effect, long starttime, long expirationtime, boolean isSilent) { if (effect.isDragonBlood()) { prepareDragonBlood(effect); @@ -4180,6 +4347,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { appliedStatups.put(ps.getLeft(), new MapleBuffStatValueHolder(effect, starttime, ps.getRight())); } + boolean active = effect.isActive(mapid); if(ServerConstants.USE_BUFF_MOST_SIGNIFICANT) { toDeploy = new LinkedHashMap<>(); Map> retrievedEffects = new LinkedHashMap<>(); @@ -4189,25 +4357,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject { MapleBuffStatValueHolder mbsvh = effects.get(statup.getKey()); MapleBuffStatValueHolder statMbsvh = statup.getValue(); - if(mbsvh == null || mbsvh.value < statMbsvh.value || (mbsvh.value == statMbsvh.value && mbsvh.effect.getStatups().size() <= statMbsvh.effect.getStatups().size())) { - toDeploy.put(statup.getKey(), statMbsvh); - } else { - if(!isSingletonStatup(statup.getKey())) { - retrievedEffects.put(mbsvh.effect.getBuffSourceId(), new Pair<>(mbsvh.effect, mbsvh.startTime)); - for(Pair mbs : mbsvh.effect.getStatups()) { - retrievedStats.add(mbs.getLeft()); + if(active) { + if(mbsvh == null || mbsvh.value < statMbsvh.value || (mbsvh.value == statMbsvh.value && mbsvh.effect.getStatups().size() <= statMbsvh.effect.getStatups().size())) { + toDeploy.put(statup.getKey(), statMbsvh); + } else { + if(!isSingletonStatup(statup.getKey())) { + for(Pair mbs : mbsvh.effect.getStatups()) { + retrievedStats.add(mbs.getLeft()); + } } } } - Byte val = buffEffectsCount.get(statup.getKey()); - if (val != null) { - val = (byte) (val + 1); - } else { - val = (byte) 1; - } - - buffEffectsCount.put(statup.getKey(), val); + addItemEffectHolderCount(statup.getKey()); } if(!isSilent) { @@ -4216,22 +4378,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject { effects.put(statup.getKey(), statup.getValue()); } - retrievedEffects.put(sourceid, new Pair<>(effect, starttime)); - propagateBuffEffectUpdates(retrievedEffects, retrievedStats); + if (active) { + retrievedEffects.put(sourceid, new Pair<>(effect, starttime)); + } + + propagateBuffEffectUpdates(retrievedEffects, retrievedStats, new LinkedHashSet()); } } else { for (Entry statup : appliedStatups.entrySet()) { - Byte val = buffEffectsCount.get(statup.getKey()); - if (val != null) { - val = (byte) (val + 1); - } else { - val = (byte) 1; - } - - buffEffectsCount.put(statup.getKey(), val); + addItemEffectHolderCount(statup.getKey()); } - toDeploy = appliedStatups; + toDeploy = (active ? appliedStatups : new LinkedHashMap()); } addItemEffectHolder(sourceid, expirationtime, appliedStatups); @@ -4607,6 +4765,24 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return w.getMesoRate() * w.getQuestRate(); } + public float getCardRate(int itemid) { + float rate = 100.0f; + + if (itemid == 0) { + MapleStatEffect mseMeso = getBuffEffect(MapleBuffStat.MESO_UP_BY_ITEM); + if (mseMeso != null) { + rate += mseMeso.getCardRate(mapid, itemid); + } + } else { + MapleStatEffect mseItem = getBuffEffect(MapleBuffStat.ITEM_UP_BY_ITEM); + if (mseItem != null) { + rate += mseItem.getCardRate(mapid, itemid); + } + } + + return rate / 100; + } + public int getFace() { return face; } @@ -5169,6 +5345,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject { this.gmLevel = Math.max(level, 0); } + public void closePartySearchInteractions() { + this.getWorldServer().getPartySearchCoordinator().unregisterPartyLeader(this); + if (canRecvPartySearchInvite) { + this.getWorldServer().getPartySearchCoordinator().detachPlayer(this); + } + } + public void closePlayerInteractions() { closeNpcShop(); closeTrade(); @@ -5570,8 +5753,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return vanquisherStage; } - public Collection getVisibleMapObjects() { - return Collections.unmodifiableCollection(visibleMapObjects); + public MapleMapObject[] getVisibleMapObjects() { + return visibleMapObjects.toArray(new MapleMapObject[visibleMapObjects.size()]); } public int getWorld() { @@ -6717,6 +6900,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int buddyCapacity = rs.getInt("buddyCapacity"); ret.buddylist = new BuddyList(buddyCapacity); ret.lastExpGainTime = rs.getTimestamp("lastExpGainTime").getTime(); + ret.canRecvPartySearchInvite = rs.getBoolean("partySearch"); ret.getInventory(MapleInventoryType.EQUIP).setSlotLimit(rs.getByte("equipslots")); ret.getInventory(MapleInventoryType.USE).setSlotLimit(rs.getByte("useslots")); @@ -7979,12 +8163,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); con.setAutoCommit(false); PreparedStatement ps; - ps = con.prepareStatement("UPDATE characters SET level = ?, fame = ?, str = ?, dex = ?, luk = ?, `int` = ?, exp = ?, gachaexp = ?, hp = ?, mp = ?, maxhp = ?, maxmp = ?, sp = ?, ap = ?, gm = ?, skincolor = ?, gender = ?, job = ?, hair = ?, face = ?, map = ?, meso = ?, hpMpUsed = ?, spawnpoint = ?, party = ?, buddyCapacity = ?, messengerid = ?, messengerposition = ?, mountlevel = ?, mountexp = ?, mounttiredness= ?, equipslots = ?, useslots = ?, setupslots = ?, etcslots = ?, monsterbookcover = ?, vanquisherStage = ?, dojoPoints = ?, lastDojoStage = ?, finishedDojoTutorial = ?, vanquisherKills = ?, matchcardwins = ?, matchcardlosses = ?, matchcardties = ?, omokwins = ?, omoklosses = ?, omokties = ?, dataString = ?, fquest = ?, jailexpire = ?, partnerId = ?, marriageItemId = ?, lastExpGainTime = ?, ariantPoints = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS); - if (gmLevel < 1 && level > 199) { - ps.setInt(1, isCygnus() ? 120 : 200); - } else { - ps.setInt(1, level); - } + ps = con.prepareStatement("UPDATE characters SET level = ?, fame = ?, str = ?, dex = ?, luk = ?, `int` = ?, exp = ?, gachaexp = ?, hp = ?, mp = ?, maxhp = ?, maxmp = ?, sp = ?, ap = ?, gm = ?, skincolor = ?, gender = ?, job = ?, hair = ?, face = ?, map = ?, meso = ?, hpMpUsed = ?, spawnpoint = ?, party = ?, buddyCapacity = ?, messengerid = ?, messengerposition = ?, mountlevel = ?, mountexp = ?, mounttiredness= ?, equipslots = ?, useslots = ?, setupslots = ?, etcslots = ?, monsterbookcover = ?, vanquisherStage = ?, dojoPoints = ?, lastDojoStage = ?, finishedDojoTutorial = ?, vanquisherKills = ?, matchcardwins = ?, matchcardlosses = ?, matchcardties = ?, omokwins = ?, omoklosses = ?, omokties = ?, dataString = ?, fquest = ?, jailexpire = ?, partnerId = ?, marriageItemId = ?, lastExpGainTime = ?, ariantPoints = ?, partySearch = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS); + ps.setInt(1, level); // thanks CanIGetaPR for noticing an unnecessary "level" limitation when persisting DB data ps.setInt(2, fame); effLock.lock(); @@ -8094,7 +8274,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ps.setInt(52, marriageItemid); ps.setTimestamp(53, new Timestamp(lastExpGainTime)); ps.setInt(54, ariantPoints); - ps.setInt(55, id); + ps.setBoolean(55, canRecvPartySearchInvite); + ps.setInt(56, id); int updateRows = ps.executeUpdate(); ps.close(); diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java index 4af9f01cb3..3145bc2a45 100644 --- a/src/client/MapleClient.java +++ b/src/client/MapleClient.java @@ -888,6 +888,7 @@ public class MapleClient { player.cancelAllBuffs(true); player.closePlayerInteractions(); + player.closePartySearchInteractions(); if (!serverTransition) { // thanks MedicOP for detecting an issue with party leader change on changing channels removePartyPlayer(wserv); @@ -1463,6 +1464,7 @@ public class MapleClient { } player.closePlayerInteractions(); + player.closePartySearchInteractions(); player.unregisterChairBuff(); server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs()); diff --git a/src/client/MapleJob.java b/src/client/MapleJob.java index 60cc28f578..25511ef139 100644 --- a/src/client/MapleJob.java +++ b/src/client/MapleJob.java @@ -114,11 +114,6 @@ public enum MapleJob { return BEGINNER; } } - - public boolean isBeginner(MapleJob beginners) { - return MAGICIAN == beginners || WARRIOR == beginners || THIEF == beginners || PIRATE == beginners || BOWMAN == beginners || ARAN1 == beginners || THUNDERBREAKER1 == beginners - || DAWNWARRIOR1 == beginners || NIGHTWALKER1 == beginners || BLAZEWIZARD1 == beginners; - } public boolean isA(MapleJob basejob) { // thanks Steve (kaito1410) for pointing out an improvement here int basebranch = basejob.getId() / 10; diff --git a/src/client/command/commands/gm2/IdCommand.java b/src/client/command/commands/gm2/IdCommand.java index 91ecace67a..304f19c62a 100644 --- a/src/client/command/commands/gm2/IdCommand.java +++ b/src/client/command/commands/gm2/IdCommand.java @@ -9,6 +9,7 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.*; +import server.ThreadManager; public class IdCommand extends Command { { @@ -34,7 +35,7 @@ public class IdCommand extends Command { return; } final String queryItem = joinStringArr(Arrays.copyOfRange(params, 1, params.length), " "); - player.yellowMessage("Querying for entry... May take some time... Please try to refine your search"); + player.yellowMessage("Querying for entry... May take some time... Please try to refine your search."); Runnable queryRunnable = new Runnable() { @Override public void run() { @@ -42,28 +43,31 @@ public class IdCommand extends Command { populateIdMap(params[0].toLowerCase()); Map resultList = fetchResults(itemMap.get(params[0]), queryItem); - + StringBuilder sb = new StringBuilder(); + if (resultList.size() > 0) { int count = 0; for (Map.Entry entry: resultList.entrySet()) { - player.yellowMessage(String.format("Id for %s is: %s", entry.getKey(), entry.getValue())); + sb.append(String.format("Id for %s is: #b%s#k", entry.getKey(), entry.getValue()) + "\r\n"); if (++count > 100) { break; } } - player.yellowMessage(String.format("Results found: %d | Returned: %d/100 | Refine search query to improve time.", resultList.size(), count - 1)); + sb.append(String.format("Results found: #r%d#k | Returned: #b%d#k/100 | Refine search query to improve time.", resultList.size(), count) + "\r\n"); + + player.getClient().getAbstractPlayerInteraction().npcTalk(9010000, sb.toString()); } else { - player.yellowMessage(String.format("Id not found for item: %s, of type: %s", queryItem, params[0])); + player.yellowMessage(String.format("Id not found for item: %s, of type: %s.", queryItem, params[0])); } } catch (IdTypeNotSupportedException e) { - player.yellowMessage("Your query type is not supported"); + player.yellowMessage("Your query type is not supported."); } catch (IOException e) { - player.yellowMessage("Error reading file, please contact your administrator"); + player.yellowMessage("Error reading file, please contact your administrator."); } } }; - Thread thread = new Thread(queryRunnable); - thread.start(); + + ThreadManager.getInstance().newTask(queryRunnable); } private void populateIdMap(String type) throws IdTypeNotSupportedException, IOException { diff --git a/src/client/command/commands/gm5/DebugCommand.java b/src/client/command/commands/gm5/DebugCommand.java index 18700b424b..e2617b73f4 100644 --- a/src/client/command/commands/gm5/DebugCommand.java +++ b/src/client/command/commands/gm5/DebugCommand.java @@ -39,10 +39,9 @@ import tools.MaplePacketCreator; import java.awt.*; import java.util.Arrays; import java.util.List; -import tools.packets.Wedding; public class DebugCommand extends Command { - private final static String debugTypes[] = {"monster", "packet", "portal", "spawnpoint", "pos", "map", "mobsp", "event", "areas", "reactors", "servercoupons", "playercoupons", "timer", "marriage", ""}; + private final static String debugTypes[] = {"monster", "packet", "portal", "spawnpoint", "pos", "map", "mobsp", "event", "areas", "reactors", "servercoupons", "playercoupons", "timer", "marriage", "buff", ""}; { setDescription(""); @@ -158,7 +157,10 @@ public class DebugCommand extends Command { case "marriage": c.getChannelServer().debugMarriageStatus(); break; - + + case "buff": + c.getPlayer().debugListAllBuffs(); + break; } } } diff --git a/src/client/inventory/MaplePet.java b/src/client/inventory/MaplePet.java index 95cc8bb771..acaa12c9bd 100644 --- a/src/client/inventory/MaplePet.java +++ b/src/client/inventory/MaplePet.java @@ -98,6 +98,65 @@ public class MaplePet extends Item { } } + private static void unreferenceMissingPetsFromInventoryDb() { + PreparedStatement ps = null; + Connection con = null; + try { + con = DatabaseConnection.getConnection(); + + ps = con.prepareStatement("UPDATE inventoryitems SET petid = -1, expiration = 0 WHERE petid != -1 AND petid NOT IN (SELECT petid FROM pets)"); + ps.executeUpdate(); + + ps.close(); + con.close(); + } catch(SQLException ex) { + ex.printStackTrace(); + } finally { + try { + if(ps != null && !ps.isClosed()) { + ps.close(); + } + if(con != null && !con.isClosed()) { + con.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + private static void deleteMissingPetsFromDb() { + PreparedStatement ps = null; + Connection con = null; + try { + con = DatabaseConnection.getConnection(); + + ps = con.prepareStatement("DELETE FROM pets WHERE petid NOT IN (SELECT petid FROM inventoryitems WHERE petid != -1)"); + ps.executeUpdate(); + + ps.close(); + con.close(); + } catch(SQLException ex) { + ex.printStackTrace(); + } finally { + try { + if(ps != null && !ps.isClosed()) { + ps.close(); + } + if(con != null && !con.isClosed()) { + con.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + public static void clearMissingPetsFromDb() { + unreferenceMissingPetsFromInventoryDb(); + deleteMissingPetsFromDb(); + } + public static void deleteFromDb(MapleCharacter owner, int petid) { try { Connection con = DatabaseConnection.getConnection(); diff --git a/src/client/inventory/manipulator/MapleInventoryManipulator.java b/src/client/inventory/manipulator/MapleInventoryManipulator.java index af689f2a57..ccc3646161 100644 --- a/src/client/inventory/manipulator/MapleInventoryManipulator.java +++ b/src/client/inventory/manipulator/MapleInventoryManipulator.java @@ -439,7 +439,7 @@ public class MapleInventoryManipulator { announceModifyInventory(c, item, fromDrop, allowZero); } - MaplePet.deleteFromDb(chr, petid); + // thanks Robin Schulz for noticing pet issues when moving pets out of inventory } else { inv.removeItem(slot, quantity, allowZero); if(type != MapleInventoryType.CANHOLD) { diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index 317f45679f..1d6cb3ae55 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -75,11 +75,10 @@ public class ServerConstants { public static final boolean USE_FIXED_RATIO_HPMP_UPDATE = true; //Enables the HeavenMS-builtin HPMP update based on the current pool to max pool ratio. public static final boolean USE_FAMILY_SYSTEM = false; public static final boolean USE_DUEY = true; - public static final boolean USE_RANDOMIZE_HPMP_GAIN = true; //Enables randomizing on MaxHP/MaxMP gains and INT accounting for the MaxMP gain. + public static final boolean USE_RANDOMIZE_HPMP_GAIN = true; //Enables randomizing on MaxHP/MaxMP gains and INT accounting for the MaxMP gain on level up. public static final boolean USE_STORAGE_ITEM_SORT = true; //Enables storage "Arrange Items" feature. public static final boolean USE_ITEM_SORT = true; //Enables inventory "Item Sort/Merge" feature. public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id. - public static final boolean USE_PARTY_SEARCH = false; public static final boolean USE_PARTY_FOR_STARTERS = true; //Players level 10 or below can create/invite other players on the given level range. public static final boolean USE_AUTOASSIGN_STARTERS_AP = false; //Beginners level 10 or below have their AP autoassigned (they can't choose to levelup a stat). Set true ONLY if the localhost doesn't support AP assigning for beginners level 10 or below. public static final boolean USE_AUTOASSIGN_SECONDARY_CAP = true;//Prevents AP autoassign from spending on secondary stats after the player class' cap (defined on the autoassign handler) has been reached. @@ -95,8 +94,8 @@ public class ServerConstants { public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs. public static final boolean USE_ENFORCE_JOB_SP_RANGE = false; //Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved. public static final boolean USE_ENFORCE_ITEM_SUGGESTION = false;//Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured by the players. - public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = true;//Forces players to not sell CASH items via merchants. - public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues) + public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = false;//Forces players to not sell CASH items via merchants. + public static final boolean USE_ENFORCE_UNMERCHABLE_PET = false; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues) public static final boolean USE_ENFORCE_MERCHANT_SAVE = true; //Forces automatic DB save on merchant owners, at every item movement on shop. public static final boolean USE_ENFORCE_MDOOR_POSITION = false; //Forces mystic door to be spawned near spawnpoints. public static final boolean USE_SPAWN_LOOT_ON_ANIMATION = false;//Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly). diff --git a/src/constants/skills/Legend.java b/src/constants/skills/Legend.java index aabdd13d21..e67742ce66 100644 --- a/src/constants/skills/Legend.java +++ b/src/constants/skills/Legend.java @@ -36,7 +36,7 @@ public class Legend { public static final int MAKER = 20001007; public static final int BAMBOO_THRUST = 20001009; public static final int INVICIBLE_BARRIER = 20001010; - public static final int POWER_EXPLOSION = 20011011; + public static final int POWER_EXPLOSION = 20001011; public static final int METEO_SHOWER = 20001011; public static final int BLESSING_OF_THE_FAIRY = 20000012; public static final int TUTORIAL_SKILL1 = 20000014; diff --git a/src/net/PacketProcessor.java b/src/net/PacketProcessor.java index 2b8d264305..bbea92c5a3 100644 --- a/src/net/PacketProcessor.java +++ b/src/net/PacketProcessor.java @@ -241,13 +241,13 @@ public final class PacketProcessor { registerHandler(RecvOpcode.ACCEPT_FAMILY, new AcceptFamilyHandler()); registerHandler(RecvOpcode.DUEY_ACTION, new DueyHandler()); registerHandler(RecvOpcode.USE_DEATHITEM, new UseDeathItemHandler()); - //registerHandler(RecvOpcode.PLAYER_UPDATE, new PlayerUpdateHandler()); unused registerHandler(RecvOpcode.PLAYER_MAP_TRANSFER, new PlayerMapTransitionHandler()); registerHandler(RecvOpcode.USE_MAPLELIFE, new UseMapleLifeHandler()); registerHandler(RecvOpcode.USE_CATCH_ITEM, new UseCatchItemHandler()); registerHandler(RecvOpcode.MOB_DAMAGE_MOB_FRIENDLY, new MobDamageMobFriendlyHandler()); registerHandler(RecvOpcode.PARTY_SEARCH_REGISTER, new PartySearchRegisterHandler()); registerHandler(RecvOpcode.PARTY_SEARCH_START, new PartySearchStartHandler()); + registerHandler(RecvOpcode.PARTY_SEARCH_UPDATE, new PartySearchUpdateHandler()); registerHandler(RecvOpcode.ITEM_SORT2, new InventorySortHandler()); registerHandler(RecvOpcode.LEFT_KNOCKBACK, new LeftKnockbackHandler()); registerHandler(RecvOpcode.SNOWBALL, new SnowballHandler()); diff --git a/src/net/opcodes/RecvOpcode.java b/src/net/opcodes/RecvOpcode.java index 15a2278227..3d1a942f09 100644 --- a/src/net/opcodes/RecvOpcode.java +++ b/src/net/opcodes/RecvOpcode.java @@ -186,7 +186,7 @@ public enum RecvOpcode { MONSTER_CARNIVAL(0xDA), PARTY_SEARCH_REGISTER(0xDC), PARTY_SEARCH_START(0xDE), - PLAYER_UPDATE(0xDF), + PARTY_SEARCH_UPDATE(0xDF), CHECK_CASH(0xE4), CASHSHOP_OPERATION(0xE5), COUPON_CODE(0xE6), diff --git a/src/net/server/Server.java b/src/net/server/Server.java index 8aa00f8810..cd45f00249 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -80,8 +80,10 @@ import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import client.MapleClient; import client.MapleCharacter; import client.SkillFactory; +import client.command.CommandsExecutor; import client.inventory.Item; import client.inventory.ItemFactory; +import client.inventory.MaplePet; import client.inventory.manipulator.MapleCashidGenerator; import client.newyear.NewYearCardRecord; import constants.ItemConstants; @@ -838,33 +840,6 @@ public class Server { return rankSystem; } - private static void clearUnreferencedPetIds() { - PreparedStatement ps = null; - Connection con = null; - try { - con = DatabaseConnection.getConnection(); - - ps = con.prepareStatement("UPDATE inventoryitems SET petid = -1, expiration = 0 WHERE petid != -1 AND petid NOT IN (SELECT petid FROM pets)"); - ps.executeUpdate(); - - ps.close(); - con.close(); - } catch(SQLException ex) { - ex.printStackTrace(); - } finally { - try { - if(ps != null && !ps.isClosed()) { - ps.close(); - } - if(con != null && !con.isClosed()) { - con.close(); - } - } catch (SQLException e) { - e.printStackTrace(); - } - } - } - public void init() { Properties p = loadWorldINI(); if(p == null) { @@ -897,7 +872,7 @@ public class Server { sqle.printStackTrace(); } - clearUnreferencedPetIds(); + MaplePet.clearMissingPetsFromDb(); MapleCashidGenerator.loadExistentCashIdsFromDb(); IoBuffer.setUseDirectBuffer(false); @@ -971,6 +946,7 @@ public class Server { MapleSkillbookInformationProvider.getInstance(); OpcodeConstants.generateOpcodeNames(); + CommandsExecutor.getInstance(); } public static void main(String args[]) { diff --git a/src/net/server/audit/locks/MonitoredLockType.java b/src/net/server/audit/locks/MonitoredLockType.java index 50d86fcbfc..1632e91022 100644 --- a/src/net/server/audit/locks/MonitoredLockType.java +++ b/src/net/server/audit/locks/MonitoredLockType.java @@ -65,6 +65,7 @@ public enum MonitoredLockType { PARTY, WORLD_PARTY, WORLD_PARTY_SEARCH_ECHELON, + WORLD_PARTY_SEARCH_QUEUE, WORLD_PARTY_SEARCH_STORAGE, WORLD_SRVMESSAGES, WORLD_PETS, diff --git a/src/net/server/channel/handlers/DenyPartyRequestHandler.java b/src/net/server/channel/handlers/DenyPartyRequestHandler.java index 19b18d84a3..b6f12b4b71 100644 --- a/src/net/server/channel/handlers/DenyPartyRequestHandler.java +++ b/src/net/server/channel/handlers/DenyPartyRequestHandler.java @@ -35,10 +35,15 @@ public final class DenyPartyRequestHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { slea.readByte(); - MapleCharacter cfrom = c.getChannelServer().getPlayerStorage().getCharacterByName(slea.readMapleAsciiString()); + String[] cname = slea.readMapleAsciiString().split("PS: "); + + MapleCharacter cfrom = c.getChannelServer().getPlayerStorage().getCharacterByName(cname[cname.length - 1]); if (cfrom != null) { - if (MapleInviteCoordinator.answerInvite(InviteType.PARTY, c.getPlayer().getId(), cfrom.getPartyId(), false).getLeft() == InviteResult.DENIED) { - cfrom.getClient().announce(MaplePacketCreator.partyStatusMessage(23, c.getPlayer().getName())); + MapleCharacter chr = c.getPlayer(); + + if (MapleInviteCoordinator.answerInvite(InviteType.PARTY, chr.getId(), cfrom.getPartyId(), false).getLeft() == InviteResult.DENIED) { + chr.updatePartySearchAvailability(chr.getParty() == null); + cfrom.getClient().announce(MaplePacketCreator.partyStatusMessage(23, chr.getName())); } } } diff --git a/src/net/server/channel/handlers/EnterCashShopHandler.java b/src/net/server/channel/handlers/EnterCashShopHandler.java index 7d86d574b8..915bf996d2 100644 --- a/src/net/server/channel/handlers/EnterCashShopHandler.java +++ b/src/net/server/channel/handlers/EnterCashShopHandler.java @@ -61,6 +61,7 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler { } mc.closePlayerInteractions(); + mc.closePartySearchInteractions(); mc.unregisterChairBuff(); Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(mc.getId(), mc.getAllBuffs()); diff --git a/src/net/server/channel/handlers/EnterMTSHandler.java b/src/net/server/channel/handlers/EnterMTSHandler.java index 4bab7b410f..b62781026e 100644 --- a/src/net/server/channel/handlers/EnterMTSHandler.java +++ b/src/net/server/channel/handlers/EnterMTSHandler.java @@ -87,6 +87,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { } chr.closePlayerInteractions(); + chr.closePartySearchInteractions(); chr.unregisterChairBuff(); Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs()); diff --git a/src/net/server/channel/handlers/GuildOperationHandler.java b/src/net/server/channel/handlers/GuildOperationHandler.java index 7d36678730..cb1a41e189 100644 --- a/src/net/server/channel/handlers/GuildOperationHandler.java +++ b/src/net/server/channel/handlers/GuildOperationHandler.java @@ -78,7 +78,8 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler { Set eligibleMembers = new HashSet<>(MapleGuild.getEligiblePlayersForGuild(mc)); if (eligibleMembers.size() < ServerConstants.CREATE_GUILD_MIN_PARTNERS) { if (mc.getMap().getAllPlayers().size() < ServerConstants.CREATE_GUILD_MIN_PARTNERS) { - mc.dropMessage(1, "The Guild you are trying to create don't meet the minimum criteria of number of founders."); + // thanks NovaStory for noticing message in need of smoother info + mc.dropMessage(1, "Your Guild doesn't have enough cofounders present here and therefore cannot be created at this time."); } else { // players may be unaware of not belonging on a party in order to become eligible, thanks Hair (Legalize) for pointing this out mc.dropMessage(1, "Please make sure everyone you are trying to invite is neither on a guild nor on a party."); diff --git a/src/net/server/channel/handlers/PartyOperationHandler.java b/src/net/server/channel/handlers/PartyOperationHandler.java index 0583e56408..605ee0f173 100644 --- a/src/net/server/channel/handlers/PartyOperationHandler.java +++ b/src/net/server/channel/handlers/PartyOperationHandler.java @@ -56,6 +56,7 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler { List partymembers = player.getPartyMembers(); MapleParty.leaveParty(party, c); + player.updatePartySearchAvailability(true); player.partyOperationUpdate(party, partymembers); } break; diff --git a/src/net/server/channel/handlers/PartySearchRegisterHandler.java b/src/net/server/channel/handlers/PartySearchRegisterHandler.java index 905d4cbcdb..e0f3f57030 100644 --- a/src/net/server/channel/handlers/PartySearchRegisterHandler.java +++ b/src/net/server/channel/handlers/PartySearchRegisterHandler.java @@ -30,13 +30,7 @@ import client.MapleClient; * @author Quasar */ public class PartySearchRegisterHandler extends AbstractMaplePacketHandler { - public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - return; //Disabling this for now. -/* MapleCharacter chr = c.getPlayer(); - int min = slea.readInt(); - int max = slea.readInt(); - if (chr.getLevel() < min || chr.getLevel() > max || (max - min) > 30 || min > max) { // Client editing - return; - }*/ - } + + @Override + public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {} } \ No newline at end of file diff --git a/src/net/server/channel/handlers/PartySearchStartHandler.java b/src/net/server/channel/handlers/PartySearchStartHandler.java index c75f083c0e..ca7f1c53f1 100644 --- a/src/net/server/channel/handlers/PartySearchStartHandler.java +++ b/src/net/server/channel/handlers/PartySearchStartHandler.java @@ -21,108 +21,52 @@ */ package net.server.channel.handlers; -import java.util.Collection; - import net.AbstractMaplePacketHandler; import net.server.world.MapleParty; -import server.maps.MapleMap; -import server.maps.MapleMapObject; -import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; +import tools.MaplePacketCreator; import client.MapleCharacter; import client.MapleClient; -import client.MapleJob; -import constants.ServerConstants; -import net.server.world.MaplePartyCharacter; -import net.server.world.PartyOperation; import net.server.world.World; /** * * @author XoticStory * @author BubblesDev + * @author Ronan */ public class PartySearchStartHandler extends AbstractMaplePacketHandler { @Override public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - if(!ServerConstants.USE_PARTY_SEARCH){ - return; - } - - int min = slea.readInt(); - int max = slea.readInt(); - slea.readInt(); // members - int jobs = slea.readInt(); - - MapleParty party = c.getPlayer().getParty(); - if(party == null) return; - - MapleCharacter chr = c.getPlayer(); - MapleMap map = chr.getMap(); - World world = c.getWorldServer(); - - Collection mapobjs = map.getPlayers(); - - for (MapleMapObject mapobj : mapobjs) { - if (party.getMembers().size() > 5) { - break; - } - if (mapobj instanceof MapleCharacter) { - MapleCharacter tchar = (MapleCharacter) mapobj; - int charlvl = tchar.getLevel(); - if (charlvl >= min && charlvl <= max && isValidJob(tchar.getJob(), jobs)) { - if (tchar.getParty() == null) { - MaplePartyCharacter partyplayer = new MaplePartyCharacter(tchar); - tchar.getMap().addPartyMember(tchar); + int min = slea.readInt(); + int max = slea.readInt(); - world.updateParty(party.getId(), PartyOperation.JOIN, partyplayer); - tchar.receivePartyMemberHP(); - tchar.updatePartyMemberHP(); - } - } - } - } - } + MapleCharacter chr = c.getPlayer(); + if (min > max) { + chr.dropMessage(1, "The min. value is higher than the max!"); + c.announce(MaplePacketCreator.enableActions()); + return; + } - private static boolean isValidJob(MapleJob thejob, int jobs) { - int jobid = thejob.getId(); - if (jobid == 0) { - return ((jobs & 2) > 0); - } else if (jobid == 100) { - return ((jobs & 4) > 0); - } else if (jobid > 100 && jobid < 113) { - return ((jobs & 8) > 0); - } else if (jobid > 110 && jobid < 123) { - return ((jobs & 16) > 0); - } else if (jobid > 120 && jobid < 133) { - return ((jobs & 32) > 0); - } else if (jobid == 200) { - return ((jobs & 64) > 0); - } else if (jobid > 209 && jobid < 213) { - return ((jobs & 128) > 0); - } else if (jobid > 219 && jobid < 223) { - return ((jobs & 256) > 0); - } else if (jobid > 229 && jobid < 233) { - return ((jobs & 512) > 0); - } else if (jobid == 500) { - return ((jobs & 1024) > 0); - } else if (jobid > 509 && jobid < 513) { - return ((jobs & 2048) > 0); - } else if (jobid > 519 && jobid < 523) { - return ((jobs & 4096) > 0); - } else if (jobid == 400) { - return ((jobs & 8192) > 0); - } else if (jobid > 400 && jobid < 413) { - return ((jobs & 16384) > 0); - } else if (jobid > 419 && jobid < 423) { - return ((jobs & 32768) > 0); - } else if (jobid == 300) { - return ((jobs & 65536) > 0); - } else if (jobid > 300 && jobid < 313) { - return ((jobs & 131072) > 0); - } else if (jobid > 319 && jobid < 323) { - return ((jobs & 262144) > 0); - } - return false; + if (max - min > 30) { + chr.dropMessage(1, "You can only search for party members within a range of 30 levels."); + c.announce(MaplePacketCreator.enableActions()); + return; + } + + if (chr.getLevel() < min || chr.getLevel() > max) { + chr.dropMessage(1, "The range of level for search has to include your own level."); + c.announce(MaplePacketCreator.enableActions()); + return; + } + + slea.readInt(); // members + int jobs = slea.readInt(); + + MapleParty party = c.getPlayer().getParty(); + if (party == null || !c.getPlayer().isPartyLeader()) return; + + World world = c.getWorldServer(); + world.getPartySearchCoordinator().registerPartyLeader(chr, min, max, jobs); } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/PlayerUpdateHandler.java b/src/net/server/channel/handlers/PartySearchUpdateHandler.java similarity index 85% rename from src/net/server/channel/handlers/PlayerUpdateHandler.java rename to src/net/server/channel/handlers/PartySearchUpdateHandler.java index 672c2af3a6..fb448c7f3d 100644 --- a/src/net/server/channel/handlers/PlayerUpdateHandler.java +++ b/src/net/server/channel/handlers/PartySearchUpdateHandler.java @@ -25,8 +25,10 @@ import client.MapleClient; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; -public final class PlayerUpdateHandler extends AbstractMaplePacketHandler { +public final class PartySearchUpdateHandler extends AbstractMaplePacketHandler { @Override - public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {} + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + c.getWorldServer().getPartySearchCoordinator().unregisterPartyLeader(c.getPlayer()); + } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/PlayerInteractionHandler.java b/src/net/server/channel/handlers/PlayerInteractionHandler.java index 9281dd3f66..cc0d4c9ba3 100644 --- a/src/net/server/channel/handlers/PlayerInteractionHandler.java +++ b/src/net/server/channel/handlers/PlayerInteractionHandler.java @@ -608,6 +608,8 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { MaplePlayerShop shop = chr.getPlayerShop(); MapleHiredMerchant merchant = chr.getHiredMerchant(); if (shop != null && shop.isOwner(chr)) { + System.out.println(shopItem.getItem().getPet() + " " + shopItem.getItem().getPetId()); + System.out.println(ivItem.getPet() + " " + ivItem.getPetId()); if (shop.isOpen() || !shop.addItem(shopItem)) { // thanks Vcoc for pointing an exploit with unlimited shop slots c.announce(MaplePacketCreator.serverNotice(1, "You can't sell it anymore.")); return; diff --git a/src/net/server/channel/handlers/TakeDamageHandler.java b/src/net/server/channel/handlers/TakeDamageHandler.java index 17d3b65ce2..63bb21195b 100644 --- a/src/net/server/channel/handlers/TakeDamageHandler.java +++ b/src/net/server/channel/handlers/TakeDamageHandler.java @@ -107,7 +107,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler { if (damage > 0) { loseItems = attacker.getStats().loseItem(); if (loseItems != null) { - if (chr.getBuffEffect(MapleBuffStat.ARIANT_PQ_SHIELD) == null) { + if (chr.getBuffEffect(MapleBuffStat.AURA) == null) { MapleInventoryType type; final int playerpos = chr.getPosition().x; byte d = 1; diff --git a/src/net/server/coordinator/MaplePartySearchCoordinator.java b/src/net/server/coordinator/MaplePartySearchCoordinator.java new file mode 100644 index 0000000000..9bb579f314 --- /dev/null +++ b/src/net/server/coordinator/MaplePartySearchCoordinator.java @@ -0,0 +1,366 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 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 . +*/ +package net.server.coordinator; + +import client.MapleCharacter; +import client.MapleJob; +import constants.ServerConstants; +import java.io.File; +import net.server.world.MapleParty; +import net.server.coordinator.MapleInviteCoordinator.InviteType; +import net.server.coordinator.partysearch.PartySearchEchelon; +import net.server.coordinator.partysearch.PartySearchStorage; +import tools.MaplePacketCreator; +import tools.Pair; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import provider.MapleData; +import provider.MapleDataProviderFactory; +import provider.MapleDataTool; + +/** + * + * @author Ronan + */ +public class MaplePartySearchCoordinator { + + private Map storage = new HashMap<>(); + private Map upcomers = new HashMap<>(); + + private List leaderQueue = new LinkedList<>(); + private final ReentrantReadWriteLock leaderQueueLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_QUEUE, true); + private final ReadLock leaderQueueRLock = leaderQueueLock.readLock(); + private final WriteLock leaderQueueWLock = leaderQueueLock.writeLock(); + + private Map searchLeaders = new HashMap<>(); + private Map searchSettings = new HashMap<>(); + + private static Map> mapNeighbors = fetchNeighbouringMaps(); + private static Map jobTable = instantiateJobTable(); + + private static Map> fetchNeighbouringMaps() { + Map> mapLinks = new HashMap<>(); + + MapleData data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Etc.wz")).getData("MapNeighbors.img"); + if (data != null) { + for (MapleData mapdata : data.getChildren()) { + int mapid = Integer.valueOf(mapdata.getName()); + + Set neighborMaps = new HashSet<>(); + mapLinks.put(mapid, neighborMaps); + + for (MapleData neighbordata : mapdata.getChildren()) { + int neighborid = MapleDataTool.getInt(neighbordata, 999999999); + + if (neighborid != 999999999) { + neighborMaps.add(neighborid); + } + } + } + } + + return mapLinks; + } + + public static boolean isInVicinity(int callerMapid, int calleeMapid) { + Set vicinityMapids = mapNeighbors.get(calleeMapid); + + if (vicinityMapids != null) { + return vicinityMapids.contains(calleeMapid); + } else { + int callerRange = callerMapid / 10000000; + if (callerRange >= 90) { + return callerRange == (calleeMapid / 1000000); + } else { + return callerRange == (calleeMapid / 10000000); + } + } + } + + private static Map instantiateJobTable() { + Map table = new HashMap<>(); + + List> jobSearchTypes = new LinkedList>() {{ + add(new Pair<>(MapleJob.MAPLELEAF_BRIGADIER.getId(), 0)); + add(new Pair<>(0, 0)); + add(new Pair<>(MapleJob.ARAN1.getId(), 0)); + add(new Pair<>(100, 3)); + add(new Pair<>(MapleJob.DAWNWARRIOR1.getId(), 0)); + add(new Pair<>(200, 3)); + add(new Pair<>(MapleJob.BLAZEWIZARD1.getId(), 0)); + add(new Pair<>(500, 2)); + add(new Pair<>(MapleJob.THUNDERBREAKER1.getId(), 0)); + add(new Pair<>(400, 2)); + add(new Pair<>(MapleJob.NIGHTWALKER1.getId(), 0)); + add(new Pair<>(300, 2)); + add(new Pair<>(MapleJob.WINDARCHER1.getId(), 0)); + add(new Pair<>(MapleJob.EVAN1.getId(), 0)); + }}; + + int i = 0; + for (Pair p : jobSearchTypes) { + table.put(i, MapleJob.getById(p.getLeft())); + i++; + + for (int j = 1; j <= p.getRight(); j++) { + table.put(i, MapleJob.getById(p.getLeft() + 10 * j)); + i++; + } + } + + return table; + } + + private class LeaderSearchMetadata { + private int minLevel; + private int maxLevel; + private List searchedJobs; + + private int reentryCount; + + private List decodeSearchedJobs(int jobsSelected) { + List searchedJobs = new LinkedList<>(); + + int topByte = (int)((Math.log(jobsSelected) / Math.log(2)) + 1e-5); + + for (int i = 0; i <= topByte; i++) { + if (jobsSelected % 2 == 1) { + MapleJob job = jobTable.get(i); + if (job != null) { + searchedJobs.add(job); + } + } + + jobsSelected = jobsSelected >> 1; + if (jobsSelected == 0) { + break; + } + } + + return searchedJobs; + } + + private LeaderSearchMetadata(int minLevel, int maxLevel, int jobs) { + this.minLevel = minLevel; + this.maxLevel = maxLevel; + this.searchedJobs = decodeSearchedJobs(jobs); + this.reentryCount = 0; + } + + } + + public MaplePartySearchCoordinator() { + for (MapleJob job : jobTable.values()) { + storage.put(job, new PartySearchStorage()); + upcomers.put(job, new PartySearchEchelon()); + } + } + + public void attachPlayer(MapleCharacter chr) { + upcomers.get(getPartySearchJob(chr.getJob())).attachPlayer(chr); + } + + public void detachPlayer(MapleCharacter chr) { + MapleJob psJob = getPartySearchJob(chr.getJob()); + + if (!upcomers.get(psJob).detachPlayer(chr)) { + storage.get(psJob).detachPlayer(chr); + } + } + + public void updatePartySearchStorage() { + for (Entry psUpdate : upcomers.entrySet()) { + storage.get(psUpdate.getKey()).updateStorage(psUpdate.getValue().exportEchelon()); + } + } + + private static MapleJob getPartySearchJob(MapleJob job) { + if (job.getJobNiche() == 0) { + return MapleJob.BEGINNER; + } else if (job.getId() < 600) { // explorers + return MapleJob.getById((job.getId() / 10) * 10); + } else if (job.getId() >= 1000) { + return MapleJob.getById((job.getId() / 100) * 100); + } else { + return MapleJob.MAPLELEAF_BRIGADIER; + } + } + + private MapleCharacter fetchPlayer(int callerCid, int callerMapid, MapleJob job, int minLevel, int maxLevel) { + return storage.get(getPartySearchJob(job)).callPlayer(callerCid, callerMapid, minLevel, maxLevel); + } + + private void addQueueLeader(MapleCharacter leader) { + leaderQueueRLock.lock(); + try { + leaderQueue.add(leader); + } finally { + leaderQueueRLock.unlock(); + } + } + + private void removeQueueLeader(MapleCharacter leader) { + leaderQueueRLock.lock(); + try { + leaderQueue.remove(leader); + } finally { + leaderQueueRLock.unlock(); + } + } + + public void registerPartyLeader(MapleCharacter leader, int minLevel, int maxLevel, int jobs) { + if (searchLeaders.containsKey(leader.getId())) return; + + searchSettings.put(leader.getId(), new LeaderSearchMetadata(minLevel, maxLevel, jobs)); + searchLeaders.put(leader.getId(), leader); + addQueueLeader(leader); + } + + public void unregisterPartyLeader(MapleCharacter leader) { + MapleCharacter toRemove = searchLeaders.remove(leader.getId()); + if (toRemove != null) { + removeQueueLeader(toRemove); + searchSettings.remove(leader.getId()); + } + } + + private MapleCharacter searchPlayer(MapleCharacter leader) { + LeaderSearchMetadata settings = searchSettings.get(leader.getId()); + if (settings != null) { + int minLevel = settings.minLevel, maxLevel = settings.maxLevel; + Collections.shuffle(settings.searchedJobs); + + int leaderCid = leader.getId(); + int leaderMapid = leader.getMapId(); + for (MapleJob searchJob : settings.searchedJobs) { + MapleCharacter chr = fetchPlayer(leaderCid, leaderMapid, searchJob, minLevel, maxLevel); + if (chr != null) { + return chr; + } + } + } + + return null; + } + + private boolean sendPartyInviteFromSearch(MapleCharacter chr, MapleCharacter leader) { + if (chr == null) { + return false; + } + + int partyid = leader.getPartyId(); + if (partyid < 0) { + return false; + } + + if (MapleInviteCoordinator.createInvite(InviteType.PARTY, leader, partyid, chr.getId())) { + chr.disablePartySearchInvite(leader.getId()); + chr.announce(MaplePacketCreator.partySearchInvite(leader)); + return true; + } else { + return false; + } + } + + private Pair, List> fetchQueuedLeaders() { + List queuedLeaders, nextLeaders; + + leaderQueueWLock.lock(); + try { + int splitIdx = Math.min(leaderQueue.size(), 100); + + queuedLeaders = new LinkedList<>(leaderQueue.subList(0, splitIdx)); + nextLeaders = new LinkedList<>(leaderQueue.subList(splitIdx, leaderQueue.size())); + } finally { + leaderQueueWLock.unlock(); + } + + return new Pair<>(queuedLeaders, nextLeaders); + } + + public void runPartySearch() { + Pair, List> queuedLeaders = fetchQueuedLeaders(); + + List searchedLeaders = new LinkedList<>(); + List recalledLeaders = new LinkedList<>(); + List expiredLeaders = new LinkedList<>(); + + for (MapleCharacter leader : queuedLeaders.getLeft()) { + MapleCharacter chr = searchPlayer(leader); + if (sendPartyInviteFromSearch(chr, leader)) { + searchedLeaders.add(leader); + } else { + LeaderSearchMetadata settings = searchSettings.get(leader.getId()); + if (settings != null) { + if (settings.reentryCount < ServerConstants.PARTY_SEARCH_REENTRY_LIMIT) { + settings.reentryCount += 1; + recalledLeaders.add(leader); + } else { + expiredLeaders.add(leader); + } + } + } + } + + leaderQueueRLock.lock(); + try { + leaderQueue.clear(); + leaderQueue.addAll(queuedLeaders.getRight()); + + try { + leaderQueue.addAll(25, recalledLeaders); + } catch (IndexOutOfBoundsException e) { + leaderQueue.addAll(recalledLeaders); + } + } finally { + leaderQueueRLock.unlock(); + } + + for (MapleCharacter leader : searchedLeaders) { + MapleParty party = leader.getParty(); + if (party != null && party.getMembers().size() < 6) { + addQueueLeader(leader); + } else { + if (leader.isLoggedinWorld()) leader.dropMessage(5, "Your Party Search token session has finished as your party reached full capacity."); + searchLeaders.remove(leader.getId()); + searchSettings.remove(leader.getId()); + } + } + + for (MapleCharacter leader : expiredLeaders) { + if (leader.isLoggedinWorld()) leader.dropMessage(5, "Your Party Search token session expired, please stop your Party Search and retry again later."); + searchLeaders.remove(leader.getId()); + searchSettings.remove(leader.getId()); + } + } + +} diff --git a/src/net/server/coordinator/partysearch/PartySearchCharacter.java b/src/net/server/coordinator/partysearch/PartySearchCharacter.java new file mode 100644 index 0000000000..e7cdf50d07 --- /dev/null +++ b/src/net/server/coordinator/partysearch/PartySearchCharacter.java @@ -0,0 +1,79 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 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 . +*/ +package net.server.coordinator.partysearch; + +import client.MapleCharacter; +import net.server.coordinator.MaplePartySearchCoordinator; + +import java.lang.ref.WeakReference; + +/** + * + * @author Ronan + */ +public class PartySearchCharacter { + + private WeakReference player; + private int level; + private boolean queued; + + public PartySearchCharacter(MapleCharacter chr) { + player = new WeakReference(chr); + level = chr.getLevel(); + queued = true; + } + + @Override + public String toString() { + MapleCharacter chr = player.get(); + return chr == null ? "[empty]" : chr.toString(); + } + + public MapleCharacter callPlayer(int leaderid, int callerMapid) { + MapleCharacter chr = player.get(); + if (chr == null || !MaplePartySearchCoordinator.isInVicinity(callerMapid, chr.getMapId())) { + return null; + } + + if (chr.hasDisabledPartySearchInvite(leaderid)) { + return null; + } + + queued = false; + if (chr.isLoggedinWorld() && chr.getParty() == null) { + return chr; + } else { + return null; + } + } + + public MapleCharacter getPlayer() { + return player.get(); + } + + public int getLevel() { + return level; + } + + public boolean isQueued() { + return queued; + } + +} diff --git a/src/net/server/coordinator/partysearch/PartySearchEchelon.java b/src/net/server/coordinator/partysearch/PartySearchEchelon.java new file mode 100644 index 0000000000..ff0b08fd7d --- /dev/null +++ b/src/net/server/coordinator/partysearch/PartySearchEchelon.java @@ -0,0 +1,86 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 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 . +*/ +package net.server.coordinator.partysearch; + +import client.MapleCharacter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantReadWriteLock; + +import java.lang.ref.WeakReference; + +/** + * + * @author Ronan + */ +public class PartySearchEchelon { + + private final ReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_ECHELON, true); + private final ReadLock psRLock = psLock.readLock(); + private final WriteLock psWLock = psLock.writeLock(); + + private Map> echelon = new HashMap<>(20); + + public List exportEchelon() { + psWLock.lock(); // reversing read/write actually could provide a lax yet sure performance/precision trade-off here + try { + List players = new ArrayList<>(echelon.size()); + + for (WeakReference chrRef : echelon.values()) { + MapleCharacter chr = chrRef.get(); + if (chr != null) { + players.add(chr); + } + } + + echelon.clear(); + return players; + } finally { + psWLock.unlock(); + } + } + + public void attachPlayer(MapleCharacter chr) { + psRLock.lock(); + try { + echelon.put(chr.getId(), new WeakReference<>(chr)); + } finally { + psRLock.unlock(); + } + } + + public boolean detachPlayer(MapleCharacter chr) { + psRLock.lock(); + try { + return echelon.remove(chr.getId()) != null; + } finally { + psRLock.unlock(); + } + } + +} diff --git a/src/net/server/coordinator/partysearch/PartySearchStorage.java b/src/net/server/coordinator/partysearch/PartySearchStorage.java new file mode 100644 index 0000000000..a5cc871548 --- /dev/null +++ b/src/net/server/coordinator/partysearch/PartySearchStorage.java @@ -0,0 +1,258 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 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 . +*/ +package net.server.coordinator.partysearch; + +import client.MapleCharacter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantReadWriteLock; + +import java.awt.geom.Line2D; + +/** + * + * @author Ronan + */ +public class PartySearchStorage { + + private List storage = new ArrayList<>(20); + private PartySearchEmptyIntervals emptyManager = new PartySearchEmptyIntervals(); + + private class PartySearchEmptyIntervals { + + private List emptyLimits = new ArrayList<>(); + + private void refitEmptyIntervals(int st, int en, int minLevel, int maxLevel) { + List checkLimits = new ArrayList<>(emptyLimits.subList(st, en)); + + float newLimitX1, newLimitX2; + if (!checkLimits.isEmpty()) { + Line2D firstLimit = checkLimits.get(0); + Line2D lastLimit = checkLimits.get(checkLimits.size() - 1); + + newLimitX1 = (float) ((minLevel < firstLimit.getX1()) ? minLevel : firstLimit.getX1()); + newLimitX2 = (float) ((maxLevel > lastLimit.getX2()) ? maxLevel : lastLimit.getX2()); + + for (Line2D limit : checkLimits) { + emptyLimits.remove(st); + } + } else { + newLimitX1 = minLevel; + newLimitX2 = maxLevel; + } + + emptyLimits.add(st, new Line2D.Float((float) newLimitX1, 0, (float) newLimitX2, 0)); + } + + private int bsearchInterval(int level) { + int st = 0, en = emptyLimits.size() - 1; + + int mid, idx; + while (en >= st) { + idx = (st + en) / 2; + mid = (int) emptyLimits.get(idx).getX1(); + + if (mid == level) { + return idx; + } else if (mid < level) { + st = idx + 1; + } else { + en = idx - 1; + } + } + + return en; + } + + public void addEmptyInterval(int fromLevel, int toLevel) { + synchronized (emptyLimits) { // adding intervals occurs on a same-thread process, so this is actually not performance grinding + int st = bsearchInterval(fromLevel); + if (st < 0) { + st = 0; + } else if (emptyLimits.get(st).getX2() < fromLevel) { + st += 1; + } + + int en = bsearchInterval(toLevel); + if (en < st) en = st - 1; + + refitEmptyIntervals(st, en + 1, fromLevel, toLevel); + } + } + + public boolean isInEmptyInterval(int minLevel, int maxLevel) { + synchronized (emptyLimits) { + int idx = bsearchInterval(minLevel); + return idx >= 0 && maxLevel <= emptyLimits.get(idx).getX2(); + } + } + + public void clearEmptyInterval() { + synchronized (emptyLimits) { + emptyLimits.clear(); + } + } + + } + + private final ReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_STORAGE, true); + private final ReadLock psRLock = psLock.readLock(); + private final WriteLock psWLock = psLock.writeLock(); + + public List getStorageList() { + psRLock.lock(); + try { + return new ArrayList<>(storage); + } finally { + psRLock.unlock(); + } + } + + private Map fetchRemainingPlayers() { + List players = getStorageList(); + Map remainingPlayers = new HashMap<>(players.size()); + + for (PartySearchCharacter psc : players) { + if (psc.isQueued()) { + MapleCharacter chr = psc.getPlayer(); + if (chr != null) { + remainingPlayers.put(chr.getId(), chr); + } + } + } + + return remainingPlayers; + } + + public void updateStorage(Collection echelon) { + Map newcomers = new HashMap<>(); + for (MapleCharacter chr : echelon) { + newcomers.put(chr.getId(), chr); + } + + Map curStorage = fetchRemainingPlayers(); + curStorage.putAll(newcomers); + + List pscList = new ArrayList<>(curStorage.size()); + for (MapleCharacter chr : curStorage.values()) { + pscList.add(new PartySearchCharacter(chr)); + } + + Collections.sort(pscList, new Comparator() { + @Override + public int compare(PartySearchCharacter c1, PartySearchCharacter c2) + { + int levelP1 = c1.getLevel(), levelP2 = c2.getLevel(); + return levelP1 > levelP2 ? 1 : (levelP1 == levelP2 ? 0 : -1); + } + }); + + psWLock.lock(); + try { + storage.clear(); + storage.addAll(pscList); + } finally { + psWLock.unlock(); + } + + emptyManager.clearEmptyInterval(); + } + + private static int bsearchStorage(List storage, int level) { + int st = 0, en = storage.size() - 1; + + int mid, idx; + while (en >= st) { + idx = (st + en) / 2; + mid = storage.get(idx).getLevel(); + + if (mid == level) { + return idx; + } else if (mid < level) { + st = idx + 1; + } else { + en = idx - 1; + } + } + + return en; + } + + public MapleCharacter callPlayer(int callerCid, int callerMapid, int minLevel, int maxLevel) { + if (emptyManager.isInEmptyInterval(minLevel, maxLevel)) { + return null; + } + + List pscList = getStorageList(); + + int idx = bsearchStorage(pscList, maxLevel); + for (int i = idx; i >= 0; i--) { + PartySearchCharacter psc = pscList.get(i); + if (!psc.isQueued()) { + continue; + } + + if (psc.getLevel() < minLevel) { + break; + } + + MapleCharacter chr = psc.callPlayer(callerCid, callerMapid); + if (chr != null) { + return chr; + } + } + + emptyManager.addEmptyInterval(minLevel, maxLevel); + return null; + } + + public void detachPlayer(MapleCharacter chr) { + PartySearchCharacter toRemove = null; + for (PartySearchCharacter psc : getStorageList()) { + MapleCharacter player = psc.getPlayer(); + + if (player != null && player.getId() == chr.getId()) { + toRemove = psc; + break; + } + } + + if (toRemove != null) { + psWLock.lock(); + try { + storage.remove(toRemove); + } finally { + psWLock.unlock(); + } + } + } + +} diff --git a/src/net/server/worker/PartySearchWorker.java b/src/net/server/worker/PartySearchWorker.java new file mode 100644 index 0000000000..aefa89325d --- /dev/null +++ b/src/net/server/worker/PartySearchWorker.java @@ -0,0 +1,38 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 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 . +*/ +package net.server.worker; + +import net.server.world.World; + +/** + * + * @author Ronan + */ +public class PartySearchWorker extends BaseWorker implements Runnable { + + @Override + public void run() { + wserv.runPartySearchUpdateSchedule(); + } + + public PartySearchWorker(World world) { + super(world); + } +} diff --git a/src/net/server/world/MapleParty.java b/src/net/server/world/MapleParty.java index 2dec908d1c..728cfbe4a4 100644 --- a/src/net/server/world/MapleParty.java +++ b/src/net/server/world/MapleParty.java @@ -341,7 +341,8 @@ public class MapleParty { player.setMPC(partyplayer); player.getMap().addPartyMember(player); player.silentPartyUpdate(); - + + player.updatePartySearchAvailability(false); player.partyOperationUpdate(party, null); player.announce(MaplePacketCreator.partyCreated(party, partyplayer.getId())); @@ -370,7 +371,9 @@ public class MapleParty { world.updateParty(party.getId(), PartyOperation.JOIN, partyplayer); player.receivePartyMemberHP(); player.updatePartyMemberHP(); - + + player.resetPartySearchInvite(party.getLeaderId()); + player.updatePartySearchAvailability(false); player.partyOperationUpdate(party, null); return true; } else { @@ -466,7 +469,8 @@ public class MapleParty { emc.setParty(null); world.updateParty(party.getId(), PartyOperation.EXPEL, expelled); - + + emc.updatePartySearchAvailability(true); emc.partyOperationUpdate(party, partyMembers); } else { world.updateParty(party.getId(), PartyOperation.EXPEL, expelled); diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index b4fef1e337..db516bca89 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -70,6 +70,7 @@ import net.server.worker.FishingWorker; import net.server.worker.HiredMerchantWorker; import net.server.worker.MapOwnershipWorker; import net.server.worker.MountTirednessWorker; +import net.server.worker.PartySearchWorker; import net.server.worker.PetFullnessWorker; import net.server.worker.ServerMessageWorker; import net.server.worker.TimedMapObjectWorker; @@ -94,6 +95,7 @@ import net.server.coordinator.MapleInviteCoordinator; import net.server.coordinator.MapleInviteCoordinator.InviteResult; import net.server.coordinator.MapleInviteCoordinator.InviteType; import net.server.coordinator.MapleMatchCheckerCoordinator; +import net.server.coordinator.MaplePartySearchCoordinator; /** * @@ -115,6 +117,7 @@ public class World { private Map gsStore = new HashMap<>(); private PlayerStorage players = new PlayerStorage(); private MapleMatchCheckerCoordinator matchChecker = new MapleMatchCheckerCoordinator(); + private MaplePartySearchCoordinator partySearch = new MaplePartySearchCoordinator(); private final ReentrantReadWriteLock chnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_CHANNELS, true); private ReadLock chnRLock = chnLock.readLock(); @@ -170,6 +173,7 @@ public class World { private ScheduledFuture marriagesSchedule; private ScheduledFuture mapOwnershipSchedule; private ScheduledFuture fishingSchedule; + private ScheduledFuture partySearchSchedule; public World(int world, int flag, String eventmsg, int exprate, int droprate, int bossdroprate, int mesorate, int questrate, int travelrate, int fishingrate) { this.id = world; @@ -202,6 +206,7 @@ public class World { marriagesSchedule = tman.register(new WeddingReservationWorker(this), ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000, ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000); mapOwnershipSchedule = tman.register(new MapOwnershipWorker(this), 20 * 1000, 20 * 1000); fishingSchedule = tman.register(new FishingWorker(this), 10 * 1000, 10 * 1000); + partySearchSchedule = tman.register(new PartySearchWorker(this), 10 * 1000, 10 * 1000); } @@ -494,6 +499,10 @@ public class World { public MapleMatchCheckerCoordinator getMatchCheckerCoordinator() { return matchChecker; } + + public MaplePartySearchCoordinator getPartySearchCoordinator() { + return partySearch; + } public void addPlayer(MapleCharacter chr) { players.addPlayer(chr); @@ -1975,6 +1984,11 @@ public class World { } } + public void runPartySearchUpdateSchedule() { + partySearch.updatePartySearchStorage(); + partySearch.runPartySearch(); + } + private void clearWorldData() { List pList; partyLock.lock(); @@ -2061,6 +2075,11 @@ public class World { fishingSchedule = null; } + if(partySearchSchedule != null) { + partySearchSchedule.cancel(false); + partySearchSchedule = null; + } + players.disconnectAll(); players = null; diff --git a/src/server/MapleStatEffect.java b/src/server/MapleStatEffect.java index 62fcb526e4..2e8ac01bc8 100644 --- a/src/server/MapleStatEffect.java +++ b/src/server/MapleStatEffect.java @@ -146,7 +146,55 @@ public class MapleStatEffect { private Point lt, rb; private byte bulletCount, bulletConsume; private byte mapProtection; - + private CardItemupStats cardStats; + + private static class CardItemupStats { + protected int itemCode, prob; + private List> areas; + + private CardItemupStats(int code, int prob, List> areas) { + this.itemCode = code; + this.prob = prob; + this.areas = areas; + } + + private boolean isInArea(int mapid) { + if (this.areas == null) { + return true; + } + + for (Pair a : this.areas) { + if (mapid >= a.left && mapid <= a.right) { + return true; + } + } + + return false; + } + } + + public boolean isActive(int mapid) { + return cardStats == null || cardStats.isInArea(mapid); + } + + public int getCardRate(int mapid, int itemid) { + if (cardStats != null) { + if (cardStats.itemCode == Integer.MAX_VALUE) { + return cardStats.prob; + } else if (cardStats.itemCode < 1000) { + if (itemid / 10000 == cardStats.itemCode) { + return cardStats.prob; + } + } else { + if (itemid == cardStats.itemCode) { + return cardStats.prob; + } + } + } + + return 0; + } + public static MapleStatEffect loadSkillEffectFromData(MapleData source, int skillid, boolean overtime) { return loadFromData(source, skillid, true, overtime); } @@ -248,7 +296,7 @@ public class MapleStatEffect { ret.jump = (short) MapleDataTool.getInt("jump", source, 0); ret.barrier = MapleDataTool.getInt("barrier", source, 0); - addBuffStatPairToListIfNotZero(statups, MapleBuffStat.ARIANT_PQ_SHIELD, ret.barrier); + addBuffStatPairToListIfNotZero(statups, MapleBuffStat.AURA, ret.barrier); ret.mapProtection = mapProtection(sourceid); addBuffStatPairToListIfNotZero(statups, MapleBuffStat.MAP_PROTECTION, Integer.valueOf(ret.mapProtection)); @@ -303,6 +351,65 @@ public class MapleStatEffect { addBuffStatPairToListIfNotZero(statups, MapleBuffStat.COUPON_DRP3, 1); break; } + } else if (isMonsterCard(sourceid)) { + int prob = 0, itemupCode = Integer.MAX_VALUE; + List> areas = null; + + MapleData con = source.getChildByPath("con"); + if (con != null) { + areas = new ArrayList<>(3); + + for (MapleData conData : con.getChildren()) { + int startMap = MapleDataTool.getInt("sMap", conData, 0); + int endMap = MapleDataTool.getInt("eMap", conData, 0); + + areas.add(new Pair<>(startMap, endMap)); + } + } + + if (MapleDataTool.getInt("mesoupbyitem", source, 0) != 0) { + addBuffStatPairToListIfNotZero(statups, MapleBuffStat.MESO_UP_BY_ITEM, 4); + prob = MapleDataTool.getInt("prob", source, 1); + } + + int itemupType = MapleDataTool.getInt("itemupbyitem", source, 0); + if (itemupType != 0) { + addBuffStatPairToListIfNotZero(statups, MapleBuffStat.ITEM_UP_BY_ITEM, 4); + prob = MapleDataTool.getInt("prob", source, 1); + + switch (itemupType) { + case 2: + itemupCode = MapleDataTool.getInt("itemCode", source, 1); + break; + + case 3: + itemupCode = MapleDataTool.getInt("itemRange", source, 1); // 3 digits + break; + } + } + + if (MapleDataTool.getInt("respectPimmune", source, 0) != 0) { + addBuffStatPairToListIfNotZero(statups, MapleBuffStat.RESPECT_PIMMUNE, 4); + } + + if (MapleDataTool.getInt("respectMimmune", source, 0) != 0) { + addBuffStatPairToListIfNotZero(statups, MapleBuffStat.RESPECT_MIMMUNE, 4); + } + + if (MapleDataTool.getString("defenseAtt", source, null) != null) { + addBuffStatPairToListIfNotZero(statups, MapleBuffStat.DEFENSE_ATT, 4); + } + + if (MapleDataTool.getString("defenseState", source, null) != null) { + addBuffStatPairToListIfNotZero(statups, MapleBuffStat.DEFENSE_STATE, 4); + } + + int thaw = MapleDataTool.getInt("thaw", source, 0); + if (thaw != 0) { + addBuffStatPairToListIfNotZero(statups, MapleBuffStat.MAP_PROTECTION, thaw > 0 ? 1 : 2); + } + + ret.cardStats = new CardItemupStats(itemupCode, prob, areas); } else if (isExpIncrease(sourceid)) { addBuffStatPairToListIfNotZero(statups, MapleBuffStat.EXP_INCREASE, MapleDataTool.getInt("expinc", source, 0)); } @@ -1139,7 +1246,7 @@ public class MapleStatEffect { target.announce(MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), (int) leftDuration, activeStats)); } } - + private void applyBuffEffect(MapleCharacter applyfrom, MapleCharacter applyto, boolean primary) { if (!isMonsterRiding() && !isCouponBuff() && !isMysticDoor() && !isHyperBody() && !isCombo()) { // last mystic door already dispelled if it has been used before. applyto.cancelEffect(this, true, -1); @@ -1208,7 +1315,7 @@ public class MapleStatEffect { if (localstatups.size() > 0) { byte[] buff = null; byte[] mbuff = null; - if (getSummonMovementType() == null) { + if (getSummonMovementType() == null && this.isActive(applyto.getMapId())) { buff = MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), localDuration, localstatups); } if (isDash()) { @@ -1253,7 +1360,7 @@ public class MapleStatEffect { List> stat = Collections.singletonList(new Pair<>(MapleBuffStat.MORPH, Integer.valueOf(getMorph(applyto)))); mbuff = MaplePacketCreator.giveForeignBuff(applyto.getId(), stat); } else if (isAriantShield()) { - List> stat = Collections.singletonList(new Pair<>(MapleBuffStat.ARIANT_PQ_SHIELD, 1)); + List> stat = Collections.singletonList(new Pair<>(MapleBuffStat.AURA, 1)); mbuff = MaplePacketCreator.giveForeignBuff(applyto.getId(), stat); } @@ -1495,6 +1602,11 @@ public class MapleStatEffect { public static boolean isAriantShield(int sourceid) { return sourceid == 2022269; } + + public static boolean isMonsterCard(int sourceid) { + int itemType = sourceid / 10000; + return itemType == 238; + } private boolean isDs() { return skill && (sourceid == Rogue.DARK_SIGHT || sourceid == NightWalker.DARK_SIGHT); diff --git a/src/server/life/MapleMonster.java b/src/server/life/MapleMonster.java index db3e2ae569..e1492fa3b7 100644 --- a/src/server/life/MapleMonster.java +++ b/src/server/life/MapleMonster.java @@ -475,7 +475,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { return takenDamage.containsKey(chr.getId()); } - private void distributeExperienceToParty(int pid, float exp, int mostDamageCid, int minThresholdLevel, int killerLevel, Set underleveled, Map partyExpReward) { + private void distributeExperienceToParty(int pid, float exp, int mostDamageCid, int minThresholdLevel, int killerLevel, Set underleveled, Map partyExpReward, Set participants) { MapleCharacter pchar = getMap().getAnyCharacterFromParty(pid); // thanks G h o s t, Alfred, Vcoc, BHB for poiting out a bug in detecting party members after membership transactions in a party took place List members; @@ -486,7 +486,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { } List expSharers = new LinkedList<>(); - int expSharersMaxLevel = 1; + int expParticipantsMaxLevel = 1; boolean hasMostDamageCid = false; for (MapleCharacter mc : members) { if (mc.getId() == mostDamageCid) { @@ -497,8 +497,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { if (Math.abs(killerLevel - mc.getLevel()) < ServerConstants.MIN_RANGELEVEL_TO_EXP_LEECH) { // thanks Thora for pointing out leech level limitation - if (expSharersMaxLevel < mc.getLevel()) { - expSharersMaxLevel = mc.getLevel(); + if (expParticipantsMaxLevel < mc.getLevel() && participants.contains(mc)) { + expParticipantsMaxLevel = mc.getLevel(); } expSharers.add(mc); } @@ -509,14 +509,16 @@ public class MapleMonster extends AbstractLoadedMapleLife { int numExpSharers = expSharers.size(); - // PARTY BONUS: 2p -> +2% , 3p -> +4% , 4p -> +6% , 5p -> +8% , 6p -> +10% + // PARTY BONUS: 2p -> +5% , 3p -> +6.25% , 4p -> +7.5% , 5p -> +8.75% , 6p -> +10% // MOST DAMAGE BONUS: 1.5x bonus - final float partyModifier = numExpSharers <= 1 ? 0.0f : 0.02f * (numExpSharers - 1); + + // thanks Crypter for reporting an insufficiency on party exp bonuses + final float partyModifier = numExpSharers <= 1 ? 0.0f : 0.05f + (0.0125f * (numExpSharers - 1)); final float mostDamageModifier = hasMostDamageCid ? 1.5f : 1.0f; final float partyExp = exp * partyModifier * mostDamageModifier; for (MapleCharacter mc : expSharers) { - float levelPenaltyModifier = (float) Math.sqrt(((float) mc.getLevel()) / expSharersMaxLevel); + float levelPenaltyModifier = (float) Math.min(1.0, Math.sqrt(((float) mc.getLevel()) / expParticipantsMaxLevel)); partyExpReward.put(mc, partyExp * levelPenaltyModifier); } } @@ -640,9 +642,10 @@ public class MapleMonster extends AbstractLoadedMapleLife { } } + Set participants = personalExpReward.keySet(); int mostDamageCid = this.getHighestDamagerId(); for (Entry party : partyExp.entrySet()) { - distributeExperienceToParty(party.getKey(), party.getValue(), mostDamageCid, minThresholdLevel, killerLevel, underleveled, partyExpReward); + distributeExperienceToParty(party.getKey(), party.getValue(), mostDamageCid, minThresholdLevel, killerLevel, underleveled, partyExpReward, participants); } for(MapleCharacter mc : underleveled) { diff --git a/src/server/maps/MapleHiredMerchant.java b/src/server/maps/MapleHiredMerchant.java index c7c909cad1..f8e5a89f2c 100644 --- a/src/server/maps/MapleHiredMerchant.java +++ b/src/server/maps/MapleHiredMerchant.java @@ -239,6 +239,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { } private static boolean canBuy(MapleClient c, Item newItem) { + System.out.println(newItem.getPet().getName()); return MapleInventoryManipulator.checkSpace(c, newItem.getItemId(), newItem.getQuantity(), newItem.getOwner()) && MapleInventoryManipulator.addFromDrop(c, newItem, false); } diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index 990e306413..50675f9d51 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -652,7 +652,8 @@ public class MapleMap { MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); for (final MonsterDropEntry de : dropEntry) { - int dropChance = (int) Math.min((float) de.chance * chRate, Integer.MAX_VALUE); + float cardRate = chr.getCardRate(de.itemId); + int dropChance = (int) Math.min((float) de.chance * chRate * cardRate, Integer.MAX_VALUE); if (Randomizer.nextInt(999999) < dropChance) { if (droptype == 3) { @@ -2481,7 +2482,9 @@ public class MapleMap { } finally { chrWLock.unlock(); } + chr.setMapId(mapid); + chr.updateActiveEffects(); if (chrSize == 1) { if(!hasItemMonitor()) { @@ -3305,11 +3308,10 @@ public class MapleMap { player.setPosition(newPosition); try { - Collection visibleObjects = player.getVisibleMapObjects(); - MapleMapObject[] visibleObjectsNow = visibleObjects.toArray(new MapleMapObject[visibleObjects.size()]); + MapleMapObject[] visibleObjects = player.getVisibleMapObjects(); Map mapObjects = getCopyMapObjects(); - for (MapleMapObject mo : visibleObjectsNow) { + for (MapleMapObject mo : visibleObjects) { if (mo != null) { if (mapObjects.get(mo.getObjectId()) == mo) { updateMapObjectVisibility(player, mo); diff --git a/src/server/partyquest/MonsterCarnival.java b/src/server/partyquest/MonsterCarnival.java index aed6f96219..19ecefd217 100644 --- a/src/server/partyquest/MonsterCarnival.java +++ b/src/server/partyquest/MonsterCarnival.java @@ -1,6 +1,5 @@ package server.partyquest; -import java.util.List; import java.util.concurrent.ScheduledFuture; import client.MapleCharacter; import constants.LanguageConstants; @@ -10,9 +9,7 @@ import net.server.channel.Channel; import net.server.world.MapleParty; import net.server.world.MaplePartyCharacter; import server.TimerManager; -import server.life.MapleMonster; import server.maps.MapleMap; -import server.maps.MapleMapObject; import server.maps.MapleReactor; import tools.MaplePacketCreator; diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index a445e713ff..a04f087288 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -1853,36 +1853,7 @@ public class MaplePacketCreator { return mplew.getPacket(); } - /** - * Gets a packet spawning a player as a mapobject to other clients. - * - * @param target The client receiving this packet. - * @param chr The character to spawn to other clients. - * @param enteringField Whether the character to spawn is not yet present in the map or already is. - * @return The spawn player packet. - */ - public static byte[] spawnPlayerMapObject(MapleClient target, MapleCharacter chr, boolean enteringField) { - final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.SPAWN_PLAYER.getValue()); - mplew.writeInt(chr.getId()); - mplew.write(chr.getLevel()); //v83 - mplew.writeMapleAsciiString(chr.getName()); - if (chr.getGuildId() < 1) { - mplew.writeMapleAsciiString(""); - mplew.write(new byte[6]); - } else { - MapleGuildSummary gs = chr.getClient().getWorldServer().getGuildSummary(chr.getGuildId(), chr.getWorld()); - if (gs != null) { - mplew.writeMapleAsciiString(gs.getName()); - mplew.writeShort(gs.getLogoBG()); - mplew.write(gs.getLogoBGColor()); - mplew.writeShort(gs.getLogo()); - mplew.write(gs.getLogoColor()); - } else { - mplew.writeMapleAsciiString(""); - mplew.write(new byte[6]); - } - } + private static void writeForeignBuffs(MaplePacketLittleEndianWriter mplew, MapleCharacter chr) { mplew.writeInt(0); mplew.writeShort(0); //v83 mplew.write(0xFC); @@ -1962,6 +1933,40 @@ public class MaplePacketCreator { mplew.writeInt(CHAR_MAGIC_SPAWN); mplew.writeShort(0); mplew.write(0); + } + + /** + * Gets a packet spawning a player as a mapobject to other clients. + * + * @param target The client receiving this packet. + * @param chr The character to spawn to other clients. + * @param enteringField Whether the character to spawn is not yet present in the map or already is. + * @return The spawn player packet. + */ + public static byte[] spawnPlayerMapObject(MapleClient target, MapleCharacter chr, boolean enteringField) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.SPAWN_PLAYER.getValue()); + mplew.writeInt(chr.getId()); + mplew.write(chr.getLevel()); //v83 + mplew.writeMapleAsciiString(chr.getName()); + if (chr.getGuildId() < 1) { + mplew.writeMapleAsciiString(""); + mplew.write(new byte[6]); + } else { + MapleGuildSummary gs = chr.getClient().getWorldServer().getGuildSummary(chr.getGuildId(), chr.getWorld()); + if (gs != null) { + mplew.writeMapleAsciiString(gs.getName()); + mplew.writeShort(gs.getLogoBG()); + mplew.write(gs.getLogoBGColor()); + mplew.writeShort(gs.getLogo()); + mplew.write(gs.getLogoColor()); + } else { + mplew.writeMapleAsciiString(""); + mplew.write(new byte[6]); + } + } + + writeForeignBuffs(mplew, chr); mplew.writeShort(chr.getJob().getId()); @@ -3807,6 +3812,16 @@ public class MaplePacketCreator { mplew.write(0); return mplew.getPacket(); } + + public static byte[] partySearchInvite(MapleCharacter from) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.PARTY_OPERATION.getValue()); + mplew.write(4); + mplew.writeInt(from.getParty().getId()); + mplew.writeMapleAsciiString("PS: " + from.getName()); + mplew.write(0); + return mplew.getPacket(); + } /** * 10: A beginner can't create a party. 1/5/6/11/14/19: Your request for a diff --git a/tools/MapleMesoFetcher/dist/MapleMesoFetcher.jar b/tools/MapleMesoFetcher/dist/MapleMesoFetcher.jar index 91ffd84215..11bf1501b7 100644 Binary files a/tools/MapleMesoFetcher/dist/MapleMesoFetcher.jar and b/tools/MapleMesoFetcher/dist/MapleMesoFetcher.jar differ diff --git a/tools/MapleSkillbookChanceFetcher/build.xml b/tools/MapleSkillbookChanceFetcher/build.xml new file mode 100644 index 0000000000..d8592441fc --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project MapleSkillbookChanceFetcher. + + + diff --git a/tools/MapleSkillbookChanceFetcher/lib/meso_drop_data.sql b/tools/MapleSkillbookChanceFetcher/lib/meso_drop_data.sql new file mode 100644 index 0000000000..41b45f9522 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/lib/meso_drop_data.sql @@ -0,0 +1,447 @@ + # SQL File autogenerated from the MapleMesoFetcher feature by Ronan Lana. + # Generated data takes into account mob stats such as level and boss for the meso ranges. + # Only mobs with 4 or more items with no meso entry on the DB it was compiled are presented here. + + INSERT IGNORE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES +(100122, 0, 35, 52, 0, 400000), +(100123, 0, 38, 55, 0, 400000), +(100124, 0, 40, 59, 0, 400000), +(100130, 0, 31, 45, 0, 400000), +(100131, 0, 33, 49, 0, 400000), +(100132, 0, 35, 52, 0, 400000), +(100133, 0, 38, 55, 0, 400000), +(100134, 0, 40, 59, 0, 400000), +(1110130, 0, 49, 72, 0, 400000), +(1140130, 0, 56, 83, 0, 400000), +(2100100, 0, 58, 85, 0, 400000), +(2100101, 0, 60, 88, 0, 400000), +(2100106, 0, 66, 97, 0, 400000), +(2100107, 0, 73, 108, 0, 400000), +(2100108, 0, 78, 115, 0, 400000), +(2110300, 0, 66, 97, 0, 400000), +(2110301, 0, 78, 115, 0, 400000), +(2230105, 0, 64, 94, 0, 400000), +(2230107, 0, 66, 97, 0, 400000), +(2230110, 0, 64, 94, 0, 400000), +(2230111, 0, 66, 97, 0, 400000), +(2230131, 0, 66, 97, 0, 400000), +(3000005, 0, 81, 119, 0, 400000), +(3100101, 0, 86, 127, 0, 400000), +(3100102, 0, 81, 119, 0, 400000), +(3110301, 0, 86, 127, 0, 400000), +(3110302, 0, 95, 140, 0, 400000), +(3110303, 0, 105, 155, 0, 400000), +(3220000, 0, 285, 1400, 0, 400000), +(3220001, 0, 315, 1550, 0, 400000), +(3300000, 0, 81, 119, 0, 400000), +(3300001, 0, 81, 119, 0, 400000), +(3300002, 0, 83, 123, 0, 400000), +(3300003, 0, 86, 127, 0, 400000), +(3300004, 0, 89, 131, 0, 400000), +(3300005, 0, 95, 140, 0, 400000), +(3300006, 0, 95, 140, 0, 400000), +(3300007, 0, 95, 140, 0, 400000), +(3300008, 0, 315, 1550, 0, 400000), +(4110300, 0, 120, 177, 0, 400000), +(4110301, 0, 132, 195, 0, 400000), +(4130103, 0, 423, 2080, 0, 400000), +(4220000, 0, 396, 1950, 0, 400000), +(4230122, 0, 120, 177, 0, 400000), +(4230125, 0, 128, 189, 0, 400000), +(4230400, 0, 132, 195, 0, 400000), +(4230502, 0, 124, 182, 0, 400000), +(4230503, 0, 132, 195, 0, 400000), +(4230504, 0, 132, 195, 0, 400000), +(4230600, 0, 112, 165, 0, 400000), +(4240000, 0, 151, 222, 0, 400000), +(4250000, 0, 120, 177, 0, 400000), +(4250001, 0, 137, 201, 0, 400000), +(5110300, 0, 156, 230, 0, 400000), +(5120500, 0, 190, 280, 0, 400000), +(5120501, 0, 172, 254, 0, 400000), +(5120502, 0, 178, 262, 0, 400000), +(5120505, 0, 203, 299, 0, 400000), +(5120506, 0, 184, 271, 0, 400000), +(5130105, 0, 190, 280, 0, 400000), +(5130108, 0, 196, 289, 0, 400000), +(5220000, 0, 552, 2710, 0, 400000), +(5220002, 0, 468, 2300, 0, 400000), +(5220003, 0, 630, 3090, 0, 400000), +(5220004, 0, 468, 2300, 0, 400000), +(5250000, 0, 184, 271, 0, 400000), +(5250001, 0, 161, 237, 0, 400000), +(5250002, 0, 196, 289, 0, 400000), +(6110300, 0, 255, 377, 0, 400000), +(6130102, 0, 217, 320, 0, 400000), +(6130103, 0, 217, 320, 0, 400000), +(6130203, 0, 217, 320, 0, 400000), +(6130207, 0, 231, 341, 0, 400000), +(6130209, 0, 264, 389, 0, 400000), +(6220000, 0, 765, 3770, 0, 400000), +(6220001, 0, 765, 3770, 0, 400000), +(6230100, 0, 231, 341, 0, 400000), +(6230401, 0, 239, 353, 0, 400000), +(6300005, 0, 765, 3770, 0, 400000), +(6400006, 0, 384, 1890, 0, 400000), +(6400008, 0, 552, 2710, 0, 400000), +(6400009, 0, 552, 2710, 0, 400000), +(7110300, 0, 355, 524, 0, 400000), +(7110301, 0, 332, 490, 0, 400000), +(7120103, 0, 301, 444, 0, 400000), +(7120104, 0, 311, 459, 0, 400000), +(7120106, 0, 355, 524, 0, 400000), +(7120107, 0, 355, 524, 0, 400000), +(7120108, 0, 379, 559, 0, 400000), +(7120109, 0, 405, 597, 0, 400000), +(7130000, 0, 332, 490, 0, 400000), +(7130002, 0, 321, 474, 0, 400000), +(7130003, 0, 367, 541, 0, 400000), +(7130004, 0, 391, 578, 0, 400000), +(7130102, 0, 391, 578, 0, 400000), +(7130103, 0, 332, 490, 0, 400000), +(7130400, 0, 903, 4440, 0, 400000), +(7130401, 0, 903, 4440, 0, 400000), +(7130402, 0, 903, 4440, 0, 400000), +(7130601, 0, 367, 541, 0, 400000), +(7220000, 0, 933, 4590, 0, 400000), +(7220001, 0, 903, 4440, 0, 400000), +(7220002, 0, 1137, 5590, 0, 400000), +(8110300, 0, 418, 617, 0, 400000), +(8120102, 0, 446, 659, 0, 400000), +(8120103, 0, 477, 704, 0, 400000), +(8120104, 0, 509, 752, 0, 400000), +(8120105, 0, 544, 803, 0, 400000), +(8120106, 0, 562, 830, 0, 400000), +(8120107, 0, 562, 830, 0, 400000), +(8140000, 0, 418, 617, 0, 400000), +(8140100, 0, 446, 659, 0, 400000), +(8140511, 0, 581, 871, 0, 400000), +(8140512, 0, 608, 908, 0, 400000), +(8140600, 0, 594, 889, 0, 400000), +(8140702, 0, 637, 946, 0, 400000), +(8140703, 0, 666, 986, 0, 400000), +(8141300, 0, 622, 927, 0, 400000), +(8150000, 0, 2142, 10490, 0, 400000), +(8150100, 0, 714, 1049, 0, 400000), +(8150101, 0, 747, 1093, 0, 400000), +(8150200, 0, 714, 1049, 0, 400000), +(8150201, 0, 800, 1162, 0, 400000), +(8150300, 0, 666, 986, 0, 400000), +(8150301, 0, 730, 1070, 0, 400000), +(8150302, 0, 764, 1115, 0, 400000), +(8190001, 0, 800, 1162, 0, 400000), +(8220003, 0, 3381, 15830, 0, 400000), +(8220005, 0, 4350, 19860, 0, 400000), +(8220006, 0, 5466, 24400, 0, 400000), +(8220007, 0, 1704, 8530, 0, 400000), +(8220009, 0, 1479, 7280, 0, 400000), +(8830000, 0, 2400, 11620, 0, 400000), +(9001009, 0, 1254, 6170, 0, 400000), +(9001011, 0, 95, 140, 0, 400000), +(9200016, 0, 81, 119, 0, 400000), +(9200019, 0, 203, 299, 0, 400000), +(9300011, 0, 109, 160, 0, 400000), +(9300058, 0, 38, 55, 0, 400000), +(9300059, 0, 42, 61, 0, 400000), +(9300060, 0, 120, 177, 0, 400000), +(9300078, 0, 800, 1162, 0, 400000), +(9300080, 0, 282, 416, 0, 400000), +(9300096, 0, 544, 803, 0, 400000), +(9300105, 0, 717, 3530, 0, 400000), +(9300106, 0, 846, 4160, 0, 400000), +(9300127, 0, 81, 119, 0, 400000), +(9300129, 0, 81, 119, 0, 400000), +(9300131, 0, 109, 160, 0, 400000), +(9300132, 0, 81, 119, 0, 400000), +(9300133, 0, 81, 119, 0, 400000), +(9300134, 0, 81, 119, 0, 400000), +(9300136, 0, 243, 1190, 0, 400000), +(9300139, 0, 1296, 6380, 0, 400000), +(9300155, 0, 38, 55, 0, 400000), +(9300160, 0, 396, 1950, 0, 400000), +(9300161, 0, 396, 1950, 0, 400000), +(9300163, 0, 1127, 1583, 0, 400000), +(9300164, 0, 1127, 1583, 0, 400000), +(9300165, 0, 1127, 1583, 0, 400000), +(9300182, 0, 1479, 7280, 0, 400000), +(9300184, 0, 174, 850, 0, 400000), +(9300185, 0, 285, 1400, 0, 400000), +(9300186, 0, 315, 1550, 0, 400000), +(9300187, 0, 336, 1650, 0, 400000), +(9300188, 0, 468, 2300, 0, 400000), +(9300189, 0, 468, 2300, 0, 400000), +(9300190, 0, 552, 2710, 0, 400000), +(9300191, 0, 651, 3200, 0, 400000), +(9300192, 0, 570, 2800, 0, 400000), +(9300193, 0, 630, 3090, 0, 400000), +(9300194, 0, 765, 3770, 0, 400000), +(9300195, 0, 765, 3770, 0, 400000), +(9300196, 0, 765, 3770, 0, 400000), +(9300197, 0, 765, 3770, 0, 400000), +(9300198, 0, 651, 3200, 0, 400000), +(9300199, 0, 903, 4440, 0, 400000), +(9300200, 0, 933, 4590, 0, 400000), +(9300201, 0, 1479, 7280, 0, 400000), +(9300202, 0, 1137, 5590, 0, 400000), +(9300203, 0, 1254, 6170, 0, 400000), +(9300204, 0, 1383, 6810, 0, 400000), +(9300205, 0, 1296, 6380, 0, 400000), +(9300206, 0, 1479, 7280, 0, 400000), +(9300207, 0, 1479, 7280, 0, 400000), +(9300208, 0, 1704, 8530, 0, 400000), +(9300209, 0, 1704, 8530, 0, 400000), +(9300210, 0, 2142, 10490, 0, 400000), +(9300211, 0, 2400, 11620, 0, 400000), +(9300212, 0, 2400, 11620, 0, 400000), +(9300213, 0, 3381, 15830, 0, 400000), +(9300214, 0, 3792, 17550, 0, 400000), +(9300215, 0, 6714, 29370, 0, 400000), +(9300217, 0, 32, 47, 0, 400000), +(9300218, 0, 34, 50, 0, 400000), +(9300219, 0, 34, 50, 0, 400000), +(9300220, 0, 52, 77, 0, 400000), +(9300221, 0, 68, 101, 0, 400000), +(9300222, 0, 75, 111, 0, 400000), +(9300223, 0, 36, 54, 0, 400000), +(9300224, 0, 102, 150, 0, 400000), +(9300225, 0, 102, 150, 0, 400000), +(9300226, 0, 112, 165, 0, 400000), +(9300227, 0, 102, 150, 0, 400000), +(9300228, 0, 146, 215, 0, 400000), +(9300229, 0, 39, 57, 0, 400000), +(9300230, 0, 116, 171, 0, 400000), +(9300231, 0, 137, 201, 0, 400000), +(9300232, 0, 92, 136, 0, 400000), +(9300233, 0, 112, 165, 0, 400000), +(9300234, 0, 86, 127, 0, 400000), +(9300235, 0, 167, 245, 0, 400000), +(9300236, 0, 190, 280, 0, 400000), +(9300237, 0, 210, 309, 0, 400000), +(9300238, 0, 66, 97, 0, 400000), +(9300239, 0, 120, 177, 0, 400000), +(9300240, 0, 132, 195, 0, 400000), +(9300241, 0, 282, 416, 0, 400000), +(9300242, 0, 301, 444, 0, 400000), +(9300243, 0, 190, 280, 0, 400000), +(9300244, 0, 190, 280, 0, 400000), +(9300245, 0, 217, 320, 0, 400000), +(9300246, 0, 231, 341, 0, 400000), +(9300247, 0, 255, 377, 0, 400000), +(9300248, 0, 264, 389, 0, 400000), +(9300249, 0, 301, 444, 0, 400000), +(9300250, 0, 355, 524, 0, 400000), +(9300251, 0, 332, 490, 0, 400000), +(9300252, 0, 132, 195, 0, 400000), +(9300253, 0, 156, 230, 0, 400000), +(9300254, 0, 332, 490, 0, 400000), +(9300255, 0, 141, 208, 0, 400000), +(9300256, 0, 217, 320, 0, 400000), +(9300257, 0, 217, 320, 0, 400000), +(9300258, 0, 255, 377, 0, 400000), +(9300259, 0, 58, 85, 0, 400000), +(9300260, 0, 418, 617, 0, 400000), +(9300261, 0, 544, 803, 0, 400000), +(9300262, 0, 544, 803, 0, 400000), +(9300263, 0, 544, 803, 0, 400000), +(9300264, 0, 764, 1115, 0, 400000), +(9300265, 0, 730, 1070, 0, 400000), +(9300266, 0, 933, 4590, 0, 400000), +(9300267, 0, 1254, 6170, 0, 400000), +(9300268, 0, 933, 4590, 0, 400000), +(9300269, 0, 174, 850, 0, 400000), +(9300270, 0, 418, 617, 0, 400000), +(9300274, 0, 39, 57, 0, 400000), +(9300289, 0, 1704, 8530, 0, 400000), +(9300294, 0, 2142, 10490, 0, 400000), +(9300315, 0, 483, 2370, 0, 400000), +(9300316, 0, 516, 2540, 0, 400000), +(9300317, 0, 552, 2710, 0, 400000), +(9300318, 0, 588, 2890, 0, 400000), +(9300319, 0, 630, 3090, 0, 400000), +(9300320, 0, 672, 3300, 0, 400000), +(9300321, 0, 717, 3530, 0, 400000), +(9300322, 0, 765, 3770, 0, 400000), +(9300332, 0, 112, 165, 0, 400000), +(9300334, 0, 151, 222, 0, 400000), +(9300335, 0, 116, 171, 0, 400000), +(9300336, 0, 137, 201, 0, 400000), +(9300337, 0, 137, 201, 0, 400000), +(9300367, 0, 126, 610, 0, 400000), +(9300368, 0, 174, 850, 0, 400000), +(9300369, 0, 243, 1190, 0, 400000), +(9300370, 0, 336, 1650, 0, 400000), +(9300371, 0, 468, 2300, 0, 400000), +(9300372, 0, 651, 3200, 0, 400000), +(9300373, 0, 903, 4440, 0, 400000), +(9300374, 0, 1254, 6170, 0, 400000), +(9300375, 0, 1704, 8530, 0, 400000), +(9300376, 0, 1704, 8530, 0, 400000), +(9300377, 0, 2691, 12890, 0, 400000), +(9303000, 0, 42, 61, 0, 400000), +(9303001, 0, 42, 61, 0, 400000), +(9303003, 0, 42, 61, 0, 400000), +(9303004, 0, 42, 61, 0, 400000), +(9303005, 0, 71, 104, 0, 400000), +(9303006, 0, 71, 104, 0, 400000), +(9303007, 0, 71, 104, 0, 400000), +(9303008, 0, 71, 104, 0, 400000), +(9303009, 0, 161, 237, 0, 400000), +(9303010, 0, 161, 237, 0, 400000), +(9303011, 0, 161, 237, 0, 400000), +(9303013, 0, 432, 638, 0, 400000), +(9303014, 0, 432, 638, 0, 400000), +(9303016, 0, 432, 638, 0, 400000), +(9400009, 0, 7014, 8225, 0, 400000), +(9400012, 0, 217, 320, 0, 400000), +(9400120, 0, 1911, 9460, 0, 400000), +(9400122, 0, 1911, 9460, 0, 400000), +(9400200, 0, 184, 271, 0, 400000), +(9400203, 0, 112, 165, 0, 400000), +(9400205, 0, 1704, 8530, 0, 400000), +(9400238, 0, 81, 119, 0, 400000), +(9400239, 0, 66, 97, 0, 400000), +(9400241, 0, 38, 55, 0, 400000), +(9400242, 0, 42, 61, 0, 400000), +(9400243, 0, 184, 271, 0, 400000), +(9400244, 0, 210, 309, 0, 400000), +(9400245, 0, 66, 97, 0, 400000), +(9400246, 0, 62, 91, 0, 400000), +(9400247, 0, 81, 119, 0, 400000), +(9400248, 0, 66, 97, 0, 400000), +(9400500, 0, 93, 450, 0, 400000), +(9400501, 0, 35, 52, 0, 400000), +(9400502, 0, 105, 520, 0, 400000), +(9400503, 0, 105, 520, 0, 400000), +(9400504, 0, 31, 45, 0, 400000), +(9400538, 0, 56, 83, 0, 400000), +(9400539, 0, 60, 88, 0, 400000), +(9400540, 0, 68, 101, 0, 400000), +(9400541, 0, 68, 101, 0, 400000), +(9400542, 0, 98, 145, 0, 400000), +(9400543, 0, 116, 171, 0, 400000), +(9400544, 0, 156, 230, 0, 400000), +(9400546, 0, 128, 189, 0, 400000), +(9400547, 0, 73, 108, 0, 400000), +(9400548, 0, 81, 119, 0, 400000), +(9400550, 0, 73, 108, 0, 400000), +(9400556, 0, 60, 88, 0, 400000), +(9400558, 0, 81, 119, 0, 400000), +(9400560, 0, 156, 230, 0, 400000), +(9400561, 0, 217, 320, 0, 400000), +(9400562, 0, 217, 320, 0, 400000), +(9400563, 0, 112, 165, 0, 400000), +(9400565, 0, 60, 88, 0, 400000), +(9400570, 0, 49, 72, 0, 400000), +(9400571, 0, 468, 2300, 0, 400000), +(9400573, 0, 112, 165, 0, 400000), +(9400574, 0, 714, 1049, 0, 400000), +(9400576, 0, 301, 444, 0, 400000), +(9400578, 0, 568, 853, 0, 400000), +(9400579, 0, 714, 1049, 0, 400000), +(9400580, 0, 637, 946, 0, 400000), +(9400581, 0, 418, 617, 0, 400000), +(9400582, 0, 1127, 1583, 0, 400000), +(9400609, 0, 204, 1010, 0, 400000), +(9400612, 0, 204, 1010, 0, 400000), +(9400633, 0, 258, 1270, 0, 400000), +(9400644, 0, 42, 61, 0, 400000), +(9410014, 0, 493, 728, 0, 400000), +(9410015, 0, 1479, 7280, 0, 400000), +(9420507, 0, 146, 215, 0, 400000), +(9420527, 0, 132, 195, 0, 400000), +(9420528, 0, 141, 208, 0, 400000), +(9420529, 0, 167, 245, 0, 400000), +(9420530, 0, 190, 280, 0, 400000), +(9420531, 0, 210, 309, 0, 400000), +(9420532, 0, 210, 309, 0, 400000), +(9420533, 0, 224, 330, 0, 400000), +(9420534, 0, 255, 377, 0, 400000), +(9420535, 0, 282, 416, 0, 400000), +(9420536, 0, 321, 474, 0, 400000), +(9420537, 0, 355, 524, 0, 400000), +(9420538, 0, 446, 659, 0, 400000), +(9420539, 0, 526, 777, 0, 400000), +(9420545, 0, 210, 309, 0, 400000), +(9420550, 0, 210, 309, 0, 400000), +(9500101, 0, 38, 55, 0, 400000), +(9500102, 0, 39, 57, 0, 400000), +(9500103, 0, 49, 72, 0, 400000), +(9500104, 0, 44, 65, 0, 400000), +(9500105, 0, 49, 72, 0, 400000), +(9500106, 0, 62, 91, 0, 400000), +(9500107, 0, 81, 119, 0, 400000), +(9500108, 0, 86, 127, 0, 400000), +(9500109, 0, 86, 127, 0, 400000), +(9500110, 0, 95, 140, 0, 400000), +(9500111, 0, 95, 140, 0, 400000), +(9500112, 0, 95, 140, 0, 400000), +(9500113, 0, 98, 145, 0, 400000), +(9500115, 0, 102, 150, 0, 400000), +(9500116, 0, 112, 165, 0, 400000), +(9500117, 0, 98, 145, 0, 400000), +(9500118, 0, 109, 160, 0, 400000), +(9500119, 0, 109, 160, 0, 400000), +(9500120, 0, 132, 195, 0, 400000), +(9500121, 0, 146, 215, 0, 400000), +(9500122, 0, 151, 222, 0, 400000), +(9500123, 0, 210, 309, 0, 400000), +(9500124, 0, 651, 3200, 0, 400000), +(9500125, 0, 217, 320, 0, 400000), +(9500126, 0, 247, 365, 0, 400000), +(9500127, 0, 273, 402, 0, 400000), +(9500128, 0, 282, 416, 0, 400000), +(9500129, 0, 301, 444, 0, 400000), +(9500130, 0, 903, 4440, 0, 400000), +(9500131, 0, 332, 490, 0, 400000), +(9500132, 0, 355, 524, 0, 400000), +(9500134, 0, 418, 617, 0, 400000), +(9500135, 0, 493, 728, 0, 400000), +(9500136, 0, 682, 1006, 0, 400000), +(9500137, 0, 637, 946, 0, 400000), +(9500138, 0, 568, 853, 0, 400000), +(9500139, 0, 1254, 6170, 0, 400000), +(9500140, 0, 2142, 10490, 0, 400000), +(9500156, 0, 146, 215, 0, 400000), +(9500157, 0, 95, 140, 0, 400000), +(9500158, 0, 903, 4440, 0, 400000), +(9500159, 0, 903, 4440, 0, 400000), +(9500160, 0, 903, 4440, 0, 400000), +(9500161, 0, 418, 617, 0, 400000), +(9500162, 0, 418, 617, 0, 400000), +(9500163, 0, 461, 681, 0, 400000), +(9500164, 0, 544, 803, 0, 400000), +(9500165, 0, 544, 803, 0, 400000), +(9500166, 0, 544, 803, 0, 400000), +(9500178, 0, 112, 165, 0, 400000), +(9500180, 0, 1704, 8530, 0, 400000), +(9500181, 0, 1704, 8530, 0, 400000), +(9500306, 0, 174, 850, 0, 400000), +(9500307, 0, 285, 1400, 0, 400000), +(9500308, 0, 468, 2300, 0, 400000), +(9500309, 0, 552, 2710, 0, 400000), +(9500310, 0, 630, 3090, 0, 400000), +(9500311, 0, 765, 3770, 0, 400000), +(9500312, 0, 903, 4440, 0, 400000), +(9500313, 0, 933, 4590, 0, 400000), +(9500314, 0, 1137, 5590, 0, 400000), +(9500317, 0, 126, 610, 0, 400000), +(9500318, 0, 336, 1650, 0, 400000), +(9500319, 0, 903, 4440, 0, 400000), +(9500321, 0, 42, 61, 0, 400000), +(9500326, 0, 396, 1950, 0, 400000), +(9500327, 0, 243, 1190, 0, 400000), +(9500328, 0, 285, 1400, 0, 400000), +(9500331, 0, 552, 2710, 0, 400000), +(9500332, 0, 396, 1950, 0, 400000), +(9500333, 0, 468, 2300, 0, 400000), +(9500334, 0, 552, 2710, 0, 400000), +(9500335, 0, 468, 2300, 0, 400000), +(9500366, 0, 49, 72, 0, 400000), +(9500367, 0, 49, 72, 0, 400000), +(9500368, 0, 49, 72, 0, 400000), +(9500369, 0, 49, 72, 0, 400000), +(9500370, 0, 49, 72, 0, 400000), +(9500371, 0, 49, 72, 0, 400000), +(9500372, 0, 49, 72, 0, 400000); + + DELETE FROM drop_data WHERE dropperid >= 9300184 AND dropperid <= 9300215 AND itemid = 0; diff --git a/tools/MapleSkillbookChanceFetcher/lib/mysql-connector-java-bin.jar b/tools/MapleSkillbookChanceFetcher/lib/mysql-connector-java-bin.jar new file mode 100644 index 0000000000..0539039f71 Binary files /dev/null and b/tools/MapleSkillbookChanceFetcher/lib/mysql-connector-java-bin.jar differ diff --git a/tools/MapleSkillbookChanceFetcher/lib/skillbook_drop_data.sql b/tools/MapleSkillbookChanceFetcher/lib/skillbook_drop_data.sql new file mode 100644 index 0000000000..8dc97ded65 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/lib/skillbook_drop_data.sql @@ -0,0 +1,619 @@ + # SQL File autogenerated from the MapleSkillbookChanceFetcher feature by Ronan Lana. + # Generated data takes into account mob stats such as level and boss for the chance rates. + + REPLACE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES +(851000, 2290132, 1, 1, 0, 3861), +(7090000, 2290087, 1, 1, 0, 40000), +(8090000, 2290045, 1, 1, 0, 40000), +(8140103, 2290044, 1, 1, 0, 2000), +(8140511, 2290009, 1, 1, 0, 2000), +(8140511, 2290050, 1, 1, 0, 2000), +(8140511, 2290083, 1, 1, 0, 2000), +(8140511, 2290134, 1, 1, 0, 6000), +(8140512, 2290013, 1, 1, 0, 2000), +(8140512, 2290067, 1, 1, 0, 2000), +(8140512, 2290082, 1, 1, 0, 2000), +(8140512, 2290097, 1, 1, 0, 2000), +(8140512, 2290116, 1, 1, 0, 2000), +(8140512, 2290131, 1, 1, 0, 6000), +(8140600, 2290132, 1, 1, 0, 6000), +(8140700, 2290106, 1, 1, 0, 2000), +(8140700, 2290126, 1, 1, 0, 6000), +(8140701, 2290122, 1, 1, 0, 2000), +(8140702, 2290112, 1, 1, 0, 2000), +(8140703, 2290088, 1, 1, 0, 2000), +(8140703, 2290099, 1, 1, 0, 2000), +(8141000, 2290082, 1, 1, 0, 2000), +(8141000, 2290097, 1, 1, 0, 2000), +(8141100, 2280005, 1, 1, 0, 2000), +(8141300, 2290098, 1, 1, 0, 2000), +(8142100, 2290032, 1, 1, 0, 2000), +(8142100, 2290082, 1, 1, 0, 2000), +(8142100, 2290114, 1, 1, 0, 2000), +(8143000, 2280004, 1, 1, 0, 2000), +(8150000, 2280013, 1, 1, 0, 120000), +(8150000, 2290070, 1, 1, 0, 40000), +(8150000, 2290091, 1, 1, 0, 40000), +(8150100, 2290042, 1, 1, 0, 2000), +(8150100, 2290053, 1, 1, 0, 2000), +(8150100, 2290073, 1, 1, 0, 2000), +(8150100, 2290102, 1, 1, 0, 2000), +(8150100, 2290118, 1, 1, 0, 2000), +(8150101, 2290017, 1, 1, 0, 2000), +(8150101, 2290021, 1, 1, 0, 2000), +(8150101, 2290035, 1, 1, 0, 2000), +(8150101, 2290042, 1, 1, 0, 2000), +(8150101, 2290052, 1, 1, 0, 2000), +(8150101, 2290102, 1, 1, 0, 2000), +(8150200, 2290024, 1, 1, 0, 2000), +(8150200, 2290100, 1, 1, 0, 2000), +(8150200, 2290135, 1, 1, 0, 6000), +(8150201, 2290004, 1, 1, 0, 2000), +(8150201, 2290006, 1, 1, 0, 2000), +(8150201, 2290024, 1, 1, 0, 2000), +(8150201, 2290036, 1, 1, 0, 2000), +(8150201, 2290056, 1, 1, 0, 2000), +(8150201, 2290072, 1, 1, 0, 2000), +(8150201, 2290078, 1, 1, 0, 2000), +(8150201, 2290117, 1, 1, 0, 2000), +(8150300, 2290003, 1, 1, 0, 2000), +(8150300, 2290033, 1, 1, 0, 2000), +(8150300, 2290111, 1, 1, 0, 2000), +(8150300, 2290120, 1, 1, 0, 2000), +(8150300, 2290127, 1, 1, 0, 6000), +(8150301, 2290023, 1, 1, 0, 2000), +(8150301, 2290029, 1, 1, 0, 2000), +(8150301, 2290101, 1, 1, 0, 2000), +(8150301, 2290107, 1, 1, 0, 2000), +(8150302, 2290010, 1, 1, 0, 2000), +(8150302, 2290019, 1, 1, 0, 2000), +(8150302, 2290026, 1, 1, 0, 2000), +(8150302, 2290076, 1, 1, 0, 2000), +(8150302, 2290085, 1, 1, 0, 2000), +(8150302, 2290096, 1, 1, 0, 2000), +(8150302, 2290113, 1, 1, 0, 2000), +(8150302, 2290119, 1, 1, 0, 2000), +(8150302, 2290128, 1, 1, 0, 6000), +(8160000, 2290017, 1, 1, 0, 2000), +(8160000, 2290045, 1, 1, 0, 2000), +(8160000, 2290065, 1, 1, 0, 2000), +(8160000, 2290067, 1, 1, 0, 2000), +(8160000, 2290081, 1, 1, 0, 2000), +(8170000, 2290012, 1, 1, 0, 2000), +(8170000, 2290086, 1, 1, 0, 2000), +(8170000, 2290087, 1, 1, 0, 2000), +(8170000, 2290134, 1, 1, 0, 6000), +(8180000, 2290002, 1, 1, 0, 40000), +(8180000, 2290003, 1, 1, 0, 40000), +(8180000, 2290014, 1, 1, 0, 40000), +(8180000, 2290015, 1, 1, 0, 40000), +(8180000, 2290030, 1, 1, 0, 40000), +(8180000, 2290035, 1, 1, 0, 40000), +(8180000, 2290036, 1, 1, 0, 40000), +(8180000, 2290063, 1, 1, 0, 40000), +(8180000, 2290080, 1, 1, 0, 40000), +(8180000, 2290098, 1, 1, 0, 40000), +(8180000, 2290101, 1, 1, 0, 40000), +(8180000, 2290117, 1, 1, 0, 40000), +(8180000, 2290130, 1, 1, 0, 120000), +(8180001, 2290018, 1, 1, 0, 40000), +(8180001, 2290019, 1, 1, 0, 40000), +(8180001, 2290032, 1, 1, 0, 40000), +(8180001, 2290042, 1, 1, 0, 40000), +(8180001, 2290058, 1, 1, 0, 40000), +(8180001, 2290059, 1, 1, 0, 40000), +(8180001, 2290068, 1, 1, 0, 40000), +(8180001, 2290069, 1, 1, 0, 40000), +(8180001, 2290072, 1, 1, 0, 40000), +(8180001, 2290092, 1, 1, 0, 40000), +(8180001, 2290099, 1, 1, 0, 40000), +(8180001, 2290100, 1, 1, 0, 40000), +(8180001, 2290102, 1, 1, 0, 40000), +(8180001, 2290119, 1, 1, 0, 40000), +(8180001, 2290128, 1, 1, 0, 120000), +(8190000, 2280016, 1, 1, 0, 6000), +(8190000, 2290030, 1, 1, 0, 2000), +(8190000, 2290044, 1, 1, 0, 2000), +(8190000, 2290054, 1, 1, 0, 2000), +(8190000, 2290066, 1, 1, 0, 2000), +(8190000, 2290075, 1, 1, 0, 2000), +(8190000, 2290092, 1, 1, 0, 2000), +(8190000, 2290103, 1, 1, 0, 2000), +(8190002, 2290000, 1, 1, 0, 2000), +(8190002, 2290008, 1, 1, 0, 2000), +(8190002, 2290018, 1, 1, 0, 2000), +(8190002, 2290038, 1, 1, 0, 2000), +(8190002, 2290060, 1, 1, 0, 2000), +(8190002, 2290080, 1, 1, 0, 2000), +(8190002, 2290124, 1, 1, 0, 2000), +(8190003, 2280013, 1, 1, 0, 6000), +(8190003, 2290007, 1, 1, 0, 2000), +(8190003, 2290012, 1, 1, 0, 2000), +(8190003, 2290014, 1, 1, 0, 2000), +(8190003, 2290033, 1, 1, 0, 2000), +(8190003, 2290045, 1, 1, 0, 2000), +(8190003, 2290050, 1, 1, 0, 2000), +(8190003, 2290055, 1, 1, 0, 2000), +(8190003, 2290062, 1, 1, 0, 2000), +(8190003, 2290063, 1, 1, 0, 2000), +(8190003, 2290070, 1, 1, 0, 2000), +(8190003, 2290086, 1, 1, 0, 2000), +(8190003, 2290108, 1, 1, 0, 2000), +(8190003, 2290133, 1, 1, 0, 6000), +(8190004, 2290002, 1, 1, 0, 2000), +(8190004, 2290009, 1, 1, 0, 2000), +(8190004, 2290021, 1, 1, 0, 2000), +(8190004, 2290034, 1, 1, 0, 2000), +(8190004, 2290041, 1, 1, 0, 2000), +(8190004, 2290052, 1, 1, 0, 2000), +(8190004, 2290053, 1, 1, 0, 2000), +(8190004, 2290058, 1, 1, 0, 2000), +(8190004, 2290068, 1, 1, 0, 2000), +(8190004, 2290071, 1, 1, 0, 2000), +(8190004, 2290073, 1, 1, 0, 2000), +(8190004, 2290090, 1, 1, 0, 2000), +(8190004, 2290112, 1, 1, 0, 2000), +(8190004, 2290121, 1, 1, 0, 2000), +(8190004, 2290130, 1, 1, 0, 6000), +(8190005, 2290000, 1, 1, 0, 2000), +(8190005, 2290008, 1, 1, 0, 2000), +(8190005, 2290018, 1, 1, 0, 2000), +(8190005, 2290038, 1, 1, 0, 2000), +(8190005, 2290060, 1, 1, 0, 2000), +(8190005, 2290080, 1, 1, 0, 2000), +(8190005, 2290124, 1, 1, 0, 2000), +(8200000, 2290005, 1, 1, 0, 2000), +(8200000, 2290011, 1, 1, 0, 2000), +(8200000, 2290114, 1, 1, 0, 2000), +(8200001, 2280015, 1, 1, 0, 6000), +(8200001, 2290050, 1, 1, 0, 2000), +(8200001, 2290059, 1, 1, 0, 2000), +(8200001, 2290065, 1, 1, 0, 2000), +(8200001, 2290129, 1, 1, 0, 6000), +(8200002, 2290062, 1, 1, 0, 2000), +(8200002, 2290066, 1, 1, 0, 2000), +(8200002, 2290070, 1, 1, 0, 2000), +(8200002, 2290131, 1, 1, 0, 6000), +(8200002, 2290139, 1, 1, 0, 6000), +(8200003, 2290012, 1, 1, 0, 2000), +(8200003, 2290056, 1, 1, 0, 2000), +(8200003, 2290071, 1, 1, 0, 2000), +(8200003, 2290101, 1, 1, 0, 2000), +(8200003, 2290136, 1, 1, 0, 6000), +(8200004, 2280016, 1, 1, 0, 6000), +(8200004, 2290069, 1, 1, 0, 2000), +(8200004, 2290072, 1, 1, 0, 2000), +(8200004, 2290073, 1, 1, 0, 2000), +(8200004, 2290127, 1, 1, 0, 6000), +(8200004, 2290134, 1, 1, 0, 6000), +(8200005, 2280014, 1, 1, 0, 6000), +(8200005, 2290078, 1, 1, 0, 2000), +(8200005, 2290079, 1, 1, 0, 2000), +(8200005, 2290095, 1, 1, 0, 2000), +(8200006, 2290003, 1, 1, 0, 2000), +(8200006, 2290064, 1, 1, 0, 2000), +(8200006, 2290076, 1, 1, 0, 2000), +(8200006, 2290077, 1, 1, 0, 2000), +(8200006, 2290129, 1, 1, 0, 6000), +(8200006, 2290138, 1, 1, 0, 6000), +(8200007, 2290006, 1, 1, 0, 2000), +(8200007, 2290007, 1, 1, 0, 2000), +(8200007, 2290011, 1, 1, 0, 2000), +(8200007, 2290016, 1, 1, 0, 2000), +(8200007, 2290125, 1, 1, 0, 2000), +(8200007, 2290136, 1, 1, 0, 6000), +(8200008, 2290006, 1, 1, 0, 2000), +(8200008, 2290051, 1, 1, 0, 2000), +(8200008, 2290121, 1, 1, 0, 2000), +(8200008, 2290122, 1, 1, 0, 2000), +(8200008, 2290133, 1, 1, 0, 6000), +(8200009, 2290013, 1, 1, 0, 2000), +(8200009, 2290016, 1, 1, 0, 2000), +(8200009, 2290031, 1, 1, 0, 2000), +(8200009, 2290039, 1, 1, 0, 2000), +(8200010, 2290026, 1, 1, 0, 2000), +(8200010, 2290059, 1, 1, 0, 2000), +(8200010, 2290088, 1, 1, 0, 2000), +(8200010, 2290089, 1, 1, 0, 2000), +(8200010, 2290127, 1, 1, 0, 6000), +(8200011, 2290001, 1, 1, 0, 3000), +(8200011, 2290040, 1, 1, 0, 3000), +(8200011, 2290046, 1, 1, 0, 3000), +(8200011, 2290048, 1, 1, 0, 3000), +(8200011, 2290049, 1, 1, 0, 3000), +(8200011, 2290114, 1, 1, 0, 3000), +(8200011, 2290137, 1, 1, 0, 9000), +(8200012, 2290041, 1, 1, 0, 3000), +(8200012, 2290092, 1, 1, 0, 3000), +(8200012, 2290093, 1, 1, 0, 3000), +(8200012, 2290115, 1, 1, 0, 3000), +(8200012, 2290137, 1, 1, 0, 9000), +(8200012, 2290139, 1, 1, 0, 9000), +(8220002, 2290020, 1, 1, 0, 40000), +(8220002, 2290081, 1, 1, 0, 40000), +(8220002, 2290085, 1, 1, 0, 40000), +(8220002, 2290133, 1, 1, 0, 120000), +(8220003, 2290006, 1, 1, 0, 40000), +(8220003, 2290030, 1, 1, 0, 40000), +(8220003, 2290031, 1, 1, 0, 40000), +(8220003, 2290032, 1, 1, 0, 40000), +(8220003, 2290033, 1, 1, 0, 40000), +(8220003, 2290060, 1, 1, 0, 40000), +(8220003, 2290061, 1, 1, 0, 40000), +(8220003, 2290076, 1, 1, 0, 40000), +(8220003, 2290077, 1, 1, 0, 40000), +(8220003, 2290104, 1, 1, 0, 40000), +(8220003, 2290105, 1, 1, 0, 40000), +(8220003, 2290117, 1, 1, 0, 40000), +(8220003, 2290118, 1, 1, 0, 40000), +(8220004, 2290018, 1, 1, 0, 40000), +(8220004, 2290019, 1, 1, 0, 40000), +(8220004, 2290024, 1, 1, 0, 40000), +(8220004, 2290025, 1, 1, 0, 40000), +(8220004, 2290058, 1, 1, 0, 40000), +(8220004, 2290059, 1, 1, 0, 40000), +(8220004, 2290076, 1, 1, 0, 40000), +(8220004, 2290077, 1, 1, 0, 40000), +(8220004, 2290106, 1, 1, 0, 40000), +(8220004, 2290127, 1, 1, 0, 120000), +(8220004, 2290134, 1, 1, 0, 120000), +(8220005, 2290002, 1, 1, 0, 60000), +(8220005, 2290003, 1, 1, 0, 60000), +(8220005, 2290036, 1, 1, 0, 60000), +(8220005, 2290037, 1, 1, 0, 60000), +(8220005, 2290055, 1, 1, 0, 60000), +(8220005, 2290080, 1, 1, 0, 60000), +(8220005, 2290099, 1, 1, 0, 60000), +(8220005, 2290131, 1, 1, 0, 180000), +(8220005, 2290136, 1, 1, 0, 180000), +(8220006, 2290012, 1, 1, 0, 80000), +(8220006, 2290013, 1, 1, 0, 80000), +(8220006, 2290042, 1, 1, 0, 80000), +(8220006, 2290043, 1, 1, 0, 80000), +(8220006, 2290060, 1, 1, 0, 80000), +(8220006, 2290061, 1, 1, 0, 80000), +(8220006, 2290090, 1, 1, 0, 80000), +(8220006, 2290119, 1, 1, 0, 80000), +(8220006, 2290120, 1, 1, 0, 80000), +(8220006, 2290135, 1, 1, 0, 240000), +(8220006, 2290138, 1, 1, 0, 240000), +(8220007, 2290035, 1, 1, 0, 40000), +(8220007, 2290091, 1, 1, 0, 40000), +(8220007, 2290108, 1, 1, 0, 40000), +(8220009, 2290031, 1, 1, 0, 40000), +(8220009, 2290129, 1, 1, 0, 120000), +(8220015, 2280004, 1, 1, 0, 40000), +(8220015, 2280005, 1, 1, 0, 40000), +(8220015, 2280006, 1, 1, 0, 40000), +(8500002, 2280007, 1, 1, 0, 60000), +(8500002, 2280008, 1, 1, 0, 60000), +(8500002, 2280009, 1, 1, 0, 60000), +(8500002, 2280010, 1, 1, 0, 60000), +(8500002, 2290006, 1, 1, 0, 60000), +(8500002, 2290010, 1, 1, 0, 60000), +(8500002, 2290011, 1, 1, 0, 60000), +(8500002, 2290013, 1, 1, 0, 60000), +(8500002, 2290028, 1, 1, 0, 60000), +(8500002, 2290037, 1, 1, 0, 60000), +(8500002, 2290043, 1, 1, 0, 60000), +(8500002, 2290051, 1, 1, 0, 60000), +(8500002, 2290056, 1, 1, 0, 60000), +(8500002, 2290061, 1, 1, 0, 60000), +(8500002, 2290066, 1, 1, 0, 60000), +(8500002, 2290071, 1, 1, 0, 60000), +(8500002, 2290078, 1, 1, 0, 60000), +(8500002, 2290089, 1, 1, 0, 60000), +(8500002, 2290091, 1, 1, 0, 60000), +(8500002, 2290104, 1, 1, 0, 60000), +(8500002, 2290107, 1, 1, 0, 60000), +(8500002, 2290121, 1, 1, 0, 60000), +(8500002, 2290123, 1, 1, 0, 60000), +(8500002, 2290126, 1, 1, 0, 180000), +(8500002, 2290129, 1, 1, 0, 180000), +(8510000, 2280007, 1, 1, 0, 40000), +(8510000, 2280008, 1, 1, 0, 40000), +(8510000, 2280009, 1, 1, 0, 40000), +(8510000, 2280010, 1, 1, 0, 40000), +(8510000, 2290000, 1, 1, 0, 40000), +(8510000, 2290001, 1, 1, 0, 40000), +(8510000, 2290004, 1, 1, 0, 40000), +(8510000, 2290005, 1, 1, 0, 40000), +(8510000, 2290024, 1, 1, 0, 40000), +(8510000, 2290025, 1, 1, 0, 40000), +(8510000, 2290026, 1, 1, 0, 40000), +(8510000, 2290027, 1, 1, 0, 40000), +(8510000, 2290052, 1, 1, 0, 40000), +(8510000, 2290053, 1, 1, 0, 40000), +(8510000, 2290054, 1, 1, 0, 40000), +(8510000, 2290055, 1, 1, 0, 40000), +(8510000, 2290076, 1, 1, 0, 40000), +(8510000, 2290077, 1, 1, 0, 40000), +(8510000, 2290082, 1, 1, 0, 40000), +(8510000, 2290083, 1, 1, 0, 40000), +(8510000, 2290097, 1, 1, 0, 40000), +(8510000, 2290099, 1, 1, 0, 40000), +(8510000, 2290106, 1, 1, 0, 40000), +(8510000, 2290108, 1, 1, 0, 40000), +(8510000, 2290112, 1, 1, 0, 40000), +(8510000, 2290114, 1, 1, 0, 40000), +(8510000, 2290122, 1, 1, 0, 40000), +(8510000, 2290124, 1, 1, 0, 40000), +(8510000, 2290132, 1, 1, 0, 120000), +(8520000, 2280007, 1, 1, 0, 40000), +(8520000, 2280008, 1, 1, 0, 40000), +(8520000, 2280009, 1, 1, 0, 40000), +(8520000, 2280010, 1, 1, 0, 40000), +(8520000, 2290000, 1, 1, 0, 40000), +(8520000, 2290001, 1, 1, 0, 40000), +(8520000, 2290004, 1, 1, 0, 40000), +(8520000, 2290005, 1, 1, 0, 40000), +(8520000, 2290024, 1, 1, 0, 40000), +(8520000, 2290025, 1, 1, 0, 40000), +(8520000, 2290026, 1, 1, 0, 40000), +(8520000, 2290027, 1, 1, 0, 40000), +(8520000, 2290052, 1, 1, 0, 40000), +(8520000, 2290053, 1, 1, 0, 40000), +(8520000, 2290054, 1, 1, 0, 40000), +(8520000, 2290055, 1, 1, 0, 40000), +(8520000, 2290076, 1, 1, 0, 40000), +(8520000, 2290077, 1, 1, 0, 40000), +(8520000, 2290082, 1, 1, 0, 40000), +(8520000, 2290083, 1, 1, 0, 40000), +(8520000, 2290097, 1, 1, 0, 40000), +(8520000, 2290099, 1, 1, 0, 40000), +(8520000, 2290106, 1, 1, 0, 40000), +(8520000, 2290108, 1, 1, 0, 40000), +(8520000, 2290112, 1, 1, 0, 40000), +(8520000, 2290114, 1, 1, 0, 40000), +(8520000, 2290122, 1, 1, 0, 40000), +(8520000, 2290124, 1, 1, 0, 40000), +(8520000, 2290132, 1, 1, 0, 120000), +(8800002, 2280007, 1, 1, 0, 80000), +(8800002, 2280008, 1, 1, 0, 80000), +(8800002, 2280009, 1, 1, 0, 80000), +(8800002, 2280010, 1, 1, 0, 80000), +(8800002, 2280013, 1, 1, 0, 240000), +(8800002, 2280014, 1, 1, 0, 240000), +(8800002, 2280015, 1, 1, 0, 240000), +(8800002, 2280016, 1, 1, 0, 240000), +(8800002, 2290006, 1, 1, 0, 80000), +(8800002, 2290007, 1, 1, 0, 80000), +(8800002, 2290016, 1, 1, 0, 80000), +(8800002, 2290020, 1, 1, 0, 80000), +(8800002, 2290022, 1, 1, 0, 80000), +(8800002, 2290024, 1, 1, 0, 80000), +(8800002, 2290028, 1, 1, 0, 80000), +(8800002, 2290029, 1, 1, 0, 80000), +(8800002, 2290040, 1, 1, 0, 80000), +(8800002, 2290046, 1, 1, 0, 80000), +(8800002, 2290048, 1, 1, 0, 80000), +(8800002, 2290056, 1, 1, 0, 80000), +(8800002, 2290057, 1, 1, 0, 80000), +(8800002, 2290058, 1, 1, 0, 80000), +(8800002, 2290064, 1, 1, 0, 80000), +(8800002, 2290067, 1, 1, 0, 80000), +(8800002, 2290074, 1, 1, 0, 80000), +(8800002, 2290079, 1, 1, 0, 80000), +(8800002, 2290084, 1, 1, 0, 80000), +(8800002, 2290094, 1, 1, 0, 80000), +(8800002, 2290110, 1, 1, 0, 80000), +(8800002, 2290115, 1, 1, 0, 80000), +(8810018, 2290017, 1, 1, 0, 100000), +(8810018, 2290021, 1, 1, 0, 100000), +(8810018, 2290023, 1, 1, 0, 100000), +(8810018, 2290041, 1, 1, 0, 100000), +(8810018, 2290047, 1, 1, 0, 100000), +(8810018, 2290049, 1, 1, 0, 100000), +(8810018, 2290065, 1, 1, 0, 100000), +(8810018, 2290075, 1, 1, 0, 100000), +(8810018, 2290085, 1, 1, 0, 100000), +(8810018, 2290095, 1, 1, 0, 100000), +(8810018, 2290096, 1, 1, 0, 100000), +(8810018, 2290111, 1, 1, 0, 100000), +(8810018, 2290116, 1, 1, 0, 100000), +(8810018, 2290125, 1, 1, 0, 100000), +(8810018, 2290133, 1, 1, 0, 300000), +(8810018, 2290137, 1, 1, 0, 300000), +(8810018, 2290139, 1, 1, 0, 300000), +(8820000, 2290010, 1, 1, 0, 120000), +(8820000, 2290022, 1, 1, 0, 120000), +(8820000, 2290040, 1, 1, 0, 120000), +(8820000, 2290046, 1, 1, 0, 120000), +(8820000, 2290048, 1, 1, 0, 120000), +(8820000, 2290052, 1, 1, 0, 120000), +(8820000, 2290084, 1, 1, 0, 120000), +(8820000, 2290090, 1, 1, 0, 120000), +(8820000, 2290106, 1, 1, 0, 120000), +(8820000, 2290119, 1, 1, 0, 120000), +(8820001, 2290010, 1, 1, 0, 120000), +(8820001, 2290022, 1, 1, 0, 120000), +(8820001, 2290040, 1, 1, 0, 120000), +(8820001, 2290046, 1, 1, 0, 120000), +(8820001, 2290048, 1, 1, 0, 120000), +(8820001, 2290052, 1, 1, 0, 120000), +(8820001, 2290084, 1, 1, 0, 120000), +(8820001, 2290090, 1, 1, 0, 120000), +(8820001, 2290106, 1, 1, 0, 120000), +(8820001, 2290119, 1, 1, 0, 120000), +(9300028, 2280015, 1, 1, 0, 120000), +(9300028, 2290026, 1, 1, 0, 40000), +(9300028, 2290064, 1, 1, 0, 40000), +(9300028, 2290075, 1, 1, 0, 40000), +(9300028, 2290093, 1, 1, 0, 40000), +(9300028, 2290111, 1, 1, 0, 40000), +(9300094, 2280004, 1, 1, 0, 40000), +(9300094, 2280005, 1, 1, 0, 40000), +(9300094, 2280006, 1, 1, 0, 40000), +(9300095, 2280004, 1, 1, 0, 2000), +(9300095, 2280005, 1, 1, 0, 2000), +(9300095, 2280006, 1, 1, 0, 2000), +(9303016, 2290006, 1, 1, 0, 2000), +(9303016, 2290030, 1, 1, 0, 2000), +(9303016, 2290032, 1, 1, 0, 2000), +(9303016, 2290060, 1, 1, 0, 2000), +(9303016, 2290076, 1, 1, 0, 2000), +(9303016, 2290104, 1, 1, 0, 2000), +(9303016, 2290117, 1, 1, 0, 2000), +(9400014, 2290053, 1, 1, 0, 40000), +(9400014, 2290087, 1, 1, 0, 40000), +(9400014, 2290112, 1, 1, 0, 40000), +(9400014, 2290122, 1, 1, 0, 40000), +(9400120, 2290045, 1, 1, 0, 40000), +(9400121, 2280014, 1, 1, 0, 180000), +(9400121, 2290081, 1, 1, 0, 60000), +(9400121, 2290087, 1, 1, 0, 60000), +(9400121, 2290101, 1, 1, 0, 60000), +(9400121, 2290103, 1, 1, 0, 60000), +(9400122, 2290007, 1, 1, 0, 40000), +(9400122, 2290062, 1, 1, 0, 40000), +(9400122, 2290116, 1, 1, 0, 40000), +(9400300, 2290045, 1, 1, 0, 120000), +(9400300, 2290055, 1, 1, 0, 120000), +(9400300, 2290063, 1, 1, 0, 120000), +(9400300, 2290079, 1, 1, 0, 120000), +(9400300, 2290081, 1, 1, 0, 120000), +(9400300, 2290096, 1, 1, 0, 120000), +(9400514, 2290023, 1, 1, 0, 40000), +(9400514, 2290057, 1, 1, 0, 40000), +(9400514, 2290088, 1, 1, 0, 40000), +(9400514, 2290095, 1, 1, 0, 40000), +(9400514, 2290115, 1, 1, 0, 40000), +(9400514, 2290139, 1, 1, 0, 120000), +(9400549, 2290001, 1, 1, 0, 40000), +(9400549, 2290020, 1, 1, 0, 40000), +(9400549, 2290045, 1, 1, 0, 40000), +(9400549, 2290057, 1, 1, 0, 40000), +(9400549, 2290086, 1, 1, 0, 40000), +(9400575, 2290009, 1, 1, 0, 40000), +(9400575, 2290051, 1, 1, 0, 40000), +(9400575, 2290081, 1, 1, 0, 40000), +(9400575, 2290087, 1, 1, 0, 40000), +(9400575, 2290107, 1, 1, 0, 40000), +(9400575, 2290123, 1, 1, 0, 40000), +(9400580, 2290004, 1, 1, 0, 2000), +(9400580, 2290024, 1, 1, 0, 2000), +(9400580, 2290083, 1, 1, 0, 2000), +(9400580, 2290087, 1, 1, 0, 2000), +(9400580, 2290103, 1, 1, 0, 2000), +(9400580, 2290121, 1, 1, 0, 2000), +(9400582, 2290005, 1, 1, 0, 2000), +(9400582, 2290010, 1, 1, 0, 2000), +(9400582, 2290029, 1, 1, 0, 2000), +(9400582, 2290047, 1, 1, 0, 2000), +(9400582, 2290049, 1, 1, 0, 2000), +(9400582, 2290074, 1, 1, 0, 2000), +(9400582, 2290079, 1, 1, 0, 2000), +(9400582, 2290081, 1, 1, 0, 2000), +(9400582, 2290135, 1, 1, 0, 6000), +(9400590, 2290088, 1, 1, 0, 60000), +(9400590, 2290125, 1, 1, 0, 60000), +(9400590, 2290135, 1, 1, 0, 180000), +(9400591, 2290039, 1, 1, 0, 60000), +(9400591, 2290074, 1, 1, 0, 60000), +(9400591, 2290113, 1, 1, 0, 60000), +(9400592, 2290047, 1, 1, 0, 60000), +(9400592, 2290123, 1, 1, 0, 60000), +(9400592, 2290131, 1, 1, 0, 180000), +(9400593, 2290069, 1, 1, 0, 60000), +(9400593, 2290093, 1, 1, 0, 60000), +(9400593, 2290138, 1, 1, 0, 180000), +(9420513, 2290039, 1, 1, 0, 40000), +(9420513, 2290100, 1, 1, 0, 40000), +(9420513, 2290108, 1, 1, 0, 40000), +(9420513, 2290118, 1, 1, 0, 40000), +(9420513, 2290138, 1, 1, 0, 120000), +(9420514, 2290099, 1, 1, 0, 1287), +(9420517, 2290000, 1, 1, 0, 1287), +(9420517, 2290008, 1, 1, 0, 1287), +(9420517, 2290018, 1, 1, 0, 1287), +(9420517, 2290038, 1, 1, 0, 1287), +(9420517, 2290060, 1, 1, 0, 1287), +(9420517, 2290080, 1, 1, 0, 1287), +(9420517, 2290103, 1, 1, 0, 1287), +(9420518, 2290123, 1, 1, 0, 1287), +(9420519, 2290113, 1, 1, 0, 1287), +(9420522, 2290000, 1, 1, 0, 1287), +(9420522, 2290001, 1, 1, 0, 1287), +(9420522, 2290011, 1, 1, 0, 1287), +(9420522, 2290025, 1, 1, 0, 1287), +(9420522, 2290028, 1, 1, 0, 1287), +(9420522, 2290037, 1, 1, 0, 1287), +(9420522, 2290043, 1, 1, 0, 1287), +(9420522, 2290066, 1, 1, 0, 1287), +(9420522, 2290082, 1, 1, 0, 1287), +(9420522, 2290083, 1, 1, 0, 1287), +(9420522, 2290089, 1, 1, 0, 1287), +(9420522, 2290091, 1, 1, 0, 1287), +(9420522, 2290107, 1, 1, 0, 1287), +(9420540, 2280006, 1, 1, 0, 2000), +(9420540, 2290119, 1, 1, 0, 2000), +(9420540, 2290120, 1, 1, 0, 2000), +(9420544, 2280007, 1, 1, 0, 80000), +(9420544, 2280008, 1, 1, 0, 80000), +(9420544, 2280009, 1, 1, 0, 80000), +(9420544, 2280010, 1, 1, 0, 80000), +(9420544, 2290002, 1, 1, 0, 80000), +(9420544, 2290015, 1, 1, 0, 80000), +(9420544, 2290022, 1, 1, 0, 80000), +(9420544, 2290027, 1, 1, 0, 80000), +(9420544, 2290034, 1, 1, 0, 80000), +(9420544, 2290052, 1, 1, 0, 80000), +(9420544, 2290054, 1, 1, 0, 80000), +(9420544, 2290089, 1, 1, 0, 80000), +(9420544, 2290094, 1, 1, 0, 80000), +(9420544, 2290098, 1, 1, 0, 80000), +(9420544, 2290105, 1, 1, 0, 80000), +(9420544, 2290110, 1, 1, 0, 80000), +(9420544, 2290119, 1, 1, 0, 80000), +(9420549, 2280007, 1, 1, 0, 80000), +(9420549, 2280008, 1, 1, 0, 80000), +(9420549, 2280009, 1, 1, 0, 80000), +(9420549, 2280010, 1, 1, 0, 80000), +(9420549, 2290002, 1, 1, 0, 80000), +(9420549, 2290015, 1, 1, 0, 80000), +(9420549, 2290022, 1, 1, 0, 80000), +(9420549, 2290027, 1, 1, 0, 80000), +(9420549, 2290034, 1, 1, 0, 80000), +(9420549, 2290052, 1, 1, 0, 80000), +(9420549, 2290054, 1, 1, 0, 80000), +(9420549, 2290089, 1, 1, 0, 80000), +(9420549, 2290094, 1, 1, 0, 80000), +(9420549, 2290098, 1, 1, 0, 80000), +(9420549, 2290105, 1, 1, 0, 80000), +(9420549, 2290110, 1, 1, 0, 80000), +(9420549, 2290119, 1, 1, 0, 80000), +(9500166, 2290044, 1, 1, 0, 2000), +(9500173, 2290018, 1, 1, 0, 40000), +(9500173, 2290019, 1, 1, 0, 40000), +(9500173, 2290032, 1, 1, 0, 40000), +(9500173, 2290042, 1, 1, 0, 40000), +(9500173, 2290058, 1, 1, 0, 40000), +(9500173, 2290068, 1, 1, 0, 40000), +(9500173, 2290072, 1, 1, 0, 40000), +(9500173, 2290092, 1, 1, 0, 40000), +(9500173, 2290099, 1, 1, 0, 40000), +(9500173, 2290102, 1, 1, 0, 40000), +(9500173, 2290119, 1, 1, 0, 40000), +(9500173, 2290128, 1, 1, 0, 120000), +(9500174, 2290002, 1, 1, 0, 40000), +(9500174, 2290014, 1, 1, 0, 40000), +(9500174, 2290030, 1, 1, 0, 40000), +(9500174, 2290080, 1, 1, 0, 40000), +(9500174, 2290130, 1, 1, 0, 120000), +(9500180, 2290010, 1, 1, 0, 40000), +(9500180, 2290028, 1, 1, 0, 40000), +(9500180, 2290126, 1, 1, 0, 120000), +(9500181, 2290010, 1, 1, 0, 40000), +(9500181, 2290028, 1, 1, 0, 40000), +(9500181, 2290126, 1, 1, 0, 120000), +(9500331, 2290010, 1, 1, 0, 40000), +(9500331, 2290028, 1, 1, 0, 40000), +(9500331, 2290126, 1, 1, 0, 120000), +(9500332, 2290132, 1, 1, 0, 120000), +(9500333, 2290006, 1, 1, 0, 40000), +(9500333, 2290030, 1, 1, 0, 40000), +(9500333, 2290032, 1, 1, 0, 40000), +(9500333, 2290060, 1, 1, 0, 40000), +(9500333, 2290076, 1, 1, 0, 40000), +(9500333, 2290104, 1, 1, 0, 40000), +(9500333, 2290117, 1, 1, 0, 40000), diff --git a/tools/MapleSkillbookChanceFetcher/manifest.mf b/tools/MapleSkillbookChanceFetcher/manifest.mf new file mode 100644 index 0000000000..328e8e5bc3 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/MapleSkillbookChanceFetcher/nbproject/build-impl.xml b/tools/MapleSkillbookChanceFetcher/nbproject/build-impl.xml new file mode 100644 index 0000000000..7b66374940 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/nbproject/build-impl.xml @@ -0,0 +1,1448 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set platform.home + Must set platform.bootcp + Must set platform.java + Must set platform.javac + + The J2SE Platform is not correctly set up. + Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files. + Either open the project in the IDE and setup the Platform with the same name or add it manually. + For example like this: + ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file) + or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + ${platform.java} -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/MapleSkillbookChanceFetcher/nbproject/genfiles.properties b/tools/MapleSkillbookChanceFetcher/nbproject/genfiles.properties new file mode 100644 index 0000000000..54707bdeab --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=661885c2 +build.xml.script.CRC32=5e98558c +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=661885c2 +nbproject/build-impl.xml.script.CRC32=73de760b +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 diff --git a/tools/MapleSkillbookChanceFetcher/nbproject/project.properties b/tools/MapleSkillbookChanceFetcher/nbproject/project.properties new file mode 100644 index 0000000000..9c0ca12f7e --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/nbproject/project.properties @@ -0,0 +1,77 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=MapleSkillbookChanceFetcher +application.vendor=USER +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Os arquivos em build.classes.dir que devem ser exclu\u00eddos do jar de distribui\u00e7\u00e3o +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/MapleSkillbookChanceFetcher.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.mysql-connector-java-bin.jar=lib/mysql-connector-java-bin.jar +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.mysql-connector-java-bin.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=mapleskillbookchancefetcher.MapleSkillbookChanceFetcher +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=JDK_1.7 +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/tools/MapleSkillbookChanceFetcher/nbproject/project.xml b/tools/MapleSkillbookChanceFetcher/nbproject/project.xml new file mode 100644 index 0000000000..51d885e745 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/nbproject/project.xml @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + MapleSkillbookChanceFetcher + + + + + + + + + + diff --git a/tools/MapleSkillbookChanceFetcher/src/life/Element.java b/tools/MapleSkillbookChanceFetcher/src/life/Element.java new file mode 100644 index 0000000000..5520ba3501 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/life/Element.java @@ -0,0 +1,46 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package life; + +public enum Element { + NEUTRAL, FIRE, ICE, LIGHTING, POISON, HOLY, DARK; + + public static Element getFromChar(char c) { + switch (Character.toUpperCase(c)) { + case 'F': + return FIRE; + case 'I': + return ICE; + case 'L': + return LIGHTING; + case 'S': + return POISON; + case 'H': + return HOLY; + case 'D': + return DARK; + case 'P': + return NEUTRAL; + } + throw new IllegalArgumentException("unknown elemnt char " + c); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/life/ElementalEffectiveness.java b/tools/MapleSkillbookChanceFetcher/src/life/ElementalEffectiveness.java new file mode 100644 index 0000000000..f8d23ef5c7 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/life/ElementalEffectiveness.java @@ -0,0 +1,41 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package life; + +public enum ElementalEffectiveness { + NORMAL, IMMUNE, STRONG, WEAK, NEUTRAL; + + public static ElementalEffectiveness getByNumber(int num) { + switch (num) { + case 1: + return IMMUNE; + case 2: + return STRONG; + case 3: + return WEAK; + case 4: + return NEUTRAL; + default: + throw new IllegalArgumentException("Unkown effectiveness: " + num); + } + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/life/MapleLifeFactory.java b/tools/MapleSkillbookChanceFetcher/src/life/MapleLifeFactory.java new file mode 100644 index 0000000000..23ccd67e43 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/life/MapleLifeFactory.java @@ -0,0 +1,240 @@ +/* +This file is part of the OdinMS Maple Story Server +Copyright (C) 2008 Patrick Huy +Matthias Butz +Jan Christian Meyer + +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 . + */ +package life; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import provider.MapleData; +import provider.MapleDataDirectoryEntry; +import provider.MapleDataFileEntry; +import provider.MapleDataProvider; +import provider.MapleDataProviderFactory; +import provider.MapleDataTool; +import provider.wz.MapleDataType; +import tools.Pair; + +public class MapleLifeFactory { + private static String wzPath = "../../wz"; + private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(wzPath + "/Mob.wz")); + private final static MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(new File(wzPath + "/String.wz")); + private static MapleData mobStringData = stringDataWZ.getData("Mob.img"); + private static MapleData npcStringData = stringDataWZ.getData("Npc.img"); + private static Map monsterStats = new HashMap<>(); + + private static int getMonsterId(String fileName) { + return Integer.parseInt(fileName.substring(0, 7)); + } + + public static Map getAllMonsterStats() { + MapleDataDirectoryEntry root = data.getRoot(); + + System.out.print("Parsing mob stats... "); + for (MapleDataFileEntry mFile : root.getFiles()) { + try { + String fileName = mFile.getName(); + + //System.out.println("Parsing '" + fileName + "'"); + MapleData monsterData = data.getData(fileName); + if (monsterData == null) { + continue; + } + + Integer mid = getMonsterId(fileName); + + MapleData monsterInfoData = monsterData.getChildByPath("info"); + MapleMonsterStats stats = new MapleMonsterStats(); + stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData)); + stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, 0) == 1); + stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData)); + stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData)); + stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData)); + stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData)); + stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0)); + stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0)); + stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData)); + stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, 0)); + stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, 0) > 0); + stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, 0) > 0); + stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, 0) > 0); + stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, 0) > 0); + stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO")); + stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, -1)); + stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0)); + stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, 0) > 0); + + MapleData special = monsterInfoData.getChildByPath("coolDamage"); + if (special != null) { + int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData); + int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0); + stats.setCool(new Pair<>(coolDmg, coolProb)); + } + special = monsterInfoData.getChildByPath("loseItem"); + if (special != null) { + for (MapleData liData : special.getChildren()) { + stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x")))); + } + } + special = monsterInfoData.getChildByPath("selfDestruction"); + if (special != null) { + stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1))); + } + MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack"); + int firstAttack = 0; + if (firstAttackData != null) { + if (firstAttackData.getType() == MapleDataType.FLOAT) { + firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData)); + } else { + firstAttack = MapleDataTool.getInt(firstAttackData); + } + } + stats.setFirstAttack(firstAttack > 0); + stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, 0) * 10000); + + stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0)); + stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0)); + + for (MapleData idata : monsterData) { + if (!idata.getName().equals("info")) { + int delay = 0; + for (MapleData pic : idata.getChildren()) { + delay += MapleDataTool.getIntConvert("delay", pic, 0); + } + stats.setAnimationTime(idata.getName(), delay); + } + } + MapleData reviveInfo = monsterInfoData.getChildByPath("revive"); + if (reviveInfo != null) { + List revives = new LinkedList<>(); + for (MapleData data_ : reviveInfo) { + revives.add(MapleDataTool.getInt(data_)); + } + stats.setRevives(revives); + } + decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, "")); + MapleData monsterSkillData = monsterInfoData.getChildByPath("skill"); + if (monsterSkillData != null) { + int i = 0; + List> 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)))); + i++; + } + stats.setSkills(skills); + } + MapleData banishData = monsterInfoData.getChildByPath("ban"); + if (banishData != null) { + stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp"))); + } + + monsterStats.put(mid, stats); + } catch(NullPointerException npe) { + //System.out.println("[SEVERE] " + mFile.getName() + " failed to load. Issue: " + npe.getMessage() + "\n\n"); + } + } + + System.out.println("done!"); + return monsterStats; + } + + private static void decodeElementalString(MapleMonsterStats stats, String elemAttr) { + for (int i = 0; i < elemAttr.length(); i += 2) { + stats.setEffectiveness(Element.getFromChar(elemAttr.charAt(i)), ElementalEffectiveness.getByNumber(Integer.valueOf(String.valueOf(elemAttr.charAt(i + 1))))); + } + } + + public static class BanishInfo { + + private int map; + private String portal, msg; + + public BanishInfo(String msg, int map, String portal) { + this.msg = msg; + this.map = map; + this.portal = portal; + } + + public int getMap() { + return map; + } + + public String getPortal() { + return portal; + } + + public String getMsg() { + return msg; + } + } + + public static class loseItem { + + private int id; + private byte chance, x; + + private loseItem(int id, byte chance, byte x) { + this.id = id; + this.chance = chance; + this.x = x; + } + + public int getId() { + return id; + } + + public byte getChance() { + return chance; + } + + public byte getX() { + return x; + } + } + + public static class selfDestruction { + + private byte action; + private int removeAfter; + private int hp; + + private selfDestruction(byte action, int removeAfter, int hp) { + this.action = action; + this.removeAfter = removeAfter; + this.hp = hp; + } + + public int getHp() { + return hp; + } + + public byte getAction() { + return action; + } + + public int removeAfter() { + return removeAfter; + } + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/life/MapleMonsterStats.java b/tools/MapleSkillbookChanceFetcher/src/life/MapleMonsterStats.java new file mode 100644 index 0000000000..42b091545d --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/life/MapleMonsterStats.java @@ -0,0 +1,336 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package life; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import life.MapleLifeFactory.BanishInfo; +import life.MapleLifeFactory.loseItem; +import life.MapleLifeFactory.selfDestruction; +import tools.Pair; + +/** + * @author Frz + */ +public class MapleMonsterStats { + private boolean changeable; + private int exp, hp, mp, level, PADamage, PDDamage, MADamage, MDDamage, dropPeriod, cp, buffToGive, removeAfter; + private boolean boss, undead, ffaLoot, isExplosiveReward, firstAttack, removeOnMiss; + private String name; + private Map animationTimes = new HashMap(); + private Map resistance = new HashMap(); + private List revives = Collections.emptyList(); + private byte tagColor, tagBgColor; + private List> skills = new ArrayList>(); + private Pair cool = null; + private BanishInfo banish = null; + private List loseItem = null; + private selfDestruction selfDestruction = null; + private boolean friendly; + + public void setChange(boolean change) { + this.changeable = change; + } + + public boolean isChangeable() { + return changeable; + } + + public int getExp() { + return exp; + } + + public void setExp(int exp) { + this.exp = exp; + } + + public int getHp() { + return hp; + } + + public void setHp(int hp) { + this.hp = hp; + } + + public int getMp() { + return mp; + } + + public void setMp(int mp) { + this.mp = mp; + } + + public int getLevel() { + return level; + } + + public void setLevel(int level) { + this.level = level; + } + + public int removeAfter() { + return removeAfter; + } + + public void setRemoveAfter(int removeAfter) { + this.removeAfter = removeAfter; + } + + public int getDropPeriod() { + return dropPeriod; + } + + public void setDropPeriod(int dropPeriod) { + this.dropPeriod = dropPeriod; + } + + public void setBoss(boolean boss) { + this.boss = boss; + } + + public boolean isBoss() { + return boss; + } + + public void setFfaLoot(boolean ffaLoot) { + this.ffaLoot = ffaLoot; + } + + public boolean isFfaLoot() { + return ffaLoot; + } + + public void setAnimationTime(String name, int delay) { + animationTimes.put(name, delay); + } + + public int getAnimationTime(String name) { + Integer ret = animationTimes.get(name); + if (ret == null) { + return 500; + } + return ret.intValue(); + } + + public boolean isMobile() { + return animationTimes.containsKey("move") || animationTimes.containsKey("fly"); + } + + public List getRevives() { + return revives; + } + + public void setRevives(List revives) { + this.revives = revives; + } + + public void setUndead(boolean undead) { + this.undead = undead; + } + + public boolean getUndead() { + return undead; + } + + public void setEffectiveness(Element e, ElementalEffectiveness ee) { + resistance.put(e, ee); + } + + public ElementalEffectiveness getEffectiveness(Element e) { + ElementalEffectiveness elementalEffectiveness = resistance.get(e); + if (elementalEffectiveness == null) { + return ElementalEffectiveness.NORMAL; + } else { + return elementalEffectiveness; + } + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public byte getTagColor() { + return tagColor; + } + + public void setTagColor(int tagColor) { + this.tagColor = (byte) tagColor; + } + + public byte getTagBgColor() { + return tagBgColor; + } + + public void setTagBgColor(int tagBgColor) { + this.tagBgColor = (byte) tagBgColor; + } + + public void setSkills(List> skills) { + for (Pair skill : skills) { + this.skills.add(skill); + } + } + + public List> getSkills() { + return Collections.unmodifiableList(this.skills); + } + + public int getNoSkills() { + return this.skills.size(); + } + + public boolean hasSkill(int skillId, int level) { + for (Pair skill : skills) { + if (skill.getLeft() == skillId && skill.getRight() == level) { + return true; + } + } + return false; + } + + public void setFirstAttack(boolean firstAttack) { + this.firstAttack = firstAttack; + } + + public boolean isFirstAttack() { + return firstAttack; + } + + public void setBuffToGive(int buff) { + this.buffToGive = buff; + } + + public int getBuffToGive() { + return buffToGive; + } + + void removeEffectiveness(Element e) { + resistance.remove(e); + } + + public BanishInfo getBanishInfo() { + return banish; + } + + public void setBanishInfo(BanishInfo banish) { + this.banish = banish; + } + + public int getPADamage() { + return PADamage; + } + + public void setPADamage(int PADamage) { + this.PADamage = PADamage; + } + + public int getCP() { + return cp; + } + + public void setCP(int cp) { + this.cp = cp; + } + + public List loseItem() { + return loseItem; + } + + public void addLoseItem(loseItem li) { + if (loseItem == null) { + loseItem = new LinkedList(); + } + loseItem.add(li); + } + + public selfDestruction selfDestruction() { + return selfDestruction; + } + + public void setSelfDestruction(selfDestruction sd) { + this.selfDestruction = sd; + } + + public void setExplosiveReward(boolean isExplosiveReward) { + this.isExplosiveReward = isExplosiveReward; + } + + public boolean isExplosiveReward() { + return isExplosiveReward; + } + + public void setRemoveOnMiss(boolean removeOnMiss) { + this.removeOnMiss = removeOnMiss; + } + + public boolean removeOnMiss() { + return removeOnMiss; + } + + public void setCool(Pair cool) { + this.cool = cool; + } + + public Pair getCool() { + return cool; + } + + public int getPDDamage() { + return PDDamage; + } + + public int getMADamage() { + return MADamage; + } + + public int getMDDamage() { + return MDDamage; + } + + public boolean isFriendly() { + return friendly; + } + + public void setFriendly(boolean value) { + this.friendly = value; + } + + public void setPDDamage(int PDDamage) { + this.PDDamage = PDDamage; + } + + public void setMADamage(int MADamage) { + this.MADamage = MADamage; + } + + public void setMDDamage(int MDDamage) { + this.MDDamage = MDDamage; + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/mapleskillbookchancefetcher/MapleSkillbookChanceFetcher.java b/tools/MapleSkillbookChanceFetcher/src/mapleskillbookchancefetcher/MapleSkillbookChanceFetcher.java new file mode 100644 index 0000000000..b28c4b499e --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/mapleskillbookchancefetcher/MapleSkillbookChanceFetcher.java @@ -0,0 +1,159 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 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 . +*/ +package mapleskillbookchancefetcher; + +import life.MapleLifeFactory; +import life.MapleMonsterStats; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +import java.io.*; +import java.util.Collections; +import java.util.Comparator; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import tools.DatabaseConnection; +import tools.Pair; + +/** + * + * @author RonanLana + * + * This application traces missing meso drop data on the underlying DB (that must be + * defined on the DatabaseConnection file of this project) and generates a + * SQL file that proposes missing drop entries for the drop_data table. + * + * The meso range is calculated accordingly with the target mob stats, such as level + * and if it's a boss or not, similarly as how it has been done for the actual meso + * drops. + * + */ +public class MapleSkillbookChanceFetcher { + + private static PrintWriter printWriter; + private static String newFile = "lib/skillbook_drop_data.sql"; + + private static Map mobStats; + private static Map, Integer> skillbookChances = new HashMap<>(); + + private static List, Integer>> sortedSkillbookChances() { + List, Integer>> skillbookChancesList = new ArrayList<>(skillbookChances.entrySet()); + + Collections.sort(skillbookChancesList, new Comparator, Integer>>() { + @Override + public int compare(Entry, Integer> o1, Entry, Integer> o2) { + if (o1.getKey().getLeft().equals(o2.getKey().getLeft())) { + return o1.getKey().getRight() < o2.getKey().getRight() ? -1 : (o1.getKey().getRight().equals(o2.getKey().getRight()) ? 0 : 1); + } + + return (o1.getKey().getLeft() < o2.getKey().getLeft()) ? -1 : 1; + } + }); + + return skillbookChancesList; + } + + private static boolean isLegendSkillUpgradeBook(int itemid) { + int itemidBranch = itemid / 10000; + return (itemidBranch == 228 && itemid >= 2280013 || itemidBranch == 229 && itemid >= 2290126); // drop rate of Legends are higher + } + + private static void fetchSkillbookDropChances() { + Connection con = DatabaseConnection.getConnection(); + + try { + PreparedStatement ps = con.prepareStatement("SELECT dropperid, itemid FROM drop_data WHERE itemid >= 2280000 AND itemid < 2300000"); + ResultSet rs = ps.executeQuery(); + + while(rs.next()) { + int mobid = rs.getInt("dropperid"); + int itemid = rs.getInt("itemid"); + + int expectedChance = 1000; + + if (mobStats.get(mobid) != null) { + int level = mobStats.get(mobid).getLevel(); + expectedChance *= Math.max(2, (level - 80) / 15); + + if (mobStats.get(mobid).isBoss()) { + expectedChance *= 20; + } else { + expectedChance *= 1; + } + } else { + expectedChance = 1287; + } + + if (isLegendSkillUpgradeBook(itemid)) { // drop rate of Legends seems to be higher than explorers, in retrospect from values in DB + expectedChance *= 3; + } + + skillbookChances.put(new Pair<>(mobid, itemid), expectedChance); + } + + rs.close(); + ps.close(); + con.close(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + private static void printSkillbookChanceUpdateSqlHeader() { + printWriter.println(" # SQL File autogenerated from the MapleSkillbookChanceFetcher feature by Ronan Lana."); + printWriter.println(" # Generated data takes into account mob stats such as level and boss for the chance rates."); + printWriter.println(); + + printWriter.println(" REPLACE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES"); + } + + private static void generateSkillbookChanceUpdateFile() { + try { + printWriter = new PrintWriter(newFile, "UTF-8"); + + printSkillbookChanceUpdateSqlHeader(); + + List, Integer>> skillbookChancesList = sortedSkillbookChances(); + for (Entry, Integer> e : skillbookChancesList) { + printWriter.println("(" + e.getKey().getLeft() + ", " + e.getKey().getRight() + ", 1, 1, 0, " + e.getValue() + "),"); + } + + printWriter.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + public static void main(String[] args) { + // load mob stats from WZ + mobStats = MapleLifeFactory.getAllMonsterStats(); + + fetchSkillbookDropChances(); + generateSkillbookChanceUpdateFile(); + } + +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/MapleCanvas.java b/tools/MapleSkillbookChanceFetcher/src/provider/MapleCanvas.java new file mode 100644 index 0000000000..10ab682196 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/MapleCanvas.java @@ -0,0 +1,30 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider; + +import java.awt.image.BufferedImage; + +public interface MapleCanvas { + int getHeight(); + int getWidth(); + BufferedImage getImage(); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/MapleData.java b/tools/MapleSkillbookChanceFetcher/src/provider/MapleData.java new file mode 100644 index 0000000000..4d90a93804 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/MapleData.java @@ -0,0 +1,34 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider; + +import java.util.List; +import provider.wz.MapleDataType; + +public interface MapleData extends MapleDataEntity, Iterable { + @Override + public String getName(); + public MapleDataType getType(); + public List getChildren(); + public MapleData getChildByPath(String path); + public Object getData(); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataDirectoryEntry.java b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataDirectoryEntry.java new file mode 100644 index 0000000000..cb043e0c94 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataDirectoryEntry.java @@ -0,0 +1,34 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider; + +import java.util.List; + +/** + * + * @author Matze + */ +public interface MapleDataDirectoryEntry extends MapleDataEntry { + public List getSubdirectories(); + public List getFiles(); + public MapleDataEntry getEntry(String name); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataEntity.java b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataEntity.java new file mode 100644 index 0000000000..03ff77649c --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataEntity.java @@ -0,0 +1,31 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider; + +/** + * + * @author Matze + */ +public interface MapleDataEntity { + public String getName(); + public MapleDataEntity getParent(); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataEntry.java b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataEntry.java new file mode 100644 index 0000000000..62db6d0abe --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataEntry.java @@ -0,0 +1,33 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider; + +/** + * + * @author Matze + */ +public interface MapleDataEntry extends MapleDataEntity { + public String getName(); + public int getSize(); + public int getChecksum(); + public int getOffset(); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataFileEntry.java b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataFileEntry.java new file mode 100644 index 0000000000..902130a612 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataFileEntry.java @@ -0,0 +1,30 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider; + +/** + * + * @author Matze + */ +public interface MapleDataFileEntry extends MapleDataEntry { + public void setOffset(int offset); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataProvider.java b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataProvider.java new file mode 100644 index 0000000000..5237b7ac37 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataProvider.java @@ -0,0 +1,27 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider; + +public interface MapleDataProvider { + MapleData getData(String path); + MapleDataDirectoryEntry getRoot(); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataProviderFactory.java b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataProviderFactory.java new file mode 100644 index 0000000000..14753d4406 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataProviderFactory.java @@ -0,0 +1,55 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider; + +import java.io.File; +import java.io.IOException; +import provider.wz.WZFile; +import provider.wz.XMLWZFile; + +public class MapleDataProviderFactory { + private final static String wzPath = System.getProperty("wzpath"); + + private static MapleDataProvider getWZ(File in, boolean provideImages) { + if (in.getName().toLowerCase().endsWith("wz") && !in.isDirectory()) { + try { + return new WZFile(in, provideImages); + } catch (IOException e) { + throw new RuntimeException("Loading WZ File failed", e); + } + } else { + return new XMLWZFile(in); + } + } + + public static MapleDataProvider getDataProvider(File in) { + return getWZ(in, false); + } + + public static MapleDataProvider getImageProvidingDataProvider(File in) { + return getWZ(in, true); + } + + public static File fileInWZPath(String filename) { + return new File(wzPath, filename); + } +} \ No newline at end of file diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataTool.java b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataTool.java new file mode 100644 index 0000000000..25f4c7f817 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/MapleDataTool.java @@ -0,0 +1,145 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider; + +import java.awt.Point; +import java.awt.image.BufferedImage; +import provider.wz.MapleDataType; + +public class MapleDataTool { + public static String getString(MapleData data) { + return ((String) data.getData()); + } + + public static String getString(MapleData data, String def) { + if (data == null || data.getData() == null) { + return def; + } else { + return ((String) data.getData()); + } + } + + public static String getString(String path, MapleData data) { + return getString(data.getChildByPath(path)); + } + + public static String getString(String path, MapleData data, String def) { + return getString(data.getChildByPath(path), def); + } + + public static double getDouble(MapleData data) { + return ((Double) data.getData()).doubleValue(); + } + + public static float getFloat(MapleData data) { + return ((Float) data.getData()).floatValue(); + } + + public static int getInt(MapleData data) { + if (data == null || data.getData() == null) { + return 0;// DEF? + } + return ((Integer) data.getData()).intValue(); + } + + public static int getInt(String path, MapleData data) { + return getInt(data.getChildByPath(path)); + } + + public static int getIntConvert(MapleData data) { + if (data.getType() == MapleDataType.STRING) { + return Integer.parseInt(getString(data)); + } else { + return getInt(data); + } + } + + public static int getIntConvert(String path, MapleData data) { + MapleData d = data.getChildByPath(path); + if (d.getType() == MapleDataType.STRING) { + return Integer.parseInt(getString(d)); + } else { + return getInt(d); + } + } + + public static int getInt(MapleData data, int def) { + if (data == null || data.getData() == null) { + return def; + } else if (data.getType() == MapleDataType.STRING) { + return Integer.parseInt(getString(data)); + } else { + return ((Integer) data.getData()).intValue(); + } + } + + public static int getInt(String path, MapleData data, int def) { + return getInt(data.getChildByPath(path), def); + } + + public static int getIntConvert(String path, MapleData data, int def) { + MapleData d = data.getChildByPath(path); + if (d == null) { + return def; + } + if (d.getType() == MapleDataType.STRING) { + try { + return Integer.parseInt(getString(d)); + } catch (NumberFormatException nfe) { + nfe.printStackTrace(); + return def; + } + } else { + return getInt(d, def); + } + } + + public static BufferedImage getImage(MapleData data) { + return ((MapleCanvas) data.getData()).getImage(); + } + + public static Point getPoint(MapleData data) { + return ((Point) data.getData()); + } + + public static Point getPoint(String path, MapleData data) { + return getPoint(data.getChildByPath(path)); + } + + public static Point getPoint(String path, MapleData data, Point def) { + final MapleData pointData = data.getChildByPath(path); + if (pointData == null) { + return def; + } + return getPoint(pointData); + } + + public static String getFullDataPath(MapleData data) { + String path = ""; + MapleDataEntity myData = data; + while (myData != null) { + path = myData.getName() + "/" + path; + myData = myData.getParent(); + } + return path.substring(0, path.length() - 1); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/FileStoredPngMapleCanvas.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/FileStoredPngMapleCanvas.java new file mode 100644 index 0000000000..21736c2c16 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/FileStoredPngMapleCanvas.java @@ -0,0 +1,70 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; +import provider.MapleCanvas; + +public class FileStoredPngMapleCanvas implements MapleCanvas { + private File file; + private int width; + private int height; + private BufferedImage image; + + public FileStoredPngMapleCanvas(int width, int height, File fileIn) { + this.width = width; + this.height = height; + this.file = fileIn; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public BufferedImage getImage() { + loadImageIfNecessary(); + return image; + } + + private void loadImageIfNecessary() { + if (image == null) { + try { + image = ImageIO.read(file); + // replace the dimensions loaded from the wz by the REAL dimensions from the image - should be equal tho + width = image.getWidth(); + height = image.getHeight(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/ImgMapleSound.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/ImgMapleSound.java new file mode 100644 index 0000000000..8add2ccb36 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/ImgMapleSound.java @@ -0,0 +1,39 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +public class ImgMapleSound { + private int dataLength, offset; + + public ImgMapleSound(int dataLength, int offset) { + this.dataLength = dataLength; + this.offset = offset; + } + + public int getDataLength() { + return dataLength; + } + + public int getOffset() { + return offset; + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/ListWZFile.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/ListWZFile.java new file mode 100644 index 0000000000..1672a08c59 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/ListWZFile.java @@ -0,0 +1,86 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import provider.MapleDataProviderFactory; +import tools.data.input.GenericLittleEndianAccessor; +import tools.data.input.InputStreamByteStream; +import tools.data.input.LittleEndianAccessor; + +public class ListWZFile { + private LittleEndianAccessor lea; + private List entries = new ArrayList(); + private static Collection modernImgs = new HashSet(); + + public static byte[] xorBytes(byte[] a, byte[] b) { + byte[] wusched = new byte[a.length]; + for (int i = 0; i < a.length; i++) { + wusched[i] = (byte) (a[i] ^ b[i]); + } + return wusched; + } + + public ListWZFile(File listwz) throws FileNotFoundException { + lea = new GenericLittleEndianAccessor(new InputStreamByteStream(new BufferedInputStream(new FileInputStream(listwz)))); + while (lea.available() > 0) { + int l = lea.readInt() * 2; + byte[] chunk = new byte[l]; + for (int i = 0; i < chunk.length; i++) { + chunk[i] = lea.readByte(); + } + lea.readChar(); + final String value = String.valueOf(WZTool.readListString(chunk)); + entries.add(value); + } + entries = Collections.unmodifiableList(entries); + } + + public List getEntries() { + return entries; + } + + public static void init() { + final String listWz = System.getProperty("listwz"); + if (listWz != null) { + ListWZFile listwz; + try { + listwz = new ListWZFile(MapleDataProviderFactory.fileInWZPath("List.wz")); + modernImgs = new HashSet(listwz.getEntries()); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + } + + public static boolean isModernImgFile(String path) { + return modernImgs.contains(path); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/MapleDataType.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/MapleDataType.java new file mode 100644 index 0000000000..e074d57d14 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/MapleDataType.java @@ -0,0 +1,26 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +public enum MapleDataType { + NONE, IMG_0x00, SHORT, INT, FLOAT, DOUBLE, STRING, EXTENDED, PROPERTY, CANVAS, VECTOR, CONVEX, SOUND, UOL, UNKNOWN_TYPE, UNKNOWN_EXTENDED_TYPE; +} \ No newline at end of file diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/PNGMapleCanvas.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/PNGMapleCanvas.java new file mode 100644 index 0000000000..97c2303804 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/PNGMapleCanvas.java @@ -0,0 +1,151 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.PixelInterleavedSampleModel; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; +import provider.MapleCanvas; + +public class PNGMapleCanvas implements MapleCanvas { + private static final int[] ZAHLEN = new int[]{2, 1, 0, 3}; + private int height; + private int width; + private int dataLength; + private int format; + private byte[] data; + + public PNGMapleCanvas(int width, int height, int dataLength, int format, byte[] data) { + super(); + this.height = height; + this.width = width; + this.dataLength = dataLength; + this.format = format; + this.data = data; + } + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } + + public int getFormat() { + return format; + } + + private byte[] getData() { + return data; + } + + @Override + public BufferedImage getImage() { + int sizeUncompressed = 0; + int size8888 = 0; + int maxWriteBuf = 2; + int maxHeight = 3; + byte[] writeBuf = new byte[maxWriteBuf]; + @SuppressWarnings ("unused") + byte[] rowPointers = new byte[maxHeight]; + switch (getFormat()) { + case 1: + case 513: + sizeUncompressed = getHeight() * getWidth() * 4; + break; + case 2: + sizeUncompressed = getHeight() * getWidth() * 8; + break; + case 517: + sizeUncompressed = getHeight() * getWidth() / 128; + break; + } + size8888 = getHeight() * getWidth() * 8; + if (size8888 > maxWriteBuf) { + maxWriteBuf = size8888; + writeBuf = new byte[maxWriteBuf]; + } + if (getHeight() > maxHeight) { + maxHeight = getHeight(); + rowPointers = new byte[maxHeight]; + } + Inflater dec = new Inflater(); + dec.setInput(getData(), 0, dataLength); + int declen = 0; + byte[] uc = new byte[sizeUncompressed]; + try { + declen = dec.inflate(uc); + } catch (DataFormatException ex) { + throw new RuntimeException("zlib fucks", ex); + } + dec.end(); + if (getFormat() == 1) { + for (int i = 0; i < sizeUncompressed; i++) { + byte low = (byte) (uc[i] & 0x0F); + byte high = (byte) (uc[i] & 0xF0); + writeBuf[(i << 1)] = (byte) (((low << 4) | low) & 0xFF); + writeBuf[(i << 1) + 1] = (byte) (high | ((high >>> 4) & 0xF)); + } + } else if (getFormat() == 2) { + writeBuf = uc; + } else if (getFormat() == 513) { + for (int i = 0; i < declen; i += 2) { + byte bBits = (byte) ((uc[i] & 0x1F) << 3); + byte gBits = (byte) (((uc[i + 1] & 0x07) << 5) | ((uc[i] & 0xE0) >> 3)); + byte rBits = (byte) (uc[i + 1] & 0xF8); + writeBuf[(i << 1)] = (byte) (bBits | (bBits >> 5)); + writeBuf[(i << 1) + 1] = (byte) (gBits | (gBits >> 6)); + writeBuf[(i << 1) + 2] = (byte) (rBits | (rBits >> 5)); + writeBuf[(i << 1) + 3] = (byte) 0xFF; + } + } else if (getFormat() == 517) { + byte b = 0x00; + int pixelIndex = 0; + for (int i = 0; i < declen; i++) { + for (int j = 0; j < 8; j++) { + b = (byte) (((uc[i] & (0x01 << (7 - j))) >> (7 - j)) * 255); + for (int k = 0; k < 16; k++) { + pixelIndex = (i << 9) + (j << 6) + k * 2; + writeBuf[pixelIndex] = b; + writeBuf[pixelIndex + 1] = b; + writeBuf[pixelIndex + 2] = b; + writeBuf[pixelIndex + 3] = (byte) 0xFF; + } + } + } + } + DataBufferByte imgData = new DataBufferByte(writeBuf, sizeUncompressed); + SampleModel sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, getWidth(), getHeight(), 4, getWidth() * 4, ZAHLEN); + WritableRaster imgRaster = Raster.createWritableRaster(sm, imgData, new Point(0, 0)); + BufferedImage aa = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); + aa.setData(imgRaster); + return aa; + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZDirectoryEntry.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZDirectoryEntry.java new file mode 100644 index 0000000000..d24b8cb2b9 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZDirectoryEntry.java @@ -0,0 +1,68 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import provider.MapleDataDirectoryEntry; +import provider.MapleDataEntity; +import provider.MapleDataEntry; +import provider.MapleDataFileEntry; + +public class WZDirectoryEntry extends WZEntry implements MapleDataDirectoryEntry { + private List subdirs = new ArrayList(); + private List files = new ArrayList(); + private Map entries = new HashMap(); + + public WZDirectoryEntry(String name, int size, int checksum, MapleDataEntity parent) { + super(name, size, checksum, parent); + } + + public WZDirectoryEntry() { + super(null, 0, 0, null); + } + + public void addDirectory(MapleDataDirectoryEntry dir) { + subdirs.add(dir); + entries.put(dir.getName(), dir); + } + + public void addFile(MapleDataFileEntry fileEntry) { + files.add(fileEntry); + entries.put(fileEntry.getName(), fileEntry); + } + + public List getSubdirectories() { + return Collections.unmodifiableList(subdirs); + } + + public List getFiles() { + return Collections.unmodifiableList(files); + } + + public MapleDataEntry getEntry(String name) { + return entries.get(name); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZEntry.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZEntry.java new file mode 100644 index 0000000000..1e921b2082 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZEntry.java @@ -0,0 +1,61 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import provider.MapleDataEntity; +import provider.MapleDataEntry; + +public class WZEntry implements MapleDataEntry { + private String name; + private int size; + private int checksum; + private int offset; + private MapleDataEntity parent; + + public WZEntry(String name, int size, int checksum, MapleDataEntity parent) { + super(); + this.name = name; + this.size = size; + this.checksum = checksum; + this.parent = parent; + } + + public String getName() { + return name; + } + + public int getSize() { + return size; + } + + public int getChecksum() { + return checksum; + } + + public int getOffset() { + return offset; + } + + public MapleDataEntity getParent() { + return parent; + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZFile.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZFile.java new file mode 100644 index 0000000000..c6c0abf537 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZFile.java @@ -0,0 +1,154 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import provider.MapleData; +import provider.MapleDataDirectoryEntry; +import provider.MapleDataFileEntry; +import provider.MapleDataProvider; +import tools.data.input.GenericLittleEndianAccessor; +import tools.data.input.GenericSeekableLittleEndianAccessor; +import tools.data.input.InputStreamByteStream; +import tools.data.input.LittleEndianAccessor; +import tools.data.input.RandomAccessByteStream; +import tools.data.input.SeekableLittleEndianAccessor; + +public class WZFile implements MapleDataProvider { + static { + ListWZFile.init(); + } + private File wzfile; + private LittleEndianAccessor lea; + private SeekableLittleEndianAccessor slea; + private int headerSize; + private WZDirectoryEntry root; + private boolean provideImages; + private int cOffset; + + public WZFile(File wzfile, boolean provideImages) throws IOException { + this.wzfile = wzfile; + lea = new GenericLittleEndianAccessor(new InputStreamByteStream(new BufferedInputStream(new FileInputStream(wzfile)))); + RandomAccessFile raf = new RandomAccessFile(wzfile, "r"); + slea = new GenericSeekableLittleEndianAccessor(new RandomAccessByteStream(raf)); + root = new WZDirectoryEntry(wzfile.getName(), 0, 0, null); + this.provideImages = provideImages; + load(); + } + + private void load() throws IOException { + lea.readAsciiString(4); + lea.readInt(); + lea.readInt(); + headerSize = lea.readInt(); + lea.readNullTerminatedAsciiString(); + lea.readShort(); + parseDirectory(root); + cOffset = (int) lea.getBytesRead(); + getOffsets(root); + } + + private void getOffsets(MapleDataDirectoryEntry dir) { + for (MapleDataFileEntry file : dir.getFiles()) { + file.setOffset(cOffset); + cOffset += file.getSize(); + } + for (MapleDataDirectoryEntry sdir : dir.getSubdirectories()) { + getOffsets(sdir); + } + } + + private void parseDirectory(WZDirectoryEntry dir) { + int entries = WZTool.readValue(lea); + for (int i = 0; i < entries; i++) { + byte marker = lea.readByte(); + String name = null; + int size, checksum; + switch (marker) { + case 0x02: + name = WZTool.readDecodedStringAtOffsetAndReset(slea, lea.readInt() + this.headerSize + 1); + size = WZTool.readValue(lea); + checksum = WZTool.readValue(lea); + lea.readInt(); //dummy int + dir.addFile(new WZFileEntry(name, size, checksum, dir)); + break; + case 0x03: + case 0x04: + name = WZTool.readDecodedString(lea); + size = WZTool.readValue(lea); + checksum = WZTool.readValue(lea); + lea.readInt(); //dummy int + if (marker == 3) { + dir.addDirectory(new WZDirectoryEntry(name, size, checksum, dir)); + } else { + dir.addFile(new WZFileEntry(name, size, checksum, dir)); + } + break; + default: + } + } + for (MapleDataDirectoryEntry idir : dir.getSubdirectories()) { + parseDirectory((WZDirectoryEntry) idir); + } + } + + public WZIMGFile getImgFile(String path) throws IOException { + String segments[] = path.split("/"); + WZDirectoryEntry dir = root; + for (int x = 0; x < segments.length - 1; x++) { + dir = (WZDirectoryEntry) dir.getEntry(segments[x]); + if (dir == null) { + return null; + } + } + WZFileEntry entry = (WZFileEntry) dir.getEntry(segments[segments.length - 1]); + if (entry == null) { + return null; + } + String fullPath = wzfile.getName().substring(0, wzfile.getName().length() - 3).toLowerCase() + "/" + path; + return new WZIMGFile(this.wzfile, entry, provideImages, ListWZFile.isModernImgFile(fullPath)); + } + + @Override + public synchronized MapleData getData(String path) { + try { + WZIMGFile imgFile = getImgFile(path); + if (imgFile == null) { + return null; + } + MapleData ret = imgFile.getRoot(); + return ret; + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public MapleDataDirectoryEntry getRoot() { + return root; + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZFileEntry.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZFileEntry.java new file mode 100644 index 0000000000..792371d9cf --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZFileEntry.java @@ -0,0 +1,42 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import provider.MapleDataEntity; +import provider.MapleDataFileEntry; + +public class WZFileEntry extends WZEntry implements MapleDataFileEntry { + private int offset; + + public WZFileEntry(String name, int size, int checksum, MapleDataEntity parent) { + super(name, size, checksum, parent); + } + + @Override + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZIMGEntry.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZIMGEntry.java new file mode 100644 index 0000000000..385d785183 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZIMGEntry.java @@ -0,0 +1,118 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import provider.MapleData; +import provider.MapleDataEntity; + +public class WZIMGEntry implements MapleData { + private String name; + private MapleDataType type; + private List children = new ArrayList(10); + private Object data; + private MapleDataEntity parent; + + public WZIMGEntry(MapleDataEntity parent) { + this.parent = parent; + } + + @Override + public String getName() { + return name; + } + + @Override + public MapleDataType getType() { + return type; + } + + @Override + public List getChildren() { + return Collections.unmodifiableList(children); + } + + @Override + public MapleData getChildByPath(String path) { + String segments[] = path.split("/"); + if (segments[0].equals("..")) { + return ((MapleData) getParent()).getChildByPath(path.substring(path.indexOf("/") + 1)); + } + MapleData ret = this; + for (int x = 0; x < segments.length; x++) { + boolean foundChild = false; + for (MapleData child : ret.getChildren()) { + if (child.getName().equals(segments[x])) { + ret = child; + foundChild = true; + break; + } + } + if (!foundChild) { + return null; + } + } + return ret; + } + + @Override + public Object getData() { + return data; + } + + public void setName(String name) { + this.name = name; + } + + public void setType(MapleDataType type) { + this.type = type; + } + + public void setData(Object data) { + this.data = data; + } + + public void addChild(WZIMGEntry entry) { + children.add(entry); + } + + @Override + public Iterator iterator() { + return getChildren().iterator(); + } + + @Override + public String toString() { + return getName() + ":" + getData(); + } + + public MapleDataEntity getParent() { + return parent; + } + + public void finish() { + ((ArrayList) children).trimToSize(); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZIMGFile.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZIMGFile.java new file mode 100644 index 0000000000..bec06c78bd --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZIMGFile.java @@ -0,0 +1,227 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import java.awt.Point; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import tools.data.input.GenericSeekableLittleEndianAccessor; +import tools.data.input.RandomAccessByteStream; +import tools.data.input.SeekableLittleEndianAccessor; + +public class WZIMGFile { + private WZFileEntry file; + private WZIMGEntry root; + private boolean provideImages; + @SuppressWarnings ("unused") + private boolean modernImg; + + public WZIMGFile(File wzfile, WZFileEntry file, boolean provideImages, boolean modernImg) throws IOException { + RandomAccessFile raf = new RandomAccessFile(wzfile, "r"); + SeekableLittleEndianAccessor slea = new GenericSeekableLittleEndianAccessor(new RandomAccessByteStream(raf)); + slea.seek(file.getOffset()); + this.file = file; + this.provideImages = provideImages; + root = new WZIMGEntry(file.getParent()); + root.setName(file.getName()); + root.setType(MapleDataType.EXTENDED); + this.modernImg = modernImg; + parseExtended(root, slea, 0); + root.finish(); + raf.close(); + } + + protected void dumpImg(OutputStream out, SeekableLittleEndianAccessor slea) throws IOException { + DataOutputStream os = new DataOutputStream(out); + long oldPos = slea.getPosition(); + slea.seek(file.getOffset()); + for (int x = 0; x < file.getSize(); x++) { + os.write(slea.readByte()); + } + slea.seek(oldPos); + } + + public WZIMGEntry getRoot() { + return root; + } + + private void parse(WZIMGEntry entry, SeekableLittleEndianAccessor slea) { + byte marker = slea.readByte(); + switch (marker) { + case 0: { + String name = WZTool.readDecodedString(slea); + entry.setName(name); + break; + } + case 1: { + String name = WZTool.readDecodedStringAtOffsetAndReset(slea, file.getOffset() + slea.readInt()); + entry.setName(name); + break; + } + default: + System.out.println("Unknown Image identifier: " + marker + " at offset " + (slea.getPosition() - file.getOffset())); + } + marker = slea.readByte(); + switch (marker) { + case 0: + entry.setType(MapleDataType.IMG_0x00); + break; + case 2: + case 11: //??? no idea, since 0.49 + entry.setType(MapleDataType.SHORT); + entry.setData(Short.valueOf(slea.readShort())); + break; + case 3: + entry.setType(MapleDataType.INT); + entry.setData(Integer.valueOf(WZTool.readValue(slea))); + break; + case 4: + entry.setType(MapleDataType.FLOAT); + entry.setData(Float.valueOf(WZTool.readFloatValue(slea))); + break; + case 5: + entry.setType(MapleDataType.DOUBLE); + entry.setData(Double.valueOf(slea.readDouble())); + break; + case 8: + entry.setType(MapleDataType.STRING); + byte iMarker = slea.readByte(); + if (iMarker == 0) { + entry.setData(WZTool.readDecodedString(slea)); + } else if (iMarker == 1) { + entry.setData(WZTool.readDecodedStringAtOffsetAndReset(slea, slea.readInt() + file.getOffset())); + } else { + System.out.println("Unknown String type " + iMarker); + } + break; + case 9: + entry.setType(MapleDataType.EXTENDED); + long endOfExtendedBlock = slea.readInt(); + endOfExtendedBlock += slea.getPosition(); + parseExtended(entry, slea, endOfExtendedBlock); + break; + default: + System.out.println("Unknown Image type " + marker); + } + } + + private void parseExtended(WZIMGEntry entry, SeekableLittleEndianAccessor slea, long endOfExtendedBlock) { + byte marker = slea.readByte(); + String type; + switch (marker) { + case 0x73: + type = WZTool.readDecodedString(slea); + break; + case 0x1B: + type = WZTool.readDecodedStringAtOffsetAndReset(slea, file.getOffset() + slea.readInt()); + break; + default: + throw new RuntimeException("Unknown extended image identifier: " + marker + " at offset " + + (slea.getPosition() - file.getOffset())); + } + if (type.equals("Property")) { + entry.setType(MapleDataType.PROPERTY); + slea.readByte(); + slea.readByte(); + int children = WZTool.readValue(slea); + for (int i = 0; i < children; i++) { + WZIMGEntry cEntry = new WZIMGEntry(entry); + parse(cEntry, slea); + cEntry.finish(); + entry.addChild(cEntry); + } + } else if (type.equals("Canvas")) { + entry.setType(MapleDataType.CANVAS); + slea.readByte(); + marker = slea.readByte(); + if (marker == 0) { + // do nothing + } else if (marker == 1) { + slea.readByte(); + slea.readByte(); + int children = WZTool.readValue(slea); + for (int i = 0; i < children; i++) { + WZIMGEntry child = new WZIMGEntry(entry); + parse(child, slea); + child.finish(); + entry.addChild(child); + } + } else { + System.out.println("Canvas marker != 1 (" + marker + ")"); + } + int width = WZTool.readValue(slea); + int height = WZTool.readValue(slea); + int format = WZTool.readValue(slea); + int format2 = slea.readByte(); + slea.readInt(); + int dataLength = slea.readInt() - 1; + slea.readByte(); + if (provideImages) { + byte[] pngdata = slea.read(dataLength); + entry.setData(new PNGMapleCanvas(width, height, dataLength, format + format2, pngdata)); + } else { + entry.setData(new PNGMapleCanvas(width, height, dataLength, format + format2, null)); + slea.skip(dataLength); + } + } else if (type.equals("Shape2D#Vector2D")) { + entry.setType(MapleDataType.VECTOR); + int x = WZTool.readValue(slea); + int y = WZTool.readValue(slea); + entry.setData(new Point(x, y)); + } else if (type.equals("Shape2D#Convex2D")) { + int children = WZTool.readValue(slea); + for (int i = 0; i < children; i++) { + WZIMGEntry cEntry = new WZIMGEntry(entry); + parseExtended(cEntry, slea, 0); + cEntry.finish(); + entry.addChild(cEntry); + } + } else if (type.equals("Sound_DX8")) { + entry.setType(MapleDataType.SOUND); + slea.readByte(); + int dataLength = WZTool.readValue(slea); + WZTool.readValue(slea); // no clue what this is + int offset = (int) slea.getPosition(); + entry.setData(new ImgMapleSound(dataLength, offset - file.getOffset())); + slea.seek(endOfExtendedBlock); + } else if (type.equals("UOL")) { + entry.setType(MapleDataType.UOL); + slea.readByte(); + byte uolmarker = slea.readByte(); + switch (uolmarker) { + case 0: + entry.setData(WZTool.readDecodedString(slea)); + break; + case 1: + entry.setData(WZTool.readDecodedStringAtOffsetAndReset(slea, file.getOffset() + slea.readInt())); + break; + default: + System.out.println("Unknown UOL marker: " + uolmarker + " " + entry.getName()); + } + } else { + throw new RuntimeException("Unhandled extended type: " + type); + } + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZTool.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZTool.java new file mode 100644 index 0000000000..85e1c8d90b --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/WZTool.java @@ -0,0 +1,187 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; +import tools.data.input.LittleEndianAccessor; +import tools.data.input.SeekableLittleEndianAccessor; + +/* + * Ported Code, see WZFile.java for more info + */ +public class WZTool { + private static byte[] encKey; + + static { + byte[] iv = new byte[]{(byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b, + (byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b, + (byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b, + (byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b,}; + byte[] key = new byte[]{(byte) 0x13, 0x00, 0x00, 0x00, + (byte) 0x08, 0x00, 0x00, 0x00, + (byte) 0x06, 0x00, 0x00, 0x00, + (byte) 0xB4, 0x00, 0x00, 0x00, + (byte) 0x1B, 0x00, 0x00, 0x00, + (byte) 0x0F, 0x00, 0x00, 0x00, + (byte) 0x33, 0x00, 0x00, 0x00, + (byte) 0x52, 0x00, 0x00, 0x00 + }; + Cipher cipher = null; + SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); + try { + cipher = Cipher.getInstance("AES"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } + try { + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } + encKey = new byte[0xFFFF]; + for (int i = 0; i < (0xFFFF / 16); i++) { + try { + iv = cipher.doFinal(iv); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } + System.arraycopy(iv, 0, encKey, (i * 16), 16); + } + try { + iv = cipher.doFinal(iv); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } + System.arraycopy(iv, 0, encKey, 65520, 15); + } + + public static byte[] readListString(byte[] str) { + for (int i = 0; i < str.length; i++) { + str[i] = (byte) (str[i] ^ encKey[i]); + } + return str; + } + + public static String readDecodedString(LittleEndianAccessor llea) { + int strLength; + byte b = llea.readByte(); + if (b == 0x00) { + return ""; + } + if (b >= 0) { + if (b == 0x7F) { + strLength = llea.readInt(); + } else { + strLength = (int) b; + } + if (strLength < 0) { + return ""; + } + byte str[] = new byte[strLength * 2]; + for (int i = 0; i < strLength * 2; i++) { + str[i] = llea.readByte(); + } + return DecryptUnicodeStr(str); + } else { + if (b == -128) { + strLength = llea.readInt(); + } else { + strLength = -b; + } + if (strLength < 0) { + return ""; + } + byte str[] = new byte[strLength]; + for (int i = 0; i < strLength; i++) { + str[i] = llea.readByte(); + } + return DecryptAsciiStr(str); + } + } + + public static String DecryptAsciiStr(byte[] str) { + byte xorByte = (byte) 0xAA; + for (int i = 0; i < str.length; i++) { + str[i] = (byte) (str[i] ^ xorByte ^ encKey[i]); + xorByte++; + } + return new String(str); + } + + public static String DecryptUnicodeStr(byte[] str) { + int xorByte = 0xAAAA; + char[] charRet = new char[str.length / 2]; + for (int i = 0; i < str.length; i++) { + str[i] = (byte) (str[i] ^ encKey[i]); + } + for (int i = 0; i < (str.length / 2); i++) { + char toXor = (char) ((str[i] << 8) | str[i + 1]); + charRet[i] = (char) (toXor ^ xorByte); + xorByte++; + } + return String.valueOf(charRet); + } + + public static String readDecodedStringAtOffset(SeekableLittleEndianAccessor slea, int offset) { + slea.seek(offset); + return readDecodedString(slea); + } + + public static String readDecodedStringAtOffsetAndReset(SeekableLittleEndianAccessor slea, int offset) { + long pos = 0; + pos = slea.getPosition(); + slea.seek(offset); + String ret = readDecodedString(slea); + slea.seek(pos); + return ret; + } + + public static int readValue(LittleEndianAccessor lea) { + byte b = lea.readByte(); + if (b == -128) { + return lea.readInt(); + } else { + return ((int) b); + } + } + + public static float readFloatValue(LittleEndianAccessor lea) { + byte b = lea.readByte(); + if (b == -128) { + return lea.readFloat(); + } else { + return 0; + } + } +} \ No newline at end of file diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/XMLDomMapleData.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/XMLDomMapleData.java new file mode 100644 index 0000000000..151a04c2fd --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/XMLDomMapleData.java @@ -0,0 +1,219 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . + */ +package provider.wz; + +import java.awt.Point; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.text.NumberFormat; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import provider.MapleData; +import provider.MapleDataEntity; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +public class XMLDomMapleData implements MapleData { + private Node node; + private File imageDataDir; + private NumberFormat nf; + + public XMLDomMapleData(FileInputStream fis, File imageDataDir) { + try { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = documentBuilder.parse(fis); + this.node = document.getFirstChild(); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } catch (SAXException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + this.imageDataDir = imageDataDir; + this.nf = NumberFormat.getInstance(Locale.FRANCE); + } + + private XMLDomMapleData(Node node) { + this.node = node; + this.nf = NumberFormat.getInstance(Locale.FRANCE); + } + + @Override + public MapleData getChildByPath(String path) { + String segments[] = path.split("/"); + if (segments[0].equals("..")) { + return ((MapleData) getParent()).getChildByPath(path.substring(path.indexOf("/") + 1)); + } + + Node myNode = node; + for (int x = 0; x < segments.length; x++) { + NodeList childNodes = myNode.getChildNodes(); + boolean foundChild = false; + for (int i = 0; i < childNodes.getLength(); i++) { + Node childNode = childNodes.item(i); + if (childNode.getNodeType() == Node.ELEMENT_NODE && childNode.getAttributes().getNamedItem("name").getNodeValue().equals(segments[x])) { + myNode = childNode; + foundChild = true; + break; + } + } + if (!foundChild) { + return null; + } + } + XMLDomMapleData ret = new XMLDomMapleData(myNode); + ret.imageDataDir = new File(imageDataDir, getName() + "/" + path).getParentFile(); + return ret; + } + + @Override + public List getChildren() { + List ret = new ArrayList(); + NodeList childNodes = node.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node childNode = childNodes.item(i); + if (childNode.getNodeType() == Node.ELEMENT_NODE) { + XMLDomMapleData child = new XMLDomMapleData(childNode); + child.imageDataDir = new File(imageDataDir, getName()); + ret.add(child); + } + } + return ret; + } + + @Override + public Object getData() { + NamedNodeMap attributes = node.getAttributes(); + MapleDataType type = getType(); + switch (type) { + case DOUBLE: + case FLOAT: + case INT: + case SHORT: { + String value = attributes.getNamedItem("value").getNodeValue(); + Number nval; + + try { + nval = nf.parse(value); + } + catch(java.text.ParseException pe) { + pe.printStackTrace(); + nval = 0.0f; + } + + switch (type) { + case DOUBLE: + return nval.doubleValue(); + case FLOAT: + return nval.floatValue(); + case INT: + return nval.intValue(); + case SHORT: + return nval.shortValue(); + default: + return null; + } + } + case STRING: + case UOL: { + String value = attributes.getNamedItem("value").getNodeValue(); + return value; + } + case VECTOR: { + String x = attributes.getNamedItem("x").getNodeValue(); + String y = attributes.getNamedItem("y").getNodeValue(); + return new Point(Integer.parseInt(x), Integer.parseInt(y)); + } + case CANVAS: { + String width = attributes.getNamedItem("width").getNodeValue(); + String height = attributes.getNamedItem("height").getNodeValue(); + return new FileStoredPngMapleCanvas(Integer.parseInt(width), Integer.parseInt(height), new File( + imageDataDir, getName() + ".png")); + } + default: + return null; + } + } + + @Override + public MapleDataType getType() { + String nodeName = node.getNodeName(); + if (nodeName.equals("imgdir")) { + return MapleDataType.PROPERTY; + } else if (nodeName.equals("canvas")) { + return MapleDataType.CANVAS; + } else if (nodeName.equals("convex")) { + return MapleDataType.CONVEX; + } else if (nodeName.equals("sound")) { + return MapleDataType.SOUND; + } else if (nodeName.equals("uol")) { + return MapleDataType.UOL; + } else if (nodeName.equals("double")) { + return MapleDataType.DOUBLE; + } else if (nodeName.equals("float")) { + return MapleDataType.FLOAT; + } else if (nodeName.equals("int")) { + return MapleDataType.INT; + } else if (nodeName.equals("short")) { + return MapleDataType.SHORT; + } else if (nodeName.equals("string")) { + return MapleDataType.STRING; + } else if (nodeName.equals("vector")) { + return MapleDataType.VECTOR; + } else if (nodeName.equals("null")) { + return MapleDataType.IMG_0x00; + } + return null; + } + + @Override + public MapleDataEntity getParent() { + Node parentNode = node.getParentNode(); + if (parentNode.getNodeType() == Node.DOCUMENT_NODE) { + return null; + } + XMLDomMapleData parentData = new XMLDomMapleData(parentNode); + parentData.imageDataDir = imageDataDir.getParentFile(); + return parentData; + } + + @Override + public String getName() { + return node.getAttributes().getNamedItem("name").getNodeValue(); + } + + @Override + public Iterator iterator() { + return getChildren().iterator(); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/provider/wz/XMLWZFile.java b/tools/MapleSkillbookChanceFetcher/src/provider/wz/XMLWZFile.java new file mode 100644 index 0000000000..2a7694fdc9 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/provider/wz/XMLWZFile.java @@ -0,0 +1,85 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package provider.wz; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import provider.MapleData; +import provider.MapleDataDirectoryEntry; +import provider.MapleDataProvider; + +public class XMLWZFile implements MapleDataProvider { + private File root; + private WZDirectoryEntry rootForNavigation; + + public XMLWZFile(File fileIn) { + root = fileIn; + rootForNavigation = new WZDirectoryEntry(fileIn.getName(), 0, 0, null); + fillMapleDataEntitys(root, rootForNavigation); + } + + private void fillMapleDataEntitys(File lroot, WZDirectoryEntry wzdir) { + for (File file : lroot.listFiles()) { + String fileName = file.getName(); + if (file.isDirectory() && !fileName.endsWith(".img")) { + WZDirectoryEntry newDir = new WZDirectoryEntry(fileName, 0, 0, wzdir); + wzdir.addDirectory(newDir); + fillMapleDataEntitys(file, newDir); + } else if (fileName.endsWith(".xml")) { + wzdir.addFile(new WZFileEntry(fileName.substring(0, fileName.length() - 4), 0, 0, wzdir)); + } + } + } + + @Override + public MapleData getData(String path) { + File dataFile = new File(root, path + ".xml"); + File imageDataDir = new File(root, path); + if (!dataFile.exists()) { + return null;//bitches + } + FileInputStream fis; + try { + fis = new FileInputStream(dataFile); + } catch (FileNotFoundException e) { + throw new RuntimeException("Datafile " + path + " does not exist in " + root.getAbsolutePath()); + } + final XMLDomMapleData domMapleData; + try { + domMapleData = new XMLDomMapleData(fis, imageDataDir.getParentFile()); + } finally { + try { + fis.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return domMapleData; + } + + @Override + public MapleDataDirectoryEntry getRoot() { + return rootForNavigation; + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/DatabaseConnection.java b/tools/MapleSkillbookChanceFetcher/src/tools/DatabaseConnection.java new file mode 100644 index 0000000000..9dcd4e6545 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/DatabaseConnection.java @@ -0,0 +1,51 @@ +package tools; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +/** + * @author Frz (Big Daddy) + * @author The Real Spookster (some modifications to this beautiful code) + */ +public class DatabaseConnection { + private static String DB_URL = "jdbc:mysql://localhost:3306/heavenms"; + private static String DB_USER = "root"; + private static String DB_PASS = ""; + + public static final int RETURN_GENERATED_KEYS = 1; + + private static ThreadLocal con = new ThreadLocalConnection(); + + public static Connection getConnection() { + Connection c = con.get(); + try { + c.getMetaData(); + } catch (SQLException e) { // connection is dead, therefore discard old object 5ever + con.remove(); + c = con.get(); + } + return c; + } + + private static class ThreadLocalConnection extends ThreadLocal { + + @Override + protected Connection initialValue() { + try { + Class.forName("com.mysql.jdbc.Driver"); // touch the mysql driver + } catch (ClassNotFoundException e) { + System.out.println("[SEVERE] SQL Driver Not Found. Consider death by clams."); + e.printStackTrace(); + return null; + } + try { + return DriverManager.getConnection(DB_URL, DB_USER, DB_PASS); + } catch (SQLException e) { + System.out.println("[SEVERE] Unable to make database connection."); + e.printStackTrace(); + return null; + } + } + } +} \ No newline at end of file diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/HexTool.java b/tools/MapleSkillbookChanceFetcher/src/tools/HexTool.java new file mode 100644 index 0000000000..8cc0c8aa84 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/HexTool.java @@ -0,0 +1,79 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools; + +import java.io.ByteArrayOutputStream; + +public class HexTool { + private static final char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + private static String toString(byte byteValue) { + int tmp = byteValue << 8; + char[] retstr = new char[]{HEX[(tmp >> 12) & 0x0F], HEX[(tmp >> 8) & 0x0F]}; + return String.valueOf(retstr); + } + + public static String toString(byte[] bytes) { + StringBuilder hexed = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + hexed.append(toString(bytes[i])); + hexed.append(' '); + } + return hexed.substring(0, hexed.length() - 1); + } + + public static byte[] getByteArrayFromHexString(String hex) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int nexti = 0; + int nextb = 0; + boolean highoc = true; + outer: + for (;;) { + int number = -1; + while (number == -1) { + if (nexti == hex.length()) { + break outer; + } + char chr = hex.charAt(nexti); + if (chr >= '0' && chr <= '9') { + number = chr - '0'; + } else if (chr >= 'a' && chr <= 'f') { + number = chr - 'a' + 10; + } else if (chr >= 'A' && chr <= 'F') { + number = chr - 'A' + 10; + } else { + number = -1; + } + nexti++; + } + if (highoc) { + nextb = number << 4; + highoc = false; + } else { + nextb |= number; + highoc = true; + baos.write(nextb); + } + } + return baos.toByteArray(); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/Pair.java b/tools/MapleSkillbookChanceFetcher/src/tools/Pair.java new file mode 100644 index 0000000000..f88718cbe3 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/Pair.java @@ -0,0 +1,121 @@ +/* +This file is part of the OdinMS Maple Story Server +Copyright (C) 2008 ~ 2010 Patrick Huy +Matthias Butz +Jan Christian Meyer + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License 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 . + */ +package tools; + +/** + * Represents a pair of values. + * + * @author Frz + * @since Revision 333 + * @version 1.0 + * + * @param The type of the left value. + * @param The type of the right value. + */ +public class Pair { + + public E left; + public F right; + + /** + * Class constructor - pairs two objects together. + * + * @param left The left object. + * @param right The right object. + */ + public Pair(E left, F right) { + this.left = left; + this.right = right; + } + + /** + * Gets the left value. + * + * @return The left value. + */ + public E getLeft() { + return left; + } + + /** + * Gets the right value. + * + * @return The right value. + */ + public F getRight() { + return right; + } + + /** + * Turns the pair into a string. + * + * @return Each value of the pair as a string joined by a colon. + */ + @Override + public String toString() { + return left.toString() + ":" + right.toString(); + } + + /** + * Gets the hash code of this pair. + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((left == null) ? 0 : left.hashCode()); + result = prime * result + ((right == null) ? 0 : right.hashCode()); + return result; + } + + /** + * Checks to see if two pairs are equal. + */ + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Pair other = (Pair) obj; + if (left == null) { + if (other.left != null) { + return false; + } + } else if (!left.equals(other.left)) { + return false; + } + if (right == null) { + if (other.right != null) { + return false; + } + } else if (!right.equals(other.right)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/input/ByteArrayByteStream.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/ByteArrayByteStream.java new file mode 100644 index 0000000000..eac7de21ea --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/ByteArrayByteStream.java @@ -0,0 +1,72 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.input; + +import java.io.IOException; +import tools.HexTool; + +public class ByteArrayByteStream implements SeekableInputStreamBytestream { + private int pos = 0; + private long bytesRead = 0; + private byte[] arr; + + public ByteArrayByteStream(byte[] arr) { + this.arr = arr; + } + + @Override + public long getPosition() { + return pos; + } + + @Override + public void seek(long offset) throws IOException { + pos = (int) offset; + } + + @Override + public long getBytesRead() { + return bytesRead; + } + + @Override + public int readByte() { + bytesRead++; + return ((int) arr[pos++]) & 0xFF; + } + + @Override + public String toString() { + String nows = "kevintjuh93 pwns";//I lol'd + if (arr.length - pos > 0) { + byte[] now = new byte[arr.length - pos]; + System.arraycopy(arr, pos, now, 0, arr.length - pos); + nows = HexTool.toString(now); + } + return "All: " + HexTool.toString(arr) + "\nNow: " + nows; + } + + @Override + public long available() { + return arr.length - pos; + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/input/ByteInputStream.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/ByteInputStream.java new file mode 100644 index 0000000000..107f71843e --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/ByteInputStream.java @@ -0,0 +1,35 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.input; + +/** + * Represents an abstract stream of bytes. + * + * @author Frz + * @version 1.0 + * @since Revision 323 + */ +public interface ByteInputStream { + int readByte(); + long getBytesRead(); + long available(); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/input/GenericLittleEndianAccessor.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/GenericLittleEndianAccessor.java new file mode 100644 index 0000000000..d08a9b8374 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/GenericLittleEndianAccessor.java @@ -0,0 +1,239 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.input; + +import java.awt.Point; +import java.io.ByteArrayOutputStream; + +/** + * Provides a generic interface to a Little Endian stream of bytes. + * + * @version 1.0 + * @author Frz + * @since Revision 323 + */ +public class GenericLittleEndianAccessor implements LittleEndianAccessor { + private ByteInputStream bs; + + /** + * Class constructor - Wraps the accessor around a stream of bytes. + * + * @param bs The byte stream to wrap the accessor around. + */ + public GenericLittleEndianAccessor(ByteInputStream bs) { + this.bs = bs; + } + + /** + * Read a single byte from the stream. + * + * @return The byte read. + * @see tools.data.input.ByteInputStream#readByte + */ + @Override + public byte readByte() { + return (byte) bs.readByte(); + } + + /** + * Reads an integer from the stream. + * + * @return The integer read. + */ + @Override + public int readInt() { + return bs.readByte() + (bs.readByte() << 8) + (bs.readByte() << 16) + (bs.readByte() << 24); + } + + /** + * Reads a short integer from the stream. + * + * @return The short read. + */ + @Override + public short readShort() { + return (short) (bs.readByte() + (bs.readByte() << 8)); + } + + /** + * Reads a single character from the stream. + * + * @return The character read. + */ + @Override + public char readChar() { + return (char) readShort(); + } + + /** + * Reads a long integer from the stream. + * + * @return The long integer read. + */ + @Override + public long readLong() { + long byte1 = bs.readByte(); + long byte2 = bs.readByte(); + long byte3 = bs.readByte(); + long byte4 = bs.readByte(); + long byte5 = bs.readByte(); + long byte6 = bs.readByte(); + long byte7 = bs.readByte(); + long byte8 = bs.readByte(); + return (byte8 << 56) + (byte7 << 48) + (byte6 << 40) + (byte5 << 32) + (byte4 << 24) + (byte3 << 16) + (byte2 << 8) + byte1; + } + + /** + * Reads a floating point integer from the stream. + * + * @return The float-type integer read. + */ + @Override + public float readFloat() { + return Float.intBitsToFloat(readInt()); + } + + /** + * Reads a double-precision integer from the stream. + * + * @return The double-type integer read. + */ + @Override + public double readDouble() { + return Double.longBitsToDouble(readLong()); + } + + /** + * Reads an ASCII string from the stream with length n. + * + * @param n Number of characters to read. + * @return The string read. + */ + public final String readAsciiString(int n) { + char ret[] = new char[n]; + for (int x = 0; x < n; x++) { + ret[x] = (char) readByte(); + } + return String.valueOf(ret); + } + + /** + * Reads a null-terminated string from the stream. + * + * @return The string read. + */ + public final String readNullTerminatedAsciiString() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte b; + while (true) { + b = readByte(); + if (b == 0) { + break; + } + baos.write(b); + } + byte[] buf = baos.toByteArray(); + char[] chrBuf = new char[buf.length]; + for (int x = 0; x < buf.length; x++) { + chrBuf[x] = (char) buf[x]; + } + return String.valueOf(chrBuf); + } + + /** + * Gets the number of bytes read from the stream so far. + * + * @return A long integer representing the number of bytes read. + * @see tools.data.input.ByteInputStream#getBytesRead() + */ + public long getBytesRead() { + return bs.getBytesRead(); + } + + /** + * Reads a MapleStory convention lengthed ASCII string. + * This consists of a short integer telling the length of the string, + * then the string itself. + * + * @return The string read. + */ + @Override + public String readMapleAsciiString() { + return readAsciiString(readShort()); + } + + /** + * Reads num bytes off the stream. + * + * @param num The number of bytes to read. + * @return An array of bytes with the length of num + */ + @Override + public byte[] read(int num) { + byte[] ret = new byte[num]; + for (int x = 0; x < num; x++) { + ret[x] = readByte(); + } + return ret; + } + + /** + * Reads a MapleStory Position information. + * This consists of 2 short integer. + * + * @return The Position read. + */ + @Override + public final Point readPos() { + final int x = readShort(); + final int y = readShort(); + return new Point(x, y); + } + + /** + * Skips the current position of the stream num bytes ahead. + * + * @param num Number of bytes to skip. + */ + @Override + public void skip(int num) { + for (int x = 0; x < num; x++) { + readByte(); + } + } + + /** + * @see tools.data.input.ByteInputStream#available + */ + @Override + public long available() { + return bs.available(); + } + + /** + * @see java.lang.Object#toString + */ + @Override + public String toString() { + return bs.toString(); + } +} \ No newline at end of file diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/input/GenericSeekableLittleEndianAccessor.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/GenericSeekableLittleEndianAccessor.java new file mode 100644 index 0000000000..fdd147d796 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/GenericSeekableLittleEndianAccessor.java @@ -0,0 +1,91 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.input; + +import java.io.IOException; + +/** + * Provides an abstract accessor to a generic Little Endian byte stream. This + * accessor is seekable. + * + * @author Frz + * @version 1.0 + * @since Revision 323 + * @see tools.data.input.GenericLittleEndianAccessor + */ +public class GenericSeekableLittleEndianAccessor extends GenericLittleEndianAccessor implements SeekableLittleEndianAccessor { + private SeekableInputStreamBytestream bs; + + /** + * Class constructor + * Provide a seekable input stream to wrap this object around. + * + * @param bs The byte stream to wrap this around. + */ + public GenericSeekableLittleEndianAccessor(SeekableInputStreamBytestream bs) { + super(bs); + this.bs = bs; + } + + /** + * Seek the pointer to offset + * + * @param offset The offset to seek to. + * @see tools.data.input.SeekableInputStreamBytestream#seek + */ + @Override + public void seek(long offset) { + try { + bs.seek(offset); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("Seek failed " + e); + } + } + + /** + * Get the current position of the pointer. + * + * @return The current position of the pointer as a long integer. + * @see tools.data.input.SeekableInputStreamBytestream#getPosition + */ + @Override + public long getPosition() { + try { + return bs.getPosition(); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("getPosition failed" + e); + return -1; + } + } + + /** + * Skip num number of bytes in the stream. + * + * @param num The number of bytes to skip. + */ + @Override + public void skip(int num) { + seek(getPosition() + num); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/input/InputStreamByteStream.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/InputStreamByteStream.java new file mode 100644 index 0000000000..70aef3489f --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/InputStreamByteStream.java @@ -0,0 +1,93 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.input; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Provides an abstract wrapper to a stream of bytes. + * + * @author Frz + * @version 1.0 + * @since Revision 323 + */ +public class InputStreamByteStream implements ByteInputStream { + private InputStream is; + private long read = 0; + + /** + * Class constructor. + * Provide an input stream to wrap this around. + * + * @param is The input stream to wrap this object around. + */ + public InputStreamByteStream(InputStream is) { + this.is = is; + } + + /** + * Reads the next byte from the stream. + * + * @return Then next byte in the stream. + */ + @Override + public int readByte() { + int temp; + try { + temp = is.read(); + if (temp == -1) { + throw new RuntimeException("EOF"); + } + read++; + return temp; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Gets the number of bytes read from the stream. + * + * @return The number of bytes read as a long integer. + */ + @Override + public long getBytesRead() { + return read; + } + + /** + * Returns the number of bytes left in the stream. + * + * @return The number of bytes available for reading as a long integer. + */ + @Override + public long available() { + try { + return is.available(); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("ERROR" + e); + return 0; + } + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/input/LittleEndianAccessor.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/LittleEndianAccessor.java new file mode 100644 index 0000000000..f991dbf537 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/LittleEndianAccessor.java @@ -0,0 +1,45 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.input; + +import java.awt.Point; + +/** + * @author Frz + */ +public interface LittleEndianAccessor { + byte readByte(); + char readChar(); + short readShort(); + int readInt(); + Point readPos(); + long readLong(); + void skip(int num); + byte[] read(int num); + float readFloat(); + double readDouble(); + String readAsciiString(int n); + String readNullTerminatedAsciiString(); + String readMapleAsciiString(); + long getBytesRead(); + long available(); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/input/RandomAccessByteStream.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/RandomAccessByteStream.java new file mode 100644 index 0000000000..c0004be17f --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/RandomAccessByteStream.java @@ -0,0 +1,84 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.input; + +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * Provides an abstract layer to a byte stream. This layer can be accessed + * randomly. + * + * @author Frz + * @version 1.0 + * @since Revision 323 + */ +public class RandomAccessByteStream implements SeekableInputStreamBytestream { + private RandomAccessFile raf; + private long read = 0; + + public RandomAccessByteStream(RandomAccessFile raf) { + super(); + this.raf = raf; + } + + @Override + public int readByte() { + int temp; + try { + temp = raf.read(); + if (temp == -1) { + throw new RuntimeException("EOF"); + } + read++; + return temp; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void seek(long offset) throws IOException { + raf.seek(offset); + } + + @Override + public long getPosition() throws IOException { + return raf.getFilePointer(); + } + + @Override + public long getBytesRead() { + return read; + } + + @Override + public long available() { + try { + return raf.length() - raf.getFilePointer(); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("ERROR " + e); + return 0; + } + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/input/SeekableInputStreamBytestream.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/SeekableInputStreamBytestream.java new file mode 100644 index 0000000000..f4922dc876 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/SeekableInputStreamBytestream.java @@ -0,0 +1,51 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.input; + +import java.io.IOException; + +/** + * Provides an abstract interface to a stream of bytes. This stream can be + * seeked. + * + * @author Frz + * @version 1.0 + * @since 299 + */ +public interface SeekableInputStreamBytestream extends ByteInputStream { + /** + * Seeks the stream by the specified offset. + * + * @param offset + * Number of bytes to seek. + * @throws IOException + */ + void seek(long offset) throws IOException; + + /** + * Gets the current position of the stream. + * + * @return The stream position as a long integer. + * @throws IOException + */ + long getPosition() throws IOException; +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/input/SeekableLittleEndianAccessor.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/SeekableLittleEndianAccessor.java new file mode 100644 index 0000000000..16b2317f7a --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/input/SeekableLittleEndianAccessor.java @@ -0,0 +1,27 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.input; + +public interface SeekableLittleEndianAccessor extends LittleEndianAccessor { + void seek(long offset); + long getPosition(); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/output/BAOSByteOutputStream.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/BAOSByteOutputStream.java new file mode 100644 index 0000000000..80cbc9301e --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/BAOSByteOutputStream.java @@ -0,0 +1,56 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.output; + +import java.io.ByteArrayOutputStream; + +/** + * Uses a byte array to output a stream of bytes. + * + * @author Frz + * @version 1.0 + * @since Revision 352 + */ +class BAOSByteOutputStream implements ByteOutputStream { + private ByteArrayOutputStream baos; + + /** + * Class constructor - Wraps the stream around a Java BAOS. + * + * @param baos The ByteArrayOutputStream to wrap this around. + */ + BAOSByteOutputStream(ByteArrayOutputStream baos) { + super(); + this.baos = baos; + } + + /** + * Writes a byte to the stream. + * + * @param b The byte to write to the stream. + * @see tools.data.output.ByteOutputStream#writeByte(byte) + */ + @Override + public void writeByte(byte b) { + baos.write(b); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/output/ByteOutputStream.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/ByteOutputStream.java new file mode 100644 index 0000000000..0df7ca7753 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/ByteOutputStream.java @@ -0,0 +1,38 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.output; + +/** + * Provides an interface to an output stream of bytes. + * + * @author Frz + * @since Revision 323 + * @version 1.0 + */ +interface ByteOutputStream { + /** + * Writes a byte to the stream. + * + * @param b The byte to write. + */ + void writeByte(byte b); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/output/GenericLittleEndianWriter.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/GenericLittleEndianWriter.java new file mode 100644 index 0000000000..e804fd8000 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/GenericLittleEndianWriter.java @@ -0,0 +1,183 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.output; + +import java.awt.Point; +import java.nio.charset.Charset; + +/** + * Provides a generic writer of a little-endian sequence of bytes. + * + * @author Frz + * @version 1.0 + * @since Revision 323 + */ +public class GenericLittleEndianWriter implements LittleEndianWriter { + private static Charset ASCII = Charset.forName("US-ASCII"); + private ByteOutputStream bos; + + /** + * Class constructor - Protected to prevent instantiation with no arguments. + */ + protected GenericLittleEndianWriter() { + // Blah! + } + + /** + * Sets the byte-output stream for this instance of the object. + * + * @param bos The new output stream to set. + */ + void setByteOutputStream(ByteOutputStream bos) { + this.bos = bos; + } + + /** + * Write an array of bytes to the stream. + * + * @param b The bytes to write. + */ + @Override + public void write(byte[] b) { + for (int x = 0; x < b.length; x++) { + bos.writeByte(b[x]); + } + } + + /** + * Write a byte to the stream. + * + * @param b The byte to write. + */ + @Override + public void write(byte b) { + bos.writeByte(b); + } + + /** + * Write a byte in integer form to the stream. + * + * @param b The byte as an Integer to write. + */ + @Override + public void write(int b) { + bos.writeByte((byte) b); + } + + @Override + public void skip(int b) { + write(new byte[b]); + } + + /** + * Write a short integer to the stream. + * + * @param i The short integer to write. + */ + @Override + public void writeShort(int i) { + bos.writeByte((byte) (i & 0xFF)); + bos.writeByte((byte) ((i >>> 8) & 0xFF)); + } + + /** + * Writes an integer to the stream. + * + * @param i The integer to write. + */ + @Override + public void writeInt(int i) { + bos.writeByte((byte) (i & 0xFF)); + bos.writeByte((byte) ((i >>> 8) & 0xFF)); + bos.writeByte((byte) ((i >>> 16) & 0xFF)); + bos.writeByte((byte) ((i >>> 24) & 0xFF)); + } + + /** + * Writes an ASCII string the the stream. + * + * @param s The ASCII string to write. + */ + @Override + public void writeAsciiString(String s) { + write(s.getBytes(ASCII)); + } + + /** + * Writes a maple-convention ASCII string to the stream. + * + * @param s The ASCII string to use maple-convention to write. + */ + @Override + public void writeMapleAsciiString(String s) { + writeShort((short) s.length()); + writeAsciiString(s); + } + + /** + * Writes a null-terminated ASCII string to the stream. + * + * @param s The ASCII string to write. + */ + @Override + public void writeNullTerminatedAsciiString(String s) { + writeAsciiString(s); + write(0); + } + + /** + * Write a long integer to the stream. + * @param l The long integer to write. + */ + @Override + public void writeLong(long l) { + bos.writeByte((byte) (l & 0xFF)); + bos.writeByte((byte) ((l >>> 8) & 0xFF)); + bos.writeByte((byte) ((l >>> 16) & 0xFF)); + bos.writeByte((byte) ((l >>> 24) & 0xFF)); + bos.writeByte((byte) ((l >>> 32) & 0xFF)); + bos.writeByte((byte) ((l >>> 40) & 0xFF)); + bos.writeByte((byte) ((l >>> 48) & 0xFF)); + bos.writeByte((byte) ((l >>> 56) & 0xFF)); + } + + /** + * Writes a 2D 4 byte position information + * + * @param s The Point position to write. + */ + @Override + public void writePos(Point s) { + writeShort(s.x); + writeShort(s.y); + } + + /** + * Writes a boolean true ? 1 : 0 + * + * @param b The boolean to write. + */ + @Override + public void writeBool(final boolean b) { + write(b ? 1 : 0); + } +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/output/LittleEndianWriter.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/LittleEndianWriter.java new file mode 100644 index 0000000000..f17bd7c72e --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/LittleEndianWriter.java @@ -0,0 +1,114 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . + */ +package tools.data.output; + +import java.awt.Point; + +/** + * Provides an interface to a writer class that writes a little-endian sequence + * of bytes. + * + * @author Frz + * @version 1.0 + * @since Revision 323 + */ +public interface LittleEndianWriter { + + /** + * Write an array of bytes to the sequence. + * + * @param b The bytes to write. + */ + public void write(byte b[]); + + /** + * Write a byte to the sequence. + * + * @param b The byte to write. + */ + public void write(byte b); + + /** + * Write a byte in integer form to the sequence. + * + * @param b The byte as an Integer to write. + */ + public void write(int b); + + public void skip(int b); + + /** + * Writes an integer to the sequence. + * + * @param i The integer to write. + */ + public void writeInt(int i); + + /** + * Write a short integer to the sequence. + * + * @param s The short integer to write. + */ + public void writeShort(int s); + + /** + * Write a long integer to the sequence. + * + * @param l The long integer to write. + */ + public void writeLong(long l); + + /** + * Writes an ASCII string the the sequence. + * + * @param s The ASCII string to write. + */ + void writeAsciiString(String s); + + /** + * Writes a null-terminated ASCII string to the sequence. + * + * @param s The ASCII string to write. + */ + void writeNullTerminatedAsciiString(String s); + + /** + * Writes a maple-convention ASCII string to the sequence. + * + * @param s The ASCII string to use maple-convention to write. + */ + void writeMapleAsciiString(String s); + + /** + * Writes a 2D 4 byte position information + * + * @param s The Point position to write. + */ + void writePos(Point s); + + /** + * Writes a boolean true ? 1 : 0 + * + * @param b The boolean to write. + */ + void writeBool(final boolean b); +} diff --git a/tools/MapleSkillbookChanceFetcher/src/tools/data/output/MaplePacketLittleEndianWriter.java b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/MaplePacketLittleEndianWriter.java new file mode 100644 index 0000000000..b02365ec62 --- /dev/null +++ b/tools/MapleSkillbookChanceFetcher/src/tools/data/output/MaplePacketLittleEndianWriter.java @@ -0,0 +1,73 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + 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 . +*/ +package tools.data.output; + +import java.io.ByteArrayOutputStream; +import tools.HexTool; + +/** + * Writes a maplestory-packet little-endian stream of bytes. + * + * @author Frz + * @version 1.0 + * @since Revision 352 + */ +public class MaplePacketLittleEndianWriter extends GenericLittleEndianWriter { + private ByteArrayOutputStream baos; + + /** + * Constructor - initializes this stream with a default size. + */ + public MaplePacketLittleEndianWriter() { + this(32); + } + + /** + * Constructor - initializes this stream with size size. + * + * @param size The size of the underlying stream. + */ + public MaplePacketLittleEndianWriter(int size) { + this.baos = new ByteArrayOutputStream(size); + setByteOutputStream(new BAOSByteOutputStream(baos)); + } + + /** + * Gets a MaplePacket instance representing this + * sequence of bytes. + * + * @return A MaplePacket with the bytes in this stream. + */ + public byte[] getPacket() { + return baos.toByteArray(); + } + + /** + * Changes this packet into a human-readable hexadecimal stream of bytes. + * + * @return This packet as hex digits. + */ + @Override + public String toString() { + return HexTool.toString(baos.toByteArray()); + } +} diff --git a/wz/Etc.wz/MapNeighbors.img.xml b/wz/Etc.wz/MapNeighbors.img.xml new file mode 100644 index 0000000000..a37628bca5 --- /dev/null +++ b/wz/Etc.wz/MapNeighbors.img.xml @@ -0,0 +1,59793 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Quest.wz/QuestInfo.img.xml b/wz/Quest.wz/QuestInfo.img.xml index 4bf2cd8705..296d0a4c72 100644 --- a/wz/Quest.wz/QuestInfo.img.xml +++ b/wz/Quest.wz/QuestInfo.img.xml @@ -2541,7 +2541,7 @@ - + diff --git a/wz/Quest.wz/Say.img.xml b/wz/Quest.wz/Say.img.xml index 93d550fc3d..63ab21999a 100644 --- a/wz/Quest.wz/Say.img.xml +++ b/wz/Quest.wz/Say.img.xml @@ -4503,7 +4503,7 @@ - + diff --git a/wz/String.wz/Consume.img.xml b/wz/String.wz/Consume.img.xml index 7a2dae9b20..a1782e3a6c 100644 --- a/wz/String.wz/Consume.img.xml +++ b/wz/String.wz/Consume.img.xml @@ -5978,10 +5978,6 @@ - - - -