From dc73cb00de5035d07d32309e358642a85b4e88c7 Mon Sep 17 00:00:00 2001 From: ronancpl Date: Sat, 22 Dec 2018 00:12:13 -0200 Subject: [PATCH] Mob banish on touch + GMS-like cosmetics + Clear missing names Fixed erroneous clean slate scroll block on equipments having certain properties. Implemented a log for bought cash items from Cash Shop. Implemented mob's player banish by touch (e. g. used by mobs from the Hypnotize quest). Thoroughly revised stylist/surgeon NPCs, adding several missing GMS-like cosmetic contents. Thoroughly revised String.wz names having missing item content throughout the WZ files. Revised missing name info for several faces throughout the WZ files. Solved a possible deadlock case related with a player vision's spawn object method. Fixed an issue with "Movement allowed only within account" items being given as "Untradeable" by NPCs. Happy Holidays and Great New Year everyone!!! --- .gitignore | 20 +- docs/issues.txt | 1 + docs/mychanges_ptbr.txt | 28 +- handbook/Equip/Face.txt | 1018 +++--- handbook/Equip/Hair.txt | 44 +- handbook/Use.txt | 2 +- scripts/npc/1012002.js | 2 +- scripts/npc/1012103.js | 22 +- scripts/npc/1012104.js | 53 +- scripts/npc/1012105.js | 2 +- scripts/npc/1012117.js | 72 +- scripts/npc/1052004.js | 21 +- scripts/npc/1052005.js | 21 +- scripts/npc/1052100.js | 21 +- scripts/npc/1052101.js | 56 +- scripts/npc/2010001.js | 20 +- scripts/npc/2010002.js | 22 +- scripts/npc/2012007.js | 76 +- scripts/npc/2012008.js | 2 +- scripts/npc/2012009.js | 22 +- scripts/npc/2040019.js | 22 +- scripts/npc/2041007.js | 22 +- scripts/npc/2041009.js | 54 +- scripts/npc/2041010.js | 22 +- scripts/npc/2041013.js | 2 +- scripts/npc/2090100.js | 22 +- scripts/npc/2090101.js | 22 +- scripts/npc/2090102.js | 2 +- scripts/npc/2090103.js | 103 +- scripts/npc/2090104.js | 76 +- scripts/npc/2100005.js | 26 +- scripts/npc/2100006.js | 24 +- scripts/npc/2100008.js | 142 +- scripts/npc/2100009.js | 104 +- scripts/npc/9120100.js | 22 +- scripts/npc/9120101.js | 59 +- scripts/npc/9120102.js | 163 + scripts/npc/9120103.js | 110 + scripts/npc/9200100.js | 63 +- scripts/npc/9200101.js | 63 +- scripts/npc/9200102.js | 63 +- scripts/npc/9201015.js | 22 +- scripts/npc/9201016.js | 120 +- scripts/npc/9201017.js | 63 +- scripts/npc/9201018.js | 22 +- scripts/npc/9201019.js | 22 +- scripts/npc/9201039.js | 25 +- scripts/npc/9201061.js | 14 +- scripts/npc/9201062.js | 73 +- scripts/npc/9201063.js | 22 +- scripts/npc/9201064.js | 24 +- scripts/npc/9201065.js | 2 +- scripts/npc/9201069.js | 22 +- scripts/npc/9201070.js | 23 +- scripts/npc/9270023.js | 20 +- scripts/npc/9270024.js | 20 +- scripts/npc/9270026.js | 57 +- scripts/npc/9270036.js | 28 +- scripts/npc/9270037.js | 20 +- scripts/npc/9270043.js | 4 +- scripts/npc/9977777.js | 6 +- scripts/npc/gachaponold.js | 2 +- src/client/MapleBuffStat.java | 2 +- src/client/MapleCharacter.java | 27 +- src/client/command/CommandsExecutor.java | 2 +- .../commands/gm0/JoinEventCommand.java | 1 + .../commands/gm0/LeaveEventCommand.java | 1 + .../command/commands/gm1/GotoCommand.java | 1 + ...d.java => ClearSavedLocationsCommand.java} | 22 +- .../command/commands/gm2/JailCommand.java | 1 + .../command/commands/gm2/ReachCommand.java | 1 + .../command/commands/gm2/SetStatCommand.java | 3 +- .../command/commands/gm2/SummonCommand.java | 2 + .../command/commands/gm2/WarpCommand.java | 1 + .../commands/gm3/ReloadMapCommand.java | 1 + .../commands/gm3/WarpSnowBallCommand.java | 1 + src/net/PacketProcessor.java | 1 + src/net/opcodes/RecvOpcode.java | 1 + .../handlers/CashOperationHandler.java | 20 +- .../channel/handlers/ItemRewardHandler.java | 5 +- .../handlers/MobBanishPlayerHandler.java | 45 + src/net/server/world/World.java | 4 +- src/provider/wz/XMLWZFile.java | 2 +- src/scripting/npc/NPCConversationManager.java | 29 + src/server/MapleItemInformationProvider.java | 34 +- src/server/MapleStatEffect.java | 6 + src/server/life/MapleMonster.java | 7 +- src/server/maps/MapleMap.java | 16 +- src/server/maps/MapleMapFactory.java | 2 +- src/tools/FilePrinter.java | 1 + tools/MapleCashCosmeticsChecker/build.xml | 73 + .../lib/care/amoria/face.txt | 4 + .../lib/care/amoria/hair.txt | 4 + .../lib/care/ariant/face.txt | 4 + .../lib/care/ariant/hair.txt | 4 + .../lib/care/cbd/face.txt | 4 + .../lib/care/cbd/hair.txt | 4 + .../lib/care/henesys/face.txt | 4 + .../lib/care/henesys/hair.txt | 6 + .../lib/care/kerning_city/face.txt | 4 + .../lib/care/kerning_city/hair.txt | 6 + .../lib/care/ludibrium/face.txt | 4 + .../lib/care/ludibrium/hair.txt | 6 + .../lib/care/mu_lung/face.txt | 4 + .../lib/care/mu_lung/hair.txt | 4 + .../lib/care/nlc/face.txt | 4 + .../lib/care/nlc/hair.txt | 4 + .../lib/care/orbis/face.txt | 4 + .../lib/care/orbis/hair.txt | 6 + .../lib/care/showa/face.txt | 4 + .../lib/care/showa/hair.txt | 4 + .../MapleCashCosmeticsChecker/lib/colors.txt | 10 + .../MapleCashCosmeticsChecker/lib/result.txt | 213 ++ tools/MapleCashCosmeticsChecker/manifest.mf | 3 + .../MapleCashCosmeticsChecker.java | 722 +++++ .../src/maplecashcosmeticschecker/Pair.java | 121 + tools/MapleCashCosmeticsFetcher/build.xml | 73 + tools/MapleCashCosmeticsFetcher/manifest.mf | 3 + .../MapleCashCosmeticsFetcher.java | 145 + .../src/provider/MapleCanvas.java | 30 + .../src/provider/MapleData.java | 34 + .../src/provider/MapleDataDirectoryEntry.java | 34 + .../src/provider/MapleDataEntity.java | 31 + .../src/provider/MapleDataEntry.java | 33 + .../src/provider/MapleDataFileEntry.java | 30 + .../src/provider/MapleDataProvider.java | 27 + .../provider/MapleDataProviderFactory.java | 55 + .../src/provider/MapleDataTool.java | 145 + .../provider/wz/FileStoredPngMapleCanvas.java | 70 + .../src/provider/wz/ImgMapleSound.java | 39 + .../src/provider/wz/ListWZFile.java | 86 + .../src/provider/wz/MapleDataType.java | 26 + .../src/provider/wz/PNGMapleCanvas.java | 151 + .../src/provider/wz/WZDirectoryEntry.java | 68 + .../src/provider/wz/WZEntry.java | 61 + .../src/provider/wz/WZFile.java | 154 + .../src/provider/wz/WZFileEntry.java | 42 + .../src/provider/wz/WZIMGEntry.java | 118 + .../src/provider/wz/WZIMGFile.java | 227 ++ .../src/provider/wz/WZTool.java | 187 ++ .../src/provider/wz/XMLDomMapleData.java | 219 ++ .../src/provider/wz/XMLWZFile.java | 85 + .../src/tools/ArrayMap.java | 149 + .../src/tools/DatabaseConnection.java | 51 + .../src/tools/FilePrinter.java | 188 ++ .../src/tools/HexTool.java | 79 + .../tools/MapleItemInformationProvider.java | 664 ++++ .../src/tools/Pair.java | 121 + .../src/tools/StringUtil.java | 128 + .../tools/data/input/ByteArrayByteStream.java | 72 + .../src/tools/data/input/ByteInputStream.java | 35 + .../input/GenericLittleEndianAccessor.java | 239 ++ .../GenericSeekableLittleEndianAccessor.java | 91 + .../data/input/InputStreamByteStream.java | 93 + .../data/input/LittleEndianAccessor.java | 45 + .../data/input/RandomAccessByteStream.java | 84 + .../input/SeekableInputStreamBytestream.java | 51 + .../input/SeekableLittleEndianAccessor.java | 27 + .../data/output/BAOSByteOutputStream.java | 56 + .../tools/data/output/ByteOutputStream.java | 38 + .../output/GenericLittleEndianWriter.java | 183 ++ .../tools/data/output/LittleEndianWriter.java | 114 + .../output/MaplePacketLittleEndianWriter.java | 73 + tools/MapleEmptyItemWzChecker/build.xml | 73 + tools/MapleEmptyItemWzChecker/lib/Report.txt | 149 + tools/MapleEmptyItemWzChecker/manifest.mf | 3 + .../MapleEmptyItemWzChecker.java | 476 +++ wz/Character.wz/Face/00020816.img.xml | 432 +++ wz/Character.wz/Face/00020817.img.xml | 432 +++ wz/Character.wz/Face/00021817.img.xml | 432 +++ wz/Character.wz/Face/00021820.img.xml | 434 +++ wz/Etc.wz/Commodity.img.xml | 74 +- wz/Item.wz/Consume/0202.img.xml | 5 +- wz/Map.wz/Map/Map9/925010000.img.xml | 2 +- wz/Map.wz/Map/Map9/925010100.img.xml | 2 +- wz/Map.wz/Map/Map9/925010200.img.xml | 2 +- wz/Map.wz/Map/Map9/925010300.img.xml | 2887 ++++++++--------- wz/Skill.wz/1510.img.xml | 2 +- wz/String.wz/Cash.img.xml | 94 +- wz/String.wz/Consume.img.xml | 129 - wz/String.wz/Eqp.img.xml | 1238 ++++--- wz/String.wz/Etc.img.xml | 111 - wz/String.wz/Ins.img.xml | 7 - wz/String.wz/Pet.img.xml | 15 - 184 files changed, 12897 insertions(+), 3647 deletions(-) create mode 100644 scripts/npc/9120102.js create mode 100644 scripts/npc/9120103.js rename src/client/command/commands/gm2/{MesosCommand.java => ClearSavedLocationsCommand.java} (63%) create mode 100644 src/net/server/channel/handlers/MobBanishPlayerHandler.java create mode 100644 tools/MapleCashCosmeticsChecker/build.xml create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/amoria/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/amoria/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/ariant/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/ariant/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/cbd/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/cbd/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/henesys/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/henesys/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/kerning_city/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/kerning_city/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/ludibrium/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/ludibrium/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/mu_lung/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/mu_lung/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/nlc/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/nlc/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/orbis/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/orbis/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/showa/face.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/care/showa/hair.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/colors.txt create mode 100644 tools/MapleCashCosmeticsChecker/lib/result.txt create mode 100644 tools/MapleCashCosmeticsChecker/manifest.mf create mode 100644 tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/MapleCashCosmeticsChecker.java create mode 100644 tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/Pair.java create mode 100644 tools/MapleCashCosmeticsFetcher/build.xml create mode 100644 tools/MapleCashCosmeticsFetcher/manifest.mf create mode 100644 tools/MapleCashCosmeticsFetcher/src/maplecashcosmeticsfetcher/MapleCashCosmeticsFetcher.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/MapleCanvas.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/MapleData.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/MapleDataDirectoryEntry.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/MapleDataEntity.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/MapleDataEntry.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/MapleDataFileEntry.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/MapleDataProvider.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/MapleDataProviderFactory.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/MapleDataTool.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/FileStoredPngMapleCanvas.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/ImgMapleSound.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/ListWZFile.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/MapleDataType.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/PNGMapleCanvas.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/WZDirectoryEntry.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/WZEntry.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/WZFile.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/WZFileEntry.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/WZIMGEntry.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/WZIMGFile.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/WZTool.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/XMLDomMapleData.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/provider/wz/XMLWZFile.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/ArrayMap.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/DatabaseConnection.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/FilePrinter.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/HexTool.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/MapleItemInformationProvider.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/Pair.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/StringUtil.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/input/ByteArrayByteStream.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/input/ByteInputStream.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/input/GenericLittleEndianAccessor.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/input/GenericSeekableLittleEndianAccessor.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/input/InputStreamByteStream.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/input/LittleEndianAccessor.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/input/RandomAccessByteStream.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/input/SeekableInputStreamBytestream.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/input/SeekableLittleEndianAccessor.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/output/BAOSByteOutputStream.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/output/ByteOutputStream.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/output/GenericLittleEndianWriter.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/output/LittleEndianWriter.java create mode 100644 tools/MapleCashCosmeticsFetcher/src/tools/data/output/MaplePacketLittleEndianWriter.java create mode 100644 tools/MapleEmptyItemWzChecker/build.xml create mode 100644 tools/MapleEmptyItemWzChecker/lib/Report.txt create mode 100644 tools/MapleEmptyItemWzChecker/manifest.mf create mode 100644 tools/MapleEmptyItemWzChecker/src/mapleemptyitemwzchecker/MapleEmptyItemWzChecker.java create mode 100644 wz/Character.wz/Face/00020816.img.xml create mode 100644 wz/Character.wz/Face/00020817.img.xml create mode 100644 wz/Character.wz/Face/00021817.img.xml create mode 100644 wz/Character.wz/Face/00021820.img.xml diff --git a/.gitignore b/.gitignore index fe7e410792..dd9b39182b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,14 @@ /tools/MapleBossHpBarFetcher/dist/ /tools/MapleBossHpBarFetcher/nbproject/ +/tools/MapleCashCosmeticsChecker/build/ +/tools/MapleCashCosmeticsChecker/dist/ +/tools/MapleCashCosmeticsChecker/nbproject/ + +/tools/MapleCashCosmeticsFetcher/build/ +/tools/MapleCashCosmeticsFetcher/dist/ +/tools/MapleCashCosmeticsFetcher/nbproject/ + /tools/MapleCashDropFetcher/build/ /tools/MapleCashDropFetcher/dist/ /tools/MapleCashDropFetcher/nbproject/ @@ -27,9 +35,13 @@ /tools/MapleCouponInstaller/dist/ /tools/MapleCouponInstaller/nbproject/ -/tools/MapleDojoUpdate/build/ -/tools/MapleDojoUpdate/dist/ -/tools/MapleDojoUpdate/nbproject/ +/tools/MapleDojoUpdater/build/ +/tools/MapleDojoUpdater/dist/ +/tools/MapleDojoUpdater/nbproject/ + +/tools/MapleEmptyItemWzChecker/build/ +/tools/MapleEmptyItemWzChecker/dist/ +/tools/MapleEmptyItemWzChecker/nbproject/ /tools/MapleEquipmentOmnileveler/build/ /tools/MapleEquipmentOmnileveler/dist/ @@ -100,4 +112,4 @@ /tools/SpiderDropFetcher/nbproject/ /out -*.onetoc2 +*.onetoc2 \ No newline at end of file diff --git a/docs/issues.txt b/docs/issues.txt index 67dc79ce4e..bea3d980b2 100644 --- a/docs/issues.txt +++ b/docs/issues.txt @@ -12,6 +12,7 @@ Known issues: - If there are multiple bosses that shows HPBar on the map, if a player hits more than one the HPBar may start flickering on the screen. - Sometimes battleship may behave oddly with the enhanced buff system, making the character d/c in certain scenarios. - Dragon Roar doesn't show the stun effect to players. +- Snipe will show much higher damage value than actually applicable to the attacker. - Some monster status such as freeze and weapon/magic reflect doesn't behave properly in certain scenarios. Freeze seems to not work on mobs with low OID or are starters from server boot time. - On low-end connections, things such as command summoning a player that is currently logging in (already visible to other players) may cause the player to freeze, consequently freezing the account as well since the server-side disconnection doesn't happen. - Reportedly, there are cases where mob positions fail to sync between player's client-view. diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index be3bc589a5..20d681ed6c 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -1504,4 +1504,30 @@ Corrigido sistema de server messages/boss HP bar não atuando de forma esperada Corrigido um problema com o gerador de code coupons, que levava em consideração hifens ("-") no código do cupom, além de um problema de overflow na data de expiração. 03 Dezembro 2018, -Melhorado desenho do worldmap de Victoria, na região do metro de Kerning. \ No newline at end of file +Melhorado desenho do worldmap de Victoria, na região do metro de Kerning. + +07 - 08 Dezembro 2018, +Corrigido clean slates não checando limites corretamente, levando a bloqueio de uso de clean slates em itens com certas propriedades. +Removido comando ambíguo Mesos. +Corrigido limites inválidos no comando SetStat. +Implementado log em compra de itens de cash. +Implementado retorno ao stage 1 na questline Hypnotize ao tocar em mobs. +Implementado diversas novas ferramentas, que auxiliam na busca e processamento de itens cosméticos ainda não catalogados pelos NPCs estilistas/cirurgiões. +Implementado e revisado scripts dos NPCs estilistas/cirurgiões, adicionando itens cosméticos ainda não referenciados nos scripts. +Nova ferramenta: MapleEmptyItemWzChecker, cuja finalidade é reportar itemids presentes nos arquivos Wz onde faltam registros de nome ou de propriedades. +Aplicado nova reformatação nos wzs de itens e strings. Removido possibilidade de itens sem nome ou dados nos arquivos, que poderiam levar potencialmente a quebras no cliente. + +09 - 12 Dezembro 2018, +Removido todas as opções de tingimento de cores em todas as cidades com salão de beleza. Agora possui um comportamento GMS-like. +Aplicado correções nos nomes das faces em ambos String.wz e handbook. +Resolvido um possível caso de deadlock com a interação de spawn de objetos para os jogadores no mapa. +Melhorado itens cosméticos dos NPCs estilistas/cirurgiões, garantindo o uso de grande parte dos cosméticos disponíveis na fonte, enquanto seguindo o padrão GMS-like. + +13 - 14 Dezembro 2018, +Corrigido possibilidade de mudança de face/cabelo podendo levar jogadores a adquirir itens inexistentes, invariavelmente tornando o personagem inacessível. +Corrigido equipamentos labelados como "somente movimentação dentro da conta" sendo incorretamente gerados com a flag de "Untradeable" também. +Corrigido comandos que transportam jogadores entre mapas não levando em consideração retorno ao mapa onde estavam antes do transporte, para os casos onde se espera retornar ao mapa anterior (e.g. FM's). + +21 Dezembro 2018, +Cosméticos já usados pelo jogador não estão mais disponíveis nos estilistas/cirurgiões. +Corrigido alguns itens estéticos não sendo devidamente disponibilizados se jogadores usam algum cosmético de cor não-default. \ No newline at end of file diff --git a/handbook/Equip/Face.txt b/handbook/Equip/Face.txt index d2ad5ee3c6..e428bcfab0 100644 --- a/handbook/Equip/Face.txt +++ b/handbook/Equip/Face.txt @@ -1,540 +1,540 @@ -20000 - Male 1 (Black) - (no description) -20001 - Male 2 (Black) - (no description) +20000 - Motivated Look (Black) - (no description) +20001 - Perplexed Stare (Black) - (no description) 20002 - Leisure Look (Black) - (no description) -20003 - Male 4 (Black) - (no description) -20004 - Male 5 (Black) - (no description) -20005 - Male 6 (Black) - (no description) -20006 - Male 7 (Black) - (no description) -20007 - Male 8 (Black) - (no description) -20008 - Male 9 (Black) - (no description) -20009 - Male JP (Black) - (no description) -20010 - Male JP (Black) - (no description) -20011 - Male JP (Black) - (no description) -20012 - Male 10 (Black) - (no description) -20013 - Male JP (Black) - (no description) -20014 - Male 11 (Black) - (no description) -20016 - Male Face 12 - (no description) -20017 - Male Face 14 - (no description) -20018 - Male 12 (Black) - (no description) -20019 - Male 13 (Black) - (no description) +20003 - Dramatic Face (Black) - (no description) +20004 - Rebel's Fire (Black) - (no description) +20005 - Alert Face (Black) - (no description) +20006 - Babyface Pout (Black) - (no description) +20007 - Sad Innocence (Black) - (no description) +20008 - Worrisome Glare (Black) - (no description) +20009 - Smart Aleck (Black) - (no description) +20010 - Anger's Blaze (Black) - (no description) +20011 - Cool Guy Gaze (Black) - (no description) +20012 - Curious Dog (Black) - (no description) +20013 - Insomniac Daze (Black) - (no description) +20014 - Look of Wonder (Black) - (no description) +20015 - Eye of the Lion (Black) - (no description) +20016 - Ghostface Stare (Black) - (no description) +20017 - Demure Poise (Black) - (no description) +20018 - Champion Focus (Black) - (no description) +20019 - Irritable Face (Black) - (no description) 20020 - Fierce Edge (Black) - (no description) -20021 - Male Face JP - (no description) -20022 - Male Face 16 - (no description) -20023 - malefaceGL(Black) - (no description) -20024 - Awakening (Black) - (no description) -20100 - Male 1 (Blue) - (no description) -20101 - Male 2 (Blue) - (no description) +20021 - Overjoyed Smile (Black) - (no description) +20022 - Child's Play (Black) - (no description) +20023 - Hypnotized Look (Black) - (no description) +20024 - Intense Stare (Black) - (no description) +20025 - Edge of Emotion (Black) - (no description) +20026 - Shuteye (Black) - (no description) +20027 - Pensive Look (Black) - (no description) +20028 - Sarcastic Face (Black) - (no description) +20029 - Shade of Cool (Black) - (no description) +20031 - Fearful Glance (Black) - (no description) +20032 - Undecided Face (Black) - (no description) +20100 - Motivated Look (Blue) - (no description) +20101 - Perplexed Stare (Blue) - (no description) 20102 - Leisure Look (Blue) - (no description) -20103 - Male 4 (Blue) - (no description) -20015 - Male 15 - (no description) -20115 - Male 15 - (no description) -20215 - Male 15 - (no description) -20315 - Male 15 - (no description) -20415 - Male 15 - (no description) -20515 - Male 15 - (no description) -20615 - Male 15 - (no description) -20715 - Male 15 - (no description) -20104 - Male 5 (Blue) - (no description) -20105 - Male 6 (Blue) - (no description) -20106 - Male 7 (Blue) - (no description) -20107 - Male 8 (Blue) - (no description) -20108 - Male 9 (Blue) - (no description) -20109 - Male JP (Blue) - (no description) -20110 - Male JP (Blue) - (no description) -20111 - Male JP (Blue) - (no description) -20112 - Male 10 (Blue) - (no description) -20113 - Male JP (Blue) - (no description) -20114 - Male 11 (Blue) - (no description) -20116 - Male Face 12 - (no description) -20117 - Male Face 14 - (no description) -20118 - Male 12 (Blue) - (no description) -20119 - Male 13 (Blue) - (no description) +20103 - Dramatic Face (Blue) - (no description) +20104 - Rebel's Fire (Blue) - (no description) +20105 - Alert Face (Blue) - (no description) +20106 - Babyface Pout (Blue) - (no description) +20107 - Sad Innocence (Blue) - (no description) +20108 - Worrisome Glare (Blue) - (no description) +20109 - Smart Aleck (Blue) - (no description) +20110 - Anger's Blaze (Blue) - (no description) +20111 - Cool Guy Gaze (Blue) - (no description) +20112 - Curious Dog (Blue) - (no description) +20113 - Insomniac Daze (Blue) - (no description) +20114 - Look of Wonder (Blue) - (no description) +20115 - Eye of the Lion (Blue) - (no description) +20116 - Ghostface Stare (Blue) - (no description) +20117 - Demure Poise (Blue) - (no description) +20118 - Champion Focus (Blue) - (no description) +20119 - Irritable Face (Blue) - (no description) 20120 - Fierce Edge (Blue) - (no description) -20121 - Male Face JP - (no description) -20122 - Male Face 16(Blue Eyes) - (no description) -20123 - malefaceGL(Blue) - (no description) -20124 - Awakening (Blue) - (no description) -20200 - Male 1 (Red) - (no description) -20201 - Male 2 (Red) - (no description) +20121 - Overjoyed Smile (Blue) - (no description) +20122 - Child's Play (Blue) - (no description) +20123 - Hypnotized Look (Blue) - (no description) +20124 - Intense Stare (Blue) - (no description) +20125 - Edge of Emotion (Blue) - (no description) +20126 - Shuteye (Blue) - (no description) +20127 - Pensive Look (Blue) - (no description) +20128 - Sarcastic Face (Blue) - (no description) +20129 - Shade of Cool (Blue) - (no description) +20131 - Fearful Glance (Blue) - (no description) +20132 - Undecided Face (Blue) - (no description) +20200 - Motivated Look (Red) - (no description) +20201 - Perplexed Stare (Red) - (no description) 20202 - Leisure Look (Red) - (no description) -20203 - Male 4 (Red) - (no description) -20204 - Male 5 (Red) - (no description) -20205 - Male 6 (Red) - (no description) -20206 - Male 7 (Red) - (no description) -20207 - Male 8 (Red) - (no description) -20208 - Male 9 (Red) - (no description) -20209 - Male JP (Red) - (no description) -20210 - Male JP (Red) - (no description) -20211 - Male JP (Red) - (no description) -20212 - Male 10 (Red) - (no description) -20213 - Male JP (Red) - (no description) -20214 - Male 11 (Red) - (no description) -20216 - Male Face 12 - (no description) -20217 - Male Face 14 - (no description) -20218 - Male 12 (Red) - (no description) -20219 - Male 13 (Red) - (no description) +20203 - Dramatic Face (Red) - (no description) +20204 - Rebel's Fire (Red) - (no description) +20205 - Alert Face (Red) - (no description) +20206 - Babyface Pout (Red) - (no description) +20207 - Sad Innocence (Red) - (no description) +20208 - Worrisome Glare (Red) - (no description) +20209 - Smart Aleck (Red) - (no description) +20210 - Anger's Blaze (Red) - (no description) +20211 - Cool Guy Gaze (Red) - (no description) +20212 - Curious Dog (Red) - (no description) +20213 - Insomniac Daze (Red) - (no description) +20214 - Look of Wonder (Red) - (no description) +20215 - Eye of the Lion (Red) - (no description) +20216 - Ghostface Stare (Red) - (no description) +20217 - Demure Poise (Red) - (no description) +20218 - Champion Focus (Red) - (no description) +20219 - Irritable Face (Red) - (no description) 20220 - Fierce Edge (Red) - (no description) -20221 - Male Face JP - (no description) -20222 - Male Face 16(Red Eyes) - (no description) -20223 - malefaceGL(Red) - (no description) -20224 - Awakening (Red) - (no description) -20300 - Male 1 (Green) - (no description) -20301 - Male 2 (Green) - (no description) +20221 - Overjoyed Smile (Red) - (no description) +20222 - Child's Play (Red) - (no description) +20223 - Hypnotized Look (Red) - (no description) +20224 - Intense Stare (Red) - (no description) +20225 - Edge of Emotion (Red) - (no description) +20226 - Shuteye (Red) - (no description) +20227 - Pensive Look (Red) - (no description) +20228 - Sarcastic Face (Red) - (no description) +20229 - Shade of Cool (Red) - (no description) +20231 - Fearful Glance (Red) - (no description) +20232 - Undecided Face (Red) - (no description) +20300 - Motivated Look (Green) - (no description) +20301 - Perplexed Stare (Green) - (no description) 20302 - Leisure Look (Green) - (no description) -20303 - Male 4 (Green) - (no description) -20304 - Male 5 (Green) - (no description) -20305 - Male 6 (Green) - (no description) -20306 - Male 7 (Green) - (no description) -20307 - Male 8 (Green) - (no description) -20308 - Male 9 (Green) - (no description) -20309 - Male JP (Green) - (no description) -20310 - Male JP (Green) - (no description) -20311 - Male JP (Green) - (no description) -20312 - Male 10 (Green) - (no description) -20313 - Male JP (Green) - (no description) -20314 - Male 11 (Green) - (no description) -20316 - Male Face 12 - (no description) -20317 - Male Face 14 - (no description) -20318 - Male 12 (Green) - (no description) -20319 - Male 13 (Green) - (no description) +20303 - Dramatic Face (Green) - (no description) +20304 - Rebel's Fire (Green) - (no description) +20305 - Alert Face (Green) - (no description) +20306 - Babyface Pout (Green) - (no description) +20307 - Sad Innocence (Green) - (no description) +20308 - Worrisome Glare (Green) - (no description) +20309 - Smart Aleck (Green) - (no description) +20310 - Anger's Blaze (Green) - (no description) +20311 - Cool Guy Gaze (Green) - (no description) +20312 - Curious Dog (Green) - (no description) +20313 - Insomniac Daze (Green) - (no description) +20314 - Look of Wonder (Green) - (no description) +20315 - Eye of the Lion (Green) - (no description) +20316 - Ghostface Stare (Green) - (no description) +20317 - Demure Poise (Green) - (no description) +20318 - Champion Focus (Green) - (no description) +20319 - Irritable Face (Green) - (no description) 20320 - Fierce Edge (Green) - (no description) -20321 - Male Face JP - (no description) -20322 - Male Face 16(Green Eyes) - (no description) -20323 - malefaceGL(Green) - (no description) -20324 - Awakening (Green) - (no description) -20400 - Male 1 (Hazel) - (no description) -20401 - Male 2 (Hazel) - (no description) +20321 - Overjoyed Smile (Green) - (no description) +20322 - Child's Play (Green) - (no description) +20323 - Hypnotized Look (Green) - (no description) +20324 - Intense Stare (Green) - (no description) +20325 - Edge of Emotion (Green) - (no description) +20326 - Shuteye (Green) - (no description) +20327 - Pensive Look (Green) - (no description) +20328 - Sarcastic Face (Green) - (no description) +20329 - Shade of Cool (Green) - (no description) +20331 - Fearful Glance (Green) - (no description) +20332 - Undecided Face (Green) - (no description) +20400 - Motivated Look (Hazel) - (no description) +20401 - Perplexed Stare (Hazel) - (no description) 20402 - Leisure Look (Hazel) - (no description) -20403 - Male 4 (Hazel) - (no description) -20404 - Male 5 (Hazel) - (no description) -20405 - Male 6 (Hazel) - (no description) -20406 - Male 7 (Hazel) - (no description) -20407 - Male 8 (Hazel) - (no description) -20408 - Male 9 (Hazel) - (no description) -20409 - Male JP (Hazel) - (no description) -20410 - Male JP (Hazel) - (no description) -20411 - Male JP (Hazel) - (no description) -20412 - Male 10 (Hazel) - (no description) -20413 - Male JP (Hazel) - (no description) -20414 - Male 11 (Hazel) - (no description) -20416 - Male Face 12 - (no description) -20417 - Male Face 14 - (no description) -20418 - Male 12 (Hazel) - (no description) -20419 - Male 13 (Hazel) - (no description) +20403 - Dramatic Face (Hazel) - (no description) +20404 - Rebel's Fire (Hazel) - (no description) +20405 - Alert Face (Hazel) - (no description) +20406 - Babyface Pout (Hazel) - (no description) +20407 - Sad Innocence (Hazel) - (no description) +20408 - Worrisome Glare (Hazel) - (no description) +20409 - Smart Aleck (Hazel) - (no description) +20410 - Anger's Blaze (Hazel) - (no description) +20411 - Cool Guy Gaze (Hazel) - (no description) +20412 - Curious Dog (Hazel) - (no description) +20413 - Insomniac Daze (Hazel) - (no description) +20414 - Look of Wonder (Hazel) - (no description) +20415 - Eye of the Lion (Hazel) - (no description) +20416 - Ghostface Stare (Hazel) - (no description) +20417 - Demure Poise (Hazel) - (no description) +20418 - Champion Focus (Hazel) - (no description) +20419 - Irritable Face (Hazel) - (no description) 20420 - Fierce Edge (Hazel) - (no description) -20421 - Male Face JP - (no description) -20422 - Male Face 16(Brown Eyes) - (no description) -20423 - malefaceGL(Hazel) - (no description) -20424 - Awakening (Hazel) - (no description) -20500 - Male 1 (Sapphire) - (no description) -20501 - Male 2 (Sapphire) - (no description) +20421 - Overjoyed Smile (Hazel) - (no description) +20422 - Child's Play (Hazel) - (no description) +20423 - Hypnotized Look (Hazel) - (no description) +20424 - Intense Stare (Hazel) - (no description) +20425 - Edge of Emotion (Hazel) - (no description) +20426 - Shuteye (Hazel) - (no description) +20427 - Pensive Look (Hazel) - (no description) +20428 - Sarcastic Face (Hazel) - (no description) +20429 - Shade of Cool (Hazel) - (no description) +20431 - Fearful Glance (Hazel) - (no description) +20432 - Undecided Face (Hazel) - (no description) +20500 - Motivated Look (Sapphire) - (no description) +20501 - Perplexed Stare (Sapphire) - (no description) 20502 - Leisure Look (Sapphire) - (no description) -20503 - Male 4 (Sapphire) - (no description) -20504 - Male 5 (Sapphire) - (no description) -20505 - Male 6 (Sapphire) - (no description) -20506 - Male 7 (Sapphire) - (no description) -20507 - Male 8 (Sapphire) - (no description) -20508 - Male 9 (Sapphire) - (no description) -20509 - Male JP (Sapphire) - (no description) -20510 - Male JP (Sapphire) - (no description) -20511 - Male JP (Sapphire) - (no description) -20512 - Male 10 (Sapphire) - (no description) -20513 - Male JP (Sapphire) - (no description) -20514 - Male 11 (Sapphire) - (no description) -20516 - Male Face 12 - (no description) -20517 - Male Face 14 - (no description) -20518 - Male 12 (Sapphire) - (no description) -20519 - Male 13 (Sapphire) - (no description) +20503 - Dramatic Face (Sapphire) - (no description) +20504 - Rebel's Fire (Sapphire) - (no description) +20505 - Alert Face (Sapphire) - (no description) +20506 - Babyface Pout (Sapphire) - (no description) +20507 - Sad Innocence (Sapphire) - (no description) +20508 - Worrisome Glare (Sapphire) - (no description) +20509 - Smart Aleck (Sapphire) - (no description) +20510 - Anger's Blaze (Sapphire) - (no description) +20511 - Cool Guy Gaze (Sapphire) - (no description) +20512 - Curious Dog (Sapphire) - (no description) +20513 - Insomniac Daze (Sapphire) - (no description) +20514 - Look of Wonder (Sapphire) - (no description) +20515 - Eye of the Lion (Sapphire) - (no description) +20516 - Ghostface Stare (Sapphire) - (no description) +20517 - Demure Poise (Sapphire) - (no description) +20518 - Champion Focus (Sapphire) - (no description) +20519 - Irritable Face (Sapphire) - (no description) 20520 - Fierce Edge (Sapphire) - (no description) -20521 - Male Face JP - (no description) -20522 - Male Face 16(Sapphire Eyes) - (no description) -20523 - malefaceGL(Sapphire) - (no description) -20524 - Awakening (Sapphire) - (no description) -20600 - Male 1 (Violet) - (no description) -20601 - Male 2 (Violet) - (no description) +20521 - Overjoyed Smile (Sapphire) - (no description) +20522 - Child's Play (Sapphire) - (no description) +20523 - Hypnotized Look (Sapphire) - (no description) +20524 - Intense Stare (Sapphire) - (no description) +20525 - Edge of Emotion (Sapphire) - (no description) +20526 - Shuteye (Sapphire) - (no description) +20527 - Pensive Look (Sapphire) - (no description) +20528 - Sarcastic Face (Sapphire) - (no description) +20529 - Shade of Cool (Sapphire) - (no description) +20531 - Fearful Glance (Sapphire) - (no description) +20532 - Undecided Face (Sapphire) - (no description) +20600 - Motivated Look (Violet) - (no description) +20601 - Perplexed Stare (Violet) - (no description) 20602 - Leisure Look (Violet) - (no description) -20603 - Male 4 (Violet) - (no description) -20604 - Male 5 (Violet) - (no description) -20605 - Male 6 (Violet) - (no description) -20606 - Male 7 (Violet) - (no description) -20607 - Male 8 (Violet) - (no description) -20608 - Male 9 (Violet) - (no description) -20609 - Male JP (Violet) - (no description) -20610 - Male JP (Violet) - (no description) -20611 - Male JP (Violet) - (no description) -20612 - Male 10 (Violet) - (no description) -20613 - Male JP (Violet) - (no description) -20614 - Male 11 (Violet) - (no description) -20616 - Male Face 12 - (no description) -20617 - Male Face 14 - (no description) -20618 - Male 12 (Violet) - (no description) -20619 - Male 13 (Violet) - (no description) +20603 - Dramatic Face (Violet) - (no description) +20604 - Rebel's Fire (Violet) - (no description) +20605 - Alert Face (Violet) - (no description) +20606 - Babyface Pout (Violet) - (no description) +20607 - Sad Innocence (Violet) - (no description) +20608 - Worrisome Glare (Violet) - (no description) +20609 - Smart Aleck (Violet) - (no description) +20610 - Anger's Blaze (Violet) - (no description) +20611 - Cool Guy Gaze (Violet) - (no description) +20612 - Curious Dog (Violet) - (no description) +20613 - Insomniac Daze (Violet) - (no description) +20614 - Look of Wonder (Violet) - (no description) +20615 - Eye of the Lion (Violet) - (no description) +20616 - Ghostface Stare (Violet) - (no description) +20617 - Demure Poise (Violet) - (no description) +20618 - Champion Focus (Violet) - (no description) +20619 - Irritable Face (Violet) - (no description) 20620 - Fierce Edge (Violet) - (no description) -20621 - Male Face JP - (no description) -20622 - Male Face 16(Violet Eyes) - (no description) -20623 - malefaceGL(Violet) - (no description) -20624 - Awakening (Violet) - (no description) -20700 - Male 1 (Amethyst) - (no description) -20701 - Male 2 (Amethyst) - (no description) +20621 - Overjoyed Smile (Violet) - (no description) +20622 - Child's Play (Violet) - (no description) +20623 - Hypnotized Look (Violet) - (no description) +20624 - Intense Stare (Violet) - (no description) +20625 - Edge of Emotion (Violet) - (no description) +20626 - Shuteye (Violet) - (no description) +20627 - Pensive Look (Violet) - (no description) +20628 - Sarcastic Face (Violet) - (no description) +20629 - Shade of Cool (Violet) - (no description) +20631 - Fearful Glance (Violet) - (no description) +20632 - Undecided Face (Violet) - (no description) +20700 - Motivated Look (Amethyst) - (no description) +20701 - Perplexed Stare (Amethyst) - (no description) 20702 - Leisure Look (Amethyst) - (no description) -20703 - Male 4 (Amethyst) - (no description) -20704 - Male 5 (Amethyst) - (no description) -20705 - Male 6 (Amethyst) - (no description) -20706 - Male 7 (Amethyst) - (no description) -20707 - Male 8 (Amethyst) - (no description) -20708 - Male 9 (Amethyst) - (no description) -20709 - Male JP (Amethyst) - (no description) -20710 - Male JP (Amethyst) - (no description) -20711 - Male JP (Amethyst) - (no description) -20712 - Male 10 (Amethyst) - (no description) -20713 - Male JP (Amethyst) - (no description) -20714 - Male 11 (Amethyst) - (no description) -20716 - Male Face 12 - (no description) -20717 - Male Face 14 - (no description) -20718 - Male 12 (Amethyst) - (no description) -20719 - Male 13 (Amethyst) - (no description) +20703 - Dramatic Face (Amethyst) - (no description) +20704 - Rebel's Fire (Amethyst) - (no description) +20705 - Alert Face (Amethyst) - (no description) +20706 - Babyface Pout (Amethyst) - (no description) +20707 - Sad Innocence (Amethyst) - (no description) +20708 - Worrisome Glare (Amethyst) - (no description) +20709 - Smart Aleck (Amethyst) - (no description) +20710 - Anger's Blaze (Amethyst) - (no description) +20711 - Cool Guy Gaze (Amethyst) - (no description) +20712 - Curious Dog (Amethyst) - (no description) +20713 - Insomniac Daze (Amethyst) - (no description) +20714 - Look of Wonder (Amethyst) - (no description) +20715 - Eye of the Lion (Amethyst) - (no description) +20716 - Ghostface Stare (Amethyst) - (no description) +20717 - Demure Poise (Amethyst) - (no description) +20718 - Champion Focus (Amethyst) - (no description) +20719 - Irritable Face (Amethyst) - (no description) 20720 - Fierce Edge (Amethyst) - (no description) -20721 - Male Face JP - (no description) -20722 - Male Face 16(Amethyst Eyes) - (no description) -20723 - malefaceGL(Amethyst) - (no description) -20724 - Awakening (Amethyst) - (no description) -21000 - Female 1 (Black) - (no description) -21001 - Female 2 (Black) - (no description) -21002 - Female 3 (Black) - (no description) -21003 - Female 4 (Black) - (no description) -21004 - Female 5 (Black) - (no description) -21005 - Female 6 (Black) - (no description) -21006 - Female 7 (Black) - (no description) -21007 - Female 8 (Black) - (no description) -21008 - Female 9 (Black) - (no description) -21009 - Female JP (Black) - (no description) -21010 - Female JP (Black) - (no description) -21011 - Female JP (Black) - (no description) -21012 - Female 10 (Black) - (no description) -21013 - Female JP (Black) - (no description) -21014 - Female 11 (Black) - (no description) -21016 - Female Face 12 - (no description) -21017 - Female Face 14 - (no description) +20721 - Overjoyed Smile (Amethyst) - (no description) +20722 - Child's Play (Amethyst) - (no description) +20723 - Hypnotized Look (Amethyst) - (no description) +20724 - Intense Stare (Amethyst) - (no description) +20725 - Edge of Emotion (Amethyst) - (no description) +20726 - Shuteye (Amethyst) - (no description) +20727 - Pensive Look (Amethyst) - (no description) +20728 - Sarcastic Face (Amethyst) - (no description) +20729 - Shade of Cool (Amethyst) - (no description) +20731 - Fearful Glance (Amethyst) - (no description) +20732 - Undecided Face (Amethyst) - (no description) +20800 - Motivated Look (White) - (no description) +20801 - Perplexed Stare (White) - (no description) +20802 - Leisure Look (White) - (no description) +20803 - Dramatic Face (White) - (no description) +20804 - Rebel's Fire (White) - (no description) +20805 - Alert Face (White) - (no description) +20806 - Babyface Pout (White) - (no description) +20807 - Sad Innocence (White) - (no description) +20808 - Worrisome Glare (White) - (no description) +20809 - Smart Aleck (White) - (no description) +20810 - Anger's Blaze (White) - (no description) +20811 - Cool Guy Gaze (White) - (no description) +20812 - Curious Dog (White) - (no description) +20813 - Insomniac Daze (White) - (no description) +20814 - Look of Wonder (White) - (no description) +20816 - Ghostface Stare (White) - (no description) +20817 - Demure Poise (White) - (no description) +20818 - Champion Focus (White) - (no description) +20819 - Irritable Face (White) - (no description) +20820 - Fierce Edge (White) - (no description) +20821 - Overjoyed Smile (White) - (no description) +20822 - Child's Play (White) - (no description) +20823 - Hypnotized Look (White) - (no description) +20824 - Intense Stare (White) - (no description) +20826 - Shuteye (White) - (no description) +20828 - Sarcastic Face (White) - (no description) +21000 - Motivated Look (Black) - (no description) +21001 - Fearful Stare (Black) - (no description) +21002 - Leisure Look (Black) - (no description) +21003 - Strong Stare (Black) - (no description) +21004 - Angel Glow (Black) - (no description) +21005 - Babyface Pout (Black) - (no description) +21006 - Pucker Up Face (Black) - (no description) +21007 - Dollface Look (Black) - (no description) +21008 - Hopeless Gaze (Black) - (no description) +21009 - Look of Death (Black) - (no description) +21010 - Wisdom Glance (Black) - (no description) +21011 - Hypnotized Look (Black) - (no description) +21012 - Soul's Window (Black) - (no description) +21013 - Wide-eyed Girl (Black) - (no description) +21014 - Curious Look (Black) - (no description) +21016 - Beauty Stare (Black) - (no description) +21017 - Demure Poise Eyes (Black) - (no description) 21018 - Athena's Grace (Black) - (no description) -21019 - Hera's Radiance (Black) - (no description) -21020 - Female Face 13 - (no description) -21021 - Compassion's Countenance (Black) - (no description) -21022 - femalefaceGL(Black) - (no description) -21024 - Leisure Look (Black) - (no description) -21100 - Female 1 (Blue) - (no description) -21101 - Female 2 (Blue) - (no description) -21102 - Female 3 (Blue) - (no description) -21103 - Female 4 (Blue) - (no description) -21104 - Female 5 (Blue) - (no description) -21105 - Female 6 (Blue) - (no description) -21106 - Female 7 (Blue) - (no description) -21107 - Female 8 (Blue) - (no description) -21108 - Female 9 (Blue) - (no description) -21109 - Female JP (Blue) - (no description) -21110 - Female JP (Blue) - (no description) -21111 - Female JP (Blue) - (no description) -21112 - Female 10 (Blue) - (no description) -21113 - Female JP (Blue) - (no description) -21114 - Female 11 (Blue) - (no description) -21116 - Female Face 12 - (no description) -21117 - Female Face 14 - (no description) +21019 - Hera's Radiance (Black) - (no description) +21020 - Gentle Glow (Black) - (no description) +21021 - Compassion Look (Black) - (no description) +21022 - Glitzy Face (Black) - (no description) +21023 - Innocent Look (Black) - (no description) +21024 - Keen Look (Black) - (no description) +21025 - Shuteye (Black) - (no description) +21026 - Tender Love (Black) - (no description) +21027 - Glamorous Edge (Black) - (no description) +21029 - Kitty Cat (Black) - (no description) +21030 - Poker Face (Black) - (no description) +21100 - Motivated Look (Blue) - (no description) +21101 - Fearful Stare (Blue) - (no description) +21102 - Leisure Look (Blue) - (no description) +21103 - Strong Stare (Blue) - (no description) +21104 - Angel Glow (Blue) - (no description) +21105 - Babyface Pout (Blue) - (no description) +21106 - Pucker Up Face (Blue) - (no description) +21107 - Dollface Look (Blue) - (no description) +21108 - Hopeless Gaze (Blue) - (no description) +21109 - Look of Death (Blue) - (no description) +21110 - Wisdom Glance (Blue) - (no description) +21111 - Hypnotized Look (Blue) - (no description) +21112 - Soul's Window (Blue) - (no description) +21113 - Wide-eyed Girl (Blue) - (no description) +21114 - Curious Look (Blue) - (no description) +21116 - Beauty Stare (Blue) - (no description) +21117 - Demure Poise Eyes (Blue) - (no description) 21118 - Athena's Grace (Blue) - (no description) 21119 - Hera's Radiance (Blue) - (no description) -21120 - Female Face 13 - (no description) -21121 - Compassion's Countenance (Blue) - (no description) -21122 - femalefaceGL(Blue) - (no description) -21124 - Leisure Look (Blue) - (no description) -21200 - Female 1 (Red) - (no description) -21201 - Female 2 (Red) - (no description) -21202 - Female 3 (Red) - (no description) -21203 - Female 4 (Red) - (no description) -21204 - Female 5 (Red) - (no description) -21205 - Female 6 (Red) - (no description) -21206 - Female 7 (Red) - (no description) -21207 - Female 8 (Red) - (no description) -21208 - Female 9 (Red) - (no description) -21209 - Female JP (Red) - (no description) -21210 - Female JP (Red) - (no description) -21211 - Female JP (Red) - (no description) -21212 - Female 10 (Red) - (no description) -21213 - Female JP (Red) - (no description) -21214 - Female 11 (Red) - (no description) -21216 - Female Face 12 - (no description) -21217 - Female Face 14 - (no description) +21120 - Gentle Glow (Blue) - (no description) +21121 - Compassion Look (Blue) - (no description) +21122 - Glitzy Face (Blue) - (no description) +21123 - Innocent Look (Blue) - (no description) +21124 - Keen Look (Blue) - (no description) +21125 - Shuteye (Blue) - (no description) +21126 - Tender Love (Blue) - (no description) +21127 - Glamorous Edge (Blue) - (no description) +21129 - Kitty Cat (Blue) - (no description) +21130 - Poker Face (Blue) - (no description) +21200 - Motivated Look (Red) - (no description) +21201 - Fearful Stare (Red) - (no description) +21202 - Leisure Look (Red) - (no description) +21203 - Strong Stare (Red) - (no description) +21204 - Angel Glow (Red) - (no description) +21205 - Babyface Pout (Red) - (no description) +21206 - Pucker Up Face (Red) - (no description) +21207 - Dollface Look (Red) - (no description) +21208 - Hopeless Gaze (Red) - (no description) +21209 - Look of Death (Red) - (no description) +21210 - Wisdom Glance (Red) - (no description) +21211 - Hypnotized Look (Red) - (no description) +21212 - Soul's Window (Red) - (no description) +21213 - Wide-eyed Girl (Red) - (no description) +21214 - Curious Look (Red) - (no description) +21216 - Beauty Stare (Red) - (no description) +21217 - Demure Poise Eyes (Red) - (no description) 21218 - Athena's Grace (Red) - (no description) -21219 - Hera's Radiance(Red) - (no description) -21220 - Female Face 13 - (no description) -21221 - Compassion's Countenance (Red) - (no description) -21222 - femalefaceGL(Red) - (no description) -21224 - Leisure Look (Red) - (no description) -21300 - Female 1 (Green) - (no description) -21301 - Female 2 (Green) - (no description) -21302 - Female 3 (Green) - (no description) -21303 - Female 4 (Green) - (no description) -21304 - Female 5 (Green) - (no description) -21305 - Female 6 (Green) - (no description) -21306 - Female 7 (Green) - (no description) -21307 - Female 8 (Green) - (no description) -21308 - Female 9 (Green) - (no description) -21309 - Female JP (Green) - (no description) -21310 - Female JP (Green) - (no description) -21311 - Female JP (Green) - (no description) -21312 - Female 10 (Green) - (no description) -21313 - Female JP (Green) - (no description) -21314 - Female 11 (Green) - (no description) -21316 - Female Face 12 - (no description) -21317 - Female Face 14 - (no description) +21219 - Hera's Radiance (Red) - (no description) +21220 - Gentle Glow (Red) - (no description) +21221 - Compassion Look (Red) - (no description) +21222 - Glitzy Face (Red) - (no description) +21223 - Innocent Look (Red) - (no description) +21224 - Keen Look (Red) - (no description) +21225 - Shuteye (Red) - (no description) +21226 - Tender Love (Red) - (no description) +21227 - Glamorous Edge (Red) - (no description) +21229 - Kitty Cat (Red) - (no description) +21230 - Poker Face (Red) - (no description) +21300 - Motivated Look (Green) - (no description) +21301 - Fearful Stare (Green) - (no description) +21302 - Leisure Look (Green) - (no description) +21303 - Strong Stare (Green) - (no description) +21304 - Angel Glow (Green) - (no description) +21305 - Babyface Pout (Green) - (no description) +21306 - Pucker Up Face (Green) - (no description) +21307 - Dollface Look (Green) - (no description) +21308 - Hopeless Gaze (Green) - (no description) +21309 - Look of Death (Green) - (no description) +21310 - Wisdom Glance (Green) - (no description) +21311 - Hypnotized Look (Green) - (no description) +21312 - Soul's Window (Green) - (no description) +21313 - Wide-eyed Girl (Green) - (no description) +21314 - Curious Look (Green) - (no description) +21316 - Beauty Stare (Green) - (no description) +21317 - Demure Poise Eyes (Green) - (no description) 21318 - Athena's Grace (Green) - (no description) 21319 - Hera's Radiance (Green) - (no description) -21320 - Female Face 13 - (no description) -21321 - Compassion's Countenance (Green) - (no description) -21322 - femalefaceGL(Green) - (no description) -21324 - Leisure Look (Green) - (no description) -21400 - Female 1 (Hazel) - (no description) -21401 - Female 2 (Hazel) - (no description) -21402 - Female 3 (Hazel) - (no description) -21403 - Female 4 (Hazel) - (no description) -21404 - Female 5 (Hazel) - (no description) -21405 - Female 6 (Hazel) - (no description) -21406 - Female 7 (Hazel) - (no description) -21407 - Female 8 (Hazel) - (no description) -21408 - Female 9 (Hazel) - (no description) -21409 - Female JP (Hazel) - (no description) -21410 - Female JP (Hazel) - (no description) -21411 - Female JP (Hazel) - (no description) -21412 - Female 10 (Hazel) - (no description) -21413 - Female JP (Hazel) - (no description) -21414 - Female 11 (Hazel) - (no description) -21416 - Female Face 12 - (no description) -21417 - Female Face 14 - (no description) +21320 - Gentle Glow (Green) - (no description) +21321 - Compassion Look (Green) - (no description) +21322 - Glitzy Face (Green) - (no description) +21323 - Innocent Look (Green) - (no description) +21324 - Keen Look (Green) - (no description) +21325 - Shuteye (Green) - (no description) +21326 - Tender Love (Green) - (no description) +21327 - Glamorous Edge (Green) - (no description) +21329 - Kitty Cat (Green) - (no description) +21330 - Poker Face (Green) - (no description) +21400 - Motivated Look (Hazel) - (no description) +21401 - Fearful Stare (Hazel) - (no description) +21402 - Leisure Look (Hazel) - (no description) +21403 - Strong Stare (Hazel) - (no description) +21404 - Angel Glow (Hazel) - (no description) +21405 - Babyface Pout (Hazel) - (no description) +21406 - Pucker Up Face (Hazel) - (no description) +21407 - Dollface Look (Hazel) - (no description) +21408 - Hopeless Gaze (Hazel) - (no description) +21409 - Look of Death (Hazel) - (no description) +21410 - Wisdom Glance (Hazel) - (no description) +21411 - Hypnotized Look (Hazel) - (no description) +21412 - Soul's Window (Hazel) - (no description) +21413 - Wide-eyed Girl (Hazel) - (no description) +21414 - Curious Look (Hazel) - (no description) +21416 - Beauty Stare (Hazel) - (no description) +21417 - Demure Poise Eyes (Hazel) - (no description) 21418 - Athena's Grace (Hazel) - (no description) 21419 - Hera's Radiance (Hazel) - (no description) -21420 - Female Face 13 - (no description) -21421 - Compassion's Countenance (Hazel) - (no description) -21422 - femalefaceGL(Hazel) - (no description) -21424 - Leisure Look (Hazel) - (no description) -21500 - Female 1 (Sapphire) - (no description) -21501 - Female 2 (Sapphire) - (no description) -21502 - Female 3 (Sapphire) - (no description) -21503 - Female 4 (Sapphire) - (no description) -21504 - Female 5 (Sapphire) - (no description) -21505 - Female 6 (Sapphire) - (no description) -21506 - Female 7 (Sapphire) - (no description) -21507 - Female 8 (Sapphire) - (no description) -21508 - Female 9 (Sapphire) - (no description) -21509 - Female JP (Sapphire) - (no description) -21510 - Female JP (Sapphire) - (no description) -21511 - Female JP (Sapphire) - (no description) -21512 - Female 10 (Sapphire) - (no description) -21513 - Female JP (Sapphire) - (no description) -21514 - Female 11 (Sapphire) - (no description) -21516 - Female Face 12 - (no description) -21517 - Female Face 14 - (no description) +21420 - Gentle Glow (Hazel) - (no description) +21421 - Compassion Look (Hazel) - (no description) +21422 - Glitzy Face (Hazel) - (no description) +21423 - Innocent Look (Hazel) - (no description) +21424 - Keen Look (Hazel) - (no description) +21425 - Shuteye (Hazel) - (no description) +21426 - Tender Love (Hazel) - (no description) +21427 - Glamorous Edge (Hazel) - (no description) +21429 - Kitty Cat (Hazel) - (no description) +21430 - Poker Face (Hazel) - (no description) +21500 - Motivated Look (Sapphire) - (no description) +21501 - Fearful Stare (Sapphire) - (no description) +21502 - Leisure Look (Sapphire) - (no description) +21503 - Strong Stare (Sapphire) - (no description) +21504 - Angel Glow (Sapphire) - (no description) +21505 - Babyface Pout (Sapphire) - (no description) +21506 - Pucker Up Face (Sapphire) - (no description) +21507 - Dollface Look (Sapphire) - (no description) +21508 - Hopeless Gaze (Sapphire) - (no description) +21509 - Look of Death (Sapphire) - (no description) +21510 - Wisdom Glance (Sapphire) - (no description) +21511 - Hypnotized Look (Sapphire) - (no description) +21512 - Soul's Window (Sapphire) - (no description) +21513 - Wide-eyed Girl (Sapphire) - (no description) +21514 - Curious Look (Sapphire) - (no description) +21516 - Beauty Stare (Sapphire) - (no description) +21517 - Demure Poise Eyes (Sapphire) - (no description) 21518 - Athena's Grace (Sapphire) - (no description) -21519 - Hera's Radiance(Sapphire) - (no description) -21520 - Female Face 13 - (no description) -21521 - Compassion's Countenance (Sapphire) - (no description) -21522 - femalefaceGL(Sapphire) - (no description) -21524 - Leisure Look (Sapphire) - (no description) -21600 - Female 1 (Violet) - (no description) -21601 - Female 2 (Violet) - (no description) -21602 - Female 3 (Violet) - (no description) -21603 - Female 4 (Violet) - (no description) -21604 - Female 5 (Violet) - (no description) -21605 - Female 6 (Violet) - (no description) -21606 - Female 7 (Violet) - (no description) -21607 - Female 8 (Violet) - (no description) -21608 - Female 9 (Violet) - (no description) -21609 - Female JP (Violet) - (no description) -21610 - Female JP (Violet) - (no description) -21611 - Female JP (Violet) - (no description) -21612 - Female 10 (Violet) - (no description) -21613 - Female JP (Violet) - (no description) -21614 - Female 11 (Violet) - (no description) -21616 - Female Face 12 - (no description) -21617 - Female Face 14 - (no description) +21519 - Hera's Radiance (Sapphire) - (no description) +21520 - Gentle Glow (Sapphire) - (no description) +21521 - Compassion Look (Sapphire) - (no description) +21522 - Glitzy Face (Sapphire) - (no description) +21523 - Innocent Look (Sapphire) - (no description) +21524 - Keen Look (Sapphire) - (no description) +21525 - Shuteye (Sapphire) - (no description) +21526 - Tender Love (Sapphire) - (no description) +21527 - Glamorous Edge (Sapphire) - (no description) +21529 - Kitty Cat (Sapphire) - (no description) +21530 - Poker Face (Sapphire) - (no description) +21600 - Motivated Look (Violet) - (no description) +21601 - Fearful Stare (Violet) - (no description) +21602 - Leisure Look (Violet) - (no description) +21603 - Strong Stare (Violet) - (no description) +21604 - Angel Glow (Violet) - (no description) +21605 - Babyface Pout (Violet) - (no description) +21606 - Pucker Up Face (Violet) - (no description) +21607 - Dollface Look (Violet) - (no description) +21608 - Hopeless Gaze (Violet) - (no description) +21609 - Look of Death (Violet) - (no description) +21610 - Wisdom Glance (Violet) - (no description) +21611 - Hypnotized Look (Violet) - (no description) +21612 - Soul's Window (Violet) - (no description) +21613 - Wide-eyed Girl (Violet) - (no description) +21614 - Curious Look (Violet) - (no description) +21616 - Beauty Stare (Violet) - (no description) +21617 - Demure Poise Eyes (Violet) - (no description) 21618 - Athena's Grace (Violet) - (no description) 21619 - Hera's Radiance (Violet) - (no description) -21620 - Female Face 13 - (no description) -21621 - Compassion's Countenance (Violet) - (no description) -21622 - femalefaceGL(Violet) - (no description) -21624 - Leisure Look (Violet) - (no description) -21700 - Female 1 (Amethyst) - (no description) -21701 - Female 2 (Amethyst) - (no description) -21702 - Female 3 (Amethyst) - (no description) -21703 - Female 4 (Amethyst) - (no description) -21704 - Female 5 (Amethyst) - (no description) -21705 - Female 6 (Amethyst) - (no description) -21706 - Female 7 (Amethyst) - (no description) -21707 - Female 8 (Amethyst) - (no description) -21708 - Female 9 (Amethyst) - (no description) -21709 - Female JP (Amethyst) - (no description) -21710 - Female JP (Amethyst) - (no description) -21711 - Female JP (Amethyst) - (no description) -21712 - Female 10 (Amethyst) - (no description) -21713 - Female JP (Amethyst) - (no description) -21714 - Female 11 (Amethyst) - (no description) -21716 - Female Face 12 - (no description) -21717 - Female Face 14 - (no description) +21620 - Gentle Glow (Violet) - (no description) +21621 - Compassion Look (Violet) - (no description) +21622 - Glitzy Face (Violet) - (no description) +21623 - Innocent Look (Violet) - (no description) +21624 - Keen Look (Violet) - (no description) +21625 - Shuteye (Violet) - (no description) +21626 - Tender Love (Violet) - (no description) +21627 - Glamorous Edge (Violet) - (no description) +21629 - Kitty Cat (Violet) - (no description) +21630 - Poker Face (Violet) - (no description) +21700 - Motivated Look (Amethyst) - (no description) +21701 - Fearful Stare (Amethyst) - (no description) +21702 - Leisure Look (Amethyst) - (no description) +21703 - Strong Stare (Amethyst) - (no description) +21704 - Angel Glow (Amethyst) - (no description) +21705 - Babyface Pout (Amethyst) - (no description) +21706 - Pucker Up Face (Amethyst) - (no description) +21707 - Dollface Look (Amethyst) - (no description) +21708 - Hopeless Gaze (Amethyst) - (no description) +21709 - Look of Death (Amethyst) - (no description) +21710 - Wisdom Glance (Amethyst) - (no description) +21711 - Hypnotized Look (Amethyst) - (no description) +21712 - Soul's Window (Amethyst) - (no description) +21713 - Wide-eyed Girl (Amethyst) - (no description) +21714 - Curious Look (Amethyst) - (no description) +21716 - Beauty Stare (Amethyst) - (no description) +21717 - Demure Poise Eyes (Amethyst) - (no description) 21718 - Athena's Grace (Amethyst) - (no description) 21719 - Hera's Radiance (Amethyst) - (no description) -21720 - Female Face 13 - (no description) -21721 - Compassion's Countenance (Amethyst) - (no description) -21722 - femalefaceGL(Amethyst) - (no description) -21724 - Leisure Look (Amethyst) - (no description) -20800 - Male 1 (White) - (no description) -20801 - Male 2 (White) - (no description) -20802 - Leisure Look (Black) - (no description) -20803 - Male 4 (White) - (no description) -20804 - Male 5 (White) - (no description) -20805 - Male 6 (White) - (no description) -20806 - Male 7 (White) - (no description) -20807 - Male 8 (White) - (no description) -20808 - Male 9 (White) - (no description) -20809 - Male JP (White) - (no description) -20810 - Male JP (White) - (no description) -20811 - Male JP (White) - (no description) -20812 - Male 10 (White) - (no description) -20813 - Male JP (White) - (no description) -20814 - Male 11 (White) - (no description) -20816 - Male Face 12 (White) - (no description) -20817 - Male Face 14 (White) - (no description) -20818 - Male 12 (White) - (no description) -20819 - Male 13 (White) - (no description) -20820 - Fierce Edge (White) - (no description) -20821 - Male Face JP (White) - (no description) -20822 - Male Face 16 (White Eyes) - (no description) -20823 - malefaceGL (White) - (no description) -20824 - Awakening (White) - (no description) -20826 - Closed Lids (White) - (no description) -21800 - Female 1 (White) - (no description) -21801 - Female 2 (White) - (no description) -21802 - Female 3 (White) - (no description) -21803 - Female 4 (White) - (no description) -21804 - Female 5 (White) - (no description) -21805 - Female 6 (White) - (no description) -21806 - Female 7 (White) - (no description) -21807 - Female 8 (White) - (no description) -21808 - Female 9 (White) - (no description) -21809 - Female JP (White) - (no description) -21810 - Female JP (White) - (no description) -21811 - Female JP (White) - (no description) -21812 - Female 10 (White) - (no description) -21813 - Female JP (White) - (no description) -21814 - Female 11 (White) - (no description) -21816 - Female Face 12 (White) - (no description) -21817 - Female Face 14 (White) - (no description) +21720 - Gentle Glow (Amethyst) - (no description) +21721 - Compassion Look (Amethyst) - (no description) +21722 - Glitzy Face (Amethyst) - (no description) +21723 - Innocent Look (Amethyst) - (no description) +21724 - Keen Look (Amethyst) - (no description) +21725 - Shuteye (Amethyst) - (no description) +21726 - Tender Love (Amethyst) - (no description) +21727 - Glamorous Edge (Amethyst) - (no description) +21729 - Kitty Cat (Amethyst) - (no description) +21730 - Poker Face (Amethyst) - (no description) +21800 - Motivated Look (White) - (no description) +21801 - Fearful Stare (White) - (no description) +21802 - Leisure Look (White) - (no description) +21803 - Strong Stare (White) - (no description) +21804 - Angel Glow (White) - (no description) +21805 - Babyface Pout (White) - (no description) +21806 - Pucker Up Face (White) - (no description) +21807 - Dollface Look (White) - (no description) +21808 - Hopeless Gaze (White) - (no description) +21809 - Look of Death (White) - (no description) +21810 - Wisdom Glance (White) - (no description) +21811 - Hypnotized Look (White) - (no description) +21812 - Soul's Window (White) - (no description) +21813 - Wide-eyed Girl (White) - (no description) +21814 - Curious Look (White) - (no description) +21816 - Beauty Stare (White) - (no description) +21817 - Demure Poise Eyes (White) - (no description) 21818 - Athena's Grace (White) - (no description) 21819 - Hera's Radiance (White) - (no description) -21820 - Female Face 13 (White) - (no description) -21821 - Compassion's Countenance (White) - (no description) -21822 - femalefaceGL (White) - (no description) -21824 - Leisure Look (White) - (no description) -21825 - Closed Lids (White) - (no description) -20126 - Closed Lids (Blue) - (no description) -20226 - Closed Lids (Red) - (no description) -20326 - Closed Lids (Green) - (no description) -20426 - Closed Lids (Hazel) - (no description) -20526 - Closed Lids (Sapphire) - (no description) -20626 - Closed Lids (Violet) - (no description) -20726 - Closed Lids (Amethyst) - (no description) -20026 - Closed Lids (Black) - (no description) -21025 - Closed Lids (Black) - (no description) -21125 - Closed Lids (Blue) - (no description) -21225 - Closed Lids (Red) - (no description) -21325 - Closed Lids (Green) - (no description) -21425 - Closed Lids (Hazel) - (no description) -21525 - Closed Lids (Sapphire) - (no description) -21625 - Closed Lids (Violet) - (no description) -21725 - Closed Lids (Amethyst) - (no description) -20025 - Male 17 (Black) - (no description) -20125 - Male 17 (Blue) - (no description) -20225 - Male 17 (Red) - (no description) -20325 - Male 17 (Green) - (no description) -20425 - Male 17 (Hazel) - (no description) -20525 - Male 17 (Sapphire) - (no description) -20625 - Male 17 (Violet) - (no description) -20725 - Male 17 (Amethyst) - (no description) -20027 - Male 18 (Black) - (no description) -20127 - Male 18 (Blue) - (no description) -20227 - Male 18 (Red) - (no description) -20327 - Male 18 (Green) - (no description) -20427 - Male 18 (Hazel) - (no description) -20527 - Male 18 (Sapphire) - (no description) -20627 - Male 18 (Violet) - (no description) -20727 - Male 18 (Amethyst) - (no description) -20028 - Male Face 19 - (no description) -20128 - Male Face 19(Blue Eyes) - (no description) -20228 - Male Face 19(Red Eyes) - (no description) -20328 - Male Face 19(Green Eyes) - (no description) -20428 - Male Face 19(Brown Eyes) - (no description) -20528 - Male Face 19(Sapphire Eyes) - (no description) -20628 - Male Face 19(Violet Eyes) - (no description) -20728 - Male Face 19(Amethyst Eyes) - (no description) -21023 - Female Face 17 - (no description) -21123 - Female Face 17(Blue Eyes) - (no description) -21223 - Female Face 17(Red Eyes) - (no description) -21323 - Female Face 17(Green Eyes) - (no description) -21423 - Female Face 17(Brown Eyes) - (no description) -21523 - Female Face 17(Sapphire Eyes) - (no description) -21623 - Female Face 17(Violet Eyes) - (no description) -21723 - Female Face 17(Amethyst Eyes) - (no description) -21026 - Female Face 19 - (no description) -21126 - Female Face 19(Blue Eyes) - (no description) -21226 - Female Face 19(Red Eyes) - (no description) -21326 - Female Face 19(Green Eyes) - (no description) -21426 - Female Face 19(Brown Eyes) - (no description) -21526 - Female Face 19(Sapphire Eyes) - (no description) -21626 - Female Face 19(Violet Eyes) - (no description) -21726 - Female Face 19(Amethyst Eyes) - (no description) -20828 - Male Face 19(White Eyes) - (no description) -21823 - Female Face 17(Amethyst Eyes) - (no description) -21826 - Female Face 19(White Eyes) - (no description) -20029 - Male Face 20 - (no description) -20129 - Male Face 20 - (no description) -20229 - Male Face 20 - (no description) -20329 - Male Face 20 - (no description) -20429 - Male Face 20 - (no description) -20529 - Male Face 20 - (no description) -20629 - Male Face 20 - (no description) -20729 - Male Face 20 - (no description) -21027 - Female Face 20 - (no description) -21127 - Female Face 20 - (no description) -21227 - Female Face 20 - (no description) -21327 - Female Face 20 - (no description) -21427 - Female Face 20 - (no description) -21527 - Female Face 20 - (no description) -21627 - Female Face 20 - (no description) -21727 - Female Face 20 - (no description) -20031 - Male Face 21 - (no description) -20131 - Male Face 21 - (no description) -20231 - Male Face 21 - (no description) -20331 - Male Face 21 - (no description) -20431 - Male Face 21 - (no description) -20531 - Male Face 21 - (no description) -20631 - Male Face 21 - (no description) -20731 - Male Face 21 - (no description) -20032 - Male Face 22 - (no description) -20132 - Male Face 22 - (no description) -20232 - Male Face 22 - (no description) -20332 - Male Face 22 - (no description) -20432 - Male Face 22 - (no description) -20532 - Male Face 22 - (no description) -20632 - Male Face 22 - (no description) -20732 - Male Face 22 - (no description) -21029 - Female Face 21 - (no description) -21129 - Female Face 21 - (no description) -21229 - Female Face 21 - (no description) -21329 - Female Face 21 - (no description) -21429 - Female Face 21 - (no description) -21529 - Female Face 21 - (no description) -21629 - Female Face 21 - (no description) -21729 - Female Face 21 - (no description) -21030 - Female Face 22 - (no description) -21130 - Female Face 22 - (no description) -21230 - Female Face 22 - (no description) -21330 - Female Face 22 - (no description) -21430 - Female Face 22 - (no description) -21530 - Female Face 22 - (no description) -21630 - Female Face 22 - (no description) -21730 - Female Face 22 - (no description) +21820 - Gentle Glow (White) - (no description) +21821 - Compassion Look (White) - (no description) +21822 - Glitzy Face (White) - (no description) +21823 - Innocent Look (White) - (no description) +21824 - Keen Look (White) - (no description) +21825 - Shuteye (White) - (no description) +21826 - Tender Love (White) - (no description) diff --git a/handbook/Equip/Hair.txt b/handbook/Equip/Hair.txt index eebe4640d4..7ecad4488e 100644 --- a/handbook/Equip/Hair.txt +++ b/handbook/Equip/Hair.txt @@ -55,6 +55,9 @@ 30102 - Brown Fantasy - (no description) 30103 - Green Fantasy - (no description) 30104 - Blue-Green Fantasy - (no description) +30105 - Blue Fantasy - (no description) +30106 - Maroon Fantasy - (no description) +30107 - Brown Fantasy - (no description) 30110 - Black Fireball - (no description) 30111 - Red Fireball - (no description) 30112 - Orange Fireball - (no description) @@ -167,14 +170,14 @@ 30245 - Blue Monkey - (no description) 30246 - Purple Monkey - (no description) 30247 - Brown Monkey - (no description) -30250 - Black Afro - (no description) -30251 - Red Afro - (no description) -30252 - Orange Afro - (no description) -30253 - Blonde Afro - (no description) -30254 - Green Afro - (no description) -30255 - Blue Afro - (no description) -30256 - Purple Afro - (no description) -30257 - Brown Afro - (no description) +30250 - Black Afro (big) - (no description) +30251 - Red Afro (big) - (no description) +30252 - Orange Afro (big) - (no description) +30253 - Blonde Afro (big) - (no description) +30254 - Green Afro (big) - (no description) +30255 - Blue Afro (big) - (no description) +30256 - Purple Afro (big) - (no description) +30257 - Brown Afro (big) - (no description) 30260 - Black Metrosexual - (no description) 30261 - Red Metrosexual - (no description) 30262 - Orange Metrosexual - (no description) @@ -223,14 +226,14 @@ 30315 - Blue Acorn - (no description) 30316 - Purple Acorn - (no description) 30317 - Brown Acorn - (no description) -30320 - Black Afro - (no description) -30321 - Red Afro - (no description) -30322 - Orange Afro - (no description) -30323 - Blonde Afro - (no description) -30324 - Green Afro - (no description) -30325 - Blue Afro - (no description) -30326 - Purple Afro - (no description) -30327 - Brown Afro - (no description) +30320 - Black Afro (small) - (no description) +30321 - Red Afro (small) - (no description) +30322 - Orange Afro (small) - (no description) +30323 - Blonde Afro (small) - (no description) +30324 - Green Afro (small) - (no description) +30325 - Blue Afro (small) - (no description) +30326 - Purple Afro (small) - (no description) +30327 - Brown Afro (small) - (no description) 30330 - Black Cabana Boy - (no description) 30331 - Red Cabana Boy - (no description) 30332 - Orange Cabana Boy - (no description) @@ -1151,6 +1154,14 @@ 31655 - Blue Dashing Damsel - (no description) 31656 - Purple Dashing Damsel - (no description) 31657 - Brown Dashing Damsel - (no description) +31660 - Black Tighty Bun - (no description) +31661 - Red Tighty Bun - (no description) +31662 - Orange Tighty Bun - (no description) +31663 - Blonde Tighty Bun - (no description) +31664 - Green Tighty Bun - (no description) +31665 - Blue Tighty Bun - (no description) +31666 - Purple Tighty Bun - (no description) +31667 - Brown Tighty Bun - (no description) 31670 - Black Grandma ma' - (no description) 31671 - Red Grandma ma' - (no description) 31672 - Orange Grandma ma' - (no description) @@ -1488,6 +1499,7 @@ 34056 - Purple Aran Hair - (no description) 34057 - Brown Aran Hair - (no description) 33100 - Black The Coco - (no description) +33101 - Red The Coco - (no description) 33102 - Orange The Coco - (no description) 33103 - Blond The Coco - (no description) 33104 - Green The Coco - (no description) diff --git a/handbook/Use.txt b/handbook/Use.txt index 4bb475df52..0a2f651a57 100644 --- a/handbook/Use.txt +++ b/handbook/Use.txt @@ -1,4 +1,4 @@ -2000000 - Red Potion - A potion made out of red herbs.\nRecovers 50 HP. +2000000 - Red Potion - A potion made out of red herbs.\nRecovers 50 HP. 2000001 - Orange Potion - A concentrated potion made out of red herbs.\nRecovers 150 HP. 2000002 - White Potion - A highly-concentrated potion made out of red herbs.\nRecovers 300 HP. 2000003 - Blue Potion - A potion made out of blue herbs.\nRecovers 100 MP. diff --git a/scripts/npc/1012002.js b/scripts/npc/1012002.js index ac67aca488..90eb1193f3 100644 --- a/scripts/npc/1012002.js +++ b/scripts/npc/1012002.js @@ -106,7 +106,7 @@ function action(mode, type, selection) { if (selectedType != 4) selectedItem = selection; else - qty = (selection > 0) ? selection : (selection < 0 ? -selection : 1); + qty = (selection > 0) ? selection : (selection < 0 ? -selection : 1); // thanks br1337 for showing such a simple exploit with quantity on NPC crafters if (selectedType == 0) { //bow refine var matSet = [[4003001,4000000],[4011001,4003000],[4003001,4000016],[4011001,4021006,4003000],[4011001,4011006,4021003,4021006,4003000],[4011004,4021000,4021004,4003000],[4021008,4011001,4011006,4003000,4000014]]; var matQtySet = [[5,30],[1,3],[30,50],[2,2,8],[5,5,3,3,30],[7,6,3,35],[1,10,3,40,50]]; diff --git a/scripts/npc/1012103.js b/scripts/npc/1012103.js index 5074166d7f..e1bd30ddfb 100644 --- a/scripts/npc/1012103.js +++ b/scripts/npc/1012103.js @@ -21,13 +21,15 @@ */ /* Natalie Henesys VIP Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30030, 30020, 30000, 30310, 30330, 30060, 30150, 30410, 30210, 30140, 30120, 30200); -var fhair = Array(31050, 31040, 31000, 31150, 31310, 31300, 31160, 31100, 31410, 31030, 31080, 31070); +var mhair_v = Array(30060, 30140, 30200, 30210, 30310, 33040, 33100); +var fhair_v = Array(31150, 31300, 31350, 31700, 31740, 34050, 34110); var hairnew = Array(); function start() { @@ -35,6 +37,12 @@ function start() { action(1, 0, 0); } +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function action(mode, type, selection) { if (mode < 1) { cm.dispose(); @@ -47,18 +55,18 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) - for(var i = 0; i < mhair.length; i++) - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair()% 10)); + for(var i = 0; i < mhair_v.length; i++) + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair()% 10)); if (cm.getPlayer().getGender() == 1) - for(var i = 0; i < fhair.length; i++) - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < fhair_v.length; i++) + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); cm.sendStyle("I can totally change up your hairstyle and make it look so good. Why don't you change it up a bit? If you have #b#t5150001##k I'll change it for you. Choose the one to your liking~.", hairnew); } else if (selection == 2) { beauty = 2; haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); cm.sendStyle("I can totally change your haircolor and make it look so good. Why don't you change it up a bit? With #b#t51051001##k I'll change it for you. Choose the one to your liking.", haircolor); } } else if (status == 2){ diff --git a/scripts/npc/1012104.js b/scripts/npc/1012104.js index 10b4d18e66..d00ec8f207 100644 --- a/scripts/npc/1012104.js +++ b/scripts/npc/1012104.js @@ -21,15 +21,24 @@ */ /* Brittany Henesys Random Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30310, 30330, 30060, 30150, 30410, 30210, 30140, 30120, 30200, 30560, 30510, 30610, 30470); -var fhair = Array(31150, 31310, 31300, 31160, 31100, 31410, 31030, 31080, 31070, 31610, 31350, 31510, 31740); +var mhair_r = Array(30060, 30140, 30200, 30210, 30310, 30610, 33040, 33100); +var fhair_r = Array(31070, 31080, 31150, 31300, 31350, 31700, 34050, 34110); +var mhair_e = Array(30030, 30140, 30200, 30210, 30310, 30610, 33040, 33100); +var fhair_e = Array(31070, 31150, 31300, 31350, 31430, 31700, 34050, 34110); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} function start() { status = -1; action(1, 0, 0); @@ -48,18 +57,30 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("I'm Brittany the assistant. If you have #b#t5150010##k or #b#t5151000##k by any chance, then how about letting me change your hairdo?\r\n#L1#Haircut: #i5150010##t5150010##l\r\n#L2#Dye your hair: #i5151000##t5151000##l"); + cm.sendSimple("I'm Brittany the assistant. If you have #b#t5150000##k, #b#t5150010##k or #b#t5151000##k by any chance, then how about letting me change your hairdo?\r\n#L0#Haircut: #i5150000##t5150000##l\r\n#L1#Haircut: #i5150010##t5150010##l\r\n#L2#Dye your hair: #i5151000##t5151000##l"); } else if (status == 1) { - if (selection == 1) { + if (selection == 0) { + beauty = 3; + hairnew = Array(); + if (cm.getPlayer().getGender() == 0) { + for(var i = 0; i < mhair_r.length; i++) + pushIfItemExists(hairnew, mhair_r[i] + parseInt(cm.getPlayer().getHair()% 10)); + } + if (cm.getPlayer().getGender() == 1) { + for(var i = 0; i < fhair_r.length; i++) + pushIfItemExists(hairnew, fhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); + } + cm.sendYesNo("If you use the REG coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that even you didn't think was possible. Are you going to use #b#t5150000##k and really change your hairstyle?"); + } else if (selection == 1) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair()% 10)); + for(var i = 0; i < mhair_e.length; i++) + pushIfItemExists(hairnew, mhair_e[i] + parseInt(cm.getPlayer().getHair()% 10)); } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < fhair_e.length; i++) + pushIfItemExists(hairnew, fhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); } cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that even you didn't think was possible. Are you going to use #b#t5150010##k and really change your hairstyle?"); } else if (selection == 2) { @@ -67,7 +88,7 @@ function action(mode, type, selection) { haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5151000##k and change it up?"); } } @@ -81,8 +102,7 @@ function action(mode, type, selection) { } else { cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); } - } - if (beauty == 2){ + } else if (beauty == 2){ if (cm.haveItem(5151000) == true){ cm.gainItem(5151000, -1); cm.setHair(haircolor[Math.floor(Math.random() * haircolor.length)]); @@ -90,8 +110,15 @@ function action(mode, type, selection) { } else { cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't dye your hair without it. I'm sorry..."); } - } - if (beauty == 0){ + } else if (beauty == 3){ + if (cm.haveItem(5150000) == true){ + cm.gainItem(5150000, -1); + cm.setHair(hairnew[Math.floor(Math.random() * hairnew.length)]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else { + cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); + } + } else if (beauty == 0){ if (selection == 0 && cm.getMeso() >= hairprice) { cm.gainMeso(-hairprice); cm.gainItem(5150010, 1); diff --git a/scripts/npc/1012105.js b/scripts/npc/1012105.js index c6e43d2c99..c928d6662d 100644 --- a/scripts/npc/1012105.js +++ b/scripts/npc/1012105.js @@ -47,7 +47,7 @@ function action(mode, type, selection) { if (status == 0) { - cm.sendSimple("Well, hello! Welcome to the Henesys Skin-Care! Would you like to have a firm, tight, healthy looking skin like mine? With a #b#t5153000##k, you can let us take care of the rest and have the kind of skin you've always wanted~!\r\n#L1#I already have a Coupon!#l"); + cm.sendSimple("Well, hello! Welcome to the Henesys Skin-Care! Would you like to have a firm, tight, healthy looking skin like mine? With a #b#t5153000##k, you can let us take care of the rest and have the kind of skin you've always wanted~!\r\n#L1#Skin Care: #i5153000##t5153000##l"); } else if (status == 1) { if (cm.haveItem(5153000)){ diff --git a/scripts/npc/1012117.js b/scripts/npc/1012117.js index a2d88c8a31..4dd03728d7 100644 --- a/scripts/npc/1012117.js +++ b/scripts/npc/1012117.js @@ -23,13 +23,25 @@ NPC Name: Big Headward Map(s): Victoria Road : Henesys Hair Salon (100000104) Description: Random haircut + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; -var mhair = Array(30100, 30850, 30890); -var fhair = Array(31180, 31420, 31870, 31400, 31880, 31820, 31860, 31420, 31940, 34000, 31890); + +var mhair_r = Array(30010, 30070, 30080, 30090, 30100, 30690, 30760, 33000); +var fhair_r = Array(31130, 31530, 31820, 31920, 31940, 34000, 34030); + +var mhair_v = Array(30010, 30070, 30080, 30090, 30100, 30480, 30560, 30690, 30760, 30850, 30890, 30930, 30950); +var fhair_v = Array(31020, 31130, 31510, 31530, 31820, 31860, 31890, 31920, 31940, 31950, 34000); + var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} function start() { status = -1; action(1, 0, 0); @@ -50,29 +62,57 @@ function action(mode, type, selection) { status--; if (status == 0) { - cm.sendSimple("If you use this regular coupon, your hair may transform into a random new look...do you still want to do it using #b#t5150040##k, I will do it anyways for you. But don't forget, it will be random!\r\n\#L2#OK! (Uses #i5150040# #t5150040#)#l"); + cm.sendSimple("Hi, I'm #p1012117#, the most charming and stylish stylist around. If you're looking for the best looking hairdos around, look no further!\r\n\#L0##i5150040##t5150040##l\r\n\#L1##i5150044##t5150044##l"); } else if (status == 1) { - cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that even you didn't think was possible. Are you going to use #b#t5150040##k and really change your hairstyle?"); - } - else if (status == 2) { - if (cm.haveItem(5150040) == true){ + if (selection == 0) { + beauty = 1; + cm.sendYesNo("If you use this REGULAR coupon, your hair may transform into a random new look...do you still want to do it using #b#t5150040##k, I will do it anyways for you. But don't forget, it will be random!"); + } else { + beauty = 2; + hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < mhair_v.length; i++) { + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); } } else { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < fhair_v.length; i++) { + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); } } - cm.gainItem(5150040, -1); - cm.setHair(hairnew[Math.floor(Math.random() * hairnew.length)]); - cm.sendOk("Enjoy your new and improved hairstyle!"); - } else { - cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); + cm.sendStyle("Using the SPECIAL coupon you can choose the style your hair will become. Pick the style that best provides you delight...", hairnew); + } + } else if (status == 2) { + if (beauty == 1) { + if (cm.haveItem(5150040) == true){ + hairnew = Array(); + if (cm.getPlayer().getGender() == 0) { + for(var i = 0; i < mhair_r.length; i++) { + pushIfItemExists(hairnew, mhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); + } + } + else { + for(var i = 0; i < fhair_r.length; i++) { + pushIfItemExists(hairnew, fhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); + } + } + + cm.gainItem(5150040, -1); + cm.setHair(hairnew[Math.floor(Math.random() * hairnew.length)]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else { + cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); + } + } else if (beauty == 2) { + if (cm.haveItem(5150044) == true){ + cm.gainItem(5150044, -1); + cm.setHair(hairnew[selection]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else { + cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); + } } cm.dispose(); diff --git a/scripts/npc/1052004.js b/scripts/npc/1052004.js index 8e314519a6..1a44a73f86 100644 --- a/scripts/npc/1052004.js +++ b/scripts/npc/1052004.js @@ -21,14 +21,21 @@ */ /* Denma the Owner Henesys VIP Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014); -var fface = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014); +var mface_v = Array(20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014, 20015, 20022, 20028, 20031); +var fface_v = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21013, 21014, 21023, 21026); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} function start() { status = -1; action(1, 0, 0); @@ -47,17 +54,17 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Well, hello! Welcome to the Henesys Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152001##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Well, hello! Welcome to the Henesys Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152001##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#Plastic Surgery: #i5152001##t5152001##l"); } else if (status == 1) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) - facenew.push(mface[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); + for(var i = 0; i < mface_v.length; i++) + pushIfItemExists(facenew, mface_v[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) - facenew.push(fface[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); + for(var i = 0; i < fface_v.length; i++) + pushIfItemExists(facenew, fface_v[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); } cm.sendStyle("Let's see... I can totally transform your face into something new. Don't you want to try it? For #b#t5152001##k, you can get the face of your liking. Take your time in choosing the face of your preference.", facenew); } diff --git a/scripts/npc/1052005.js b/scripts/npc/1052005.js index 988249bc5e..40b92bf9f2 100644 --- a/scripts/npc/1052005.js +++ b/scripts/npc/1052005.js @@ -21,14 +21,21 @@ */ /* Dr. Feeble Henesys Random Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014); -var fface = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014); +var mface_r = Array(20000, 20005, 20008, 20012, 20016, 20022, 20032); +var fface_r = Array(21000, 21002, 21008, 21014, 21020, 21024, 21029); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} function start() { status = -1; action(1, 0, 0); @@ -47,20 +54,20 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hi, I pretty much shouldn't be doing this, but with a #b#t5152000##k, I will do it anyways for you. But don't forget, it will be random!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Hi, I pretty much shouldn't be doing this, but with a #b#t5152000##k, I will do it anyways for you. But don't forget, it will be random!\r\n#L2#Plastic Surgery: #i5152000##t5152000##l"); } else if (status == 1) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getPlayer().getFace() + for(var i = 0; i < mface_r.length; i++) { + pushIfItemExists(facenew, mface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getPlayer().getFace() + for(var i = 0; i < fface_r.length; i++) { + pushIfItemExists(facenew, fface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } diff --git a/scripts/npc/1052100.js b/scripts/npc/1052100.js index 424a4282dc..2f8a772c13 100644 --- a/scripts/npc/1052100.js +++ b/scripts/npc/1052100.js @@ -21,15 +21,22 @@ */ /* Don Giovanni Kerning VIP Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30030, 30020, 30000, 30130, 30350, 30190, 30110, 30180, 30050, 30040, 30160); -var fhair = Array(31050, 31040, 31000, 31060, 31090, 31020, 31130, 31120, 31140, 31330, 31010); +var mhair_v = Array(30040, 30130, 30780, 30850, 30860, 30920, 33040); +var fhair_v = Array(31090, 31140, 31330, 31440, 31760, 31880, 34050); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} function start() { status = -1; action(1, 0, 0); @@ -54,12 +61,12 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < mhair_v.length; i++) + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < fhair_v.length; i++) + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); } cm.sendStyle("I can totally change up your hairstyle and make it look so good. Why don't you change it up a bit? If you have #b#t5150003##k I'll change it for you. Choose the one to your liking~.", hairnew); } else if (selection == 2) { @@ -67,7 +74,7 @@ function action(mode, type, selection) { haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendStyle("I can totally change your haircolor and make it look so good. Why don't you change it up a bit? With #b#t5151003##k I'll change it for you. Choose the one to your liking.", haircolor); } diff --git a/scripts/npc/1052101.js b/scripts/npc/1052101.js index 553236cd7a..f35f863af2 100644 --- a/scripts/npc/1052101.js +++ b/scripts/npc/1052101.js @@ -21,15 +21,24 @@ */ /* Andre Kerning Random Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30130, 30350, 30190, 30110, 30180, 30050, 30040, 30160, 30770, 30620, 30550, 30520); -var fhair = Array(31060, 31090, 31020, 31130, 31120, 31140, 31330, 31010, 31520, 31440, 31750, 31620); +var mhair_r = Array(30040, 30130, 30520, 30770, 30780, 30850, 30920, 33040); +var fhair_r = Array(31060, 31140, 31330, 31440, 31520, 31750, 31760, 31880, 34050); +var mhair_e = Array(30130, 30430, 30520, 30770, 30780, 30850, 30920, 33040); +var fhair_e = Array(31060, 31140, 31330, 31520, 31760, 31880, 34010, 34050); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} function start() { status = -1; action(1, 0, 0); @@ -48,31 +57,47 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("I'm Andre, Don's assistant. Everyone calls me Andre, though. If you have a #b#t5150011##k or a #b#t5151002##k, please let me change your hairdo!\r\n#L1#Haircut: #i5150011##t5150011##l\r\n#L2#Dye your hair: #i5151002##t5151002##l"); + cm.sendSimple("I'm Andre, Don's assistant. Everyone calls me Andre, though. If you have a #b#t5150002##k, #b#t5150011##k or a #b#t5151002##k, please let me change your hairdo!\r\n#L0#Haircut: #i5150002##t5150002##l\r\n#L1#Haircut: #i5150011##t5150011##l\r\n#L2#Dye your hair: #i5151002##t5151002##l"); } else if (status == 1) { - if (selection == 1) { - beauty = 1; + if (selection == 0) { + beauty = 3; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < mhair_r.length; i++) { + pushIfItemExists(hairnew, mhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < fhair_r.length; i++) { + pushIfItemExists(hairnew, fhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); } } - cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150010##k and really change your hairstyle?"); + cm.sendYesNo("If you use the REG coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150011##k and really change your hairstyle?"); + } else if (selection == 1) { + beauty = 1; + hairnew = Array(); + if (cm.getPlayer().getGender() == 0) { + for(var i = 0; i < mhair_e.length; i++) { + pushIfItemExists(hairnew, mhair_e[i] + parseInt(cm.getPlayer().getHair() + % 10)); + } + } + if (cm.getPlayer().getGender() == 1) { + for(var i = 0; i < fhair_e.length; i++) { + pushIfItemExists(hairnew, fhair_e[i] + parseInt(cm.getPlayer().getHair() + % 10)); + } + } + cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150011##k and really change your hairstyle?"); } else if (selection == 2) { beauty = 2; haircolor = Array(); var current = parseInt(cm.getPlayer().getHair() /10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5151002##k and change it up?"); } @@ -97,6 +122,15 @@ function action(mode, type, selection) { cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't dye your hair without it. I'm sorry..."); } } + if (beauty == 3){ + if (cm.haveItem(5150002) == true){ + cm.gainItem(5150002, -1); + cm.setHair(hairnew[Math.floor(Math.random() * hairnew.length)]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else { + cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); + } + } if (beauty == 0){ if (selection == 0 && cm.getMeso() >= hairprice) { cm.gainMeso(-hairprice); diff --git a/scripts/npc/2010001.js b/scripts/npc/2010001.js index 6df9d45014..64dcfb3bd6 100644 --- a/scripts/npc/2010001.js +++ b/scripts/npc/2010001.js @@ -21,15 +21,23 @@ /* Mino the Owner Orbis VIP Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30030, 30020, 30000, 30270, 30230, 30260, 30280, 30240, 30290, 30340); -var fhair = Array(31040, 31000, 31250, 31220, 31260, 31240, 31110, 31270, 31030, 31230); +var mhair_v = Array(30230, 30260, 30280, 30340, 30490); +var fhair_v = Array(31110, 31220, 31230, 31630, 31790); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -54,14 +62,14 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < mhair_v.length; i++) { + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); } } if (cm.getPlayer().getGender() == 1) { for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() + pushIfItemExists(hairnew, fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); } } @@ -71,7 +79,7 @@ function action(mode, type, selection) { haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendStyle("I can totally change your haircolor and make it look so good. Why don't you change it up a bit? With #b#t5151005##k, I'll take care of the rest. Choose the color of your liking!", haircolor); } diff --git a/scripts/npc/2010002.js b/scripts/npc/2010002.js index 770c47fb05..9be9a9784c 100644 --- a/scripts/npc/2010002.js +++ b/scripts/npc/2010002.js @@ -21,14 +21,22 @@ /* Franz the Owner Orbis VIP Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014); -var fface = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014); +var mface_v = Array(20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014, 20022, 20028, 20031); +var fface_v = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014, 21023, 21026); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -47,20 +55,20 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Well well well, welcome to the Orbis Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152005##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Well well well, welcome to the Orbis Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152005##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#Plastic Surgery: #i5152005##t5152005##l"); } else if (status == 1) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getPlayer().getFace() + for(var i = 0; i < mface_v.length; i++) { + pushIfItemExists(facenew, mface_v[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getPlayer().getFace() + for(var i = 0; i < fface_v.length; i++) { + pushIfItemExists(facenew, fface_v[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } diff --git a/scripts/npc/2012007.js b/scripts/npc/2012007.js index ae03bab004..27542016c6 100644 --- a/scripts/npc/2012007.js +++ b/scripts/npc/2012007.js @@ -21,17 +21,29 @@ /* Rinz the assistant Orbis Random Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30030, 30020, 30000, 30270, 30230, 30260, 30280, 30240, 30290, 30340, 30370, 30630, 30530, 30760); -var fhair = Array(31040, 31000, 31250, 31220, 31260, 31240, 31110, 31270, 31030, 31230, 31530, 31710, 31320, 31650, 31630); +var mhair_d = Array(30030, 30020, 30000, 30270, 30230); +var fhair_d = Array(31040, 31000, 31250, 31220, 31260); +var mhair_r = Array(30230, 30260, 30280, 30340, 30490, 30530, 30630, 30740); +var fhair_r = Array(31110, 31220, 31230, 31630, 31650, 31710, 31790, 31890, 31930); +var mhair_e = Array(30230, 30280, 30340, 30490, 30530, 30740); +var fhair_e = Array(31110, 31220, 31230, 31710, 31790, 31890, 31930); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { - cm.sendSimple("I'm Rinz, the assistant. Do you have #b#t5150013##k or #b#t5151004##k with you? If so, what do you think about letting me take care of your hairdo? What do you want to do with your hair?\r\n#L1#Haircut: #i5150013##t5150013##l\r\n#L2#Dye your hair: #i5151004##t5151004##l"); + cm.sendSimple("I'm Rinz, the assistant. Do you have #b#t5154000##k, #b#t5150004##k, #b#t5150013##k or #b#t5151004##k with you? If so, what do you think about letting me take care of your hairdo? What do you want to do with your hair?\r\n#L0#Haircut: #i5154000##t5154000##l\r\n#L1#Haircut: #i5150004##t5150004##l\r\n#L2#Haircut: #i5150013##t5150013##l\r\n#L3#Dye your hair: #i5151004##t5151004##l"); } function action(mode, type, selection) { @@ -40,23 +52,43 @@ function action(mode, type, selection) { else { status++; if (status == 1) { - if (selection == 1) { + if (selection == 0) { + beauty = 4; + hairnew = Array(); + if (cm.getPlayer().getGender() == 0) + for(var i = 0; i < mhair_d.length; i++) + pushIfItemExists(hairnew, mhair_d[i] + parseInt(cm.getPlayer().getHair() % 10)); + else + for (var i = 0; i < fhair_d.length; i++) + pushIfItemExists(hairnew, fhair_d[i] + parseInt(cm.getPlayer().getHair() % 10)); + cm.sendYesNo("If you use the DRT coupon your hair will change RANDOMLY with a chance to obtain the basic styles that I came up with. Are you going to use #b#t5154000##k and really change your hairstyle?"); + } else if (selection == 1) { + beauty = 3; + hairnew = Array(); + if (cm.getPlayer().getGender() == 0) + for(var i = 0; i < mhair_r.length; i++) + pushIfItemExists(hairnew, mhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); + else + for (var i = 0; i < fhair_r.length; i++) + pushIfItemExists(hairnew, fhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); + cm.sendYesNo("If you use the REG coupon your hair will change RANDOMLY. Are you going to use #b#t5150004##k and really change your hairstyle?"); + } else if (selection == 2) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) - for(var i = 0; i < mhair.length; i++) - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < mhair_e.length; i++) + pushIfItemExists(hairnew, mhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); else - for (var i = 0; i < fhair.length; i++) - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); - cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150010##k and really change your hairstyle?"); - } else if (selection == 2) { + for (var i = 0; i < fhair_e.length; i++) + pushIfItemExists(hairnew, fhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); + cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150013##k and really change your hairstyle?"); + } else if (selection == 3) { beauty = 2; haircolor = Array(); var current = (cm.getPlayer().getHair() / 10) | 0; for (var i = 0; i < 8; i++) - haircolor.push(current + i); - cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5151004##k and change it up?"); + pushIfItemExists(haircolor, current + i); + cm.sendYesNo("If you use a regular coupon your hair color will change RANDOMLY. Do you still want to use #b#t5151004##k and change it up?"); } } else if (status == 2){ @@ -68,16 +100,28 @@ function action(mode, type, selection) { cm.sendOk("Enjoy your new and improved hairstyle!"); } else cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); - } - if (beauty == 2){ + } else if (beauty == 2){ if (cm.haveItem(5151004)){ cm.gainItem(5151004, -1); cm.setHair(haircolor[Math.floor(Math.random() * haircolor.length)]); cm.sendOk("Enjoy your new and improved haircolor!"); } else cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't dye your hair without it. I'm sorry..."); - } - if (beauty == 0){ + } else if (beauty == 3){ + if (cm.haveItem(5150004)){ + cm.gainItem(5150004, -1); + cm.setHair(hairnew[Math.floor(Math.random() * hairnew.length)]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else + cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); + } else if (beauty == 4){ + if (cm.haveItem(5154000)){ + cm.gainItem(5154000, -1); + cm.setHair(hairnew[Math.floor(Math.random() * hairnew.length)]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else + cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); + } else if (beauty == 0){ if (selection == 0 && cm.getMeso() >= hairprice) { cm.gainMeso(-hairprice); cm.gainItem(5150013, 1); diff --git a/scripts/npc/2012008.js b/scripts/npc/2012008.js index 7e553d706c..914dd002cb 100644 --- a/scripts/npc/2012008.js +++ b/scripts/npc/2012008.js @@ -27,7 +27,7 @@ var price = 1000000; var skin = Array(0, 1, 2, 3, 4); function start() { - cm.sendSimple("Well, hello! Welcome to the Orbis Skin-Care~! Would you like to have a firm, tight, healthy looking skin like mine? With #b#t5153001##k, you can let us take care of the rest and have the kind of skin you've always wanted~!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Well, hello! Welcome to the Orbis Skin-Care~! Would you like to have a firm, tight, healthy looking skin like mine? With #b#t5153001##k, you can let us take care of the rest and have the kind of skin you've always wanted~!\r\n#L2#Skin Care: #i5153001##t5153001##l"); } function action(mode, type, selection) { diff --git a/scripts/npc/2012009.js b/scripts/npc/2012009.js index 7de29684d2..6d426fd260 100644 --- a/scripts/npc/2012009.js +++ b/scripts/npc/2012009.js @@ -21,16 +21,24 @@ /* Riza the Assistant Orbis Random Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014); -var fface = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014); +var mface_r = Array(20003, 20011, 20021, 20022, 20023, 20027, 20031); +var fface_r = Array(21004, 21007, 21010, 21012, 21020, 21021, 21030); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { - cm.sendSimple("Hi, I pretty much shouldn't be doing this, but with a #b#t5152004##k, I will do it anyways for you. But don't forget, it will be random!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Hi, I pretty much shouldn't be doing this, but with a #b#t5152004##k, I will do it anyways for you. But don't forget, it will be random!\r\n#L2#Plastic Surgery: #i5152004##t5152004##l"); } function action(mode, type, selection) { @@ -42,11 +50,11 @@ function action(mode, type, selection) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) - for (var i = 0; i < mface.length; i++) - facenew.push(mface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + for (var i = 0; i < mface_r.length; i++) + pushIfItemExists(facenew, mface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); else - for (var i = 0; i < fface.length; i++) - facenew.push(fface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + for (var i = 0; i < fface_r.length; i++) + pushIfItemExists(facenew, fface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); cm.sendYesNo("If you use the regular coupon, your face may transform into a random new look...do you still want to do it using #b#t5152004##k?"); } } diff --git a/scripts/npc/2040019.js b/scripts/npc/2040019.js index 33ff4935ed..cd4ccf4708 100644 --- a/scripts/npc/2040019.js +++ b/scripts/npc/2040019.js @@ -21,14 +21,22 @@ /* Everton Ludibrium Random Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014); -var fface = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014); +var mface_r = Array(20001, 20003, 20007, 20013, 20021, 20023, 20025); +var fface_r = Array(21002, 21004, 21006, 21008, 21022, 21027, 21029); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -47,20 +55,20 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Well, I'm bored, so I'll help out the doctor. For a #b#t5152006##k, I will change the way you look. But don't forget, it will be random!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Well, I'm bored, so I'll help out the doctor. For a #b#t5152006##k, I will change the way you look. But don't forget, it will be random!\r\n#L2#Plastic Surgery: #i5152006##t5152006##l"); } else if (status == 1) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getPlayer().getFace() + for(var i = 0; i < mface_r.length; i++) { + pushIfItemExists(facenew, mface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getPlayer().getFace() + for(var i = 0; i < fface_r.length; i++) { + pushIfItemExists(facenew, fface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } diff --git a/scripts/npc/2041007.js b/scripts/npc/2041007.js index a30220a0e3..d7c2f8ad23 100644 --- a/scripts/npc/2041007.js +++ b/scripts/npc/2041007.js @@ -21,15 +21,23 @@ /* Miyu Ludibrium VIP Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30030, 30020, 30000, 30250, 30190, 30150, 30050, 30280, 30240, 30300, 30160); -var fhair = Array(31040, 31000, 31150, 31280, 31160, 31120, 31290, 31270, 31030, 31230, 31010); +var mhair_v = Array(30160, 30190, 30250, 30640, 30660, 30840, 30870, 30990); +var fhair_v = Array(31270, 31290, 31550, 31680, 31810, 31830, 31840, 31870); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -54,13 +62,13 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair()% 10)); + for(var i = 0; i < mhair_v.length; i++) { + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair()% 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair()% 10)); + for(var i = 0; i < fhair_v.length; i++) { + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getPlayer().getHair()% 10)); } } cm.sendStyle("I can completely change the look of your hair. Aren't you ready for a change? With #b#t5150007##k, I'll take care of the rest for you. Choose the style of your liking!", hairnew); @@ -69,7 +77,7 @@ function action(mode, type, selection) { haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendStyle("I can completely change the color of your hair. Aren't you ready for a change? With #b#t5151007##k, I'll take care of the rest. Choose the color of your liking!", haircolor); } diff --git a/scripts/npc/2041009.js b/scripts/npc/2041009.js index 6d58b1ee0a..d89c13a6d2 100644 --- a/scripts/npc/2041009.js +++ b/scripts/npc/2041009.js @@ -21,15 +21,25 @@ /* Mini Ludibrium Random Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30250, 30190, 30150, 30050, 30280, 30240, 30300, 30160, 30650, 30540, 30640, 30680); -var fhair = Array(31150, 31280, 31160, 31120, 31290, 31270, 31030, 31230, 31010, 31640, 31540, 31680, 31600); +var mhair_r = Array(30190, 30220, 30250, 30540, 30610, 30620, 30640, 30650, 30660, 30840, 30870, 30940, 30990); +var fhair_r = Array(31170, 31270, 31290, 31510, 31540, 31550, 31600, 31640, 31680, 31810, 31830, 31840, 31870); +var mhair_e = Array(30030, 30190, 30220, 30250, 30540, 30610, 30620, 30640, 30650, 30660, 30840, 30990); +var fhair_e = Array(31170, 31270, 31430, 31510, 31540, 31550, 31600, 31680, 31810, 31830, 31840, 31870); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -48,28 +58,42 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hi, I'm the assistant here. Don't worry, I'm plenty good enough for this. If you have #b#t5150012##k or #b#t5151006##k by any chance, then allow me to take care of the rest, alright?\r\n#L1#Haircut: #i5150012##t5150012##l\r\n#L2#Dye your hair: #i5151006##t5151006##l"); + cm.sendSimple("Hi, I'm the assistant here. Don't worry, I'm plenty good enough for this. If you have #b#t5150006##k, #b#t5150012##k or #b#t5151006##k by any chance, then allow me to take care of the rest, alright?\r\n#L0#Haircut: #i5150006##t5150006##l\r\n#L1#Haircut: #i5150012##t5150012##l\r\n#L2#Dye your hair: #i5151006##t5151006##l"); } else if (status == 1) { - if (selection == 1) { - beauty = 1; + if (selection == 0) { + beauty = 3; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < mhair_r.length; i++) { + pushIfItemExists(hairnew, mhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < fhair_r.length; i++) { + pushIfItemExists(hairnew, fhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); } } - cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150010##k and really change your hairstyle?"); + cm.sendYesNo("If you use the REG coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150012##k and really change your hairstyle?"); + } else if (selection == 1) { + beauty = 1; + hairnew = Array(); + if (cm.getPlayer().getGender() == 0) { + for(var i = 0; i < mhair_e.length; i++) { + pushIfItemExists(hairnew, mhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); + } + } + if (cm.getPlayer().getGender() == 1) { + for(var i = 0; i < fhair_e.length; i++) { + pushIfItemExists(hairnew, fhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); + } + } + cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150012##k and really change your hairstyle?"); } else if (selection == 2) { beauty = 2; haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5151006##k and change it up?"); } @@ -92,6 +116,14 @@ function action(mode, type, selection) { } else { cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't dye your hair without it. I'm sorry..."); } + } else if (beauty == 3){ + if (cm.haveItem(5150006)){ + cm.gainItem(5150006, -1); + cm.setHair(haircolor[Math.floor(Math.random() * haircolor.length)]); + cm.sendOk("Enjoy your new and improved haircolor!"); + } else { + cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't dye your hair without it. I'm sorry..."); + } } else if (beauty == 0){ if (selection == 0 && cm.getMeso() >= hairprice) { cm.gainMeso(-hairprice); diff --git a/scripts/npc/2041010.js b/scripts/npc/2041010.js index 85c2a18ba2..d76e15c5fd 100644 --- a/scripts/npc/2041010.js +++ b/scripts/npc/2041010.js @@ -21,14 +21,22 @@ /* Ellie Ludibrium VIP Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014); -var fface = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014); +var mface_v = Array(20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20011, 20012, 20014, 20031); +var fface_v = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21010, 21012, 21014); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -47,20 +55,20 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Well, hello! Welcome to the Ludibrium Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152007##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Well, hello! Welcome to the Ludibrium Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152007##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#Plastic Surgery: #i5152007##t5152007##l"); } else if (status == 1) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getPlayer().getFace() + for(var i = 0; i < mface_v.length; i++) { + pushIfItemExists(facenew, mface_v[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getPlayer().getFace() + for(var i = 0; i < fface_v.length; i++) { + pushIfItemExists(facenew, fface_v[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } diff --git a/scripts/npc/2041013.js b/scripts/npc/2041013.js index cbbeb8e313..41f54a4445 100644 --- a/scripts/npc/2041013.js +++ b/scripts/npc/2041013.js @@ -41,7 +41,7 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Oh, hello! Welcome to the Ludibrium Skin-Care! Are you interested in getting tanned and looking sexy? How about a beautiful, snow-white skin? If you have #b#t5153002##k, you can let us take care of the rest and have the kind of skin you've always dreamed of!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Oh, hello! Welcome to the Ludibrium Skin-Care! Are you interested in getting tanned and looking sexy? How about a beautiful, snow-white skin? If you have #b#t5153002##k, you can let us take care of the rest and have the kind of skin you've always dreamed of!\r\n#L2#Skin Care: #i5153002##t5153002##l"); } else if (status == 1) { if (selection == 2) { cm.sendStyle("With our specialized machine, you can see the way you'll look after the treatment PRIOR to the procedure. What kind of a look are you looking for? Go ahead and choose the style of your liking~!", skin); diff --git a/scripts/npc/2090100.js b/scripts/npc/2090100.js index b26fd89017..c4e8b89721 100644 --- a/scripts/npc/2090100.js +++ b/scripts/npc/2090100.js @@ -21,15 +21,23 @@ /* Grandpa Luo Mu Lung VIP Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30250, 30350, 30270, 30150, 30300, 30600, 30160); -var fhair = Array(31040, 31250, 31310, 31220, 31300, 31680, 31160, 31030, 31230); +var mhair_v = Array(30150, 30240, 30370, 30420, 30640, 30710, 30750, 30810); +var fhair_v = Array(31140, 31160, 31180, 31300, 31460, 31470, 31660, 31910); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { cm.sendSimple("Welcome to the Mu Lung hair shop. If you have a #b#t5150025##k, or a #b#t5151020##k, allow me to take care of your hairdo. Please choose the one you want.\r\n#L1#Haircut: #i5150025##t5150025##l\r\n#L2#Dye your hair: #i5151020##t5151020##l"); } @@ -51,14 +59,14 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < mhair_v.length; i++) { + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < fhair_v.length; i++) { + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); } } @@ -69,7 +77,7 @@ function action(mode, type, selection) { var current = parseInt(cm.getPlayer().getHair() /10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendStyle("I can totally change your haircolor and make it look so good. Why don't you change it up a bit? With #b#t5151020##k, I'll take care of the rest. Choose the color of your liking!", haircolor); } diff --git a/scripts/npc/2090101.js b/scripts/npc/2090101.js index ed794588b8..9d23503983 100644 --- a/scripts/npc/2090101.js +++ b/scripts/npc/2090101.js @@ -21,15 +21,23 @@ /* Lilishu Mu Lung Random Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30250, 30350, 30270, 30150, 30300, 30600, 30160, 30700, 30720, 30420); -var fhair = Array(31040, 31250, 31310, 31220, 31300, 31680, 31160, 31030, 31230, 31690, 31210, 31170, 31450); +var mhair_e = Array(30030, 30150, 30240, 30370, 30420, 30550, 30600, 30640, 30700, 30710, 30720, 30750, 30810, 30830); +var fhair_e = Array(31140, 31160, 31180, 31210, 31300, 31430, 31460, 31470, 31660, 31690, 31800, 31890, 31910, 31940); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -54,14 +62,14 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < mhair_e.length; i++) { + pushIfItemExists(hairnew, mhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < fhair_e.length; i++) { + pushIfItemExists(hairnew, fhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); } } @@ -72,7 +80,7 @@ function action(mode, type, selection) { var current = parseInt(cm.getPlayer().getHair() /10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5151019##k and change it up?"); } diff --git a/scripts/npc/2090102.js b/scripts/npc/2090102.js index 346b48ff47..c16055f951 100644 --- a/scripts/npc/2090102.js +++ b/scripts/npc/2090102.js @@ -44,7 +44,7 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Well, hello! Welcome to the Mu Lung Skin-Care! Would you like to have a firm, tight, healthy looking skin like mine? With #b#t5153006##k, you can let us take care of the rest and have the kind of skin you've always wanted~!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Well, hello! Welcome to the Mu Lung Skin-Care! Would you like to have a firm, tight, healthy looking skin like mine? With #b#t5153006##k, you can let us take care of the rest and have the kind of skin you've always wanted~!\r\n#L2#Skin Care: #i5153006##t5153006##l"); } else if (status == 1) { if (selection == 2) { cm.sendStyle("With our specialized machine, you can see the way you'll look after the treatment PRIOR to the procedure. What kind of a look are you looking for? Go ahead and choose the style of your liking~!", skin); diff --git a/scripts/npc/2090103.js b/scripts/npc/2090103.js index f157b7bda0..4edf8d6ed4 100644 --- a/scripts/npc/2090103.js +++ b/scripts/npc/2090103.js @@ -21,12 +21,29 @@ /* Pata Mu Lung Random/VIP Eye Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var regprice = 1000000; -var vipprice = 1000000; -var colors = Array(); +var mface_v = Array(20000, 20001, 20004, 20005, 20006, 20007, 20009, 20012, 20022, 20028, 20031); +var fface_v = Array(21000, 21003, 21005, 21006, 21008, 21009, 21011, 21012, 21023, 21024, 21026); + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} function start() { status = -1; @@ -46,21 +63,26 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hey, I'm Pata, and I am a cosmetic lens expert here in Mu Lung. I believe your eyes are the most important feature in your body, and with #b#t5152042##k or #b#t5152041##k, I can prescribe the right kind of cosmetic lenses for you. Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152042##t5152042##l\r\n#L2#Cosmetic Lenses: #i5152041##t5152041##l"); + cm.sendSimple("Hey, I'm Pata, and I am a renowned plastic surgeon and cosmetic lens expert here in Mu Lung. I believe your face and eyes are the most important features in your body, and with #b#t5152028##k or #b#t5152041##k, I can prescribe the right kind of facial care and cosmetic lenses for you. Now, what would you like to use?\r\n#L1#Plastic Surgery: #i5152028##t5152028##l\r\n#L2#Cosmetic Lenses: #i5152041##t5152041##l\r\n#L3#One-time Cosmetic Lenses: #i5152100# (any color)#l"); } else if (status == 1) { if (selection == 1) { beauty = 1; + facenew = Array(); if (cm.getPlayer().getGender() == 0) { - var current = cm.getPlayer().getFace() - % 100 + 20000; + for(var i = 0; i < mface_v.length; i++) { + pushIfItemExists(facenew, mface_v[i] + cm.getPlayer().getFace() + % 1000 - (cm.getPlayer().getFace() + % 100)); + } } if (cm.getPlayer().getGender() == 1) { - var current = cm.getPlayer().getFace() - % 100 + 21000; + for(var i = 0; i < fface_v.length; i++) { + pushIfItemExists(facenew, fface_v[i] + cm.getPlayer().getFace() + % 1000 - (cm.getPlayer().getFace() + % 100)); + } } - colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); - cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use a #b#t5152042##k and really make the change to your eyes?"); + cm.sendStyle("I can totally transform your face into something new... how about giving us a try? For #b#t5152028##k, you can get the face of your liking...take your time in choosing the face of your preference.", facenew); } else if (selection == 2) { beauty = 2; if (cm.getPlayer().getGender() == 0) { @@ -72,22 +94,46 @@ function action(mode, type, selection) { % 100 + 21000; } colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current , current + 100, current + 300, current + 500, current + 600, current + 700]); cm.sendStyle("With our new computer program, you can see yourself after the treatment in advance. What kind of lens would you like to wear? Please choose the style of your liking.", colors); + } else if (selection == 3) { + beauty = 3; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + + colors = Array(); + for (var i = 0; i < 8; i++) { + if (cm.haveItem(5152100 + i)) { + pushIfItemExists(colors, current + 100 * i); + } + } + + if (colors.length == 0) { + cm.sendOk("You don't have any One-Time Cosmetic Lens to use."); + cm.dispose(); + return; + } + + cm.sendStyle("What kind of lens would you like to wear? Please choose the style of your liking.", colors); } } else if (status == 2){ cm.dispose(); if (beauty == 1){ - if (cm.haveItem(5152042)){ - cm.gainItem(5152042, -1); - cm.setFace(colors[Math.floor(Math.random() * colors.length)]); - cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + if (cm.haveItem(5152028)){ + cm.gainItem(5152028, -1); + cm.setFace(facenew[selection]); + cm.sendOk("Enjoy your new and improved face!"); } else { - cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + cm.sendOk("I'm sorry, but I don't think you have our plastic surgery coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); } - } - if (beauty == 2){ + } else if (beauty == 2){ if (cm.haveItem(5152041)){ cm.gainItem(5152041, -1); cm.setFace(colors[selection]); @@ -95,18 +141,15 @@ function action(mode, type, selection) { } else { cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); } - } - if (beauty == 0){ - if (selection == 0 && cm.getMeso() >= regprice) { - cm.gainMeso(-regprice); - cm.gainItem(5152042, 1); - cm.sendOk("Enjoy!"); - } else if (selection == 1 && cm.getMeso() >= vipprice) { - cm.gainMeso(-vipprice); - cm.gainItem(5152041, 1); - cm.sendOk("Enjoy!"); + } else if (beauty == 3){ + var color = (colors[selection] / 100) % 100 | 0; + + if (cm.haveItem(5152100 + color)){ + cm.gainItem(5152100 + color, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); } else { - cm.sendOk("You don't have enough mesos to buy a coupon!"); + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); } } } diff --git a/scripts/npc/2090104.js b/scripts/npc/2090104.js index 83a0497a93..ae1138ce45 100644 --- a/scripts/npc/2090104.js +++ b/scripts/npc/2090104.js @@ -21,14 +21,29 @@ /* Noma Mu Lung Random/VIP Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var regprice = 1000000; -var vipprice = 1000000; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014, 20009, 20010); -var fface = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014, 21009, 21011); -var facenew = Array(); +var mface_r = Array(20002, 20005, 20007, 20011, 20014, 20017, 20029); +var fface_r = Array(21001, 21010, 21013, 21018, 21020, 21021, 21030); + +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} function start() { status = -1; @@ -48,21 +63,21 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hey, I'm Noma, and I am assiting Pata in changing faces into beautiful things here in Mu Lung. With #b#t5152027##k or #b#t5152028##k, I can change the way you look. Now, what would you like to use?\r\n#L1#Plastic Surgery: #i5152027##t5152027##l\r\n#L2#Plastic Surgery: #i5152028##t5152028##l"); + cm.sendSimple("Hey, I'm Noma, and I am assisting Pata in changing faces and applying lenses as my internship studies. With #b#t5152027##k or #b#t5152042##k, I can change the way you look. Now, what would you like to use?\r\n#L1#Plastic Surgery: #i5152027##t5152027##l\r\n#L2#Cosmetic Lenses: #i5152042##t5152042##l"); } else if (status == 1) { if (selection == 1) { beauty = 1; facenew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getPlayer().getFace() + for(var i = 0; i < mface_r.length; i++) { + pushIfItemExists(facenew, mface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getPlayer().getFace() + for(var i = 0; i < fface_r.length; i++) { + pushIfItemExists(facenew, fface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } @@ -71,20 +86,16 @@ function action(mode, type, selection) { } else if (selection == 2) { beauty = 2; if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getPlayer().getFace() - % 1000 - (cm.getPlayer().getFace() - % 100)); - } + var current = cm.getPlayer().getFace() + % 100 + 20000; } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getPlayer().getFace() - % 1000 - (cm.getPlayer().getFace() - % 100)); - } + var current = cm.getPlayer().getFace() + % 100 + 21000; } - cm.sendStyle("I can totally transform your face into something new... how about giving us a try? For #b#t5152028##k, you can get the face of your liking...take your time in choosing the face of your preference.", facenew); + colors = Array(); + pushIfItemsExists(colors, [current , current + 100, current + 300, current + 500, current + 600, current + 700]); + cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use a #b#t5152042##k and really make the change to your eyes?"); } } else if (status == 2){ @@ -99,25 +110,12 @@ function action(mode, type, selection) { } } if (beauty == 2){ - if (cm.haveItem(5152028)){ - cm.gainItem(5152028, -1); - cm.setFace(facenew[selection]); - cm.sendOk("Enjoy your new and improved face!"); + if (cm.haveItem(5152042)){ + cm.gainItem(5152042, -1); + cm.setFace(colors[Math.floor(Math.random() * colors.length)]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); } else { - cm.sendOk("I'm sorry, but I don't think you have our plastic surgery coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); - } - } - if (beauty == 0){ - if (selection == 0 && cm.getMeso() >= regprice) { - cm.gainMeso(-regprice); - cm.gainItem(5152012, 1); - cm.sendOk("Enjoy!"); - } else if (selection == 1 && cm.getMeso() >= vipprice) { - cm.gainMeso(-vipprice); - cm.gainItem(5152028, 1); - cm.sendOk("Enjoy!"); - } else { - cm.sendOk("You don't have enough mesos to buy a coupon!"); + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); } } } diff --git a/scripts/npc/2100005.js b/scripts/npc/2100005.js index 2319919aa9..02dff1f73c 100644 --- a/scripts/npc/2100005.js +++ b/scripts/npc/2100005.js @@ -2,14 +2,22 @@ NPC Name: Shati Map(s): The Burning Road: Ariant(2600000000) Description: Assistant Hairdresser + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var mhair = Array(30250, 30350, 30270, 30150, 30300, 30600, 30160, 30700, 30720, 30420); -var fhair = Array(31040, 31250, 31310, 31220, 31300, 31680, 31160, 31030, 31230, 31690, 31210, 31170, 31450); +var mhair_r = Array(30150, 30170, 30180, 30320, 30330, 30410, 30460, 30680, 30800, 30820, 30900); +var fhair_r = Array(31090, 31190, 31330, 31340, 31400, 31420, 31520, 31620, 31650, 31660, 34000); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -29,31 +37,31 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hey there! I'm Shatti, and I'm Mazra's apprentice. If you have #bAriant hair style coupon(REG)#k or #bAriant hair color coupon(REG)#k with you, how about allowing me to work on your hair? \r\n#L0##bChange Hairstyle (Reg Coupon) \r\n#L1##bDye Hair(Reg. coupon)"); + cm.sendSimple("Hey there! I'm Shatti, and I'm Mazra's apprentice. If you have #bAriant hair style coupon(REG)#k or #bAriant hair color coupon(REG)#k with you, how about allowing me to work on your hair? \r\n#L0#Haircut: #i5150026##t5150026##l\r\n#L1#Dye your hair: #i5151021##t5151021##l"); } else if (status == 1) { if (selection == 0) { beauty = 1; hairnew = Array(); if (cm.getChar().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getChar().getHair() + for(var i = 0; i < mhair_r.length; i++) { + pushIfItemExists(hairnew, mhair_r[i] + parseInt(cm.getChar().getHair() % 10)); } } if (cm.getChar().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getChar().getHair() + for(var i = 0; i < fhair_r.length; i++) { + pushIfItemExists(hairnew, fhair_r[i] + parseInt(cm.getChar().getHair() % 10)); } } - cm.sendYesNo("If you use the Reg. coupon, your hairstyle will be changed to a random new look. You'll also have access to new hairstyles I worked on that's not available for VIP coupons. Would you like to use #bAriant hair style coupon(REG)#k for a fabulous new look?"); + cm.sendYesNo("If you use the REG coupon, your hairstyle will be changed to a random new look. You'll also have access to new hairstyles I worked on that's not available for VIP coupons. Would you like to use #bAriant hair style coupon(REG)#k for a fabulous new look?"); } else if (selection == 1) { beauty = 2; haircolor = Array(); var current = parseInt(cm.getChar().getHair() /10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendYesNo("If you use the regular coupon, your hair color will change to a random new color. Are you sure you want to use #b#t5151021##k and randomly change your hair color?"); } diff --git a/scripts/npc/2100006.js b/scripts/npc/2100006.js index 54e0e80a1d..9e6a054c2b 100644 --- a/scripts/npc/2100006.js +++ b/scripts/npc/2100006.js @@ -2,15 +2,21 @@ NPC Name: Mazra Map(s): The Burning Road: Ariant(2600000000) Description: Hair Salon Owner + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var mhair = Array(30030, 30020, 30000, 30130, 30350, 30190, 30110, 30180, 30050, 30040, 30160); -var fhair = Array(31050, 31040, 31000, 31060, 31090, 31020, 31130, 31120, 31140, 31330, 31010); +var mhair_v = Array(30150, 30170, 30180, 30320, 30330, 30410, 30460, 30820, 30900); +var fhair_v = Array(31040, 31090, 31190, 31330, 31340, 31400, 31420, 31620, 31660); var hairnew = Array(); - +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} function start() { status = -1; @@ -30,20 +36,20 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hahaha... it takes a lot of style and flair for someone to pay attention to his or her hairsyle in a desert. Someone like you...If you have #bAriant hair style coupon(VIP)#k or #bAriant hair color coupon(VIP)#k, I'll give your hair a fresh new look. \r\n#L0##bChange Hairstyle(VIP Coupon)#k#l \r\n#L1##bDye Hair(VIP Coupon)#k#l"); + cm.sendSimple("Hahaha... it takes a lot of style and flair for someone to pay attention to his or her hairsyle in a desert. Someone like you...If you have #bAriant hair style coupon(VIP)#k or #bAriant hair color coupon(VIP)#k, I'll give your hair a fresh new look. \r\n#L0#Haircut: #i5150027##t5150027##l\r\n#L1#Dye your hair: #i5151022##t5151022##l"); } else if (status == 1) { if (selection == 0) { beauty = 1; hairnew = Array(); if (cm.getChar().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getChar().getHair() + for(var i = 0; i < mhair_v.length; i++) { + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getChar().getHair() % 10)); } } if (cm.getChar().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getChar().getHair() + for(var i = 0; i < fhair_v.length; i++) { + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getChar().getHair() % 10)); } } @@ -54,7 +60,7 @@ function action(mode, type, selection) { var current = parseInt(cm.getChar().getHair() /10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendStyle("Every once in a while, it doesn't hurt to change up your hair color... it's fun. Allow me, the great Mazra, to dye your hair, so you just bring me #bAriant hair color coupon(VIP)#k, and choose your new hair color.", haircolor); } diff --git a/scripts/npc/2100008.js b/scripts/npc/2100008.js index 8f54c40b88..ac28e0c1b1 100644 --- a/scripts/npc/2100008.js +++ b/scripts/npc/2100008.js @@ -1,15 +1,33 @@ /* Author: aaroncsn - NPC Name: Badr + NPC Name: Vard Map(s): The Burning Road: Ariant(2600000000) Description: Ariant Plastic Surgery + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014); -var fface = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014); +var mface_v = Array(20000, 20004, 20005, 20012, 20013, 20031); +var fface_v = Array(21000, 21003, 21006, 21009, 21012, 21024); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + function start() { status = -1; action(1, 0, 0); @@ -27,33 +45,99 @@ function action(mode, type, selection) { status++; else status--; - if (status == 0) { - facenew = Array(); - if (cm.getChar().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getChar().getFace() - % 1000 - (cm.getChar().getFace() - % 100)); - } - } - if (cm.getChar().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getChar().getFace() - % 1000 - (cm.getChar().getFace() - % 100)); - } - } - cm.sendStyle("Hmmm... Face of beauty glows even under cover and burning desert. With #bAriant face coupon(VIP)#k, I can make your face so much better. Choose the face you want, and I will pull out my outstanding skill for the great make over.", facenew); - } - else if (status == 1){ + + if (status == 0) { + cm.sendSimple("Ah, welcome to the Ariant Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152030##k or a #b#t5152047##k, I can make your face so much better!\r\n#L1#Plastic Surgery: #i5152030##t5152030##l\r\n#L2#Cosmetic Lens: #i5152047##t5152047##l\r\n#L3#One-time Cosmetic Lenses: #i5152101# (any color)#l"); + } else if (status == 1) { + if (selection == 1) { + beauty = 0; + + facenew = Array(); + if (cm.getChar().getGender() == 0) { + for(var i = 0; i < mface_v.length; i++) { + pushIfItemExists(facenew, mface_v[i] + cm.getChar().getFace() + % 1000 - (cm.getChar().getFace() + % 100)); + } + } + if (cm.getChar().getGender() == 1) { + for(var i = 0; i < fface.length; i++) { + pushIfItemExists(facenew, fface[i] + cm.getChar().getFace() + % 1000 - (cm.getChar().getFace() + % 100)); + } + } + cm.sendStyle("Hmmm... Face of beauty glows even under cover and burning desert. Choose the face you want, and I will pull out my outstanding skill for the great make over.", facenew); + } else if (selection == 2) { + beauty = 1; + + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + colors = Array(); + pushIfItemsExists(colors, [current , current + 100, current + 300, current + 600, current + 700]); + cm.sendStyle("With the utmost finesse matching that of the sparkling sands of the desert that gleefully embraces the rooftop of the Palace, we will make your eyes shine even brighter with the new lenses. Select the one you want to use...", colors); + } else if (selection == 3) { + beauty = 3; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + + colors = Array(); + for (var i = 0; i < 8; i++) { + if (cm.haveItem(5152100 + i)) { + pushIfItemExists(colors, current + 100 * i); + } + } + + if (colors.length == 0) { + cm.sendOk("You don't have any One-Time Cosmetic Lens to use."); + cm.dispose(); + return; + } + + cm.sendStyle("What kind of lens would you like to wear? Please choose the style of your liking.", colors); + } + } else if (status == 2){ cm.dispose(); - if (cm.haveItem(5152030) == true){ - cm.gainItem(5152030, -1); - cm.setFace(facenew[selection]); - cm.sendOk("Enjoy your new and improved face!"); - } else { - cm.sendNext("Erm... You don't seem to have the exclusive coupon for this hospital. Without the coupon, I'm afraid I can't do it for you."); - } + + if (beauty == 0) { + if (cm.haveItem(5152030) == true){ + cm.gainItem(5152030, -1); + cm.setFace(facenew[selection]); + cm.sendOk("Enjoy your new and improved face!"); + } else { + cm.sendNext("Erm... You don't seem to have the exclusive coupon for this hospital. Without the coupon, I'm afraid I can't do it for you."); + } + } else if (beauty == 1) { + if (cm.haveItem(5152047) == true){ + cm.gainItem(5152047, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("Hmm ... it looks like you don't have the coupon specifically for this place. Sorry to say this, but without the coupon, there's no plastic surgery for you..."); + } + } else if (beauty == 3){ + var color = (colors[selection] / 100) % 100 | 0; + + if (cm.haveItem(5152100 + color)){ + cm.gainItem(5152100 + color, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } + } } } } \ No newline at end of file diff --git a/scripts/npc/2100009.js b/scripts/npc/2100009.js index 394a5e73f5..5da61a5c5c 100644 --- a/scripts/npc/2100009.js +++ b/scripts/npc/2100009.js @@ -2,14 +2,32 @@ NPC Name: Aldin Map(s): The Burning Road: Ariant(2600000000) Description: Ariant Plastic Surgery + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014); -var fface = Array(21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014); +var mface_r = Array(20001, 20003, 20009, 20010, 20025, 20031); +var fface_r = Array(21002, 21009, 21011, 21013, 21016, 21029, 21030); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + function start() { status = -1; action(1, 0, 0); @@ -28,33 +46,63 @@ function action(mode, type, selection) { status++; else status--; - if (status == 0) { - facenew = Array(); - if (cm.getChar().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getChar().getFace() - % 1000 - (cm.getChar().getFace() - % 100)); - } - } - if (cm.getChar().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getChar().getFace() - % 1000 - (cm.getChar().getFace() - % 100)); - } - } - cm.sendYesNo("If you use the regular coupon, your face may transform into a random new look...do you still want to do it using #b#t5152029##k?"); - } - else if (status == 1){ + + if (status == 0) { + cm.sendSimple("Hi, I'm the face surgery assistant doctor from around here. With a #b#t5152029##k or a #b#t5152048##k, I can make it kick in just nice, trust me. Ah, don't forget, what comes next after the operation will be random! Then, what are you going for?\r\n#L1#Plastic Surgery: #i5152029##t5152029##l\r\n#L2#Cosmetic Lens: #i5152048##t5152048##l"); + } else if (status == 1) { + if (selection == 1) { + beauty = 0; + + facenew = Array(); + if (cm.getChar().getGender() == 0) { + for(var i = 0; i < mface_r.length; i++) { + pushIfItemExists(facenew, mface_r[i] + cm.getChar().getFace() + % 1000 - (cm.getChar().getFace() + % 100)); + } + } + if (cm.getChar().getGender() == 1) { + for(var i = 0; i < fface_r.length; i++) { + pushIfItemExists(facenew, fface_r[i] + cm.getChar().getFace() + % 1000 - (cm.getChar().getFace() + % 100)); + } + } + cm.sendYesNo("If you use the regular coupon, your face may transform into a random new look...do you still want to do it using #b#t5152029##k?"); + } else if (selection == 2) { + beauty = 1; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + colors = Array(); + pushIfItemsExists(colors, [current , current + 100, current + 300, current + 600, current + 700]); + cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use a #b#t5152048##k and really make the change to your eyes?"); + } + } else if (status == 2){ cm.dispose(); - if (cm.haveItem(5152029) == true){ - cm.gainItem(5152029, -1); - cm.setFace(facenew[Math.floor(Math.random() * facenew.length)]); - cm.sendOk("Enjoy your new and improved face!"); - } else { - cm.sendNext("Um ... it looks like you don't have the coupon specifically for this place...sorry to say this, but without the coupon, there's no plastic surgery for you."); - } + + if (beauty == 0) { + if (cm.haveItem(5152029) == true){ + cm.gainItem(5152029, -1); + cm.setFace(facenew[Math.floor(Math.random() * facenew.length)]); + cm.sendOk("Enjoy your new and improved face!"); + } else { + cm.sendNext("Um ... it looks like you don't have the coupon specifically for this place...sorry to say this, but without the coupon, there's no plastic surgery for you."); + } + } else if (beauty == 1) { + if (cm.haveItem(5152048)){ + cm.gainItem(5152048, -1); + cm.setFace(colors[Math.floor(Math.random() * colors.length)]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("Hmm ... it looks like you don't have the coupon specifically for this place. Sorry to say this, but without the coupon, there's no plastic surgery for you..."); + } + } } } } \ No newline at end of file diff --git a/scripts/npc/9120100.js b/scripts/npc/9120100.js index 53aa159e58..eb4810a715 100644 --- a/scripts/npc/9120100.js +++ b/scripts/npc/9120100.js @@ -21,15 +21,23 @@ /* Tepei Showa VIP Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30230, 30030, 30260, 30280, 30240, 30290, 30020, 30270, 30340, 30710, 30810); -var fhair = Array(31310, 31300, 31050, 31040, 31160, 31100, 31410, 31030, 31790, 31550); +var mhair_v = Array(30260, 30280, 30340, 30710, 30780, 30800, 30810, 30820, 30920); +var fhair_v = Array(31000, 31030, 31100, 31350, 31460, 31550, 31770, 31790, 31850); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -54,13 +62,13 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair()% 10)); + for(var i = 0; i < mhair_v.length; i++) { + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair()% 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair()% 10)); + for(var i = 0; i < fhair_v.length; i++) { + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getPlayer().getHair()% 10)); } } cm.sendStyle("I can totally change up your hairstyle and make it look so good. Why don't you change it up a bit? With #b#t5150009##k, I'll take care of the rest for you. Choose the style of your liking!", hairnew); @@ -69,7 +77,7 @@ function action(mode, type, selection) { haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendStyle("I can totally change your haircolor and make it look so good. Why don't you change it up a bit? With #b#t5151009##k, I'll take care of the rest. Choose the color of your liking!", haircolor); } diff --git a/scripts/npc/9120101.js b/scripts/npc/9120101.js index d07f5102d4..70df6cfea9 100644 --- a/scripts/npc/9120101.js +++ b/scripts/npc/9120101.js @@ -19,17 +19,25 @@ along with this program. If not, see . */ -/* Unkown - Showa Town VIP Hair/Hair Color Change. +/* Midori + Showa Random Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30000, 30120, 30140, 30190, 30210, 30360, 30220, 30370, 30400, 30440, 30790, 30800, 30810, 30770, 30760); -var fhair = Array(31030, 31050, 31000, 31070, 31100, 31120, 31130, 31250, 31340, 31680, 31350, 31400, 31650, 31550, 31800); +var mhair_r = Array(30260, 30280, 30340, 30360, 30710, 30780, 30790, 30800, 30810, 30820, 30920); +var fhair_r = Array(31350, 31410, 31460, 31540, 31550, 31710, 31720, 31770, 31790, 31800, 31850, 34000); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -48,63 +56,58 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("I'm the head of Showa hair salon. If you have a #b#t5150009##k or a #b#t5151009##k allow me to take care of your hairdo. Please choose the one you want.\r\n#L1#Haircut: #i5150009##t5150009##l\r\n#L2#Dye your hair: #i5151009##t5151009##l"); + cm.sendSimple("Hi, I'm the assistant here. Don't worry, I'm plenty good enough for this. If you have #b#t5150008##k or #b#t5151008##k by any chance, then allow me to take care of the rest, alright?\r\n#L1#Haircut: #i5150008##t5150008##l\r\n#L2#Dye your hair: #i5151008##t5151008##l"); } else if (status == 1) { if (selection == 1) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() - % 10)); + for(var i = 0; i < mhair_r.length; i++) { + pushIfItemExists(hairnew, mhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() - % 10)); + for(var i = 0; i < fhair_r.length; i++) { + pushIfItemExists(hairnew, fhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); } } - cm.sendStyle("I can totally change up your hairstyle and make it look so good. Why don't you change it up a bit? If you have #b#t5150009##k I'll change it for you. Choose the one to your liking~.", hairnew); + cm.sendYesNo("If you use the REG coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150008##k and really change your hairstyle?"); } else if (selection == 2) { beauty = 2; haircolor = Array(); - var current = parseInt(cm.getPlayer().getHair() - /10)*10; + var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } - cm.sendStyle("I can totally change your haircolor and make it look so good. Why don't you change it up a bit? With #b#t51051009##k I'll change it for you. Choose the one to your liking.", haircolor); + cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5151008##k and change it up?"); } } else if (status == 2){ cm.dispose(); if (beauty == 1){ - if (cm.haveItem(5150009)){ - cm.gainItem(5150009, -1); - cm.setHair(hairnew[selection]); + if (cm.haveItem(5150008)){ + cm.gainItem(5150008, -1); + cm.setHair(hairnew[Math.floor(Math.random() * hairnew.length)]); cm.sendOk("Enjoy your new and improved hairstyle!"); } else { cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); } - } - if (beauty == 2){ - if (cm.haveItem(5151009)){ - cm.gainItem(5151009, -1); - cm.setHair(haircolor[selection]); + } else if (beauty == 2){ + if (cm.haveItem(5151008)){ + cm.gainItem(5151008, -1); + cm.setHair(haircolor[Math.floor(Math.random() * haircolor.length)]); cm.sendOk("Enjoy your new and improved haircolor!"); } else { cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't dye your hair without it. I'm sorry..."); } - } - if (beauty == 0){ + } else if (beauty == 0){ if (selection == 0 && cm.getMeso() >= hairprice) { cm.gainMeso(-hairprice); - cm.gainItem(5150009, 1); + cm.gainItem(5150008, 1); cm.sendOk("Enjoy!"); } else if (selection == 1 && cm.getMeso() >= haircolorprice) { cm.gainMeso(-haircolorprice); - cm.gainItem(5151009, 1); + cm.gainItem(5151008, 1); cm.sendOk("Enjoy!"); } else { cm.sendOk("You don't have enough mesos to buy a coupon!"); diff --git a/scripts/npc/9120102.js b/scripts/npc/9120102.js new file mode 100644 index 0000000000..b8bf440aa9 --- /dev/null +++ b/scripts/npc/9120102.js @@ -0,0 +1,163 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + Copyleft (L) 2016 - 2018 RonanLana (HeavenMS) + + 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 . +*/ + +/* Hikekuro the Owner + Showa VIP Face & Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove +*/ +var status = 0; +var beauty = 0; +var price = 1000000; +var mface_v = Array(20000, 20004, 20005, 20012, 20020, 20031); +var fface_v = Array(21000, 21003, 21006, 21012, 21021, 21024); +var facenew = Array(); +var colors = Array(); + +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && status == 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + if (status == 0) { + cm.sendSimple("Well well well, welcome to the Showa Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152009##k or a #b#t5152045##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L1#Plastic Surgery: #i5152009##t5152009##l\r\n#L2#Cosmetic Lens: #i5152045##t5152045##l\r\n#L3#One-time Cosmetic Lenses: #i5152102# (any color)#l"); + } else if (status == 1) { + if (selection == 1) { + beauty = 0; + facenew = Array(); + if (cm.getPlayer().getGender() == 0) { + for(var i = 0; i < mface_v.length; i++) { + pushIfItemExists(facenew, mface_v[i] + cm.getPlayer().getFace() + % 1000 - (cm.getPlayer().getFace() + % 100)); + } + } + if (cm.getPlayer().getGender() == 1) { + for(var i = 0; i < fface_v.length; i++) { + pushIfItemExists(facenew, fface_v[i] + cm.getPlayer().getFace() + % 1000 - (cm.getPlayer().getFace() + % 100)); + } + } + cm.sendStyle("I can totally transform your face into something new... how about giving us a try? For #b#t5152009##k, you can get the face of your liking...take your time in choosing the face of your preference.", facenew); + } else if (selection == 2) { + beauty = 1; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + colors = Array(); + pushIfItemsExists(colors, [current , current + 100, current + 200, current + 300, current +400, current + 500, current + 700]); + cm.sendStyle("With our new computer program, you can see yourself after the treatment in advance. What kind of lens would you like to wear? Please choose the style of your liking.", colors); + } else if (selection == 3) { + beauty = 3; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + + colors = Array(); + for (var i = 0; i < 8; i++) { + if (cm.haveItem(5152100 + i)) { + pushIfItemExists(colors, current + 100 * i); + } + } + + if (colors.length == 0) { + cm.sendOk("You don't have any One-Time Cosmetic Lens to use."); + cm.dispose(); + return; + } + + cm.sendStyle("What kind of lens would you like to wear? Please choose the style of your liking.", colors); + } + } + else if (status == 2){ + cm.dispose(); + + if (beauty == 0) { + if (cm.haveItem(5152009) == true){ + cm.gainItem(5152009, -1); + cm.setFace(facenew[selection]); + cm.sendOk("Enjoy your new and improved face!"); + } else { + cm.sendOk("Hmm ... it looks like you don't have the coupon specifically for this place. Sorry to say this, but without the coupon, there's no plastic surgery for you..."); + } + } else if (beauty == 1) { + if (cm.haveItem(5152045) == true){ + cm.gainItem(5152045, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("Hmm ... it looks like you don't have the coupon specifically for this place. Sorry to say this, but without the coupon, there's no plastic surgery for you..."); + } + } else if (beauty == 3){ + var color = (colors[selection] / 100) % 100 | 0; + + if (cm.haveItem(5152100 + color)){ + cm.gainItem(5152100 + color, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } + } + } + } +} diff --git a/scripts/npc/9120103.js b/scripts/npc/9120103.js new file mode 100644 index 0000000000..97641fe734 --- /dev/null +++ b/scripts/npc/9120103.js @@ -0,0 +1,110 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + Copyleft (L) 2016 - 2018 RonanLana (HeavenMS) + + 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 . +*/ + +/* Saeko the Assistant + Showa Random Face & Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove +*/ +var status = 0; +var beauty = 0; +var price = 1000000; +var mface_r = Array(20000, 20016, 20019, 20020, 20021, 20024, 20026); +var fface_r = Array(21000, 21002, 21009, 21016, 21022, 21025, 21027); +var facenew = Array(); +var colors = Array(); + +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + +function start() { + cm.sendSimple("Hi, I pretty much shouldn't be doing this, but with a #b#t5152008##k or a #b#t5152046##k, I will do it anyways for you. But don't forget, it will be random!\r\n#L1#Plastic Surgery: #i5152008##t5152008##l\r\n#L2#Cosmetic Lens: #i5152046##t5152046##l"); +} + +function action(mode, type, selection) { + if (mode < 1) { + cm.dispose(); + } else { + status++; + if (status == 1) { + if (selection == 1) { + beauty = 0; + facenew = Array(); + if (cm.getPlayer().getGender() == 0) + for (var i = 0; i < mface_r.length; i++) + pushIfItemExists(facenew, mface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + else + for (var i = 0; i < fface_r.length; i++) + pushIfItemExists(facenew, fface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + cm.sendYesNo("If you use the regular coupon, your face may transform into a random new look...do you still want to do it using #b#t5152008##k?"); + } else if (selection == 2) { + beauty = 1; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + colors = Array(); + pushIfItemsExists(colors, [current , current + 100, current + 200, current + 300, current +400, current + 500, current + 700]); + cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use a #b#t5152046##k and really make the change to your eyes?"); + } + } + else if (status == 2){ + if (beauty == 0) { + if (cm.haveItem(5152008)){ + cm.gainItem(5152008, -1); + cm.setFace(facenew[Math.floor(Math.random() * facenew.length)]); + cm.sendOk("Enjoy your new and improved face!"); + } else { + cm.sendOk("Hmm ... it looks like you don't have the coupon specifically for this place. Sorry to say this, but without the coupon, there's no plastic surgery for you..."); + } + } else if (beauty == 1) { + if (cm.haveItem(5152046)){ + cm.gainItem(5152046, -1); + cm.setFace(colors[Math.floor(Math.random() * colors.length)]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("Hmm ... it looks like you don't have the coupon specifically for this place. Sorry to say this, but without the coupon, there's no plastic surgery for you..."); + } + } + + cm.dispose(); + } + } +} diff --git a/scripts/npc/9200100.js b/scripts/npc/9200100.js index d97485e95c..516d82248c 100644 --- a/scripts/npc/9200100.js +++ b/scripts/npc/9200100.js @@ -28,6 +28,22 @@ var regprice = 1000000; var vipprice = 1000000; var colors = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + function start() { status = -1; action(1, 0, 0); @@ -44,7 +60,7 @@ function action(mode, type, selection) { else status--; if (status == 0) - cm.sendSimple("Hi, there~! I'm Dr. Lenu, in charge of the cosmetic lenses here at the Henesys Plastic Surgery Shop! With #b#t5152010##k or #b#t5152013##k, you can let us take care of the rest and have the kind of beautiful look you've always craved~! Remember, the first thing everyone notices about you is the eyes, and we can help you find the cosmetic lens that most fits you! Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152010##t5152010##l\r\n#L2#Cosmetic Lenses: #i5152013##t5152013##l"); + cm.sendSimple("Hi, there~! I'm Dr. Lenu, in charge of the cosmetic lenses here at the Henesys Plastic Surgery Shop! With #b#t5152010##k or #b#t5152013##k, you can let us take care of the rest and have the kind of beautiful look you've always craved~! Remember, the first thing everyone notices about you is the eyes, and we can help you find the cosmetic lens that most fits you! Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152010##t5152010##l\r\n#L2#Cosmetic Lenses: #i5152013##t5152013##l\r\n#L3#One-time Cosmetic Lenses: #i5152103# (any color)#l"); else if (status == 1) { if (selection == 1) { beauty = 1; @@ -53,7 +69,7 @@ function action(mode, type, selection) { if (cm.getPlayer().getGender() == 1) var current = cm.getPlayer().getFace()% 100 + 21000; colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current , current + 100, current + 200, current +400, current + 600, current + 700]); cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use a #b#t5152010##k and really make the change to your eyes?"); } else if (selection == 2) { beauty = 2; @@ -62,8 +78,33 @@ function action(mode, type, selection) { if (cm.getPlayer().getGender() == 1) var current = cm.getPlayer().getFace() % 100 + 21000; colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current , current + 100, current + 200, current +400, current + 600, current + 700]); cm.sendStyle("With our specialized machine, you can see yourself after the treatment in advance. What kind of lens would you like to wear? Choose the style of your liking.", colors); + } else if (selection == 3) { + beauty = 3; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + + colors = Array(); + for (var i = 0; i < 8; i++) { + if (cm.haveItem(5152100 + i)) { + pushIfItemExists(colors, current + 100 * i); + } + } + + if (colors.length == 0) { + cm.sendOk("You don't have any One-Time Cosmetic Lens to use."); + cm.dispose(); + return; + } + + cm.sendStyle("What kind of lens would you like to wear? Please choose the style of your liking.", colors); } } else if (status == 2){ @@ -75,16 +116,24 @@ function action(mode, type, selection) { cm.sendOk("Enjoy your new and improved cosmetic lenses!"); } else cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); - } - if (beauty == 2){ + } else if (beauty == 2){ if (cm.haveItem(5152013) == true){ cm.gainItem(5152013, -1); cm.setFace(colors[selection]); cm.sendOk("Enjoy your new and improved cosmetic lenses!"); } else cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); - } - if (beauty == 0){ + } else if (beauty == 3){ + var color = (colors[selection] / 100) % 100 | 0; + + if (cm.haveItem(5152100 + color)){ + cm.gainItem(5152100 + color, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } + } else if (beauty == 0){ if (selection == 0 && cm.getMeso() >= regprice) { cm.gainMeso(-regprice); cm.gainItem(5152010, 1); diff --git a/scripts/npc/9200101.js b/scripts/npc/9200101.js index 13a3c7ae46..fc5a8a8a6b 100644 --- a/scripts/npc/9200101.js +++ b/scripts/npc/9200101.js @@ -28,6 +28,22 @@ var regprice = 1000000; var vipprice = 1000000; var colors = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + function start() { status = -1; action(1, 0, 0); @@ -46,7 +62,7 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hello, I'm Dr. Rhomes, head of the cosmetic lens department here at the Orbis Plastic Surgery Shop.\r\nMy goal here is to add personality to everyone's eyes through the wonders of cosmetic lenses, and with #b#t5152011##k or #b#t5152014##k, I can do the same for you, too! Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152011##t5152011##l\r\n#L2#Cosmetic Lenses: #i5152014##t5152014##l"); + cm.sendSimple("Hello, I'm Dr. Rhomes, head of the cosmetic lens department here at the Orbis Plastic Surgery Shop.\r\nMy goal here is to add personality to everyone's eyes through the wonders of cosmetic lenses, and with #b#t5152011##k or #b#t5152014##k, I can do the same for you, too! Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152011##t5152011##l\r\n#L2#Cosmetic Lenses: #i5152014##t5152014##l\r\n#L3#One-time Cosmetic Lenses: #i5152104# (any color)#l"); } else if (status == 1) { if (selection == 1) { beauty = 1; @@ -59,7 +75,7 @@ function action(mode, type, selection) { % 100 + 21000; } colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current + 100, current + 300, current +400, current + 700]); cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use a #b#t5152011##k and really make the change to your eyes?"); } else if (selection == 2) { beauty = 2; @@ -72,8 +88,33 @@ function action(mode, type, selection) { % 100 + 21000; } colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current + 100, current + 300, current +400, current + 700]); cm.sendStyle("With our new computer program, you can see yourself after the treatment in advance. What kind of lens would you like to wear? Please choose the style of your liking.", colors); + } else if (selection == 3) { + beauty = 3; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + + colors = Array(); + for (var i = 0; i < 8; i++) { + if (cm.haveItem(5152100 + i)) { + pushIfItemExists(colors, current + 100 * i); + } + } + + if (colors.length == 0) { + cm.sendOk("You don't have any One-Time Cosmetic Lens to use."); + cm.dispose(); + return; + } + + cm.sendStyle("What kind of lens would you like to wear? Please choose the style of your liking.", colors); } } else if (status == 2){ @@ -86,8 +127,7 @@ function action(mode, type, selection) { } else { cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); } - } - if (beauty == 2){ + } else if (beauty == 2){ if (cm.haveItem(5152014)){ cm.gainItem(5152014, -1); cm.setFace(colors[selection]); @@ -95,8 +135,17 @@ function action(mode, type, selection) { } else { cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); } - } - if (beauty == 0){ + } else if (beauty == 3){ + var color = (colors[selection] / 100) % 100 | 0; + + if (cm.haveItem(5152100 + color)){ + cm.gainItem(5152100 + color, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } + } else if (beauty == 0){ if (selection == 0 && cm.getMeso() >= regprice) { cm.gainMeso(-regprice); cm.gainItem(5152011, 1); diff --git a/scripts/npc/9200102.js b/scripts/npc/9200102.js index 3c16150d1e..78ff68ade6 100644 --- a/scripts/npc/9200102.js +++ b/scripts/npc/9200102.js @@ -28,6 +28,22 @@ var regprice = 1000000; var vipprice = 1000000; var colors = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + function start() { status = -1; action(1, 0, 0); @@ -46,7 +62,7 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Um... hi, I'm Dr. Bosch, and I am a cosmetic lens expert here at the Ludibrium Plastic Surgery Shop. I believe your eyes are the most important feature in your body, and with #b#t5152012##k or #b#t5152015##k, I can prescribe the right kind of cosmetic lenses for you. Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152012##t5152012##l\r\n#L2#Cosmetic Lenses: #i5152015##t5152015##l"); + cm.sendSimple("Um... hi, I'm Dr. Bosch, and I am a cosmetic lens expert here at the Ludibrium Plastic Surgery Shop. I believe your eyes are the most important feature in your body, and with #b#t5152012##k or #b#t5152015##k, I can prescribe the right kind of cosmetic lenses for you. Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152012##t5152012##l\r\n#L2#Cosmetic Lenses: #i5152015##t5152015##l\r\n#L3#One-time Cosmetic Lenses: #i5152105# (any color)#l"); } else if (status == 1) { if (selection == 1) { beauty = 1; @@ -57,7 +73,7 @@ function action(mode, type, selection) { var current = cm.getPlayer().getFace() % 100 + 21000; } colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current + 200, current + 300, current +400, current + 500, current + 700]); cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use a #b#t5152012##k and really make the change to your eyes?"); } else if (selection == 2) { beauty = 2; @@ -68,8 +84,33 @@ function action(mode, type, selection) { var current = cm.getPlayer().getFace() % 100 + 21000; } colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current + 200, current + 300, current +400, current + 500, current + 700]); cm.sendStyle("With our new computer program, you can see yourself after the treatment in advance. What kind of lens would you like to wear? Please choose the style of your liking.", colors); + } else if (selection == 3) { + beauty = 3; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + + colors = Array(); + for (var i = 0; i < 8; i++) { + if (cm.haveItem(5152100 + i)) { + pushIfItemExists(colors, current + 100 * i); + } + } + + if (colors.length == 0) { + cm.sendOk("You don't have any One-Time Cosmetic Lens to use."); + cm.dispose(); + return; + } + + cm.sendStyle("What kind of lens would you like to wear? Please choose the style of your liking.", colors); } } else if (status == 2){ @@ -83,8 +124,7 @@ function action(mode, type, selection) { cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); cm.dispose(); } - } - if (beauty == 2){ + } else if (beauty == 2){ if (cm.haveItem(5152015) == true){ cm.gainItem(5152015, -1); cm.setFace(colors[selection]); @@ -94,8 +134,17 @@ function action(mode, type, selection) { cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); cm.dispose(); } - } - if (beauty == 0){ + } else if (beauty == 3){ + var color = (colors[selection] / 100) % 100 | 0; + + if (cm.haveItem(5152100 + color)){ + cm.gainItem(5152100 + color, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } + } else if (beauty == 0){ if (selection == 0 && cm.getMeso() >= regprice) { cm.gainMeso(-regprice); cm.gainItem(5152012, 1); diff --git a/scripts/npc/9201015.js b/scripts/npc/9201015.js index 689222d894..58ecd8a92a 100644 --- a/scripts/npc/9201015.js +++ b/scripts/npc/9201015.js @@ -21,15 +21,23 @@ /* Julius Styleman Amoria VIP Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30580, 30590, 30280, 30670, 30410, 30200, 30050, 30230, 30290, 30300, 30250); -var fhair = Array(31580, 31590, 31310, 31200, 31150, 31160, 31020, 31260, 31230, 31220, 31110); +var mhair_v = Array(30050, 30300, 30410, 30450, 30510, 30570, 30580, 30590, 30660, 30910); +var fhair_v = Array(31150, 31220, 31260, 31310, 31420, 31480, 31490, 31580, 31590, 31610, 31630); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -54,14 +62,14 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < mhair_v.length; i++) { + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < fhair_v.length; i++) { + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); } } @@ -72,7 +80,7 @@ function action(mode, type, selection) { var current = parseInt(cm.getPlayer().getHair() /10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendStyle("I can totally change your haircolor and make it look so good. Why don't you change it up a bit? With #b#t5151017##k, I'll take care of the rest. Choose the color of your liking!", haircolor); } diff --git a/scripts/npc/9201016.js b/scripts/npc/9201016.js index da4de68582..fb1d2d799e 100644 --- a/scripts/npc/9201016.js +++ b/scripts/npc/9201016.js @@ -21,15 +21,23 @@ */ /* Salon Seamus Amoria Random Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30570, 30690, 30250, 30230, 30050, 30280, 30410, 30290, 30300, 30580, 30590, 30200, 30450); -var fhair = Array(31490, 31570, 31150, 31590, 31310, 31220, 31260, 31020, 31160, 31110, 31230, 31580, 31480); +var mhair_e = Array(30000, 30020, 30110, 30130, 30160, 30190, 30240, 30270, 30430); +var fhair_e = Array(31000, 31030, 31050, 31070, 31090, 31150, 31310, 31910, 34010); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -54,14 +62,14 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < mhair_e.length; i++) { + pushIfItemExists(hairnew, mhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() + for(var i = 0; i < fhair_e.length; i++) { + pushIfItemExists(hairnew, fhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); } } @@ -72,9 +80,9 @@ function action(mode, type, selection) { var current = parseInt(cm.getPlayer().getHair() /10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } - cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5150019##k and change it up?"); + cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5150016##k and change it up?"); } } else if (status == 2){ @@ -112,98 +120,4 @@ function action(mode, type, selection) { } } } -} -/* Salon Seamus - Amoria Random Hair/Hair Color Change. -*/ -var status = 0; -var beauty = 0; -var hairprice = 1000000; -var haircolorprice = 1000000; -var mhair = Array(30570, 30690, 30250, 30230, 30050, 30280, 30410, 30290, 30300, 30580, 30590, 30200, 30450); -var fhair = Array(31490, 31570, 31150, 31590, 31310, 31220, 31260, 31020, 31160, 31110, 31230, 31580, 31480); -var hairnew = Array(); - -function start() { - status = -1; - action(1, 0, 0); -} - -function action(mode, type, selection) { - if (mode == -1) { - cm.dispose(); - } else { - if (mode == 0 && status == 0) { - cm.dispose(); - return; - } - if (mode == 1) - status++; - else - status--; - if (status == 0) { - cm.sendSimple("I'm Salon Seamus. If you have #b#t5150019##k or #b#t5151016##k by any chance, then how about letting me change your hairdo?\r\n#L1#Haircut: #i5150019##t5150019##l\r\n#L2#Dye your hair: #i5151016##t5151016##l"); - } else if (status == 1) { - if (selection == 1) { - beauty = 1; - hairnew = Array(); - if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() - % 10)); - } - } - if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() - % 10)); - } - } - cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that even you didn't think was possible. Are you going to use #b#t5150019##k and really change your hairstyle?"); - } else if (selection == 2) { - beauty = 2; - haircolor = Array(); - var current = parseInt(cm.getPlayer().getHair() - /10)*10; - for(var i = 0; i < 8; i++) { - haircolor.push(current + i); - } - cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5151016##k and change it up?"); - } - } - else if (status == 2){ - cm.dispose(); - if (beauty == 1){ - if (cm.haveItem(5150019) == true){ - cm.gainItem(5150019, -1); - cm.setHair(hairnew[Math.floor(Math.random() * hairnew.length)]); - cm.sendOk("Enjoy your new and improved hairstyle!"); - } else { - cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't give you a haircut without it. I'm sorry..."); - } - } - if (beauty == 2){ - if (cm.haveItem(5151016) == true){ - cm.gainItem(5151016, -1); - cm.setHair(haircolor[Math.floor(Math.random() * haircolor.length)]); - cm.sendOk("Enjoy your new and improved haircolor!"); - } else { - cm.sendOk("Hmmm...it looks like you don't have our designated coupon...I'm afraid I can't dye your hair without it. I'm sorry..."); - } - } - if (beauty == 0){ - if (selection == 0 && cm.getMeso() >= hairprice) { - cm.gainMeso(-hairprice); - cm.gainItem(5150019, 1); - cm.sendOk("Enjoy!"); - } else if (selection == 1 && cm.getMeso() >= haircolorprice) { - cm.gainMeso(-haircolorprice); - cm.gainItem(5151016, 1); - cm.sendOk("Enjoy!"); - } else { - cm.sendOk("You don't have enough mesos to buy a coupon!"); - } - } - } - } -} +} \ No newline at end of file diff --git a/scripts/npc/9201017.js b/scripts/npc/9201017.js index 3ca19ca995..98e2257568 100644 --- a/scripts/npc/9201017.js +++ b/scripts/npc/9201017.js @@ -28,6 +28,22 @@ var regprice = 1000000; var vipprice = 1000000; var colors = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + function start() { status = -1; action(1, 0, 0); @@ -46,7 +62,7 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hi, there~! I'm Dr.Roberts, in charge of the cosmetic lenses here at the Amoria Plastic Surgery Shop! With #b#t5152025##k or #b#t5152026##k, you can let us take care of the rest and have the kind of beautiful look you've always craved~! Remember, the first thing everyone notices about you is the eyes, and we can help you find the cosmetic lens that most fits you! Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152025##t5152025##l\r\n#L2#Cosmetic Lenses: #i5152026##t5152026##l"); + cm.sendSimple("Hi, there~! I'm Dr.Roberts, in charge of the cosmetic lenses here at the Amoria Plastic Surgery Shop! With #b#t5152025##k or #b#t5152026##k, you can let us take care of the rest and have the kind of beautiful look you've always craved~! Remember, the first thing everyone notices about you is the eyes, and we can help you find the cosmetic lens that most fits you! Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152025##t5152025##l\r\n#L2#Cosmetic Lenses: #i5152026##t5152026##l\r\n#L3#One-time Cosmetic Lenses: #i5152106# (any color)#l"); } else if (status == 1) { if (selection == 1) { beauty = 1; @@ -57,7 +73,7 @@ function action(mode, type, selection) { var current = cm.getPlayer().getFace() % 100 + 21000; } colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current , current + 100, current + 300, current +400, current + 500, current + 700]); cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use a #b#t5152025##k and really make the change to your eyes?"); } else if (selection == 2) { beauty = 2; @@ -68,8 +84,33 @@ function action(mode, type, selection) { var current = cm.getPlayer().getFace() % 100 + 21000; } colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current , current + 100, current + 300, current +400, current + 500, current + 700]); cm.sendStyle("With our specialized machine, you can see yourself after the treatment in advance. What kind of lens would you like to wear? Choose the style of your liking.", colors); + } else if (selection == 3) { + beauty = 3; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + + colors = Array(); + for (var i = 0; i < 8; i++) { + if (cm.haveItem(5152100 + i)) { + pushIfItemExists(colors, current + 100 * i); + } + } + + if (colors.length == 0) { + cm.sendOk("You don't have any One-Time Cosmetic Lens to use."); + cm.dispose(); + return; + } + + cm.sendStyle("What kind of lens would you like to wear? Please choose the style of your liking.", colors); } } else if (status == 2){ @@ -82,8 +123,7 @@ function action(mode, type, selection) { cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); cm.dispose(); } - } - if (beauty == 2){ + } else if (beauty == 2){ if (cm.haveItem(5152026)){ cm.gainItem(5152026, -1); cm.setFace(colors[selection]); @@ -92,8 +132,17 @@ function action(mode, type, selection) { cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); cm.dispose(); } - } - if (beauty == 0){ + } else if (beauty == 3){ + var color = (colors[selection] / 100) % 100 | 0; + + if (cm.haveItem(5152100 + color)){ + cm.gainItem(5152100 + color, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } + } else if (beauty == 0){ if (selection == 0 && cm.getMeso() >= regprice) { cm.gainMeso(-regprice); cm.gainItem(5152025, 1); diff --git a/scripts/npc/9201018.js b/scripts/npc/9201018.js index 35aac9042b..c511102ca1 100644 --- a/scripts/npc/9201018.js +++ b/scripts/npc/9201018.js @@ -21,14 +21,22 @@ */ /* Dr. 90212 Amoria VIP Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20018, 20019); -var fface = Array(21018, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21012, 21019); +var mface_v = Array(20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20018, 20019); +var fface_v = Array(21001, 21002, 21003, 21004, 21005, 21006, 21007, 21012, 21018, 21019); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -47,18 +55,18 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Well, hello! Welcome to Amoria Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152022##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Well, hello! Welcome to Amoria Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152022##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#Plastic Surgery: #i5152022##t5152022##l"); } else if (status == 1) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + for(var i = 0; i < mface_v.length; i++) { + pushIfItemExists(facenew, mface_v[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + for(var i = 0; i < fface_v.length; i++) { + pushIfItemExists(facenew, fface_v[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } } cm.sendStyle("Let's see... I can totally transform your face into something new. Don't you want to try it? For #b#t5152022##k, you can get the face of your liking. Take your time in choosing the face of your preference.", facenew); diff --git a/scripts/npc/9201019.js b/scripts/npc/9201019.js index a7948308c4..f7c66d73f5 100644 --- a/scripts/npc/9201019.js +++ b/scripts/npc/9201019.js @@ -21,14 +21,22 @@ */ /* Intern Shakihands Amoria Random Eye Change + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20018, 20019); -var fface = Array(21018, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21012, 21019); +var mface_r = Array(20002, 20005, 20007, 20011, 20014, 20027, 20029); +var fface_r = Array(21001, 21005, 21007, 21017, 21018, 21020, 21022); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -47,18 +55,18 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hi, I pretty much shouldn't be doing this, but with a #b#t5152021##k, I will do it anyways for you. But don't forget, it will be random!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Hi, I pretty much shouldn't be doing this, but with a #b#t5152021##k, I will do it anyways for you. But don't forget, it will be random!\r\n#L2#Plastic Surgery: #i5152021##t5152021##l"); } else if (status == 1) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) { - facenew.push(mface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + for(var i = 0; i < mface_r.length; i++) { + pushIfItemExists(facenew, mface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + for(var i = 0; i < fface_r.length; i++) { + pushIfItemExists(facenew, fface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); } } cm.sendYesNo("If you use the regular coupon, your face may transform into a random new look...do you still want to do it using #b#t5152021##k?"); diff --git a/scripts/npc/9201039.js b/scripts/npc/9201039.js index baf63f8692..491e3b8e0a 100644 --- a/scripts/npc/9201039.js +++ b/scripts/npc/9201039.js @@ -19,11 +19,24 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ + +/* Claudia + Amoria Quest Hair Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove +*/ + var status = 0; -var mhair = Array(30270, 30240, 30020, 30000, 30132, 30192, 30032, 30112, 30162); -var fhair = Array(31150, 31250, 31310, 31050, 31050, 31030, 31070, 31091, 31001); +var mhair_q = Array(30270, 30240, 30020, 30000, 30132, 30192, 30032, 30112, 30162); +var fhair_q = Array(31150, 31250, 31310, 31050, 31050, 31030, 31070, 31091, 31001); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { if (cm.isQuestCompleted(8860) && !cm.haveItem(4031528)) { cm.sendNext("I've already done your hair once as a trade-for-services, sport. You'll have to snag an EXP Hair coupon from the Cash Shop if you want to change it again!"); @@ -44,11 +57,11 @@ function action(mode, type, selection) { if (status == 1) { hairnew = Array(); if (cm.getPlayer().getGender() == 0) - for(var i = 0; i < mhair.length; i++) - hairnew.push(mhair[i]); + for(var i = 0; i < mhair_q.length; i++) + pushIfItemExists(hairnew, mhair_q[i]); else - for(var j = 0; j < fhair.length; j++) - hairnew.push(fhair[j]); + for(var j = 0; j < fhair_q.length; j++) + pushIfItemExists(hairnew, fhair_q[j]); cm.sendNext("Here we go!"); } else { if (cm.haveItem(4031528)) { diff --git a/scripts/npc/9201061.js b/scripts/npc/9201061.js index 477ad87fe2..0c61db6b64 100644 --- a/scripts/npc/9201061.js +++ b/scripts/npc/9201061.js @@ -26,6 +26,16 @@ var status = 0; var price = 1000000; var colors = Array(); +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + function start() { status = -1; action(1, 0, 0); @@ -44,7 +54,7 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hi, there~! I'm Bomack. If you have a #b#t5152035##k, I can prescribe the right kind of cosmetic lenses for you. Now, what would you like to do?\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Hi, there~! I'm Bomack. If you have a #b#t5152035##k, I can prescribe the right kind of cosmetic lenses for you. Now, what would you like to do?\r\n#L2#Cosmetic Lens: #i5152035##t5152035##l"); } else if (status == 1) { if (selection == 2) { if (cm.getPlayer().getGender() == 0) { @@ -54,7 +64,7 @@ function action(mode, type, selection) { var current = cm.getPlayer().getFace() % 100 + 21000; } colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700]); cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use #b#t5152035##k and really make the change to your eyes?"); } } diff --git a/scripts/npc/9201062.js b/scripts/npc/9201062.js index 7fd8d26eb5..228ad43bc3 100644 --- a/scripts/npc/9201062.js +++ b/scripts/npc/9201062.js @@ -23,9 +23,26 @@ NLC VIP Eye Color Change. */ var status = 0; +var beauty = 0; var price = 1000000; var colors = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + function start() { status = -1; action(1, 0, 0); @@ -44,7 +61,7 @@ function action(mode, type, selection) { else status--; if (status == 0) { - cm.sendSimple("Hey, there~! I'm J.J.! I'm in charge of the cosmetic lenses here at NLC Shop! If you have a #b#t5152036##k, I can get you the best cosmetic lenses you have ever had! Now, what would you like to do?\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Hey, there~! I'm J.J.! I'm in charge of the cosmetic lenses here at NLC Shop! If you have a #b#t5152036##k, I can get you the best cosmetic lenses you have ever had! Now, what would you like to do?\r\n#L2#Cosmetic Lenses: #i5152036##t5152036##l\r\n#L3#One-time Cosmetic Lenses: #i5152107# (any color)#l"); } else if (status == 1) { if (selection == 2) { if (cm.getPlayer().getGender() == 0) { @@ -54,18 +71,54 @@ function action(mode, type, selection) { var current = cm.getPlayer().getFace() % 100 + 21000; } colors = Array(); - colors = Array(current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700); + pushIfItemsExists(colors, [current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700]); cm.sendStyle("With our specialized machine, you can see yourself after the treatment in advance. What kind of lens would you like to wear? Choose the style of your liking.", colors); + } else if (selection == 3) { + beauty = 3; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + + colors = Array(); + for (var i = 0; i < 8; i++) { + if (cm.haveItem(5152100 + i)) { + pushIfItemExists(colors, current + 100 * i); + } + } + + if (colors.length == 0) { + cm.sendOk("You don't have any One-Time Cosmetic Lens to use."); + cm.dispose(); + return; + } + + cm.sendStyle("What kind of lens would you like to wear? Please choose the style of your liking.", colors); } - } - else if (status == 2){ + } else if (status == 2){ cm.dispose(); - if (cm.haveItem(5152036) == true){ - cm.gainItem(5152036, -1); - cm.setFace(colors[selection]); - cm.sendOk("Enjoy your new and improved cosmetic lenses!"); - } else { - cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + if (beauty == 0) { + if (cm.haveItem(5152036) == true){ + cm.gainItem(5152036, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } + } else if (beauty == 3){ + var color = (colors[selection] / 100) % 100 | 0; + + if (cm.haveItem(5152100 + color)){ + cm.gainItem(5152100 + color, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } } } } diff --git a/scripts/npc/9201063.js b/scripts/npc/9201063.js index 7585600e39..47224f97a3 100644 --- a/scripts/npc/9201063.js +++ b/scripts/npc/9201063.js @@ -21,15 +21,23 @@ */ /* Ari NLC Random Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30250, 30110, 30230, 30050, 30280, 30410, 30730, 30160, 30200, 30440, 30360, 30740, 30400); -var fhair = Array(31150, 31310, 31220, 31300, 31260, 31160, 31730, 31410, 31410, 31720, 31560, 31450); +var mhair_e = Array(30250, 30400, 30430, 30440, 30490, 30730, 30830, 30870, 30880, 33100); +var fhair_e = Array(31320, 31450, 31560, 31570, 31690, 31720, 31730, 31830, 34010); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -54,13 +62,13 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mhair.length; i++) { - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < mhair_e.length; i++) { + pushIfItemExists(hairnew, mhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); } } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fhair.length; i++) { - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < fhair_e.length; i++) { + pushIfItemExists(hairnew, fhair_e[i] + parseInt(cm.getPlayer().getHair() % 10)); } } cm.sendYesNo("If you use the EXP coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150030##k and really change your hairstyle?"); @@ -69,7 +77,7 @@ function action(mode, type, selection) { haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) { - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); } cm.sendYesNo("If you use a regular coupon your hair will change RANDOMLY. Do you still want to use #b#t5151025##k and change it up?"); } diff --git a/scripts/npc/9201064.js b/scripts/npc/9201064.js index 1dd52ce443..06e696ea62 100644 --- a/scripts/npc/9201064.js +++ b/scripts/npc/9201064.js @@ -20,16 +20,24 @@ along with this program. If not, see . */ /* Mani -NLC VIP Hair/Hair Color Change. + NLC VIP Hair/Hair Color Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var hairprice = 1000000; var haircolorprice = 1000000; -var mhair = Array(30250, 30110, 30230, 30050, 30280, 30410, 30730, 30160, 30200); -var fhair = Array(31150, 31310, 31220, 31300, 31260, 31160, 31730, 31410, 31410); +var mhair_v = Array(30250, 30490, 30730, 30870, 30880, 33100); +var fhair_v = Array(31320, 31450, 31560, 31730, 31830); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { cm.sendSimple("I'm the head of this hair salon Mani. If you have a #b#t5150031##k or a #b#t5151026##k, allow me to take care of your hairdo. Please choose the one you want.\r\n#L1#Haircut: #i5150031##t5150031##l\r\n#L2#Dye your hair: #i5151026##t5151026##l"); } @@ -51,18 +59,18 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) - for(var i = 0; i < mhair.length; i++) - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair()% 10)); + for(var i = 0; i < mhair_v.length; i++) + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair()% 10)); if (cm.getPlayer().getGender() == 1) - for(var i = 0; i < fhair.length; i++) - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for(var i = 0; i < fhair_v.length; i++) + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getPlayer().getHair() % 10)); cm.sendStyle("I can totally change up your hairstyle and make it look so good. Why don't you change it up a bit? With #b#t5150031##k, I'll take care of the rest for you. Choose the style of your liking!", hairnew); } else if (selection == 2) { beauty = 2; haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); cm.sendStyle("I can totally change your haircolor and make it look so good. Why don't you change it up a bit? With #b#t5151026##k, I'll take care of the rest. Choose the color of your liking!", haircolor); } } diff --git a/scripts/npc/9201065.js b/scripts/npc/9201065.js index 7d73da7cdf..94811914ee 100644 --- a/scripts/npc/9201065.js +++ b/scripts/npc/9201065.js @@ -27,7 +27,7 @@ var price = 1000000; var skin = Array(0, 1, 2, 3, 4); function start() { - cm.sendSimple("Well, hello! Welcome to the NLC Skin-Care! Would you like to have a firm, tight, healthy looking skin like mine? With #b#t5153009##k, you can let us take care of the rest and have the kind of skin you've always wanted~!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Well, hello! Welcome to the NLC Skin-Care! Would you like to have a firm, tight, healthy looking skin like mine? With #b#t5153009##k, you can let us take care of the rest and have the kind of skin you've always wanted~!\r\n#L2#Skin Care: #i5153009##t5153009##l"); } function action(mode, type, selection) { diff --git a/scripts/npc/9201069.js b/scripts/npc/9201069.js index 0495e04fb5..fe70fa205b 100644 --- a/scripts/npc/9201069.js +++ b/scripts/npc/9201069.js @@ -21,16 +21,24 @@ */ /* V. Isage NLC VIP Eye Change. + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012); -var fface = Array(21001, 21002, 21003, 21004, 21005, 21006, 21008, 21012, 21014, 21016); +var mface_v = Array(20000, 20001, 20003, 20004, 20005, 20006, 20008, 20012, 20031); +var fface_v = Array(21001, 21002, 21003, 21004, 21005, 21006, 21008, 21012, 21016); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { - cm.sendSimple("Well, hello! Welcome to the New Leaf City Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152034##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Well, hello! Welcome to the New Leaf City Plastic Surgery! Would you like to transform your face into something new? With a #b#t5152034##k, you can let us take care of the rest and have the face you've always wanted~!\r\n#L2#Plastic Surgery: #i5152034##t5152034##l"); } function action(mode, type, selection) { @@ -49,11 +57,11 @@ function action(mode, type, selection) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) - for(var i = 0; i < mface.length; i++) - facenew.push(mface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace()% 100)); + for(var i = 0; i < mface_v.length; i++) + pushIfItemExists(facenew, mface_v[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace()% 100)); if (cm.getPlayer().getGender() == 1) - for(var i = 0; i < fface.length; i++) - facenew.push(fface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + for(var i = 0; i < fface_v.length; i++) + pushIfItemExists(facenew, fface_v[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); cm.sendStyle("Let's see... I can totally transform your face into something new. Don't you want to try it? For #b#t5152034##k, you can get the face of your liking. Take your time in choosing the face of your preference.", facenew); } } diff --git a/scripts/npc/9201070.js b/scripts/npc/9201070.js index b0b372f796..92d19f172a 100644 --- a/scripts/npc/9201070.js +++ b/scripts/npc/9201070.js @@ -21,17 +21,24 @@ */ /* Nerbit NLC Random Eye Change. -@TODO: Make this not sell tickets like in GMS + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; var price = 1000000; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012); -var fface = Array(21001, 21002, 21003, 21004, 21005, 21006, 21008, 21012, 21014, 21016); +var mface_r = Array(20001, 20008, 20011, 20013, 20024, 20029, 20032); +var fface_r = Array(21000, 21007, 21011, 21012, 21017, 21020, 21022); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { - cm.sendSimple("Hi, I pretty much shouldn't be doing this, but with a #b#t5152033##k, I will do it anyways for you. But don't forget, it will be random!\r\n#L2#I already have a Coupon!#l"); + cm.sendSimple("Hi, I pretty much shouldn't be doing this, but with a #b#t5152033##k, I will do it anyways for you. But don't forget, it will be random!\r\n#L2#Plastic Surgery: #i5152033##t5152033##l"); } function action(mode, type, selection) { @@ -50,11 +57,11 @@ function action(mode, type, selection) { if (selection == 2) { facenew = Array(); if (cm.getPlayer().getGender() == 0) - for(var i = 0; i < mface.length; i++) - facenew.push(mface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + for(var i = 0; i < mface_r.length; i++) + pushIfItemExists(facenew, mface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); if (cm.getPlayer().getGender() == 1) - for(var i = 0; i < fface.length; i++) - facenew.push(fface[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); + for(var i = 0; i < fface_r.length; i++) + pushIfItemExists(facenew, fface_r[i] + cm.getPlayer().getFace() % 1000 - (cm.getPlayer().getFace() % 100)); cm.sendYesNo("If you use the regular coupon, your face may transform into a random new look...do you still want to do it using #b#t5152033##k?"); } } else if (status == 2){ diff --git a/scripts/npc/9270023.js b/scripts/npc/9270023.js index 60263714da..45e93e6846 100644 --- a/scripts/npc/9270023.js +++ b/scripts/npc/9270023.js @@ -23,13 +23,21 @@ Singapore Random Face Changer @Author AAron (aaroncsn), Cody Side note by aaron [If there is something wrong PM me on fMS] + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var mface = Array(20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20012); -var fface = Array(21001, 21002, 21003, 21004, 21005, 21006, 21008, 21012, 21014, 21016); +var mface_r = Array(20002, 20005, 20006, 20013, 20017, 20021, 20024); +var fface_r = Array(21002, 21003, 21014, 21016, 21017, 21021, 21027); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { cm.sendSimple("If you use this regular coupon, your face may transform into a random new look...do you still want to do it using #b#t5152037##k, I will do it anyways for you. But don't forget, it will be random!\r\n\#L2#OK! (Uses #i5152037# #t5152037#)#l"); } @@ -57,11 +65,11 @@ function action(mode, type, selection) { facenew = Array(); if (cm.getPlayer().getGender() == 0) - for(var i = 0; i < mface.length; i++) - facenew.push(mface[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); + for(var i = 0; i < mface_r.length; i++) + pushIfItemExists(facenew, mface_r[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); if (cm.getPlayer().getGender() == 1) - for(var i = 0; i < fface.length; i++) - facenew.push(fface[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); + for(var i = 0; i < fface_r.length; i++) + pushIfItemExists(facenew, fface_r[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); cm.sendYesNo("If you use the regular coupon, your face may transform into a random new look...do you still want to do it using #b#t5152037##k?"); } else if (status == 2){ cm.gainItem(5152037 , -1); diff --git a/scripts/npc/9270024.js b/scripts/npc/9270024.js index 0ede90abf7..605f93f4f6 100644 --- a/scripts/npc/9270024.js +++ b/scripts/npc/9270024.js @@ -22,13 +22,21 @@ /* Kelvin SingaPore VIP Face changer @Author AAron (aaroncsn), Cody + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var mface = Array(20109, 20110, 20106, 20108, 20112, 20013); -var fface = Array(21021, 21009, 21010, 21006, 21008, 21012); +var mface_v = Array(20005, 20012, 20013, 20020, 20021, 20026); +var fface_v = Array(21006, 21009, 21011, 21012, 21021, 21025); var facenew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { status = -1; action(1, 0, 0); @@ -58,12 +66,12 @@ function action(mode, type, selection) { facenew = Array(); if (cm.getPlayer().getGender() == 0) { - for(var i = 0; i < mface.length; i++) - facenew.push(mface[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); + for(var i = 0; i < mface_v.length; i++) + pushIfItemExists(facenew, mface_v[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); } if (cm.getPlayer().getGender() == 1) { - for(var i = 0; i < fface.length; i++) { - facenew.push(fface[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); + for(var i = 0; i < fface_v.length; i++) { + pushIfItemExists(facenew, fface_v[i] + cm.getPlayer().getFace()% 1000 - (cm.getPlayer().getFace()% 100)); } } cm.sendStyle("Let's see... I can totally transform your face into something new. Don't you want to try it? For #b#t5152038##k, you can get the face of your liking. Take your time in choosing the face of your preference...", facenew); diff --git a/scripts/npc/9270026.js b/scripts/npc/9270026.js index 7a0c6a7009..9dbc3a6629 100644 --- a/scripts/npc/9270026.js +++ b/scripts/npc/9270026.js @@ -27,8 +27,24 @@ var status = 0; var beauty = 0; var colors = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + +function pushIfItemsExists(array, itemidList) { + for (var i = 0; i < itemidList.length; i++) { + var itemid = itemidList[i]; + + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } + } +} + function start() { - cm.sendSimple("Hi, there! I'm Sixx, in charge of Da Yan Jing Lens Shop here at CBD! With #b#t5152039##k or #b#t5152040##k, you can let us take care of the rest and have the kind of beautiful look you've always craved! Remember, the first thing everyone notices about you are the eyes, and we can help you find the cosmetic lens that most fits you! Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152039##t5152039##l\r\n#L2#Cosmetic Lenses: #i5152040##t5152040##l"); + cm.sendSimple("Hi, there! I'm Sixx, in charge of Da Yan Jing Lens Shop here at CBD! With #b#t5152039##k or #b#t5152040##k, you can let us take care of the rest and have the kind of beautiful look you've always craved! Remember, the first thing everyone notices about you are the eyes, and we can help you find the cosmetic lens that most fits you! Now, what would you like to use?\r\n#L1#Cosmetic Lenses: #i5152039##t5152039##l\r\n#L2#Cosmetic Lenses: #i5152040##t5152040##l\r\n#L3#One-time Cosmetic Lenses: #i5152107# (any color)#l"); } function action(mode, type, selection) { @@ -41,11 +57,36 @@ function action(mode, type, selection) { beauty = 1; var current = cm.getPlayer().getFace()% 100 + 20000 + cm.getPlayer().getGender() * 1000; cm.sendYesNo("If you use the regular coupon, you'll be awarded a random pair of cosmetic lenses. Are you going to use a #b#t5152039##k and really make the change to your eyes?"); - } else { + } else if (selection == 2) { beauty = 2; var current = cm.getPlayer().getFace()% 100 + 20000 + cm.getPlayer().getGender() * 1000; - colors = [current , current + 100, current + 200, current + 300, current +400, current + 500, current + 600, current + 700]; + pushIfItemsExists(colors, [current + 200, current + 300, current +400, current + 700]); cm.sendStyle("With our specialized machine, you can see yourself after the treatment in advance. What kind of lens would you like to wear? Choose the style of your liking.", colors); + } else if (selection == 3) { + beauty = 3; + if (cm.getPlayer().getGender() == 0) { + var current = cm.getPlayer().getFace() + % 100 + 20000; + } + if (cm.getPlayer().getGender() == 1) { + var current = cm.getPlayer().getFace() + % 100 + 21000; + } + + colors = Array(); + for (var i = 0; i < 8; i++) { + if (cm.haveItem(5152100 + i)) { + pushIfItemExists(colors, current + 100 * i); + } + } + + if (colors.length == 0) { + cm.sendOk("You don't have any One-Time Cosmetic Lens to use."); + cm.dispose(); + return; + } + + cm.sendStyle("What kind of lens would you like to wear? Please choose the style of your liking.", colors); } } else if (status == 2) { @@ -63,6 +104,16 @@ function action(mode, type, selection) { cm.sendOk("Enjoy your new and improved cosmetic lenses!"); } else cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } else if (beauty == 3){ + var color = (colors[selection] / 100) % 100 | 0; + + if (cm.haveItem(5152100 + color)){ + cm.gainItem(5152100 + color, -1); + cm.setFace(colors[selection]); + cm.sendOk("Enjoy your new and improved cosmetic lenses!"); + } else { + cm.sendOk("I'm sorry, but I don't think you have our cosmetic lens coupon with you right now. Without the coupon, I'm afraid I can't do it for you.."); + } } cm.dispose(); } diff --git a/scripts/npc/9270036.js b/scripts/npc/9270036.js index f997c1ea79..b4df39879f 100644 --- a/scripts/npc/9270036.js +++ b/scripts/npc/9270036.js @@ -20,17 +20,25 @@ along with this program. If not, see . */ /* Eric - Singapore VIP Hair/Color Changer - @Author AAron, Cody (FlowsionMS) Forums + Singapore VIP Hair/Color Changer + @Author AAron, Cody (FlowsionMS) Forums + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var mhair = Array(30110, 30290, 30230, 30260, 30320, 30190, 30240, 30350, 30270, 30180); -var fhair = Array(31260, 31090, 31220, 31250, 31140, 31160, 31100, 31120, 31030, 31270, 31810); +var mhair_v = Array(30000, 30020, 30110, 30120, 30270, 30290, 30310, 30670, 30840); +var fhair_v = Array(31010, 31050, 31110, 31120, 31240, 31250, 31280, 31670, 31810); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { - cm.sendSimple("Welcome to the Quick-Hand Hair-Salon!. Do you, by any chance, have #b#t5150033##k or #b#t5151028 ##k? If so, how about letting me take care of your hair? Please what you want to do with it.\r\n#L1#Haircut: #i5150033##t5150033##l\r\n#L2#Dye your hair: #i5151028##t5151028##l"); + cm.sendSimple("Welcome to the Quick-Hand Hair-Salon!. Do you, by any chance, have #b#t5150033##k or #b#t5151028##k? If so, how about letting me take care of your hair? Please what you want to do with it.\r\n#L1#Haircut: #i5150033##t5150033##l\r\n#L2#Dye your hair: #i5151028##t5151028##l"); } function action(mode, type, selection) { @@ -42,18 +50,18 @@ function action(mode, type, selection) { beauty = 1; hairnew = Array(); if (cm.getPlayer().getGender() == 0) - for(var i = 0; i < mhair.length; i++) - hairnew.push(mhair[i] + parseInt(cm.getPlayer().getHair()% 10)); + for(var i = 0; i < mhair_v.length; i++) + pushIfItemExists(hairnew, mhair_v[i] + parseInt(cm.getPlayer().getHair()% 10)); else - for(var i = 0; i < fhair.length; i++) - hairnew.push(fhair[i] + parseInt(cm.getPlayer().getHair()% 10)); + for(var i = 0; i < fhair_v.length; i++) + pushIfItemExists(hairnew, fhair_v[i] + parseInt(cm.getPlayer().getHair()% 10)); cm.sendStyle("I can completely change the look of your hair. Aren't you ready for a change? With #b#t5150033##k, I'll take care of the rest for you. Choose the style of your liking!", hairnew); } else if (selection == 2) { beauty = 2; haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); cm.sendStyle("I can completely change the look of your hair. Aren't you ready for a change? With #b#t5151028##k, I'll take care of the rest. Choose the color of your liking!", haircolor); } else if (status == 2) { if (beauty == 1){ diff --git a/scripts/npc/9270037.js b/scripts/npc/9270037.js index 2c26b2bd5f..14d5f07e0e 100644 --- a/scripts/npc/9270037.js +++ b/scripts/npc/9270037.js @@ -20,16 +20,24 @@ along with this program. If not, see . */ /* Jimmy - Singa Random Hair/Color Changer + Singapore Random Hair/Color Changer @Author Cody (FlowsionMS) @Author AAron (FlowsionMS) + + GMS-like revised by Ronan. Contents found thanks to Mitsune (GamerBewbs), Waltzing, AyumiLove */ var status = 0; var beauty = 0; -var mhair = Array(30110, 30290, 30230, 30260, 30320, 30190, 30240, 30350, 30270, 30180); -var fhair = Array(31260, 31090, 31220, 31250, 31140, 31160, 31100, 31120, 31030, 31270, 31810); +var mhair_r = Array(30110, 30180, 30260, 30290, 30300, 30350, 30470, 30720, 30840); +var fhair_r = Array(31110, 31200, 31250, 31280, 31600, 31640, 31670, 31810, 34020); var hairnew = Array(); +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { + array.push(itemid); + } +} + function start() { cm.sendSimple("Hi, I'm the assistant here. Dont worry, I'm plenty good enough for this. If you have #b#t5150032##k or #b#t5151027##k by any chance, then allow me to take care of the rest?\r\n#L1#Haircut: #i5150032##t5150032##l\r\n#L2#Dye your hair: #i5151027##t5151027##l"); } @@ -42,15 +50,15 @@ function action(mode, type, selection) { if (selection == 1) { beauty = 1; hairnew = Array(); - for (var id = 0; id < cm.getPlayer().getGender() == 0 ? mhair.length : fhair.length; id++) - hairnew.push(cm.getPlayer().getGender == 0 ? mhair[i] : fhair[i] + parseInt(cm.getPlayer().getHair() % 10)); + for (var id = 0; id < cm.getPlayer().getGender() == 0 ? mhair_r.length : fhair_r.length; id++) + pushIfItemExists(hairnew, cm.getPlayer().getGender == 0 ? mhair_r[i] : fhair_r[i] + parseInt(cm.getPlayer().getHair() % 10)); cm.sendYesNo("If you use the REG coupon your hair will change RANDOMLY with a chance to obtain a new experimental style that I came up with. Are you going to use #b#t5150032##k and really change your hairstyle?"); } else if (selection == 2) { beauty = 2; haircolor = Array(); var current = parseInt(cm.getPlayer().getHair()/10)*10; for(var i = 0; i < 8; i++) - haircolor.push(current + i); + pushIfItemExists(haircolor, current + i); cm.sendYesNo("If you use the REG coupon your hair will change RANDOMLY. Do you still want to use #b#t5151027##k and change it up?"); } else if (status == 2) { if (beauty == 1){ diff --git a/scripts/npc/9270043.js b/scripts/npc/9270043.js index 35eb969995..0975bcb503 100644 --- a/scripts/npc/9270043.js +++ b/scripts/npc/9270043.js @@ -27,7 +27,7 @@ var status = 0; function start() { if (cm.haveItem(5451000)) { cm.gainItem(5451000, -1); - cm.processGachapon(ids, true); + cm.doGachapon(); cm.dispose(); } else if (cm.haveItem(5220000)) cm.sendYesNo("You may use Gachapon. Would you like to use your Gachapon ticket?"); @@ -38,7 +38,7 @@ function start() { function action(mode, type, selection){ if (mode == 1 && cm.haveItem(5220000)) { - cm.processGachapon(ids, false); + cm.doGachapon(); cm.dispose(); } else { if (mode > 0) { diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js index ac3158a2d6..6162248be3 100644 --- a/scripts/npc/9977777.js +++ b/scripts/npc/9977777.js @@ -100,6 +100,8 @@ function writeFeatureTab_CashItems() { addFeature("Close-quarters evaluation mode for items."); addFeature("Reviewed Karma scissors & Untradeable items."); addFeature("Reviewed an pet position issue within CASH inventory."); + addFeature("Reviewed fashion-related contents, almost GMS-like."); + addFeature("Plastic surgeons/stylists no longer stuck characters."); addFeature("Scroll for Spikes on Shoes."); addFeature("Scroll for Cold Protection."); addFeature("Vega's spell."); @@ -109,6 +111,7 @@ function writeFeatureTab_CashItems() { addFeature("Kite."); addFeature("Cash Shop surprise."); addFeature("Maple Life."); + addFeature("EXP Increase."); } function writeFeatureTab_MonstersMapsReactors() { @@ -120,7 +123,7 @@ function writeFeatureTab_MonstersMapsReactors() { addFeature("Mobs only drop items collectable by the player/party."); addFeature("Mobs shouldn't fall from foothold too often now."); addFeature("Properly applying MP cost on non-skill mob moves."); - addFeature("Implemented banish mob skill move."); + addFeature("Implemented mob banish by touch & skill move."); addFeature("Redesigned HT mechanics: assemble & dmg taken."); addFeature("Implemented Zombify disease status."); addFeature("Dmg Reflect mobskill icons no longer lags to display."); @@ -247,6 +250,7 @@ function writeFeatureTab_Project() { addFeature("Usage of HikariCP to improve DB connection calls."); addFeature("Usage of Java Threadpool to improve runnable calls."); addFeature("Developed many survey tools for content profiling."); + addFeature("Removed dangling item name throughout game files."); addFeature("ThreadTracker: runtime tool for deadlock detection."); addFeature("Channel, World and Server-wide timer management."); addFeature("Thoroughly reviewed encapsulation for player stats."); diff --git a/scripts/npc/gachaponold.js b/scripts/npc/gachaponold.js index a8c3c413b9..e4b660644f 100644 --- a/scripts/npc/gachaponold.js +++ b/scripts/npc/gachaponold.js @@ -48,7 +48,7 @@ function action(mode, type, selection){ } else if (mode == 1 && cm.haveItem(ticketId)) { if(cm.canHold(1302000) && cm.canHold(2000000) && cm.canHold(3010001) && cm.canHold(4000000)) { // One free slot in every inventory. cm.gainItem(ticketId, -1); - cm.doGachapon(cm.getNpc()); + cm.doGachapon(); } else { cm.sendOk("Please have at least one slot in your #rEQUIP, USE, SET-UP, #kand #bETC#k inventories free."); } diff --git a/src/client/MapleBuffStat.java b/src/client/MapleBuffStat.java index 4848b89c64..aea3ca13e5 100644 --- a/src/client/MapleBuffStat.java +++ b/src/client/MapleBuffStat.java @@ -97,7 +97,7 @@ public enum MapleBuffStat { PICKPOCKET(0x800000000000000L), PUPPET(0x800000000000000L), MESOGUARD(0x1000000000000000L), - //0x2000000000000000L + EXP_INCREASE(0x2000000000000000L), WEAKEN(0x4000000000000000L), //THAT GAP diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index 47fe80b034..10662c614d 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -1243,9 +1243,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int banSp = this.getMap().findClosestPlayerSpawnpoint(this.getPosition()).getId(); long banTime = System.currentTimeMillis(); - dropMessage(5, msg); + if (msg != null) dropMessage(5, msg); + MapleMap map_ = getWarpMap(mapid); - changeMap(map_, map_.getPortal(portal)); + MaplePortal portal_ = map_.getPortal(portal); + changeMap(map_, portal_ != null ? portal_ : map_.getRandomPlayerSpawnpoint()); setBanishPlayerData(banMap, banSp, banTime); } @@ -2123,7 +2125,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } } - + try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM inventoryequipment WHERE inventoryitemid = ?")) { ps2.setInt(1, inventoryitemid); ps2.executeUpdate(); @@ -2897,7 +2899,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { gainExpInternal(-loss, 0, 0, show, inChat, white); } - private void gainExpInternal(long gain, int equip, int party, boolean show, boolean inChat, boolean white) { + private synchronized void gainExpInternal(long gain, int equip, int party, boolean show, boolean inChat, boolean white) { // need of method synchonization here detected thanks to MedicOP long total = Math.max(gain, -exp.get()); if (level < getMaxLevel()) { @@ -5161,7 +5163,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return rankMove; } - private void clearSavedLocation(SavedLocationType type) { + public void clearSavedLocation(SavedLocationType type) { savedLocations[type.ordinal()] = null; } @@ -7441,6 +7443,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + public void saveLocationOnWarp() { // suggestion to remember the map before warp command thanks to Lei + MaplePortal closest = map.findClosestPortal(getPosition()); + int curMapid = getMapId(); + + for (int i = 0; i < savedLocations.length; i++) { + if (savedLocations[i] == null) { + savedLocations[i] = new SavedLocation(curMapid, closest != null ? closest.getId() : 0); + } + } + } + public void saveLocation(String type) { MaplePortal closest = map.findClosestPortal(getPosition()); savedLocations[SavedLocationType.fromString(type).ordinal()] = new SavedLocation(getMapId(), closest != null ? closest.getId() : 0); @@ -8612,8 +8625,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } /* - for (Entry e : statups.entrySet()) { - System.out.println(e); + for (Entry es : statups.entrySet()) { + System.out.println(es); } */ diff --git a/src/client/command/CommandsExecutor.java b/src/client/command/CommandsExecutor.java index 5cf01fe660..3ddda29c7c 100644 --- a/src/client/command/CommandsExecutor.java +++ b/src/client/command/CommandsExecutor.java @@ -216,6 +216,7 @@ public class CommandsExecutor { addCommand("dc", 2, DcCommand.class); addCommand("cleardrops", 2, ClearDropsCommand.class); addCommand("clearslot", 2, ClearSlotCommand.class); + addCommand("clearsavelocs", 2, ClearSavedLocationsCommand.class); addCommand("warp", 2, WarpCommand.class); addCommand(new String[]{"warphere", "summon"}, 2, SummonCommand.class); addCommand(new String[]{"warpto", "reach", "follow"}, 2, ReachCommand.class); @@ -229,7 +230,6 @@ public class CommandsExecutor { addCommand("maxstat", 2, MaxStatCommand.class); addCommand("maxskill", 2, MaxSkillCommand.class); addCommand("resetskill", 2, ResetSkillCommand.class); - addCommand("mesos", 2, MesosCommand.class); addCommand("search", 2, SearchCommand.class); addCommand("jail", 2, JailCommand.class); addCommand("unjail", 2, UnJailCommand.class); diff --git a/src/client/command/commands/gm0/JoinEventCommand.java b/src/client/command/commands/gm0/JoinEventCommand.java index 60ea01e506..5b0b66eace 100644 --- a/src/client/command/commands/gm0/JoinEventCommand.java +++ b/src/client/command/commands/gm0/JoinEventCommand.java @@ -49,6 +49,7 @@ public class JoinEventCommand extends Command { event.minusLimit(); + player.saveLocationOnWarp(); player.changeMap(event.getMapId()); } else { player.dropMessage(5, "The limit of players for the event has already been reached."); diff --git a/src/client/command/commands/gm0/LeaveEventCommand.java b/src/client/command/commands/gm0/LeaveEventCommand.java index 41dfc9db44..2269f938c2 100644 --- a/src/client/command/commands/gm0/LeaveEventCommand.java +++ b/src/client/command/commands/gm0/LeaveEventCommand.java @@ -46,6 +46,7 @@ public class LeaveEventCommand extends Command { player.setFitness(null); } + player.saveLocationOnWarp(); player.changeMap(returnMap); if(c.getChannelServer().getEvent() != null) { c.getChannelServer().getEvent().addLimit(); diff --git a/src/client/command/commands/gm1/GotoCommand.java b/src/client/command/commands/gm1/GotoCommand.java index 845dead6a4..3ab2baf76f 100644 --- a/src/client/command/commands/gm1/GotoCommand.java +++ b/src/client/command/commands/gm1/GotoCommand.java @@ -51,6 +51,7 @@ public class GotoCommand extends Command { // expedition issue with this command detected thanks to Masterrulax MaplePortal targetPortal = target.getRandomPlayerSpawnpoint(); + player.saveLocationOnWarp(); player.changeMap(target, targetPortal); } else { player.dropMessage(5, "Area '" + params[0] + "' is not registered."); diff --git a/src/client/command/commands/gm2/MesosCommand.java b/src/client/command/commands/gm2/ClearSavedLocationsCommand.java similarity index 63% rename from src/client/command/commands/gm2/MesosCommand.java rename to src/client/command/commands/gm2/ClearSavedLocationsCommand.java index 65c2e87ba9..d049b19382 100644 --- a/src/client/command/commands/gm2/MesosCommand.java +++ b/src/client/command/commands/gm2/ClearSavedLocationsCommand.java @@ -26,17 +26,31 @@ package client.command.commands.gm2; import client.command.Command; import client.MapleClient; import client.MapleCharacter; +import server.maps.SavedLocationType; -public class MesosCommand extends Command { +public class ClearSavedLocationsCommand extends Command { { setDescription(""); } @Override public void execute(MapleClient c, String[] params) { - MapleCharacter player = c.getPlayer(); - if (params.length >= 1) { - player.gainMeso(Integer.parseInt(params[0]), true); + MapleCharacter player = c.getPlayer(), victim; + + if (params.length > 0) { + victim = c.getChannelServer().getPlayerStorage().getCharacterByName(params[0]); + if (victim == null) { + player.message("Player '" + params[0] + "' could not be found on this channel."); + return; + } + } else { + victim = c.getPlayer(); } + + for (SavedLocationType type : SavedLocationType.values()) { + victim.clearSavedLocation(type); + } + + player.message("Cleared " + params[0] + "'s saved locations."); } } diff --git a/src/client/command/commands/gm2/JailCommand.java b/src/client/command/commands/gm2/JailCommand.java index 78720b2314..ceb5c6063f 100644 --- a/src/client/command/commands/gm2/JailCommand.java +++ b/src/client/command/commands/gm2/JailCommand.java @@ -60,6 +60,7 @@ public class JailCommand extends Command { if (victim.getMapId() != mapid) { // those gone to jail won't be changing map anyway MapleMap target = c.getChannelServer().getMapFactory().getMap(mapid); MaplePortal targetPortal = target.getPortal(0); + victim.saveLocationOnWarp(); victim.changeMap(target, targetPortal); player.message(victim.getName() + " was jailed for " + minutesJailed + " minutes."); } else { diff --git a/src/client/command/commands/gm2/ReachCommand.java b/src/client/command/commands/gm2/ReachCommand.java index 6f4702b995..331114fd9f 100644 --- a/src/client/command/commands/gm2/ReachCommand.java +++ b/src/client/command/commands/gm2/ReachCommand.java @@ -47,6 +47,7 @@ public class ReachCommand extends Command { player.dropMessage(5, "Player '" + victim.getName() + "' is at channel " + victim.getClient().getChannel() + "."); } else { MapleMap map = victim.getMap(); + player.saveLocationOnWarp(); player.forceChangeMap(map, map.findClosestPortal(victim.getPosition())); } } else { diff --git a/src/client/command/commands/gm2/SetStatCommand.java b/src/client/command/commands/gm2/SetStatCommand.java index 42234661f3..7efb7584e9 100644 --- a/src/client/command/commands/gm2/SetStatCommand.java +++ b/src/client/command/commands/gm2/SetStatCommand.java @@ -23,7 +23,6 @@ */ package client.command.commands.gm2; -import client.MapleStat; import client.command.Command; import client.MapleClient; import client.MapleCharacter; @@ -45,7 +44,7 @@ public class SetStatCommand extends Command { int x = Integer.parseInt(params[0]); if (x > Short.MAX_VALUE) x = Short.MAX_VALUE; - else if (x < 0) x = 0; + else if (x < 4) x = 4; // thanks Vcoc for pointing the minimal allowed stat value here player.updateStrDexIntLuk(x); } catch (NumberFormatException nfe) {} diff --git a/src/client/command/commands/gm2/SummonCommand.java b/src/client/command/commands/gm2/SummonCommand.java index 591d44b3b2..f46bafe726 100644 --- a/src/client/command/commands/gm2/SummonCommand.java +++ b/src/client/command/commands/gm2/SummonCommand.java @@ -68,11 +68,13 @@ public class SummonCommand extends Command { if (player.getEventInstance() != null && changingEvent) { if (player.getClient().getChannel() == victim.getClient().getChannel()) { player.getEventInstance().registerPlayer(victim); + victim.saveLocationOnWarp(); victim.changeMap(player.getEventInstance().getMapInstance(player.getMapId()), player.getMap().findClosestPortal(player.getPosition())); } else { player.dropMessage("Target isn't on your channel, not able to warp into event instance."); } } else {//If victim isn't in an event instance or is in the same event instance as the one the caller is, just warp them. + victim.saveLocationOnWarp(); victim.changeMap(player.getMapId(), player.getMap().findClosestPortal(player.getPosition())); } if (player.getClient().getChannel() != victim.getClient().getChannel()) {//And then change channel if needed. diff --git a/src/client/command/commands/gm2/WarpCommand.java b/src/client/command/commands/gm2/WarpCommand.java index 6e123bcc50..aee84fc769 100644 --- a/src/client/command/commands/gm2/WarpCommand.java +++ b/src/client/command/commands/gm2/WarpCommand.java @@ -49,6 +49,7 @@ public class WarpCommand extends Command { } // expedition issue with this command detected thanks to Masterrulax + player.saveLocationOnWarp(); player.changeMap(target, target.getRandomPlayerSpawnpoint()); } catch (Exception ex) { player.yellowMessage("Map ID " + params[0] + " is invalid."); diff --git a/src/client/command/commands/gm3/ReloadMapCommand.java b/src/client/command/commands/gm3/ReloadMapCommand.java index 4485def5e8..61a47117ce 100644 --- a/src/client/command/commands/gm3/ReloadMapCommand.java +++ b/src/client/command/commands/gm3/ReloadMapCommand.java @@ -41,6 +41,7 @@ public class ReloadMapCommand extends Command { int callerid = c.getPlayer().getId(); for (MapleCharacter chr : oldMap.getCharacters()) { + chr.saveLocationOnWarp(); chr.changeMap(newMap); if (chr.getId() != callerid) chr.dropMessage("You have been relocated due to map reloading. Sorry for the inconvenience."); diff --git a/src/client/command/commands/gm3/WarpSnowBallCommand.java b/src/client/command/commands/gm3/WarpSnowBallCommand.java index c66e4563fe..4018010e2e 100644 --- a/src/client/command/commands/gm3/WarpSnowBallCommand.java +++ b/src/client/command/commands/gm3/WarpSnowBallCommand.java @@ -40,6 +40,7 @@ public class WarpSnowBallCommand extends Command { MapleCharacter player = c.getPlayer(); List chars = new ArrayList<>(player.getMap().getCharacters()); for (MapleCharacter chr : chars) { + chr.saveLocationOnWarp(); chr.changeMap(109060000, chr.getTeam()); } } diff --git a/src/net/PacketProcessor.java b/src/net/PacketProcessor.java index 3e2815dbd6..ae8ec01675 100644 --- a/src/net/PacketProcessor.java +++ b/src/net/PacketProcessor.java @@ -216,6 +216,7 @@ public final class PacketProcessor { registerHandler(RecvOpcode.TOUCH_MONSTER_ATTACK, new TouchMonsterDamageHandler()); registerHandler(RecvOpcode.TROCK_ADD_MAP, new TrockAddMapHandler()); registerHandler(RecvOpcode.HIRED_MERCHANT_REQUEST, new HiredMerchantRequest()); + registerHandler(RecvOpcode.MOB_BANISH_PLAYER, new MobBanishPlayerHandler()); registerHandler(RecvOpcode.MOB_DAMAGE_MOB, new MobDamageMobHandler()); registerHandler(RecvOpcode.REPORT, new ReportHandler()); registerHandler(RecvOpcode.MONSTER_BOOK_COVER, new MonsterBookCoverHandler()); diff --git a/src/net/opcodes/RecvOpcode.java b/src/net/opcodes/RecvOpcode.java index 3759e2be3f..c6b5ba1517 100644 --- a/src/net/opcodes/RecvOpcode.java +++ b/src/net/opcodes/RecvOpcode.java @@ -69,6 +69,7 @@ public enum RecvOpcode { FACE_EXPRESSION(0x33), USE_ITEMEFFECT(0x34), USE_DEATHITEM(0x35), + MOB_BANISH_PLAYER(0x38), MONSTER_BOOK_COVER(0x39), NPC_TALK(0x3A), REMOTE_STORE(0x3B), diff --git a/src/net/server/channel/handlers/CashOperationHandler.java b/src/net/server/channel/handlers/CashOperationHandler.java index a4d6a01aad..6ad2bd3722 100644 --- a/src/net/server/channel/handlers/CashOperationHandler.java +++ b/src/net/server/channel/handlers/CashOperationHandler.java @@ -39,6 +39,7 @@ import server.CashShop.CashItem; import server.CashShop.CashItemFactory; import client.inventory.manipulator.MapleInventoryManipulator; import constants.ServerConstants; +import server.MapleItemInformationProvider; import tools.FilePrinter; import tools.MaplePacketCreator; import tools.Pair; @@ -62,7 +63,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { final int useNX = slea.readInt(); final int snCS = slea.readInt(); CashItem cItem = CashItemFactory.getItem(snCS); - if (!canBuy(cItem, cs.getCash(useNX))) { + if (!canBuy(chr, cItem, cs.getCash(useNX))) { FilePrinter.printError(FilePrinter.ITEM, "Denied to sell cash item with SN " + snCS); // preventing NPE here thanks to MedicOP c.enableCSActions(); return; @@ -98,7 +99,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { CashItem cItem = CashItemFactory.getItem(slea.readInt()); Map recipient = MapleCharacter.getCharacterFromDatabase(slea.readMapleAsciiString()); String message = slea.readMapleAsciiString(); - if (!canBuy(cItem, cs.getCash(4)) || message.length() < 1 || message.length() > 73) { + if (!canBuy(chr, cItem, cs.getCash(4)) || message.length() < 1 || message.length() > 73) { c.enableCSActions(); return; } @@ -151,7 +152,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { } else { CashItem cItem = CashItemFactory.getItem(slea.readInt()); int type = (cItem.getItemId() - 9110000) / 1000; - if (!canBuy(cItem, cs.getCash(cash))) { + if (!canBuy(chr, cItem, cs.getCash(cash))) { c.enableCSActions(); return; } @@ -181,7 +182,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { } else { CashItem cItem = CashItemFactory.getItem(slea.readInt()); - if (!canBuy(cItem, cs.getCash(cash))) { + if (!canBuy(chr, cItem, cs.getCash(cash))) { c.enableCSActions(); return; } @@ -199,7 +200,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { int cash = slea.readInt(); CashItem cItem = CashItemFactory.getItem(slea.readInt()); - if (!canBuy(cItem, cs.getCash(cash))) { + if (!canBuy(chr, cItem, cs.getCash(cash))) { c.enableCSActions(); return; } @@ -380,7 +381,12 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { return c.checkBirthDate(cal); } - private static boolean canBuy(CashItem item, int cash) { - return item != null && item.isOnSale() && item.getPrice() <= cash; + private static boolean canBuy(MapleCharacter chr, CashItem item, int cash) { + if (item != null && item.isOnSale() && item.getPrice() <= cash) { + FilePrinter.print(FilePrinter.CASHITEM_BOUGHT, chr + " bought " + MapleItemInformationProvider.getInstance().getName(item.getItemId()) + " (SN " + item.getSN() + ") for " + item.getPrice()); + return true; + } else { + return false; + } } } diff --git a/src/net/server/channel/handlers/ItemRewardHandler.java b/src/net/server/channel/handlers/ItemRewardHandler.java index 1d4f7c1ac8..2794c1aedd 100644 --- a/src/net/server/channel/handlers/ItemRewardHandler.java +++ b/src/net/server/channel/handlers/ItemRewardHandler.java @@ -45,7 +45,10 @@ public final class ItemRewardHandler extends AbstractMaplePacketHandler { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { byte slot = (byte) slea.readShort(); int itemId = slea.readInt(); // will load from xml I don't care. - if (c.getPlayer().getInventory(MapleInventoryType.USE).getItem(slot).getItemId() != itemId || c.getPlayer().getInventory(MapleInventoryType.USE).countById(itemId) < 1) return; + + Item it = c.getPlayer().getInventory(MapleInventoryType.USE).getItem(slot); // null check here thanks to Thora + if (it == null || it.getItemId() != itemId || c.getPlayer().getInventory(MapleInventoryType.USE).countById(itemId) < 1) return; + MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); Pair> rewards = ii.getItemReward(itemId); for (RewardItem reward : rewards.getRight()) { diff --git a/src/net/server/channel/handlers/MobBanishPlayerHandler.java b/src/net/server/channel/handlers/MobBanishPlayerHandler.java new file mode 100644 index 0000000000..cf841414e7 --- /dev/null +++ b/src/net/server/channel/handlers/MobBanishPlayerHandler.java @@ -0,0 +1,45 @@ +/* + 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.channel.handlers; + +import client.MapleCharacter; +import client.MapleClient; +import server.life.MapleMonster; +import server.life.MapleLifeFactory.BanishInfo; +import net.AbstractMaplePacketHandler; +import tools.data.input.SeekableLittleEndianAccessor; + +public final class MobBanishPlayerHandler extends AbstractMaplePacketHandler { + + @Override + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + int mobid = slea.readInt(); // mob banish handling detected thanks to MedicOP + + MapleCharacter chr = c.getPlayer(); + MapleMonster mob = chr.getMap().getMonsterById(mobid); + + if (mob != null) { + BanishInfo banishInfo = mob.getBanish(); + if (banishInfo != null) { + chr.changeMapBanish(banishInfo.getMap(), banishInfo.getPortal(), banishInfo.getMsg()); + } + } + } +} \ No newline at end of file diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index 13f4afee49..443fd56fb5 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -1333,7 +1333,7 @@ public class World { activePetsLock.lock(); try { petUpdate = Server.getInstance().getCurrentTime(); - deployedPets = Collections.unmodifiableMap(activePets); + deployedPets = new HashMap<>(activePets); // exception here found thanks to MedicOP } finally { activePetsLock.unlock(); } @@ -1391,7 +1391,7 @@ public class World { activeMountsLock.lock(); try { mountUpdate = Server.getInstance().getCurrentTime(); - deployedMounts = Collections.unmodifiableMap(activeMounts); + deployedMounts = new HashMap<>(activeMounts); } finally { activeMountsLock.unlock(); } diff --git a/src/provider/wz/XMLWZFile.java b/src/provider/wz/XMLWZFile.java index 2a7694fdc9..755b29e902 100644 --- a/src/provider/wz/XMLWZFile.java +++ b/src/provider/wz/XMLWZFile.java @@ -53,7 +53,7 @@ public class XMLWZFile implements MapleDataProvider { } @Override - public MapleData getData(String path) { + public synchronized MapleData getData(String path) { File dataFile = new File(root, path + ".xml"); File imageDataDir = new File(root, path); if (!dataFile.exists()) { diff --git a/src/scripting/npc/NPCConversationManager.java b/src/scripting/npc/NPCConversationManager.java index 31a2d7c81b..8c205358ba 100644 --- a/src/scripting/npc/NPCConversationManager.java +++ b/src/scripting/npc/NPCConversationManager.java @@ -531,6 +531,35 @@ public class NPCConversationManager extends AbstractPlayerInteraction { return true; } + public boolean itemExists(int itemid) { + return MapleItemInformationProvider.getInstance().getName(itemid) != null; + } + + public int getCosmeticItem(int itemid) { + if (itemExists(itemid)) return itemid; + + int baseid; + if (itemid < 30000) { + baseid = (itemid / 1000) * 1000 + (itemid % 100); + } else { + baseid = (itemid / 10) * 10; + } + + return itemid != baseid && itemExists(baseid) ? baseid : -1; + } + + private int getEquippedItemid(int itemid) { + if (itemid < 30000) { + return getPlayer().getFace(); + } else { + return getPlayer().getHair(); + } + } + + public boolean isCosmeticEquipped(int itemid) { + return getEquippedItemid(itemid) == itemid; + } + public boolean isUsingOldPqNpcStyle() { return ServerConstants.USE_OLD_GMS_STYLED_PQ_NPCS && this.getPlayer().getParty() != null; } diff --git a/src/server/MapleItemInformationProvider.java b/src/server/MapleItemInformationProvider.java index 2c5d838a9b..7a5853f4d9 100644 --- a/src/server/MapleItemInformationProvider.java +++ b/src/server/MapleItemInformationProvider.java @@ -110,7 +110,8 @@ public class MapleItemInformationProvider { protected Map pickupRestrictionCache = new HashMap<>(); protected Map getMesoCache = new HashMap<>(); protected Map monsterBookID = new HashMap<>(); - protected Map onEquipUntradableCache = new HashMap<>(); + protected Map untradeableCache = new HashMap<>(); + protected Map onEquipUntradeableCache = new HashMap<>(); protected Map scriptedItemCache = new HashMap<>(); protected Map karmaCache = new HashMap<>(); protected Map triggerItemCache = new HashMap<>(); @@ -942,7 +943,7 @@ public class MapleItemInformationProvider { public boolean canUseCleanSlate(Equip nEquip) { Map eqstats = this.getEquipStats(nEquip.getItemId()); - return ServerConstants.USE_ENHANCED_CLNSLATE || nEquip.getLevel() + nEquip.getUpgradeSlots() < eqstats.get("tuc"); + return ServerConstants.USE_ENHANCED_CLNSLATE || nEquip.getUpgradeSlots() < eqstats.get("tuc"); // issue with clean slate found thanks to Masterrulax } public Item scrollEquipWithId(Item equip, int scrollId, boolean usingWhiteScroll, int vegaItemId, boolean isGM) { @@ -1102,7 +1103,7 @@ public class MapleItemInformationProvider { nEquip.setMp((short) stat.getValue().intValue()); } else if (stat.getKey().equals("tuc")) { nEquip.setUpgradeSlots((byte) stat.getValue().intValue()); - } else if (isDropRestricted(equipId)) { + } else if (isUntradeableRestricted(equipId)) { // thanks Hyun & Thora for showing an issue with more than only "Untradeable" items being flagged as such here byte flag = nEquip.getFlag(); flag |= ItemConstants.UNTRADEABLE; nEquip.setFlag(flag); @@ -1230,6 +1231,23 @@ public class MapleItemInformationProvider { return ret; } + public boolean isUntradeableRestricted(int itemId) { + if (untradeableCache.containsKey(itemId)) { + return untradeableCache.get(itemId); + } + + boolean bRestricted = false; + if(itemId != 0) { + MapleData data = getItemData(itemId); + if (data != null) { + bRestricted = MapleDataTool.getIntConvert("info/tradeBlock", data, 0) == 1; + } + } + + untradeableCache.put(itemId, bRestricted); + return bRestricted; + } + public boolean isLootRestricted(int itemId) { if (dropRestrictionCache.containsKey(itemId)) { return dropRestrictionCache.get(itemId); @@ -1421,12 +1439,12 @@ public class MapleItemInformationProvider { } public boolean isUntradeableOnEquip(int itemId) { - if (onEquipUntradableCache.containsKey(itemId)) { - return onEquipUntradableCache.get(itemId); + if (onEquipUntradeableCache.containsKey(itemId)) { + return onEquipUntradeableCache.get(itemId); } - boolean untradableOnEquip = MapleDataTool.getIntConvert("info/equipTradeBlock", getItemData(itemId), 0) > 0; - onEquipUntradableCache.put(itemId, untradableOnEquip); - return untradableOnEquip; + boolean untradeableOnEquip = MapleDataTool.getIntConvert("info/equipTradeBlock", getItemData(itemId), 0) > 0; + onEquipUntradeableCache.put(itemId, untradeableOnEquip); + return untradeableOnEquip; } public scriptedItem getScriptedItemInfo(int itemId) { diff --git a/src/server/MapleStatEffect.java b/src/server/MapleStatEffect.java index f40ec72a07..05c6ee24b5 100644 --- a/src/server/MapleStatEffect.java +++ b/src/server/MapleStatEffect.java @@ -257,6 +257,8 @@ public class MapleStatEffect { addBuffStatPairToListIfNotZero(statups, MapleBuffStat.COUPON_DRP3, 1); break; } + } else if(isExpIncrease(sourceid)) { + addBuffStatPairToListIfNotZero(statups, MapleBuffStat.EXP_INCREASE, MapleDataTool.getInt("expinc", source, 0)); } } else { if(isMapChair(sourceid)) { @@ -1366,6 +1368,10 @@ public class MapleStatEffect { return itemType == 5211 || itemType == 5360; } + public static boolean isExpIncrease(int sourceid) { + return sourceid >= 2022450 && sourceid <= 2022452; + } + 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 1e62e6f410..df73a292f2 100644 --- a/src/server/life/MapleMonster.java +++ b/src/server/life/MapleMonster.java @@ -544,8 +544,13 @@ public class MapleMonster extends AbstractLoadedMapleLife { int partyExp = 0; if (attacker.getHp() > 0) { exp *= attacker.getExpRate(); + + Integer expBonus = attacker.getBuffedValue(MapleBuffStat.EXP_INCREASE); + if (expBonus != null) { // exp increase buff found thanks to HighKey21 + exp += expBonus; + } + int personalExp = (int) exp; - if (exp <= Integer.MAX_VALUE) { // assuming no negative xp here if (partyModifier > 0.0f) { partyExp = (int) (personalExp * partyModifier * ServerConstants.PARTY_BONUS_EXP_RATE); diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index 9c078e9a7e..9482cc2de6 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -384,6 +384,8 @@ public class MapleMap { } private void spawnAndAddRangedMapObject(MapleMapObject mapobject, DelayedPacketCreation packetbakery, SpawnCondition condition) { + List inRangeCharacters = new LinkedList<>(); + chrRLock.lock(); objectWLock.lock(); try { @@ -393,7 +395,7 @@ public class MapleMap { for (MapleCharacter chr : characters) { if (condition == null || condition.canSpawn(chr)) { if (chr.getPosition().distanceSq(mapobject.getPosition()) <= getRangedDistance()) { - packetbakery.sendPackets(chr.getClient()); + inRangeCharacters.add(chr); chr.addVisibleMapObject(mapobject); } } @@ -402,9 +404,15 @@ public class MapleMap { objectWLock.unlock(); chrRLock.unlock(); } + + for (MapleCharacter chr : inRangeCharacters) { + packetbakery.sendPackets(chr.getClient()); + } } private void spawnRangedMapObject(MapleMapObject mapobject, DelayedPacketCreation packetbakery, SpawnCondition condition) { + List inRangeCharacters = new LinkedList<>(); + chrRLock.lock(); try { int curOID = getUsableOID(); @@ -412,7 +420,7 @@ public class MapleMap { for (MapleCharacter chr : characters) { if (condition == null || condition.canSpawn(chr)) { if (chr.getPosition().distanceSq(mapobject.getPosition()) <= getRangedDistance()) { - packetbakery.sendPackets(chr.getClient()); + inRangeCharacters.add(chr); chr.addVisibleMapObject(mapobject); } } @@ -420,6 +428,10 @@ public class MapleMap { } finally { chrRLock.unlock(); } + + for (MapleCharacter chr : inRangeCharacters) { + packetbakery.sendPackets(chr.getClient()); + } } private int getUsableOID() { diff --git a/src/server/maps/MapleMapFactory.java b/src/server/maps/MapleMapFactory.java index d959a3686d..cea8800b36 100644 --- a/src/server/maps/MapleMapFactory.java +++ b/src/server/maps/MapleMapFactory.java @@ -100,7 +100,7 @@ public class MapleMapFactory { } String mapName = getMapName(mapid); - MapleData mapData = source.getData(mapName); + MapleData mapData = source.getData(mapName); // source.getData issue with giving nulls in rare ocasions found thanks to MedicOP MapleData infoData = mapData.getChildByPath("info"); String link = MapleDataTool.getString(infoData.getChildByPath("link"), ""); diff --git a/src/tools/FilePrinter.java b/src/tools/FilePrinter.java index 95e798e2f8..4ae5a12416 100644 --- a/src/tools/FilePrinter.java +++ b/src/tools/FilePrinter.java @@ -18,6 +18,7 @@ public class FilePrinter { MAPLE_MAP = "mapleMap.txt", ERROR38 = "error38.txt", PACKET_LOG = "log.txt", + CASHITEM_BOUGHT = "cashlog.txt", EXCEPTION = "exceptions.txt", SQL_EXCEPTION = "sqlexceptions.txt", PACKET_HANDLER = "PacketHandler/", diff --git a/tools/MapleCashCosmeticsChecker/build.xml b/tools/MapleCashCosmeticsChecker/build.xml new file mode 100644 index 0000000000..6cacd4baa8 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project MapleCashCosmeticsChecker. + + + diff --git a/tools/MapleCashCosmeticsChecker/lib/care/amoria/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/amoria/face.txt new file mode 100644 index 0000000000..0a65e1bb64 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/amoria/face.txt @@ -0,0 +1,4 @@ +Male VIP: Champion Focus, Irritable Face, Motivated Look, Perplexed Stare, Dramatic Face, Rebel's Fire, Alert Face, Babyface Pout, Sad Innocence, Worrisome Glare +Male REG: Pensive Look, Shade of Cool, Alert Face, Cool Guy Gaze, Leisure Look, Sad Innocence, Look of Wonder +Female VIP: Athena's Grace, Hera's Radiance, Fearful Stare, Leisure Look, Strong Stare, Angel Glow, Babyface Pout, Pucker Up Face, Dollface Look, Soul's Window +Female REG: Glitzy Face, Demure Poise Eyes, Fearful Stare, Babyface Pout, Dollface Look, Gentle Glow, Athena's Grace \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/amoria/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/amoria/hair.txt new file mode 100644 index 0000000000..aa5914a738 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/amoria/hair.txt @@ -0,0 +1,4 @@ +Male VIP: Casanova, Eternal Elegance, Fuzz, Jun Pyo Hair, Metro, Natural, Rockie, Romance, Saturday Special, Super Suave, Windy Hair +Male EXP: Antagonist, Explosion, w/ Bald Spot, Bowl Cut, Trip Scratch, Rebel, Fireball, Toben, Monkey, Head +Female VIP: Angelica, Ballroom Classic, Carla, Caspia, Daisy Do, The Honeybun, Victorian Wrap, Darling Diva, Cecelia Twist, Classy Sass, Lana +Female EXP: Connie, Bridget, Sammy, Carla, Stella, Angelica, Polly, Housewife, Rollered Hair \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/ariant/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/ariant/face.txt new file mode 100644 index 0000000000..279ee08ee9 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/ariant/face.txt @@ -0,0 +1,4 @@ +Male VIP: Insomniac Daze, Motivated Look, Fearful Glance, Rebel's Fire, Alert Face, Curious Dog +Male REG: Edge of Emotion, Fearful Glance, Anger's Blaze, Dramatic Face, Smart Aleck, Perplexed Stare +Female VIP: Look of Death, Motivated Look, Keen Look, Strong Stare, Pucker Up Face, Soul's Window +Female REG: Kitty Cat, Poker Face, Look of Death, Wide-eyed Girl, Beauty Stare, Hypnotized Look, Leisure Look \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/ariant/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/ariant/hair.txt new file mode 100644 index 0000000000..8cbfffa164 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/ariant/hair.txt @@ -0,0 +1,4 @@ +Male VIP: Afro (small), Cabana Boy, Dreadlocks, Kravitz Locks, Line Scratch, Mane, Matinee Idol, Natural, Tornade Hair +Male REG: Afro (small), Cabana Boy, Dreadlocks, Dreamcatcher, Hobo, Kravitz Locks, Line Scratch, Mane, Matinee Idol, Natural, Tornade Hair +Female VIP: Bridget, Celeb, Edgy, Lana, Penelope, Rae, Boyish, Desert Flower, Tighty Bun +Female REG: Boyish, Bridget, Celeb, Curly Stream, Dashing Damsel, Desert Flower, Lana, Oh So Windy, Palm Tree Hair, Penelope, Rae, Tighty Bun \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/cbd/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/cbd/face.txt new file mode 100644 index 0000000000..40f1344948 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/cbd/face.txt @@ -0,0 +1,4 @@ +Male VIP: Fierce Edge, Insomniac Daze, Overjoyed Smile, Shuteye, Alert Face, Curious Dog +Male REG: Intense Stare, Demure Poise, Leisure Look, Insomniac Daze, Alert Face, Babyface Pout, Overjoyed Smile +Female VIP: Compassion Look, Hypnotized Look, Look of Death, Shuteye, Pucker Up Face, Soul's Window +Female REG: Glamorous Edge, Demure Poise Eyes, Leisure Look, Strong Stare, Curious Look, Beauty Stare, Compassion Look \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/cbd/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/cbd/hair.txt new file mode 100644 index 0000000000..390ed8827d --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/cbd/hair.txt @@ -0,0 +1,4 @@ +Male VIP: Acorn, w/ Bald Spot, Fireball, Julian Hair, Old Man 'Do, Preppy Spike, Rebel, Toben, Vincent +Male REG: Astro, Fireball, Julian Hair, Mane, Metrosexual, Old Man 'Do, Romance, Slick Dean, Exotica +Female VIP: Apple Hair, Bowlcut, Connie, Disheveled, Ellie, Grandma ma', Miru, Monica, Veronica +Female REG: Apple Hair, Bowlcut, Ellie, Grandma ma', Holla' Back Do, Monica, Oh So Windy, Sonara Wave, Tall Bun, School Girl Hair \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/henesys/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/henesys/face.txt new file mode 100644 index 0000000000..00fed1e657 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/henesys/face.txt @@ -0,0 +1,4 @@ +Male VIP: Motivated Look, Perplexed Stare, Fearful Glance, Dramatic Face, Rebel's Fire, Alert Face, Babyface Pout, Sad Innocence, Worrisome Glare, Curious Dog, Look of Wonder, Eye of the Lion, Child's Play, Sarcastic Face +Male REG: Ghostface Stare, Undecided Face, Worrisome Glare, Curious Dog, Motivated Look, Child's Play, Alert Face +Female VIP: Motivated Look, Fearful Stare, Leisure Look, Strong Stare, Angel Glow, Babyface Pout, Pucker Up Face, Dollface Look, Hopeless Gaze, Soul's Window, Curious Look, Wide-eyed Girl, Innocent Look, Tender Love +Female REG: Gentle Glow, Kitty Cat, Motivated Look, Hopeless Gaze, Keen Look, Leisure Look, Curious Look \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/henesys/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/henesys/hair.txt new file mode 100644 index 0000000000..a43c0f8ea4 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/henesys/hair.txt @@ -0,0 +1,6 @@ +Male VIP: Aran Cut, Catalyst, Acorn, Topknot, Wind, Shaggy Wax, The Coco +Male REG: Aran Cut, Catalyst, Evan Hair (M), Acorn, Shaggy Wax, The Coco, The Mo Rawk, Topknot, Wind +Male EXP: Aran Cut, Acorn, Shaggy Wax, The Coco, The Mo Rawk, Topknot, Wind, Buzz +Female VIP: Angelica, Aran Hair, Chantelle, Crazy Medusa, Fourtail Braids, Frizzle Dizzle, Full Bangs +Female REG: Angelica, Aran Hair, Chantelle, Crazy Medusa, Evan Hair (F), Fourtail Braids, Full Bangs, Rainbow, Stella +Female EXP: Angelica, Aran Hair, Chantelle, Crazy Medusa, Fourtail Braids, Full Bangs, Stella, Head \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/kerning_city/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/kerning_city/face.txt new file mode 100644 index 0000000000..82dc4d22aa --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/kerning_city/face.txt @@ -0,0 +1,4 @@ +Male VIP: +Male REG: +Female VIP: +Female REG: \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/kerning_city/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/kerning_city/hair.txt new file mode 100644 index 0000000000..e97ad8983d --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/kerning_city/hair.txt @@ -0,0 +1,6 @@ +Male VIP: Antagonist, Aran Cut, Cornrow, Dragon Tail, Rockstar, Short Top Tail, Male Runway Hair +Male REG: Antagonist, Aran Cut, Cornrow, Dragon Tail, Dual Blade Hair, Hontas, Lucky Charms, Rockstar, Short Top Tail +Male EXP: Antagonist, Aran Cut, Cornrow, Dragon Tail, Hontas, Lucky Charms, Short Top Tail, Head +Female VIP: Aran Hair, Bridget, Gardener, Pei Pei, Penelope, Shaggy Dog, Ravishing Raven +Female REG: Annie, Aran Hair, Curly Stream, Gardener, Pei Pei, Penelope, Ravishing Raven, Shaggy Dog, Super Diva +Female EXP: Annie, Aran Hair, Curly Stream, Gardener, Pei Pei, Penelope, Shaggy Dog, Rollered Hair \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/ludibrium/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/ludibrium/face.txt new file mode 100644 index 0000000000..2d01b0a2ff --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/ludibrium/face.txt @@ -0,0 +1,4 @@ +Male VIP: Motivated Look, Perplexed Stare, Fearful Glance, Dramatic Face, Rebel's Fire, Alert Face, Babyface Pout, Sad Innocence, Worrisome Glare, Curious Dog, Look of Wonder, Cool Guy Gaze +Male REG: Hypnotized Look, Edge of Emotion, Insomniac Daze, Dramatic Face, Perplexed Stare, Sad Innocence, Overjoyed Smile +Female VIP: Motivated Look, Fearful Stare, Leisure Look, Strong Stare, Angel Glow, Babyface Pout, Pucker Up Face, Dollface Look, Hopeless Gaze, Soul's Window, Curious Look, Wisdom Glance +Female REG: Glamorous Edge, Kitty Cat, Leisure Look, Hopeless Gaze, Angel Glow, Pucker Up Face, Glitzy Face \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/ludibrium/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/ludibrium/hair.txt new file mode 100644 index 0000000000..71e85593fa --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/ludibrium/hair.txt @@ -0,0 +1,6 @@ +Male VIP: Afro (big), Bowl Cut, Fuzz, Hector Hair, Julian Hair, Tentacle Hair, Trip Scratch, Urban Dragon +Male REG: Afro (big), Bowl Cut, Bubba GoaTee, Fuzz, Hector Hair, Grooovy Do, Julian Hair, Hip Hop Cut, Rise N Shine, Robot, Tentacle Hair, The Mo Rawk, Urban Dragon +Male EXP: Afro (big), Bowl Cut, Bubba GoaTee, Fuzz, Grooovy Do, Julian Hair, Rise N Shine, Robot, Tentacle Hair, The Mo Rawk, Urban Dragon, Buzz +Female VIP: Apple Hair, Candy Heart, Eye-skimming Bang, Female Runway Hair, Lovely Ladyhawk, Naomi, Pigtails, Ayu +Female REG: Apple Hair, Ayu, Bohemian Hair, Candy Heart, Eye-skimming Bang, Female Runway Hair, Jean, Lovely Ladyhawk, Naomi, Pigtails, Rastafari, Sonara Wave, Tall Bun, Minnie +Female EXP: Apple Hair, Ayu, Bohemian Hair, Candy Heart, Eye-skimming Bang, Female Runway Hair, Jean, Lovely Ladyhawk, Pigtails, Rastafari, Tall Bun, Minnie, Head \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/mu_lung/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/mu_lung/face.txt new file mode 100644 index 0000000000..fde46df078 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/mu_lung/face.txt @@ -0,0 +1,4 @@ +Male VIP: Motivated Look, Perplexed Stare, Fearful Glance, Rebel's Fire, Alert Face, Babyface Pout, Sad Innocence, Curious Dog, Smart Aleck, Child's Play, Sarcastic Face +Male REG: Demure Poise, Shade of Cool, Sad Innocence, Look of Wonder, Leisure Look, Alert Face, Cool Guy Gaze +Female VIP: Hypnotized Look, Motivated Look, Keen Look, Strong Stare, Babyface Pout, Pucker Up Face, Hopeless Gaze, Soul's Window, Look of Death, Innocent Look, Tender Love +Female REG: Gentle Glow, Poker Face, Athena's Grace, Compassion Look, Fearful Stare, Wisdom Glance, Wide-eyed Girl \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/mu_lung/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/mu_lung/hair.txt new file mode 100644 index 0000000000..cf83e6d206 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/mu_lung/hair.txt @@ -0,0 +1,4 @@ +Male VIP: Buddha Fire, Cozy Amber, Dreadlocks, Gruff & Tough, Monkey, Puffy Fro, Shaggy Dragon, Urban Dragon +Male EXP: Alex, Buddha Fire, Cozy Amber, Dreadlocks, Exotica, Gruff & Tough, Kongfu Braids, Monkey, Puffy Fro, Rising Rocker, Shaggy Dragon, The Curl, Urban Dragon, Buzz +Female VIP: Chantelle, Cutey Doll, Housewife, Lady Mariko, Lori, Ming Ming, Pei Pei, Tighty Bun +Female EXP: Chantelle, Cutey Doll, Demolishing Diva, Housewife, Lady Mariko, Lori, Ming Ming, Onna's Honor, Pei Pei, Perfect Stranger, Spunky Do, Tighty Bun, Short Twin Tails, Head \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/nlc/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/nlc/face.txt new file mode 100644 index 0000000000..cc6eb2de6d --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/nlc/face.txt @@ -0,0 +1,4 @@ +Male VIP: Curious Dog, Motivated Look, Perplexed Stare, Fearful Glance, Dramatic Face, Rebel's Fire, Alert Face, Babyface Pout, Worrisome Glare +Male REG: Undecided Face, Shade of Cool, Intense Stare, Perplexed Stare, Worrisome Glare, Cool Guy Gaze, Insomniac Daze +Female VIP: Beauty Stare, Fearful Stare, Leisure Look, Strong Stare, Angel Glow, Babyface Pout, Pucker Up Face, Hopeless Gaze, Soul's Window +Female REG: Demure Poise Eyes, Glitzy Face, Hypnotized Look, Motivated Look, Gentle Glow, Dollface Look, Soul's Window \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/nlc/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/nlc/hair.txt new file mode 100644 index 0000000000..38c180069f --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/nlc/hair.txt @@ -0,0 +1,4 @@ +Male VIP: Afro (big), Hector Hair, Messy Spike, Roving Rockstar, Spiky Shag, The Coco, Unbalanced +Male EXP: Afro (big), Alex, Evan Hair (M), Fury, Hector Hair, Messy Spike, Roving Rockstar, Spiky Shag, The Coco, Tribal Buzz, Unbalanced, Head +Female VIP: Eye-skimming Bang, Fluffy Dolly, Low Cut Bob, Messy Pigtails, Model's Ambition, Roxy, Sunflower Power +Female EXP: Demolishing Diva, Eye-skimming Bang, Evan Hair (F), Fluffy Dolly, Low Cut Bob, Maiden's Weave, Messy Pigtails, Model's Ambition, Roxy, Streaky Siren, Sunflower Power, Rollered Hair \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/orbis/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/orbis/face.txt new file mode 100644 index 0000000000..fb60cc44f8 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/orbis/face.txt @@ -0,0 +1,4 @@ +Male VIP: Motivated Look, Perplexed Stare, Fearful Glance, Dramatic Face, Rebel's Fire, Alert Face, Babyface Pout, Sad Innocence, Worrisome Glare, Curious Dog, Look of Wonder, Child's Play, Sarcastic Face +Male REG: Pensive Look, Fearful Glance, Cool Guy Gaze, Overjoyed Smile, Child's Play, Hypnotized Look, Dramatic Face +Female VIP: Motivated Look, Fearful Stare, Leisure Look, Strong Stare, Angel Glow, Babyface Pout, Pucker Up Face, Dollface Look, Hopeless Gaze, Soul's Window, Curious Look, Innocent Look, Tender Love +Female REG: Gentle Glow, Poker Face, Compassion Look, Angel Glow, Wisdom Glance, Soul's Window, Dollface Look \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/orbis/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/orbis/hair.txt new file mode 100644 index 0000000000..d2515f676d --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/orbis/hair.txt @@ -0,0 +1,6 @@ +Male VIP: Clean-Cut Short Hair, Foil Perm, Messy Spike, Metrosexual, Mohecan Shaggy Do, Spiky Shag, Tristan +Male REG: Baldie, Clean-Cut Short Hair, Foil Perm, Messy Spike, Metrosexual, Mohecan Shaggy Do, Neon Cactus, Receding Hair, Spiky Shag, Tristan, Updo +Male EXP: Baldie, Clean-Cut Short Hair, Foil Perm, Messy Spike, Mohecan Shaggy Do, Receding Hair, Spiky Shag, Tristan, Updo +Female VIP: Bow Hair, Caspia, Monica, Princessa, Rose, The Honeybun, Top Tied Hair +Female REG: Bow Hair, Caspia, Dashing Damsel, Lilin Hair, Monica, Princess Warrior, Princessa, Rose, Short Twin Tails, The Honeybun, Top Tied Hair, Bowl Cut +Female EXP: Bow Hair, Caspia, Lilin Hair, Monica, Princess Warrior, Princessa, Rose, Short Twin Tails, Top Tied Hair, Bowl Cut \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/showa/face.txt b/tools/MapleCashCosmeticsChecker/lib/care/showa/face.txt new file mode 100644 index 0000000000..93a64287ed --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/showa/face.txt @@ -0,0 +1,4 @@ +Male VIP: Fierce Edge, Motivated Look, Fearful Glance, Rebel's Fire, Alert Face, Curious Dog +Male REG: Ghostface Stare, Intense Stare, Shuteye, Motivated Look, Fierce Edge, Irritable Face, Overjoyed Smile +Female VIP: Compassion Look, Motivated Look, Keen Look, Strong Stare, Pucker Up Face, Soul's Window +Female REG: Glitzy Face, Glamorous Edge, Motivated Look, Leisure Look, Look of Death, Shuteye, Beauty Stare \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/care/showa/hair.txt b/tools/MapleCashCosmeticsChecker/lib/care/showa/hair.txt new file mode 100644 index 0000000000..d379b7b13d --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/care/showa/hair.txt @@ -0,0 +1,4 @@ +Male VIP: Dreamcatcher, Clean-Cut Short Hair, Dragon Tail, Gruff & Tough, Matinee Idol, Metrosexual, Mohecan Shaggy Do, Puffy Fro, Short Top Tail, Tristan +Male REG: Clean-Cut Short Hair, Dragon Tail, Dreamcatcher, Gruff & Tough, Lion Hair, Matinee Idol, Metrosexual, Mohecan Shaggy Do, Puffy Fro, Short Top Tail, Spiky Tail, Tristan +Female VIP: Candy Heart, Dambi, Fourtail Braids, Lady Mariko, Mary, Polly, Princessa, Sammy, Short Shaggy Hair, Top Tied Hair +Female REG: Candy Heart, Dambi, Fourtail Braids, Jean, Lady Mariko, Onna's Honor, Paula, Princess Warrior, Princessa, Short Shaggy Hair, Streaky Siren, Top Tied Hair, Palm Tree Hair \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/colors.txt b/tools/MapleCashCosmeticsChecker/lib/colors.txt new file mode 100644 index 0000000000..215a123647 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/colors.txt @@ -0,0 +1,10 @@ +amoria 0 1 3 4 5 7 +ariant 0 1 3 6 7 +cbd 2 3 4 7 +henesys 0 1 2 4 6 7 +kerning +ludi 2 3 4 5 7 +mulung 0 1 3 5 6 7 +nlc 1 2 3 4 5 6 7 +orbis 1 3 4 7 +showa 0 1 2 3 4 5 7 \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/lib/result.txt b/tools/MapleCashCosmeticsChecker/lib/result.txt new file mode 100644 index 0000000000..60ce233efb --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/lib/result.txt @@ -0,0 +1,213 @@ + # Report File autogenerated from the MapleCashCosmeticsChecker feature by Ronan Lana. + # Generated data takes into account several data info from the server source files and the server-side WZ.xmls. + +Found 42 entries with missing cosmetic entries. + NPC 1012103:VIP + 30060, 30140, 30200, 30210, 30310, 33040, 33100 + 31150, 31300, 31350, 31700, 31740, 34050, 34110 + + NPC 1012104:EXP + 30030, 30140, 30200, 30210, 30310, 30610, 33040, 33100 + 31070, 31150, 31300, 31350, 31430, 31700, 34050, 34110 + + NPC 1012104:REG + 30060, 30140, 30200, 30210, 30310, 30610, 33040, 33100 + 31070, 31080, 31150, 31300, 31350, 31700, 34050, 34110 + + NPC 1052004:VIP + 20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014, 20015, 20022, 20028, 20031 + 21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21013, 21014, 21023, 21026 + + NPC 1052005:REG + 20000, 20005, 20008, 20012, 20016, 20022, 20032 + 21000, 21002, 21008, 21014, 21020, 21024, 21029 + + NPC 1052100:VIP + 30040, 30130, 30780, 30850, 30860, 30920, 33040 + 31090, 31140, 31330, 31440, 31760, 31880, 34050 + + NPC 1052101:EXP + 30130, 30430, 30520, 30770, 30780, 30850, 30920, 33040 + 31060, 31140, 31330, 31520, 31760, 31880, 34010, 34050 + + NPC 1052101:REG + 30040, 30130, 30520, 30770, 30780, 30850, 30920, 33040 + 31060, 31140, 31330, 31440, 31520, 31750, 31760, 31880, 34050 + + NPC 2010001:VIP + 30230, 30260, 30280, 30340, 30490 + 31110, 31220, 31230, 31630, 31790 + + NPC 2010002:VIP + 20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20012, 20014, 20022, 20028, 20031 + 21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21012, 21014, 21023, 21026 + + NPC 2012007:EXP + 30230, 30280, 30340, 30490, 30530, 30740 + 31110, 31220, 31230, 31710, 31790, 31890, 31930 + + NPC 2012007:REG + 30230, 30260, 30280, 30340, 30490, 30530, 30630, 30740 + 31110, 31220, 31230, 31630, 31650, 31710, 31790, 31890, 31930 + + NPC 2012009:REG + 20003, 20011, 20021, 20022, 20023, 20027, 20031 + 21004, 21007, 21010, 21012, 21020, 21021, 21030 + + NPC 2040019:REG + 20001, 20003, 20007, 20013, 20021, 20023, 20025 + 21002, 21004, 21006, 21008, 21022, 21027, 21029 + + NPC 2041007:VIP + 30160, 30190, 30250, 30640, 30660, 30840, 30870, 30990 + 31270, 31290, 31550, 31680, 31810, 31830, 31840, 31870 + + NPC 2041009:EXP + 30030, 30190, 30220, 30250, 30540, 30610, 30620, 30640, 30650, 30660, 30840, 30990 + 31170, 31270, 31430, 31510, 31540, 31550, 31600, 31680, 31810, 31830, 31840, 31870 + + NPC 2041009:REG + 30190, 30220, 30250, 30540, 30610, 30620, 30640, 30650, 30660, 30840, 30870, 30940, 30990 + 31170, 31270, 31290, 31510, 31540, 31550, 31600, 31640, 31680, 31810, 31830, 31840, 31870 + + NPC 2041010:VIP + 20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20011, 20012, 20014, 20031 + 21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21010, 21012, 21014 + + NPC 2090100:VIP + 30150, 30240, 30370, 30420, 30640, 30710, 30750, 30810 + 31140, 31160, 31180, 31300, 31460, 31470, 31660, 31910 + + NPC 2090101:EXP + 30030, 30150, 30240, 30370, 30420, 30550, 30600, 30640, 30700, 30710, 30720, 30750, 30810, 30830 + 31140, 31160, 31180, 31210, 31300, 31430, 31460, 31470, 31660, 31690, 31800, 31890, 31910, 31940 + + NPC 2090104:REG + 20002, 20005, 20007, 20011, 20014, 20017, 20029 + 21001, 21010, 21013, 21018, 21020, 21021, 21030 + + NPC 2090104:VIP + 20000, 20001, 20004, 20005, 20006, 20007, 20009, 20012, 20022, 20028, 20031 + 21000, 21003, 21005, 21006, 21008, 21009, 21011, 21012, 21023, 21024, 21026 + + NPC 2100005:REG + 30150, 30170, 30180, 30320, 30330, 30410, 30460, 30680, 30800, 30820, 30900 + 31090, 31190, 31330, 31340, 31400, 31420, 31520, 31620, 31650, 31660, 34000 + + NPC 2100006:VIP + 30150, 30170, 30180, 30320, 30330, 30410, 30460, 30820, 30900 + 31040, 31090, 31190, 31330, 31340, 31400, 31420, 31620, 31660 + + NPC 2100008:VIP + 20000, 20004, 20005, 20012, 20013, 20031 + 21000, 21003, 21006, 21009, 21012, 21024 + + NPC 2100009:REG + 20001, 20003, 20009, 20010, 20025, 20031 + 21002, 21009, 21011, 21013, 21016, 21029, 21030 + + NPC 9120100:VIP + 30260, 30280, 30340, 30710, 30780, 30800, 30810, 30820, 30920 + 31000, 31030, 31100, 31350, 31460, 31550, 31770, 31790, 31850 + + NPC 9120101:REG + 30260, 30280, 30340, 30360, 30710, 30780, 30790, 30800, 30810, 30820, 30920 + 31350, 31410, 31460, 31540, 31550, 31710, 31720, 31770, 31790, 31800, 31850, 34000 + + NPC 9120102:VIP + 20000, 20004, 20005, 20012, 20020, 20031 + 21000, 21003, 21006, 21012, 21021, 21024 + + NPC 9120103:REG + 20000, 20016, 20019, 20020, 20021, 20024, 20026 + 21000, 21002, 21009, 21016, 21022, 21025, 21027 + + NPC 9201015:VIP + 30050, 30300, 30410, 30450, 30510, 30570, 30580, 30590, 30660, 30910 + 31150, 31220, 31260, 31310, 31420, 31480, 31490, 31580, 31590, 31610, 31630 + + NPC 9201016:EXP + 30000, 30020, 30110, 30130, 30160, 30190, 30240, 30270, 30430 + 31000, 31030, 31050, 31070, 31090, 31150, 31310, 31910, 34010 + + NPC 9201018:VIP + 20000, 20001, 20003, 20004, 20005, 20006, 20007, 20008, 20018, 20019 + 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21012, 21018, 21019 + + NPC 9201019:REG + 20002, 20005, 20007, 20011, 20014, 20027, 20029 + 21001, 21005, 21007, 21017, 21018, 21020, 21022 + + NPC 9201063:EXP + 30250, 30400, 30430, 30440, 30490, 30730, 30830, 30870, 30880, 33100 + 31320, 31450, 31560, 31570, 31690, 31720, 31730, 31830, 34010 + + NPC 9201064:VIP + 30250, 30490, 30730, 30870, 30880, 33100 + 31320, 31450, 31560, 31730, 31830 + + NPC 9201069:VIP + 20000, 20001, 20003, 20004, 20005, 20006, 20008, 20012, 20031 + 21001, 21002, 21003, 21004, 21005, 21006, 21008, 21012, 21016 + + NPC 9201070:REG + 20001, 20008, 20011, 20013, 20024, 20029, 20032 + 21000, 21007, 21011, 21012, 21017, 21020, 21022 + + NPC 9270023:REG + 20002, 20005, 20006, 20013, 20017, 20021, 20024 + 21002, 21003, 21014, 21016, 21017, 21021, 21027 + + NPC 9270024:VIP + 20005, 20012, 20013, 20020, 20021, 20026 + 21006, 21009, 21011, 21012, 21021, 21025 + + NPC 9270036:VIP + 30000, 30020, 30110, 30120, 30270, 30290, 30310, 30670, 30840 + 31010, 31050, 31110, 31120, 31240, 31250, 31280, 31670, 31810 + + NPC 9270037:REG + 30110, 30180, 30260, 30290, 30300, 30350, 30470, 30720, 30840 + 31110, 31200, 31250, 31280, 31600, 31640, 31670, 31810, 34020 + +Unused cosmetics: 22 +30010 Zeta +30070 Back +30080 Buzzcut +30090 Mohawk +30100 Fantasy +30480 Babby Cut +30560 Grand Lionman +30690 Metro Man +30760 Bowling Ball +30890 Eastern Mystery +30930 Boy Band Cut +30950 Volume Cut +31020 Francesca +31130 Jolie +31530 Zessica +31780 ???? ?? +31820 Grace +31860 Laguna Beach +31920 CL Hair +31950 Vintage Flip +33000 Prince Cut +34030 Designer Hair + +Missing cosmetic itemids: 15 +Bohemian Hair +Bow Hair +Clean-Cut Short Hair +Dual Blade Hair +Evan Hair (F) +Evan Hair (M) +Explosion +Lilin Hair +Low Cut Bob +Messy Pigtails +Oh So Windy +Spiky Shag +Top Tied Hair +Updo +Windy Hair + diff --git a/tools/MapleCashCosmeticsChecker/manifest.mf b/tools/MapleCashCosmeticsChecker/manifest.mf new file mode 100644 index 0000000000..328e8e5bc3 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/MapleCashCosmeticsChecker.java b/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/MapleCashCosmeticsChecker.java new file mode 100644 index 0000000000..3ab8ee7c70 --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/MapleCashCosmeticsChecker.java @@ -0,0 +1,722 @@ +/* + 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 maplecashcosmeticschecker; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Set; + +/** + * + * @author RonanLana + + This application parses the cosmetic recipes defined within "lib/care" folder, loads + every present cosmetic itemid from the XML data, then checks the scripts for missed + cosmetics within the stylist/surgeon. Results from the search are reported in a report + file. + + Note: to best make use of this feature, set ignoreCurrentScriptCosmetics = true. This + way, every available cosmetic present on the recipes will be listed on the report. + + Estimated parse time: 1 minute + + */ +public class MapleCashCosmeticsChecker { + static String libPath = "lib"; + static String handbookPath = "../../handbook"; + static String wzPath = "../../wz"; + static String scriptPath = "../../scripts"; + + private static PrintWriter printWriter = null; + static InputStreamReader fileReader = null; + static BufferedReader bufferedReader = null; + + static boolean ignoreCurrentScriptCosmetics = false; + + static int initialStringLength = 50; + + static byte status = 0; + + static Map> scriptCosmetics = new HashMap<>(); + static Map scriptEntries = new HashMap<>(500); + + static Set allCosmetics = new HashSet<>(); + + static Set unusedCosmetics = new HashSet<>(); + static Map> usedCosmetics = new HashMap<>(); + + static Map couponNames = new HashMap<>(); + static Map cosmeticNpcs = new HashMap<>(); // expected only 1 NPC per cosmetic coupon (town care/salon) + static Map, Integer> cosmeticNpcids = new HashMap<>(); + + static Set missingCosmeticNames = new HashSet<>(); + static Map cosmeticNameIds = new HashMap<>(); + static Map cosmeticIdNames = new HashMap<>(); + + static Map, Set> missingCosmeticsNpcTypes = new HashMap<>(); + + private static String getName(String token) { + int i, j; + char[] dest; + String d; + + i = token.lastIndexOf("name"); + i = token.indexOf("\"", i) + 1; //lower bound of the string + j = token.indexOf("\"", i); //upper bound + + dest = new char[initialStringLength]; + token.getChars(i, j, dest, 0); + + d = new String(dest); + return(d.trim()); + } + + private static String getValue(String token) { + int i, j; + char[] dest; + String d; + + i = token.lastIndexOf("value"); + i = token.indexOf("\"", i) + 1; //lower bound of the string + j = token.indexOf("\"", i); //upper bound + + dest = new char[initialStringLength]; + token.getChars(i, j, dest, 0); + + d = new String(dest); + return(d.trim()); + } + + private static void forwardCursor(int st) { + String line = null; + + try { + while(status >= st && (line = bufferedReader.readLine()) != null) { + simpleToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void simpleToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + status += 1; + } + } + + private static void translateToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + status += 1; + + if (status == 3) { + String d = getName(token); + + if (!(d.contentEquals("Face") || d.contentEquals("Hair"))) { + forwardCursor(status); + } + } else if (status == 4) { + String d = getName(token); + int itemid = Integer.valueOf(d); + + int cosmeticid; + if (itemid >= 30000) { + cosmeticid = (itemid / 10) * 10; + } else { + cosmeticid = itemid - ((itemid / 100) % 10) * 100; + } + + allCosmetics.add(cosmeticid); + forwardCursor(status); + } + } + } + + private static void readEqpStringData(String eqpStringDirectory) throws IOException { + String line; + + fileReader = new InputStreamReader(new FileInputStream(eqpStringDirectory), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + while((line = bufferedReader.readLine()) != null) { + translateToken(line); + } + + bufferedReader.close(); + fileReader.close(); + } + + private static void loadCosmeticWzData() throws IOException { + System.out.println("Reading String.wz ..."); + readEqpStringData(wzPath + "/String.wz/Eqp.img.xml"); + } + + private static void setCosmeticUsage(List usedByNpcids, int cosmeticid) { + if (!usedByNpcids.isEmpty()) { + usedCosmetics.put(cosmeticid, usedByNpcids); + } else { + unusedCosmetics.add(cosmeticid); + } + } + + private static void listFiles(String directoryName, ArrayList files) { + File directory = new File(directoryName); + + // get all the files from a directory + File[] fList = directory.listFiles(); + for (File file : fList) { + if (file.isFile()) { + files.add(file); + } else if (file.isDirectory()) { + listFiles(file.getAbsolutePath(), files); + } + } + } + + private static int getNpcIdFromFilename(String name) { + try { + return Integer.valueOf(name.substring(0, name.indexOf('.'))); + } catch(Exception e) { + return -1; + } + } + + private static List findCosmeticDataNpcids(int itemid) { + List npcids = new LinkedList<>(); + for (Entry> sc : scriptCosmetics.entrySet()) { + if (sc.getValue().contains(itemid)) { + npcids.add(itemid); + } + } + + return npcids; + } + + private static void loadScripts() throws IOException { + ArrayList files = new ArrayList<>(); + listFiles(scriptPath + "/npc", files); + + for(File f : files) { + Integer npcid = getNpcIdFromFilename(f.getName()); + + //System.out.println("Parsing " + f.getAbsolutePath()); + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + String line; + + StringBuilder stringBuffer = new StringBuilder(); + + boolean cosmeticNpc = false; + Set cosmeticids = new HashSet<>(); + while((line = bufferedReader.readLine())!=null){ + String[] s = line.split("hair_. = Array\\(", 2); + + if (s.length > 1) { + cosmeticNpc = true; + s = s[1].split("\\)", 2); + s = s[0].split(", "); + + for (String st : s) { + if (!st.isEmpty()) { + int itemid = Integer.valueOf(st); + cosmeticids.add(itemid); + } + } + } else { + s = line.split("face_. = Array\\(", 2); + + if (s.length > 1) { + cosmeticNpc = true; + s = s[1].split("\\)", 2); + s = s[0].split(", "); + + for (String st : s) { + if (!st.isEmpty()) { + int itemid = Integer.valueOf(st); + cosmeticids.add(itemid); + } + } + } + } + + stringBuffer.append(line).append("\n"); + } + + scriptEntries.put(npcid, stringBuffer.toString()); + + if (cosmeticNpc) { + scriptCosmetics.put(npcid, cosmeticids); + } + + bufferedReader.close(); + fileReader.close(); + } + } + + private static void processCosmeticScriptData() throws IOException { + System.out.println("Reading script files ..."); + loadScripts(); + + if (ignoreCurrentScriptCosmetics) { + for (Set npcCosmetics : scriptCosmetics.values()) { + npcCosmetics.clear(); + } + } + + for (Integer itemid : allCosmetics) { + List npcids = findCosmeticDataNpcids(itemid); + setCosmeticUsage(npcids, itemid); + } + } + + private static List loadCosmeticCouponids() throws IOException { + List couponItemids = new LinkedList<>(); + + fileReader = new InputStreamReader(new FileInputStream(handbookPath + "/Cash.txt"), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + String line; + while((line = bufferedReader.readLine())!=null){ + if (line.isEmpty()) continue; + String[] s = line.split(" - ", 3); + + int itemid = Integer.valueOf(s[0]); + if (itemid >= 5150000 && itemid < 5160000) { + couponItemids.add(itemid); + couponNames.put(itemid, s[1]); + } + } + + bufferedReader.close(); + fileReader.close(); + + return couponItemids; + } + + private static List findItemidOnScript(int itemid) { + List files = new LinkedList<>(); + String t = String.valueOf(itemid); + + for (Entry text : scriptEntries.entrySet()) { + if (text.getValue().contains(t)) { + files.add(text.getKey()); + } + } + + return files; + } + + private static void loadCosmeticCouponNpcs() throws IOException { + System.out.println("Locating cosmetic NPCs ..."); + + for (Integer itemid : loadCosmeticCouponids()) { + List npcids = findItemidOnScript(itemid); + + if (!npcids.isEmpty()) { + cosmeticNpcs.put(itemid, npcids.get(0)); + } + } + } + + private enum CosmeticType { + HAIRSTYLE, + HAIRCOLOR, + DIRTYHAIR, + FACE_SURGERY, + EYE_COLOR, + SKIN_CARE + } + + private static Pair parseCosmeticCoupon(String[] tokens) { + for (int i = 0; i < tokens.length; i++) { + String s = tokens[i]; + + if (s.startsWith("Hair")) { + if (s.contentEquals("Hairstyle")) { + return new Pair<>(i, CosmeticType.HAIRSTYLE); + } else { + if (i - 1 >= 0 && tokens[i - 1].contentEquals("Dirty")) { + return new Pair<>(i - 1, CosmeticType.DIRTYHAIR); + } else if (i + 1 < tokens.length && tokens[i + 1].contentEquals("Color")) { + return new Pair<>(i, CosmeticType.HAIRCOLOR); + } else { + return new Pair<>(i, CosmeticType.HAIRSTYLE); + } + } + } else if (s.startsWith("Face")) { + return new Pair<>(i, CosmeticType.FACE_SURGERY); + } else if (s.startsWith("Cosmetic")) { + return new Pair<>(i, CosmeticType.EYE_COLOR); + } else if (s.startsWith("Plastic")) { + return new Pair<>(i, CosmeticType.FACE_SURGERY); + } else if (s.startsWith("Skin")) { + return new Pair<>(i, CosmeticType.SKIN_CARE); + } + } + + return null; + } + + private static List getCosmeticCouponData(String town, String type, String subtype) { + List ret = new ArrayList<>(3); + ret.add(town); + ret.add(type); + ret.add(subtype); + return ret; + } + + private static List parseCosmeticCoupon(String couponName) { + String town, type, subtype = "EXP"; + + String[] s = couponName.split(" Coupon ", 2); + + if (s.length > 1) { + subtype = s[1].substring(1, s[1].length() - 1); + } + + String[] tokens = s[0].split(" "); + Pair cosmeticData = parseCosmeticCoupon(tokens); + if (cosmeticData == null) return null; + + town = ""; + for (int i = 0; i < cosmeticData.left; i++) { + town += (tokens[i] + "_"); + } + town = town.substring(0, town.length() - 1).toLowerCase(); + + switch (cosmeticData.right) { + case HAIRSTYLE: + type = "hair"; + break; + + case FACE_SURGERY: + type = "face"; + break; + + default: + return null; + } + + return getCosmeticCouponData(town, type, subtype); + } + + private static void generateCosmeticPlaceNpcs() { + for (Entry e : couponNames.entrySet()) { + Integer npcid = cosmeticNpcs.get(e.getKey()); + if (npcid == null) continue; + + String couponName = e.getValue(); + List couponData = parseCosmeticCoupon(couponName); + + if (couponData == null) continue; + cosmeticNpcids.put(couponData, npcid); + } + } + + private static Integer getCosmeticNpcid(String townName, String typeCosmetic, String typeCoupon) { + return cosmeticNpcids.get(getCosmeticCouponData(townName, typeCosmetic, typeCoupon)); + } + + private static String getCosmeticName(String name, boolean gender) { + String ret = name + " (" + (gender ? "F" : "M") + ")"; + return ret; + } + + private static void loadCosmeticNames(String cosmeticPath) throws IOException { + fileReader = new InputStreamReader(new FileInputStream(cosmeticPath), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + String line; + while((line = bufferedReader.readLine()) != null) { + String[] s = line.split(" - ", 3); + int itemid = Integer.valueOf(s[0]); + + String name; + if (itemid < 30000) { + itemid = itemid - ((itemid / 100) % 10) * 100; + + int idx = s[1].lastIndexOf(" "); + if (idx > -1) { + name = s[1].substring(0, idx); + } else { + name = s[1]; + } + } else { + itemid = (Integer.valueOf(s[0]) / 10) * 10; + + int idx = s[1].indexOf(" "); + if (idx > -1) { + name = s[1].substring(idx + 1); + } else { + name = s[1]; + } + } + + name = name.trim(); + + String cname = getCosmeticName(name, (((itemid / 1000) % 10) % 3) != 0); + + /* + if (cosmeticNameIds.containsKey(cname) && Math.abs(cosmeticNameIds.get(cname) - itemid) > 50) { + System.out.println("Clashing '" + name + "' " + itemid + "/" + cosmeticNameIds.get(cname)); + } + */ + + cosmeticNameIds.put(cname, itemid); + cosmeticIdNames.put(itemid, name); + } + + bufferedReader.close(); + fileReader.close(); + } + + private static void loadCosmeticNames() throws IOException { + System.out.println("Reading cosmetics from handbook ..."); + + loadCosmeticNames(handbookPath + "/Equip/Face.txt"); + loadCosmeticNames(handbookPath + "/Equip/Hair.txt"); + } + + private static List fetchExpectedCosmetics(String[] cosmeticList, boolean gender) { + List list = new LinkedList<>(); + + for (String cosmetic : cosmeticList) { + String cname = getCosmeticName(cosmetic, gender); + Integer itemid = cosmeticNameIds.get(cname); + if (itemid != null) { + list.add(itemid); + } else { + missingCosmeticNames.add(cosmetic); + } + } + + return list; + } + + private static void verifyCosmeticExpectedFile(File f) throws IOException { + String townName = f.getParent().substring(f.getParent().lastIndexOf("\\") + 1); + String typeCosmetic = f.getName().substring(0, f.getName().indexOf(".")); + + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + String line; + while((line = bufferedReader.readLine())!=null){ + String[] s = line.split(": ", 2); + String[] t = s[0].split("ale "); + + String typeCoupon = t[1]; + boolean gender = !t[0].contentEquals("M"); + + Integer npcid = getCosmeticNpcid(townName, typeCosmetic, typeCoupon); + if (npcid != null) { + String[] cosmetics = s[1].split(", "); + List cosmeticItemids = fetchExpectedCosmetics(cosmetics, gender); + + Set npcCosmetics = scriptCosmetics.get(npcid); + Set missingCosmetics = new HashSet<>(); + for (Integer itemid : cosmeticItemids) { + if (!npcCosmetics.contains(itemid)) { + missingCosmetics.add(itemid); + } + } + + if (!missingCosmetics.isEmpty()) { + Pair key = new Pair<>(npcid, typeCoupon); + + Set list = missingCosmeticsNpcTypes.get(key); + if (list == null) { + missingCosmeticsNpcTypes.put(key, missingCosmetics); + } else { + list.addAll(missingCosmetics); + } + } + } + } + + bufferedReader.close(); + fileReader.close(); + } + + private static void verifyCosmeticExpectedData() throws IOException { + System.out.println("Analyzing cosmetic NPC scripts ..."); + + ArrayList cosmeticRecipes = new ArrayList<>(); + listFiles(libPath + "/care", cosmeticRecipes); + + for (File f : cosmeticRecipes) { + verifyCosmeticExpectedFile(f); + } + } + + private static List, List>> getSortedMapEntries(Map, Set> map) { + List, List>> list = new ArrayList<>(map.size()); + for(Entry, Set> e : map.entrySet()) { + List il = new ArrayList<>(2); + for(Integer i : e.getValue()) { + il.add(i); + } + + Collections.sort(il, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o1 - o2; + } + }); + + list.add(new Pair<>(e.getKey(), il)); + } + + Collections.sort(list, new Comparator, List>>() { + @Override + public int compare(Pair, List> o1, Pair, List> o2) { + int cmp = o1.getLeft().getLeft() - o2.getLeft().getLeft(); + if (cmp == 0) { + return o1.getLeft().getRight().compareTo(o2.getLeft().getRight()); + } else { + return cmp; + } + } + }); + + return list; + } + + private static void printReportFileHeader() { + printWriter.println(" # Report File autogenerated from the MapleCashCosmeticsChecker feature by Ronan Lana."); + printWriter.println(" # Generated data takes into account several data info from the server source files and the server-side WZ.xmls."); + printWriter.println(); + } + + private static Pair, List> getCosmeticReport(List itemids) { + List maleItemids = new LinkedList<>(); + List femaleItemids = new LinkedList<>(); + + for (Integer i : itemids) { + if ((((i / 1000) % 10) % 3) == 0) { + maleItemids.add(i); + } else { + femaleItemids.add(i); + } + } + + return new Pair<>(maleItemids, femaleItemids); + } + + private static void reportNpcCosmetics(List itemids) { + if (!itemids.isEmpty()) { + String res = " "; + for (Integer i : itemids) { + res += (i + ", "); + unusedCosmetics.remove(i); + } + + printWriter.println(res.substring(0, res.length() - 2)); + } + } + + private static void reportCosmeticResults() throws IOException { + System.out.println("Reporting results ..."); + + printWriter = new PrintWriter("lib/result.txt", "UTF-8"); + + printReportFileHeader(); + + if (!missingCosmeticsNpcTypes.isEmpty()) { + printWriter.println("Found " + missingCosmeticsNpcTypes.size() + " entries with missing cosmetic entries."); + + for (Pair, List> mcn : getSortedMapEntries(missingCosmeticsNpcTypes)) { + printWriter.println(" NPC " + mcn.getLeft()); + + Pair, List> genderItemids = getCosmeticReport(mcn.getRight()); + reportNpcCosmetics(genderItemids.getLeft()); + reportNpcCosmetics(genderItemids.getRight()); + printWriter.println(); + } + } + + if (!unusedCosmetics.isEmpty()) { + printWriter.println("Unused cosmetics: " + unusedCosmetics.size()); + + List list = new ArrayList<>(unusedCosmetics); + Collections.sort(list); + + for (Integer i : list) { + printWriter.println(i + " " + cosmeticIdNames.get(i)); + } + + printWriter.println(); + } + + if (!missingCosmeticNames.isEmpty()) { + printWriter.println("Missing cosmetic itemids: " + missingCosmeticNames.size()); + + List listString = new ArrayList<>(missingCosmeticNames); + Collections.sort(listString); + + for (String c : listString) { + printWriter.println(c); + } + + printWriter.println(); + } + + printWriter.close(); + } + + public static void main(String[] args) { + try { + loadCosmeticWzData(); + processCosmeticScriptData(); + + loadCosmeticCouponNpcs(); + generateCosmeticPlaceNpcs(); + + loadCosmeticNames(); + verifyCosmeticExpectedData(); + + reportCosmeticResults(); + System.out.println("Done!"); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/Pair.java b/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/Pair.java new file mode 100644 index 0000000000..1cdc70eece --- /dev/null +++ b/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/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 maplecashcosmeticschecker; + +/** + * 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/MapleCashCosmeticsFetcher/build.xml b/tools/MapleCashCosmeticsFetcher/build.xml new file mode 100644 index 0000000000..bcf28617e6 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project MapleCashCosmeticsFetcher. + + + diff --git a/tools/MapleCashCosmeticsFetcher/manifest.mf b/tools/MapleCashCosmeticsFetcher/manifest.mf new file mode 100644 index 0000000000..328e8e5bc3 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/MapleCashCosmeticsFetcher/src/maplecashcosmeticsfetcher/MapleCashCosmeticsFetcher.java b/tools/MapleCashCosmeticsFetcher/src/maplecashcosmeticsfetcher/MapleCashCosmeticsFetcher.java new file mode 100644 index 0000000000..484034a1e7 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/src/maplecashcosmeticsfetcher/MapleCashCosmeticsFetcher.java @@ -0,0 +1,145 @@ +/* + 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 maplecashcosmeticsfetcher; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.ArrayList; + +import tools.MapleItemInformationProvider; + +/** + * + * @author RonanLana + + This application gathers info from the WZ.XML files, fetching all cosmetic coupons and tickets from there, and then + searches the NPC script files, identifying the stylish NPCs that supposedly uses them. It will reports all NPCs that + uses up a card, as well as report those currently unused. + + Estimated parse time: 10 seconds + + */ +public class MapleCashCosmeticsFetcher { + static MapleItemInformationProvider ii; + + static String wzPath = "../../wz"; + static String scriptPath = "../../scripts"; + static InputStreamReader fileReader = null; + static BufferedReader bufferedReader = null; + + static Map scriptEntries = new HashMap<>(500); + + private static void listFiles(String directoryName, ArrayList files) { + File directory = new File(directoryName); + + // get all the files from a directory + File[] fList = directory.listFiles(); + for (File file : fList) { + if (file.isFile()) { + files.add(file); + } else if (file.isDirectory()) { + listFiles(file.getAbsolutePath(), files); + } + } + } + + private static int getNpcIdFromFilename(String name) { + try { + return Integer.valueOf(name.substring(0, name.indexOf('.'))); + } catch(Exception e) { + return -1; + } + } + + private static void loadScripts() throws Exception { + ArrayList files = new ArrayList<>(); + listFiles(scriptPath + "/npc", files); + + for(File f : files) { + Integer npcid = getNpcIdFromFilename(f.getName()); + + //System.out.println("Parsing " + f.getAbsolutePath()); + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + StringBuilder stringBuffer = new StringBuilder(); + String line; + + while((line = bufferedReader.readLine())!=null){ + stringBuffer.append(line).append("\n"); + } + + scriptEntries.put(npcid, stringBuffer.toString()); + + bufferedReader.close(); + fileReader.close(); + } + } + + private static List findItemidOnScript(int itemid) { + List files = new LinkedList<>(); + String t = String.valueOf(itemid); + + for (Entry text : scriptEntries.entrySet()) { + if (text.getValue().contains(t)) { + files.add(text.getKey()); + } + } + + return files; + } + + private static void reportCosmeticCouponResults() { + for (int itemid = 5150000; itemid <= 5154000; itemid++) { + String itemName = ii.getName(itemid); + + if (itemName != null) { + List npcids = findItemidOnScript(itemid); + + if (!npcids.isEmpty()) { + System.out.println("Itemid " + itemid + " found on " + npcids + ". (" + itemName + ")"); + } else { + System.out.println("NOT FOUND ITEMID " + itemid + " (" + itemName + ")"); + } + } + } + } + + public static void main(String[] args) { + System.setProperty("wzpath", wzPath); + ii = MapleItemInformationProvider.getInstance(); + + try { + loadScripts(); + System.out.println("Loaded scripts"); + + reportCosmeticCouponResults(); + } catch(Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/tools/MapleCashCosmeticsFetcher/src/provider/MapleCanvas.java b/tools/MapleCashCosmeticsFetcher/src/provider/MapleCanvas.java new file mode 100644 index 0000000000..10ab682196 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/MapleData.java b/tools/MapleCashCosmeticsFetcher/src/provider/MapleData.java new file mode 100644 index 0000000000..4d90a93804 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/MapleDataDirectoryEntry.java b/tools/MapleCashCosmeticsFetcher/src/provider/MapleDataDirectoryEntry.java new file mode 100644 index 0000000000..cb043e0c94 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/MapleDataEntity.java b/tools/MapleCashCosmeticsFetcher/src/provider/MapleDataEntity.java new file mode 100644 index 0000000000..03ff77649c --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/MapleDataEntry.java b/tools/MapleCashCosmeticsFetcher/src/provider/MapleDataEntry.java new file mode 100644 index 0000000000..62db6d0abe --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/MapleDataFileEntry.java b/tools/MapleCashCosmeticsFetcher/src/provider/MapleDataFileEntry.java new file mode 100644 index 0000000000..902130a612 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/MapleDataProvider.java b/tools/MapleCashCosmeticsFetcher/src/provider/MapleDataProvider.java new file mode 100644 index 0000000000..5237b7ac37 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/MapleDataProviderFactory.java b/tools/MapleCashCosmeticsFetcher/src/provider/MapleDataProviderFactory.java new file mode 100644 index 0000000000..14753d4406 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/MapleDataTool.java b/tools/MapleCashCosmeticsFetcher/src/provider/MapleDataTool.java new file mode 100644 index 0000000000..25f4c7f817 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/FileStoredPngMapleCanvas.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/FileStoredPngMapleCanvas.java new file mode 100644 index 0000000000..21736c2c16 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/ImgMapleSound.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/ImgMapleSound.java new file mode 100644 index 0000000000..8add2ccb36 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/ListWZFile.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/ListWZFile.java new file mode 100644 index 0000000000..1672a08c59 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/MapleDataType.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/MapleDataType.java new file mode 100644 index 0000000000..e074d57d14 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/PNGMapleCanvas.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/PNGMapleCanvas.java new file mode 100644 index 0000000000..97c2303804 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/WZDirectoryEntry.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/WZDirectoryEntry.java new file mode 100644 index 0000000000..d24b8cb2b9 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/WZEntry.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/WZEntry.java new file mode 100644 index 0000000000..1e921b2082 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/WZFile.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/WZFile.java new file mode 100644 index 0000000000..c6c0abf537 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/WZFileEntry.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/WZFileEntry.java new file mode 100644 index 0000000000..792371d9cf --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/WZIMGEntry.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/WZIMGEntry.java new file mode 100644 index 0000000000..385d785183 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/WZIMGFile.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/WZIMGFile.java new file mode 100644 index 0000000000..bec06c78bd --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/WZTool.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/WZTool.java new file mode 100644 index 0000000000..85e1c8d90b --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/XMLDomMapleData.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/XMLDomMapleData.java new file mode 100644 index 0000000000..151a04c2fd --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/provider/wz/XMLWZFile.java b/tools/MapleCashCosmeticsFetcher/src/provider/wz/XMLWZFile.java new file mode 100644 index 0000000000..2a7694fdc9 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/ArrayMap.java b/tools/MapleCashCosmeticsFetcher/src/tools/ArrayMap.java new file mode 100644 index 0000000000..c08508f7e3 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/src/tools/ArrayMap.java @@ -0,0 +1,149 @@ +/* + 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.util.AbstractMap; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +public class ArrayMap extends AbstractMap { + + static class Entry implements Map.Entry { + protected K key; + protected V value; + + public Entry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public V setValue(V newValue) { + V oldValue = value; + value = newValue; + return oldValue; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) { + return false; + } + Map.Entry e = (Map.Entry) o; + return (key == null ? e.getKey() == null : key.equals(e.getKey())) && (value == null ? e.getValue() == null : value.equals(e.getValue())); + } + + @Override + public int hashCode() { + int keyHash = (key == null ? 0 : key.hashCode()); + int valueHash = (value == null ? 0 : value.hashCode()); + return keyHash ^ valueHash; + } + + @Override + public String toString() { + return key + "=" + value; + } + } + private Set> entries = null; + private ArrayList> list; + + public ArrayMap() { + list = new ArrayList<>(); + } + + public ArrayMap(Map map) { + list = new ArrayList<>(); + putAll(map); + } + + public ArrayMap(int initialCapacity) { + list = new ArrayList<>(initialCapacity); + } + + @Override + @SuppressWarnings ("unchecked") + public Set> entrySet() { + if (entries == null) { + entries = new AbstractSet>() { + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Iterator> iterator() { + return list.iterator(); + } + + @Override + public int size() { + return list.size(); + } + }; + } + return (Set>) entries; + } + + @Override + public V put(K key, V value) { + int size = list.size(); + Entry entry = null; + int i; + if (key == null) { + for (i = 0; i < size; i++) { + entry = (list.get(i)); + if (entry.getKey() == null) { + break; + } + } + } else { + for (i = 0; i < size; i++) { + entry = (list.get(i)); + if (key.equals(entry.getKey())) { + break; + } + } + } + V oldValue = null; + if (i < size) { + oldValue = entry.getValue(); + entry.setValue(value); + } else { + list.add(new Entry<>(key, value)); + } + return oldValue; + } +} diff --git a/tools/MapleCashCosmeticsFetcher/src/tools/DatabaseConnection.java b/tools/MapleCashCosmeticsFetcher/src/tools/DatabaseConnection.java new file mode 100644 index 0000000000..9dcd4e6545 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/FilePrinter.java b/tools/MapleCashCosmeticsFetcher/src/tools/FilePrinter.java new file mode 100644 index 0000000000..340129765c --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/src/tools/FilePrinter.java @@ -0,0 +1,188 @@ +package tools; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.Calendar; + +public class FilePrinter { + + public static final String + ACCOUNT_STUCK = "accountStuck.txt", + EXCEPTION_CAUGHT = "exceptionCaught.txt", + CLIENT_START = "clientStartError.txt", + ADD_PLAYER = "addPlayer.txt", + MAPLE_MAP = "mapleMap.txt", + ERROR38 = "error38.txt", + PACKET_LOG = "log.txt", + EXCEPTION = "exceptions.txt", + SQL_EXCEPTION = "sqlexceptions.txt", + PACKET_HANDLER = "PacketHandler/", + PORTAL = "portals/", + NPC = "npcs/", + INVOCABLE = "invocable/", + REACTOR = "reactors/", + QUEST = "quests/", + ITEM = "items/", + MOB_MOVEMENT = "mobmovement.txt", + MAP_SCRIPT = "mapscript/", + DIRECTION = "directions/", + SAVE_CHAR = "saveToDB.txt", + INSERT_CHAR = "insertCharacter.txt", + LOAD_CHAR = "loadCharFromDB.txt", + UNHANDLED_EVENT = "doesNotExist.txt", + SESSION = "sessions.txt", + EXPLOITS = "exploits/", + STORAGE = "storage/", + PACKET_LOGS = "packetlogs/", + DELETED_CHARACTERS = "deletedchars/", + FREDRICK = "fredrick/", + NPC_UNCODED = "uncodedNPCs.txt", + QUEST_UNCODED = "uncodedQuests.txt", + AUTOSAVING_CHARACTER = "saveCharAuto.txt", + SAVING_CHARACTER = "saveChar.txt", + USED_COMMANDS = "usedCommands.txt";//more to come (maps) + + private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); //for file system purposes, it's nice to use yyyy-MM-dd + private static final String FILE_PATH = "logs/" + sdf.format(Calendar.getInstance().getTime()) + "/"; // + sdf.format(Calendar.getInstance().getTime()) + "/" + private static final String ERROR = "error/"; + + public static void printError(final String name, final Throwable t) { + System.out.println("Error thrown: " + name); + System.out.println(getString(t)); + FileOutputStream out = null; + final String file = FILE_PATH + ERROR + name; + try { + File outputFile = new File(file); + if (outputFile.getParentFile() != null) { + outputFile.getParentFile().mkdirs(); + } + out = new FileOutputStream(file, true); + out.write(getString(t).getBytes()); + out.write("\n---------------------------------\r\n".getBytes()); + } catch (IOException ess) { + ess.printStackTrace(); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException ignore) { + ignore.printStackTrace(); + } + } + } + + public static void printError(final String name, final Throwable t, final String info) { + System.out.println("Error thrown: " + name); + System.out.println(getString(t)); + FileOutputStream out = null; + final String file = FILE_PATH + ERROR + name; + try { + File outputFile = new File(file); + if (outputFile.getParentFile() != null) { + outputFile.getParentFile().mkdirs(); + } + out = new FileOutputStream(file, true); + out.write((info + "\r\n").getBytes()); + out.write(getString(t).getBytes()); + out.write("\n---------------------------------\r\n".getBytes()); + } catch (IOException ess) { + ess.printStackTrace(); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException ignore) { + ignore.printStackTrace(); + } + } + } + + public static void printError(final String name, final String s) { + System.out.println("Error thrown: " + name); + System.out.println(s); + FileOutputStream out = null; + final String file = FILE_PATH + ERROR + name; + try { + File outputFile = new File(file); + if (outputFile.getParentFile() != null) { + outputFile.getParentFile().mkdirs(); + } + out = new FileOutputStream(file, true); + out.write(s.getBytes()); + //out.write("\n---------------------------------\n".getBytes()); + } catch (IOException ess) { + ess.printStackTrace(); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException ignore) { + ignore.printStackTrace(); + } + } + } + + public static void print(final String name, final String s) { + print(name, s, true); + } + + public static void print(final String name, final String s, boolean line) { + System.out.println("Log: " + name); + System.out.println(s); + FileOutputStream out = null; + String file = FILE_PATH + name; + try { + File outputFile = new File(file); + if (outputFile.getParentFile() != null) { + outputFile.getParentFile().mkdirs(); + } + out = new FileOutputStream(file, true); + out.write(s.getBytes()); + out.write("\r\n".getBytes()); + if (line) { + out.write("---------------------------------\r\n".getBytes()); + } + } catch (IOException ess) { + ess.printStackTrace(); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException ignore) { + ignore.printStackTrace(); + } + } + } + + private static String getString(final Throwable e) { + String retValue = null; + StringWriter sw = null; + PrintWriter pw = null; + try { + sw = new StringWriter(); + pw = new PrintWriter(sw); + e.printStackTrace(pw); + retValue = sw.toString(); + } finally { + try { + if (pw != null) { + pw.close(); + } + if (sw != null) { + sw.close(); + } + } catch (IOException ignore) { + ignore.printStackTrace(); + } + } + return retValue; + } +} \ No newline at end of file diff --git a/tools/MapleCashCosmeticsFetcher/src/tools/HexTool.java b/tools/MapleCashCosmeticsFetcher/src/tools/HexTool.java new file mode 100644 index 0000000000..8cc0c8aa84 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/MapleItemInformationProvider.java b/tools/MapleCashCosmeticsFetcher/src/tools/MapleItemInformationProvider.java new file mode 100644 index 0000000000..37addfc389 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/src/tools/MapleItemInformationProvider.java @@ -0,0 +1,664 @@ +/* + 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.File; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +//import net.server.Server; +import provider.MapleData; +import provider.MapleDataDirectoryEntry; +import provider.MapleDataFileEntry; +import provider.MapleDataProvider; +import provider.MapleDataProviderFactory; +import provider.MapleDataTool; +import tools.DatabaseConnection; +//import tools.MaplePacketCreator; +import tools.Pair; +//import client.MapleCharacter; +//import client.MapleClient; +//import client.MapleJob; +//import client.Skill; +//import client.SkillFactory; +//import client.autoban.AutobanFactory; +//import client.inventory.Equip; +//import client.inventory.Item; +//import client.inventory.MapleInventory; +//import client.inventory.MapleInventoryType; +//import client.inventory.MapleWeaponType; +//import constants.ServerConstants; +//import constants.EquipSlot; +//import constants.ItemConstants; +//import constants.skills.Assassin; +//import constants.skills.Gunslinger; +//import constants.skills.NightWalker; +import java.sql.Connection; +//import server.life.MapleMonsterInformationProvider; + +/** + * + * @author Matze + * + */ +public class MapleItemInformationProvider { + + private static MapleItemInformationProvider instance = null; + protected MapleDataProvider itemData; + protected MapleDataProvider equipData; + protected MapleDataProvider stringData; + protected MapleData cashStringData; + protected MapleData consumeStringData; + protected MapleData eqpStringData; + protected MapleData etcStringData; + protected MapleData insStringData; + protected MapleData petStringData; + protected Map slotMaxCache = new HashMap<>(); + protected Map itemEffects = new HashMap<>(); + protected Map> equipStatsCache = new HashMap<>(); + protected Map equipCache = new HashMap<>(); + protected Map priceCache = new HashMap<>(); + protected Map wholePriceCache = new HashMap<>(); + protected Map projectileWatkCache = new HashMap<>(); + protected Map nameCache = new HashMap<>(); + protected Map descCache = new HashMap<>(); + protected Map msgCache = new HashMap<>(); + protected Map dropRestrictionCache = new HashMap<>(); + protected Map pickupRestrictionCache = new HashMap<>(); + protected Map getMesoCache = new HashMap<>(); + protected Map onEquipUntradableCache = new HashMap<>(); + protected Map karmaCache = new HashMap<>(); + protected Map triggerItemCache = new HashMap<>(); + protected Map expCache = new HashMap<>(); + protected Map levelCache = new HashMap<>(); + protected List> itemNameCache = new ArrayList<>(); + protected Map consumeOnPickupCache = new HashMap<>(); + protected Map isQuestItemCache = new HashMap<>(); + protected Map equipmentSlotCache = new HashMap<>(); + protected Map noCancelMouseCache = new HashMap<>(); + + private MapleItemInformationProvider() { + itemData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz")); + equipData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Character.wz")); + stringData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")); + cashStringData = stringData.getData("Cash.img"); + consumeStringData = stringData.getData("Consume.img"); + eqpStringData = stringData.getData("Eqp.img"); + etcStringData = stringData.getData("Etc.img"); + insStringData = stringData.getData("Ins.img"); + petStringData = stringData.getData("Pet.img"); + } + + public static MapleItemInformationProvider getInstance() { + if (instance == null) { + instance = new MapleItemInformationProvider(); + } + return instance; + } + + public List> getAllItems() { + if (!itemNameCache.isEmpty()) { + return itemNameCache; + } + List> itemPairs = new ArrayList<>(); + MapleData itemsData; + itemsData = stringData.getData("Cash.img"); + for (MapleData itemFolder : itemsData.getChildren()) { + itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME"))); + } + itemsData = stringData.getData("Consume.img"); + for (MapleData itemFolder : itemsData.getChildren()) { + itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME"))); + } + itemsData = stringData.getData("Eqp.img").getChildByPath("Eqp"); + for (MapleData eqpType : itemsData.getChildren()) { + for (MapleData itemFolder : eqpType.getChildren()) { + itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME"))); + } + } + itemsData = stringData.getData("Etc.img").getChildByPath("Etc"); + for (MapleData itemFolder : itemsData.getChildren()) { + itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME"))); + } + itemsData = stringData.getData("Ins.img"); + for (MapleData itemFolder : itemsData.getChildren()) { + itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME"))); + } + itemsData = stringData.getData("Pet.img"); + for (MapleData itemFolder : itemsData.getChildren()) { + itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME"))); + } + return itemPairs; + } + + public List> getAllEtcItems() { + if (!itemNameCache.isEmpty()) { + return itemNameCache; + } + + List> itemPairs = new ArrayList<>(); + MapleData itemsData; + + itemsData = stringData.getData("Etc.img").getChildByPath("Etc"); + for (MapleData itemFolder : itemsData.getChildren()) { + itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME"))); + } + return itemPairs; + } + + private MapleData getStringData(int itemId) { + String cat = "null"; + MapleData theData; + if (itemId >= 5010000) { + theData = cashStringData; + } else if (itemId >= 2000000 && itemId < 3000000) { + theData = consumeStringData; + } else if ((itemId >= 1010000 && itemId < 1040000) || (itemId >= 1122000 && itemId < 1123000) || (itemId >= 1132000 && itemId < 1133000) || (itemId >= 1142000 && itemId < 1143000)) { + theData = eqpStringData; + cat = "Eqp/Accessory"; + } else if (itemId >= 1000000 && itemId < 1010000) { + theData = eqpStringData; + cat = "Eqp/Cap"; + } else if (itemId >= 1102000 && itemId < 1103000) { + theData = eqpStringData; + cat = "Eqp/Cape"; + } else if (itemId >= 1040000 && itemId < 1050000) { + theData = eqpStringData; + cat = "Eqp/Coat"; + } else if (itemId >= 20000 && itemId < 22000) { + theData = eqpStringData; + cat = "Eqp/Face"; + } else if (itemId >= 1080000 && itemId < 1090000) { + theData = eqpStringData; + cat = "Eqp/Glove"; + } else if (itemId >= 30000 && itemId < 35000) { + theData = eqpStringData; + cat = "Eqp/Hair"; + } else if (itemId >= 1050000 && itemId < 1060000) { + theData = eqpStringData; + cat = "Eqp/Longcoat"; + } else if (itemId >= 1060000 && itemId < 1070000) { + theData = eqpStringData; + cat = "Eqp/Pants"; + } else if (itemId >= 1802000 && itemId < 1842000) { + theData = eqpStringData; + cat = "Eqp/PetEquip"; + } else if (itemId >= 1112000 && itemId < 1120000) { + theData = eqpStringData; + cat = "Eqp/Ring"; + } else if (itemId >= 1092000 && itemId < 1100000) { + theData = eqpStringData; + cat = "Eqp/Shield"; + } else if (itemId >= 1070000 && itemId < 1080000) { + theData = eqpStringData; + cat = "Eqp/Shoes"; + } else if (itemId >= 1900000 && itemId < 2000000) { + theData = eqpStringData; + cat = "Eqp/Taming"; + } else if (itemId >= 1300000 && itemId < 1800000) { + theData = eqpStringData; + cat = "Eqp/Weapon"; + } else if (itemId >= 4000000 && itemId < 5000000) { + theData = etcStringData; + cat = "Etc"; + } else if (itemId >= 3000000 && itemId < 4000000) { + theData = insStringData; + } else if (itemId / 1000 == 5000) { + theData = petStringData; + } else { + return null; + } + if (cat.equalsIgnoreCase("null")) { + return theData.getChildByPath(String.valueOf(itemId)); + } else { + return theData.getChildByPath(cat + "/" + itemId); + } + } + + public boolean noCancelMouse(int itemId) { + if (noCancelMouseCache.containsKey(itemId)) { + return noCancelMouseCache.get(itemId); + } + + MapleData item = getItemData(itemId); + if (item == null) { + noCancelMouseCache.put(itemId, false); + return false; + } + + boolean blockMouse = MapleDataTool.getIntConvert("info/noCancelMouse", item, 0) == 1; + noCancelMouseCache.put(itemId, blockMouse); + return blockMouse; + } + + private MapleData getItemData(int itemId) { + MapleData ret = null; + String idStr = "0" + String.valueOf(itemId); + MapleDataDirectoryEntry root = itemData.getRoot(); + for (MapleDataDirectoryEntry topDir : root.getSubdirectories()) { + for (MapleDataFileEntry iFile : topDir.getFiles()) { + if (iFile.getName().equals(idStr.substring(0, 4) + ".img")) { + ret = itemData.getData(topDir.getName() + "/" + iFile.getName()); + if (ret == null) { + return null; + } + ret = ret.getChildByPath(idStr); + return ret; + } else if (iFile.getName().equals(idStr.substring(1) + ".img")) { + return itemData.getData(topDir.getName() + "/" + iFile.getName()); + } + } + } + root = equipData.getRoot(); + for (MapleDataDirectoryEntry topDir : root.getSubdirectories()) { + for (MapleDataFileEntry iFile : topDir.getFiles()) { + if (iFile.getName().equals(idStr + ".img")) { + return equipData.getData(topDir.getName() + "/" + iFile.getName()); + } + } + } + return ret; + } + + public List getItemIdsInRange(int minId, int maxId, boolean ignoreCashItem) { + List list = new ArrayList<>(); + + if(ignoreCashItem) { + for(int i = minId; i <= maxId; i++) { + if(getItemData(i) != null && !isCash(i)) { + list.add(i); + } + } + } + else { + for(int i = minId; i <= maxId; i++) { + if(getItemData(i) != null) { + list.add(i); + } + } + } + + + return list; + } + + public short getSlotMax(int itemId) { + Short slotMax = slotMaxCache.get(itemId); + if (slotMax != null) { + return (short)(slotMax); + } + short ret = 0; + MapleData item = getItemData(itemId); + if (item != null) { + MapleData smEntry = item.getChildByPath("info/slotMax"); + if (smEntry == null) { + ret = 100; + } else { + ret = (short) MapleDataTool.getInt(smEntry); + } + } + + slotMaxCache.put(itemId, ret); + return (short)(ret); + } + + public int getMeso(int itemId) { + if (getMesoCache.containsKey(itemId)) { + return getMesoCache.get(itemId); + } + MapleData item = getItemData(itemId); + if (item == null) { + return -1; + } + int pEntry; + MapleData pData = item.getChildByPath("info/meso"); + if (pData == null) { + return -1; + } + pEntry = MapleDataTool.getInt(pData); + getMesoCache.put(itemId, pEntry); + return pEntry; + } + + public int getWholePrice(int itemId) { + if (wholePriceCache.containsKey(itemId)) { + return wholePriceCache.get(itemId); + } + MapleData item = getItemData(itemId); + if (item == null) { + return -1; + } + int pEntry; + MapleData pData = item.getChildByPath("info/price"); + if (pData == null) { + return -1; + } + pEntry = MapleDataTool.getInt(pData); + wholePriceCache.put(itemId, pEntry); + return pEntry; + } + + public double getPrice(int itemId) { + if (priceCache.containsKey(itemId)) { + return priceCache.get(itemId); + } + MapleData item = getItemData(itemId); + if (item == null) { + return -1; + } + double pEntry; + MapleData pData = item.getChildByPath("info/unitPrice"); + if (pData != null) { + try { + pEntry = MapleDataTool.getDouble(pData); + } catch (Exception e) { + pEntry = (double) MapleDataTool.getInt(pData); + } + } else { + pData = item.getChildByPath("info/price"); + if (pData == null) { + return -1; + } + try { + pEntry = (double) MapleDataTool.getInt(pData); + } catch(Exception e) { + priceCache.put(itemId, 0.0); + return 0; + } + } + priceCache.put(itemId, pEntry); + return pEntry; + } + + protected String getEquipmentSlot(int itemId) { + if (equipmentSlotCache.containsKey(itemId)) { + return equipmentSlotCache.get(itemId); + } + + String ret = ""; + + MapleData item = getItemData(itemId); + + if (item == null) { + return null; + } + + MapleData info = item.getChildByPath("info"); + + if (info == null) { + return null; + } + + ret = MapleDataTool.getString("islot", info, ""); + + equipmentSlotCache.put(itemId, ret); + + return ret; + } + + public Map getEquipStats(int itemId) { + if (equipStatsCache.containsKey(itemId)) { + return equipStatsCache.get(itemId); + } + Map ret = new LinkedHashMap<>(); + MapleData item = getItemData(itemId); + if (item == null) { + return null; + } + MapleData info = item.getChildByPath("info"); + if (info == null) { + return null; + } + for (MapleData data : info.getChildren()) { + if (data.getName().startsWith("inc")) { + ret.put(data.getName().substring(3), MapleDataTool.getIntConvert(data)); + } + /*else if (data.getName().startsWith("req")) + ret.put(data.getName(), MapleDataTool.getInt(data.getName(), info, 0));*/ + } + ret.put("reqJob", MapleDataTool.getInt("reqJob", info, 0)); + ret.put("reqLevel", MapleDataTool.getInt("reqLevel", info, 0)); + ret.put("reqDEX", MapleDataTool.getInt("reqDEX", info, 0)); + ret.put("reqSTR", MapleDataTool.getInt("reqSTR", info, 0)); + ret.put("reqINT", MapleDataTool.getInt("reqINT", info, 0)); + ret.put("reqLUK", MapleDataTool.getInt("reqLUK", info, 0)); + ret.put("reqPOP", MapleDataTool.getInt("reqPOP", info, 0)); + ret.put("cash", MapleDataTool.getInt("cash", info, 0)); + ret.put("tuc", MapleDataTool.getInt("tuc", info, 0)); + ret.put("cursed", MapleDataTool.getInt("cursed", info, 0)); + ret.put("success", MapleDataTool.getInt("success", info, 0)); + ret.put("fs", MapleDataTool.getInt("fs", info, 0)); + equipStatsCache.put(itemId, ret); + return ret; + } + + public List getScrollReqs(int itemId) { + List ret = new ArrayList<>(); + MapleData data = getItemData(itemId); + data = data.getChildByPath("req"); + if (data == null) { + return ret; + } + for (MapleData req : data.getChildren()) { + ret.add(MapleDataTool.getInt(req)); + } + return ret; + } + + public String getName(int itemId) { + if (nameCache.containsKey(itemId)) { + return nameCache.get(itemId); + } + MapleData strings = getStringData(itemId); + if (strings == null) { + return null; + } + String ret = MapleDataTool.getString("name", strings, null); + nameCache.put(itemId, ret); + return ret; + } + + public String getMsg(int itemId) { + if (msgCache.containsKey(itemId)) { + return msgCache.get(itemId); + } + MapleData strings = getStringData(itemId); + if (strings == null) { + return null; + } + String ret = MapleDataTool.getString("msg", strings, null); + msgCache.put(itemId, ret); + return ret; + } + + public boolean isDropRestricted(int itemId) { + if (dropRestrictionCache.containsKey(itemId)) { + return dropRestrictionCache.get(itemId); + } + MapleData data = getItemData(itemId); + boolean bRestricted = MapleDataTool.getIntConvert("info/tradeBlock", data, 0) == 1; + if (!bRestricted) { + bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1; + } + if (!bRestricted) { + bRestricted = MapleDataTool.getIntConvert("info/quest", data, 0) == 1; + } + dropRestrictionCache.put(itemId, bRestricted); + return bRestricted; + } + + public boolean isPickupRestricted(int itemId) { + if (pickupRestrictionCache.containsKey(itemId)) { + return pickupRestrictionCache.get(itemId); + } + MapleData data = getItemData(itemId); + boolean bRestricted = MapleDataTool.getIntConvert("info/only", data, 0) == 1; + pickupRestrictionCache.put(itemId, bRestricted); + return bRestricted; + } + + public Map getSkillStats(int itemId, double playerJob) { + Map ret = new LinkedHashMap<>(); + MapleData item = getItemData(itemId); + if (item == null) { + return null; + } + MapleData info = item.getChildByPath("info"); + if (info == null) { + return null; + } + for (MapleData data : info.getChildren()) { + if (data.getName().startsWith("inc")) { + ret.put(data.getName().substring(3), MapleDataTool.getIntConvert(data)); + } + } + ret.put("masterLevel", MapleDataTool.getInt("masterLevel", info, 0)); + ret.put("reqSkillLevel", MapleDataTool.getInt("reqSkillLevel", info, 0)); + ret.put("success", MapleDataTool.getInt("success", info, 0)); + MapleData skill = info.getChildByPath("skill"); + int curskill; + for (int i = 0; i < skill.getChildren().size(); i++) { + curskill = MapleDataTool.getInt(Integer.toString(i), skill, 0); + if (curskill == 0) { + break; + } + if (curskill / 10000 == playerJob) { + ret.put("skillid", curskill); + break; + } + } + if (ret.get("skillid") == null) { + ret.put("skillid", 0); + } + return ret; + } + + public List petsCanConsume(int itemId) { + List ret = new ArrayList<>(); + MapleData data = getItemData(itemId); + int curPetId; + for (int i = 0; i < data.getChildren().size(); i++) { + curPetId = MapleDataTool.getInt("spec/" + Integer.toString(i), data, 0); + if (curPetId == 0) { + break; + } + ret.add(Integer.valueOf(curPetId)); + } + return ret; + } + + public boolean isQuestItem(int itemId) { + if (isQuestItemCache.containsKey(itemId)) { + return isQuestItemCache.get(itemId); + } + MapleData data = getItemData(itemId); + boolean questItem = MapleDataTool.getIntConvert("info/quest", data, 0) == 1; + isQuestItemCache.put(itemId, questItem); + return questItem; + } + + public int getQuestIdFromItem(int itemId) { + MapleData data = getItemData(itemId); + int questItem = MapleDataTool.getIntConvert("info/quest", data, 0); + return questItem; + } + + public boolean isUntradeableOnEquip(int itemId) { + if (onEquipUntradableCache.containsKey(itemId)) { + return onEquipUntradableCache.get(itemId); + } + boolean untradableOnEquip = MapleDataTool.getIntConvert("info/equipTradeBlock", getItemData(itemId), 0) > 0; + onEquipUntradableCache.put(itemId, untradableOnEquip); + return untradableOnEquip; + } + + public boolean isKarmaAble(int itemId) { + if (karmaCache.containsKey(itemId)) { + return karmaCache.get(itemId); + } + boolean bRestricted = MapleDataTool.getIntConvert("info/tradeAvailable", getItemData(itemId), 0) > 0; + karmaCache.put(itemId, bRestricted); + return bRestricted; + } + + public int getStateChangeItem(int itemId) { + if (triggerItemCache.containsKey(itemId)) { + return triggerItemCache.get(itemId); + } else { + int triggerItem = MapleDataTool.getIntConvert("info/stateChangeItem", getItemData(itemId), 0); + triggerItemCache.put(itemId, triggerItem); + return triggerItem; + } + } + + public int getExpById(int itemId) { + if (expCache.containsKey(itemId)) { + return expCache.get(itemId); + } else { + int exp = MapleDataTool.getIntConvert("spec/exp", getItemData(itemId), 0); + expCache.put(itemId, exp); + return exp; + } + } + + public int getMaxLevelById(int itemId) { + if (levelCache.containsKey(itemId)) { + return levelCache.get(itemId); + } else { + int level = MapleDataTool.getIntConvert("info/maxLevel", getItemData(itemId), 256); + levelCache.put(itemId, level); + return level; + } + } + + public boolean isConsumeOnPickup(int itemId) { + if (consumeOnPickupCache.containsKey(itemId)) { + return consumeOnPickupCache.get(itemId); + } + MapleData data = getItemData(itemId); + boolean consume = MapleDataTool.getIntConvert("spec/consumeOnPickup", data, 0) == 1 || MapleDataTool.getIntConvert("specEx/consumeOnPickup", data, 0) == 1; + consumeOnPickupCache.put(itemId, consume); + return consume; + } + + public boolean isCash(int itemId) { + return itemId / 1000000 == 5 || getEquipStats(itemId).get("cash") == 1; + } + + public ArrayList> getItemDataByName(String name) + { + ArrayList> ret = new ArrayList<>(); + for (Pair itemPair : MapleItemInformationProvider.getInstance().getAllItems()) { + if (itemPair.getRight().toLowerCase().contains(name.toLowerCase())) { + ret.add(itemPair); + } + } + return ret; + } +} diff --git a/tools/MapleCashCosmeticsFetcher/src/tools/Pair.java b/tools/MapleCashCosmeticsFetcher/src/tools/Pair.java new file mode 100644 index 0000000000..f88718cbe3 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/StringUtil.java b/tools/MapleCashCosmeticsFetcher/src/tools/StringUtil.java new file mode 100644 index 0000000000..b471e4aef2 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/src/tools/StringUtil.java @@ -0,0 +1,128 @@ +/* + 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; + +public class StringUtil { + /** + * Gets a string padded from the left to length by + * padchar. + * + * @param in The input string to be padded. + * @param padchar The character to pad with. + * @param length The length to pad to. + * @return The padded string. + */ + public static String getLeftPaddedStr(String in, char padchar, int length) { + StringBuilder builder = new StringBuilder(length); + for (int x = in.length(); x < length; x++) { + builder.append(padchar); + } + builder.append(in); + return builder.toString(); + } + + /** + * Gets a string padded from the right to length by + * padchar. + * + * @param in The input string to be padded. + * @param padchar The character to pad with. + * @param length The length to pad to. + * @return The padded string. + */ + public static String getRightPaddedStr(String in, char padchar, int length) { + StringBuilder builder = new StringBuilder(in); + for (int x = in.length(); x < length; x++) { + builder.append(padchar); + } + return builder.toString(); + } + + /** + * Joins an array of strings starting from string start with + * a space. + * + * @param arr The array of strings to join. + * @param start Starting from which string. + * @return The joined strings. + */ + public static String joinStringFrom(String arr[], int start) { + return joinStringFrom(arr, start, " "); + } + + /** + * Joins an array of strings starting from string start with + * sep as a seperator. + * + * @param arr The array of strings to join. + * @param start Starting from which string. + * @return The joined strings. + */ + public static String joinStringFrom(String arr[], int start, String sep) { + StringBuilder builder = new StringBuilder(); + for (int i = start; i < arr.length; i++) { + builder.append(arr[i]); + if (i != arr.length - 1) { + builder.append(sep); + } + } + return builder.toString(); + } + + /** + * Makes an enum name human readable (fixes spaces, capitalization, etc) + * + * @param enumName The name of the enum to neaten up. + * @return The human-readable enum name. + */ + public static String makeEnumHumanReadable(String enumName) { + StringBuilder builder = new StringBuilder(enumName.length() + 1); + String[] words = enumName.split("_"); + for (String word : words) { + if (word.length() <= 2) { + builder.append(word); // assume that it's an abbrevation + } else { + builder.append(word.charAt(0)); + builder.append(word.substring(1).toLowerCase()); + } + builder.append(' '); + } + return builder.substring(0, enumName.length()); + } + + /** + * Counts the number of chr's in str. + * + * @param str The string to check for instances of chr. + * @param chr The character to check for. + * @return The number of times chr occurs in str. + */ + public static int countCharacters(String str, char chr) { + int ret = 0; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) == chr) { + ret++; + } + } + return ret; + } +} \ No newline at end of file diff --git a/tools/MapleCashCosmeticsFetcher/src/tools/data/input/ByteArrayByteStream.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/input/ByteArrayByteStream.java new file mode 100644 index 0000000000..eac7de21ea --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/input/ByteInputStream.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/input/ByteInputStream.java new file mode 100644 index 0000000000..107f71843e --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/input/GenericLittleEndianAccessor.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/input/GenericLittleEndianAccessor.java new file mode 100644 index 0000000000..d08a9b8374 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/input/GenericSeekableLittleEndianAccessor.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/input/GenericSeekableLittleEndianAccessor.java new file mode 100644 index 0000000000..fdd147d796 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/input/InputStreamByteStream.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/input/InputStreamByteStream.java new file mode 100644 index 0000000000..70aef3489f --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/input/LittleEndianAccessor.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/input/LittleEndianAccessor.java new file mode 100644 index 0000000000..f991dbf537 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/input/RandomAccessByteStream.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/input/RandomAccessByteStream.java new file mode 100644 index 0000000000..c0004be17f --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/input/SeekableInputStreamBytestream.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/input/SeekableInputStreamBytestream.java new file mode 100644 index 0000000000..f4922dc876 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/input/SeekableLittleEndianAccessor.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/input/SeekableLittleEndianAccessor.java new file mode 100644 index 0000000000..16b2317f7a --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/output/BAOSByteOutputStream.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/output/BAOSByteOutputStream.java new file mode 100644 index 0000000000..80cbc9301e --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/output/ByteOutputStream.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/output/ByteOutputStream.java new file mode 100644 index 0000000000..0df7ca7753 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/output/GenericLittleEndianWriter.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/output/GenericLittleEndianWriter.java new file mode 100644 index 0000000000..e804fd8000 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/output/LittleEndianWriter.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/output/LittleEndianWriter.java new file mode 100644 index 0000000000..f17bd7c72e --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/MapleCashCosmeticsFetcher/src/tools/data/output/MaplePacketLittleEndianWriter.java b/tools/MapleCashCosmeticsFetcher/src/tools/data/output/MaplePacketLittleEndianWriter.java new file mode 100644 index 0000000000..b02365ec62 --- /dev/null +++ b/tools/MapleCashCosmeticsFetcher/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/tools/MapleEmptyItemWzChecker/build.xml b/tools/MapleEmptyItemWzChecker/build.xml new file mode 100644 index 0000000000..aebb661d7b --- /dev/null +++ b/tools/MapleEmptyItemWzChecker/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project MapleEmptyItemWzChecker. + + + diff --git a/tools/MapleEmptyItemWzChecker/lib/Report.txt b/tools/MapleEmptyItemWzChecker/lib/Report.txt new file mode 100644 index 0000000000..2d4b275314 --- /dev/null +++ b/tools/MapleEmptyItemWzChecker/lib/Report.txt @@ -0,0 +1,149 @@ + # Report File autogenerated from the MapleEmptyItemWzChecker feature by Ronan Lana. + # Generated data takes into account several data info from the server-side WZ.xmls. + +String.wz NAMES with no Item.wz node, 130 entries: + 20816 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Face\ + 20817 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Face\ + 21817 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Face\ + 21820 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Face\ + 1002655 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1002657 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1002658 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1003028 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1003029 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1003030 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1003043 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1022096 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Accessory\ + 1042180 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Coat\ + 1052226 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Longcoat\ + 1060115 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1060138 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1061125 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1061160 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1062036 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1062037 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1072248 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Shoes\ + 1072249 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Shoes\ + 1072418 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Shoes\ + 1072425 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Shoes\ + 1080002 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Glove\ + 1082217 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Glove\ + 1082221 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Glove\ + 1082261 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Glove\ + 1142152 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Accessory\ + 1142155 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Accessory\ + 1302032 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1302069 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1322030 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1322034 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1332058 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1382013 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1452047 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1462020 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1462042 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1472057 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1702113 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 2002012 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2002013 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2002014 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2012004 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2022034 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2022036 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2022046 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2022114 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2070014 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2083000 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2084000 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101016 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101017 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101018 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101019 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101022 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101058 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2210023 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2210024 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240004 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240005 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240006 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240007 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240008 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240009 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240010 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240011 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240012 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240013 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240014 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240015 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2290109 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2390000 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 3010044 ../../wz/String.wz/Ins.img.xml -> Ins.img\ + 3994016 ../../wz/String.wz/Ins.img.xml -> Ins.img\ + 4000275 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4001150 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031294 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031627 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031628 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031629 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031630 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031631 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031632 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031633 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031634 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031635 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031636 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031637 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031638 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031639 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031640 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031641 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031642 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031643 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031644 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031645 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031646 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031647 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031648 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031795 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031867 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4032526 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 5000040 ../../wz/String.wz/Pet.img.xml -> Pet.img\ + 5000043 ../../wz/String.wz/Pet.img.xml -> Pet.img\ + 5000046 ../../wz/String.wz/Pet.img.xml -> Pet.img\ + 5201000 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5201001 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210000 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210001 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210002 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210003 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210004 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210005 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5211001 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5211002 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5211003 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5211047 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5240016 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5240019 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5251004 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5251005 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5251006 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360009 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360010 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360011 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360012 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360013 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360014 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + +Item.wz ITEMS with no String.wz node, 12 entries: + 1942000 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01942000.img.xml -> NOT FOUND + 1942001 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01942001.img.xml -> NOT FOUND + 1942002 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01942002.img.xml -> NOT FOUND + 1952000 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01952000.img.xml -> NOT FOUND + 1952001 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01952001.img.xml -> NOT FOUND + 1952002 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01952002.img.xml -> NOT FOUND + 1962000 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01962000.img.xml -> NOT FOUND + 1962001 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01962001.img.xml -> NOT FOUND + 1962002 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01962002.img.xml -> NOT FOUND + 1972000 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01972000.img.xml -> NOT FOUND + 1972001 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01972001.img.xml -> NOT FOUND + 1972002 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01972002.img.xml -> NOT FOUND + diff --git a/tools/MapleEmptyItemWzChecker/manifest.mf b/tools/MapleEmptyItemWzChecker/manifest.mf new file mode 100644 index 0000000000..328e8e5bc3 --- /dev/null +++ b/tools/MapleEmptyItemWzChecker/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/MapleEmptyItemWzChecker/src/mapleemptyitemwzchecker/MapleEmptyItemWzChecker.java b/tools/MapleEmptyItemWzChecker/src/mapleemptyitemwzchecker/MapleEmptyItemWzChecker.java new file mode 100644 index 0000000000..2c47beb8fa --- /dev/null +++ b/tools/MapleEmptyItemWzChecker/src/mapleemptyitemwzchecker/MapleEmptyItemWzChecker.java @@ -0,0 +1,476 @@ +/* + 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 mapleemptyitemwzchecker; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +/** + * + * @author RonanLana + * + This application has two objectives: it reports in a detailed file all itemids which is + currently missing either a name entry in the String.wz or an item entry in the Item.wz; + And it removes from the String.wz XMLs all entries which misses properties on Item.wz. + */ +public class MapleEmptyItemWzChecker { + + static String newFile = "lib/Report.txt"; + static String outputWzPath = "lib"; + static PrintWriter printWriter = null; + static InputStreamReader fileReader = null; + static BufferedReader bufferedReader = null; + + static String wzPath = "../../wz"; + static String handbookPath = "../../handbook"; + static int initialStringLength = 50; + static int itemFileNameSize = 13; + + static byte status = 0; + static int currentItemid = 0; + static int currentDepth = 0; + static Stack currentPath = new Stack<>(); + static String currentFile; + + static Map stringWzItems = new HashMap<>(); + static Map contentWzItems = new HashMap<>(); + + static Set handbookItems = new HashSet<>(); + static Set nonPropItems; + + private static String getName(String token) { + int i, j; + char[] dest; + String d; + + i = token.lastIndexOf("name"); + i = token.indexOf("\"", i) + 1; //lower bound of the string + j = token.indexOf("\"", i); //upper bound + + dest = new char[initialStringLength]; + token.getChars(i, j, dest, 0); + + d = new String(dest); + return(d.trim()); + } + + private static void forwardCursor(int st) { + String line = null; + + try { + while(status >= st && (line = bufferedReader.readLine()) != null) { + simpleToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void simpleToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + status += 1; + } + } + + private static void listFiles(String directoryName, ArrayList files) { + File directory = new File(directoryName); + + // get all the files from a directory + File[] fList = directory.listFiles(); + for (File file : fList) { + if (file.isFile()) { + files.add(file); + } else if (file.isDirectory()) { + listFiles(file.getAbsolutePath(), files); + } + } + } + + private static int getItemIdFromFilename(String name) { + try { + return Integer.valueOf(name.substring(0, name.indexOf('.'))); + } catch(Exception e) { + return -1; + } + } + + private static void inspectItemWzEntry() { + String line = null; + + try { + while((line = bufferedReader.readLine()) != null) { + translateItemToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static String currentItemPath() { + String s = currentFile + " -> "; + + for (String p : currentPath) { + s += (p + "\\"); + } + + return s; + } + + private static void translateItemToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + + currentPath.pop(); + } + else if(token.contains("imgdir")) { + status += 1; + String d = getName(token); + + if(status == 2) { + currentItemid = Integer.valueOf(d); + contentWzItems.put(currentItemid, currentItemPath()); + + forwardCursor(status); + } else { + currentPath.push(d); + } + } + } + + private static void inspectStringWzEntry() { + String line = null; + + try { + while((line = bufferedReader.readLine()) != null) { + translateStringToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void translateStringToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + currentPath.pop(); + } + else if(token.contains("imgdir")) { + status += 1; + String d = getName(token); + + if(status == currentDepth) { + currentItemid = Integer.valueOf(d); + stringWzItems.put(currentItemid, currentItemPath()); + //if (currentItemid >= 4000000) System.out.println(" " + currentItemid); + + forwardCursor(status); + } else { + currentPath.push(d); + } + } + } + + private static void loadStringWzFile(String filePath, int depth) throws IOException { + fileReader = new InputStreamReader(new FileInputStream(filePath), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + currentFile = filePath; + currentDepth = 2 + depth; + //System.out.println(filePath + " depth " + depth); + inspectStringWzEntry(); + + bufferedReader.close(); + fileReader.close(); + } + + private static void loadStringWz() throws IOException { + System.out.println("Reading String.wz ..."); + String stringWzFiles[][] = {{"Cash", "Consume", "Ins", "Pet"}, {"Etc"}, {"Eqp"}}; + String stringWzPath = wzPath + "/String.wz/"; + + for (int i = 0; i < stringWzFiles.length; i++) { + for (String dirFile : stringWzFiles[i]) { + loadStringWzFile(stringWzPath + dirFile + ".img.xml", i); + } + } + } + + private static void loadItemWz() throws IOException { + System.out.println("Reading Item.wz ..."); + ArrayList files = new ArrayList<>(); + listFiles(wzPath + "/Item.wz", files); + + for(File f : files) { + if (f.getParentFile().getName().contentEquals("Special")) continue; + + //System.out.println("Parsing " + f.getAbsolutePath()); + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + currentFile = f.getCanonicalPath(); + + if(f.getName().length() <= itemFileNameSize) { + inspectItemWzEntry(); + } else { // pet file structure is similar to equips, maybe there are other item-types following this behaviour? + int itemid = getItemIdFromFilename(f.getName()); + if(itemid < 0) { + continue; + } + + currentItemid = itemid; + contentWzItems.put(currentItemid, currentItemPath()); + } + + bufferedReader.close(); + fileReader.close(); + } + } + + private static void loadCharacterWz() throws IOException { + System.out.println("Reading Character.wz ..."); + ArrayList files = new ArrayList<>(); + listFiles(wzPath + "/Character.wz", files); + + for(File f : files) { + if (f.getParentFile().getName().contentEquals("Character.wz")) continue; + + int itemid = getItemIdFromFilename(f.getName()); + if(itemid < 0) { + continue; + } + + currentFile = f.getCanonicalPath(); + currentItemid = itemid; + contentWzItems.put(currentItemid, currentItemPath()); + } + } + + private static void calculateItemNameDiff(Set emptyItemNames, Set emptyNameItems) { + for (Integer i : contentWzItems.keySet()) { + if (!stringWzItems.containsKey(i)) { + emptyNameItems.add(i); + } + } + + for (Integer i : stringWzItems.keySet()) { + if (!contentWzItems.containsKey(i)) { + emptyItemNames.add(i); + } + } + } + + private static void readHandbookItems() throws IOException { + System.out.println("Reading handbook ..."); + String[] handbookPaths = {"Equip", "Cash.txt", "Etc.txt", "Pet.txt", "Setup.txt", "Use.txt"}; + + for (String path : handbookPaths) { + readHandbookPath(handbookPath + "/" + path); + } + } + + private static void readHandbookPath(String filePath) throws IOException { + ArrayList files = new ArrayList<>(); + + File testFile = new File(filePath); + if (testFile.isDirectory()) { + listFiles(filePath, files); + } else { + files.add(testFile); + } + + for (File f : files) { + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + String line = null; + + try { + while((line = bufferedReader.readLine()) != null) { + String[] tokens = line.split(" - "); + + if (tokens[0].length() > 0) { + int itemid = Integer.valueOf(tokens[0]); + handbookItems.add(itemid); + } + } + } + catch(Exception e) { + e.printStackTrace(); + } + + bufferedReader.close(); + fileReader.close(); + } + } + + private static void printReportFileHeader() { + printWriter.println(" # Report File autogenerated from the MapleEmptyItemWzChecker feature by Ronan Lana."); + printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls."); + printWriter.println(); + } + + private static List getSortedItems(Set items) { + List sortedItems = new ArrayList<>(items); + Collections.sort(sortedItems); + + return sortedItems; + } + + private static void printReportFileResults(Set emptyItemNames, Set emptyNameItems) { + if (!emptyItemNames.isEmpty()) { + printWriter.println("String.wz NAMES with no Item.wz node, " + emptyItemNames.size() + " entries:"); + + for(Integer itemid : getSortedItems(emptyItemNames)) { + printWriter.println(" " + itemid + " " + stringWzItems.get(itemid) + (handbookItems.contains(itemid) ? "" : " NOT FOUND")); + } + + printWriter.println(); + } + + if (!emptyNameItems.isEmpty()) { + printWriter.println("Item.wz ITEMS with no String.wz node, " + emptyNameItems.size() + " entries:"); + + for(Integer itemid : getSortedItems(emptyNameItems)) { + printWriter.println(" " + itemid + " " + contentWzItems.get(itemid) + (handbookItems.contains(itemid) ? "" : " NOT FOUND")); + } + + printWriter.println(); + } + } + + private static void reportItemNameDiff(Set emptyItemNames, Set emptyNameItems) throws IOException { + System.out.println("Reporting results..."); + printWriter = new PrintWriter(newFile, "UTF-8"); + + printReportFileHeader(); + printReportFileResults(emptyItemNames, emptyNameItems); + + printWriter.close(); + } + + private static void locateItemStringWzDiff() throws IOException { + Set emptyItemNames = new HashSet<>(), emptyNameItems = new HashSet<>(); + calculateItemNameDiff(emptyItemNames, emptyNameItems); + + reportItemNameDiff(emptyItemNames, emptyNameItems); + nonPropItems = emptyItemNames; + } + + private static void runEmptyItemWzChecker() throws IOException { + readHandbookItems(); + + loadCharacterWz(); + loadItemWz(); + loadStringWz(); + + locateItemStringWzDiff(); + } + + private static void generateStringWzEntry() { + String line = null; + + try { + while((line = bufferedReader.readLine()) != null) { + updateStringToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void updateStringToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + + } + else if(token.contains("imgdir")) { + status += 1; + + if (status == currentDepth && nonPropItems.contains(Integer.valueOf(getName(token)))) { + forwardCursor(status); + return; + } + } + + printWriter.println(token); + } + + private static void generateStringWzFile(String filePath, int depth) throws IOException { + fileReader = new InputStreamReader(new FileInputStream(wzPath + filePath), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + printWriter = new PrintWriter(outputWzPath + filePath, "UTF-8"); + currentDepth = 2 + depth; + + //System.out.println(filePath + " depth " + depth); + generateStringWzEntry(); + + printWriter.close(); + bufferedReader.close(); + fileReader.close(); + } + + private static void generateStringWz() throws IOException { + System.out.println("Generating clean String.wz ..."); + String stringWzFiles[][] = {{"Cash", "Consume", "Ins", "Pet"}, {"Etc"}, {"Eqp"}}; + String stringWzPath = "/String.wz/"; + + File folder = new File(outputWzPath + "/String.wz/"); + if (!folder.exists()) { + folder.mkdirs(); + } + + for (int i = 0; i < stringWzFiles.length; i++) { + for (String dirFile : stringWzFiles[i]) { + generateStringWzFile(stringWzPath + dirFile + ".img.xml", i); + } + } + } + + public static void main(String[] args) { + try { + runEmptyItemWzChecker(); + generateStringWz(); + + System.out.println("Done!"); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + +} diff --git a/wz/Character.wz/Face/00020816.img.xml b/wz/Character.wz/Face/00020816.img.xml new file mode 100644 index 0000000000..d56874900c --- /dev/null +++ b/wz/Character.wz/Face/00020816.img.xml @@ -0,0 +1,432 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Character.wz/Face/00020817.img.xml b/wz/Character.wz/Face/00020817.img.xml new file mode 100644 index 0000000000..6c604e6e58 --- /dev/null +++ b/wz/Character.wz/Face/00020817.img.xml @@ -0,0 +1,432 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Character.wz/Face/00021817.img.xml b/wz/Character.wz/Face/00021817.img.xml new file mode 100644 index 0000000000..38e1700ee7 --- /dev/null +++ b/wz/Character.wz/Face/00021817.img.xml @@ -0,0 +1,432 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Character.wz/Face/00021820.img.xml b/wz/Character.wz/Face/00021820.img.xml new file mode 100644 index 0000000000..79c9c25167 --- /dev/null +++ b/wz/Character.wz/Face/00021820.img.xml @@ -0,0 +1,434 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Etc.wz/Commodity.img.xml b/wz/Etc.wz/Commodity.img.xml index e8113c9b7c..68399645e9 100644 --- a/wz/Etc.wz/Commodity.img.xml +++ b/wz/Etc.wz/Commodity.img.xml @@ -77814,83 +77814,83 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -79818,14 +79818,14 @@ - + - + - + - - + + diff --git a/wz/Item.wz/Consume/0202.img.xml b/wz/Item.wz/Consume/0202.img.xml index f51b0a7ca6..6db7fddb1f 100644 --- a/wz/Item.wz/Consume/0202.img.xml +++ b/wz/Item.wz/Consume/0202.img.xml @@ -6496,8 +6496,9 @@ - + + @@ -6515,6 +6516,7 @@ + @@ -6532,6 +6534,7 @@ + diff --git a/wz/Map.wz/Map/Map9/925010000.img.xml b/wz/Map.wz/Map/Map9/925010000.img.xml index 174db76adf..70d1cf762e 100644 --- a/wz/Map.wz/Map/Map9/925010000.img.xml +++ b/wz/Map.wz/Map/Map9/925010000.img.xml @@ -5,7 +5,7 @@ - + diff --git a/wz/Map.wz/Map/Map9/925010100.img.xml b/wz/Map.wz/Map/Map9/925010100.img.xml index 5639958cca..cd3797b7ca 100644 --- a/wz/Map.wz/Map/Map9/925010100.img.xml +++ b/wz/Map.wz/Map/Map9/925010100.img.xml @@ -5,7 +5,7 @@ - + diff --git a/wz/Map.wz/Map/Map9/925010200.img.xml b/wz/Map.wz/Map/Map9/925010200.img.xml index fdbeda4e0f..7c1368d311 100644 --- a/wz/Map.wz/Map/Map9/925010200.img.xml +++ b/wz/Map.wz/Map/Map9/925010200.img.xml @@ -5,7 +5,7 @@ - + diff --git a/wz/Map.wz/Map/Map9/925010300.img.xml b/wz/Map.wz/Map/Map9/925010300.img.xml index d5b771cefe..eb7c5e13c3 100644 --- a/wz/Map.wz/Map/Map9/925010300.img.xml +++ b/wz/Map.wz/Map/Map9/925010300.img.xml @@ -1,1449 +1,18 @@ - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1453,39 +22,1463 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Skill.wz/1510.img.xml b/wz/Skill.wz/1510.img.xml index 72e19a7ca6..908ccff3d6 100644 --- a/wz/Skill.wz/1510.img.xml +++ b/wz/Skill.wz/1510.img.xml @@ -1591,13 +1591,13 @@ - + diff --git a/wz/String.wz/Cash.img.xml b/wz/String.wz/Cash.img.xml index da0f04224c..34f490c95f 100644 --- a/wz/String.wz/Cash.img.xml +++ b/wz/String.wz/Cash.img.xml @@ -1197,54 +1197,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1345,10 +1301,6 @@ - - - - @@ -1435,10 +1387,6 @@ - - - - @@ -1447,10 +1395,6 @@ - - - - @@ -1471,18 +1415,6 @@ - - - - - - - - - - - - @@ -1579,30 +1511,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -1945,7 +1853,7 @@ - + diff --git a/wz/String.wz/Consume.img.xml b/wz/String.wz/Consume.img.xml index 3025495d16..28230edbb8 100644 --- a/wz/String.wz/Consume.img.xml +++ b/wz/String.wz/Consume.img.xml @@ -140,18 +140,6 @@ - - - - - - - - - - - - @@ -268,10 +256,6 @@ - - - - @@ -540,18 +524,10 @@ - - - - - - - - @@ -588,10 +564,6 @@ - - - - @@ -812,10 +784,6 @@ - - - - @@ -3807,10 +3775,6 @@ - - - - @@ -3823,14 +3787,6 @@ - - - - - - - - @@ -4187,20 +4143,6 @@ - - - - - - - - - - - - - - @@ -4209,9 +4151,6 @@ - - - @@ -4292,10 +4231,6 @@ - - - - @@ -4567,14 +4502,6 @@ - - - - - - - - @@ -4607,54 +4534,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -6148,10 +6027,6 @@ - - - - @@ -9136,10 +9011,6 @@ - - - - diff --git a/wz/String.wz/Eqp.img.xml b/wz/String.wz/Eqp.img.xml index 4d6bcc478f..f58e7cc010 100644 --- a/wz/String.wz/Eqp.img.xml +++ b/wz/String.wz/Eqp.img.xml @@ -1471,9 +1471,6 @@ - - - @@ -1501,18 +1498,12 @@ - - - - - - @@ -3615,18 +3606,9 @@ - - - - - - - - - @@ -4303,21 +4285,9 @@ - - - - - - - - - - - - @@ -6275,9 +6245,6 @@ - - - @@ -6290,724 +6257,724 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -7016,136 +6983,136 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -7154,67 +7121,67 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -7223,136 +7190,136 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -7361,67 +7328,67 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -7430,142 +7397,142 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -7574,340 +7541,340 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -7917,9 +7884,6 @@ - - - @@ -8509,16 +8473,10 @@ - - - - - - @@ -8638,9 +8596,6 @@ - - - @@ -9155,28 +9110,28 @@ - + - + - + - + - + - + - + - + @@ -9323,28 +9278,28 @@ - + - + - + - + - + - + - + - + @@ -13160,60 +13115,46 @@ - - + - - + - - + - - - - + - - + - - + - - + - - + - - + - - + - - + - - + @@ -14689,9 +14630,6 @@ - - - @@ -15073,10 +15011,6 @@ - - - - @@ -15503,10 +15437,6 @@ - - - - @@ -15699,12 +15629,6 @@ - - - - - - @@ -15955,12 +15879,6 @@ - - - - - - @@ -17708,12 +17626,6 @@ - - - - - - @@ -18118,12 +18030,6 @@ - - - - - - @@ -18405,9 +18311,6 @@ - - - @@ -18512,9 +18415,6 @@ - - - @@ -18798,9 +18698,6 @@ - - - @@ -18810,9 +18707,6 @@ - - - @@ -19074,9 +18968,6 @@ - - - @@ -19293,9 +19184,6 @@ - - - @@ -20269,9 +20157,6 @@ - - - @@ -20363,9 +20248,6 @@ - - - @@ -20431,9 +20313,6 @@ - - - @@ -20632,9 +20511,6 @@ - - - @@ -21292,10 +21168,6 @@ - - - - @@ -22413,5 +22285,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/String.wz/Etc.img.xml b/wz/String.wz/Etc.img.xml index 7b2c617dd8..2cc65ee97e 100644 --- a/wz/String.wz/Etc.img.xml +++ b/wz/String.wz/Etc.img.xml @@ -20,10 +20,6 @@ - - - - @@ -1320,10 +1316,6 @@ - - - - @@ -3720,9 +3712,6 @@ - - - @@ -4775,94 +4764,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5291,10 +5192,6 @@ - - - - @@ -7193,10 +7090,6 @@ - - - - @@ -9498,10 +9391,6 @@ - - - - diff --git a/wz/String.wz/Ins.img.xml b/wz/String.wz/Ins.img.xml index 6aca7c5c25..72d4700927 100644 --- a/wz/String.wz/Ins.img.xml +++ b/wz/String.wz/Ins.img.xml @@ -639,9 +639,6 @@ - - - @@ -953,10 +950,6 @@ - - - - diff --git a/wz/String.wz/Pet.img.xml b/wz/String.wz/Pet.img.xml index c97ca14f6c..d1ec92578f 100644 --- a/wz/String.wz/Pet.img.xml +++ b/wz/String.wz/Pet.img.xml @@ -189,26 +189,11 @@ - - - - - - - - - - - - - - -