From 46fec675f139abd9b81cbe5e862152c89444cc59 Mon Sep 17 00:00:00 2001 From: ronancpl Date: Sun, 17 Feb 2019 16:08:53 -0300 Subject: [PATCH] Added Puppets on Aggro + Guild/Alliance packet info patch + Pet flags Added pet flags recognition, such as the "pet speed same as owner" mechanic. Fixed repeatable quest "Remnants of HT..." not rewarding Dragon Stones. Refactored character object's check slots method, now using the same algorithm from the API class. Fixed a vunerability from before reviving/respawning players to towns, on where players would get their HP restored in area map before sending them back to safety. Fixed some NPC gates in Mushking Empire, allowing players to access bosses free of quest requirements. Improved dialog of NPCs for "permission to attempt Zakum expeds". Implemented (or rather improved) item scripts, now using NPC conversation methods as it was intended originally. Rehauled mob-skill "summon mobs" limit, now using the placeholders found in the wz to determine limit of concurrent underlings spawned, and "limit" now refers to total of underlings spawned from a mob. Revised IoSession closing mechanics in several login classes. Implemented automated loggedin account shutdown if another player has gained a pass from the DB to login. Improved the server-side check for teleport rocks. Fixed removeAfter from mobs being procced after they were disposed. Reviewed EIM start references on several scripts, minding the latest EIM setup implementations. Fixed equipments that were reused Scissors of Karma not not being able to sell on pshops/merchants. Fixed a bug where entering alliances would lead a player to see information from other guilds of past alliances until an update takes place. Padronized a few skillbook names. Reviewed player puppets not gaining priority properly on the recent aggro system. Improved "item sold" message of merchants, now displaying also quantity left of an item on store. Fixed a bug with itemid limits of weapons on server. --- .gitignore | 4 + README.md | 7 +- docs/issues.txt | 2 +- docs/mychanges_ptbr.txt | 43 ++- handbook/Equip/Cape.txt | 2 +- handbook/Use.txt | 56 ++-- scripts/event/3rdJob_bowman.js | 10 +- scripts/event/3rdJob_magician.js | 10 +- scripts/event/3rdJob_mount.js | 27 +- scripts/event/3rdJob_pirate.js | 10 +- scripts/event/3rdJob_thief.js | 10 +- scripts/event/3rdJob_warrior.js | 10 +- scripts/event/4jberserk.js | 17 +- scripts/event/4jrush.js | 14 +- scripts/event/Aran_2ndmount.js | 10 +- scripts/event/Aran_3rdmount.js | 10 +- scripts/event/BalrogQuest.js | 10 +- scripts/event/DollHouse.js | 10 +- scripts/event/Hak.js | 4 +- scripts/event/KerningTrain.js | 4 +- scripts/event/RockSpirit.js | 7 +- scripts/event/RockSpiritVIP.js | 7 +- scripts/event/s4aWorld.js | 5 +- scripts/map/onUserEnter/200090000.js | 8 +- scripts/map/onUserEnter/200090010.js | 8 +- scripts/map/onUserEnter/922000000.js | 4 +- scripts/npc/1052007.js | 9 +- scripts/npc/1052125.js | 8 +- scripts/npc/1061009.js | 9 +- scripts/npc/1061014.js | 2 +- scripts/npc/1300013.js | 13 +- scripts/npc/2020008.js | 4 +- scripts/npc/2020009.js | 4 +- scripts/npc/2020010.js | 4 +- scripts/npc/2020011.js | 4 +- scripts/npc/2020013.js | 4 +- scripts/npc/2030013.js | 2 +- scripts/npc/2030013_old.js | 12 +- scripts/npc/2040002.js | 7 +- scripts/npc/2060005.js | 6 +- scripts/npc/2083004.js | 2 +- scripts/npc/2090005.js | 10 +- scripts/npc/2141001.js | 2 +- scripts/npc/9120201.js | 2 +- scripts/npc/9201113.js | 2 +- scripts/npc/9270047.js | 2 +- scripts/npc/9977777.js | 5 + scripts/portal/Depart_ToKerning.js | 10 +- scripts/portal/enterRider.js | 11 +- scripts/portal/q2073.js | 6 +- scripts/portal/rankRoom.js | 3 + scripts/quest/21401.js | 14 +- scripts/quest/21613.js | 15 +- scripts/quest/2245.js | 15 +- scripts/quest/2291.js | 13 +- scripts/quest/3714.js | 15 +- sql/db_database.sql | 1 + src/client/MapleCharacter.java | 80 +++-- src/client/MapleClient.java | 26 +- src/client/command/CommandsExecutor.java | 1 + .../commands/gm0/ToggleExpCommand.java | 44 +++ .../command/commands/gm1/GotoCommand.java | 13 +- .../command/commands/gm2/WarpCommand.java | 11 +- .../command/commands/gm3/FaceCommand.java | 5 +- .../command/commands/gm3/HairCommand.java | 5 +- src/client/inventory/MaplePet.java | 53 ++- .../manipulator/MapleKarmaManipulator.java | 1 + src/client/processor/MakerProcessor.java | 2 +- src/constants/ItemConstants.java | 10 +- src/constants/ServerConstants.java | 2 +- src/net/MapleServerHandler.java | 14 +- src/net/server/Server.java | 12 +- .../handlers/AllianceOperationHandler.java | 2 +- .../handlers/CharInfoRequestHandler.java | 2 + .../channel/handlers/DamageSummonHandler.java | 1 - .../handlers/PlayerInteractionHandler.java | 64 +++- .../handlers/PlayerLoggedinHandler.java | 2 +- .../channel/handlers/ScriptedItemHandler.java | 22 +- .../channel/handlers/TrockAddMapHandler.java | 2 + .../channel/handlers/UseCashItemHandler.java | 41 +-- .../MapleMonsterAggroCoordinator.java | 28 ++ .../coordinator/MapleSessionCoordinator.java | 29 ++ src/net/server/guild/MapleAlliance.java | 6 +- .../handlers/login/CharSelectedHandler.java | 4 +- .../login/CharSelectedWithPicHandler.java | 4 +- .../handlers/login/LoginPasswordHandler.java | 1 + .../handlers/login/RegisterPicHandler.java | 6 +- .../login/ViewAllCharRegisterPicHandler.java | 4 +- .../login/ViewAllCharSelectedHandler.java | 4 +- .../ViewAllCharSelectedWithPicHandler.java | 4 +- src/scripting/AbstractPlayerInteraction.java | 10 +- .../EventInstanceInProgressException.java} | 22 +- src/scripting/event/EventManager.java | 78 ++++- src/scripting/item/ItemScriptManager.java | 74 +---- src/scripting/map/MapScriptManager.java | 8 +- src/scripting/npc/NPCConversationManager.java | 14 +- src/scripting/npc/NPCScriptManager.java | 33 +- src/server/MapleItemInformationProvider.java | 10 +- src/server/MapleTrade.java | 111 +++++-- src/server/life/MapleMonster.java | 309 +++++++++++++++--- src/server/life/MobSkill.java | 118 ++++--- src/server/maps/MapMonitor.java | 9 +- src/server/maps/MapleHiredMerchant.java | 24 +- src/server/maps/MapleMap.java | 44 ++- src/server/quest/MapleQuest.java | 3 + src/server/quest/MapleQuestActionType.java | 4 +- src/server/quest/actions/PetSpeedAction.java | 60 ++++ src/tools/FilePrinter.java | 1 + src/tools/MaplePacketCreator.java | 35 +- .../MapleCashCosmeticsChecker.java | 2 +- tools/MapleCashVegaChecker/build.xml | 73 +++++ tools/MapleCashVegaChecker/lib/result.txt | 5 + tools/MapleCashVegaChecker/manifest.mf | 3 + .../MapleCashVegaChecker.java | 213 ++++++++++++ wz/Character.wz/Accessory/01142009.img.xml | 2 +- wz/Character.wz/Accessory/01142010.img.xml | 2 +- wz/Character.wz/Accessory/01142011.img.xml | 2 +- wz/Character.wz/Accessory/01142012.img.xml | 2 +- wz/Character.wz/Accessory/01142013.img.xml | 2 +- wz/Character.wz/Accessory/01142133.img.xml | 2 +- wz/Etc.wz/Commodity.img.xml | 2 +- wz/Etc.wz/VegaSpell.img.xml | 8 + wz/Mob.wz/8520000.img.xml | 1 - wz/Quest.wz/Check.img.xml | 6 + wz/Quest.wz/QuestInfo.img.xml | 2 +- wz/String.wz/Consume.img.xml | 42 +-- wz/String.wz/Eqp.img.xml | 2 +- 127 files changed, 1760 insertions(+), 608 deletions(-) create mode 100644 src/client/command/commands/gm0/ToggleExpCommand.java rename src/scripting/{item/ItemScriptMethods.java => event/EventInstanceInProgressException.java} (64%) create mode 100644 src/server/quest/actions/PetSpeedAction.java create mode 100644 tools/MapleCashVegaChecker/build.xml create mode 100644 tools/MapleCashVegaChecker/lib/result.txt create mode 100644 tools/MapleCashVegaChecker/manifest.mf create mode 100644 tools/MapleCashVegaChecker/src/maplecashvegachecker/MapleCashVegaChecker.java diff --git a/.gitignore b/.gitignore index 2105e475e1..32140893ac 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,10 @@ /tools/MapleCashDropFetcher/dist/ /tools/MapleCashDropFetcher/nbproject/ +/tools/MapleCashVegaChecker/build/ +/tools/MapleCashVegaChecker/dist/ +/tools/MapleCashVegaChecker/nbproject/ + /tools/MapleCodeCouponGenerator/build/ /tools/MapleCodeCouponGenerator/dist/ /tools/MapleCodeCouponGenerator/nbproject/ diff --git a/README.md b/README.md index df258ac230..33a2d0e088 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,15 @@ Java7 SDK: https://www.oracle.com/technetwork/java/javase/downloads/java-archive **Important note about localhosts**: these executables are red-flagged by antivirus tools as __potentially malicious softwares__, this happens due to the reverse engineering methods that were applied onto these software artifacts. Those depicted here have been put to use for years already and posed no harm so far, so they are soundly assumed to be safe. - Latest localhost: https://hostr.co/m2bVtnizCtmD + Latest localhost: https://hostr.co/tsYsQzzV6xT0 The following list, in bottom-up chronological order, holds information regarding all changes that were applied from the starting localhost used in this development. Some lines have a link attached, that will lead you to a snapshot of the localhost at that version of the artifact. Naturally, later versions holds all previous changes along with the proposed changes. **Change log:** - * Removed block on applying attack-based strengthening gems on non-weapon equipments. + * Fixed some 'rn' problems with quest icons & removed "tab" from party leader changed message. + + * Removed block on applying attack-based strengthening gems on non-weapon equipments. https://hostr.co/m2bVtnizCtmD * Set a higher cap for SPEED. @@ -84,6 +86,7 @@ It's never enough to tell this, thanks to everyone that have been contributing s Our Discord channel is still available on: https://discord.gg/Q7wKxHX + ### Donation If you REALLY liked what you have seen on the project, please feel free to donate a little something as a helping hand for my contributions towards Maple development. Also remember to **support Nexon**! diff --git a/docs/issues.txt b/docs/issues.txt index bea3d980b2..657eadb8e2 100644 --- a/docs/issues.txt +++ b/docs/issues.txt @@ -16,6 +16,7 @@ Known issues: - 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. +- Visual equip EXP watch value will present stuttering for early levels requirement (EXP needed less than 100), and requirement at level 200 will not progress at all due to the level cap in client. --------------------------- --------------------------- @@ -24,7 +25,6 @@ Missing features list: - Some pirate skills doesn't work for 3rd parties. - Cache frequently used SQL data. - Pet commands are not being propagated to 3rd parties, and the commandResponse packet function seems somewhat wonky. -- Pet speed. --------------------------- diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 25788d0a90..9f5614badc 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -1619,4 +1619,45 @@ Adicionado checagem em quantidade de itens nas lojas e mercantes de jogadores, a Corrigido um exploit com mercantes, envolvendo chamada à DB antes de retirar item (que está sendo inserido na loja) do inventário do jogador. 30 Janeiro 2019, -Corrigido chamadas irregulares a "qm/cm" em scripts. \ No newline at end of file +Corrigido chamadas irregulares a "qm/cm" em scripts. + +31 Janeiro 2019, +Com a ajuda de Irenex, adicionado referência a pet flags no criador de pacotes. +Adicionado funcionalidade de quest que permite pets a andarem na velocidade do jogador. +Corrigido quest repetível de Dragon Stone não apropriadamente recompensando o item ao jogador. +Corrigido método de checagem de slots em MapleCharacter, agora utilizando o método padrão implementado especificamente para checagem de slots. + +01 Fevereiro 2019, +Corrigido vulnerabilidade na ação de jogadores ao retornar para a cidade após reviver. Servidor poderia receber packet de dano e processá-lo legitimamente com o HP atualizado do jogador antes de enviá-lo de volta para cidade. + +04 - 05 Fevereiro 2019, +Corrigido NPCs de portão em Mushking Empire permitindo jogadores acesso a área de bosses sem requisitos mínimos. +Incrementado diálogos de permissão para realizar expedições contra Zakum. +Modificado gerenciador de item scripts, agora referenciando conversação de NPCs (como previsto pelo WZ). +Implementado limitação de mob summons, como previsto no WZ. +Revisado fechamento de sessões no código-fonte não utilizando o coordenador de sessões para isto. +Revisado sistema de login, agora automaticamente desconectando contas retidas no jogo quando DB determina que uma conta está disponível para login. +Revisado Teleport Rocks, agora devidamente checando se é possível chegar ao mapa alvo (ou se há bloqueio de uso do item para esse mapa), além de certas checagens contra exploits com esse item. +Corrigido agendamento de removeAfter de mobs atuando quando o mesmo já foi liberado. + +07 - 08 Fevereiro 2019, +Corrigido ocorrências de NPE ao tentar registrar-se em instância de evento, quando existe uma outra instância que leva o mesmo nome em andamento. +Tentativa de correção em todos os scripts de eventos, após ter atualizado o sistema de eventos para não mais ficar gerando instâncias livremente (agora, todas as instâncias orientadas-a-solo têm limite de acesso simultâneo entre jogadores). +Corrigido lojas de jogadores e mercantes não aceitando itens em certos casos onde já se foi usado Scissors of Karma. +Revisado novamente diversos scripts de evento, atentando às chamadas de função "setup" das mesmas. + +09 Fevereiro 2019, +Tentativa de correção em sessões com exceptionCaught, já que aparentemente a sessão nem sempre é fechada pelo MINA automaticamente. +Corrigido lista de guilds em alliances não sendo atualizado corretamente ao criar-se uma nova alliance. Era possível ver guilds de alianças passadas antes de ocorrer alguma atualização de packet em jogo que resolva isso. +Implementado comando "toggleexp", que permite jogadores a escolher ganhar nível ou não se os mesmos não estivem em instância de evento. +Nova ferramenta: MapleCashVegaChecker. Ferramenta busca por itens que supostamente deveriam permitir uso do Vega Scroll (segundo descrição) porém não o permitem. +Revisado nomes de skill e mastery books, que mantinham números no nome da skill (agora padronizado com os outros livros). + +10 Fevereiro 2019, +Editado requerimentos de level de medalhas level 200 para 180, assim evitando o problema onde nível das medalhas não apareçe na visão do cliente devido ao level cap. +Corrigido puppets não tomando prioridade na nova mecânica de aggro. +Incrementado mensagem custom de venda de produtos pelo mercante. + +13 - 14 Fevereiro 2019, +Corrigido limites na função isWeapon, que não contabilizaria certos itens corretamente. +Resolvido comportamento de puppets usando o novo sistema de aggro. \ No newline at end of file diff --git a/handbook/Equip/Cape.txt b/handbook/Equip/Cape.txt index 113117ce61..ae3945d6d9 100644 --- a/handbook/Equip/Cape.txt +++ b/handbook/Equip/Cape.txt @@ -82,7 +82,7 @@ 1102080 - Ragged Blue Cape - (no description) 1102081 - Ragged Yellow Cape - (no description) 1102082 - Ragged Black Cape - (no description) -1102083 - Ragged Red Cape - (no description) +1102083 - Ragged Green Cape - (no description) 1102084 - Pink Gaia Cape - (no description) 1102085 - Yellow Gaia Cape - (no description) 1102086 - Purple Gaia Cape - (no description) diff --git a/handbook/Use.txt b/handbook/Use.txt index 0a2f651a57..137f8b3141 100644 --- a/handbook/Use.txt +++ b/handbook/Use.txt @@ -376,15 +376,15 @@ 2030004 - Return Scroll to Henesys - Returns you to Henesys. 2030005 - Return Scroll to Kerning City - Returns you to the dark Kerning City. 2030006 - Return Scroll to Sleepywood - Returns you to Sleepywood, a quiet and dark forest-town. -2030007 - Return Scroll for Dead Mine - Returns you to the dead mine at the higher ground of El Nath.nCan only be used in Orbis and El Nath. +2030007 - Return Scroll to Dead Mine - Returns you to the dead mine at the higher ground of El Nath.nCan only be used in Orbis and El Nath. 2030008 - Coffee Milk - Returns you to the nearest town. 2030009 - Strawberry Milk - Returns you to Mushroom Shrine. 2030010 - Fruit Milk - Returns you to Showa Town. 2030011 - Command Center Warp Capsule - A warp capsule that allows the owner of the capsule to warp to the Command Center of Omega Sector. 2030012 - Ludibrium Warp Capsule - A warp capsule that returns you to Ludibrium. 2030016 - Phyllia's Warp Powder - Warp powder made by fairy Phyllia. Teleports you to Magatia when used inside the Nihal desert region. -2030019 - Nautilus Return Scroll - This scroll enables you to return to the Pirate village, Nautilus. This is a one use item and will disappear after use. -2030020 - Return to New Leaf City Scroll - Use this scroll to venture back to New Leaf City whenever you want! +2030019 - Return Scroll to Nautilus - This scroll enables you to return to the Pirate village, Nautilus. This is a one use item and will disappear after use. +2030020 - Return Scroll to New Leaf City - Use this scroll to venture back to New Leaf City whenever you want! 2030100 - Return Scroll - Banished Area - Returns you to the map where you were last banished. Requires immediate use and have changed neither maps nor channels. 2031000 - Masked Man's Invitation - An invitation from the Masked Man to the Halloween Party at the Haunted Mansion. Double-click to move straight to the mansion. 2031001 - Studio Invitation - An invitation to the studio for the event "For Guild Only". @@ -1182,7 +1182,7 @@ 2280001 - Black Cloud Machine - A mechanical device that produces black clouds. Enables the character to acquire the Smokescreen skill using the clouds. 2280002 - Firm Hand - A stimulant packaged inside a bottle that resembles a clenched fist. Drinking the stimulant will allow the character to acquire The Will of a Warrior. 2280003 - [Skill Book] Maple Warrior - You can learn #cMaple Warrior# with this book.rnJob : All 4th jobsrnCondition : #cMaple Warrior# not acquired. -2280004 - [Skill Book] Infinity - You can learn #Infinity# with this book.rnJob : 4th Advancement MagicianrnCondition : #cInfinity# not acquired +2280004 - [Skill Book] Infinity - You can learn #cInfinity# with this book.rnJob : 4th Advancement MagicianrnCondition : #cInfinity# not acquired 2280005 - [Skill Book] Dragon's Breath - You can learn #cDragon's Breath# with this book.wnJob : 4th Advancement BowmanrnCondition : #cDragon's Breath# not acquired 2280006 - [Skill Book] Taunt - You can learn #cTaunt# with this book.rnJob : 4th Advancement ThiefrnCondition : #cTaunt# not acquired 2280007 - [Skill Book] Advanced Combo Attack - You can learn #cAdvanced Combo Attack# with this book.rnClass : HerornCondition : #cAdvanced Combo# not acquired @@ -1191,6 +1191,13 @@ 2280010 - [Skill Book] Triple Throw - You can learn #cTriple Throw# with this book.rnClass : Night LordrnCondition : #cTriple Throw# not acquired 2280011 - Ancient Ice Powder - This is a pack full of ancient ice powder. If you eat this, you will feel chilled and can learn Ice Demon. 2280012 - [Skill Book] Rush - You can learn #cRush# with this book.rnJob : 4th Advancement WarriorrnCondition : #cRush# not acquired +2280013 - [Skill Book] Final Blow - Skill Book from which you can learn about the #cFinal Blow# skill.\nJob: 4th Lv Aran\nCondition: #cFinal Blow# not available +2280014 - [Skill Book] High Defense - Skill Book from which you can learn about the #cHigh Defense# skill.\nJob: 4th Lv Aran\nCondition: #cHigh Defense# not available +2280015 - [Skill Book] Combo Tempest - Skill Book from which you can learn about the #cCombo Tempest# skill.\nJob: 4th Lv Aran\nCondition: #cCombo Tempest# not available +2280017 - [Skill Book] Pig's Weakness - Skill Book from which you can learn about the #cPig's Weakness# skill.\nCondition: #cPig's Weakness# not available +2280016 - [Skill Book] Combo Barrier - Skill Book from which you can learn about the #cCombo Barrier# skill.\nJob: 4th Lv Aran\nCondition: #cCombo Barrier# not available +2280018 - [Skill Book] Stump's Weakness - Skill Book from which you can learn about the #cStump's Weakness# skill.\nCondition: #cStump's Weakness# not available +2280019 - [Skill Book] Slime's Weakness - Skill Book from which you can learn about the #cSlime's Weakness# skill.\nCondition: #cSlime's Weakness# not available 2290000 - [Mastery Book] Monster Magnet - This increases master level of the #cMonster Magnet# skill up to 20 with 70% chance of success.rnJob : 4th Advancement WarriorrnCondition : Skill level above 5 2290001 - [Mastery Book] Monster Magnet - This increases master level of the #cMonster Magnet# skill up to 30 with 50% chance of success. rnJob : 4th Advancement WarriorrnCondition : Skill Level above 15 2290002 - [Mastery Book] Achilles - This increases the master level of #cAchilles# up to 20 with 70% of chance.rnJob : 4th Advancement WarriorrnCondition : Skill level above 5 @@ -1287,7 +1294,7 @@ 2290093 - [Mastery Book] Assassinate - This increases the master level of #cAssassinate# up to 30 with 50% of chance.rnClass : ShadowerrnCondition : Skill level above 15 2290094 - [Mastery Book] Smokescreen - This increases the master level of #cSmokescreen# up to 20 with 70% of chance.rnClass : ShadowerrnCondition : Skill level above 5 2290095 - [Mastery Book] Smokescreen - This increases the master level of #cSmokescreen# up to 30 with 50% of chance.rnClass : ShadowerrnCondition : Skill level above 15 -2290096 - [Mastery Book] Maple Warrior 20 - This increases the master level of #cMaple Warrior# up to 20 with 70% of chance.rnJob : All 4th Advancement JobsrnCondition : Skill level above 5 +2290096 - [Mastery Book] Maple Warrior - This increases the master level of #cMaple Warrior# up to 20 with 70% of chance.rnJob : All 4th Advancement JobsrnCondition : Skill level above 5 2290097 - [Mastery Book] Dragon Strike - With a 70% success rate, it raises the Master Level of #cDragon Strike# to 20.nJob : BuccaneernRequirement : At least Level 5 in this skill 2290098 - [Mastery Book] Dragon Strike - With a 50% success rate, it raises the Master Level of #cDragon Strike# to 30.nJob : BuccaneernRequirement : At least Level 15 in this skill 2290099 - [Mastery Book] Energy Orb - With a 70% success rate, it raises the Master Level of #cEnergy Orb# to 20.nJob : BuccaneernRequirement : At least Level 5 in this skill @@ -1316,7 +1323,21 @@ 2290122 - [Mastery Book] Battleship Torpedo - With a 50% success rate, it raises the Master Level of #cBattleship Torpedo# to 30.nJob : CorsairnRequirement : At least Level 15 in this skill 2290123 - [Mastery Book] Hypnotize - With a 70% success rate, it raises the Master Level of #cHypnotize# to 20.nJob : CorsairnRequirement : At least Level 5 in this skill 2290124 - [Mastery Book] Bullseye - With a 70% success rate, it raises the Master Level of #cBullseye# to 20.nJob : CorsairnRequirement : At least Level 5 in this skill -2290125 - [Mastery Book] Maple Warrior 30 - This increases the master level of #cMaple Warrior# up to 30 with 50% of chance.rnJob : All 4th Advancement JobsrnCondition : Skill level above 15 +2290125 - [Mastery Book] Maple Warrior - This increases the master level of #cMaple Warrior# up to 30 with 50% of chance.rnJob : All 4th Advancement JobsrnCondition : Skill level above 15 +2290126 - [Mastery Book] Overswing - With a 70% success rate, raises the Mastery Level of #cOverswing# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 +2290127 - [Mastery Book] Overswing - With a 50% success rate, raises the Mastery Level of #cOverswing# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 +2290128 - [Mastery Book] High Mastery - With a 70% success rate, raises the Mastery Level of #cHigh Mastery# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 +2290129 - [Mastery Book] High Mastery - With a 50% success rate, raises the Mastery Level of #cHigh Mastery# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 +2290130 - [Mastery Book] Freeze Standing - With a 70% success rate, raises the Mastery Level of #cFreeze Standing# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 +2290131 - [Mastery Book] Freeze Standing - With a 50% success rate, raises the Mastery Level of #cFreeze Standing# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 +2290132 - [Mastery Book] Final Blow - With a 70% success rate, raises the Mastery Level of #cFinal Blow# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 +2290133 - [Mastery Book] Final Blow - With a 50% success rate, raises the Mastery Level of #cFinal Blow# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 +2290134 - [Mastery Book] High Defense - With a 70% success rate, raises the Mastery Level of #cHigh Defense# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 +2290135 - [Mastery Book] High Defense - With a 50% success rate, raises the Mastery Level of #cHigh Defense# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 +2290136 - [Mastery Book] Combo Tempest - With a 70% success rate, raises the Mastery Level of #cCombo Tempest# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 +2290137 - [Mastery Book] Combo Tempest - With a 50% success rate, raises the Mastery Level of #cCombo Tempest# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 +2290138 - [Mastery Book] Combo Barrier - With a 70% success rate, raises the Mastery Level of #cCombo Barrier# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 +2290139 - [Mastery Book] Combo Barrier - With a 50% success rate, raises the Mastery Level of #cCombo Barrier# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 2310000 - The Owl of Minerva - #cThe Owl of Minerva#, which represents wisdom, can be used to search for items sold at the Free Market. #cDisappears right after showing the results of the item search#. 2320000 - Teleport Rock - Remembers 5 maps of your choice. This rock will enable you to #cteleport to the map you remembered#. It can even allow you to #cmove to the map where a certain character is#, provided that the person is in the same channel at the same world. 2330000 - Bullet - A bullet made out of steel. A set contains numerous bullets and once they are used up, they'll need to be recharged.\nAttack + 10 @@ -2162,27 +2183,6 @@ 2109008 - Bubbling Summoning Bag - For use in the restricted bonus map area of the subway station. 2109009 - Jr. Yeti Pharaoh Summoning Bag - Summons Jr. Yeti Pharaoh for the bonus stage of Nett's Pyramid (Easy, Normal, Hard) 2109010 - Jr. Yeti Pharaoh Summoning Bag - Summons Jr. Yeti Pharaoh for the bonus stage of Nett's Pyramid (Hell Mode) -2280013 - [Skill Book] Final Blow - Skill Book from which you can learn about the #cFinal Blow# skill.\nJob: 4th Lv Aran\nCondition: #cFinal Blow# not available -2280014 - [Skill Book] High Defense - Skill Book from which you can learn about the #cHigh Defense# skill.\nJob: 4th Lv Aran\nCondition: #cHigh Defense# not available -2280015 - [Skill Book] Combo Tempest - Skill Book from which you can learn about the #cCombo Tempest# skill.\nJob: 4th Lv Aran\nCondition: #cCombo Tempest# not available -2280017 - [Skill Book] Pig's Weakness - Skill Book from which you can learn about the #cPig's Weakness# skill.\nCondition: #cPig's Weakness# not available -2280016 - [Skill Book] Combo Barrier - Skill Book from which you can learn about the #cCombo Barrier# skill.\nJob: 4th Lv Aran\nCondition: #cCombo Barrier# not available -2280018 - [Skill Book] Stump's Weakness - Skill Book from which you can learn about the #cStump's Weakness# skill.\nCondition: #cStump's Weakness# not available -2280019 - [Skill Book] Slime's Weakness - Skill Book from which you can learn about the #cSlime's Weakness# skill.\nCondition: #cSlime's Weakness# not available -2290126 - [Mastery Book] Overswing 20 - With a 70% success rate, raises the Mastery Level of #cOverswing# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 -2290127 - [Mastery Book] Overswing 30 - With a 50% success rate, raises the Mastery Level of #cOverswing# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 -2290128 - [Mastery Book] High Mastery 20 - With a 70% success rate, raises the Mastery Level of #cHigh Mastery# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 -2290129 - [Mastery Book]High Mastery 30 - With a 50% success rate, raises the Mastery Level of #cHigh Mastery# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 -2290130 - [Mastery Book] Freeze Standing 20 - With a 70% success rate, raises the Mastery Level of #cFreeze Standing# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 -2290131 - [Mastery Book] Freeze Standing 30 - With a 50% success rate, raises the Mastery Level of #cFreeze Standing# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 -2290132 - [Mastery Book] Final Blow 20 - With a 70% success rate, raises the Mastery Level of #cFinal Blow# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 -2290133 - [Mastery Book] Final Blow 30 - With a 50% success rate, raises the Mastery Level of #cFinal Blow# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 -2290134 - [Mastery Book] High Defense 20 - With a 70% success rate, raises the Mastery Level of #cHigh Defense# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 -2290135 - [Mastery Book] High Defense 30 - With a 50% success rate, raises the Mastery Level of #cHigh Defense# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 -2290136 - [Mastery Book] Combo Tempest 20 - With a 70% success rate, raises the Mastery Level of #cCombo Tempest# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 -2290137 - [Mastery Book] Combo Tempest 30 - With a 50% success rate, raises the Mastery Level of #cCombo Tempest# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 -2290138 - [Mastery Book] Combo Barrier 20 - With a 70% success rate, raises the Mastery Level of #cCombo Barrier# to 20.\nJob: Aran\nCondition: Min Skill Lv. 5 -2290139 - [Mastery Book] Combo Barrier 30 - With a 50% success rate, raises the Mastery Level of #cCombo Barrier# to 30.\nJob: Aran\nCondition: Min Skill Lv. 15 2380014 - Dejected Green Mushroom Card - A card featuring the Dejected Green Mushroom. 2380015 - Muru Card - A card featuring Muru. 2380016 - Murupa Card - A card featuring Murupa. @@ -2209,7 +2209,7 @@ 2430016 - Crystal Chest - A chest full of items that's sure to excite anyone who sees it. Open it by August 31st, 2009, or it'll disappear. 2450000 - Hunter's Luck - For 30 minutes, doubles your EXP from hunting. 2040758 - Scroll for Shoes for ATT - Improves attack on shoes.\nSuccess rate: 100%. Weapon Attack +1 -2040759 - Scroll for Shoes for ATT - Improves attack on gloves.\nSuccess rate: 60%. Weapon Attack +2. The success rate of this scroll can be enhanced by Vega's Spell. +2040759 - Scroll for Shoes for ATT - Improves attack on shoes.\nSuccess rate: 60%. Weapon Attack +2. The success rate of this scroll can be enhanced by Vega's Spell. 2040760 - Scroll for Shoes for ATT - Improves attack on shoes.\nSuccess rate: 10%, Weapon Attack +3. The success rate of this scroll can be enhanced by Vega's Spell. 2044815 - Scroll for Knuckler for Attack 100% - Improves attack on Knucklers.\nSuccess rate: 100%. Weapon Attack +2, STR +1 2044817 - Scroll for Knuckler for Attack 50% - Improves attack on Knucklers.\nSuccess rate: 50%. Weapon Attack +5, STR +3, DEX +1 diff --git a/scripts/event/3rdJob_bowman.js b/scripts/event/3rdJob_bowman.js index e622903724..447ae673d7 100644 --- a/scripts/event/3rdJob_bowman.js +++ b/scripts/event/3rdJob_bowman.js @@ -41,6 +41,14 @@ function init() { em.setProperty("noEntry","false"); } +function setup(level, lobbyid) { + var eim = em.newInstance("3rdJob_bowman_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + + return eim; +} + function playerEntry(eim, player) { eim.getInstanceMap(maxMapId).resetPQ(1); @@ -97,8 +105,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function disbandParty(eim, player) {} function afterSetup(eim) {} diff --git a/scripts/event/3rdJob_magician.js b/scripts/event/3rdJob_magician.js index 563efe6de9..aa244b5114 100644 --- a/scripts/event/3rdJob_magician.js +++ b/scripts/event/3rdJob_magician.js @@ -41,6 +41,14 @@ function init() { em.setProperty("noEntry","false"); } +function setup(level, lobbyid) { + var eim = em.newInstance("3rdJob_magician_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + + return eim; +} + function playerEntry(eim, player) { eim.getInstanceMap(maxMapId).resetPQ(1); @@ -97,8 +105,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function disbandParty(eim, player) {} function afterSetup(eim) {} diff --git a/scripts/event/3rdJob_mount.js b/scripts/event/3rdJob_mount.js index 1b100dd27b..b3f4bc0f7a 100644 --- a/scripts/event/3rdJob_mount.js +++ b/scripts/event/3rdJob_mount.js @@ -43,15 +43,38 @@ function init() { em.setProperty("noEntry","false"); } +function checkHogHealth(eim) { + var watchHog = eim.getInstanceMap(923010000).getMonsterById(9300102); + if (watchHog != null) { + var hp = watchHog.getHp(); + var oldHp = eim.getIntProperty("whog_hp"); + + if (oldHp - hp > 1000) { // or 800, if using mobHP / eventTime + eim.dropMessage(6, "Please protect the pig from the aliens!"); // thanks Vcoc + } + eim.setIntProperty("whog_hp", hp); + } +} + function respawnStages(eim) { var i; for (i = 0; i < eventMaps.length; i++) { eim.getInstanceMap(eventMaps[i]).instanceMapRespawn(); } - + checkHogHealth(eim); + eim.schedule("respawnStages", 10 * 1000); } +function setup(level, lobbyid) { + var eim = em.newInstance("3rdJob_mount_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + eim.setProperty("whog_hp", "0"); + + return eim; +} + function playerEntry(eim, player) { var mapObj = eim.getInstanceMap(entryMap); @@ -127,8 +150,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function disbandParty(eim, player) {} function afterSetup(eim) {} diff --git a/scripts/event/3rdJob_pirate.js b/scripts/event/3rdJob_pirate.js index fb7ba2be9e..ef18f1dc31 100644 --- a/scripts/event/3rdJob_pirate.js +++ b/scripts/event/3rdJob_pirate.js @@ -51,6 +51,14 @@ function playerEntry(eim, player) { eim.startEventTimer(eventTime * 60000); } +function setup(level, lobbyid) { + var eim = em.newInstance("3rdJob_pirate_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + + return eim; +} + function playerUnregistered(eim, player) {} function playerExit(eim, player) { @@ -97,8 +105,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function disbandParty(eim, player) {} function afterSetup(eim) {} diff --git a/scripts/event/3rdJob_thief.js b/scripts/event/3rdJob_thief.js index 6160f3c221..285c4d0484 100644 --- a/scripts/event/3rdJob_thief.js +++ b/scripts/event/3rdJob_thief.js @@ -41,6 +41,14 @@ function init() { em.setProperty("noEntry","false"); } +function setup(level, lobbyid) { + var eim = em.newInstance("3rdJob_thief_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + + return eim; +} + function playerEntry(eim, player) { eim.getInstanceMap(maxMapId).resetPQ(1); @@ -97,8 +105,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function disbandParty(eim, player) {} function afterSetup(eim) {} diff --git a/scripts/event/3rdJob_warrior.js b/scripts/event/3rdJob_warrior.js index af119f877b..f3b7f0bf97 100644 --- a/scripts/event/3rdJob_warrior.js +++ b/scripts/event/3rdJob_warrior.js @@ -41,6 +41,14 @@ function init() { em.setProperty("noEntry","false"); } +function setup(level, lobbyid) { + var eim = em.newInstance("3rdJob_warrior_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + + return eim; +} + function playerEntry(eim, player) { eim.getInstanceMap(maxMapId).resetPQ(1); @@ -97,8 +105,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function disbandParty(eim, player) {} function afterSetup(eim) {} diff --git a/scripts/event/4jberserk.js b/scripts/event/4jberserk.js index 9480843488..d3fd4fa747 100644 --- a/scripts/event/4jberserk.js +++ b/scripts/event/4jberserk.js @@ -20,27 +20,22 @@ along with this program. If not, see . */ var exitMap; -var instanceId; var minPlayers = 3; -function init() { - instanceId = 1; -} +function init() {} function monsterValue(eim, mobId) { return 1; } -function setup() { +function setup(level, lobbyid) { exitMap = em.getChannelServer().getMapFactory().getMap(105090800); // - var instanceName = "4jberserk" + instanceId; - - var eim = em.newInstance(instanceName); + + var eim = em.newInstance("4jberserk_" + lobbyid); + eim.setProperty("level", level); var mf = eim.getMapFactory(); - - instanceId++; - + var map = mf.getMap(910500200); map.addMapTimer(3*60); em.schedule("timeOut", 20 * 60000); diff --git a/scripts/event/4jrush.js b/scripts/event/4jrush.js index e5b7179d91..a1258d0ec6 100644 --- a/scripts/event/4jrush.js +++ b/scripts/event/4jrush.js @@ -27,23 +27,21 @@ */ var exitMap; -var instanceId; var minPlayers = 3; -function init() { - instanceId = 1; -} +function init() {} function monsterValue(eim, mobId) { return 1; } -function setup() { +function setup(level, lobbyid) { exitMap = em.getChannelServer().getMapFactory().getMap(105090700); // - var instanceName = "4jrush" + instanceId; - var eim = em.newInstance(instanceName); + + var eim = em.newInstance("4jrush_" + lobbyid); + eim.setProperty("level", level); + var mf = eim.getMapFactory(); - instanceId++; var map = mf.getMap(910500100); map.addMapTimer(20*60); em.schedule("timeOut", 20 * 60000); diff --git a/scripts/event/Aran_2ndmount.js b/scripts/event/Aran_2ndmount.js index 68f2e64c47..1be6114b18 100644 --- a/scripts/event/Aran_2ndmount.js +++ b/scripts/event/Aran_2ndmount.js @@ -41,6 +41,14 @@ function init() { em.setProperty("noEntry","false"); } +function setup(level, lobbyid) { + var eim = em.newInstance("Aran_2ndmount_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + + return eim; +} + function respawnStages(eim) {} function playerEntry(eim, player) { @@ -114,8 +122,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function disbandParty(eim, player) {} function afterSetup(eim) {} diff --git a/scripts/event/Aran_3rdmount.js b/scripts/event/Aran_3rdmount.js index efc469e2fc..04e135d0b8 100644 --- a/scripts/event/Aran_3rdmount.js +++ b/scripts/event/Aran_3rdmount.js @@ -41,6 +41,14 @@ function init() { em.setProperty("noEntry","false"); } +function setup(level, lobbyid) { + var eim = em.newInstance("Aran_3rdmount_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + + return eim; +} + function respawnStages(eim) {} function playerEntry(eim, player) { @@ -119,8 +127,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function disbandParty(eim, player) {} function afterSetup(eim) {} diff --git a/scripts/event/BalrogQuest.js b/scripts/event/BalrogQuest.js index 6ab3a6917f..fcef456bf4 100644 --- a/scripts/event/BalrogQuest.js +++ b/scripts/event/BalrogQuest.js @@ -41,6 +41,14 @@ function init() { em.setProperty("noEntry","false"); } +function setup(level, lobbyid) { + var eim = em.newInstance("BalrogQuest_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + + return eim; +} + function respawnStages(eim) {} function afterSetup(eim) {} @@ -104,8 +112,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function disbandParty(eim, player) {} function changedLeader(eim, leader) {} diff --git a/scripts/event/DollHouse.js b/scripts/event/DollHouse.js index d784adc0b1..a01e6e072a 100644 --- a/scripts/event/DollHouse.js +++ b/scripts/event/DollHouse.js @@ -32,6 +32,14 @@ function init() { em.setProperty("noEntry","false"); } +function setup(level, lobbyid) { + var eim = em.newInstance("DollHouse_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + + return eim; +} + function playerEntry(eim, player) { eim.getInstanceMap(entryMap).shuffleReactors(); eim.setExclusiveItems([4031094]); @@ -81,8 +89,6 @@ function dispose() {} // ---------- FILLER FUNCTIONS ---------- -function setup(eim, leaderid) {} - function monsterValue(eim, mobid) {return 0;} function disbandParty(eim, player) {} diff --git a/scripts/event/Hak.js b/scripts/event/Hak.js index a33ab7121e..24e33279d9 100644 --- a/scripts/event/Hak.js +++ b/scripts/event/Hak.js @@ -16,8 +16,8 @@ function init() { rideTime = em.getTransportationTime(rideTime); } -function setup() { - var eim = em.newInstance("Hak_" + + em.getProperty("player")); +function setup(level, lobbyid) { + var eim = em.newInstance("Hak_" + lobbyid); return eim; } diff --git a/scripts/event/KerningTrain.js b/scripts/event/KerningTrain.js index 6a30f37b5f..516af052c5 100644 --- a/scripts/event/KerningTrain.js +++ b/scripts/event/KerningTrain.js @@ -16,8 +16,8 @@ function init() { rideTime = em.getTransportationTime(rideTime); } -function setup() { - var eim = em.newInstance("KerningTrain_" + em.getProperty("player")); +function setup(level, lobbyid) { + var eim = em.newInstance("KerningTrain_" + lobbyid); return eim; } diff --git a/scripts/event/RockSpirit.js b/scripts/event/RockSpirit.js index 6ab69099ed..fe043ad251 100644 --- a/scripts/event/RockSpirit.js +++ b/scripts/event/RockSpirit.js @@ -34,8 +34,11 @@ function init() { otherMap = em.getChannelServer().getMapFactory().getMap(103040420); } -function setup() { - var eim = em.newInstance("RockSpirit_" + em.getProperty("player")); +function setup(level, lobbyid) { + var eim = em.newInstance("RockSpirit_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + respawn(eim); eim.startEventTimer(timer); return eim; diff --git a/scripts/event/RockSpiritVIP.js b/scripts/event/RockSpiritVIP.js index 2a2983211f..4de76839c1 100644 --- a/scripts/event/RockSpiritVIP.js +++ b/scripts/event/RockSpiritVIP.js @@ -34,8 +34,11 @@ function init() { otherMap = em.getChannelServer().getMapFactory().getMap(103040450); } -function setup() { - var eim = em.newInstance("RockSpiritVIP_" + em.getProperty("player")); +function setup(level, lobbyid) { + var eim = em.newInstance("RockSpiritVIP_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + respawn(eim); eim.startEventTimer(timer); return eim; diff --git a/scripts/event/s4aWorld.js b/scripts/event/s4aWorld.js index ea5039fca9..24867aaa3b 100644 --- a/scripts/event/s4aWorld.js +++ b/scripts/event/s4aWorld.js @@ -33,8 +33,9 @@ function getEligibleParty(party) { //selects, from the given party, the tea return eligible; } -function setup() { - var eim = em.newInstance("s4aWorld"); +function setup(level, lobbyid) { + var eim = em.newInstance("s4aWorld_" + lobbyid); + eim.setProperty("level", level); eim.getInstanceMap(910500000).resetPQ(1); respawnStages(eim); diff --git a/scripts/map/onUserEnter/200090000.js b/scripts/map/onUserEnter/200090000.js index 4d6b0e02b4..0a346311d3 100644 --- a/scripts/map/onUserEnter/200090000.js +++ b/scripts/map/onUserEnter/200090000.js @@ -3,12 +3,12 @@ importPackage(Packages.tools); var mapId = 200090000; -function start(pi) { - var map = pi.getClient().getChannelServer().getMapFactory().getMap(mapId); +function start(ms) { + var map = ms.getClient().getChannelServer().getMapFactory().getMap(mapId); if(map.getDocked()) { - pi.getClient().announce(MaplePacketCreator.musicChange("Bgm04/ArabPirate")); - pi.getClient().announce(MaplePacketCreator.crogBoatPacket(true)); + ms.getClient().announce(MaplePacketCreator.musicChange("Bgm04/ArabPirate")); + ms.getClient().announce(MaplePacketCreator.crogBoatPacket(true)); } return(true); diff --git a/scripts/map/onUserEnter/200090010.js b/scripts/map/onUserEnter/200090010.js index 49ebeffc72..bf114d894f 100644 --- a/scripts/map/onUserEnter/200090010.js +++ b/scripts/map/onUserEnter/200090010.js @@ -3,12 +3,12 @@ importPackage(Packages.tools); var mapId = 200090010; -function start(pi) { - var map = pi.getClient().getChannelServer().getMapFactory().getMap(mapId); +function start(ms) { + var map = ms.getClient().getChannelServer().getMapFactory().getMap(mapId); if(map.getDocked()) { - pi.getClient().announce(MaplePacketCreator.musicChange("Bgm04/ArabPirate")); - pi.getClient().announce(MaplePacketCreator.crogBoatPacket(true)); + ms.getClient().announce(MaplePacketCreator.musicChange("Bgm04/ArabPirate")); + ms.getClient().announce(MaplePacketCreator.crogBoatPacket(true)); } return(true); diff --git a/scripts/map/onUserEnter/922000000.js b/scripts/map/onUserEnter/922000000.js index 8286c4f002..8ef2f3647e 100644 --- a/scripts/map/onUserEnter/922000000.js +++ b/scripts/map/onUserEnter/922000000.js @@ -1,5 +1,5 @@ -function start(pi) { - var map = pi.getClient().getChannelServer().getMapFactory().getMap(922000000); +function start(ms) { + var map = ms.getClient().getChannelServer().getMapFactory().getMap(922000000); map.clearDrops(); map.resetReactors(); map.shuffleReactors(); diff --git a/scripts/npc/1052007.js b/scripts/npc/1052007.js index ae31e7b5a8..8636978a81 100644 --- a/scripts/npc/1052007.js +++ b/scripts/npc/1052007.js @@ -25,10 +25,11 @@ function action(mode, type, selection) { } if (status == 1) { if (selection == 0) { - var train = cm.getEventManager("KerningTrain"); - train.newInstance("KerningTrain"); - train.setProperty("player", cm.getPlayer().getName()); - train.startInstance(cm.getPlayer()); + var em = cm.getEventManager("KerningTrain"); + if (!em.startInstance(cm.getPlayer())) { + cm.sendOk("The passenger wagon is already full. Try again a bit later."); + } + cm.dispose(); return; } else if (selection == 1) { diff --git a/scripts/npc/1052125.js b/scripts/npc/1052125.js index 7c93ee15c5..05f5a8d634 100644 --- a/scripts/npc/1052125.js +++ b/scripts/npc/1052125.js @@ -47,10 +47,10 @@ function action(mode, type, selection) { if (status == 0) { if (selection == 0) { if (cm.isQuestStarted(2286) || cm.isQuestStarted(2287) || cm.isQuestStarted(2288)) { - var rock = cm.getEventManager("RockSpirit"); - rock.newInstance("RockSpirit"); - rock.setProperty("player", cm.getPlayer().getName()); - rock.startInstance(cm.getPlayer()); + var em = cm.getEventManager("RockSpirit"); + if (!em.startInstance(cm.getPlayer())) { + cm.sendOk("Uh... It looks like the rooms ahead are a bit crowded right now. Please wait around here for a bit, ok?"); + } cm.dispose(); return; } else { diff --git a/scripts/npc/1061009.js b/scripts/npc/1061009.js index f74d609784..3062c8eb24 100644 --- a/scripts/npc/1061009.js +++ b/scripts/npc/1061009.js @@ -59,13 +59,12 @@ function start() { if (em == null) cm.sendOk("Sorry, but 3rd job advancement (" + js + ") is closed."); else { - if (em.getProperty("noEntry") == "false") { - var eim = em.newInstance("3rdjob_" + js); - eim.registerPlayer(cm.getPlayer()); - } - else { + if (!em.startInstance(cm.getPlayer())) { cm.sendOk("Someone else is already challenging the clone. Please wait until the area is cleared."); } + + cm.dispose(); + return; } } diff --git a/scripts/npc/1061014.js b/scripts/npc/1061014.js index b7c51c4891..ce3cd86ffa 100644 --- a/scripts/npc/1061014.js +++ b/scripts/npc/1061014.js @@ -174,7 +174,7 @@ function action(mode, type, selection) { if (selection > 0) { var banned = expedMembers.get(selection - 1); expedition.ban(banned); - cm.sendOk("You have banned " + banned.getName() + " from the expedition."); + cm.sendOk("You have banned " + banned.getValue() + " from the expedition."); cm.dispose(); } else { cm.sendSimple(list); diff --git a/scripts/npc/1300013.js b/scripts/npc/1300013.js index 561fb5f6e6..ed62c35219 100644 --- a/scripts/npc/1300013.js +++ b/scripts/npc/1300013.js @@ -6,7 +6,7 @@ var status; function start(){ - status = -1; + status = -1; action(1, 0, 0); } @@ -26,6 +26,11 @@ function action(mode, type, selection){ if(cm.getMapId() == 106021402) { + if (!(cm.isQuestCompleted(2331))) { + cm.dispose(); + return; + } + if(status == 0){ cm.sendSimple("#L0#Enter to fight #bKing Pepe#k and #bYeti Brothers#k.#l\r\n#L1#Enter to fight #bPrime Minister#k.#l"); } @@ -48,6 +53,12 @@ function action(mode, type, selection){ } } } else { + var questProgress = cm.getQuestProgress(2330, 3300005) + cm.getQuestProgress(2330, 3300006) + cm.getQuestProgress(2330, 3300007); //3 Yetis + if (!(cm.isQuestStarted(2330) && questProgress < 3)) { // thanks Vcoc for finding an exploit with boss entry through NPC + cm.dispose(); + return; + } + if(status == 0){ cm.sendSimple("#L1#Enter to fight #bKing Pepe#k and #bYeti Brothers#k.#l"); } diff --git a/scripts/npc/2020008.js b/scripts/npc/2020008.js index bfe4b52f82..ce8ab3516e 100644 --- a/scripts/npc/2020008.js +++ b/scripts/npc/2020008.js @@ -140,11 +140,11 @@ function action(mode, type, selection){ } } else { if (cm.getPlayer().getLevel() >= 50){ - cm.sendNext("Ok, go."); + cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else - cm.sendNext("You're weak."); + cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); } } diff --git a/scripts/npc/2020009.js b/scripts/npc/2020009.js index a44bdcf957..5e759803ef 100644 --- a/scripts/npc/2020009.js +++ b/scripts/npc/2020009.js @@ -112,11 +112,11 @@ function action(mode, type, selection){ } } else { if (cm.getPlayer().getLevel() >= 50){ - cm.sendNext("Ok, go."); + cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else - cm.sendNext("You're weak."); + cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); } } diff --git a/scripts/npc/2020010.js b/scripts/npc/2020010.js index 6c51b76405..b4de6203d5 100644 --- a/scripts/npc/2020010.js +++ b/scripts/npc/2020010.js @@ -113,11 +113,11 @@ function action(mode, type, selection){ } } else { if (cm.getPlayer().getLevel() >= 50){ - cm.sendNext("Ok, go."); + cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else - cm.sendNext("You're weak."); + cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); } } diff --git a/scripts/npc/2020011.js b/scripts/npc/2020011.js index 850b295c62..811ca153f2 100644 --- a/scripts/npc/2020011.js +++ b/scripts/npc/2020011.js @@ -112,11 +112,11 @@ function action(mode, type, selection){ } } else { if (cm.getPlayer().getLevel() >= 50){ - cm.sendNext("Ok, go."); + cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else - cm.sendNext("You're weak."); + cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); } } diff --git a/scripts/npc/2020013.js b/scripts/npc/2020013.js index 1521740211..f18199331d 100644 --- a/scripts/npc/2020013.js +++ b/scripts/npc/2020013.js @@ -111,11 +111,11 @@ function action(mode, type, selection){ } } else { if (cm.getPlayer().getLevel() >= 50){ - cm.sendNext("Ok, go."); + cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else - cm.sendNext("You're weak."); + cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); } } diff --git a/scripts/npc/2030013.js b/scripts/npc/2030013.js index ef61b1d339..15af48201b 100644 --- a/scripts/npc/2030013.js +++ b/scripts/npc/2030013.js @@ -176,7 +176,7 @@ function action(mode, type, selection) { if (selection > 0) { var banned = expedMembers.get(selection - 1); expedition.ban(banned); - cm.sendOk("You have banned " + banned.getName() + " from the expedition."); + cm.sendOk("You have banned " + banned.getValue() + " from the expedition."); // getValue, thanks MedicOP for finding this issue cm.dispose(); } else { cm.sendSimple(list); diff --git a/scripts/npc/2030013_old.js b/scripts/npc/2030013_old.js index ae98e3e482..a7ea949fa0 100644 --- a/scripts/npc/2030013_old.js +++ b/scripts/npc/2030013_old.js @@ -78,13 +78,13 @@ function action(mode, type, selection) { if (getEimForString(em,passwd) != null) cm.sendOk("You may not use that password."); else { // start Zakum Battle - var eim = em.newInstance("Zakum" + passwd); - if(!em.startInstance(eim,cm.getPlayer().getName())) { - cm.sendOk("A party in your name is already registered in this instance."); - cm.dispose(); - return; + //var em = cm.getEventManager("Zakum" + passwd); + if (!em.startInstance(cm.getPlayer())) { + cm.sendOk("A party is already registered in this instance."); } - eim.registerPlayer(cm.getPlayer()); + + cm.dispose(); + return; } } if (state == 1) { // Member diff --git a/scripts/npc/2040002.js b/scripts/npc/2040002.js index 12a025f5b0..4e81a20573 100644 --- a/scripts/npc/2040002.js +++ b/scripts/npc/2040002.js @@ -56,8 +56,11 @@ function action(mode, type, selection) { if (status == 1) cm.sendYesNo("Are you ready to enter the dollhouse map?"); else if (status == 2) { - var eim = em.newInstance("DollHouse"); - eim.registerPlayer(cm.getPlayer()); + var em = cm.getEventManager("DollHouse"); + if (!em.startInstance(cm.getPlayer())) { + cm.sendOk("Hmm... The DollHouse is being challenged already, it seems. Try again later."); + } + cm.dispose(); } } diff --git a/scripts/npc/2060005.js b/scripts/npc/2060005.js index 4ff99de789..d9a2009733 100644 --- a/scripts/npc/2060005.js +++ b/scripts/npc/2060005.js @@ -36,11 +36,7 @@ function start() { if (em == null) cm.sendOk("Sorry, but 3rd job advancement (mount) is closed."); else { - if (em.getProperty("noEntry") == "false") { - var eim = em.newInstance("3rdjob_mount"); - eim.registerPlayer(cm.getPlayer()); - } - else { + if (!em.startInstance(cm.getPlayer())) { cm.sendOk("There is currently someone in this map, come back later."); } } diff --git a/scripts/npc/2083004.js b/scripts/npc/2083004.js index 17520857e5..7960c49c4f 100644 --- a/scripts/npc/2083004.js +++ b/scripts/npc/2083004.js @@ -168,7 +168,7 @@ function action(mode, type, selection) { if (selection > 0) { var banned = expedMembers.get(selection - 1); expedition.ban(banned); - cm.sendOk("You have banned " + banned.getName() + " from the expedition."); + cm.sendOk("You have banned " + banned.getValue() + " from the expedition."); cm.dispose(); } else { cm.sendSimple(list); diff --git a/scripts/npc/2090005.js b/scripts/npc/2090005.js index 1c698f0f75..be092366f1 100644 --- a/scripts/npc/2090005.js +++ b/scripts/npc/2090005.js @@ -85,10 +85,14 @@ function action(mode, type, selection) { cm.warp(250000100, 0); cm.dispose(); } else { + var em = cm.getEventManager("Hak"); + if (!em.startInstance(cm.getPlayer())) { + cm.sendOk("Uh... We are currently taking requests from too many maplers right now... Please try again in a bit."); + cm.dispose(); + return; + } + cm.gainMeso(-cost[slct]); - hak.newInstance("Hak"); - hak.setProperty("player", cm.getPlayer().getName()); - hak.startInstance(cm.getPlayer()); cm.dispose(); } } diff --git a/scripts/npc/2141001.js b/scripts/npc/2141001.js index 9772b05adc..e0336c9585 100644 --- a/scripts/npc/2141001.js +++ b/scripts/npc/2141001.js @@ -171,7 +171,7 @@ function action(mode, type, selection) { if (selection > 0) { var banned = expedMembers.get(selection - 1); expedition.ban(banned); - cm.sendOk("You have banned " + banned.getName() + " from the expedition."); + cm.sendOk("You have banned " + banned.getValue() + " from the expedition."); cm.dispose(); } else { cm.sendSimple(list); diff --git a/scripts/npc/9120201.js b/scripts/npc/9120201.js index 4f3b233776..275acfa415 100644 --- a/scripts/npc/9120201.js +++ b/scripts/npc/9120201.js @@ -175,7 +175,7 @@ function action(mode, type, selection) { if (selection > 0) { var banned = expedMembers.get(selection - 1); expedition.ban(banned); - cm.sendOk("You have banned " + banned.getName() + " from the expedition."); + cm.sendOk("You have banned " + banned.getValue() + " from the expedition."); cm.dispose(); } else { cm.sendSimple(list); diff --git a/scripts/npc/9201113.js b/scripts/npc/9201113.js index 4cd1baca90..4c9621d431 100644 --- a/scripts/npc/9201113.js +++ b/scripts/npc/9201113.js @@ -158,7 +158,7 @@ function action(mode, type, selection) { if (selection > 0) { var banned = expedMembers.get(selection - 1); expedition.ban(banned); - cm.sendOk("You have banned " + banned.getName() + " from the expedition."); + cm.sendOk("You have banned " + banned.getValue() + " from the expedition."); cm.dispose(); } else { cm.sendSimple(list); diff --git a/scripts/npc/9270047.js b/scripts/npc/9270047.js index 4d7288d24e..6b3418ca4b 100644 --- a/scripts/npc/9270047.js +++ b/scripts/npc/9270047.js @@ -175,7 +175,7 @@ function action(mode, type, selection) { if (selection > 0) { var banned = expedMembers.get(selection - 1); expedition.ban(banned); - cm.sendOk("You have banned " + banned.getName() + " from the expedition."); + cm.sendOk("You have banned " + banned.getValue() + " from the expedition."); cm.dispose(); } else { cm.sendSimple(list); diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js index e3c07313a7..9591063262 100644 --- a/scripts/npc/9977777.js +++ b/scripts/npc/9977777.js @@ -48,6 +48,7 @@ function writeFeatureTab_PQs() { addFeature("Mu Lung Dojo."); addFeature("Capt. Latanica with party fighting the boss."); addFeature("Filled up missing obligatory event script methods."); + addFeature("Secured uniquety of active lobby-name instances."); } function writeFeatureTab_Skills() { @@ -126,7 +127,9 @@ function writeFeatureTab_MonstersMapsReactors() { addFeature("Mobs now can drop more than one of the same equip."); addFeature("Mobs only drop items collectable by the player/party."); addFeature("Mobs shouldn't fall from foothold too often now."); + addFeature("Puppets holds targeted mobs nearby on new aggro feat."); addFeature("Properly applying MP cost on non-skill mob moves."); + addFeature("Limited underling mob spawns."); addFeature("Implemented mob banish by touch & skill move."); addFeature("Redesigned HT mechanics: assemble & dmg taken."); addFeature("Implemented Zombify disease status."); @@ -253,10 +256,12 @@ function writeFeatureTab_Project() { addFeature("Improved login phase, using cache over DB queries."); addFeature("Protected many flaws with login management system."); addFeature("Developed a robust anti-exploit login coordinator."); + addFeature("Revised uniqueness aspect of logged in accounts."); 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("Remodeled item scripts, properly using NPC dialogs."); 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/portal/Depart_ToKerning.js b/scripts/portal/Depart_ToKerning.js index fb775ffb92..213f7aa04f 100644 --- a/scripts/portal/Depart_ToKerning.js +++ b/scripts/portal/Depart_ToKerning.js @@ -1,8 +1,10 @@ function enter(pi) { + var em = pi.getEventManager("KerningTrain"); + if (!em.startInstance(pi.getPlayer())) { + pi.message("The passenger wagon is already full. Try again a bit later."); + return false; + } + pi.playPortalSound(); - var train = pi.getEventManager("KerningTrain"); - train.newInstance("KerningTrain"); - train.setProperty("player", pi.getPlayer().getName()); - train.startInstance(pi.getPlayer()); return true; } \ No newline at end of file diff --git a/scripts/portal/enterRider.js b/scripts/portal/enterRider.js index 6d654efb73..225c2e42a2 100644 --- a/scripts/portal/enterRider.js +++ b/scripts/portal/enterRider.js @@ -6,14 +6,13 @@ function enter(pi) { return false; } else { - if (em.getProperty("noEntry") == "false") { - var eim = em.newInstance("Aran_2ndmount"); - eim.registerPlayer(pi.getPlayer()); - return true; - } - else { + var em = pi.getEventManager("Aran_2ndmount"); + if (!em.startInstance(pi.getPlayer())) { pi.message("There is currently someone in this map, come back later."); return false; + } else { + pi.playPortalSound(); + return true; } } } else { diff --git a/scripts/portal/q2073.js b/scripts/portal/q2073.js index 598147147f..e1af259317 100644 --- a/scripts/portal/q2073.js +++ b/scripts/portal/q2073.js @@ -28,6 +28,8 @@ function enter(pi) { pi.playPortalSound(); pi.warp(900000000, 0); return true; - } - return false; + } else { + pi.message("Private property. This place can only be entered when running an errand from Camila."); + return false; + } } \ No newline at end of file diff --git a/scripts/portal/rankRoom.js b/scripts/portal/rankRoom.js index 812d6ae5b8..c112ece152 100644 --- a/scripts/portal/rankRoom.js +++ b/scripts/portal/rankRoom.js @@ -20,6 +20,9 @@ function enter(pi) { case 100000201: pi.warp(100000204, 2); //or 100000205 break; + case 101000003: // portal warp fix thanks to Vcoc + pi.warp(101000004, 2); //or 101000005 + break; default: pi.warp(pi.getMapId() + 1, 1); //or + 2 break; diff --git a/scripts/quest/21401.js b/scripts/quest/21401.js index f078a0915b..0e52708254 100644 --- a/scripts/quest/21401.js +++ b/scripts/quest/21401.js @@ -22,13 +22,13 @@ function start(mode, type, selection) { } else if (status == 3) { qm.sendAcceptDecline("Please, Aran. Please stop me from becoming enraged. Only you can control me. It's getting out of my hands now. Please do whatever it takes to #rstop me from going berserk#k!"); } else if (status == 4) { - qm.startQuest(); - - var mb = qm.getEventManager("MahaBattle"); - mb.newInstance("MahaBattle"); - mb.setProperty("player", qm.getPlayer().getName()); - mb.startInstance(qm.getPlayer()); - + var em = qm.getEventManager("MahaBattle"); + if (!em.startInstance(qm.getPlayer())) { + qm.sendOk("There is currently someone in this map, come back later."); + } else { + qm.startQuest(); + } + qm.dispose(); } } diff --git a/scripts/quest/21613.js b/scripts/quest/21613.js index b0d80f11bb..cc99db89f4 100644 --- a/scripts/quest/21613.js +++ b/scripts/quest/21613.js @@ -48,17 +48,14 @@ function start(mode, type, selection) { return; } else { - if (em.getProperty("noEntry") == "false") { - var eim = em.newInstance("Aran_3rdmount"); - eim.registerPlayer(qm.getPlayer()); - - qm.forceStartQuest(); - qm.dispose(); - } - else { + var em = qm.getEventManager("Aran_3rdmount"); + if (!em.startInstance(qm.getPlayer())) { qm.sendOk("There is currently someone in this map, come back later."); - qm.dispose(); + } else { + qm.forceStartQuest(); } + + qm.dispose(); } } } diff --git a/scripts/quest/2245.js b/scripts/quest/2245.js index c14537e296..f5d503c153 100644 --- a/scripts/quest/2245.js +++ b/scripts/quest/2245.js @@ -42,17 +42,14 @@ function start(mode, type, selection) { return; } - if (em.getProperty("noEntry") == "false") { - var eim = em.newInstance("BalrogQuest"); - eim.registerPlayer(qm.getPlayer()); - eim.startEvent(); - - qm.dispose(); - } - else { + var em = qm.getEventManager("BalrogQuest"); + if (!em.startInstance(qm.getPlayer())) { qm.sendOk("There is currently someone in this map, come back later."); - qm.dispose(); + } else { + qm.forceStartQuest(); } + + qm.dispose(); } } } diff --git a/scripts/quest/2291.js b/scripts/quest/2291.js index 48b8daa124..d9d6588f57 100644 --- a/scripts/quest/2291.js +++ b/scripts/quest/2291.js @@ -43,13 +43,14 @@ function end(mode, type, selection) { qm.sendNext("You got the #b#i4032521##k with you, great. Let me show you the way."); } else if (status == 1) { + var em = qm.getEventManager("RockSpiritVIP"); + if (!em.startInstance(qm.getPlayer())) { + qm.sendOk("Uh... It looks like the rooms ahead are a bit crowded right now. Please wait around here for a bit, ok?"); + qm.dispose(); + return; + } + qm.gainItem(4032521, -10); - - var rock = qm.getEventManager("RockSpiritVIP"); - rock.newInstance("RockSpiritVIP"); - rock.setProperty("player", qm.getPlayer().getName()); - rock.startInstance(qm.getPlayer()); - qm.forceCompleteQuest(); qm.dispose(); } diff --git a/scripts/quest/3714.js b/scripts/quest/3714.js index c724911816..7868cb80bc 100644 --- a/scripts/quest/3714.js +++ b/scripts/quest/3714.js @@ -41,9 +41,22 @@ function start(mode, type, selection) { return; } - qm.sendNext("You have brought a #b#t4001094##k, thank you for the effort!"); + if (qm.haveItem(2041200, 1)) { + qm.sendOk("(The #b#t2041200##k in my bag has grown brighter since reaching this place... Noticing again, the young dragon over there seems to be glaring bitterly towards it.)"); + qm.dispose(); + return; + } + + qm.sendNext("You have brought a #b#t4001094##k, thank you for retrieving one more of my kin to the nest! Please have this...\r\n\r\n....... (bleuuhnuhgh) (blahrgngnhhng) ...\r\n\r\nehh, #b#t2041200##k as a token of my kin's gratitude. And do a favor for us, please, get that thing out of here..."); } else if (status == 1) { + if (!qm.canHold(2041200, 1)) { + qm.sendOk("Please make a room on your USE inventory to receive the reward."); + qm.dispose(); + return; + } + qm.gainItem(4001094, -1); + qm.gainItem(2041200, 1); // quest not rewarding properly found thanks to MedicOP & Thora qm.gainExp(42000); qm.forceCompleteQuest(); diff --git a/sql/db_database.sql b/sql/db_database.sql index aae28570c9..ca9b171565 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -16458,6 +16458,7 @@ CREATE TABLE IF NOT EXISTS `pets` ( `closeness` int(10) unsigned NOT NULL, `fullness` int(10) unsigned NOT NULL, `summoned` tinyint(1) NOT NULL DEFAULT '0', + `flag` int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`petid`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index 100c2eb355..7645da228c 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -66,6 +66,7 @@ import net.server.world.World; import scripting.event.EventInstanceManager; import server.CashShop; import server.MapleItemInformationProvider; +import server.MapleItemInformationProvider.ScriptedItem; import server.MaplePortal; import server.MapleShop; import server.MapleStatEffect; @@ -287,6 +288,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private AutobanManager autoban; private boolean isbanned = false; private boolean blockCashShop = false; + private boolean allowExpGain = false; private byte pendantExp = 0, lastmobcount = 0, doorSlot = -1; private List trockmaps = new ArrayList<>(); private List viptrockmaps = new ArrayList<>(); @@ -649,6 +651,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public void addSummon(int id, MapleSummon summon) { summons.put(id, summon); + + if (summon.isPuppet()) { + map.addPlayerPuppet(this); + } } public void addVisibleMapObject(MapleMapObject mo) { @@ -805,6 +811,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public void toggleBlockCashShop() { blockCashShop = !blockCashShop; } + + public void toggleExpGain() { + allowExpGain = !allowExpGain; + } public void setClient(MapleClient c) { this.client = c; @@ -1728,6 +1738,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + public Collection getControlledMonsters() { + cpnLock.lock(); + try { + return new ArrayList<>(controlled); + } finally { + cpnLock.unlock(); + } + } + public void releaseControlledMonsters() { Collection controlledMonsters; @@ -1799,7 +1818,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { mpcs = getPartyMembersOnSameMap(); } - String itemScriptName = null; + ScriptedItem itemScript = null; mapitem.lockItem(); try { if (mapitem.isPickedUp()) { @@ -1872,9 +1891,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { this.gainMeso(mapitem.getMeso(), true, true, false); } } else if (mItem.getItemId() / 10000 == 243) { - MapleItemInformationProvider.scriptedItem info = ii.getScriptedItemInfo(mItem.getItemId()); - if (info.runOnPickup()) { - itemScriptName = info.getScript(); + ScriptedItem info = ii.getScriptedItemInfo(mItem.getItemId()); + if (info != null && info.runOnPickup()) { + itemScript = info; } else { if (!MapleInventoryManipulator.addFromDrop(client, mItem, true)) { client.announce(MaplePacketCreator.enableActions()); @@ -1908,11 +1927,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { mapitem.unlockItem(); } - if (itemScriptName != null) { + if (itemScript != null) { ItemScriptManager ism = ItemScriptManager.getInstance(); - if (ism.scriptExists(itemScriptName)) { - ism.runItemScript(client, itemScriptName); - } + ism.runItemScript(client, itemScript); } } client.announce(MaplePacketCreator.enableActions()); @@ -1927,14 +1944,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean canHold(int itemid, int quantity) { - int hold = getCleanItemQuantity(itemid, false); - - if(hold > 0) { - if(hold + quantity <= ii.getSlotMax(client, itemid)) - return true; - } - - return getInventory(ItemConstants.getInventoryType(itemid)).getNextFreeSlot() > -1; + return client.getAbstractPlayerInteraction().canHold(itemid, quantity); } public boolean isRidingBattleship() { @@ -2907,7 +2917,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { 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()) { + if (level < getMaxLevel() && (allowExpGain || this.getEventInstance() != null)) { long leftover = 0; long nextExp = exp.get() + total; @@ -3464,9 +3474,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { getMap().broadcastMessage(MaplePacketCreator.removeSummon(summon, true), summon.getPosition()); getMap().removeMapObject(summon); removeVisibleMapObject(summon); + summons.remove(summonId); - - if (summon.getSkill() == DarkKnight.BEHOLDER) { + if (summon.isPuppet()) { + map.removePlayerPuppet(this); + } else if (summon.getSkill() == DarkKnight.BEHOLDER) { if (beholderHealingSchedule != null) { beholderHealingSchedule.cancel(false); beholderHealingSchedule = null; @@ -4602,19 +4614,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public int getItemQuantity(int itemid, boolean checkEquipped) { - int possesed = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countById(itemid); + int count = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countById(itemid); if (checkEquipped) { - possesed += inventory[MapleInventoryType.EQUIPPED.ordinal()].countById(itemid); + count += inventory[MapleInventoryType.EQUIPPED.ordinal()].countById(itemid); } - return possesed; + return count; } public int getCleanItemQuantity(int itemid, boolean checkEquipped) { - int possesed = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countNotOwnedById(itemid); + int count = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countNotOwnedById(itemid); if (checkEquipped) { - possesed += inventory[MapleInventoryType.EQUIPPED.ordinal()].countNotOwnedById(itemid); + count += inventory[MapleInventoryType.EQUIPPED.ordinal()].countNotOwnedById(itemid); } - return possesed; + return count; } public MapleJob getJob() { @@ -6600,13 +6612,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { rs = ps.executeQuery(); while (rs.next()) { String name = rs.getString("name"); - if (rs.getString("name").equals("rescueGaga")) { + if (rs.getString("name").contentEquals("rescueGaga")) { ret.events.put(name, new RescueGaga(rs.getInt("info"))); } - //ret.events = new MapleEvents(new RescueGaga(rs.getInt("rescuegaga")), new ArtifactHunt(rs.getInt("artifacthunt"))); - } - if (!ret.events.containsKey("rescueGaga")) { - ret.events.put("rescueGaga", new RescueGaga(0)); } rs.close(); ps.close(); @@ -6988,13 +6996,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void respawn(EventInstanceManager eim, int returnMap) { - cancelAllBuffs(false); - - updateHp(50); - setStance(0); - if (eim != null) eim.unregisterPlayer(this); // some event scripts uses this... changeMap(returnMap); + + cancelAllBuffs(false); // thanks Oblivium91 for finding out players still could revive in area and take damage before returning to town + updateHp(50); + setStance(0); } private void prepareDragonBlood(final MapleStatEffect bloodEffect) { @@ -7514,6 +7521,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { this.getInventory(itEntry.getRight()).addItem(itEntry.getLeft()); } + this.events.put("rescueGaga", new RescueGaga(0)); + Connection con = null; PreparedStatement ps = null; @@ -9733,7 +9742,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject { events = null; mpc = null; mgc = null; - events = null; party = null; family = null; diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java index 4e5acbe135..9fdf031b53 100644 --- a/src/client/MapleClient.java +++ b/src/client/MapleClient.java @@ -517,11 +517,15 @@ public class MapleClient { } public int login(String login, String pwd, String nibbleHwid) { - loginattempt++; - if (loginattempt > 4) { - MapleSessionCoordinator.getInstance().closeSession(session, false); - } int loginok = 5; + + loginattempt++; + if (loginattempt > 4) { + loggedIn = false; + MapleSessionCoordinator.getInstance().closeSession(session, false); + return loginok; + } + Connection con = null; PreparedStatement ps = null; ResultSet rs = null; @@ -748,7 +752,12 @@ public class MapleClient { return accId; } - public void updateLoginState(int newstate) { + public void updateLoginState(int newstate) { + // rules out possibility of multiple account entries + if (newstate == LOGIN_LOGGEDIN) { + MapleSessionCoordinator.getInstance().updateOnlineSession(this.getSession()); + } + try { Connection con = DatabaseConnection.getConnection(); try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = ? WHERE id = ?")) { @@ -763,6 +772,7 @@ public class MapleClient { } catch (SQLException e) { e.printStackTrace(); } + if (newstate == LOGIN_NOTLOGGEDIN) { loggedIn = false; serverTransition = false; @@ -797,9 +807,6 @@ public class MapleClient { MapleSessionCoordinator.getInstance().closeSession(session, null); updateLoginState(LOGIN_NOTLOGGEDIN); } - } else if (state == LOGIN_LOGGEDIN && player == null) { - state = LOGIN_LOGGEDIN; - updateLoginState(LOGIN_LOGGEDIN); } rs.close(); ps.close(); @@ -1006,8 +1013,7 @@ public class MapleClient { Server.getInstance().unregisterLoginState(this); - this.session.setAttribute(MapleClient.CLIENT_KEY, null); - this.accountName = null; + this.accountName = null; this.macs = null; this.hwid = null; this.birthday = null; diff --git a/src/client/command/CommandsExecutor.java b/src/client/command/CommandsExecutor.java index 4ae2f7dedf..966809824d 100644 --- a/src/client/command/CommandsExecutor.java +++ b/src/client/command/CommandsExecutor.java @@ -193,6 +193,7 @@ public class CommandsExecutor { addCommand("int", StatIntCommand.class); addCommand("luk", StatLukCommand.class); addCommand("enableauth", EnableAuthCommand.class); + addCommand("toggleexp", ToggleExpCommand.class); commandsNameDesc.add(levelCommandsCursor); } diff --git a/src/client/command/commands/gm0/ToggleExpCommand.java b/src/client/command/commands/gm0/ToggleExpCommand.java new file mode 100644 index 0000000000..d7e6ab5e0f --- /dev/null +++ b/src/client/command/commands/gm0/ToggleExpCommand.java @@ -0,0 +1,44 @@ +/* + This file is part of the HeavenMS MapleStory Server, commands OdinMS-based + 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 . +*/ + +/* + @Author: Ronan +*/ +package client.command.commands.gm0; + +import client.command.Command; +import client.MapleClient; + +public class ToggleExpCommand extends Command { + { + setDescription(""); + } + + @Override + public void execute(MapleClient c, String[] params) { + if (c.tryacquireClient()) { + try { + c.getPlayer().toggleExpGain(); // Vcoc's idea + } finally { + c.releaseClient(); + } + } + } +} diff --git a/src/client/command/commands/gm1/GotoCommand.java b/src/client/command/commands/gm1/GotoCommand.java index 3e1a816449..72445d122b 100644 --- a/src/client/command/commands/gm1/GotoCommand.java +++ b/src/client/command/commands/gm1/GotoCommand.java @@ -46,12 +46,19 @@ public class GotoCommand extends Command { player.yellowMessage("Syntax: @goto "); return; } - - if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit()) || !player.isAlive()) { - player.dropMessage(1, "This command can not be used in this map."); + + if (!player.isAlive()) { + player.dropMessage(1, "This command cannot be used when you're dead."); return; } + if (!player.isGM()) { + if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) { + player.dropMessage(1, "This command can not be used in this map."); + return; + } + } + HashMap gotomaps; if (player.isGM()) { gotomaps = new HashMap<>(GameConstants.GOTO_AREAS); // distinct map registry for GM/users suggested thanks to Vcoc diff --git a/src/client/command/commands/gm2/WarpCommand.java b/src/client/command/commands/gm2/WarpCommand.java index ff03a70f15..80a6807275 100644 --- a/src/client/command/commands/gm2/WarpCommand.java +++ b/src/client/command/commands/gm2/WarpCommand.java @@ -50,11 +50,18 @@ public class WarpCommand extends Command { return; } - if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit()) || !player.isAlive()) { - player.dropMessage(1, "This command cannot be used in this map."); + if (!player.isAlive()) { + player.dropMessage(1, "This command cannot be used when you're dead."); return; } + if (!player.isGM()) { + if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) { + player.dropMessage(1, "This command cannot be used in this map."); + return; + } + } + // expedition issue with this command detected thanks to Masterrulax player.saveLocationOnWarp(); player.changeMap(target, target.getRandomPlayerSpawnpoint()); diff --git a/src/client/command/commands/gm3/FaceCommand.java b/src/client/command/commands/gm3/FaceCommand.java index 48bc450f35..64c52c03be 100644 --- a/src/client/command/commands/gm3/FaceCommand.java +++ b/src/client/command/commands/gm3/FaceCommand.java @@ -27,6 +27,7 @@ import client.MapleStat; import client.command.Command; import client.MapleClient; import client.MapleCharacter; +import constants.ItemConstants; import server.MapleItemInformationProvider; public class FaceCommand extends Command { @@ -45,7 +46,7 @@ public class FaceCommand extends Command { try { if (params.length == 1) { int itemId = Integer.parseInt(params[0]); - if (!(itemId >= 20000 && itemId < 22000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { + if (!ItemConstants.isFace(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { player.yellowMessage("Face id '" + params[0] + "' does not exist."); return; } @@ -55,7 +56,7 @@ public class FaceCommand extends Command { player.equipChanged(); } else { int itemId = Integer.parseInt(params[1]); - if (!(itemId >= 20000 && itemId < 22000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { + if (!ItemConstants.isFace(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { player.yellowMessage("Face id '" + params[1] + "' does not exist."); } diff --git a/src/client/command/commands/gm3/HairCommand.java b/src/client/command/commands/gm3/HairCommand.java index 489e136cc3..3bc17f3566 100644 --- a/src/client/command/commands/gm3/HairCommand.java +++ b/src/client/command/commands/gm3/HairCommand.java @@ -27,6 +27,7 @@ import client.MapleStat; import client.command.Command; import client.MapleClient; import client.MapleCharacter; +import constants.ItemConstants; import server.MapleItemInformationProvider; public class HairCommand extends Command { @@ -45,7 +46,7 @@ public class HairCommand extends Command { try { if (params.length == 1) { int itemId = Integer.parseInt(params[0]); - if (!(itemId >= 30000 && itemId < 35000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { + if (!ItemConstants.isHair(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { player.yellowMessage("Hair id '" + params[0] + "' does not exist."); return; } @@ -55,7 +56,7 @@ public class HairCommand extends Command { player.equipChanged(); } else { int itemId = Integer.parseInt(params[1]); - if (!(itemId >= 30000 && itemId < 35000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { + if (!ItemConstants.isHair(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { player.yellowMessage("Hair id '" + params[1] + "' does not exist."); return; } diff --git a/src/client/inventory/MaplePet.java b/src/client/inventory/MaplePet.java index 9d02702184..95cc8bb771 100644 --- a/src/client/inventory/MaplePet.java +++ b/src/client/inventory/MaplePet.java @@ -52,6 +52,21 @@ public class MaplePet extends Item { private Point pos; private int stance; private boolean summoned; + private int petFlag = 0; + + public enum PetFlag { + OWNER_SPEED(0x01); + + private int i; + + private PetFlag(int i) { + this.i = i; + } + + public int getValue() { + return i; + } + } private MaplePet(int id, short position, int uniqueid) { super(id, position, (short) 1); @@ -63,7 +78,7 @@ public class MaplePet extends Item { try { MaplePet ret = new MaplePet(itemid, position, petid); Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT name, level, closeness, fullness, summoned FROM pets WHERE petid = ?"); // Get pet details.. + PreparedStatement ps = con.prepareStatement("SELECT name, level, closeness, fullness, summoned, flag FROM pets WHERE petid = ?"); // Get pet details.. ps.setInt(1, petid); ResultSet rs = ps.executeQuery(); rs.next(); @@ -72,6 +87,7 @@ public class MaplePet extends Item { ret.setLevel((byte) Math.min(rs.getByte("level"), 30)); ret.setFullness(Math.min(rs.getInt("fullness"), 100)); ret.setSummoned(rs.getInt("summoned") == 1); + ret.setPetFlag(rs.getInt("flag")); rs.close(); ps.close(); con.close(); @@ -108,13 +124,14 @@ public class MaplePet extends Item { public void saveToDb() { try { Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE pets SET name = ?, level = ?, closeness = ?, fullness = ?, summoned = ? WHERE petid = ?"); + PreparedStatement ps = con.prepareStatement("UPDATE pets SET name = ?, level = ?, closeness = ?, fullness = ?, summoned = ?, flag = ? WHERE petid = ?"); ps.setString(1, getName()); ps.setInt(2, getLevel()); ps.setInt(3, getCloseness()); ps.setInt(4, getFullness()); ps.setInt(5, isSummoned() ? 1 : 0); - ps.setInt(6, getUniqueId()); + ps.setInt(6, getPetFlag()); + ps.setInt(7, getUniqueId()); ps.executeUpdate(); ps.close(); con.close(); @@ -126,7 +143,7 @@ public class MaplePet extends Item { public static int createPet(int itemid) { try { Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned) VALUES (?, ?, 1, 0, 100, 0)"); + PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned, flag) VALUES (?, ?, 1, 0, 100, 0, 0)"); int ret = MapleCashidGenerator.generateCashId(); ps.setInt(1, ret); ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid)); @@ -143,7 +160,7 @@ public class MaplePet extends Item { public static int createPet(int itemid, byte level, int closeness, int fullness) { try { Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned) VALUES (?, ?, ?, ?, ?, 0)"); + PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned, flag) VALUES (?, ?, ?, ?, ?, 0, 0)"); int ret = MapleCashidGenerator.generateCashId(); ps.setInt(1, ret); ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid)); @@ -274,6 +291,32 @@ public class MaplePet extends Item { public void setSummoned(boolean yes) { this.summoned = yes; } + + public int getPetFlag() { + return this.petFlag; + } + + private void setPetFlag(int flag) { + this.petFlag = flag; + } + + public void addPetFlag(MapleCharacter owner, PetFlag flag) { + this.petFlag |= flag.getValue(); + saveToDb(); + + Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition()); + if (petz != null) + owner.forceUpdateItem(petz); + } + + public void removePetFlag(MapleCharacter owner, PetFlag flag) { + this.petFlag &= 0xFFFFFFFF ^ flag.getValue(); + saveToDb(); + + Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition()); + if (petz != null) + owner.forceUpdateItem(petz); + } public Pair canConsume(int itemId) { return MapleItemInformationProvider.getInstance().canPetConsume(this.getItemId(), itemId); diff --git a/src/client/inventory/manipulator/MapleKarmaManipulator.java b/src/client/inventory/manipulator/MapleKarmaManipulator.java index ec08f9d507..93e0874dda 100644 --- a/src/client/inventory/manipulator/MapleKarmaManipulator.java +++ b/src/client/inventory/manipulator/MapleKarmaManipulator.java @@ -53,6 +53,7 @@ public class MapleKarmaManipulator { int flag = item.getFlag(); flag |= karmaFlag; + flag &= (0xFFFFFFFF ^ ItemConstants.UNTRADEABLE); item.setFlag((byte) flag); } } diff --git a/src/client/processor/MakerProcessor.java b/src/client/processor/MakerProcessor.java index 7c913bebcf..e08d7d97d3 100644 --- a/src/client/processor/MakerProcessor.java +++ b/src/client/processor/MakerProcessor.java @@ -218,7 +218,7 @@ public class MakerProcessor { Map reagentType = new LinkedHashMap<>(); List toRemove = new LinkedList<>(); - boolean isWeapon = ItemConstants.isWeapon(toCreate) || ServerConstants.USE_MAKER_PERMISSIVE_ATKUP; + boolean isWeapon = ItemConstants.isWeapon(toCreate) || ServerConstants.USE_MAKER_PERMISSIVE_ATKUP; // thanks Vcoc for finding a case where a weapon wouldn't be counted as such due to a bounding on isWeapon for(Map.Entry r : reagentids.entrySet()) { int curRid = r.getKey(); diff --git a/src/constants/ItemConstants.java b/src/constants/ItemConstants.java index a9a5372cd6..d1624daf66 100644 --- a/src/constants/ItemConstants.java +++ b/src/constants/ItemConstants.java @@ -217,7 +217,7 @@ public final class ItemConstants { } public static boolean isWeapon(int itemId) { - return itemId >= 1302000 && itemId < 1492024; + return itemId >= 1302000 && itemId < 1493000; } public static boolean isEquipment(int itemId) { @@ -235,4 +235,12 @@ public final class ItemConstants { public static boolean isWeddingToken(int itemId) { return itemId >= 4031357 && itemId <= 4031364; } + + public static boolean isFace(int itemId) { + return itemId >= 20000 && itemId < 22000; + } + + public static boolean isHair(int itemId) { + return itemId >= 30000 && itemId < 35000; + } } diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index 90217e1d8a..d726b8f826 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -149,7 +149,7 @@ public class ServerConstants { public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values). public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions. public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 20; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled. - public static final byte MIN_UNDERLEVEL_TO_EXP_LEECH = 40; //Characters are unable to leech EXP from party member kills whose level difference are past this limit. + public static final byte MIN_RANGELEVEL_TO_EXP_LEECH = 40; //Characters are unable to leech EXP from party member kills whose level difference are past this limit. public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires. public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner. public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system. diff --git a/src/net/MapleServerHandler.java b/src/net/MapleServerHandler.java index 75996a8052..484f0699ac 100644 --- a/src/net/MapleServerHandler.java +++ b/src/net/MapleServerHandler.java @@ -88,8 +88,8 @@ public class MapleServerHandler extends IoHandlerAdapter { @Override public void exceptionCaught(IoSession session, Throwable cause) { - if (cause instanceof IOException) { // mina closes session automatically and does not call sessionClosed on IOException - session.closeNow(); // calls sessionClosed + if (cause instanceof IOException) { + closeMapleSession(session); } else { MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); @@ -137,8 +137,7 @@ public class MapleServerHandler extends IoHandlerAdapter { session.setAttribute(MapleClient.CLIENT_KEY, client); } - @Override - public void sessionClosed(IoSession session) throws Exception { + private void closeMapleSession(IoSession session) { if (isLoginServerHandler()) { MapleSessionCoordinator.getInstance().closeLoginSession(session); } else { @@ -157,10 +156,15 @@ public class MapleServerHandler extends IoHandlerAdapter { FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, t); } finally { session.close(); - session.removeAttribute(MapleClient.CLIENT_KEY); + session.removeAttribute(MapleClient.CLIENT_KEY); //client.empty(); } } + } + + @Override + public void sessionClosed(IoSession session) throws Exception { + closeMapleSession(session); super.sessionClosed(session); } diff --git a/src/net/server/Server.java b/src/net/server/Server.java index cc2c71591e..ca0bdbbc9c 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -86,6 +86,7 @@ import constants.ItemConstants; import constants.GameConstants; import constants.ServerConstants; import java.util.TimeZone; +import net.server.coordinator.MapleSessionCoordinator; import server.CashShop.CashItemFactory; import server.MapleSkillbookInformationProvider; import server.ThreadManager; @@ -1693,7 +1694,7 @@ public class Server { if(c.isLoggedIn()) { c.disconnect(false, false); } else { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); } } } @@ -1766,8 +1767,13 @@ public class Server { System.out.println("Worlds + Channels are offline."); acceptor.unbind(); acceptor = null; - if (!restart) { - System.exit(0); + if (!restart) { // shutdown hook deadlocks if System.exit() method is used within its body chores, thanks MIKE for pointing that out + new Thread(new Runnable() { + @Override + public void run() { + System.exit(0); + } + }).start(); } else { System.out.println("\r\nRestarting the server....\r\n"); try { diff --git a/src/net/server/channel/handlers/AllianceOperationHandler.java b/src/net/server/channel/handlers/AllianceOperationHandler.java index 4824614f04..d514b5d3fe 100644 --- a/src/net/server/channel/handlers/AllianceOperationHandler.java +++ b/src/net/server/channel/handlers/AllianceOperationHandler.java @@ -130,7 +130,7 @@ public final class AllianceOperationHandler extends AbstractMaplePacketHandler { chr.saveGuildStatus(); Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.addGuildToAlliance(alliance, guildid, c), -1, -1); - Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.updateAllianceInfo(alliance, c), -1, -1); + Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.updateAllianceInfo(alliance, c.getWorld()), -1, -1); Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.allianceNotice(alliance.getId(), alliance.getNotice()), -1, -1); chr.getGuild().dropMessage("Your guild has joined the [" + alliance.getName() + "] union."); diff --git a/src/net/server/channel/handlers/CharInfoRequestHandler.java b/src/net/server/channel/handlers/CharInfoRequestHandler.java index f97ec9408b..6454a9e256 100644 --- a/src/net/server/channel/handlers/CharInfoRequestHandler.java +++ b/src/net/server/channel/handlers/CharInfoRequestHandler.java @@ -29,6 +29,8 @@ import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; public final class CharInfoRequestHandler extends AbstractMaplePacketHandler { + + @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { slea.skip(4); int cid = slea.readInt(); diff --git a/src/net/server/channel/handlers/DamageSummonHandler.java b/src/net/server/channel/handlers/DamageSummonHandler.java index 7acc6ddb72..ae54da5444 100644 --- a/src/net/server/channel/handlers/DamageSummonHandler.java +++ b/src/net/server/channel/handlers/DamageSummonHandler.java @@ -24,7 +24,6 @@ package net.server.channel.handlers; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleClient; -import client.SkillFactory; import net.AbstractMaplePacketHandler; import server.maps.MapleSummon; import server.maps.MapleMapObject; diff --git a/src/net/server/channel/handlers/PlayerInteractionHandler.java b/src/net/server/channel/handlers/PlayerInteractionHandler.java index 8e06663fea..0f74a86b60 100644 --- a/src/net/server/channel/handlers/PlayerInteractionHandler.java +++ b/src/net/server/channel/handlers/PlayerInteractionHandler.java @@ -25,6 +25,7 @@ import client.MapleCharacter; import client.MapleClient; import client.autoban.AutobanFactory; import client.inventory.Item; +import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; @@ -465,22 +466,41 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { } else if (mode == Action.SET_ITEMS.getCode()) { MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); MapleInventoryType ivType = MapleInventoryType.getByType(slea.readByte()); - Item item = chr.getInventory(ivType).getItem(slea.readShort()); + short pos = slea.readShort(); + Item item = chr.getInventory(ivType).getItem(pos); short quantity = slea.readShort(); byte targetSlot = slea.readByte(); + if (targetSlot < 1 || targetSlot > 9) { + System.out.println("[h4x] " + chr.getName() + " Trying to dupe on trade slot."); + c.announce(MaplePacketCreator.enableActions()); + return; + } + if (item == null) { c.announce(MaplePacketCreator.serverNotice(1, "Invalid item description.")); c.announce(MaplePacketCreator.enableActions()); return; } - + + if(ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH && ii.isCash(item.getItemId())) { + c.announce(MaplePacketCreator.serverNotice(1, "Cash items are not allowed to be traded.")); + return; + } + + if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(item.getItemId())) { + c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be traded.")); + return; + } + if (quantity < 1 || quantity > item.getQuantity()) { c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough quantity of the item.")); c.announce(MaplePacketCreator.enableActions()); return; } - if (chr.getTrade() != null) { + + MapleTrade trade = chr.getTrade(); + if (trade != null) { if ((quantity <= item.getQuantity() && quantity >= 0) || ItemConstants.isRechargeable(item.getItemId())) { if (ii.isDropRestricted(item.getItemId())) { // ensure that undroppable items do not make it to the trade window if (!MapleKarmaManipulator.hasKarmaFlag(item)) { @@ -489,16 +509,38 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { return; } } - Item tradeItem = item.copy(); - if (ItemConstants.isRechargeable(item.getItemId())) { - tradeItem.setQuantity(item.getQuantity()); - MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), item.getQuantity(), true); - } else { + + MapleInventory inv = chr.getInventory(ivType); + inv.lockInventory(); + try { + Item checkItem = chr.getInventory(ivType).getItem(pos); + if (checkItem != item || checkItem.getPosition() != item.getPosition()) { + c.announce(MaplePacketCreator.serverNotice(1, "Invalid item description.")); + c.announce(MaplePacketCreator.enableActions()); + return; + } + + Item tradeItem = item.copy(); + if (ItemConstants.isRechargeable(item.getItemId())) { + quantity = item.getQuantity(); + } + tradeItem.setQuantity(quantity); - MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), quantity, true); + tradeItem.setPosition(targetSlot); + + if (trade.addItem(tradeItem)) { + MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), quantity, true); + + trade.getChr().announce(MaplePacketCreator.getTradeItemAdd((byte) 0, tradeItem)); + if (trade.getPartner() != null) { + trade.getPartner().getChr().announce(MaplePacketCreator.getTradeItemAdd((byte) 1, tradeItem)); + } + } + } catch (Exception e) { + FilePrinter.printError(FilePrinter.TRADE_EXCEPTION, e, "Player '" + chr + "' tried to add " + ii.getName(item.getItemId()) + " qty. " + item.getQuantity() + " in trade (slot " + targetSlot + ") then exception occurred."); + } finally { + inv.unlockInventory(); } - tradeItem.setPosition(targetSlot); - chr.getTrade().addItem(tradeItem); } } } else if (mode == Action.CONFIRM.getCode()) { diff --git a/src/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/net/server/channel/handlers/PlayerLoggedinHandler.java index cb56de4fe2..5514a7dc24 100644 --- a/src/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -289,7 +289,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { } } if (newAlliance != null) { - c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c)); + c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c.getWorld())); c.announce(MaplePacketCreator.allianceNotice(newAlliance.getId(), newAlliance.getNotice())); if (newcomer) { diff --git a/src/net/server/channel/handlers/ScriptedItemHandler.java b/src/net/server/channel/handlers/ScriptedItemHandler.java index 98a7511ed1..1c95735b17 100644 --- a/src/net/server/channel/handlers/ScriptedItemHandler.java +++ b/src/net/server/channel/handlers/ScriptedItemHandler.java @@ -27,8 +27,7 @@ import constants.ItemConstants; import net.AbstractMaplePacketHandler; import scripting.item.ItemScriptManager; import server.MapleItemInformationProvider; -import server.MapleItemInformationProvider.scriptedItem; -import tools.MaplePacketCreator; +import server.MapleItemInformationProvider.ScriptedItem; import tools.data.input.SeekableLittleEndianAccessor; /** @@ -38,19 +37,20 @@ import tools.data.input.SeekableLittleEndianAccessor; public final class ScriptedItemHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + slea.readInt(); // trash stamp, thanks RMZero213 + short itemSlot = slea.readShort(); // item slot, thanks RMZero213 + int itemId = slea.readInt(); + MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); - slea.readInt(); // trash stamp (thanks rmzero) - short itemSlot = slea.readShort(); // item slot (thanks rmzero) - int itemId = slea.readInt(); // itemId - scriptedItem info = ii.getScriptedItemInfo(itemId); + ScriptedItem info = ii.getScriptedItemInfo(itemId); if (info == null) return; - ItemScriptManager ism = ItemScriptManager.getInstance(); + Item item = c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).getItem(itemSlot); - if (item == null || item.getItemId() != itemId || item.getQuantity() < 1 || !ism.scriptExists(info.getScript())) { + if (item == null || item.getItemId() != itemId || item.getQuantity() < 1) { return; } - ism.runItemScript(c, info.getScript()); - c.announce(MaplePacketCreator.enableActions()); - //NPCScriptManager.getInstance().start(c, info.getNpc(), null, null); + + ItemScriptManager ism = ItemScriptManager.getInstance(); + ism.runItemScript(c, info); } } diff --git a/src/net/server/channel/handlers/TrockAddMapHandler.java b/src/net/server/channel/handlers/TrockAddMapHandler.java index f105ad7c9b..0f0c34364b 100644 --- a/src/net/server/channel/handlers/TrockAddMapHandler.java +++ b/src/net/server/channel/handlers/TrockAddMapHandler.java @@ -33,6 +33,8 @@ import tools.data.input.SeekableLittleEndianAccessor; * @author kevintjuh93 */ public final class TrockAddMapHandler extends AbstractMaplePacketHandler { + + @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); byte type = slea.readByte(); diff --git a/src/net/server/channel/handlers/UseCashItemHandler.java b/src/net/server/channel/handlers/UseCashItemHandler.java index 595e1e5121..5b5cb13540 100644 --- a/src/net/server/channel/handlers/UseCashItemHandler.java +++ b/src/net/server/channel/handlers/UseCashItemHandler.java @@ -53,6 +53,7 @@ import server.MapleShop; import server.MapleShopFactory; import server.TimerManager; import server.maps.AbstractMapleMapObject; +import server.maps.FieldLimit; import server.maps.MaplePlayerShopItem; import server.maps.MapleKite; import server.maps.MapleMap; @@ -106,31 +107,32 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { if (itemType == 504) { // vip teleport rock String error1 = "Either the player could not be found or you were trying to teleport to an illegal location."; - boolean vip = slea.readByte() == 1; + boolean vip = slea.readByte() == 1 && itemId / 1000 >= 5041; remove(c, position, itemId); + boolean success = false; if (!vip) { int mapId = slea.readInt(); - if (c.getChannelServer().getMapFactory().getMap(mapId).getForcedReturnId() == 999999999) { - player.changeMap(c.getChannelServer().getMapFactory().getMap(mapId)); + if (itemId / 1000 >= 5041 || mapId / 100000000 == player.getMapId() / 100000000) { //check vip or same continent + MapleMap targetMap = c.getChannelServer().getMapFactory().getMap(mapId); + if (!FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit()) && (targetMap.getForcedReturnId() == 999999999 || mapId < 100000000)) { + player.forceChangeMap(targetMap, targetMap.getRandomPlayerSpawnpoint()); + success = true; + } else { + player.dropMessage(1, error1); + } } else { - MapleInventoryManipulator.addById(c, itemId, (short) 1); - player.dropMessage(1, error1); - c.announce(MaplePacketCreator.enableActions()); + player.dropMessage(1, "You cannot teleport between continents with this teleport rock."); } } else { String name = slea.readMapleAsciiString(); MapleCharacter victim = c.getChannelServer().getPlayerStorage().getCharacterByName(name); - boolean success = false; + if (victim != null) { - MapleMap target = victim.getMap(); - if (c.getChannelServer().getMapFactory().getMap(victim.getMapId()).getForcedReturnId() == 999999999 || victim.getMapId() < 100000000) { + MapleMap targetMap = victim.getMap(); + if (!FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit()) && (targetMap.getForcedReturnId() == 999999999 || targetMap.getId() < 100000000)) { if (victim.gmLevel() <= player.gmLevel()) { - if (itemId == 5041000 || victim.getMapId() / player.getMapId() == 1) { //viprock & same continent - player.changeMap(target, target.findClosestPlayerSpawnpoint(victim.getPosition())); - success = true; - } else { - player.dropMessage(1, "You cannot teleport between continents with this teleport rock."); - } + player.forceChangeMap(targetMap, targetMap.findClosestPlayerSpawnpoint(victim.getPosition())); + success = true; } else { player.dropMessage(1, error1); } @@ -140,10 +142,11 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { } else { player.dropMessage(1, "Player could not be found in this channel."); } - if (!success) { - MapleInventoryManipulator.addById(c, itemId, (short) 1); - c.announce(MaplePacketCreator.enableActions()); - } + } + + if (!success) { + MapleInventoryManipulator.addById(c, itemId, (short) 1); + c.announce(MaplePacketCreator.enableActions()); } } else if (itemType == 505) { // AP/SP reset if(!player.isAlive()) { diff --git a/src/net/server/coordinator/MapleMonsterAggroCoordinator.java b/src/net/server/coordinator/MapleMonsterAggroCoordinator.java index c2f2224afd..1cfc75b72c 100644 --- a/src/net/server/coordinator/MapleMonsterAggroCoordinator.java +++ b/src/net/server/coordinator/MapleMonsterAggroCoordinator.java @@ -25,8 +25,10 @@ import java.util.Comparator; import java.util.Map; import java.util.Map.Entry; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import constants.ServerConstants; import client.MapleCharacter; @@ -56,6 +58,8 @@ public class MapleMonsterAggroCoordinator { private Map> mobAggroEntries = new HashMap<>(); private Map> mobSortedAggros = new HashMap<>(); + private Set mapPuppetEntries = new HashSet<>(); + private class PlayerAggroEntry { protected int cid; protected int averageDamage = 0; @@ -291,6 +295,12 @@ public class MapleMonsterAggroCoordinator { } public boolean isLeadingCharacterAggro(MapleMonster mob, MapleCharacter player) { + if (mob.isLeadingPuppetInVicinity()) { + return false; + } else if (mob.isCharacterPuppetInVicinity(player)) { + return true; + } + // by assuming the quasi-sorted nature of "mobAggroList", this method // returns whether the player given as parameter can be elected as next aggro leader @@ -342,6 +352,24 @@ public class MapleMonsterAggroCoordinator { } } + public void addPuppetAggro(MapleCharacter player) { + synchronized (mapPuppetEntries) { + mapPuppetEntries.add(player.getId()); + } + } + + public void removePuppetAggro(Integer cid) { + synchronized (mapPuppetEntries) { + mapPuppetEntries.remove(cid); + } + } + + public List getPuppetAggroList() { + synchronized (mapPuppetEntries) { + return new ArrayList<>(mapPuppetEntries); + } + } + public void dispose() { stopAggroCoordinator(); diff --git a/src/net/server/coordinator/MapleSessionCoordinator.java b/src/net/server/coordinator/MapleSessionCoordinator.java index 86da706e5e..cc14b57c7d 100644 --- a/src/net/server/coordinator/MapleSessionCoordinator.java +++ b/src/net/server/coordinator/MapleSessionCoordinator.java @@ -68,6 +68,7 @@ public class MapleSessionCoordinator { } private final LoginStorage loginStorage = new LoginStorage(); + private final Map onlineClients = new HashMap<>(); private final Set onlineRemoteHwids = new HashSet<>(); private final Map> loginRemoteHosts = new HashMap<>(); private final Set pooledRemoteHosts = new HashSet<>(); @@ -221,6 +222,24 @@ public class MapleSessionCoordinator { return ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress(); } + private static MapleClient getSessionClient(IoSession session) { + return (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); + } + + public void updateOnlineSession(IoSession session) { + MapleClient client = getSessionClient(session); + + if (client != null) { + int accountId = client.getAccID(); + MapleClient ingameClient = onlineClients.get(accountId); + if (ingameClient != null) { // thanks MedicOP for finding out a loss of loggedin account uniqueness when using the CMS "Unstuck" feature + ingameClient.forceDisconnect(); + } + + onlineClients.put(accountId, client); + } + } + public boolean canStartLoginSession(IoSession session) { if (!ServerConstants.DETERRED_MULTICLIENT) return true; @@ -296,6 +315,11 @@ public class MapleSessionCoordinator { String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); if (nibbleHwid != null) { onlineRemoteHwids.remove(nibbleHwid); + + MapleClient client = getSessionClient(session); + if (client != null) { + onlineClients.remove(client.getAccID()); + } } } @@ -448,6 +472,11 @@ public class MapleSessionCoordinator { hwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); // making sure to clean up calls to this function on login phase onlineRemoteHwids.remove(hwid); + MapleClient client = getSessionClient(session); + if (client != null) { + onlineClients.remove(client.getAccID()); + } + if (immediately != null) { session.close(immediately); } diff --git a/src/net/server/guild/MapleAlliance.java b/src/net/server/guild/MapleAlliance.java index 97809e6787..3320aa22cd 100644 --- a/src/net/server/guild/MapleAlliance.java +++ b/src/net/server/guild/MapleAlliance.java @@ -130,7 +130,9 @@ public class MapleAlliance { Server.getInstance().addAlliance(id, alliance); - Server.getInstance().allianceMessage(id, MaplePacketCreator.updateAllianceInfo(alliance, guildMasters.get(0).getClient()), -1, -1); + int worldid = guildMasters.get(0).getWorld(); + Server.getInstance().allianceMessage(id, MaplePacketCreator.updateAllianceInfo(alliance, worldid), -1, -1); + Server.getInstance().allianceMessage(id, MaplePacketCreator.getGuildAlliances(alliance, worldid), -1, -1); // thanks Vcoc for noticing guilds from other alliances being visually stacked here due to this not being updated } catch (Exception e) { e.printStackTrace(); return null; @@ -347,7 +349,7 @@ public class MapleAlliance { public void updateAlliancePackets(MapleCharacter chr) { if (allianceId > 0) { - this.broadcastMessage(MaplePacketCreator.updateAllianceInfo(this, chr.getClient())); + this.broadcastMessage(MaplePacketCreator.updateAllianceInfo(this, chr.getWorld())); this.broadcastMessage(MaplePacketCreator.allianceNotice(this.getId(), this.getNotice())); } } diff --git a/src/net/server/handlers/login/CharSelectedHandler.java b/src/net/server/handlers/login/CharSelectedHandler.java index dd24f59873..505e61ec39 100644 --- a/src/net/server/handlers/login/CharSelectedHandler.java +++ b/src/net/server/handlers/login/CharSelectedHandler.java @@ -78,13 +78,13 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler { } if (c.hasBannedMac() || c.hasBannedHWID()) { - session.close(true); + MapleSessionCoordinator.getInstance().closeSession(session, true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - session.close(true); + MapleSessionCoordinator.getInstance().closeSession(session, true); return; } diff --git a/src/net/server/handlers/login/CharSelectedWithPicHandler.java b/src/net/server/handlers/login/CharSelectedWithPicHandler.java index 3d31ff1203..8017e4967b 100644 --- a/src/net/server/handlers/login/CharSelectedWithPicHandler.java +++ b/src/net/server/handlers/login/CharSelectedWithPicHandler.java @@ -59,13 +59,13 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { } if (c.hasBannedMac() || c.hasBannedHWID()) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } diff --git a/src/net/server/handlers/login/LoginPasswordHandler.java b/src/net/server/handlers/login/LoginPasswordHandler.java index a3963ee681..6940088b71 100644 --- a/src/net/server/handlers/login/LoginPasswordHandler.java +++ b/src/net/server/handlers/login/LoginPasswordHandler.java @@ -83,6 +83,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler { c.setAccID(rs.getInt(1)); rs.close(); } catch (SQLException | NoSuchAlgorithmException | UnsupportedEncodingException e) { + c.setAccID(-1); e.printStackTrace(); } finally { disposeSql(con, ps); diff --git a/src/net/server/handlers/login/RegisterPicHandler.java b/src/net/server/handlers/login/RegisterPicHandler.java index 016dd89527..c8fd76f349 100644 --- a/src/net/server/handlers/login/RegisterPicHandler.java +++ b/src/net/server/handlers/login/RegisterPicHandler.java @@ -59,13 +59,13 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { } if (c.hasBannedMac() || c.hasBannedHWID()) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } @@ -96,7 +96,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { e.printStackTrace(); } } else { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); } } } \ No newline at end of file diff --git a/src/net/server/handlers/login/ViewAllCharRegisterPicHandler.java b/src/net/server/handlers/login/ViewAllCharRegisterPicHandler.java index ced0c14a8e..5d2c13a500 100644 --- a/src/net/server/handlers/login/ViewAllCharRegisterPicHandler.java +++ b/src/net/server/handlers/login/ViewAllCharRegisterPicHandler.java @@ -52,7 +52,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand c.updateHWID(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } @@ -65,7 +65,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } diff --git a/src/net/server/handlers/login/ViewAllCharSelectedHandler.java b/src/net/server/handlers/login/ViewAllCharSelectedHandler.java index 1198275841..050a0419f4 100644 --- a/src/net/server/handlers/login/ViewAllCharSelectedHandler.java +++ b/src/net/server/handlers/login/ViewAllCharSelectedHandler.java @@ -72,7 +72,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler c.updateHWID(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } @@ -85,7 +85,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } diff --git a/src/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java b/src/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java index 6617d1eda8..391c4fd7ef 100644 --- a/src/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java +++ b/src/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java @@ -54,7 +54,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle c.updateHWID(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } @@ -67,7 +67,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - c.getSession().close(true); + MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); return; } diff --git a/src/scripting/AbstractPlayerInteraction.java b/src/scripting/AbstractPlayerInteraction.java index 5eb65e07ec..da8d4d6b31 100644 --- a/src/scripting/AbstractPlayerInteraction.java +++ b/src/scripting/AbstractPlayerInteraction.java @@ -228,7 +228,11 @@ public class AbstractPlayerInteraction { } public boolean canHold(int itemid, int quantity) { - return getPlayer().canHold(itemid, quantity); + return canHoldAll(Collections.singletonList(itemid), Collections.singletonList(quantity), true); + } + + public boolean canHold(int itemid, int quantity, int removeItemid, int removeQuantity) { + return canHoldAllAfterRemoving(Collections.singletonList(itemid), Collections.singletonList(quantity), Collections.singletonList(removeItemid), Collections.singletonList(removeQuantity)); } private static List convertToIntegerArray(List list) { @@ -263,10 +267,6 @@ public class AbstractPlayerInteraction { return MapleInventory.checkSpots(c.getPlayer(), addedItems, false); } - public boolean canHold(int itemid, int quantity, int removeItemid, int removeQuantity) { - return canHoldAllAfterRemoving(Collections.singletonList(itemid), Collections.singletonList(quantity), Collections.singletonList(removeItemid), Collections.singletonList(removeQuantity)); - } - private static List> prepareProofInventoryItems(List> items) { List> addedItems = new LinkedList<>(); for(Pair p : items) { diff --git a/src/scripting/item/ItemScriptMethods.java b/src/scripting/event/EventInstanceInProgressException.java similarity index 64% rename from src/scripting/item/ItemScriptMethods.java rename to src/scripting/event/EventInstanceInProgressException.java index 206a7105bc..128429562d 100644 --- a/src/scripting/item/ItemScriptMethods.java +++ b/src/scripting/event/EventInstanceInProgressException.java @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + 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 @@ -19,17 +17,19 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package scripting.item; +package scripting.event; -import client.MapleClient; -import scripting.AbstractPlayerInteraction; /** * - * @author kevintjuh93 + * @author Ronan */ -public class ItemScriptMethods extends AbstractPlayerInteraction { - public ItemScriptMethods(MapleClient c) { - super(c); +public class EventInstanceInProgressException extends Exception { + + public static String EIIP_KEY = "Event instance "; + + public EventInstanceInProgressException(String eventName, String eventInstance) { + super(EIIP_KEY + "already in progress - " + eventName + ", EM: " + eventInstance); } + } diff --git a/src/scripting/event/EventManager.java b/src/scripting/event/EventManager.java index b91ce46206..d051f2586d 100644 --- a/src/scripting/event/EventManager.java +++ b/src/scripting/event/EventManager.java @@ -260,7 +260,7 @@ public class EventManager { } } - public EventInstanceManager newInstance(String name) { + public EventInstanceManager newInstance(String name) throws EventInstanceInProgressException { EventInstanceManager ret = getReadyInstance(); if(ret == null) { @@ -271,7 +271,7 @@ public class EventManager { synchronized (instances) { if (instances.containsKey(name)) { - return null; + throw new EventInstanceInProgressException(name, this.getName()); } instances.put(name, ret); @@ -365,6 +365,34 @@ public class EventManager { return -1; } + private String getInternalScriptExceptionMessage(Throwable a) { + if (!(a instanceof ScriptException)) { + return null; + } + + while(true) { + Throwable t = a; + a = a.getCause(); + + if (a == null) { + return t.getMessage(); + } + } + } + + private EventInstanceManager createInstance(String name, Object... args) throws ScriptException, NoSuchMethodException { + return (EventInstanceManager) iv.invokeFunction(name, args); + } + + private void registerEventInstance(String eventName, int lobbyId) { + Integer oldLobby = instanceLocks.get(eventName); + if (oldLobby != null) { + setLockLobby(oldLobby, false); + } + + instanceLocks.put(eventName, lobbyId); + } + public boolean startInstance(MapleExpedition exped) { return startInstance(-1, exped); } @@ -394,9 +422,14 @@ public class EventManager { EventInstanceManager eim; try { - eim = (EventInstanceManager) (iv.invokeFunction("setup", leader.getClient().getChannel())); - instanceLocks.put(eim.getName(), lobbyId); - } catch (NullPointerException npe) { + eim = createInstance("setup", leader.getClient().getChannel()); + registerEventInstance(eim.getName(), lobbyId); + } catch (ScriptException | NullPointerException e) { + String message = getInternalScriptExceptionMessage(e); + if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) { + throw e; + } + if(lobbyId > -1) { setLockLobby(lobbyId, false); } @@ -460,9 +493,14 @@ public class EventManager { EventInstanceManager eim; try { - eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : leader.getId())); - instanceLocks.put(eim.getName(), lobbyId); - } catch (NullPointerException npe) { + eim = createInstance("setup", difficulty, (lobbyId > -1) ? lobbyId : leader.getId()); + registerEventInstance(eim.getName(), lobbyId); + } catch (ScriptException | NullPointerException e) { + String message = getInternalScriptExceptionMessage(e); + if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) { + throw e; + } + if(lobbyId > -1) { setLockLobby(lobbyId, false); } @@ -520,9 +558,14 @@ public class EventManager { EventInstanceManager eim; try { - eim = (EventInstanceManager) (iv.invokeFunction("setup", (Object) null)); - instanceLocks.put(eim.getName(), lobbyId); - } catch (NullPointerException npe) { + eim = createInstance("setup", (Object) null); + registerEventInstance(eim.getName(), lobbyId); + } catch (ScriptException | NullPointerException e) { + String message = getInternalScriptExceptionMessage(e); + if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) { + throw e; + } + if(lobbyId > -1) { setLockLobby(lobbyId, false); } @@ -582,9 +625,14 @@ public class EventManager { EventInstanceManager eim; try { - eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : party.getLeaderId())); - instanceLocks.put(eim.getName(), lobbyId); - } catch (NullPointerException npe) { + eim = createInstance("setup", difficulty, (lobbyId > -1) ? lobbyId : party.getLeaderId()); + registerEventInstance(eim.getName(), lobbyId); + } catch (ScriptException | NullPointerException e) { + String message = getInternalScriptExceptionMessage(e); + if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) { + throw e; + } + if(lobbyId > -1) { setLockLobby(lobbyId, false); } @@ -652,7 +700,7 @@ public class EventManager { } return false; } - instanceLocks.put(eim.getName(), lobbyId); + registerEventInstance(eim.getName(), lobbyId); eim.setLeader(leader); iv.invokeFunction("setup", eim); diff --git a/src/scripting/item/ItemScriptManager.java b/src/scripting/item/ItemScriptManager.java index c001a879e6..c38d9fb4f9 100644 --- a/src/scripting/item/ItemScriptManager.java +++ b/src/scripting/item/ItemScriptManager.java @@ -22,21 +22,8 @@ along with this program. If not, see . package scripting.item; import client.MapleClient; -import constants.ServerConstants; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.HashMap; -import java.util.Map; -import javax.script.Compilable; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineFactory; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import tools.FilePrinter; -import tools.MaplePacketCreator; +import scripting.npc.NPCScriptManager; +import server.MapleItemInformationProvider.ScriptedItem; public class ItemScriptManager { @@ -46,60 +33,7 @@ public class ItemScriptManager { return instance; } - private Map scripts = new HashMap<>(); - private ScriptEngineFactory sef; - - private ItemScriptManager() { - ScriptEngineManager sem = new ScriptEngineManager(); - sef = sem.getEngineByName("javascript").getFactory(); - } - - public boolean scriptExists(String scriptName) { - File scriptFile = new File("scripts/item/" + scriptName + ".js"); - return scriptFile.exists(); - } - - public void runItemScript(MapleClient c, String scriptName) { - if (scripts.containsKey(scriptName)) { - try { - scripts.get(scriptName).invokeFunction("start", new ItemScriptMethods(c)); - } catch (ScriptException | NoSuchMethodException ex) { - FilePrinter.printError(FilePrinter.ITEM + scriptName + ".txt", ex); - } - return; - } - File scriptFile = new File("scripts/item/" + scriptName + ".js"); - if (!scriptFile.exists()) { - c.announce(MaplePacketCreator.enableActions()); - return; - } - FileReader fr = null; - ScriptEngine portal = sef.getScriptEngine(); - try { - fr = new FileReader(scriptFile); - - // java 8 support here thanks to Arufonsu - if (ServerConstants.JAVA_8){ - portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); - } - - ((Compilable) portal).compile(fr).eval(); - - final Invocable script = ((Invocable) portal); - scripts.put(scriptName, script); - script.invokeFunction("start", new ItemScriptMethods(c)); - } catch (final UndeclaredThrowableException | ScriptException ute) { - FilePrinter.printError(FilePrinter.ITEM + scriptName + ".txt", ute); - } catch (final Exception e) { - FilePrinter.printError(FilePrinter.ITEM + scriptName + ".txt", e); - } finally { - if (fr != null) { - try { - fr.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } + public void runItemScript(MapleClient c, ScriptedItem scriptItem) { + NPCScriptManager.getInstance().start(c, scriptItem, null); } } \ No newline at end of file diff --git a/src/scripting/map/MapScriptManager.java b/src/scripting/map/MapScriptManager.java index a17239dfe0..04acbecc39 100644 --- a/src/scripting/map/MapScriptManager.java +++ b/src/scripting/map/MapScriptManager.java @@ -78,18 +78,18 @@ public class MapScriptManager { return; } FileReader fr = null; - ScriptEngine portal = sef.getScriptEngine(); + ScriptEngine se = sef.getScriptEngine(); try { fr = new FileReader(scriptFile); // java 8 support here thanks to Arufonsu if (ServerConstants.JAVA_8){ - portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); + se.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); } - ((Compilable) portal).compile(fr).eval(); + ((Compilable) se).compile(fr).eval(); - final Invocable script = ((Invocable) portal); + final Invocable script = ((Invocable) se); scripts.put(scriptName, script); script.invokeFunction("start", new MapScriptMethods(c)); } catch (final UndeclaredThrowableException | ScriptException ute) { diff --git a/src/scripting/npc/NPCConversationManager.java b/src/scripting/npc/NPCConversationManager.java index 0a8746270c..e5c7a41bf9 100644 --- a/src/scripting/npc/NPCConversationManager.java +++ b/src/scripting/npc/NPCConversationManager.java @@ -74,16 +74,18 @@ public class NPCConversationManager extends AbstractPlayerInteraction { private int npcOid; private String scriptName; private String getText; + private boolean itemScript; public NPCConversationManager(MapleClient c, int npc, String scriptName) { - this(c, npc, -1, scriptName); + this(c, npc, -1, scriptName, false); } - public NPCConversationManager(MapleClient c, int npc, int oid, String scriptName) { + public NPCConversationManager(MapleClient c, int npc, int oid, String scriptName, boolean itemScript) { super(c); this.npc = npc; this.npcOid = oid; this.scriptName = scriptName; + this.itemScript = itemScript; } public int getNpc() { @@ -97,6 +99,14 @@ public class NPCConversationManager extends AbstractPlayerInteraction { public String getScriptName() { return scriptName; } + + public boolean isItemScript() { + return itemScript; + } + + public void resetItemScript() { + this.itemScript = false; + } public void dispose() { NPCScriptManager.getInstance().dispose(this); diff --git a/src/scripting/npc/NPCScriptManager.java b/src/scripting/npc/NPCScriptManager.java index 3bf5a440a7..120ec9ec6f 100644 --- a/src/scripting/npc/NPCScriptManager.java +++ b/src/scripting/npc/NPCScriptManager.java @@ -32,6 +32,7 @@ import javax.script.Invocable; import javax.script.ScriptException; import scripting.AbstractScriptManager; +import server.MapleItemInformationProvider.ScriptedItem; import tools.FilePrinter; import tools.MaplePacketCreator; @@ -60,7 +61,7 @@ public class NPCScriptManager extends AbstractScriptManager { } public boolean start(MapleClient c, int npc, MapleCharacter chr) { - return start(c, npc, null, chr); + return start(c, npc, -1, chr); } public boolean start(MapleClient c, int npc, int oid, MapleCharacter chr) { @@ -70,27 +71,42 @@ public class NPCScriptManager extends AbstractScriptManager { public boolean start(MapleClient c, int npc, String fileName, MapleCharacter chr) { return start(c, npc, -1, fileName, chr); } - + public boolean start(MapleClient c, int npc, int oid, String fileName, MapleCharacter chr) { + return start(c, npc, oid, fileName, chr, false, "cm"); + } + + public boolean start(MapleClient c, ScriptedItem scriptItem, MapleCharacter chr) { + return start(c, scriptItem.getNpc(), -1, scriptItem.getScript(), chr, true, "im"); + } + + private boolean start(MapleClient c, int npc, int oid, String fileName, MapleCharacter chr, boolean itemScript, String engineName) { try { - NPCConversationManager cm = new NPCConversationManager(c, npc, oid, fileName); + NPCConversationManager cm = new NPCConversationManager(c, npc, oid, fileName, itemScript); if (cms.containsKey(c)) { dispose(c); } if (c.canClickNPC()) { cms.put(c, cm); Invocable iv = null; - if (fileName != null) { - iv = getInvocable("npc/" + fileName + ".js", c); + if (!itemScript) { + if (fileName != null) { + iv = getInvocable("npc/" + fileName + ".js", c); + } + } else { + if (fileName != null) { // thanks MiLin for drafting NPC-based item scripts + iv = getInvocable("item/" + fileName + ".js", c); + } } if (iv == null) { iv = getInvocable("npc/" + npc + ".js", c); + cm.resetItemScript(); } if (iv == null || NPCScriptManager.getInstance() == null) { dispose(c); return false; } - engine.put("cm", cm); + engine.put(engineName, cm); scripts.put(c, iv); c.setClickedNPC(); try { @@ -142,10 +158,11 @@ public class NPCScriptManager extends AbstractScriptManager { cms.remove(c); scripts.remove(c); + String scriptFolder = (cm.isItemScript() ? "item" : "npc"); if(cm.getScriptName() != null) { - resetContext("npc/" + cm.getScriptName() + ".js", c); + resetContext(scriptFolder + "/" + cm.getScriptName() + ".js", c); } else { - resetContext("npc/" + cm.getNpc() + ".js", c); + resetContext(scriptFolder + "/" + cm.getNpc() + ".js", c); } } diff --git a/src/server/MapleItemInformationProvider.java b/src/server/MapleItemInformationProvider.java index 696b89fd47..ccbf21cf57 100644 --- a/src/server/MapleItemInformationProvider.java +++ b/src/server/MapleItemInformationProvider.java @@ -112,7 +112,7 @@ public class MapleItemInformationProvider { protected Map monsterBookID = new HashMap<>(); protected Map untradeableCache = new HashMap<>(); protected Map onEquipUntradeableCache = new HashMap<>(); - protected Map scriptedItemCache = new HashMap<>(); + protected Map scriptedItemCache = new HashMap<>(); protected Map karmaCache = new HashMap<>(); protected Map triggerItemCache = new HashMap<>(); protected Map expCache = new HashMap<>(); @@ -1447,14 +1447,14 @@ public class MapleItemInformationProvider { return untradeableOnEquip; } - public scriptedItem getScriptedItemInfo(int itemId) { + public ScriptedItem getScriptedItemInfo(int itemId) { if (scriptedItemCache.containsKey(itemId)) { return scriptedItemCache.get(itemId); } if ((itemId / 10000) != 243) { return null; } - scriptedItem script = new scriptedItem(MapleDataTool.getInt("spec/npc", getItemData(itemId), 0), + ScriptedItem script = new ScriptedItem(MapleDataTool.getInt("spec/npc", getItemData(itemId), 0), MapleDataTool.getString("spec/script", getItemData(itemId), ""), MapleDataTool.getInt("spec/runOnPickup", getItemData(itemId), 0) == 1); scriptedItemCache.put(itemId, script); @@ -2100,13 +2100,13 @@ public class MapleItemInformationProvider { return skillbook; } - public class scriptedItem { + public class ScriptedItem { private boolean runOnPickup; private int npc; private String script; - public scriptedItem(int npc, String script, boolean rop) { + public ScriptedItem(int npc, String script, boolean rop) { this.npc = npc; this.script = script; this.runOnPickup = rop; diff --git a/src/server/MapleTrade.java b/src/server/MapleTrade.java index f2110b31db..a477144ac2 100644 --- a/src/server/MapleTrade.java +++ b/src/server/MapleTrade.java @@ -82,16 +82,16 @@ public class MapleTrade { partner.getChr().getClient().announce(MaplePacketCreator.getTradeConfirmation()); } - private void complete1() { + private void fetchExchangedItems() { exchangeItems = partner.getItems(); exchangeMeso = partner.getMeso(); } - private void complete2() { + private void completeTrade() { boolean show = ServerConstants.USE_DEBUG; - items.clear(); meso = 0; + for (Item item : exchangeItems) { MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item); MapleInventoryManipulator.addFromDrop(chr.getClient(), item, show); @@ -164,12 +164,21 @@ public class MapleTrade { } } - public void addItem(Item item) { - items.add(item); - chr.getClient().announce(MaplePacketCreator.getTradeItemAdd((byte) 0, item)); - if (partner != null) { - partner.getChr().getClient().announce(MaplePacketCreator.getTradeItemAdd((byte) 1, item)); + public boolean addItem(Item item) { + synchronized (items) { + if (items.size() > 9) { + return false; + } + for (Item it : items) { + if (it.getPosition() == item.getPosition()) { + return false; + } + } + + items.add(item); } + + return true; } public void chat(String message) { @@ -214,38 +223,65 @@ public class MapleTrade { return MapleInventory.checkSpotsAndOwnership(chr, tradeItems); } - + + private synchronized boolean checkTradeCompleteHandshake(boolean updateSelf) { + MapleTrade self, other; + + if (updateSelf) { + self = this; + other = this.getPartner(); + } else { + self = this.getPartner(); + other = this; + } + + if (self.isLocked()) { + return false; + } + + self.lockTrade(); + return other.isLocked(); + } + + private boolean checkCompleteHandshake() { // handshake checkout thanks to Ronan + if (this.getChr().getId() < this.getPartner().getChr().getId()) { + return this.checkTradeCompleteHandshake(true); + } else { + return this.getPartner().checkTradeCompleteHandshake(false); + } + } + public static void completeTrade(MapleCharacter c) { - c.getTrade().lockTrade(); MapleTrade local = c.getTrade(); MapleTrade partner = local.getPartner(); - if (partner.isLocked()) { - local.complete1(); - partner.complete1(); + if (local.checkCompleteHandshake()) { + local.fetchExchangedItems(); + partner.fetchExchangedItems(); + if (!local.fitsMeso()) { cancelTrade(c); c.message("There is not enough meso inventory space to complete the trade."); partner.getChr().message("Partner does not have enough meso inventory space to complete the trade."); return; - } - else if (!partner.fitsMeso()) { + } else if (!partner.fitsMeso()) { cancelTrade(c); c.message("Partner does not have enough meso inventory space to complete the trade."); partner.getChr().message("There is not enough meso inventory space to complete the trade."); return; } + if (!local.fitsInInventory()) { cancelTrade(c); c.message("There is not enough inventory space to complete the trade."); partner.getChr().message("Partner does not have enough inventory space to complete the trade."); return; - } - else if (!partner.fitsInInventory()) { + } else if (!partner.fitsInInventory()) { cancelTrade(c); c.message("Partner does not have enough inventory space to complete the trade."); partner.getChr().message("There is not enough inventory space to complete the trade."); return; } + if (local.getChr().getLevel() < 15) { if (local.getChr().getMesosTraded() + local.exchangeMeso > 1000000) { cancelTrade(c); @@ -263,16 +299,18 @@ public class MapleTrade { partner.getChr().addMesosTraded(partner.exchangeMeso); } } + LogHelper.logTrade(local, partner); - local.complete2(); - partner.complete2(); + local.completeTrade(); + partner.completeTrade(); + partner.getChr().setTrade(null); c.setTrade(null); } } - - public static void cancelTrade(MapleCharacter c) { - MapleTrade trade = c.getTrade(); + + private static void cancelTradeInternal(MapleCharacter chr) { + MapleTrade trade = chr.getTrade(); if(trade == null) return; trade.cancel(); @@ -280,9 +318,36 @@ public class MapleTrade { trade.getPartner().cancel(); trade.getPartner().getChr().setTrade(null); } - c.setTrade(null); + chr.setTrade(null); + } + + private synchronized void tradeCancelHandshake(boolean updateSelf) { + MapleTrade self; + + if (updateSelf) { + self = this; + } else { + self = this.getPartner(); + } + + cancelTradeInternal(self.getChr()); + } + + private void cancelHandshake() { // handshake checkout thanks to Ronan + if (this.getChr().getId() < this.getPartner().getChr().getId()) { + this.tradeCancelHandshake(true); + } else { + this.getPartner().tradeCancelHandshake(false); + } } + public static void cancelTrade(MapleCharacter chr) { + MapleTrade trade = chr.getTrade(); + if(trade == null) return; + + trade.cancelHandshake(); + } + public static void startTrade(MapleCharacter c) { if (c.getTrade() == null) { c.setTrade(new MapleTrade((byte) 0, c)); diff --git a/src/server/life/MapleMonster.java b/src/server/life/MapleMonster.java index 92d0b141ef..6ea511e450 100644 --- a/src/server/life/MapleMonster.java +++ b/src/server/life/MapleMonster.java @@ -63,7 +63,6 @@ import scripting.event.EventInstanceManager; import server.TimerManager; import server.life.MapleLifeFactory.BanishInfo; import server.maps.MapleMap; -import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; import tools.MaplePacketCreator; import tools.Pair; @@ -72,6 +71,8 @@ import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.coordinator.MapleMonsterAggroCoordinator; +import server.MapleStatEffect; +import server.maps.MapleSummon; public class MapleMonster extends AbstractLoadedMapleLife { private ChangeableStats ostats = null; //unused, v83 WZs offers no support for changeable stats. @@ -80,7 +81,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { private AtomicLong maxHpPlusHeal = new AtomicLong(1); private int mp; private WeakReference controller = new WeakReference<>(null); - private boolean controllerHasAggro, controllerKnowsAboutAggro; + private boolean controllerHasAggro, controllerKnowsAboutAggro, controllerHasPuppet; private Collection listeners = new LinkedList<>(); private EnumMap stati = new EnumMap<>(MonsterStatus.class); private ArrayList alreadyBuffed = new ArrayList<>(); @@ -91,10 +92,15 @@ public class MapleMonster extends AbstractLoadedMapleLife { private List> usedSkills = new ArrayList<>(); private Map, Integer> skillsUsed = new HashMap<>(); private Set usedAttacks = new HashSet<>(); + private Set calledMobOids = null; + private int calledMobCount = 0; + private WeakReference callerMob = new WeakReference<>(null); private List stolenItems = new ArrayList<>(); private int team; private int parentMobOid = 0; private final HashMap takenDamage = new HashMap<>(); + private Runnable removeAfterAction = null; + private boolean availablePuppetUpdate = true; private MonitoredReentrantLock externalLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB_EXT); private MonitoredReentrantLock monsterLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB, true); @@ -152,7 +158,59 @@ public class MapleMonster extends AbstractLoadedMapleLife { public void setParentMobOid(int parentMobId) { this.parentMobOid = parentMobId; } - + + public int countAvailableMobSummons(int limit, int skillLimit) { // limit prop for summons has another conotation, found thanks to MedicOP + Set calledOids = this.calledMobOids; + if(calledOids != null) { + limit -= calledOids.size(); + } + + return Math.min(limit, skillLimit - this.calledMobCount); + } + + public void addSummonedMob(MapleMonster mob) { + Set calledOids = this.calledMobOids; + if (calledOids == null) { + calledOids = Collections.synchronizedSet(new HashSet()); + this.calledMobOids = calledOids; + } + + calledOids.add(mob.getObjectId()); + mob.setSummonerMob(this); + this.calledMobCount += 1; + } + + private void removeSummonedMob(int mobOid) { + Set calledOids = this.calledMobOids; + if (calledOids != null) { + calledOids.remove(mobOid); + } + } + + private void setSummonerMob(MapleMonster mob) { + this.callerMob = new WeakReference<>(mob); + } + + private void dispatchClearSummons() { + MapleMonster caller = this.callerMob.get(); + if (caller != null) { + caller.removeSummonedMob(this.getObjectId()); + } + + this.calledMobOids = null; + } + + public void pushRemoveAfterAction(Runnable run) { + this.removeAfterAction = run; + } + + public Runnable popRemoveAfterAction() { + Runnable r = this.removeAfterAction; + this.removeAfterAction = null; + + return r; + } + public int getHp() { return hp.get(); } @@ -421,7 +479,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { int expSharersLevel = 0; for (MapleCharacter mc : members) { if (mc.getLevel() >= minThresholdLevel) { //NO EXP WILL BE GIVEN for those who are underleveled! - if (Math.abs(killerLevel - mc.getLevel()) < ServerConstants.MIN_UNDERLEVEL_TO_EXP_LEECH) { + if (Math.abs(killerLevel - mc.getLevel()) < ServerConstants.MIN_RANGELEVEL_TO_EXP_LEECH) { // thanks Thora for pointing out leech level limitation expSharersLevel += mc.getLevel(); @@ -710,6 +768,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { } this.aggroClearDamages(); + this.dispatchClearSummons(); MonsterListener[] listenersList; statiLock.lock(); @@ -809,6 +868,10 @@ public class MapleMonster extends AbstractLoadedMapleLife { private void setControllerKnowsAboutAggro(boolean controllerKnowsAboutAggro) { if (!fake) this.controllerKnowsAboutAggro = controllerKnowsAboutAggro; } + + private void setControllerHasPuppet(boolean controllerHasPuppet) { + this.controllerHasPuppet = controllerHasPuppet; + } public byte[] makeBossHPBarPacket() { return MaplePacketCreator.showBossHP(getId(), getHp(), getMaxHp(), getTagColor(), getTagBgColor()); @@ -1233,39 +1296,13 @@ public class MapleMonster extends AbstractLoadedMapleLife { return false; } - int useLimit = toUse.getLimit(); int useSkillid = toUse.getSkillId(); - if (useSkillid == 200) { - int i = 0; - for (MapleMapObject mo : getMap().getMapObjects()) { - if (mo.getType() == MapleMapObjectType.MONSTER) { - i++; - } - } - if (i > 100) { - return false; - } - if (map.isDojoMap()) { // spawns in dojo should be unlimited - useLimit = 0; - } - } else if (useSkillid >= 143 && useSkillid <= 145) { + if (useSkillid >= 143 && useSkillid <= 145) { if (this.isBuffed(MonsterStatus.WEAPON_REFLECT) || this.isBuffed(MonsterStatus.MAGIC_REFLECT)) { return false; } } - if (useLimit > 0) { - monsterLock.lock(); - try { - Integer times = this.skillsUsed.get(new Pair<>(useSkillid, toUse.getSkillLevel())); - if (times != null && times >= useLimit) { - return false; - } - } finally { - monsterLock.unlock(); - } - } - monsterLock.lock(); try { /* @@ -1303,13 +1340,14 @@ public class MapleMonster extends AbstractLoadedMapleLife { try { mp -= skill.getMpCon(); - this.usedSkills.add(new Pair<>(skillId, level)); - if (this.skillsUsed.containsKey(new Pair<>(skillId, level))) { - int times = this.skillsUsed.get(new Pair<>(skillId, level)) + 1; - this.skillsUsed.remove(new Pair<>(skillId, level)); - this.skillsUsed.put(new Pair<>(skillId, level), times); + Pair skillKey = new Pair<>(skillId, level); + this.usedSkills.add(skillKey); + + Integer useCount = this.skillsUsed.remove(skillKey); + if (useCount != null) { + this.skillsUsed.put(skillKey, useCount + 1); } else { - this.skillsUsed.put(new Pair<>(skillId, level), 1); + this.skillsUsed.put(skillKey, 1); } } finally { monsterLock.unlock(); @@ -1605,6 +1643,40 @@ public class MapleMonster extends AbstractLoadedMapleLife { changeLevelByDifficulty(difficulty, pqMob); } + private boolean isPuppetInVicinity(MapleSummon summon) { + return summon.getPosition().distanceSq(this.getPosition()) < 177777; + } + + public boolean isCharacterPuppetInVicinity(MapleCharacter chr) { + MapleStatEffect mse = chr.getBuffEffect(MapleBuffStat.PUPPET); + if (mse != null) { + MapleSummon summon = chr.getSummonByKey(mse.getSourceId()); + + // check whether mob is currently under a puppet's field of action or not + if (summon != null) { + if (isPuppetInVicinity(summon)) { + return true; + } + } else { + map.getAggroCoordinator().removePuppetAggro(chr.getId()); + } + } + + return false; + } + + public boolean isLeadingPuppetInVicinity() { + MapleCharacter chrController = this.getActiveController(); + + if (chrController != null) { + if (this.isCharacterPuppetInVicinity(chrController)) { + return true; + } + } + + return false; + } + private MapleCharacter getNextControllerCandidate() { int mincontrolled = Integer.MAX_VALUE; MapleCharacter newController = null; @@ -1612,11 +1684,16 @@ public class MapleMonster extends AbstractLoadedMapleLife { int mincontrolleddead = Integer.MAX_VALUE; MapleCharacter newControllerDead = null; + MapleCharacter newControllerWithPuppet = null; + for (MapleCharacter chr : getMap().getAllPlayers()) { if (!chr.isHidden()) { int ctrlMonsSize = chr.getNumControlledMonsters(); - if (chr.isAlive()) { + if (isCharacterPuppetInVicinity(chr)) { + newControllerWithPuppet = chr; + break; + } else if (chr.isAlive()) { if (ctrlMonsSize < mincontrolled) { mincontrolled = ctrlMonsSize; newController = chr; @@ -1630,7 +1707,13 @@ public class MapleMonster extends AbstractLoadedMapleLife { } } - return newController != null ? newController : newControllerDead; + if (newControllerWithPuppet != null) { + return newControllerWithPuppet; + } else if (newController != null) { + return newController; + } else { + return newControllerDead; + } } /** @@ -1682,15 +1765,39 @@ public class MapleMonster extends AbstractLoadedMapleLife { this.setController(newController); this.setControllerHasAggro(immediateAggro); this.setControllerKnowsAboutAggro(false); + this.setControllerHasPuppet(false); } finally { aggroUpdateLock.unlock(); } + this.aggroUpdatePuppetVisibility(); newController.announce(MaplePacketCreator.controlMonster(this, false, immediateAggro)); newController.controlMonster(this); } } + public void aggroAddPuppet(MapleCharacter player) { + MapleMonsterAggroCoordinator mmac = map.getAggroCoordinator(); + mmac.addPuppetAggro(player); + + aggroUpdatePuppetController(player); + + if (this.isControllerHasAggro()) { + this.aggroUpdatePuppetVisibility(); + } + } + + public void aggroRemovePuppet(MapleCharacter player) { + MapleMonsterAggroCoordinator mmac = map.getAggroCoordinator(); + mmac.removePuppetAggro(player.getId()); + + aggroUpdatePuppetController(null); + + if (this.isControllerHasAggro()) { + this.aggroUpdatePuppetVisibility(); + } + } + /** * Automagically finds a new controller for the given monster from the chars * on the map it is from... @@ -1710,6 +1817,60 @@ public class MapleMonster extends AbstractLoadedMapleLife { this.aggroSwitchController(newController, false); } + /** + * Finds a new controller for the given monster from the chars with deployed puppet + * nearby on the map it is from... + * + */ + private void aggroUpdatePuppetController(MapleCharacter newController) { + MapleCharacter chrController = this.getActiveController(); + boolean updateController = false; + + if (chrController != null && chrController.isAlive()) { + if (isCharacterPuppetInVicinity(chrController)) { + return; + } + } else { + updateController = true; + } + + if (newController == null || !isCharacterPuppetInVicinity(newController)) { + MapleMonsterAggroCoordinator mmac = map.getAggroCoordinator(); + + List puppetOwners = mmac.getPuppetAggroList(); + List toRemovePuppets = new LinkedList<>(); + + for (Integer cid : puppetOwners) { + MapleCharacter chr = map.getCharacterById(cid); + + if (chr != null) { + if (isCharacterPuppetInVicinity(chr)) { + newController = chr; + break; + } + } else { + toRemovePuppets.add(cid); + } + } + + for (Integer cid : toRemovePuppets) { + mmac.removePuppetAggro(cid); + } + + if (newController == null) { // was a new controller found? (if not there's no puppet nearby) + if (updateController) { + aggroUpdateController(); + } + + return; + } + } else if (chrController == newController) { + this.aggroUpdatePuppetVisibility(); + } + + this.aggroSwitchController(newController, this.isControllerHasAggro()); + } + /** * Ensures controllability removal of the current player controller, and * fetches for any player on the map to start controlling in place. @@ -1767,6 +1928,9 @@ public class MapleMonster extends AbstractLoadedMapleLife { if (chrController != attacker) { if (this.getMapAggroCoordinator().isLeadingCharacterAggro(this, attacker)) { this.aggroSwitchController(attacker, true); + } else { + this.setControllerHasAggro(true); + this.aggroUpdatePuppetVisibility(); } /* @@ -1780,9 +1944,69 @@ public class MapleMonster extends AbstractLoadedMapleLife { */ } else { this.setControllerHasAggro(true); + this.aggroUpdatePuppetVisibility(); } } + private void aggroRefreshPuppetVisibility(MapleCharacter chrController, MapleSummon puppet) { + // lame patch for client to redirect all aggro to the puppet + + List puppetControlled = new LinkedList<>(); + for (MapleMonster mob : chrController.getControlledMonsters()) { + if (mob.isPuppetInVicinity(puppet)) { + puppetControlled.add(mob); + } + } + + for (MapleMonster mob : puppetControlled) { + chrController.announce(MaplePacketCreator.stopControllingMonster(mob.getObjectId())); + } + chrController.announce(MaplePacketCreator.removeSummon(puppet, false)); + + for (MapleMonster mob : puppetControlled) { + chrController.announce(MaplePacketCreator.controlMonster(mob, false, mob.isControllerHasAggro())); + } + chrController.announce(MaplePacketCreator.spawnSummon(puppet, false)); + } + + public void aggroUpdatePuppetVisibility() { + if (!availablePuppetUpdate) return; + + availablePuppetUpdate = false; + Runnable r = new Runnable() { + @Override + public void run() { + try { + MapleCharacter chrController = MapleMonster.this.getActiveController(); + if (chrController == null) return; + + MapleStatEffect puppetEffect = chrController.getBuffEffect(MapleBuffStat.PUPPET); + if (puppetEffect != null) { + MapleSummon puppet = chrController.getSummonByKey(puppetEffect.getSourceId()); + + if (puppet != null && isPuppetInVicinity(puppet)) { + controllerHasPuppet = true; + aggroRefreshPuppetVisibility(chrController, puppet); + return; + } + } + + if (controllerHasPuppet) { + controllerHasPuppet = false; + + chrController.announce(MaplePacketCreator.stopControllingMonster(MapleMonster.this.getObjectId())); + chrController.announce(MaplePacketCreator.controlMonster(MapleMonster.this, false, MapleMonster.this.isControllerHasAggro())); + } + } finally { + availablePuppetUpdate = true; + } + } + }; + + // had to schedule this since mob wouldn't stick to puppet aggro who knows why + this.getMap().getChannelServer().registerOverallAction(this.getMap().getId(), r, ServerConstants.UPDATE_INTERVAL); + } + /** * Clears all applied damage input for this mob, doesn't refresh target aggro. * @@ -1805,7 +2029,12 @@ public class MapleMonster extends AbstractLoadedMapleLife { } } - public final void disposeLocks() { + public void dispose() { + this.getMap().dismissRemoveAfter(this); + disposeLocks(); + } + + private void disposeLocks() { LockCollector.getInstance().registerDisposeAction(new Runnable() { @Override public void run() { diff --git a/src/server/life/MobSkill.java b/src/server/life/MobSkill.java index 70242a96a9..c58775a0db 100644 --- a/src/server/life/MobSkill.java +++ b/src/server/life/MobSkill.java @@ -33,6 +33,7 @@ import constants.GameConstants; import java.util.LinkedList; import java.util.Map; import tools.Randomizer; +import server.maps.MapleMap; import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; import server.maps.MapleMist; @@ -235,57 +236,72 @@ public class MobSkill { case 156: // speed up break; case 200: // summon - if (monster.getMap().getSpawnedMonstersOnMap() < 80) { - for (Integer mobId : getSummons()) { - MapleMonster toSpawn = MapleLifeFactory.getMonster(mobId); - if(toSpawn != null) { - if(GameConstants.isBossRush(monster.getMap().getId())) toSpawn.disableDrops(); // no littering on BRPQ pls - - toSpawn.setPosition(monster.getPosition()); - int ypos, xpos; - xpos = (int) monster.getPosition().getX(); - ypos = (int) monster.getPosition().getY(); - switch (mobId) { - case 8500003: // Pap bomb high - toSpawn.setFh((int) Math.ceil(Math.random() * 19.0)); - ypos = -590; - break; - case 8500004: // Pap bomb - xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500); - if (ypos != -590) { - ypos = (int) monster.getPosition().getY(); - } - break; - case 8510100: //Pianus bomb - if (Math.ceil(Math.random() * 5) == 1) { - ypos = 78; - xpos = (int) Randomizer.nextInt(5) + (Randomizer.nextInt(2) == 1 ? 180 : 0); - } else { + int skillLimit = this.getLimit(); + MapleMap map = monster.getMap(); + + if (map.isDojoMap()) { // spawns in dojo should be unlimited + skillLimit = Integer.MAX_VALUE; + } + + if (map.getSpawnedMonstersOnMap() < 80) { + List summons = getSummons(); + int summonLimit = monster.countAvailableMobSummons(summons.size(), skillLimit); + if (summonLimit >= 1) { + Collections.shuffle(summons); + boolean bossRushMap = GameConstants.isBossRush(map.getId()); + + for (Integer mobId : summons.subList(0, summonLimit)) { + MapleMonster toSpawn = MapleLifeFactory.getMonster(mobId); + if(toSpawn != null) { + if(bossRushMap) toSpawn.disableDrops(); // no littering on BRPQ pls + + toSpawn.setPosition(monster.getPosition()); + int ypos, xpos; + xpos = (int) monster.getPosition().getX(); + ypos = (int) monster.getPosition().getY(); + switch (mobId) { + case 8500003: // Pap bomb high + toSpawn.setFh((int) Math.ceil(Math.random() * 19.0)); + ypos = -590; + break; + case 8500004: // Pap bomb xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500); - } - break; - } - switch (monster.getMap().getId()) { - case 220080001: //Pap map - if (xpos < -890) { - xpos = (int) (Math.ceil(Math.random() * 150) - 890); - } else if (xpos > 230) { - xpos = (int) (230 - Math.ceil(Math.random() * 150)); - } - break; - case 230040420: // Pianus map - if (xpos < -239) { - xpos = (int) (Math.ceil(Math.random() * 150) - 239); - } else if (xpos > 371) { - xpos = (int) (371 - Math.ceil(Math.random() * 150)); - } - break; - } - toSpawn.setPosition(new Point(xpos, ypos)); - if (toSpawn.getId() == 8500004) { - monster.getMap().spawnFakeMonster(toSpawn); - } else { - monster.getMap().spawnMonsterWithEffect(toSpawn, getSpawnEffect(), toSpawn.getPosition()); + if (ypos != -590) { + ypos = (int) monster.getPosition().getY(); + } + break; + case 8510100: //Pianus bomb + if (Math.ceil(Math.random() * 5) == 1) { + ypos = 78; + xpos = (int) Randomizer.nextInt(5) + (Randomizer.nextInt(2) == 1 ? 180 : 0); + } else { + xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500); + } + break; + } + switch (map.getId()) { + case 220080001: //Pap map + if (xpos < -890) { + xpos = (int) (Math.ceil(Math.random() * 150) - 890); + } else if (xpos > 230) { + xpos = (int) (230 - Math.ceil(Math.random() * 150)); + } + break; + case 230040420: // Pianus map + if (xpos < -239) { + xpos = (int) (Math.ceil(Math.random() * 150) - 239); + } else if (xpos > 371) { + xpos = (int) (371 - Math.ceil(Math.random() * 150)); + } + break; + } + toSpawn.setPosition(new Point(xpos, ypos)); + if (toSpawn.getId() == 8500004) { + map.spawnFakeMonster(toSpawn); + } else { + map.spawnMonsterWithEffect(toSpawn, getSpawnEffect(), toSpawn.getPosition()); + } + monster.addSummonedMob(toSpawn); } } } @@ -342,7 +358,7 @@ public class MobSkill { } public List getSummons() { - return Collections.unmodifiableList(toSummon); + return new ArrayList<>(toSummon); } public int getSpawnEffect() { diff --git a/src/server/maps/MapMonitor.java b/src/server/maps/MapMonitor.java index 72dc94e68d..7dc08243f7 100644 --- a/src/server/maps/MapMonitor.java +++ b/src/server/maps/MapMonitor.java @@ -44,12 +44,19 @@ public class MapMonitor { } private void cancelAction() { - monitorSchedule.cancel(false); + if (monitorSchedule != null) { // thanks Thora for pointing a NPE occurring here + monitorSchedule.cancel(false); + monitorSchedule = null; + } + map.killAllMonsters(); map.clearDrops(); if (portal != null) { portal.setPortalStatus(MaplePortal.OPEN); } map.resetReactors(); + + map = null; + portal = null; } } diff --git a/src/server/maps/MapleHiredMerchant.java b/src/server/maps/MapleHiredMerchant.java index e97a7ebe27..0f16f494e7 100644 --- a/src/server/maps/MapleHiredMerchant.java +++ b/src/server/maps/MapleHiredMerchant.java @@ -227,6 +227,18 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { return MapleInventoryManipulator.checkSpace(c, newItem.getItemId(), newItem.getQuantity(), newItem.getOwner()) && MapleInventoryManipulator.addFromDrop(c, newItem, false); } + private int getQuantityLeft(int itemid) { + int count = 0; + + for (MaplePlayerShopItem mpsi : items) { + if (mpsi.getItem().getItemId() == itemid) { + count += (mpsi.getBundles() * mpsi.getItem().getQuantity()); + } + } + + return count; + } + public void buy(MapleClient c, int item, short quantity) { synchronized (items) { MaplePlayerShopItem pItem = items.get(item); @@ -247,7 +259,6 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { if (c.getPlayer().getMeso() >= price) { if (canBuy(c, newItem)) { c.getPlayer().gainMeso(-price, false); - if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to Vcoc synchronized (sold) { sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), newItem.getQuantity(), price)); @@ -257,6 +268,11 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { if (pItem.getBundles() < 1) { pItem.setDoesExist(false); } + + if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) { // idea thanks to Vcoc + announceItemSold(newItem, price, getQuantityLeft(pItem.getItem().getItemId())); + } + MapleCharacter owner = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterByName(ownerName); if (owner != null) { owner.addMerchantMesos(price); @@ -288,12 +304,12 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { } } - private void announceItemSold(Item item, int mesos) { - String qtyStr = (item.getQuantity() > 1) ? " (qty. " + item.getQuantity() + ")" : ""; + private void announceItemSold(Item item, int mesos, int inStore) { + String qtyStr = (item.getQuantity() > 1) ? " x " + item.getQuantity() : ""; MapleCharacter player = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(ownerId); if(player != null && player.isLoggedinWorld()) { - player.dropMessage(6, "[HIRED MERCHANT] Item '" + MapleItemInformationProvider.getInstance().getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos."); + player.dropMessage(6, "[Hired Merchant] Item '" + MapleItemInformationProvider.getInstance().getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos. (" + inStore + " left)"); } } diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index b9f7198316..c0e6908dea 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -1796,21 +1796,36 @@ public class MapleMap { private void applyRemoveAfter(final MapleMonster monster) { final selfDestruction selfDestruction = monster.getStats().selfDestruction(); if (monster.getStats().removeAfter() > 0 || selfDestruction != null && selfDestruction.getHp() < 0) { + Runnable removeAfterAction; + if (selfDestruction == null) { - registerMapSchedule(new Runnable() { + removeAfterAction = new Runnable() { @Override public void run() { killMonster(monster, null, false); } - }, monster.getStats().removeAfter() * 1000); + }; + + registerMapSchedule(removeAfterAction, monster.getStats().removeAfter() * 1000); } else { - registerMapSchedule(new Runnable() { + removeAfterAction = new Runnable() { @Override public void run() { killMonster(monster, null, false, selfDestruction.getAction()); } - }, selfDestruction.removeAfter() * 1000); + }; + + registerMapSchedule(removeAfterAction, selfDestruction.removeAfter() * 1000); } + + monster.pushRemoveAfterAction(removeAfterAction); + } + } + + public void dismissRemoveAfter(final MapleMonster monster) { + Runnable removeAfterAction = monster.popRemoveAfterAction(); + if (removeAfterAction != null) { + this.getChannelServer().forceRunOverallAction(mapid, removeAfterAction); } } @@ -2583,6 +2598,18 @@ public class MapleMap { return Collections.unmodifiableCollection(portals.values()); } */ + + public void addPlayerPuppet(MapleCharacter player) { + for (MapleMonster mm : this.getMonsters()) { + mm.aggroAddPuppet(player); + } + } + + public void removePlayerPuppet(MapleCharacter player) { + for (MapleMonster mm : this.getMonsters()) { + mm.aggroRemovePuppet(player); + } + } public void removePlayer(MapleCharacter chr) { Channel cserv = chr.getClient().getChannelServer(); @@ -3699,8 +3726,11 @@ public class MapleMap { if (mapid >= 922240100 && mapid <= 922240119) { toggleHiddenNPC(9001108); } - mapMonitor.cancel(true); - mapMonitor = null; + + if (mapMonitor != null) { + mapMonitor.cancel(true); + mapMonitor = null; + } } } }, 1000); @@ -3993,7 +4023,7 @@ public class MapleMap { public void dispose() { for(MapleMonster mm : this.getMonsters()) { - mm.disposeLocks(); + mm.dispose(); } clearMapObjects(); diff --git a/src/server/quest/MapleQuest.java b/src/server/quest/MapleQuest.java index 58a1706c4a..dc214327ef 100644 --- a/src/server/quest/MapleQuest.java +++ b/src/server/quest/MapleQuest.java @@ -536,6 +536,9 @@ public class MapleQuest { case PETTAMENESS: ret = new PetTamenessAction(this, data); break; + case PETSPEED: + ret = new PetSpeedAction(this, data); + break; default: //FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unhandled Action Type: " + type.toString() + " QuestID: " + this.getId()); break; diff --git a/src/server/quest/MapleQuestActionType.java b/src/server/quest/MapleQuestActionType.java index 170e71fa5b..06721b45bd 100644 --- a/src/server/quest/MapleQuestActionType.java +++ b/src/server/quest/MapleQuestActionType.java @@ -26,7 +26,7 @@ package server.quest; * @author Matze */ public enum MapleQuestActionType { - UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), ZERO(15); + UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), PETSPEED(15), ZERO(16); final byte type; private MapleQuestActionType(int type) { @@ -62,6 +62,8 @@ public enum MapleQuestActionType { return NORMAL_AUTO_START; } else if (name.equals("pettameness")) { return PETTAMENESS; + } else if (name.equals("petspeed")) { + return PETSPEED; } else if (name.equals("0")) { return ZERO; } else { diff --git a/src/server/quest/actions/PetSpeedAction.java b/src/server/quest/actions/PetSpeedAction.java new file mode 100644 index 0000000000..24637256d1 --- /dev/null +++ b/src/server/quest/actions/PetSpeedAction.java @@ -0,0 +1,60 @@ +/* + 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 server.quest.actions; + +import client.MapleClient; +import client.MapleCharacter; +import client.inventory.MaplePet; +import provider.MapleData; +import provider.MapleDataTool; +import server.quest.MapleQuest; +import server.quest.MapleQuestActionType; + +/** + * + * @author Ronan + */ +public class PetSpeedAction extends MapleQuestAction { + + public PetSpeedAction(MapleQuest quest, MapleData data) { + super(MapleQuestActionType.PETTAMENESS, quest); + questID = quest.getId(); + } + + + @Override + public void processData(MapleData data) {} + + @Override + public void run(MapleCharacter chr, Integer extSelection) { + MapleClient c = chr.getClient(); + + MaplePet pet = chr.getPet(0); // assuming here only the pet leader will gain owner speed + if(pet == null) return; + + c.lockClient(); + try { + pet.addPetFlag(c.getPlayer(), MaplePet.PetFlag.OWNER_SPEED); + } finally { + c.unlockClient(); + } + + } +} diff --git a/src/tools/FilePrinter.java b/src/tools/FilePrinter.java index cef5a64c29..f9e5feee6a 100644 --- a/src/tools/FilePrinter.java +++ b/src/tools/FilePrinter.java @@ -28,6 +28,7 @@ public class FilePrinter { PACKET_LOG = "game/Log.txt", CASHITEM_BOUGHT = "interactions/CashLog.txt", EXCEPTION = "game/Exceptions.txt", + TRADE_EXCEPTION = "game/TradeExceptions.txt", SQL_EXCEPTION = "game/SqlExceptions.txt", PACKET_HANDLER = "game/packethandler/", PORTAL = "game/portals/", diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index 216f73e73a..e26d6370d6 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -406,7 +406,7 @@ public class MaplePacketCreator { mplew.writeShort(pet.getCloseness()); mplew.write(pet.getFullness()); addExpirationTime(mplew, item.getExpiration()); - mplew.writeInt(0); + mplew.writeInt(pet.getPetFlag()); /* pet flags found by -- Irenex & Spoon */ mplew.write(new byte[]{(byte) 0x50, (byte) 0x46}); //wonder what this is mplew.writeInt(0); return; @@ -1065,7 +1065,7 @@ public class MaplePacketCreator { mplew.writeShort(chr.getHp()); mplew.writeBool(false); mplew.writeLong(getTime(Server.getInstance().getCurrentTime())); - mplew.skip(10); + mplew.skip(14); return mplew.getPacket(); } @@ -1082,7 +1082,7 @@ public class MaplePacketCreator { mplew.writeInt(spawnPosition.x); // spawn position placement thanks to Arnah (Vertisy) mplew.writeInt(spawnPosition.y); mplew.writeLong(getTime(Server.getInstance().getCurrentTime())); - mplew.skip(10); + mplew.skip(14); return mplew.getPacket(); } @@ -1158,7 +1158,8 @@ public class MaplePacketCreator { mplew.write(0x0A); //v83 mplew.write(summon.getSkillLevel()); mplew.writePos(summon.getPosition()); - mplew.skip(3); + mplew.write(summon.getStance()); //bMoveAction & foothold, found thanks to Rien dev team + mplew.writeShort(0); mplew.write(summon.getMovementType().getValue()); // 0 = don't move, 1 = follow (4th mage summons?), 2/4 = only tele follow, 3 = bird follow mplew.write(summon.isPuppet() ? 0 : 1); // 0 and the summon can't attack - but puppets don't attack with 1 either ^.- mplew.write(animated ? 0 : 1); @@ -1828,8 +1829,7 @@ public class MaplePacketCreator { mplew.writeInt(!drop.isFFADrop() ? (recvrInParty ? drop.getPartyOwnerId() : drop.getOwnerId()) : 0); // owner charid/partyid :) mplew.write(drop.getDropType()); // 0 = timeout for non-owner, 1 = timeout for non-owner's party, 2 = FFA, 3 = explosive/FFA mplew.writePos(dropto); - // its not charId, but dropper's oid, this error will occur only if a monster's oid in map equals charId, (the item will drop from the error monster) - mplew.writeInt(drop.getDropper().getObjectId()); + mplew.writeInt(drop.getDropper().getObjectId()); // dropper oid, found thanks to Li Jixue if (mod != 2) { mplew.writePos(dropfrom); @@ -2736,9 +2736,9 @@ public class MaplePacketCreator { } } mplew.writeMapleAsciiString(guildName); - mplew.writeMapleAsciiString(allianceName); // does not seems to work + mplew.writeMapleAsciiString(allianceName); // does not seem to work + mplew.write(0); // pMedalInfo, thanks to Arnah (Vertisy) - mplew.write(0); MaplePet[] pets = chr.getPets(); Item inv = chr.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -114); for (int i = 0; i < 3; i++) { @@ -3778,11 +3778,16 @@ public class MaplePacketCreator { } /** - * 10: A beginner can't create a party. 1/11/14/19: Your request for a party - * didn't work due to an unexpected error. 13: You have yet to join a party. + * 10: A beginner can't create a party. 1/5/6/11/14/19: Your request for a + * party didn't work due to an unexpected error. 12: Quit as leader of the + * party. 13: You have yet to join a party. * 16: Already have joined a party. 17: The party you're trying to join is * already in full capacity. 19: Unable to find the requested character in - * this channel. + * this channel. 21: Player is blocking any party invitations. 22: Player + * is taking care of another invitation. 23: Player denied request. + * 25: Cannot kick another user in this map. 28/29: Leadership can only be + * given to a party member in the vicinity. 30: Change leadership only on + * same channel. * * @param message * @return @@ -4065,7 +4070,7 @@ public class MaplePacketCreator { mplew.writeInt(cid); mplew.writeInt(oid); mplew.write(12); - mplew.writeInt(damage); // damage display doesn't seems to work... + mplew.writeInt(damage); // damage display doesn't seem to work... mplew.writeInt(monsterIdFrom); mplew.write(0); return mplew.getPacket(); @@ -6746,7 +6751,7 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] updateAllianceInfo(MapleAlliance alliance, MapleClient c) { + public static byte[] updateAllianceInfo(MapleAlliance alliance, int world) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.ALLIANCE_OPERATION.getValue()); mplew.write(0x0F); @@ -6761,8 +6766,8 @@ public class MaplePacketCreator { } mplew.writeInt(alliance.getCapacity()); // probably capacity mplew.writeShort(0); - for (Integer guildd : alliance.getGuilds()) { - getGuildInfo(mplew, Server.getInstance().getGuild(guildd, c.getWorld())); + for (Integer guildid : alliance.getGuilds()) { + getGuildInfo(mplew, Server.getInstance().getGuild(guildid, world)); } return mplew.getPacket(); } diff --git a/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/MapleCashCosmeticsChecker.java b/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/MapleCashCosmeticsChecker.java index 3ab8ee7c70..dea31716d2 100644 --- a/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/MapleCashCosmeticsChecker.java +++ b/tools/MapleCashCosmeticsChecker/src/maplecashcosmeticschecker/MapleCashCosmeticsChecker.java @@ -57,7 +57,7 @@ public class MapleCashCosmeticsChecker { static String wzPath = "../../wz"; static String scriptPath = "../../scripts"; - private static PrintWriter printWriter = null; + static PrintWriter printWriter = null; static InputStreamReader fileReader = null; static BufferedReader bufferedReader = null; diff --git a/tools/MapleCashVegaChecker/build.xml b/tools/MapleCashVegaChecker/build.xml new file mode 100644 index 0000000000..c21bdb4382 --- /dev/null +++ b/tools/MapleCashVegaChecker/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project MapleCashVegaChecker. + + + diff --git a/tools/MapleCashVegaChecker/lib/result.txt b/tools/MapleCashVegaChecker/lib/result.txt new file mode 100644 index 0000000000..a1e80b03cf --- /dev/null +++ b/tools/MapleCashVegaChecker/lib/result.txt @@ -0,0 +1,5 @@ + # Report File autogenerated from the MapleCashVegaChecker feature by Ronan Lana. + # Generated data takes into account several data info from the server-side WZ.xmls. + + 2040759 + 2040760 diff --git a/tools/MapleCashVegaChecker/manifest.mf b/tools/MapleCashVegaChecker/manifest.mf new file mode 100644 index 0000000000..328e8e5bc3 --- /dev/null +++ b/tools/MapleCashVegaChecker/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/MapleCashVegaChecker/src/maplecashvegachecker/MapleCashVegaChecker.java b/tools/MapleCashVegaChecker/src/maplecashvegachecker/MapleCashVegaChecker.java new file mode 100644 index 0000000000..65207cb91f --- /dev/null +++ b/tools/MapleCashVegaChecker/src/maplecashvegachecker/MapleCashVegaChecker.java @@ -0,0 +1,213 @@ +/* + 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 maplecashvegachecker; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author RonanLana + * + This application main objective is to read Vega-related information from + the item's description report back missing nodes for these items. + + Estimated parse time: 10 seconds + */ +public class MapleCashVegaChecker { + + private static String wzPath = "../../wz"; + + static PrintWriter printWriter = null; + static InputStreamReader fileReader = null; + static BufferedReader bufferedReader = null; + + static int initialStringLength = 1000; + static int currentItem; + + static byte status = 0; + + static Set vegaItems = new HashSet<>(); + + 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 translateItemToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + status += 1; + + if (status == 2) { + currentItem = Integer.valueOf(getName(token)); + } + } else { + if (status == 2) { + if (getValue(token).endsWith("Vega's Spell.")) { + vegaItems.add(currentItem); + } + } + } + } + + private static void translateVegaToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + status += 1; + } else { + if (status == 2) { + if (getName(token).contentEquals("item")) { + vegaItems.remove(Integer.valueOf(getValue(token))); + } + } + } + } + + private static void readItemDescriptionFile(File f) { + System.out.print("Reading String.wz... "); + try { + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + String line; + while((line = bufferedReader.readLine())!=null){ + translateItemToken(line); + } + + bufferedReader.close(); + fileReader.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + System.out.println(vegaItems.size() + " Vega Scroll items found"); + } + + private static void readVegaDescriptionFile(File f) { + System.out.println("Reading Etc.wz..."); + try { + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + String line; + while((line = bufferedReader.readLine())!=null){ + translateVegaToken(line); + } + + bufferedReader.close(); + fileReader.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + private static void printReportFileHeader() { + printWriter.println(" # Report File autogenerated from the MapleCashVegaChecker feature by Ronan Lana."); + printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls."); + printWriter.println(); + } + + private static void reportMissingVegaItems() { + System.out.println("Reporting results ..."); + + try { + printWriter = new PrintWriter("lib/result.txt", "UTF-8"); + + printReportFileHeader(); + + for (Integer itemid : vegaItems) { + printWriter.println(" " + itemid); + } + + printWriter.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + } + + public static void main(String[] args) { + + readItemDescriptionFile(new File(wzPath + "/String.wz/Consume.img.xml")); + readVegaDescriptionFile(new File(wzPath + "/Etc.wz/VegaSpell.img.xml")); + + reportMissingVegaItems(); + } + +} diff --git a/wz/Character.wz/Accessory/01142009.img.xml b/wz/Character.wz/Accessory/01142009.img.xml index fb0a9d2fe3..a3419e6da5 100644 --- a/wz/Character.wz/Accessory/01142009.img.xml +++ b/wz/Character.wz/Accessory/01142009.img.xml @@ -10,7 +10,7 @@ - + diff --git a/wz/Character.wz/Accessory/01142010.img.xml b/wz/Character.wz/Accessory/01142010.img.xml index b8da01b9b0..b74620fa57 100644 --- a/wz/Character.wz/Accessory/01142010.img.xml +++ b/wz/Character.wz/Accessory/01142010.img.xml @@ -10,7 +10,7 @@ - + diff --git a/wz/Character.wz/Accessory/01142011.img.xml b/wz/Character.wz/Accessory/01142011.img.xml index 867e4440d9..9cc7d4cec6 100644 --- a/wz/Character.wz/Accessory/01142011.img.xml +++ b/wz/Character.wz/Accessory/01142011.img.xml @@ -10,7 +10,7 @@ - + diff --git a/wz/Character.wz/Accessory/01142012.img.xml b/wz/Character.wz/Accessory/01142012.img.xml index 8365d6c5b6..965488746c 100644 --- a/wz/Character.wz/Accessory/01142012.img.xml +++ b/wz/Character.wz/Accessory/01142012.img.xml @@ -10,7 +10,7 @@ - + diff --git a/wz/Character.wz/Accessory/01142013.img.xml b/wz/Character.wz/Accessory/01142013.img.xml index 5547cbc669..2112704e2e 100644 --- a/wz/Character.wz/Accessory/01142013.img.xml +++ b/wz/Character.wz/Accessory/01142013.img.xml @@ -10,7 +10,7 @@ - + diff --git a/wz/Character.wz/Accessory/01142133.img.xml b/wz/Character.wz/Accessory/01142133.img.xml index 836498a45e..1940253d89 100644 --- a/wz/Character.wz/Accessory/01142133.img.xml +++ b/wz/Character.wz/Accessory/01142133.img.xml @@ -10,7 +10,7 @@ - + diff --git a/wz/Etc.wz/Commodity.img.xml b/wz/Etc.wz/Commodity.img.xml index 68399645e9..a1d0196a03 100644 --- a/wz/Etc.wz/Commodity.img.xml +++ b/wz/Etc.wz/Commodity.img.xml @@ -48790,7 +48790,7 @@ - + diff --git a/wz/Etc.wz/VegaSpell.img.xml b/wz/Etc.wz/VegaSpell.img.xml index 666ceeb6fa..e5625d16a2 100644 --- a/wz/Etc.wz/VegaSpell.img.xml +++ b/wz/Etc.wz/VegaSpell.img.xml @@ -796,4 +796,12 @@ + + + + + + + + diff --git a/wz/Mob.wz/8520000.img.xml b/wz/Mob.wz/8520000.img.xml index 7612faefe6..3a5a15ffbc 100644 --- a/wz/Mob.wz/8520000.img.xml +++ b/wz/Mob.wz/8520000.img.xml @@ -26,7 +26,6 @@ - diff --git a/wz/Quest.wz/Check.img.xml b/wz/Quest.wz/Check.img.xml index 6d03217b48..64365fca39 100644 --- a/wz/Quest.wz/Check.img.xml +++ b/wz/Quest.wz/Check.img.xml @@ -38562,6 +38562,9 @@ + + + @@ -38736,6 +38739,9 @@ + + + diff --git a/wz/Quest.wz/QuestInfo.img.xml b/wz/Quest.wz/QuestInfo.img.xml index a66bf5124e..18f7d81372 100644 --- a/wz/Quest.wz/QuestInfo.img.xml +++ b/wz/Quest.wz/QuestInfo.img.xml @@ -6865,7 +6865,7 @@ Once there, talk to #b#p1200003##k to board the ship. - + diff --git a/wz/String.wz/Consume.img.xml b/wz/String.wz/Consume.img.xml index 28230edbb8..e398dc4076 100644 --- a/wz/String.wz/Consume.img.xml +++ b/wz/String.wz/Consume.img.xml @@ -1468,7 +1468,7 @@ - + @@ -1496,7 +1496,7 @@ - + @@ -4588,7 +4588,7 @@ - + @@ -8044,59 +8044,59 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -8205,7 +8205,7 @@ - + @@ -8556,7 +8556,7 @@ - + @@ -8960,7 +8960,7 @@ - + @@ -9072,7 +9072,7 @@ - + diff --git a/wz/String.wz/Eqp.img.xml b/wz/String.wz/Eqp.img.xml index f58e7cc010..06181f5d92 100644 --- a/wz/String.wz/Eqp.img.xml +++ b/wz/String.wz/Eqp.img.xml @@ -4573,7 +4573,7 @@ - +